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,6 +1,8 @@
|
||||
using API.Database;
|
||||
using API.Models;
|
||||
using API.Security;
|
||||
using API.Services;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.HttpLogging;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -50,6 +52,13 @@ builder.Services
|
||||
})
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
builder.Services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy(PolicyNames.AdminOnly, policy =>
|
||||
{
|
||||
policy.RequireRole(RoleNames.Admin);
|
||||
});
|
||||
});
|
||||
|
||||
builder.Services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
@@ -59,6 +68,31 @@ builder.Services.ConfigureApplicationCookie(options =>
|
||||
options.AccessDeniedPath = "/auth/forbidden";
|
||||
options.SlidingExpiration = true;
|
||||
options.Cookie.HttpOnly = true;
|
||||
options.Events = new CookieAuthenticationEvents
|
||||
{
|
||||
OnRedirectToLogin = context =>
|
||||
{
|
||||
if (IsApiRequest(context.Request))
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
context.Response.Redirect(context.RedirectUri);
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
OnRedirectToAccessDenied = context =>
|
||||
{
|
||||
if (IsApiRequest(context.Request))
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
context.Response.Redirect(context.RedirectUri);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
builder.Services.AddScoped<IdentitySeedService>();
|
||||
@@ -112,3 +146,9 @@ app.MapFallback(async context =>
|
||||
});
|
||||
|
||||
app.Run();
|
||||
|
||||
static bool IsApiRequest(HttpRequest request)
|
||||
{
|
||||
return request.Path.StartsWithSegments("/api")
|
||||
|| request.Path.StartsWithSegments("/auth");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user