| | | 1 | | using Anichron.API.Infrastructure; |
| | | 2 | | using Anichron.API.Services; |
| | | 3 | | using Anichron.API.Settings; |
| | | 4 | | using Anichron.Core.Data.Repository; |
| | | 5 | | using Microsoft.Extensions.Options; |
| | | 6 | | using System.Security.Claims; |
| | | 7 | | |
| | | 8 | | namespace Anichron.API.Endpoints; |
| | | 9 | | |
| | | 10 | | public static class UserEndpoints |
| | | 11 | | { |
| | | 12 | | public static IEndpointRouteBuilder MapUserEndpoints(this IEndpointRouteBuilder app) |
| | 0 | 13 | | { |
| | 0 | 14 | | var group = app.MapGroup(ApiPaths.Users.Group).WithTags("Users"); |
| | 0 | 15 | | group.MapGet(ApiPaths.Users.Me, GetMeAsync) |
| | 0 | 16 | | .RequireAuthorization(); |
| | 0 | 17 | | group.MapPost(ApiPaths.Users.ChangePassword, ChangePasswordAsync) |
| | 0 | 18 | | .RequireAuthorization() |
| | 0 | 19 | | .RequireRateLimiting(AuthRateLimitPolicies.Sensitive); |
| | 0 | 20 | | return app; |
| | 0 | 21 | | } |
| | | 22 | | |
| | | 23 | | internal static async Task<IResult> GetMeAsync( |
| | | 24 | | ClaimsPrincipal user, |
| | | 25 | | IUserRepository users, |
| | | 26 | | CancellationToken ct) |
| | 4 | 27 | | { |
| | 4 | 28 | | if (!Guid.TryParse(user.FindFirstValue(ClaimTypes.NameIdentifier), out var userId)) |
| | 2 | 29 | | return Results.Unauthorized(); |
| | | 30 | | |
| | 2 | 31 | | var found = await users.FindByIdAsync(userId, ct); |
| | 2 | 32 | | return found is null |
| | 2 | 33 | | ? Results.NotFound() |
| | 2 | 34 | | : Results.Ok(new UserProfileResponse(found.Id, found.Username, found.Email, found.IsAdmin, found.MustChangeP |
| | 4 | 35 | | } |
| | | 36 | | |
| | | 37 | | internal static async Task<IResult> ChangePasswordAsync( |
| | | 38 | | ChangePasswordRequest req, |
| | | 39 | | ClaimsPrincipal user, |
| | | 40 | | IAuthService auth, |
| | | 41 | | IAuthResponseMapper mapper, |
| | | 42 | | IOptions<PasswordPolicy> passwordPolicyOptions, |
| | | 43 | | CancellationToken ct) |
| | 5 | 44 | | { |
| | 5 | 45 | | if (!Guid.TryParse(user.FindFirstValue(ClaimTypes.NameIdentifier), out var userId)) |
| | 2 | 46 | | return Results.Unauthorized(); |
| | | 47 | | |
| | 3 | 48 | | var result = await auth.ChangePasswordAsync(userId, req.CurrentPassword, req.NewPassword, ct); |
| | 3 | 49 | | return mapper.GetChangePasswordResult(result, passwordPolicyOptions.Value); |
| | 5 | 50 | | } |
| | | 51 | | } |
| | | 52 | | |
| | | 53 | | public sealed record ChangePasswordRequest(string CurrentPassword, string NewPassword); |
| | | 54 | | public sealed record UserProfileResponse(Guid Id, string Username, string Email, bool IsAdmin, bool MustChangePassword); |