Ajoute une table de gestion des utilisateurs

This commit is contained in:
2026-04-15 21:53:24 +02:00
parent 655637072c
commit 0e6115e423
8 changed files with 865 additions and 189 deletions

View File

@@ -147,6 +147,63 @@ adminGroup.MapGet("/users", async Task<IResult> (
return TypedResults.Ok(users);
});
adminGroup.MapPost("/users", async Task<IResult> (
AdminCreateUserRequest request,
KeycloakAuthService keycloak,
MySqlUserProfileStore profileStore,
CancellationToken cancellationToken) =>
{
try
{
var normalized = NormalizeAdminCreate(request);
var fallbackDisplayName = BuildIdentityDisplayNameFromParts(normalized.FirstName, normalized.LastName, normalized.Username);
var siteProfileRequest = new UpdateUserProfileRequest
{
DisplayName = request.DisplayName,
Club = request.Club,
City = request.City,
PreferredFormat = request.PreferredFormat,
FavoriteCube = request.FavoriteCube,
Bio = request.Bio,
};
profileStore.ValidateAdminUpdate(fallbackDisplayName, siteProfileRequest);
var createdIdentity = await keycloak.CreateAdminUserAsync(
new AdminIdentityUserCreateRequest(
normalized.Username,
normalized.Email,
normalized.Password,
normalized.FirstName,
normalized.LastName,
normalized.IsEnabled,
normalized.IsEmailVerified),
cancellationToken);
var createdProfile = await profileStore.AdminUpsertAsync(
createdIdentity.Subject,
createdIdentity.Username,
createdIdentity.Email,
BuildIdentityDisplayName(createdIdentity),
siteProfileRequest,
cancellationToken);
return TypedResults.Created($"/api/admin/users/{Uri.EscapeDataString(createdIdentity.Subject)}", MapAdminDetail(createdIdentity, createdProfile));
}
catch (AdminUserValidationException exception)
{
return TypedResults.BadRequest(new ApiErrorResponse(exception.Message));
}
catch (UserProfileValidationException exception)
{
return TypedResults.BadRequest(new ApiErrorResponse(exception.Message));
}
catch (KeycloakAuthException exception)
{
return TypedResults.Json(new ApiErrorResponse(exception.Message), statusCode: exception.StatusCode);
}
});
adminGroup.MapGet("/users/{subject}", async Task<IResult> (
string subject,
KeycloakAuthService keycloak,
@@ -167,6 +224,24 @@ adminGroup.MapGet("/users/{subject}", async Task<IResult> (
}
});
adminGroup.MapDelete("/users/{subject}", async Task<IResult> (
string subject,
KeycloakAuthService keycloak,
MySqlUserProfileStore profileStore,
CancellationToken cancellationToken) =>
{
try
{
await keycloak.DeleteAdminUserAsync(subject, cancellationToken);
await profileStore.DeleteAsync(subject, cancellationToken);
return TypedResults.NoContent();
}
catch (KeycloakAuthException exception)
{
return TypedResults.Json(new ApiErrorResponse(exception.Message), statusCode: exception.StatusCode);
}
});
adminGroup.MapPut("/users/{subject}", async Task<IResult> (
string subject,
AdminUpdateUserRequest request,
@@ -360,6 +435,24 @@ static NormalizedAdminUserUpdate NormalizeAdminUpdate(AdminUpdateUserRequest req
request.IsEmailVerified);
}
static NormalizedAdminCreateUser NormalizeAdminCreate(AdminCreateUserRequest request)
{
var username = NormalizeRequiredValue(request.Username, "nom d'utilisateur", 120);
var email = NormalizeEmail(request.Email);
var password = NormalizePassword(request.Password, request.ConfirmPassword);
var firstName = NormalizeOptionalValue(request.FirstName, "prenom", 120);
var lastName = NormalizeOptionalValue(request.LastName, "nom", 120);
return new NormalizedAdminCreateUser(
username,
email,
password,
firstName,
lastName,
request.IsEnabled,
request.IsEmailVerified);
}
static string BuildIdentityDisplayName(AdminIdentityUser identity)
=> BuildIdentityDisplayNameFromParts(identity.FirstName, identity.LastName, identity.Username);
@@ -396,6 +489,26 @@ static string? NormalizeEmail(string? value)
}
}
static string NormalizePassword(string? password, string? confirmPassword)
{
if (string.IsNullOrWhiteSpace(password))
{
throw new AdminUserValidationException("Le mot de passe est obligatoire.");
}
if (!string.Equals(password, confirmPassword, StringComparison.Ordinal))
{
throw new AdminUserValidationException("Les mots de passe ne correspondent pas.");
}
if (password.Length < 8)
{
throw new AdminUserValidationException("Le mot de passe doit contenir au moins 8 caracteres.");
}
return password;
}
static string? NormalizeOptionalValue(string? value, string fieldName, int maxLength)
{
var trimmed = value?.Trim();
@@ -468,3 +581,12 @@ sealed record NormalizedAdminUserUpdate(
string? LastName,
bool IsEnabled,
bool IsEmailVerified);
sealed record NormalizedAdminCreateUser(
string Username,
string? Email,
string Password,
string? FirstName,
string? LastName,
bool IsEnabled,
bool IsEmailVerified);