In this volume · VOLUME 02
User Management
Users, Roles & RBAC Authentication

Chapter 2.1 — Users, Roles & RBAC

chapter: 02-user-management/01-users-roles-rbac
version: 1.0.0
status: stable
last_reviewed: 2026-05-26
owners: [platform-engineering, security]

1. Purpose

This chapter defines how identity, role assignment, and permission checks work in travoBooks. It is the security backbone of every other module: any check of the form "can this person do this thing?" resolves here.

2. Why this matters in modern travel accounting

A travel business is a collaboration of distinct functions with incompatible authority profiles:

  • A reservations agent must book and ticket but must not be able to issue refunds, post journal entries, or change a customer's credit limit.
  • An accounts officer must reconcile and post but must not be able to alter a booking that has been issued.
  • A manager must approve refunds above a threshold but should not routinely create them.
  • An auditor must see everything and change nothing.
  • A junior should never see net cost from the supplier; a manager must.

Without strict role separation, the same person who creates a customer can also write off their receivable balance — a classic embezzlement path. travoBooks encodes segregation of duties as a first-class system property.

3. Industry relevance

Both IATA agent-accreditation audits and SOX-style internal control reviews look for evidence of:

  1. Documented role definitions.
  2. Enforcement of those roles in software, not policy.
  3. An audit trail showing who did what.
  4. A "maker–checker" pattern for high-impact actions.

travoBooks satisfies all four out of the box.

4. Compliance considerations

  • Segregation of duties (SoD) — booking creation, ticket issuance, refund issuance, journal posting, and payment release must each be separable. travoBooks ships SoD-aware role templates.
  • Least privilege — default roles grant only what is necessary to perform a defined job.
  • Right to revocation — disabling a user revokes access within seconds across all sessions and API tokens.
  • Right to audit — every permission check is loggable; security-sensitive denials are always logged.

5. Business logic

5.1 Conceptual model

flowchart LR U[User] -- belongs to --> PU[Partner-User membership] PU -- assigned --> R[Roles] R -- grant --> P[Permissions] R -- bound to --> S[Scopes] P -. checked at .-> Action
  • A User is a person, identified by a globally-unique email.
  • A user has one or more Partner-User memberships (one per partner they belong to).
  • Each membership has one or more Roles.
  • Each role grants a set of Permissions.
  • Permissions may be scoped (e.g. "can refund within their own bookings only").

5.2 Permission grammar

Permissions use a three-part dotted form: resource.action.scope.

Component Examples
resource booking, invoice, customer, supplier, journal, payment, user, report, setting
action read, create, update, delete, approve, void, refund, post, export
scope any, own, team, partner, branch:{id}

Examples: - booking.create.any — create a booking for any customer of the partner. - booking.refund.own — refund a booking the actor originally created. - invoice.post.partner — post any invoice in the partner. - journal.read.any — read any journal entry (auditor).

5.3 Default role templates

travoBooks ships with the following role templates, applied on partner provisioning. Partners may clone and modify them but the templates themselves are read-only.

Role Purpose Notable permissions
partner_admin Full control of the partner *.*.partner
accountant Run the books journal.*.partner, invoice.*.partner, payment.*.partner, report.read.partner, booking.read.partner (read-only on ops)
senior_agent Sell + ticket booking.*.partner, ticket.issue.partner, customer.*.partner, invoice.create.partner
agent Sell, no ticket issuance booking.create.own, booking.read.team, customer.read.partner, invoice.create.own
cashier Receive payments payment.create.partner, payment.read.partner, invoice.read.partner
approver Approve high-value actions booking.approve.partner, refund.approve.partner, payment.approve.partner
auditor Read-only, all books *.read.partner, audit.read.partner
api_integration Server-to-server scoped per integration
viewer Dashboards only report.read.partner

5.4 Maker–checker

Certain actions require a different user to approve them. Configurable per partner with sensible defaults:

Action Threshold Default approver
Refund Any amount > partner.refund_approval_threshold (default 500 USD-eq) approver
Manual journal entry Always accountant ≠ creator
Credit note Any amount > threshold approver
Customer credit limit change Always partner_admin
Bank reconciliation force-match Always accountant ≠ matcher
User role assignment Always partner_admin

The maker may not be the checker; this is enforced server-side. See Multi-User Workflows.

5.5 Scopes

Scope qualifies which instances of a resource a permission applies to:

  • any — all instances within the partner.
  • partner — same as any (alias).
  • own — only rows where created_by_user_id = actor.user_id.
  • team — rows where created_by_user_id is in actor.team_ids.
  • branch:{id} — rows where branch_id = {id}.

Scope resolution is computed at permission-check time. A role can grant the same resource.action at multiple scopes; the union applies.

5.6 Time-bound roles

Roles can be assigned with optional valid_from / valid_until. Use cases: temporary cover during leave; contractor with a fixed engagement; emergency support elevation.

6. Inputs → processing → outputs

6.1 Creating a user (input)

user:
  email: "[email protected]"
  full_name: "Sara Khatun"
  phone: "+8801XXXXXXXXX"
  locale: "en_BD"
membership:
  partner_id: "P-001"
  roles: ["senior_agent"]
  branch_id: null
  valid_from: null
  valid_until: null

6.2 Processing

  1. Validate email format and uniqueness across the platform.
  2. Insert users row (if new) with status invited.
  3. Insert partner_users row linking user to partner.
  4. Insert partner_user_roles rows for each role.
  5. Generate a one-time activation token; email it.
  6. Write audit log.

6.3 Outputs

  • A new user_id and membership_id.
  • An activation email with a token-bearing URL (token expires in 72 hours).
  • An audit entry visible in the partner's activity feed.

7. Module dependencies

  • Reads from: partners, country/locale reference, role templates.
  • Writes to: users, partner_users, partner_user_roles, audit_logs, notification_outbox.
  • Consumed by: every other module via the permission middleware.

8. Security & permissions

8.1 Authentication (the predicate)

A user must first prove who they are; that is covered in 02-authentication.md. Permission checks presume an authenticated actor.

8.2 Permission check flow

flowchart TD R[Request arrives] --> A[Authenticate actor] A --> B[Resolve active partner from header/path] B --> C{Actor in partner_users for partner?} C -- No --> D[403 PARTNER_FORBIDDEN] C -- Yes --> E[Load cached permission set for actor + partner] E --> F{Required perm in set at sufficient scope?} F -- No --> G[403 PERMISSION_DENIED + audit log] F -- Yes --> H{Maker–checker required?} H -- Yes --> I{Different actor approving?} I -- No --> J[202 PENDING_APPROVAL] I -- Yes --> K[Proceed] H -- No --> K

8.3 Sensitive data redaction

Certain fields are redacted in API responses based on permission, not stripped from the DB:

Field Redacted unless…
Supplier net cost on a booking actor has booking.read_cost.partner
Customer credit balance actor has customer.read_credit.partner
Bank account numbers actor has payment.read_bank.partner
Passenger passport / DOB actor has passenger.read_pii.partner and the booking is within the actor's allowed scope

8.4 API tokens

  • Personal Access Tokens (PATs): owned by a user, inherit that user's permissions.
  • Service tokens: owned by the partner, assigned a dedicated api_integration role.
  • Both carry scopes (which is a token-level restriction on top of the user's permissions, never an expansion).
  • Tokens are hashed at rest (Argon2id); the raw token is shown once at creation.
  • Token rotation supported with a 7-day grace period.

8.5 Session security

  • Cookie: HttpOnly, Secure, SameSite=Strict, __Host- prefix.
  • Session TTL: 8 hours sliding, 24 hours absolute.
  • Concurrent session policy per partner: unlimited | single | count:{n}.
  • 🔒 Permission cache is keyed by (user_id, partner_id, role_set_version). Bumping role_set_version on any role change instantly invalidates all caches.

9. Validation rules

Rule Error code
Email must be valid RFC 5322 EMAIL_INVALID
Email must be unique platform-wide USER_DUPLICATE
At least one role required per membership ROLES_REQUIRED
Role must exist and be enabled for the partner ROLE_UNKNOWN
Cannot remove the last partner_admin LAST_ADMIN_PROTECTED
valid_until > valid_from VALIDITY_RANGE_INVALID
Maker cannot equal checker MAKER_CHECKER_SAME_ACTOR
Cannot self-assign roles you don't already have PRIVILEGE_ESCALATION_BLOCKED

10. Error handling

Scenario Behaviour
Expired activation token 410 GONE, code ACTIVATION_EXPIRED, offer resend
Disabled user attempts to log in 401, code USER_DISABLED (do not specify which)
Permission check fails 403 + audit log; UI hides affordance
Role removed mid-session Next request invalidates cache and re-evaluates; affected actions return 403
Maker–checker stalled Pending approvals dashboard surfaces; auto-expire after 7 days

11. Real-world example

Scenario. Junior agent Asha creates a booking with a USD 1,200 markup discount. Discounts > 10% require approval. The booking is held in pending_approval state. Senior agent Mahin opens the approvals queue, reviews the discount note, approves. The booking advances to confirmed; an audit log entry records both actors. A refund is later issued by accountant Tanvir, requiring approval by the approver role; Mahin approves. The booking, refund, and approval chain are all linked in the audit log.

🔒 Critically, Asha cannot approve her own discount because the approver check enforces actor.user_id != record.created_by. The UI hides the "Approve" button when viewing her own pending booking, and the server enforces the rule even if she calls the API directly.

12. Step-by-step workflow — granting a role

sequenceDiagram autonumber participant Admin participant UI participant API participant DB participant Cache participant Audit Admin->>UI: Open user → "Add role: approver" UI->>API: POST /users/:id/roles {role:"approver"} API->>API: Check actor has `user.assign_role.partner` API->>API: Check actor has `approver` themselves (no escalation) API->>DB: BEGIN API->>DB: INSERT partner_user_roles API->>DB: UPDATE users.role_set_version += 1 API->>DB: INSERT audit_logs API->>DB: COMMIT API->>Cache: INVALIDATE perms({user_id}) API-->>UI: 200 UI-->>Admin: "Role added"

13. Database tables touched

  • users — global identity.
  • partner_users — membership.
  • partner_user_roles — role assignments.
  • roles — partner-scoped role definitions.
  • role_permissions — role → permission bindings.
  • permissions — the catalog.
  • api_tokens — PATs and service tokens.
  • sessions — active sessions.
  • audit_logs — every change.

14. Future scalability

Pressure Response
Attribute-based access for complex rules ("only bookings from corporate clients in the EU") Layer ABAC on top of RBAC: policy expressions evaluated at request time.
SSO with enterprise IdPs SAML + OIDC support; group claims map to roles via a configurable mapping.
SCIM-driven provisioning SCIM 2.0 endpoint; per-partner secrets; conflict rules for the unique-email constraint.
Just-in-time elevation ("break-glass") Time-bounded role with mandatory approver and visible banner during elevation.

15. Common pitfalls

  • ⚠️ Granting partner_admin "for now" — there is no "for now". Use time-bounded roles instead.
  • ⚠️ Caching permissions too aggressively. The 5-minute TTL is safe because role_set_version invalidates immediately on change; do not relax this further.
  • ⚠️ Allowing the UI to drive what's possible. The server is the source of truth; the UI is a hint.
  • 🔒 Never expose a "permissions diff" to a user being denied — that aids reconnaissance.

Next: 02-authentication.md — login, MFA, session, and API token mechanics.