feat(rbac): full-CRUD permission catalog + per-role matrix
CI/CD / CI · API (dotnet build + test) (push) Successful in 55s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 29s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 1m33s
CI/CD / CI · API (dotnet build + test) (push) Successful in 55s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 29s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 1m33s
Expands the authorization catalog from 21 coarse page-level permissions to a granular set: View/Create/Edit/Delete per record module, plus distinct permissions for sensitive actions (VoidOrder, RefundOrder, ApplyDiscount, CompOrder, OpenCashDrawer, ExportReports) and the previously-uncovered pages (customers/CRM, SMS, reviews, financials, audit log, attendance, schedules). RolePermissions now derives Manager as "everything except owner-only governance" and gives Cashier/Waiter/Chef/Delivery sensible day-to-day defaults; owners refine further via custom roles. Effective permissions already flow to the client through AuthService, so no token-shape change is needed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,37 +5,135 @@ namespace Meezi.Core.Authorization;
|
|||||||
/// truth for authorization — controllers check a <see cref="Permission"/> rather
|
/// truth for authorization — controllers check a <see cref="Permission"/> rather
|
||||||
/// than hard-coding role names, so the role→capability mapping lives in exactly
|
/// than hard-coding role names, so the role→capability mapping lives in exactly
|
||||||
/// one place (<see cref="RolePermissions"/>).
|
/// one place (<see cref="RolePermissions"/>).
|
||||||
|
///
|
||||||
|
/// Granularity is "full CRUD per module + distinct sensitive actions": each page
|
||||||
|
/// has a View capability, record modules split Create/Edit/Delete, and high-risk
|
||||||
|
/// operations (void, refund, discount, comp, cash drawer, export) are their own
|
||||||
|
/// permissions so an owner can grant day-to-day work without the dangerous bits.
|
||||||
|
///
|
||||||
|
/// Names are persisted (custom roles store them by name in JSON, and they ride in
|
||||||
|
/// the JWT). Renaming or removing a value is a breaking change — add, don't rename.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum Permission
|
public enum Permission
|
||||||
{
|
{
|
||||||
// Café-level administration (Owner only)
|
// ── Café administration (owner tier) ──────────────────────────────────────
|
||||||
|
ViewCafeSettings,
|
||||||
ManageCafeSettings,
|
ManageCafeSettings,
|
||||||
|
ManageDiscoverProfile,
|
||||||
|
ViewBilling,
|
||||||
ManageBilling,
|
ManageBilling,
|
||||||
ManageBranches,
|
ViewBranches,
|
||||||
|
CreateBranch,
|
||||||
// Management (Owner + Manager)
|
EditBranch,
|
||||||
ManageStaff,
|
DeleteBranch,
|
||||||
ManageMenu,
|
ManageRoles,
|
||||||
ManageInventory,
|
ViewPrintSettings,
|
||||||
ManageExpenses,
|
|
||||||
ManageTaxes,
|
|
||||||
ManageCoupons,
|
|
||||||
ManageReservations,
|
|
||||||
ManageTables,
|
|
||||||
ViewReports,
|
|
||||||
ReviewLeave,
|
|
||||||
ManageSalaries,
|
|
||||||
ManagePrintSettings,
|
ManagePrintSettings,
|
||||||
|
|
||||||
// Front-of-house operations
|
// ── Taxes ─────────────────────────────────────────────────────────────────
|
||||||
|
ViewTaxes,
|
||||||
|
CreateTax,
|
||||||
|
EditTax,
|
||||||
|
DeleteTax,
|
||||||
|
|
||||||
|
// ── Staff & HR ──────────────────────────────────────────────────────────────
|
||||||
|
ViewStaff,
|
||||||
|
CreateStaff,
|
||||||
|
EditStaff,
|
||||||
|
DeleteStaff,
|
||||||
|
/// <summary>Assign per-branch roles / org structure (distinct from editing a record).</summary>
|
||||||
|
ManageStaff,
|
||||||
|
ManageStaffCredentials,
|
||||||
|
ViewAttendance,
|
||||||
|
ManageAttendance,
|
||||||
|
ViewSchedules,
|
||||||
|
ManageSchedules,
|
||||||
|
ViewLeave,
|
||||||
|
ReviewLeave,
|
||||||
|
ViewSalaries,
|
||||||
|
ManageSalaries,
|
||||||
|
|
||||||
|
// ── Menu ──────────────────────────────────────────────────────────────────
|
||||||
|
ViewMenu,
|
||||||
|
CreateMenuItem,
|
||||||
|
EditMenuItem,
|
||||||
|
DeleteMenuItem,
|
||||||
|
|
||||||
|
// ── Inventory ───────────────────────────────────────────────────────────────
|
||||||
|
ViewInventory,
|
||||||
|
CreateInventory,
|
||||||
|
EditInventory,
|
||||||
|
DeleteInventory,
|
||||||
|
|
||||||
|
// ── Tables ──────────────────────────────────────────────────────────────────
|
||||||
|
ViewTables,
|
||||||
|
ManageTables,
|
||||||
|
|
||||||
|
// ── Reservations ──────────────────────────────────────────────────────────
|
||||||
|
ViewReservations,
|
||||||
|
CreateReservation,
|
||||||
|
EditReservation,
|
||||||
|
DeleteReservation,
|
||||||
|
|
||||||
|
// ── Orders & POS ──────────────────────────────────────────────────────────
|
||||||
|
ViewOrders,
|
||||||
ProcessOrders,
|
ProcessOrders,
|
||||||
|
EditOrder,
|
||||||
|
VoidOrder,
|
||||||
|
RefundOrder,
|
||||||
|
ApplyDiscount,
|
||||||
|
CompOrder,
|
||||||
HandlePayments,
|
HandlePayments,
|
||||||
|
UpdateOrderStatus,
|
||||||
|
|
||||||
|
// ── Register / cash ──────────────────────────────────────────────────────
|
||||||
OperateRegister,
|
OperateRegister,
|
||||||
|
OpenCashDrawer,
|
||||||
|
|
||||||
|
// ── Queue ─────────────────────────────────────────────────────────────────
|
||||||
|
ViewQueue,
|
||||||
ManageQueue,
|
ManageQueue,
|
||||||
|
|
||||||
// Kitchen
|
// ── Kitchen ───────────────────────────────────────────────────────────────
|
||||||
ViewKitchen,
|
ViewKitchen,
|
||||||
|
ManageKitchenStations,
|
||||||
|
|
||||||
// Delivery
|
// ── Delivery ──────────────────────────────────────────────────────────────
|
||||||
|
ViewDelivery,
|
||||||
HandleDelivery,
|
HandleDelivery,
|
||||||
|
AssignDelivery,
|
||||||
|
|
||||||
|
// ── Customers / CRM ───────────────────────────────────────────────────────
|
||||||
|
ViewCustomers,
|
||||||
|
CreateCustomer,
|
||||||
|
EditCustomer,
|
||||||
|
DeleteCustomer,
|
||||||
|
|
||||||
|
// ── Coupons ───────────────────────────────────────────────────────────────
|
||||||
|
ViewCoupons,
|
||||||
|
CreateCoupon,
|
||||||
|
EditCoupon,
|
||||||
|
DeleteCoupon,
|
||||||
|
|
||||||
|
// ── SMS / marketing ──────────────────────────────────────────────────────
|
||||||
|
ViewSms,
|
||||||
|
SendSms,
|
||||||
|
ManageSmsSettings,
|
||||||
|
|
||||||
|
// ── Reviews ───────────────────────────────────────────────────────────────
|
||||||
|
ViewReviews,
|
||||||
|
ManageReviews,
|
||||||
|
|
||||||
|
// ── Reports & finance ─────────────────────────────────────────────────────
|
||||||
|
ViewReports,
|
||||||
|
ExportReports,
|
||||||
|
ViewAuditLog,
|
||||||
|
ViewFinancials,
|
||||||
|
ManageFinancials,
|
||||||
|
|
||||||
|
// ── Expenses ──────────────────────────────────────────────────────────────
|
||||||
|
ViewExpenses,
|
||||||
|
CreateExpense,
|
||||||
|
EditExpense,
|
||||||
|
DeleteExpense,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +1,66 @@
|
|||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
|
using static Meezi.Core.Authorization.Permission;
|
||||||
|
|
||||||
namespace Meezi.Core.Authorization;
|
namespace Meezi.Core.Authorization;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The authoritative role→capability matrix. Change what a role can do here and
|
/// The authoritative role→capability matrix. Change what a base role can do here
|
||||||
/// every controller that calls <c>EnsurePermission</c> updates automatically.
|
/// and every controller that calls <c>EnsurePermission</c> updates automatically.
|
||||||
|
/// Owners customise further with custom roles (which override this matrix entirely).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class RolePermissions
|
public static class RolePermissions
|
||||||
{
|
{
|
||||||
|
/// <summary>Capabilities reserved to the Owner — the rest is the Manager baseline.</summary>
|
||||||
|
private static readonly HashSet<Permission> OwnerOnly = new()
|
||||||
|
{
|
||||||
|
ManageCafeSettings,
|
||||||
|
ManageDiscoverProfile,
|
||||||
|
ViewBilling,
|
||||||
|
ManageBilling,
|
||||||
|
CreateBranch,
|
||||||
|
EditBranch,
|
||||||
|
DeleteBranch,
|
||||||
|
ManageRoles,
|
||||||
|
};
|
||||||
|
|
||||||
private static readonly IReadOnlyDictionary<EmployeeRole, HashSet<Permission>> Matrix =
|
private static readonly IReadOnlyDictionary<EmployeeRole, HashSet<Permission>> Matrix =
|
||||||
new Dictionary<EmployeeRole, HashSet<Permission>>
|
new Dictionary<EmployeeRole, HashSet<Permission>>
|
||||||
{
|
{
|
||||||
[EmployeeRole.Owner] = AllPermissions(),
|
[EmployeeRole.Owner] = AllPermissions(),
|
||||||
|
|
||||||
[EmployeeRole.Manager] = new()
|
// Manager runs the café day to day: everything except the owner-only
|
||||||
{
|
// governance (billing, branches, café identity, role definitions).
|
||||||
Permission.ManageStaff,
|
[EmployeeRole.Manager] = AllExcept(OwnerOnly),
|
||||||
Permission.ManageMenu,
|
|
||||||
Permission.ManageInventory,
|
|
||||||
Permission.ManageExpenses,
|
|
||||||
Permission.ManageTaxes,
|
|
||||||
Permission.ManageCoupons,
|
|
||||||
Permission.ManageReservations,
|
|
||||||
Permission.ManageTables,
|
|
||||||
Permission.ViewReports,
|
|
||||||
Permission.ReviewLeave,
|
|
||||||
Permission.ManageSalaries,
|
|
||||||
Permission.ManagePrintSettings,
|
|
||||||
Permission.ProcessOrders,
|
|
||||||
Permission.HandlePayments,
|
|
||||||
Permission.OperateRegister,
|
|
||||||
Permission.ManageQueue,
|
|
||||||
Permission.ViewKitchen,
|
|
||||||
Permission.HandleDelivery,
|
|
||||||
},
|
|
||||||
|
|
||||||
[EmployeeRole.Cashier] = new()
|
[EmployeeRole.Cashier] = new()
|
||||||
{
|
{
|
||||||
Permission.ProcessOrders,
|
ViewOrders, ProcessOrders, EditOrder, HandlePayments, UpdateOrderStatus,
|
||||||
Permission.HandlePayments,
|
OperateRegister, OpenCashDrawer,
|
||||||
Permission.OperateRegister,
|
ViewQueue, ManageQueue,
|
||||||
Permission.ManageQueue,
|
ViewTables,
|
||||||
Permission.ManageReservations,
|
ViewReservations, CreateReservation, EditReservation,
|
||||||
|
ViewMenu,
|
||||||
|
ViewCustomers, CreateCustomer,
|
||||||
|
ViewCoupons,
|
||||||
},
|
},
|
||||||
|
|
||||||
[EmployeeRole.Waiter] = new()
|
[EmployeeRole.Waiter] = new()
|
||||||
{
|
{
|
||||||
Permission.ProcessOrders,
|
ViewOrders, ProcessOrders, EditOrder, UpdateOrderStatus,
|
||||||
Permission.ManageReservations,
|
ViewTables,
|
||||||
Permission.ManageQueue,
|
ViewMenu,
|
||||||
|
ViewReservations, CreateReservation, EditReservation,
|
||||||
|
ViewQueue, ManageQueue,
|
||||||
},
|
},
|
||||||
|
|
||||||
[EmployeeRole.Chef] = new()
|
[EmployeeRole.Chef] = new()
|
||||||
{
|
{
|
||||||
Permission.ViewKitchen,
|
ViewKitchen, UpdateOrderStatus, ViewOrders, ViewMenu,
|
||||||
},
|
},
|
||||||
|
|
||||||
[EmployeeRole.Delivery] = new()
|
[EmployeeRole.Delivery] = new()
|
||||||
{
|
{
|
||||||
Permission.HandleDelivery,
|
ViewDelivery, HandleDelivery, ViewOrders,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -73,4 +75,7 @@ public static class RolePermissions
|
|||||||
|
|
||||||
private static HashSet<Permission> AllPermissions() =>
|
private static HashSet<Permission> AllPermissions() =>
|
||||||
new(Enum.GetValues<Permission>());
|
new(Enum.GetValues<Permission>());
|
||||||
|
|
||||||
|
private static HashSet<Permission> AllExcept(HashSet<Permission> excluded) =>
|
||||||
|
new(Enum.GetValues<Permission>().Where(p => !excluded.Contains(p)));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user