Add admin user creation & must-change flag

Add server and UI support for creating admin users and forcing password change. API: introduce CreateUserRequest contract and add CreateNewAppUser endpoint in AppUserController; extend ChangeUserRequest with MustChangePassword and handle role assignment and detailed error responses (409/422/400). Frontend: new CreateUserDialog component, integrate it into AdminUsers list, and add createAdminUser service with CreateAdminUserError and payload handling; include mustChangePassword in update payloads and EditUserDialog. UI polish: enhanced app banner enter/leave animations in Layout.vue and add auto-dismiss timers/cleanup to appBanners store to limit and auto-remove banners.
This commit is contained in:
Jonas
2026-05-03 15:56:28 +02:00
parent 1d00fb3a4b
commit 178bc8731e
9 changed files with 720 additions and 8 deletions
+1
View File
@@ -4,5 +4,6 @@
{
public string? UserName { get; set; }
public bool? IsActive { get; set; }
public bool? MustChangePassword { get; set; }
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace API.Contracts.Auth
{
public class CreateUserRequest
{
public required string UserName { get; set; }
public required string StartPassword { get; set; }
public bool IsAdmin { get; set; } = false;
public bool IsActive { get; set; } = true;
}
}
+65
View File
@@ -92,7 +92,72 @@ namespace API.Controllers.Auth
}
}
if (changeDto.MustChangePassword != null)
{
user.MustChangePassword = changeDto.MustChangePassword.Value;
await userManager.UpdateAsync(user);
}
return Ok(await user.ToCurrentUserResponseAsync(userManager));
}
[HttpPost]
public async Task<IActionResult> CreateNewAppUser([FromBody] CreateUserRequest createDto)
{
var newUser = new AppUser
{
UserName = createDto.UserName.Trim(),
MustChangePassword = true,
IsActive = createDto.IsActive,
};
var result = await userManager.CreateAsync(newUser, createDto.StartPassword);
if (!result.Succeeded)
{
if (result.Errors.Any(e => e.Code == nameof(IdentityErrorDescriber.DuplicateUserName)))
{
return Conflict(new { message = "Benutzername ist bereits vergeben." });
}
var passwordErrors = result.Errors
.Where(e => e.Code.StartsWith("Password"))
.Select(e => e.Description)
.ToList();
if (passwordErrors.Any())
{
return UnprocessableEntity(new
{
message = "Passwort erfüllt nicht die Sicherheitsanforderungen.",
errors = passwordErrors
});
}
return BadRequest(new
{
message = "Benutzer konnte nicht erstellt werden.",
errors = result.Errors.Select(e => e.Description)
});
}
if (createDto.IsAdmin)
{
var roleResult = await userManager.AddToRoleAsync(newUser, RoleNames.Admin);
if (!roleResult.Succeeded)
{
return BadRequest(new
{
message = "Benutzer wurde erstellt, aber Rolle konnte nicht zugewiesen werden.",
errors = roleResult.Errors.Select(e => e.Description)
});
}
}
return CreatedAtAction(nameof(GetAppUserById), new { id = newUser.Id },
await newUser.ToCurrentUserResponseAsync(userManager));
}
}
}