PRD: Policy definition, permission catalog & grants
| Module | Permissions (CORE-02) | PRD ID | PRD-PERM-001 |
| Status | Shipped | Owner | Permissions squad |
| Date | 2026-05-28 | Version | v1.0 |
| Packages | @nx/identity · @nx/core · apps/bo · apps/client | URD | PERM · GRANT · EFF |
TL;DR
Gives administrators and merchant owners a usable management surface for KICKO's authorization model: browse and maintain the permission catalog as CRUD screens, then render the policy definition as per-target sections - role, user, merchant, and organizer - each with its own grant/revoke dialog and table. Grant/revoke flows are idempotent and scoped to the active merchant domain, so authorization can finally be managed in-product instead of by editing the database directly.
1. Context & Problem
KICKO's authorization model - a priority-based Casbin RBAC with per-merchant domains - already stores a permission catalog and policy grants inside the identity package, and the underlying grant/revoke/effective-permission services already exist. What is missing is a usable surface to drive them: administrators cannot browse the permission catalog, attach or detach permissions, or inspect which roles, users, merchants, and organizers a policy applies to without touching the database directly.
This increment builds that management surface on top of the existing identity services. It exposes the permission catalog as CRUD screens and renders the policy definition as a set of target sections (role, user, merchant, organizer), each with its own grant/revoke dialog and table. The same surface is cloned into the merchant-facing team module (client) so an owner can manage policy within their own merchant scope, backed by an @nx/core i18n pass that supplies the en/vi labels these screens render.
2. Goals & Non-Goals
Goals
- Surface the permission catalog as create/edit/list screens - unique code, action, scope, subject, and i18n name/description.
- Surface the policy definition with per-target grant/revoke: role, user, merchant, and organizer targets - each a section + table + grant dialog.
- Make grant/revoke flows idempotent and respect the per-request merchant domain, driving the existing identity grant/revoke services.
- Clone the policy-definition surface into the client team module so owners manage policy within their own merchant scope, with list filtering by target.
- Ship the en/vi i18n strings (
@nx/core) the screens depend on.
Non-Goals
- The resource/action/domain hierarchy and the declaration toolkit - coarse
manage/writegrants and module roll-up are a separate increment (PRD-HIER-001). - Wildcard/glob permissions, permission categories / UI grouping, role templates / bundles (URD Non-Goals).
- Time- or shift-based permissions and permission audit logging.
- The underlying authorization engine - this increment is the management UI, not the Casbin enforcer or the identity services it consumes.
3. Success Metrics
| Metric | Target / signal |
|---|---|
| Self-service management | Permission catalog and grants are managed in-product; zero direct database edits to grant/revoke |
| Catalog integrity | Every permission has a globally unique code; in-use permissions cannot be deleted |
| Grant safety | Re-granting an already-granted permission is a no-op (idempotent), never a duplicate |
| Scope correctness | Each grant/revoke resolves within the active merchant domain selected per request |
| Owner reach | Merchant owners can manage policy within their own scope via the team module |
4. Personas & Use Cases
| Persona | Goal in this feature |
|---|---|
| Admin (bo) | Maintain the permission catalog and grant/revoke permissions across roles, users, merchants, and organizers |
| Owner (client team module) | Manage policy within their own merchant scope |
| Operator | Inspect which targets a policy applies to without database access |
Core scenarios: maintain the permission catalog (create/edit/list) → open the policy definition → pick a target section (role / user / merchant / organizer) → grant or revoke permissions through the section's dialog → the change resolves within the active merchant domain, idempotently.
5. User Stories
- As an admin, I want to create, edit, and list permissions with a unique code, action, scope, and subject, so the catalog is maintained in-product.
- As an admin, I want to grant or revoke permissions to a role through a role-target section, so I control what a role can do.
- As an admin, I want to grant or revoke permissions to a user, merchant, or organizer through their own target section, so policy can be attached at the right level.
- As an admin, I want re-granting an existing permission to be skipped rather than duplicated, so grant operations are safe to retry.
- As an owner, I want the same policy-definition surface inside the client team module, so I manage policy within my own merchant scope.
- As an admin, I want grant/revoke to resolve within the active merchant domain, so changes apply only where intended.
6. Functional Requirements
| # | Requirement | URD ref |
|---|---|---|
| FR-1 | Permission catalog CRUD - create/edit/list with unique code, action, scope, subject, and i18n name/description | URD-PERM-001..004 |
| FR-2 | A permission code is globally unique; an in-use permission cannot be deleted | URD-PERM-002 · URD-PERM-005 |
| FR-3 | Role-target grant/revoke - section + table + grant dialog driving the identity grant/revoke services | URD-GRANT-001..002 |
| FR-4 | User-target grant/revoke - assign/unassign through a user-target section | URD-GRANT-004 |
| FR-5 | Merchant-target and organizer-target grant/revoke - each a section + table + grant dialog | URD-GRANT-001..002 · URD-EFF-004 |
| FR-6 | Grant/revoke is idempotent - an already-granted permission is skipped, not duplicated | URD-GRANT-003 |
| FR-7 | View which permissions a role holds and which users/roles a policy target applies to | URD-GRANT-006..007 |
| FR-8 | Each grant/revoke resolves within the active merchant domain chosen per request | URD-EFF-004 |
| FR-9 | Team-module clone (client) - the same role/user/merchant/organizer target surface scoped to a merchant, filterable by target | URD-GRANT-001..007 · URD-EFF-004 |
| FR-10 | en/vi i18n strings (@nx/core) for the permission and policy-definition screens | URD-PERM-003 |
Full requirement text and acceptance criteria live in the Permissions URD. This PRD references them rather than restating them.
7. Non-Functional Requirements
| Area | Requirement |
|---|---|
| Data integrity | Permission codes are globally unique; in-use permissions cannot be deleted; records are soft-deleted, never physically removed |
| Idempotency | Grant/revoke operations are safe to retry - re-granting is skipped, returning a skip count rather than erroring |
| Tenancy & authz | Every grant/revoke resolves within one active merchant domain per request (active-merchant header); privilege-escalation guard enforced by the underlying services |
| Performance / scale | List screens read the catalog and per-target grants without per-row fan-out; grant subqueries must read all grant rows (no default-limit truncation) |
| i18n | All user-facing labels and descriptions are bilingual ({ en, vi }), supplied by the @nx/core locale pass |
8. UX & Flows
Key screens: in apps/bo, the permission catalog (permission/{create,edit,list}) and the policy definition (policy-definition/{create,edit,list}) with its role/user/merchant/organizer target sections, each a section + table + grant dialog. In apps/client, the same target surface lives under the team module (team/policy-definition), scoped to a merchant and filterable by target.
9. Data & Domain
| Entity | Role |
|---|---|
Permission | A catalog entry - globally unique code, action, scope, subject, i18n name/description |
PolicyDefinition | The grant/policy record attaching a permission to a target |
| Role target | A grant of permissions to a role |
| User target | A grant of permissions to a user (and role-to-user assignment) |
| Merchant / Organizer target | A grant scoped to a merchant or organizer domain |
Conceptual only - full schema and the policy data model in the developer RBAC docs.
10. Dependencies & Assumptions
Depends on
- Identity policy/grant services (
@nx/identity) - the PolicyDefinition / Permission / grant-revoke services this surface drives. - Casbin per-merchant RBAC (
@nx/core) - the model + per-merchant policy adapter + enforcer that resolves grants within a domain. - Fixed & custom roles (URD-ROLE · URD-CROLE) - grants attach to roles that already exist.
- i18n strings (
@nx/core) - en/vi labels the screens render.
Assumptions
- The underlying identity catalog/grant/revoke and effective-permission services already exist (they predate this increment).
- An active merchant domain is selected per request via the active-merchant header.
- Roles, users, merchants, and organizers exist as grant targets.
11. Risks & Open Questions
| Risk / question | Mitigation / status |
|---|---|
| Grant subqueries truncated by a default query limit | Read all grant rows (no default-limit cap) so users with many grants are not silently dropped |
| Duplicate grants on retry | Grant/revoke is idempotent - re-granting is skipped, never duplicated |
| Policy-definition query syntax correctness | Verified against the identity services; a query-syntax issue is corrected before launch |
| Two surfaces (bo + client team module) drift | Shared target components/hooks/utils; the team-module surface is a scoped clone, not a fork |
| Flat per-target grants do not scale to coarse access | Superseded by the resource/action/domain hierarchy (PRD-HIER-001) |
12. Release Plan & Launch Criteria
| Aspect | Plan |
|---|---|
| Phase | P2 - see URD feature catalog (PERM / GRANT / EFF marked Built) |
| Rollout | All merchants; bo admin surface + client team-module clone; no feature flag |
| Migration | None - drives existing identity entities/services; @nx/core i18n strings added |
| Launch criteria | Catalog CRUD verified; grant/revoke verified idempotent across role/user/merchant/organizer targets within a merchant domain; team-module clone verified within merchant scope |
| Monitoring | Grant/revoke error rate, idempotent-skip counts, policy-definition query correctness |
13. FAQ
Does this increment build the authorization engine? No - the Casbin enforcer and the identity grant/revoke/effective-permission services already exist. This increment is the management UI that drives them.
What are "target sections"? The policy definition is rendered as four sections - role, user, merchant, and organizer - each with its own grant/revoke dialog and table, so a grant can be attached at the right level.
Why is grant/revoke idempotent? So operations are safe to retry - re-granting an already-granted permission is skipped (and counted), never duplicated.
Can an owner manage policy themselves? Yes - the same policy-definition surface is cloned into the client team module, scoped to the owner's merchant and filterable by target.
Does a grant apply everywhere? No - each grant/revoke resolves within the active merchant domain chosen per request via the active-merchant header.
What about coarse manage/write grants? Out of scope here - module roll-up and the resource/action/domain hierarchy are owned by PRD-HIER-001.
References
- URD: Permissions - Permission Catalog · Grant / Revoke · Effective Permissions & Scope
- Related PRD: Resource, Action & Domain Hierarchy
- Module: Permissions - overview + traceability
- Developer: @nx/identity · RBAC · Casbin Authorization