b2984fcf1a
Switch user admin handling from an AppUser boolean to ASP.NET Identity roles. Removed AppUser.IsAdmin and related configuration/model entries; added migration ReplaceIsAdminWithRoles to copy Users.IsAdmin=true into a persistent admin role and drop the IsAdmin column. CurrentUserResponse now exposes roles (string[]), AuthController returns ordered roles from UserManager, and IdentitySeedService now ensures the admin role exists and assigns/creates an initial admin user in that role. Program.cs registers an Admin-only policy (PolicyNames/RoleNames), adjusts cookie auth events to return 401/403 for API requests, and wires up authorization. Frontend updated to use roles: authSession normalizes roles, adds hasRole and ROLE_ADMIN, router and layout support meta.requiredRoles, and new Forbidden and AdminUsers pages/route are added. codexInfo.md updated to reflect the migration to role-based auth.
88 lines
3.5 KiB
C#
88 lines
3.5 KiB
C#
using API.Models;
|
|
using API.Security;
|
|
using Microsoft.AspNetCore.Identity;
|
|
|
|
namespace API.Services
|
|
{
|
|
public class IdentitySeedService(
|
|
UserManager<AppUser> userManager,
|
|
RoleManager<IdentityRole<Guid>> roleManager,
|
|
IConfiguration configuration,
|
|
ILogger<IdentitySeedService> logger)
|
|
{
|
|
public async Task SeedAsync()
|
|
{
|
|
await EnsureRoleExistsAsync(RoleNames.Admin);
|
|
|
|
var adminUsers = await userManager.GetUsersInRoleAsync(RoleNames.Admin);
|
|
if (adminUsers.Count > 0)
|
|
{
|
|
logger.LogDebug("Admin-Seed übersprungen: Es existiert bereits ein Admin über Rollen.");
|
|
return;
|
|
}
|
|
|
|
var adminUserName = configuration["SeedAdmin:UserName"] ?? "admin";
|
|
var adminPassword = configuration["SeedAdmin:Password"] ?? "HoardPassword";
|
|
var adminEmail = configuration["SeedAdmin:Email"];
|
|
|
|
var admin = await userManager.FindByNameAsync(adminUserName);
|
|
if (admin is null)
|
|
{
|
|
admin = new AppUser
|
|
{
|
|
UserName = adminUserName,
|
|
Email = string.IsNullOrWhiteSpace(adminEmail) ? null : adminEmail,
|
|
IsActive = true,
|
|
MustChangePassword = true,
|
|
CreatedAt = DateTimeOffset.UtcNow,
|
|
UpdatedAt = DateTimeOffset.UtcNow
|
|
};
|
|
|
|
var createResult = await userManager.CreateAsync(admin, adminPassword);
|
|
if (!createResult.Succeeded)
|
|
{
|
|
var createErrors = string.Join(", ", createResult.Errors.Select(x => x.Description));
|
|
logger.LogError("Admin-Seed fehlgeschlagen: {Errors}", createErrors);
|
|
throw new InvalidOperationException($"Admin-Seed fehlgeschlagen: {createErrors}");
|
|
}
|
|
}
|
|
|
|
if (!await userManager.IsInRoleAsync(admin, RoleNames.Admin))
|
|
{
|
|
var roleResult = await userManager.AddToRoleAsync(admin, RoleNames.Admin);
|
|
if (!roleResult.Succeeded)
|
|
{
|
|
var roleErrors = string.Join(", ", roleResult.Errors.Select(x => x.Description));
|
|
logger.LogError("Admin-Rollenzuweisung fehlgeschlagen: {Errors}", roleErrors);
|
|
throw new InvalidOperationException($"Admin-Rollenzuweisung fehlgeschlagen: {roleErrors}");
|
|
}
|
|
}
|
|
|
|
logger.LogInformation(
|
|
"Admin-Account wurde geseedet bzw. als Rolle zugewiesen (UserName: {UserName}, Email: {Email}).",
|
|
admin.UserName,
|
|
admin.Email ?? "(keine)");
|
|
}
|
|
|
|
private async Task EnsureRoleExistsAsync(string roleName)
|
|
{
|
|
if (await roleManager.RoleExistsAsync(roleName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var createRoleResult = await roleManager.CreateAsync(new IdentityRole<Guid>
|
|
{
|
|
Name = roleName
|
|
});
|
|
|
|
if (!createRoleResult.Succeeded)
|
|
{
|
|
var roleErrors = string.Join(", ", createRoleResult.Errors.Select(x => x.Description));
|
|
logger.LogError("Rolle {RoleName} konnte nicht erstellt werden: {Errors}", roleName, roleErrors);
|
|
throw new InvalidOperationException($"Rolle {roleName} konnte nicht erstellt werden: {roleErrors}");
|
|
}
|
|
}
|
|
}
|
|
}
|