You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Tests.TokenUtils.cs 10 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using Xunit;
  5. namespace Discord
  6. {
  7. public class TokenUtilsTests
  8. {
  9. /// <summary>
  10. /// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
  11. /// to see that when a null, empty or whitespace-only string is passed as the token,
  12. /// it will throw an ArgumentNullException.
  13. /// </summary>
  14. [Theory]
  15. [InlineData(null)]
  16. [InlineData("")] // string.Empty isn't a constant type
  17. [InlineData(" ")]
  18. [InlineData(" ")]
  19. [InlineData("\t")]
  20. public void TestNullOrWhitespaceToken(string token)
  21. {
  22. // an ArgumentNullException should be thrown, regardless of the TokenType
  23. Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bearer, token));
  24. Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bot, token));
  25. Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Webhook, token));
  26. }
  27. /// <summary>
  28. /// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
  29. /// to see that valid Webhook tokens do not throw Exceptions.
  30. /// </summary>
  31. /// <param name="token"></param>
  32. [Theory]
  33. [InlineData("123123123")]
  34. // bot token
  35. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
  36. // bearer token taken from discord docs
  37. [InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
  38. // client secret
  39. [InlineData("937it3ow87i4ery69876wqire")]
  40. public void TestWebhookTokenDoesNotThrowExceptions(string token)
  41. {
  42. TokenUtils.ValidateToken(TokenType.Webhook, token);
  43. }
  44. // No tests for invalid webhook token behavior, because there is nothing there yet.
  45. /// <summary>
  46. /// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
  47. /// to see that valid Webhook tokens do not throw Exceptions.
  48. /// </summary>
  49. /// <param name="token"></param>
  50. [Theory]
  51. [InlineData("123123123")]
  52. // bot token
  53. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
  54. // bearer token taken from discord docs
  55. [InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
  56. // client secret
  57. [InlineData("937it3ow87i4ery69876wqire")]
  58. public void TestBearerTokenDoesNotThrowExceptions(string token)
  59. {
  60. TokenUtils.ValidateToken(TokenType.Bearer, token);
  61. }
  62. // No tests for invalid bearer token behavior, because there is nothing there yet.
  63. /// <summary>
  64. /// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
  65. /// to see that valid Bot tokens do not throw Exceptions.
  66. /// Valid Bot tokens can be strings of length 58 or above.
  67. /// </summary>
  68. [Theory]
  69. // missing a single character from the end, 58 char. still should be valid
  70. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKW")]
  71. // 59 char token
  72. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
  73. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWss")]
  74. // simulated token with a very old user id
  75. [InlineData("ODIzNjQ4MDEzNTAxMDcxMzY=.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKW")]
  76. public void TestBotTokenDoesNotThrowExceptions(string token)
  77. {
  78. // This example token is pulled from the Discord Docs
  79. // https://discordapp.com/developers/docs/reference#authentication-example-bot-token-authorization-header
  80. // should not throw any exception
  81. TokenUtils.ValidateToken(TokenType.Bot, token);
  82. }
  83. /// <summary>
  84. /// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> with
  85. /// a Bot token that is invalid.
  86. /// </summary>
  87. [Theory]
  88. [InlineData("This is invalid")]
  89. // bearer token
  90. [InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
  91. // client secret
  92. [InlineData("937it3ow87i4ery69876wqire")]
  93. // 57 char bot token
  94. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kK")]
  95. // ends with invalid characters
  96. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k ")]
  97. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\n")]
  98. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\t")]
  99. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\r\n")]
  100. // starts with invalid characters
  101. [InlineData(" MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")]
  102. [InlineData("\nMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")]
  103. [InlineData("\tMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")]
  104. [InlineData("\r\nMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")]
  105. [InlineData("This is an invalid token, but it passes the check for string length.")]
  106. // valid token, but passed in twice
  107. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWsMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
  108. public void TestBotTokenInvalidThrowsArgumentException(string token)
  109. {
  110. Assert.Throws<ArgumentException>(() => TokenUtils.ValidateToken(TokenType.Bot, token));
  111. }
  112. /// <summary>
  113. /// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
  114. /// to see that an <see cref="ArgumentException"/> is thrown when an invalid
  115. /// <see cref="TokenType"/> is supplied as a parameter.
  116. /// </summary>
  117. /// <remarks>
  118. /// The <see cref="TokenType.User"/> type is treated as an invalid <see cref="TokenType"/>.
  119. /// </remarks>
  120. [Theory]
  121. // TokenType.User
  122. [InlineData(0)]
  123. // out of range TokenType
  124. [InlineData(-1)]
  125. [InlineData(4)]
  126. [InlineData(7)]
  127. public void TestUnrecognizedTokenType(int type)
  128. {
  129. Assert.Throws<ArgumentException>(() =>
  130. TokenUtils.ValidateToken((TokenType)type, "MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs"));
  131. }
  132. /// <summary>
  133. /// Checks the <see cref="TokenUtils.CheckBotTokenValidity(string)"/> method for expected output.
  134. /// </summary>
  135. /// <param name="token"> The Bot Token to test.</param>
  136. /// <param name="expected"> The expected result. </param>
  137. [Theory]
  138. // this method only checks the first part of the JWT
  139. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4..", true)]
  140. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kK", true)]
  141. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4. this part is invalid. this part is also invalid", true)]
  142. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.", false)]
  143. [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4", false)]
  144. [InlineData("NDI4NDc3OTQ0MDA5MTk1NTIw.xxxx.xxxxx", true)]
  145. // should not throw an unexpected exception
  146. [InlineData("", false)]
  147. [InlineData(null, false)]
  148. public void TestCheckBotTokenValidity(string token, bool expected)
  149. {
  150. Assert.Equal(expected, TokenUtils.CheckBotTokenValidity(token));
  151. }
  152. [Theory]
  153. // cannot pass a ulong? as a param in InlineData, so have to have a separate param
  154. // indicating if a value is null
  155. [InlineData("NDI4NDc3OTQ0MDA5MTk1NTIw", false, 428477944009195520)]
  156. // user id that has base 64 '=' padding
  157. [InlineData("ODIzNjQ4MDEzNTAxMDcxMzY=", false, 82364801350107136)]
  158. // user id that does not have '=' padding, and needs it
  159. [InlineData("ODIzNjQ4MDEzNTAxMDcxMzY", false, 82364801350107136)]
  160. // should return null w/o throwing other exceptions
  161. [InlineData("", true, 0)]
  162. [InlineData(" ", true, 0)]
  163. [InlineData(null, true, 0)]
  164. [InlineData("these chars aren't allowed @U#)*@#!)*", true, 0)]
  165. public void TestDecodeBase64UserId(string encodedUserId, bool isNull, ulong expectedUserId)
  166. {
  167. var result = TokenUtils.DecodeBase64UserId(encodedUserId);
  168. if (isNull)
  169. Assert.Null(result);
  170. else
  171. Assert.Equal(expectedUserId, result);
  172. }
  173. [Theory]
  174. [InlineData("QQ", "QQ==")] // "A" encoded
  175. [InlineData("QUE", "QUE=")] // "AA"
  176. [InlineData("QUFB", "QUFB")] // "AAA"
  177. [InlineData("QUFBQQ", "QUFBQQ==")] // "AAAA"
  178. [InlineData("QUFBQUFB", "QUFBQUFB")] // "AAAAAA"
  179. // strings that already contain padding will be returned, even if invalid
  180. [InlineData("QUFBQQ==", "QUFBQQ==")]
  181. [InlineData("QUFBQQ=", "QUFBQQ=")]
  182. [InlineData("=", "=")]
  183. public void TestPadBase64String(string input, string expected)
  184. {
  185. Assert.Equal(expected, TokenUtils.PadBase64String(input));
  186. }
  187. [Theory]
  188. // no null, empty, or whitespace
  189. [InlineData("", typeof(ArgumentNullException))]
  190. [InlineData(" ", typeof(ArgumentNullException))]
  191. [InlineData("\t", typeof(ArgumentNullException))]
  192. [InlineData(null, typeof(ArgumentNullException))]
  193. // cannot require 3 padding chars
  194. [InlineData("A", typeof(FormatException))]
  195. [InlineData("QUFBQ", typeof(FormatException))]
  196. public void TestPadBase64StringException(string input, Type type)
  197. {
  198. Assert.Throws(type, () =>
  199. {
  200. TokenUtils.PadBase64String(input);
  201. });
  202. }
  203. }
  204. }