< Summary

Information
Class: Anichron.API.Endpoints.AuthResponseMapper
Assembly: Anichron.API
File(s): /home/runner/work/anichron/anichron/src/Anichron.API/Endpoints/AuthResponseMapper.cs
Tag: 228_36821334185
Line coverage
98%
Covered lines: 153
Uncovered lines: 3
Coverable lines: 156
Total lines: 234
Line coverage: 98%
Branch coverage
96%
Covered branches: 92
Total branches: 95
Branch coverage: 96.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/anichron/anichron/src/Anichron.API/Endpoints/AuthResponseMapper.cs

#LineLine coverage
 1using Anichron.API.Infrastructure;
 2using Anichron.API.Services;
 3using Anichron.API.Settings;
 4using Anichron.Core.Domain;
 5using System.Diagnostics;
 6using static System.Globalization.CultureInfo;
 7
 8namespace Anichron.API.Endpoints;
 9
 10public interface IAuthResponseMapper
 11{
 12    IResult GetRegistrationResult(AuthResult<AuthTokens> result, HttpContext http, PasswordPolicy passwordPolicy, Userna
 13    IResult GetLoginResult(AuthResult<AuthTokens> result, HttpContext http, bool setCookie);
 14    IResult GetRefreshResult(AuthResult<AuthTokens> result, HttpContext http, bool setCookie);
 15    IResult GetChangePasswordResult(AuthResult result, PasswordPolicy passwordPolicy);
 16    IResult GetAdminCreateUserResult(AuthResult<AdminCreatedUser> result);
 17    IResult GetAdminResetPasswordResult(AdminUserPasswordReset? result);
 18    IResult GetAdminGetUsersResult(List<User> users);
 19    IResult GetAdminGetUserResult(User? user);
 20    IResult GetAdminPatchUserResult(AuthResult<User> result);
 21    IResult GetAdminDeleteUserResult(AuthResult result);
 22    IResult GetAdminGetStorageConfigsResult(AuthResult<List<UserStorageConfig>> result);
 23    IResult GetAdminCreateStorageConfigResult(AuthResult<UserStorageConfig> result);
 24    IResult GetAdminDeleteStorageConfigResult(AuthResult result);
 25    void ClearRefreshCookie(HttpContext http);
 26}
 27
 6628public sealed class AuthResponseMapper(AuthCookieSettings cookieSettings, IClock clock) : IAuthResponseMapper
 29{
 30    public IResult GetRegistrationResult(AuthResult<AuthTokens> result, HttpContext http, PasswordPolicy passwordPolicy,
 1131    {
 1132        if (!result.IsSuccess)
 1033        {
 1034            return result.Error switch
 1035            {
 136                AuthError.UsernameTaken => Results.Conflict(new { error = AuthMessages.UsernameTaken }),
 137                AuthError.EmailTaken => Results.Conflict(new { error = AuthMessages.EmailTaken }),
 138                AuthError.InvalidUsername => Results.UnprocessableEntity(new { error = usernamePolicy.InvalidFormatMessa
 139                AuthError.InvalidEmail => Results.UnprocessableEntity(new { error = AuthMessages.InvalidEmail }),
 140                AuthError.PasswordTooShort => Results.UnprocessableEntity(new { error = passwordPolicy.TooShortMessage }
 141                AuthError.PasswordTooLong => Results.UnprocessableEntity(new { error = passwordPolicy.TooLongMessage }),
 142                AuthError.PasswordPwned => Results.UnprocessableEntity(new { error = AuthMessages.PasswordPwned }),
 143                AuthError.InviteTokenInvalid => Results.UnprocessableEntity(new { error = AuthMessages.InviteTokenInvali
 244                _ => throw new UnreachableException($"Unexpected AuthError in Register: {result.Error}"),
 1045            };
 46        }
 47
 148        SetRefreshCookie(http, result.Value!.RefreshToken);
 149        return Results.Ok(new { result.Value.AccessToken });
 950    }
 51
 52    public IResult GetLoginResult(AuthResult<AuthTokens> result, HttpContext http, bool setCookie)
 853    {
 854        if (!result.IsSuccess)
 655        {
 656            return result.Error switch
 657            {
 258                AuthError.AccountTemporarilyLocked => LockedResult(http, result.RetryAfterSeconds!.Value),
 159                AuthError.AccountDisabled => Results.Json(
 160                    data: new { error = AuthMessages.AccountDisabled },
 161                    statusCode: StatusCodes.Status401Unauthorized),
 162                AuthError.InvalidCredentials => Results.Json(
 163                    data: new { error = AuthMessages.InvalidCredentials },
 164                    statusCode: StatusCodes.Status401Unauthorized),
 265                _ => throw new UnreachableException($"Unexpected AuthError in Login: {result.Error}"),
 666            };
 67        }
 68
 269        return BuildTokenResponse(result.Value!, http, setCookie);
 670    }
 71
 72    public IResult GetRefreshResult(AuthResult<AuthTokens> result, HttpContext http, bool setCookie)
 773    {
 774        if (!result.IsSuccess)
 575        {
 576            return result.Error switch
 577            {
 178                AuthError.TokenInvalid => Results.Json(
 179                    data: new { error = AuthMessages.RefreshTokenInvalid },
 180                    statusCode: StatusCodes.Status401Unauthorized),
 181                AuthError.AccountDisabled => Results.Json(
 182                    data: new { error = AuthMessages.AccountDisabled },
 183                    statusCode: StatusCodes.Status401Unauthorized),
 184                AuthError.AccountTemporarilyLocked => LockedResult(http, result.RetryAfterSeconds!.Value),
 285                _ => throw new UnreachableException($"Unexpected AuthError in Refresh: {result.Error}"),
 586            };
 87        }
 88
 289        return BuildTokenResponse(result.Value!, http, setCookie);
 590    }
 91
 92    public IResult GetChangePasswordResult(AuthResult result, PasswordPolicy passwordPolicy)
 793    {
 794        if (result.IsSuccess)
 195            return Results.NoContent();
 96
 697        return result.Error switch
 698        {
 199            AuthError.InvalidCredentials => Results.Json(
 1100                data: new { error = AuthMessages.InvalidCredentials },
 1101                statusCode: StatusCodes.Status400BadRequest),
 1102            AuthError.PasswordTooShort => Results.Json(
 1103                data: new { error = passwordPolicy.TooShortMessage },
 1104                statusCode: StatusCodes.Status422UnprocessableEntity),
 1105            AuthError.PasswordTooLong => Results.Json(
 1106                data: new { error = passwordPolicy.TooLongMessage },
 1107                statusCode: StatusCodes.Status422UnprocessableEntity),
 1108            AuthError.PasswordPwned => Results.Json(
 1109                data: new { error = AuthMessages.PasswordPwned },
 1110                statusCode: StatusCodes.Status422UnprocessableEntity),
 2111            _ => throw new UnreachableException($"Unexpected AuthError in ChangePassword: {result.Error}"),
 6112        };
 5113    }
 114
 115    public IResult GetAdminCreateUserResult(AuthResult<AdminCreatedUser> result)
 5116    {
 5117        if (!result.IsSuccess)
 4118        {
 4119            return result.Error switch
 4120            {
 0121                AuthError.InvalidUsername => Results.UnprocessableEntity(new { error = AuthMessages.InvalidUsername }),
 0122                AuthError.InvalidEmail => Results.UnprocessableEntity(new { error = AuthMessages.InvalidEmail }),
 1123                AuthError.UsernameTaken => Results.Conflict(new { error = AuthMessages.UsernameTaken }),
 1124                AuthError.EmailTaken => Results.Conflict(new { error = AuthMessages.EmailTaken }),
 2125                _ => throw new UnreachableException($"Unexpected AuthError in AdminCreateUser: {result.Error}"),
 4126            };
 127        }
 128
 1129        var created = result.Value!;
 1130        var location = ApiPaths.Users.UserLocation(created.Id);
 1131        return Results.Created(location, new AdminCreatedUserResponse(
 1132            created.Id, created.Username, created.Email, created.TemporaryPassword));
 3133    }
 134
 135    public IResult GetAdminResetPasswordResult(AdminUserPasswordReset? result)
 2136        => result is null
 2137            ? Results.NotFound()
 2138            : Results.Ok(new AdminPasswordResetResponse(result.TemporaryPassword));
 139
 140    public IResult GetAdminGetUsersResult(List<User> users)
 2141        => Results.Ok(users.ConvertAll(ToAdminUserResponse));
 142
 143    public IResult GetAdminGetUserResult(User? user)
 2144        => user is null ? Results.NotFound() : Results.Ok(ToAdminUserResponse(user));
 145
 146    public IResult GetAdminPatchUserResult(AuthResult<User> result)
 5147        => result.Error switch
 5148        {
 1149            AuthError.UserNotFound => Results.NotFound(),
 1150            AuthError.CannotModifySelf => Results.Json(new { error = AuthMessages.CannotModifySelf }, statusCode: Status
 1151            null => Results.Ok(ToAdminUserResponse(result.Value!)),
 2152            _ => throw new UnreachableException($"Unexpected AuthError in AdminPatchUser: {result.Error}"),
 5153        };
 154
 155    public IResult GetAdminDeleteUserResult(AuthResult result)
 5156        => result.Error switch
 5157        {
 1158            AuthError.UserNotFound => Results.NotFound(),
 1159            AuthError.CannotModifySelf => Results.Json(new { error = AuthMessages.CannotModifySelf }, statusCode: Status
 1160            null => Results.NoContent(),
 2161            _ => throw new UnreachableException($"Unexpected AuthError in AdminDeleteUser: {result.Error}"),
 5162        };
 163
 164    public IResult GetAdminGetStorageConfigsResult(AuthResult<List<UserStorageConfig>> result)
 3165        => result.Error switch
 3166        {
 1167            AuthError.UserNotFound => Results.NotFound(),
 1168            null => Results.Ok(result.Value!.ConvertAll(ToAdminStorageConfigResponse)),
 1169            _ => throw new UnreachableException($"Unexpected AuthError in AdminGetStorageConfigs: {result.Error}"),
 3170        };
 171
 172    public IResult GetAdminCreateStorageConfigResult(AuthResult<UserStorageConfig> result)
 4173    {
 4174        if (!result.IsSuccess)
 3175        {
 3176            return result.Error switch
 3177            {
 1178                AuthError.UserNotFound => Results.NotFound(),
 0179                AuthError.PathInvalid => Results.UnprocessableEntity(new { error = AuthMessages.PathInvalid }),
 1180                AuthError.PathAlreadyAssigned => Results.Conflict(new { error = AuthMessages.PathAlreadyAssigned }),
 1181                _ => throw new UnreachableException($"Unexpected AuthError in AdminCreateStorageConfig: {result.Error}")
 3182            };
 183        }
 184
 1185        var config = result.Value!;
 1186        var location = ApiPaths.Users.StorageConfigLocation(config.UserId, config.Id);
 1187        return Results.Created(location, ToAdminStorageConfigResponse(config));
 3188    }
 189
 190    public IResult GetAdminDeleteStorageConfigResult(AuthResult result)
 4191        => result.Error switch
 4192        {
 1193            AuthError.StorageConfigNotFound => Results.NotFound(),
 1194            null => Results.NoContent(),
 2195            _ => throw new UnreachableException($"Unexpected AuthError in AdminDeleteStorageConfig: {result.Error}"),
 4196        };
 197
 198    public void ClearRefreshCookie(HttpContext http)
 1199        => http.Response.Cookies.Delete(AuthMessages.RefreshTokenCookieName);
 200
 201    private static AdminUserResponse ToAdminUserResponse(User user)
 3202        => new(user.Id, user.Username, user.Email, user.IsAdmin, user.IsDisabled, user.StorageConfigs.Count);
 203
 204    private static AdminStorageConfigResponse ToAdminStorageConfigResponse(UserStorageConfig config)
 2205        => new(config.Id, config.UserId, config.RootPath, config.IsActive);
 206
 207    private IResult BuildTokenResponse(AuthTokens tokens, HttpContext http, bool setCookie)
 4208    {
 4209        if (!setCookie)
 2210            return Results.Ok(new { tokens.AccessToken, tokens.RefreshToken });
 211
 2212        SetRefreshCookie(http, tokens.RefreshToken);
 2213        return Results.Ok(new { tokens.AccessToken });
 4214    }
 215
 216    private static IResult LockedResult(HttpContext http, int secondsRemaining)
 3217    {
 3218        http.Response.Headers.RetryAfter = secondsRemaining.ToString(InvariantCulture);
 3219        return Results.Json(
 3220            new { error = AuthMessages.AccountTemporarilyLocked(secondsRemaining) },
 3221            statusCode: StatusCodes.Status429TooManyRequests);
 3222    }
 223
 224    private void SetRefreshCookie(HttpContext http, string rawToken)
 3225    {
 3226        http.Response.Cookies.Append(AuthMessages.RefreshTokenCookieName, rawToken, new CookieOptions
 3227        {
 3228            HttpOnly = true,
 3229            Secure = true,
 3230            SameSite = cookieSettings.SameSite,
 3231            Expires = clock.GetCurrentInstant().ToDateTimeOffset().AddDays(cookieSettings.RefreshTokenDays),
 3232        });
 3233    }
 234}

Methods/Properties

.ctor(Anichron.API.Settings.AuthCookieSettings,NodaTime.IClock)
GetRegistrationResult(Anichron.API.Services.AuthResult`1<Anichron.API.Services.AuthTokens>,Microsoft.AspNetCore.Http.HttpContext,Anichron.API.Settings.PasswordPolicy,Anichron.API.Settings.UsernamePolicy)
GetLoginResult(Anichron.API.Services.AuthResult`1<Anichron.API.Services.AuthTokens>,Microsoft.AspNetCore.Http.HttpContext,System.Boolean)
GetRefreshResult(Anichron.API.Services.AuthResult`1<Anichron.API.Services.AuthTokens>,Microsoft.AspNetCore.Http.HttpContext,System.Boolean)
GetChangePasswordResult(Anichron.API.Services.AuthResult,Anichron.API.Settings.PasswordPolicy)
GetAdminCreateUserResult(Anichron.API.Services.AuthResult`1<Anichron.API.Services.AdminCreatedUser>)
GetAdminResetPasswordResult(Anichron.API.Services.AdminUserPasswordReset)
GetAdminGetUsersResult(System.Collections.Generic.List`1<Anichron.Core.Domain.User>)
GetAdminGetUserResult(Anichron.Core.Domain.User)
GetAdminPatchUserResult(Anichron.API.Services.AuthResult`1<Anichron.Core.Domain.User>)
GetAdminDeleteUserResult(Anichron.API.Services.AuthResult)
GetAdminGetStorageConfigsResult(Anichron.API.Services.AuthResult`1<System.Collections.Generic.List`1<Anichron.Core.Domain.UserStorageConfig>>)
GetAdminCreateStorageConfigResult(Anichron.API.Services.AuthResult`1<Anichron.Core.Domain.UserStorageConfig>)
GetAdminDeleteStorageConfigResult(Anichron.API.Services.AuthResult)
ClearRefreshCookie(Microsoft.AspNetCore.Http.HttpContext)
ToAdminUserResponse(Anichron.Core.Domain.User)
ToAdminStorageConfigResponse(Anichron.Core.Domain.UserStorageConfig)
BuildTokenResponse(Anichron.API.Services.AuthTokens,Microsoft.AspNetCore.Http.HttpContext,System.Boolean)
LockedResult(Microsoft.AspNetCore.Http.HttpContext,System.Int32)
SetRefreshCookie(Microsoft.AspNetCore.Http.HttpContext,System.String)