Add role control
This commit is contained in:
@@ -23,6 +23,7 @@ public class AppDbContext : IdentityDbContext<AppUser, AppRole, string>
|
||||
public DbSet<Check> Checks => Set<Check>();
|
||||
public DbSet<CheckLine> CheckLines => Set<CheckLine>();
|
||||
public DbSet<MealAttendance> MealAttendances => Set<MealAttendance>();
|
||||
public DbSet<RolePermission> RolePermissions => Set<RolePermission>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
@@ -60,6 +61,18 @@ public class AppDbContext : IdentityDbContext<AppUser, AppRole, string>
|
||||
entity.Property(e => e.Description).HasMaxLength(500);
|
||||
});
|
||||
|
||||
// ── RolePermission (configurable RBAC matrix) ───────────────────────
|
||||
builder.Entity<RolePermission>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.RoleId).HasMaxLength(450).IsRequired();
|
||||
entity.Property(e => e.Module).HasMaxLength(60).IsRequired();
|
||||
// One row per (role, module).
|
||||
entity.HasIndex(e => new { e.RoleId, e.Module }).IsUnique();
|
||||
entity.HasOne(e => e.Role).WithMany()
|
||||
.HasForeignKey(e => e.RoleId).OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
// ── FamilyUnit ──────────────────────────────────────────────────────
|
||||
builder.Entity<FamilyUnit>(entity =>
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ROLAC.API.Authorization;
|
||||
using ROLAC.API.Entities;
|
||||
|
||||
namespace ROLAC.API.Data;
|
||||
@@ -62,6 +63,60 @@ public static class DbSeeder
|
||||
("visitor", "Visitor — public pages only"),
|
||||
];
|
||||
|
||||
// Default permission matrix — mirrors the hard-coded [Authorize(Roles=...)] rules that
|
||||
// existed before the configurable RBAC system, so day-one behavior is unchanged.
|
||||
// super_admin is intentionally absent: it bypasses all checks (see PermissionAuthorizationHandler).
|
||||
// R=Read, W=Write, D=Delete, A=Approve. Rows are inserted only if missing, so an admin's
|
||||
// later edits via the Permissions UI are never clobbered on restart.
|
||||
private static readonly (string Role, string Module, bool R, bool W, bool D, bool A)[] RolePermissionSeed =
|
||||
[
|
||||
// Secretary — manages member data.
|
||||
("secretary", Modules.Members, true, true, true, false),
|
||||
|
||||
// Pastor — read-only overview of members and all expenses.
|
||||
("pastor", Modules.Members, true, false, false, false),
|
||||
("pastor", Modules.Expenses, true, false, false, false),
|
||||
|
||||
// Finance — full control over the finance modules.
|
||||
("finance", Modules.Givings, true, true, true, false),
|
||||
("finance", Modules.GivingCategories, true, true, true, false),
|
||||
("finance", Modules.Expenses, true, true, true, true),
|
||||
("finance", Modules.ExpenseCategories, true, true, true, false),
|
||||
("finance", Modules.OfferingSessions, true, true, true, true),
|
||||
("finance", Modules.FinanceDashboard, true, false, false, false),
|
||||
("finance", Modules.MonthlyStatements, true, true, false, true),
|
||||
("finance", Modules.ChurchProfile, true, true, false, false),
|
||||
("finance", Modules.Disbursements, true, true, true, true),
|
||||
];
|
||||
|
||||
public static async Task SeedRolePermissionsAsync(AppDbContext db)
|
||||
{
|
||||
var rolesByName = await db.Roles
|
||||
.Where(r => r.Name != null)
|
||||
.ToDictionaryAsync(r => r.Name!, r => r.Id);
|
||||
|
||||
foreach (var (role, module, read, write, delete, approve) in RolePermissionSeed)
|
||||
{
|
||||
if (!rolesByName.TryGetValue(role, out var roleId))
|
||||
continue;
|
||||
|
||||
var exists = await db.RolePermissions.AnyAsync(p => p.RoleId == roleId && p.Module == module);
|
||||
if (exists)
|
||||
continue; // never clobber an admin's edit
|
||||
|
||||
db.RolePermissions.Add(new RolePermission
|
||||
{
|
||||
RoleId = roleId,
|
||||
Module = module,
|
||||
CanRead = read,
|
||||
CanWrite = write,
|
||||
CanDelete = delete,
|
||||
CanApprove = approve,
|
||||
});
|
||||
}
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public static async Task SeedRolesAsync(RoleManager<AppRole> roleManager)
|
||||
{
|
||||
foreach (var (name, description) in Roles)
|
||||
@@ -159,6 +214,7 @@ public static class DbSeeder
|
||||
await SeedRolesAsync(roleManager);
|
||||
|
||||
var db = services.GetRequiredService<AppDbContext>();
|
||||
await SeedRolePermissionsAsync(db);
|
||||
await SeedGivingCategoriesAsync(db);
|
||||
await SeedMinistriesAsync(db);
|
||||
await SeedExpenseCategoriesAsync(db);
|
||||
|
||||
Reference in New Issue
Block a user