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
|
||||
/// than hard-coding role names, so the role→capability mapping lives in exactly
|
||||
/// 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>
|
||||
public enum Permission
|
||||
{
|
||||
// Café-level administration (Owner only)
|
||||
// ── Café administration (owner tier) ──────────────────────────────────────
|
||||
ViewCafeSettings,
|
||||
ManageCafeSettings,
|
||||
ManageDiscoverProfile,
|
||||
ViewBilling,
|
||||
ManageBilling,
|
||||
ManageBranches,
|
||||
|
||||
// Management (Owner + Manager)
|
||||
ManageStaff,
|
||||
ManageMenu,
|
||||
ManageInventory,
|
||||
ManageExpenses,
|
||||
ManageTaxes,
|
||||
ManageCoupons,
|
||||
ManageReservations,
|
||||
ManageTables,
|
||||
ViewReports,
|
||||
ReviewLeave,
|
||||
ManageSalaries,
|
||||
ViewBranches,
|
||||
CreateBranch,
|
||||
EditBranch,
|
||||
DeleteBranch,
|
||||
ManageRoles,
|
||||
ViewPrintSettings,
|
||||
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,
|
||||
EditOrder,
|
||||
VoidOrder,
|
||||
RefundOrder,
|
||||
ApplyDiscount,
|
||||
CompOrder,
|
||||
HandlePayments,
|
||||
UpdateOrderStatus,
|
||||
|
||||
// ── Register / cash ──────────────────────────────────────────────────────
|
||||
OperateRegister,
|
||||
OpenCashDrawer,
|
||||
|
||||
// ── Queue ─────────────────────────────────────────────────────────────────
|
||||
ViewQueue,
|
||||
ManageQueue,
|
||||
|
||||
// Kitchen
|
||||
// ── Kitchen ───────────────────────────────────────────────────────────────
|
||||
ViewKitchen,
|
||||
ManageKitchenStations,
|
||||
|
||||
// Delivery
|
||||
// ── Delivery ──────────────────────────────────────────────────────────────
|
||||
ViewDelivery,
|
||||
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 static Meezi.Core.Authorization.Permission;
|
||||
|
||||
namespace Meezi.Core.Authorization;
|
||||
|
||||
/// <summary>
|
||||
/// The authoritative role→capability matrix. Change what a role can do here and
|
||||
/// every controller that calls <c>EnsurePermission</c> updates automatically.
|
||||
/// The authoritative role→capability matrix. Change what a base role can do here
|
||||
/// and every controller that calls <c>EnsurePermission</c> updates automatically.
|
||||
/// Owners customise further with custom roles (which override this matrix entirely).
|
||||
/// </summary>
|
||||
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 =
|
||||
new Dictionary<EmployeeRole, HashSet<Permission>>
|
||||
{
|
||||
[EmployeeRole.Owner] = AllPermissions(),
|
||||
|
||||
[EmployeeRole.Manager] = new()
|
||||
{
|
||||
Permission.ManageStaff,
|
||||
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,
|
||||
},
|
||||
// Manager runs the café day to day: everything except the owner-only
|
||||
// governance (billing, branches, café identity, role definitions).
|
||||
[EmployeeRole.Manager] = AllExcept(OwnerOnly),
|
||||
|
||||
[EmployeeRole.Cashier] = new()
|
||||
{
|
||||
Permission.ProcessOrders,
|
||||
Permission.HandlePayments,
|
||||
Permission.OperateRegister,
|
||||
Permission.ManageQueue,
|
||||
Permission.ManageReservations,
|
||||
ViewOrders, ProcessOrders, EditOrder, HandlePayments, UpdateOrderStatus,
|
||||
OperateRegister, OpenCashDrawer,
|
||||
ViewQueue, ManageQueue,
|
||||
ViewTables,
|
||||
ViewReservations, CreateReservation, EditReservation,
|
||||
ViewMenu,
|
||||
ViewCustomers, CreateCustomer,
|
||||
ViewCoupons,
|
||||
},
|
||||
|
||||
[EmployeeRole.Waiter] = new()
|
||||
{
|
||||
Permission.ProcessOrders,
|
||||
Permission.ManageReservations,
|
||||
Permission.ManageQueue,
|
||||
ViewOrders, ProcessOrders, EditOrder, UpdateOrderStatus,
|
||||
ViewTables,
|
||||
ViewMenu,
|
||||
ViewReservations, CreateReservation, EditReservation,
|
||||
ViewQueue, ManageQueue,
|
||||
},
|
||||
|
||||
[EmployeeRole.Chef] = new()
|
||||
{
|
||||
Permission.ViewKitchen,
|
||||
ViewKitchen, UpdateOrderStatus, ViewOrders, ViewMenu,
|
||||
},
|
||||
|
||||
[EmployeeRole.Delivery] = new()
|
||||
{
|
||||
Permission.HandleDelivery,
|
||||
ViewDelivery, HandleDelivery, ViewOrders,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -73,4 +75,7 @@ public static class RolePermissions
|
||||
|
||||
private static HashSet<Permission> AllPermissions() =>
|
||||
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