* add a util method for padding base64 strings if they are not of an expected length * return the original string if it already contains padding, do not throw * add tests for padding method, and for token that needs padding
207 lines
9.0 KiB
C#
207 lines
9.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using Xunit;
|
|
|
|
namespace Discord
|
|
{
|
|
public class TokenUtilsTests
|
|
{
|
|
/// <summary>
|
|
/// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
|
|
/// to see that when a null, empty or whitespace-only string is passed as the token,
|
|
/// it will throw an ArgumentNullException.
|
|
/// </summary>
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")] // string.Empty isn't a constant type
|
|
[InlineData(" ")]
|
|
[InlineData(" ")]
|
|
[InlineData("\t")]
|
|
public void TestNullOrWhitespaceToken(string token)
|
|
{
|
|
// an ArgumentNullException should be thrown, regardless of the TokenType
|
|
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bearer, token));
|
|
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bot, token));
|
|
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Webhook, token));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
|
|
/// to see that valid Webhook tokens do not throw Exceptions.
|
|
/// </summary>
|
|
/// <param name="token"></param>
|
|
[Theory]
|
|
[InlineData("123123123")]
|
|
// bot token
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
|
|
// bearer token taken from discord docs
|
|
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
|
|
// client secret
|
|
[InlineData("937it3ow87i4ery69876wqire")]
|
|
public void TestWebhookTokenDoesNotThrowExceptions(string token)
|
|
{
|
|
TokenUtils.ValidateToken(TokenType.Webhook, token);
|
|
}
|
|
|
|
// No tests for invalid webhook token behavior, because there is nothing there yet.
|
|
|
|
/// <summary>
|
|
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
|
|
/// to see that valid Webhook tokens do not throw Exceptions.
|
|
/// </summary>
|
|
/// <param name="token"></param>
|
|
[Theory]
|
|
[InlineData("123123123")]
|
|
// bot token
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
|
|
// bearer token taken from discord docs
|
|
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
|
|
// client secret
|
|
[InlineData("937it3ow87i4ery69876wqire")]
|
|
public void TestBearerTokenDoesNotThrowExceptions(string token)
|
|
{
|
|
TokenUtils.ValidateToken(TokenType.Bearer, token);
|
|
}
|
|
|
|
// No tests for invalid bearer token behavior, because there is nothing there yet.
|
|
|
|
/// <summary>
|
|
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
|
|
/// to see that valid Bot tokens do not throw Exceptions.
|
|
/// Valid Bot tokens can be strings of length 58 or above.
|
|
/// </summary>
|
|
[Theory]
|
|
// missing a single character from the end, 58 char. still should be valid
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKW")]
|
|
// 59 char token
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWss")]
|
|
// simulated token with a very old user id
|
|
[InlineData("ODIzNjQ4MDEzNTAxMDcxMzY=.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKW")]
|
|
public void TestBotTokenDoesNotThrowExceptions(string token)
|
|
{
|
|
// This example token is pulled from the Discord Docs
|
|
// https://discordapp.com/developers/docs/reference#authentication-example-bot-token-authorization-header
|
|
// should not throw any exception
|
|
TokenUtils.ValidateToken(TokenType.Bot, token);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> with
|
|
/// a Bot token that is invalid.
|
|
/// </summary>
|
|
[Theory]
|
|
[InlineData("This is invalid")]
|
|
// bearer token
|
|
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
|
|
// client secret
|
|
[InlineData("937it3ow87i4ery69876wqire")]
|
|
// 57 char bot token
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kK")]
|
|
[InlineData("This is an invalid token, but it passes the check for string length.")]
|
|
// valid token, but passed in twice
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWsMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
|
|
public void TestBotTokenInvalidThrowsArgumentException(string token)
|
|
{
|
|
Assert.Throws<ArgumentException>(() => TokenUtils.ValidateToken(TokenType.Bot, token));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
|
|
/// to see that an <see cref="ArgumentException"/> is thrown when an invalid
|
|
/// <see cref="TokenType"/> is supplied as a parameter.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The <see cref="TokenType.User"/> type is treated as an invalid <see cref="TokenType"/>.
|
|
/// </remarks>
|
|
[Theory]
|
|
// TokenType.User
|
|
[InlineData(0)]
|
|
// out of range TokenType
|
|
[InlineData(-1)]
|
|
[InlineData(4)]
|
|
[InlineData(7)]
|
|
public void TestUnrecognizedTokenType(int type)
|
|
{
|
|
Assert.Throws<ArgumentException>(() =>
|
|
TokenUtils.ValidateToken((TokenType)type, "MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs"));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks the <see cref="TokenUtils.CheckBotTokenValidity(string)"/> method for expected output.
|
|
/// </summary>
|
|
/// <param name="token"> The Bot Token to test.</param>
|
|
/// <param name="expected"> The expected result. </param>
|
|
[Theory]
|
|
// this method only checks the first part of the JWT
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4..", true)]
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kK", true)]
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4. this part is invalid. this part is also invalid", true)]
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.", false)]
|
|
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4", false)]
|
|
[InlineData("NDI4NDc3OTQ0MDA5MTk1NTIw.xxxx.xxxxx", true)]
|
|
// should not throw an unexpected exception
|
|
[InlineData("", false)]
|
|
[InlineData(null, false)]
|
|
public void TestCheckBotTokenValidity(string token, bool expected)
|
|
{
|
|
Assert.Equal(expected, TokenUtils.CheckBotTokenValidity(token));
|
|
}
|
|
|
|
[Theory]
|
|
// cannot pass a ulong? as a param in InlineData, so have to have a separate param
|
|
// indicating if a value is null
|
|
[InlineData("NDI4NDc3OTQ0MDA5MTk1NTIw", false, 428477944009195520)]
|
|
// user id that has base 64 '=' padding
|
|
[InlineData("ODIzNjQ4MDEzNTAxMDcxMzY=", false, 82364801350107136)]
|
|
// user id that does not have '=' padding, and needs it
|
|
[InlineData("ODIzNjQ4MDEzNTAxMDcxMzY", false, 82364801350107136)]
|
|
// should return null w/o throwing other exceptions
|
|
[InlineData("", true, 0)]
|
|
[InlineData(" ", true, 0)]
|
|
[InlineData(null, true, 0)]
|
|
[InlineData("these chars aren't allowed @U#)*@#!)*", true, 0)]
|
|
public void TestDecodeBase64UserId(string encodedUserId, bool isNull, ulong expectedUserId)
|
|
{
|
|
var result = TokenUtils.DecodeBase64UserId(encodedUserId);
|
|
if (isNull)
|
|
Assert.Null(result);
|
|
else
|
|
Assert.Equal(expectedUserId, result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("QQ", "QQ==")] // "A" encoded
|
|
[InlineData("QUE", "QUE=")] // "AA"
|
|
[InlineData("QUFB", "QUFB")] // "AAA"
|
|
[InlineData("QUFBQQ", "QUFBQQ==")] // "AAAA"
|
|
[InlineData("QUFBQUFB", "QUFBQUFB")] // "AAAAAA"
|
|
// strings that already contain padding will be returned, even if invalid
|
|
[InlineData("QUFBQQ==", "QUFBQQ==")]
|
|
[InlineData("QUFBQQ=", "QUFBQQ=")]
|
|
[InlineData("=", "=")]
|
|
public void TestPadBase64String(string input, string expected)
|
|
{
|
|
Assert.Equal(expected, TokenUtils.PadBase64String(input));
|
|
}
|
|
|
|
[Theory]
|
|
// no null, empty, or whitespace
|
|
[InlineData("", typeof(ArgumentNullException))]
|
|
[InlineData(" ", typeof(ArgumentNullException))]
|
|
[InlineData("\t", typeof(ArgumentNullException))]
|
|
[InlineData(null, typeof(ArgumentNullException))]
|
|
// cannot require 3 padding chars
|
|
[InlineData("A", typeof(FormatException))]
|
|
[InlineData("QUFBQ", typeof(FormatException))]
|
|
public void TestPadBase64StringException(string input, Type type)
|
|
{
|
|
Assert.Throws(type, () =>
|
|
{
|
|
TokenUtils.PadBase64String(input);
|
|
});
|
|
}
|
|
}
|
|
}
|