diff --git a/Azaion.Common/BusinessException.cs b/Azaion.Common/BusinessException.cs index 43033d6..1b7d0de 100644 --- a/Azaion.Common/BusinessException.cs +++ b/Azaion.Common/BusinessException.cs @@ -36,6 +36,9 @@ public enum ExceptionEnum WrongEmail = 37, + [Description("User account is disabled.")] + UserDisabled = 38, + [Description("Hardware mismatch! You are not authorized to access this resource from this hardware.")] HardwareIdMismatch = 40, diff --git a/Azaion.Services/UserService.cs b/Azaion.Services/UserService.cs index baf0503..59f5f81 100644 --- a/Azaion.Services/UserService.cs +++ b/Azaion.Services/UserService.cs @@ -63,6 +63,9 @@ public class UserService(IDbFactory dbFactory, ICache cache) : IUserService if (request.Password.ToHash() != user.PasswordHash) throw new BusinessException(ExceptionEnum.WrongPassword); + if (!user.IsEnabled) + throw new BusinessException(ExceptionEnum.UserDisabled); + return user; }); diff --git a/e2e/Azaion.E2E/Tests/SecurityTests.cs b/e2e/Azaion.E2E/Tests/SecurityTests.cs index 9775d36..02e4e06 100644 --- a/e2e/Azaion.E2E/Tests/SecurityTests.cs +++ b/e2e/Azaion.E2E/Tests/SecurityTests.cs @@ -19,6 +19,13 @@ public sealed class SecurityTests PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + private static readonly JsonSerializerOptions ResponseJsonOptions = new() + { + PropertyNameCaseInsensitive = true + }; + + private sealed record ErrorResponse(int ErrorCode, string Message); + private readonly TestFixture _fixture; public SecurityTests(TestFixture fixture) => _fixture = fixture; @@ -195,7 +202,7 @@ public sealed class SecurityTests } } - [Fact(Skip = "API bug: login does not check IsEnabled — disabled users can still log in")] + [Fact] public async Task Disabled_user_cannot_log_in() { // Arrange @@ -218,7 +225,10 @@ public sealed class SecurityTests using var login = await client.PostAsync("/login", new { email, password }); // Assert - login.StatusCode.Should().BeOneOf(HttpStatusCode.Forbidden, HttpStatusCode.Conflict); + login.StatusCode.Should().Be(HttpStatusCode.Conflict); + var err = await login.Content.ReadFromJsonAsync(ResponseJsonOptions); + err.Should().NotBeNull(); + err!.ErrorCode.Should().Be(38); } finally {