126 lines
4.5 KiB
C#
126 lines
4.5 KiB
C#
using System.IdentityModel.Tokens.Jwt;
|
|
using System.Security.Claims;
|
|
using FsCheck;
|
|
using FsCheck.Xunit;
|
|
using HoneyBox.Core.Services;
|
|
using HoneyBox.Model.Entities;
|
|
using HoneyBox.Model.Models.Auth;
|
|
using Microsoft.Extensions.Logging;
|
|
using Moq;
|
|
|
|
namespace HoneyBox.Tests.Services;
|
|
|
|
public class JwtServicePropertyTests
|
|
{
|
|
private readonly JwtSettings _jwtSettings;
|
|
private readonly Mock<ILogger<JwtService>> _mockLogger;
|
|
private readonly JwtService _jwtService;
|
|
|
|
public JwtServicePropertyTests()
|
|
{
|
|
_jwtSettings = new JwtSettings
|
|
{
|
|
Secret = "your-secret-key-must-be-at-least-32-characters-long-for-hs256",
|
|
Issuer = "HoneyBox",
|
|
Audience = "HoneyBoxUsers",
|
|
ExpirationMinutes = 1440,
|
|
RefreshTokenExpirationDays = 7
|
|
};
|
|
|
|
_mockLogger = new Mock<ILogger<JwtService>>();
|
|
_jwtService = new JwtService(_jwtSettings, _mockLogger.Object);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 7: Token验证与授权
|
|
/// For any valid token generated by the service, validation should succeed and return a valid ClaimsPrincipal.
|
|
/// For any invalid token, validation should fail and return null.
|
|
/// Validates: Requirements 3.3, 3.4
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool ValidTokenShouldPassValidation(PositiveInt userId, NonEmptyString nickname)
|
|
{
|
|
// Create a valid user and generate a token
|
|
var user = new User
|
|
{
|
|
Id = userId.Item,
|
|
Nickname = nickname.Item,
|
|
Uid = $"uid{userId.Item}",
|
|
OpenId = $"openid{userId.Item}",
|
|
HeadImg = "https://example.com/avatar.jpg",
|
|
CreatedAt = DateTime.UtcNow,
|
|
UpdatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
var validToken = _jwtService.GenerateToken(user);
|
|
|
|
// Valid token should pass validation
|
|
var validPrincipal = _jwtService.ValidateToken(validToken);
|
|
var validTokenPasses = validPrincipal != null;
|
|
|
|
// Invalid token should fail validation
|
|
var invalidToken = "invalid.token.string";
|
|
var invalidPrincipal = _jwtService.ValidateToken(invalidToken);
|
|
var invalidTokenFails = invalidPrincipal == null;
|
|
|
|
// Null token should fail validation
|
|
var nullPrincipal = _jwtService.ValidateToken(null!);
|
|
var nullTokenFails = nullPrincipal == null;
|
|
|
|
return validTokenPasses && invalidTokenFails && nullTokenFails;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 7 (continued): Token验证与授权 - Claim Extraction
|
|
/// For any valid token, the extracted claims should match the original user data.
|
|
/// Validates: Requirements 3.3, 3.4
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool ValidatedTokenShouldContainCorrectClaims(PositiveInt userId, NonEmptyString nickname)
|
|
{
|
|
var user = new User
|
|
{
|
|
Id = userId.Item,
|
|
Nickname = nickname.Item,
|
|
Uid = $"uid{userId.Item}",
|
|
OpenId = $"openid{userId.Item}",
|
|
HeadImg = "https://example.com/avatar.jpg",
|
|
CreatedAt = DateTime.UtcNow,
|
|
UpdatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
var token = _jwtService.GenerateToken(user);
|
|
var principal = _jwtService.ValidateToken(token);
|
|
|
|
if (principal == null)
|
|
return false;
|
|
|
|
// Check that the principal contains the correct user ID claim
|
|
var userIdClaim = principal.FindFirst(ClaimTypes.NameIdentifier);
|
|
var userIdMatches = userIdClaim != null && userIdClaim.Value == userId.Item.ToString();
|
|
|
|
// Check that the principal contains the correct nickname claim
|
|
var nameClaim = principal.FindFirst(ClaimTypes.Name);
|
|
var nicknameMatches = nameClaim != null && nameClaim.Value == nickname.Item;
|
|
|
|
// Check that the principal contains the correct uid claim
|
|
var uidClaim = principal.FindFirst("uid");
|
|
var uidMatches = uidClaim != null && uidClaim.Value == $"uid{userId.Item}";
|
|
|
|
return userIdMatches && nicknameMatches && uidMatches;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 7 (continued): Token验证与授权 - Unauthorized Access
|
|
/// For any request with an invalid or missing token, the system should deny access.
|
|
/// Validates: Requirements 3.3, 3.4
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool InvalidTokenShouldDenyAccess(NonEmptyString invalidToken)
|
|
{
|
|
// Any random string should not validate as a token
|
|
var principal = _jwtService.ValidateToken(invalidToken.Item);
|
|
return principal == null;
|
|
}
|
|
}
|