Replace IsAdmin with role-based admin
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.
This commit is contained in:
@@ -1,21 +1,23 @@
|
||||
using API.Models;
|
||||
using API.Security;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Services
|
||||
{
|
||||
public class IdentitySeedService(
|
||||
UserManager<AppUser> userManager,
|
||||
RoleManager<IdentityRole<Guid>> roleManager,
|
||||
IConfiguration configuration,
|
||||
ILogger<IdentitySeedService> logger)
|
||||
{
|
||||
public async Task SeedAsync()
|
||||
{
|
||||
var hasAdmin = await userManager.Users.AnyAsync(x => x.IsAdmin);
|
||||
await EnsureRoleExistsAsync(RoleNames.Admin);
|
||||
|
||||
if (hasAdmin)
|
||||
var adminUsers = await userManager.GetUsersInRoleAsync(RoleNames.Admin);
|
||||
if (adminUsers.Count > 0)
|
||||
{
|
||||
logger.LogDebug("Admin-Seed übersprungen: Es existiert bereits ein Admin-Account.");
|
||||
logger.LogDebug("Admin-Seed übersprungen: Es existiert bereits ein Admin über Rollen.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -23,30 +25,63 @@ namespace API.Services
|
||||
var adminPassword = configuration["SeedAdmin:Password"] ?? "HoardPassword";
|
||||
var adminEmail = configuration["SeedAdmin:Email"];
|
||||
|
||||
var admin = new AppUser
|
||||
var admin = await userManager.FindByNameAsync(adminUserName);
|
||||
if (admin is null)
|
||||
{
|
||||
UserName = adminUserName,
|
||||
Email = string.IsNullOrWhiteSpace(adminEmail) ? null : adminEmail,
|
||||
IsAdmin = true,
|
||||
IsActive = true,
|
||||
MustChangePassword = true,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
};
|
||||
admin = new AppUser
|
||||
{
|
||||
UserName = adminUserName,
|
||||
Email = string.IsNullOrWhiteSpace(adminEmail) ? null : adminEmail,
|
||||
IsActive = true,
|
||||
MustChangePassword = true,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
var result = await userManager.CreateAsync(admin, adminPassword);
|
||||
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 (!result.Succeeded)
|
||||
if (!await userManager.IsInRoleAsync(admin, RoleNames.Admin))
|
||||
{
|
||||
var errors = string.Join(", ", result.Errors.Select(x => x.Description));
|
||||
logger.LogError("Admin-Seed fehlgeschlagen: {Errors}", errors);
|
||||
throw new InvalidOperationException($"Admin-Seed fehlgeschlagen: {errors}");
|
||||
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 (UserName: {UserName}, Email: {Email}).",
|
||||
"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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user