Permissions
Three kinds, not one
| Kind | Stored as | Example | Used for |
|---|---|---|---|
| Role (position, «منصب») | a row in AspNetRoles, added from /admin/roles |
General Services Officer | holding steps; holding permissions |
Relation (Relations) |
nothing: a path through the organisation data | line manager, branch officer, requester | holding steps that follow the requester |
Permission (Permission enum) |
granted to roles in RolePermissions |
ManageStock |
what the code guards |
A relation is never a role, and the code never branches on a role's name: RoleKeys is a seed for seeding and tests,
not a reference.
The permissions
| Group | Permission | Allows |
|---|---|---|
| Requests | ViewAllRequests |
Read every request |
DeleteAnyRequest |
Soft-delete any request; remove a step's photos | |
RestoreDeletedRequest |
Restore a deleted request | |
CancelAnyRequest |
Cancel any request, a locked one included | |
ReassignApproval |
Hand an approval step to someone else | |
AmendAnyRequest |
Correct an open request's data, with a reason (moves a booking) | |
| Administration | ViewAdminDashboard |
The dashboard: late and stuck steps |
ManageUsers |
Employee accounts, their roles, password resets | |
ManageOrganisation |
Branches and departments | |
ManageLookups |
Shared lists, their values and settings | |
ManageCatalog |
Service definitions and publishing | |
ManagePermissions |
The role-permission matrix (the highest privilege) | |
ImpersonateUsers |
Sign in as an employee, read-only and audited | |
ViewSystemLogs |
The server's log | |
ViewChangelog |
The release notes tab of the guide | |
| Rooms | ManageRooms |
Rooms, booking policy, closures, the floor plan |
ViewRoomBookers |
Who booked each slot | |
OverrideBookings |
Book past the policy (logged as booking.overridden) |
|
| Reports | ViewDepartmentReports |
Reports over the viewer's own department |
ViewAllReports |
Reports over everything | |
| Guide | GuideDecisions, GuideRooms |
The approvals and the rooms parts of the user guide |
| Stock | ManageStock |
The stock page, vouchers, low-stock and expiry notices |
Values are stable integers; a retired value (10, 11) is never reused, because a stored grant would read it.
Default grants
Seeded once (Permissions.Defaults), then owned by the society from the matrix; never re-applied on start.
| Role | Permissions |
|---|---|
SystemAdmin |
all |
AdminAffairsManager |
ViewAllRequests, CancelAnyRequest, AmendAnyRequest, ViewAdminDashboard, ManageLookups, ManageRooms, ViewRoomBookers, OverrideBookings, ViewAllReports, ManageStock |
GeneralServicesOfficer |
AmendAnyRequest, ManageRooms, ViewRoomBookers |
Ceo, SupportSectorAssistantCeo |
ViewAllRequests, ViewAdminDashboard, ViewAllReports |
| every role | GuideDecisions, GuideRooms (given once, Permissions.EveryPosition) |
An employee with no role holds no permission: they submit what a service's audience allows and read the guide's first parts.
Enforcement
- Policy-based authorization. Pages use
[Authorize(Policy = "perm:X")], code asksCan(Permission.X); the policies are generated from the enum. - Resource access is one rule. Who may view a request is
RequestAccess.MayViewalone, for the page, the decision sheet and the attachment endpoints: the requester, the step holders, whoever was told a step is late, orViewAllRequests. - Who may act on a step is
RequestService.CanAct, with its SQL twins inInbox;InboxTestscompares them. - Containment. Acting on another account (impersonating, editing their card, resetting their password) requires
holding every permission they hold (
Permissions.Within), so nobody reaches a permission through someone else. - Audit. Every grant and revoke is written to the audit log (
permission.granted,permission.revoked), and the matrix saves only on «حفظ».
Adding a permission
Add a value to Permission (src/ErpDarAlBer.Core/Domain/Permission.cs), its Arabic name in Permissions.NameAr,
its explanation in PermissionHelp, optionally a default grant, then guard with the policy or Can. Never check a
role name.