Cross-cutting · Role & module permission layer
Access control is the product of two independent dimensions — the user’s staff level and the centre’s enabled modules — expressed as 55 ember-can ability classes that gate menu entries, route entry and individual actions.
| Area | Cross-cutting |
| Routes | (every route) |
| Centre modules | always available |
| Staff levels | root · admin · manager · scmanager · storeManager · commerce · crmmanager · crmoperator · weboperator · content_creator · calibrator · owner · external · sales · services |
| Models | modules · staff-member |
| UI label | (abilities) |
| Coverage | 5 use cases · 2 happy · 5 failure |
Known issues (1)
- 🟠 Medium — [[R07 · campaign canDelete compares a role to a status|campaign#canDelete compares a role to a status]]
How it works
The pattern is consistent: a module-* ability holds a moduleName plus a roles array and computes canAccess as ‘role is allowed AND centre has the module’; entity abilities extend the relevant module ability and add canCreate/canEdit/canDelete/etc. on top, often also conditioned on record state (campaign status, proposal status, receipt state, month state). This is why permissions in this product are genuinely three-dimensional: role × module × record state. The navbar applies the same logic to sections (section.levels, section.module, section.disabledByModule, or section.ability). Worth knowing: a role that is not in enums/staff-levels yields an empty menu rather than an error; some abilities contain latent logic bugs (the campaign#canDelete check compares the CONTENT_CREATOR role constant against model.status); and intranet-file accepts an owner role value in its inclusion rule that the UI never offers.
Use cases
Why someone opens this (5)
- Giving a store-level user access only to their own sales and documents.
- Letting a content creator draft proposals but requiring a manager to publish them.
- Selling modules per centre so one tenant has loyalty and another does not.
- Protecting closed activities and validated months from further edits.
- Restricting the CLI to root and imports to root/admin.
Path map
flowchart LR F(["Role and module permission layer"]):::feat F --> H1["Menu and actions reflect role and modules"]:::ok F --> H2["Record state tightens permission"]:::ok F -.-> X1["Module disabled"]:::bad F -.-> X2["Role not allowed"]:::bad F -.-> X3["Unrecognised role"]:::bad F -.-> X4["Centre not loaded"]:::bad F -.-> X5["Latent ability logic bug"]:::bad classDef feat fill:#8c5a3c,stroke:#8c5a3c,color:#ffffff,font-weight:bold classDef ok fill:#e3f1ea,stroke:#0d6b42,color:#0d4a2e classDef bad fill:#f7e6e2,stroke:#9a2d1c,color:#6e1f13
Happy paths
Menu and actions reflect role and modules
- User signs in;
auth.load()resolves their role and the centre’s modules.nf-navbar#menufilters sections by level, module and ability.- Each list screen renders only the row actions whose abilities pass.
- Direct URL entry to a blocked route is rejected by the route’s own
cancheck.
Record state tightens permission
- An activity is marked finished.
campaign#canEdit/canDeletenow return false for non-root/admin roles.canShowbecomes true, so the report opens instead.
Failure paths
Module disabled
Cause — Centre lacks the module behind the feature.
What happens —
canAccessfalse — menu entry hidden and route entry rejected.
Role not allowed
Cause — User’s level is not in the ability’s
roles.What happens — Same as above; no explanatory message is shown.
Unrecognised role
Cause —
auth.user.roleis not a known staff level.What happens —
nf-navbar#menureturns[]— the user gets a functioning app with no navigation, which is confusing rather than secure-by-message.
Centre not loaded
Cause —
auth.centerstill null, orcidcookie points at an inaccessible centre.What happens —
get(center, 'has…')resolves falsy, so every module-gated feature disappears at once.
Latent ability logic bug
Cause —
campaign#canDeletetests a role constant againstmodel.status.What happens — Evaluates as a non-match, making the status guard weaker than intended — worth a fix.
QA checklist
Tick these off while regression-testing this feature. The boxes are plain Markdown, so progress is visible in git diffs.
- ✅ Menu and actions reflect role and modules
- ✅ Record state tightens permission
- ⛔ Module disabled
- ⛔ Role not allowed
- ⛔ Unrecognised role
- ⛔ Centre not loaded
- ⛔ Latent ability logic bug
Implementation
app/abilities/*.jsapp/services/can.jsapp/enums/staff-levels.jsapp/enums/modules.jsapp/enums/permissions.jsapp/utils/get-side-nav-menu.jsapp/components/nf-navbar.jsapp/routes/authenticated.js
Translation keys (39)
Key English rootROOT adminAdmin content-creatorContent creator calibratorCalibrator commerceStore crm-managerCRM Manager crm-operatorCRM Operator external-apiExternal API managerManager ownerOwner scmanagerSC Manager store-managerStore Manager web-operatorAgency staff-level-salesSales staff-level-servicesServices modules-crmCRM modules-cmsCMS modules-marketingMarketing modules-intranetIntranet modules-aiArtificiaI Intelligence modules-fidelizaFideliza modules-lotteryDigital games modules-fidcardFidcard modules-cashbackCashback modules-tallyInterest form modules-formsCustom form modules-connectifConnectif modules-blogBlog modules-theatersTheaters modules-email-validationEmail validation modules-app-operatorApp operator modules-auto-logoutAutologout modules-totem-channelChannel totem modules-proposal-inboxProposal mailbox modules-multicenter-proposalsMulticenter proposals modules-form-commercesCommerces form modules-employees-segmentsContent creator modules-commerces-mapStores map modules-foreground-locationForeground location
Related
- Same area — CSV import engine · Data export engine (email delivery) · Filter & segment builder component · File upload, gallery & image modal · Multi-language content authoring · Realtime updates (Pusher) · Error presentation & API error mapping · Product analytics & error monitoring
- Shares data with — Centre switching & multi-centre scoping · Centre editor (modules, app config, integrations) · Staff login · Password reset request · Password update from reset link · Change own password · Intranet communications (threads, confirmations, authorisations) · Staff member list, deletion & restore
Generated from doc/feature-inventory.json · id xc-permissions · commit 276788ca6