Surypus implements a comprehensive Role-Based Access Control (RBAC) system to manage user permissions across the API. This document outlines the role hierarchy, permissions, and how they are applied to API endpoints.
The system defines four primary roles with different permission levels:
- Description: Full system access
- Permissions: All permissions
- Typical Users: System administrators, developers
- Key Permissions: AdminAccess, all CRUD operations
- Description: Business operations management
- Permissions: Most write operations, all read operations
- Typical Users: Department managers, supervisors
- Limited By: Cannot access admin functions, cannot delete sensitive data
- Description: Standard user with basic read and write access
- Permissions: Limited write operations, basic read operations
- Typical Users: Regular employees, data entry staff
- Limited By: Read-only for sensitive areas (accounting, payroll), no delete permissions
- Description: Read-only access for reporting and analysis
- Permissions: Read-only across the system
- Typical Users: External stakeholders, auditors, analysts
- Limited By: Cannot perform any write or delete operations
Permissions are organized by resource type:
PersonRead- Read person dataPersonWrite- Create/update person dataPersonDelete- Delete person records
GoodsRead- Read goods catalogGoodsWrite- Create/update goodsGoodsDelete- Delete goodsStockRead- Read stock informationStockWrite- Modify stock (internal use)
BillRead- Read bills/invoicesBillWrite- Create/update billsBillDelete- Delete billsBillPost- Post/confirm bills
LocationRead- Read location dataLocationWrite- Create/update locationsLocationDelete- Delete locations
AccountingRead- Read accounting entriesAccountingWrite- Create/update accounting entriesPaymentRead- Read payment informationPaymentWrite- Create/update paymentsPaymentDelete- Delete payments
OrdersWrite- Manage orders (read/write/delete)
TaxesWrite- Manage tax rates and calculationsCurrenciesWrite- Manage currency settings
UsersRead- Read user informationUsersWrite- Create/manage usersSettingsRead- Read system settingsSettingsWrite- Modify system settings
PayrollRead- Read payroll dataPayrollWrite- Create/update payrollSalariesWrite- Manage salary information
ReportsRead- Access reportsReportsWrite- Create/manage reports
AdminAccess- Access administrative functions
POST /v1/login - Public (no auth required)
POST /v1/refresh - Public (no auth required)
GET /v1/persons - PersonRead
POST /v1/persons - PersonWrite
GET /v1/persons/:id - PersonRead
PUT /v1/persons/:id - PersonWrite
DELETE /v1/persons/:id - PersonDelete
GET /v1/persons/search/:query - PersonRead
GET /v1/goods - GoodsRead
POST /v1/goods - GoodsWrite
GET /v1/goods/:id - GoodsRead
PUT /v1/goods/:id - GoodsWrite
DELETE /v1/goods/:id - GoodsDelete
GET /v1/locations - LocationRead
POST /v1/locations - LocationWrite
GET /v1/locations/:id - LocationRead
PUT /v1/locations/:id - LocationWrite
DELETE /v1/locations/:id - LocationDelete
GET /v1/bills - BillRead
POST /v1/bills - BillWrite
GET /v1/bills/:id - BillRead
PUT /v1/bills/:id - BillWrite
DELETE /v1/bills/:id - BillDelete
PUT /v1/bills/:id/status - BillPost
GET /v1/payments - PaymentRead
POST /v1/payments - PaymentWrite
GET /v1/payments/:id - PaymentRead
PUT /v1/payments/:id - PaymentWrite
DELETE /v1/payments/:id - PaymentDelete
GET /v1/orders - OrdersWrite (for access)
POST /v1/orders - OrdersWrite
GET /v1/orders/:id - OrdersWrite (for access)
PUT /v1/orders/:id/status - OrdersWrite
DELETE /v1/orders/:id - OrdersWrite
GET /v1/taxes - TaxesWrite (for access)
POST /v1/taxes - TaxesWrite
GET /v1/taxes/:id - TaxesWrite (for access)
PUT /v1/taxes/:id - TaxesWrite
DELETE /v1/taxes/:id - TaxesWrite
GET /v1/currencies - CurrenciesWrite (for access)
POST /v1/currencies - CurrenciesWrite
GET /v1/currencies/:id - CurrenciesWrite (for access)
PUT /v1/currencies/:id - CurrenciesWrite
DELETE /v1/currencies/:id - CurrenciesWrite
GET /v1/stock - StockRead
GET /v1/stock/summary - StockRead
GET /v1/stock/:goodsId/:locId - StockRead
GET /v1/stock/goods/:goodsId - StockRead
GET /v1/accounting/accounts - AccountingRead
POST /v1/accounting/accounts - AccountingWrite
GET /v1/accounting/accounts/:id - AccountingRead
PUT /v1/accounting/accounts/:id - AccountingWrite
DELETE /v1/accounting/accounts/:id - AccountingWrite
GET /v1/accounting/entries - AccountingRead
POST /v1/accounting/entries - AccountingWrite
GET /v1/accounting/entries/:id - AccountingRead
PUT /v1/accounting/entries/:id - AccountingWrite
DELETE /v1/accounting/entries/:id - AccountingWrite
GET /v1/payroll - PayrollRead
GET /v1/payroll/employees - PayrollRead
GET /v1/payroll/employees/:id - PayrollRead
GET /v1/payroll/salaries - PayrollRead
GET /v1/payroll/salaries/:id - SalariesWrite (for access)
GET /v1/users - UsersRead
POST /v1/users - UsersWrite
GET /v1/rbac/roles - AdminAccess
POST /v1/rbac/roles - AdminAccess
PUT /v1/rbac/roles/:name - AdminAccess
DELETE /v1/rbac/roles/:name - AdminAccess
GET /v1/rbac/grants - AdminAccess
POST /v1/rbac/grants - AdminAccess
GET /v1/rbac/grants/active?principal=... - AdminAccess
POST /v1/rbac/grants/cleanup - AdminAccess
PUT /v1/rbac/grants/:from/:to/:permission?resource=... - AdminAccess
DELETE /v1/rbac/grants/:from/:to/:permission?resource=... - AdminAccess
GET /v1/rbac/audit?principal=...&resource=...&offset=...&limit=... - AdminAccess
POST /v1/rbac/audit/cleanup?keep=... - AdminAccess
Dynamic roles are stored as scoped permissions and can be restricted to a
canonical resource key such as location:1, bill:42, report:sales, or
accounting-entry:17.
Delegation grants support temporary escalation through an expiry timestamp.
Expired grants can be cleaned up explicitly through the cleanup endpoint.
Updating a grant refreshes its expiry window while keeping its principal and scoped permission key.
Audit entries can be paginated with offset/limit and trimmed with the audit cleanup endpoint.
GET /v1/reports - ReportsRead
GET /v1/reports/metadata - ReportsRead
GET /v1/reports/templates - ReportsRead
GET /v1/reports/:id - ReportsRead
GET /v1/reports/:name/jrxml - ReportsRead
The system uses a permission matrix where each role is associated with specific permissions:
data RolePermission = RolePermission
{ rpRole :: Role
, rpPermissions :: [Permission]
}Permissions are checked using the checkPermission function:
checkPermission :: Text -> Permission -> Either String ()This function:
- Takes a role (as Text)
- Takes a required permission
- Returns
Right ()if the role has the permission - Returns
Left errorif the role lacks the permission
The requirePermission helper in Surypus.API.Server provides access checking:
requirePermission :: Text -> Permission -> Handler ()This function returns a 403 Forbidden response if the permission check fails.
The runtime request pipeline now also supports a request-aware authorization resolver via advanced middleware. It can:
- Authenticate non-public requests
- Resolve required permission from the incoming request path/method
- Load dynamic roles and delegation grants from an RBAC store
- Apply resource-level checks using scoped permissions
- Emit audit entries for allow/deny decisions
case checkPermission "admin" AdminAccess of
Right () -> -- User is admin
Left err -> -- User is not adminEndpoints use comments to document required permissions:
personsList :: Handler PersonsResponse
-- | GET /v1/persons - Requires PersonRead permission
personsList = return $ PersonsResponse [...] 1let userRole = roleFromText "manager"
if hasPermission userRole PersonWrite
then -- Allow write operation
else -- Deny write operationThe RBAC system includes comprehensive tests in test/RBACSpec.hs and test/Test.hs:
- Role permission verification
- Cross-role permission boundaries
- Text-to-role conversion
- Permission checking functions
Run tests with:
stack test- Dynamic Roles: Create custom roles with specific permission sets
- Permission Delegation: Allow managers to delegate specific permissions
- Audit Logging: Track permission checks and changes
- Time-based Permissions: Temporary permission escalation
- Resource-level Permissions: Fine-grained access control per resource
- Default Deny: All permissions must be explicitly granted
- Role Validation: Invalid roles default to the most restrictive (Viewer)
- Token-based Verification: Permissions are verified from JWT tokens
- Permission Immutability: Permissions are defined at compile time
Role definitions are in src/Surypus/RBAC.hs. To modify permissions:
- Update the
Permissiondatatype - Add the permission to relevant
RolePermissionentries - Update endpoint documentation
- Add corresponding tests
For questions or issues with the RBAC system, please:
- Check the test suite in
test/RBACSpec.hs - Review endpoint documentation comments in
src/Surypus/API/Server.hs - Consult the RBAC module documentation in
src/Surypus/RBAC.hs