test(api): update AuthController tests for encrypted login
This commit is contained in:
@@ -1,8 +1,12 @@
|
|||||||
|
// NEW/tests/JdeScoping.Api.Tests/Controllers/AuthControllerTests.cs
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using JdeScoping.Api.Controllers;
|
using JdeScoping.Api.Controllers;
|
||||||
using JdeScoping.Api.Models;
|
|
||||||
using JdeScoping.Core.Interfaces;
|
using JdeScoping.Core.Interfaces;
|
||||||
using JdeScoping.Core.Models;
|
using JdeScoping.Core.Models;
|
||||||
|
using JdeScoping.Core.Models.Auth;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@@ -15,21 +19,58 @@ namespace JdeScoping.Api.Tests.Controllers;
|
|||||||
public class AuthControllerTests
|
public class AuthControllerTests
|
||||||
{
|
{
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
|
private readonly IRsaKeyService _rsaKeyService;
|
||||||
private readonly ILogger<AuthController> _logger;
|
private readonly ILogger<AuthController> _logger;
|
||||||
private readonly AuthController _controller;
|
private readonly AuthController _controller;
|
||||||
|
private readonly RSA _testRsa;
|
||||||
|
|
||||||
public AuthControllerTests()
|
public AuthControllerTests()
|
||||||
{
|
{
|
||||||
_authService = Substitute.For<IAuthService>();
|
_authService = Substitute.For<IAuthService>();
|
||||||
|
_rsaKeyService = Substitute.For<IRsaKeyService>();
|
||||||
_logger = Substitute.For<ILogger<AuthController>>();
|
_logger = Substitute.For<ILogger<AuthController>>();
|
||||||
_controller = new AuthController(_authService, _logger);
|
|
||||||
|
// Setup test RSA key pair
|
||||||
|
_testRsa = RSA.Create(2048);
|
||||||
|
var publicKeyPem = _testRsa.ExportSubjectPublicKeyInfoPem();
|
||||||
|
_rsaKeyService.GetPublicKeyPem().Returns(publicKeyPem);
|
||||||
|
_rsaKeyService.Decrypt(Arg.Any<byte[]>())
|
||||||
|
.Returns(callInfo =>
|
||||||
|
{
|
||||||
|
var ciphertext = callInfo.Arg<byte[]>();
|
||||||
|
return _testRsa.Decrypt(ciphertext, RSAEncryptionPadding.OaepSHA256);
|
||||||
|
});
|
||||||
|
|
||||||
|
_controller = new AuthController(_authService, _rsaKeyService, _logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EncryptedLoginRequest EncryptLoginModel(LoginModel model)
|
||||||
|
{
|
||||||
|
var json = JsonSerializer.Serialize(model);
|
||||||
|
var plaintext = Encoding.UTF8.GetBytes(json);
|
||||||
|
var ciphertext = _testRsa.Encrypt(plaintext, RSAEncryptionPadding.OaepSHA256);
|
||||||
|
return new EncryptedLoginRequest(Convert.ToBase64String(ciphertext));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Login_WithValidCredentials_ReturnsUserInfo()
|
public void GetPublicKey_ReturnsPublicKeyPem()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var result = _controller.GetPublicKey();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Result.ShouldBeOfType<OkObjectResult>();
|
||||||
|
var okResult = (OkObjectResult)result.Result!;
|
||||||
|
var response = okResult.Value.ShouldBeOfType<PublicKeyResponse>();
|
||||||
|
response.PublicKeyPem.ShouldStartWith("-----BEGIN PUBLIC KEY-----");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Login_WithValidCredentials_ReturnsLoginResultWithUser()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var request = new LoginRequest { Username = "testuser", Password = "password123" };
|
var loginModel = new LoginModel { Username = "testuser", Password = "password123" };
|
||||||
|
var request = EncryptLoginModel(loginModel);
|
||||||
var user = new UserInfo
|
var user = new UserInfo
|
||||||
{
|
{
|
||||||
Dn = "CN=testuser,DC=example,DC=com",
|
Dn = "CN=testuser,DC=example,DC=com",
|
||||||
@@ -42,7 +83,6 @@ public class AuthControllerTests
|
|||||||
_authService.AuthenticateAsync("testuser", "password123", Arg.Any<CancellationToken>())
|
_authService.AuthenticateAsync("testuser", "password123", Arg.Any<CancellationToken>())
|
||||||
.Returns(new AuthResult(true, user, null));
|
.Returns(new AuthResult(true, user, null));
|
||||||
|
|
||||||
// Setup HttpContext with mock authentication
|
|
||||||
var httpContext = CreateMockHttpContext();
|
var httpContext = CreateMockHttpContext();
|
||||||
_controller.ControllerContext = new ControllerContext { HttpContext = httpContext };
|
_controller.ControllerContext = new ControllerContext { HttpContext = httpContext };
|
||||||
|
|
||||||
@@ -52,20 +92,21 @@ public class AuthControllerTests
|
|||||||
// Assert
|
// Assert
|
||||||
result.Result.ShouldBeOfType<OkObjectResult>();
|
result.Result.ShouldBeOfType<OkObjectResult>();
|
||||||
var okResult = (OkObjectResult)result.Result!;
|
var okResult = (OkObjectResult)result.Result!;
|
||||||
var returnedUser = okResult.Value.ShouldBeOfType<UserInfo>();
|
var loginResult = okResult.Value.ShouldBeOfType<LoginResultModel>();
|
||||||
returnedUser.Username.ShouldBe("testuser");
|
loginResult.Success.ShouldBeTrue();
|
||||||
returnedUser.FirstName.ShouldBe("Test");
|
loginResult.User.ShouldNotBeNull();
|
||||||
|
loginResult.User.Username.ShouldBe("testuser");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Login_WithInvalidCredentials_Returns401()
|
public async Task Login_WithInvalidCredentials_Returns401WithError()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var request = new LoginRequest { Username = "testuser", Password = "wrongpassword" };
|
var loginModel = new LoginModel { Username = "testuser", Password = "wrongpassword" };
|
||||||
|
var request = EncryptLoginModel(loginModel);
|
||||||
_authService.AuthenticateAsync("testuser", "wrongpassword", Arg.Any<CancellationToken>())
|
_authService.AuthenticateAsync("testuser", "wrongpassword", Arg.Any<CancellationToken>())
|
||||||
.Returns(new AuthResult(false, null, "Incorrect username or password"));
|
.Returns(new AuthResult(false, null, "Incorrect username or password"));
|
||||||
|
|
||||||
// Setup HttpContext
|
|
||||||
var httpContext = CreateMockHttpContext();
|
var httpContext = CreateMockHttpContext();
|
||||||
_controller.ControllerContext = new ControllerContext { HttpContext = httpContext };
|
_controller.ControllerContext = new ControllerContext { HttpContext = httpContext };
|
||||||
|
|
||||||
@@ -74,6 +115,30 @@ public class AuthControllerTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Result.ShouldBeOfType<UnauthorizedObjectResult>();
|
result.Result.ShouldBeOfType<UnauthorizedObjectResult>();
|
||||||
|
var unauthorizedResult = (UnauthorizedObjectResult)result.Result!;
|
||||||
|
var loginResult = unauthorizedResult.Value.ShouldBeOfType<LoginResultModel>();
|
||||||
|
loginResult.Success.ShouldBeFalse();
|
||||||
|
loginResult.ErrorMessage.ShouldBe("Incorrect username or password");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Login_WithInvalidEncryptedData_ReturnsBadRequest()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new EncryptedLoginRequest("not-valid-base64!!!");
|
||||||
|
|
||||||
|
var httpContext = CreateMockHttpContext();
|
||||||
|
_controller.ControllerContext = new ControllerContext { HttpContext = httpContext };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _controller.Login(request, CancellationToken.None);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Result.ShouldBeOfType<BadRequestObjectResult>();
|
||||||
|
var badRequestResult = (BadRequestObjectResult)result.Result!;
|
||||||
|
var loginResult = badRequestResult.Value.ShouldBeOfType<LoginResultModel>();
|
||||||
|
loginResult.Success.ShouldBeFalse();
|
||||||
|
loginResult.ErrorMessage.ShouldBe("Invalid encrypted payload");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -119,42 +184,6 @@ public class AuthControllerTests
|
|||||||
user.Username.ShouldBe("testuser");
|
user.Username.ShouldBe("testuser");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCurrentUser_ExtractsAllClaimsCorrectly()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var claims = new List<Claim>
|
|
||||||
{
|
|
||||||
new(ClaimTypes.Name, "jsmith"),
|
|
||||||
new(ClaimTypes.GivenName, "John"),
|
|
||||||
new(ClaimTypes.Surname, "Smith"),
|
|
||||||
new(ClaimTypes.Email, "jsmith@example.com"),
|
|
||||||
new("title", "Senior Engineer"),
|
|
||||||
new("dn", "CN=jsmith,OU=Users,DC=example,DC=com")
|
|
||||||
};
|
|
||||||
var identity = new ClaimsIdentity(claims, "Test");
|
|
||||||
var principal = new ClaimsPrincipal(identity);
|
|
||||||
|
|
||||||
var httpContext = new DefaultHttpContext { User = principal };
|
|
||||||
_controller.ControllerContext = new ControllerContext { HttpContext = httpContext };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = _controller.GetCurrentUser();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Result.ShouldBeOfType<OkObjectResult>();
|
|
||||||
var okResult = (OkObjectResult)result.Result!;
|
|
||||||
var user = okResult.Value.ShouldBeOfType<UserInfo>();
|
|
||||||
|
|
||||||
user.Username.ShouldBe("jsmith");
|
|
||||||
user.FirstName.ShouldBe("John");
|
|
||||||
user.LastName.ShouldBe("Smith");
|
|
||||||
user.EmailAddress.ShouldBe("jsmith@example.com");
|
|
||||||
user.Title.ShouldBe("Senior Engineer");
|
|
||||||
user.Dn.ShouldBe("CN=jsmith,OU=Users,DC=example,DC=com");
|
|
||||||
user.DisplayName.ShouldBe("John Smith");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HttpContext CreateMockHttpContext()
|
private static HttpContext CreateMockHttpContext()
|
||||||
{
|
{
|
||||||
var authServiceMock = Substitute.For<IAuthenticationService>();
|
var authServiceMock = Substitute.For<IAuthenticationService>();
|
||||||
|
|||||||
Reference in New Issue
Block a user