Skip to content

Permissions & CASL

Authorization is implemented with CASL, an isomorphic authorization library. The TenantGuard resolves the caller's role and builds an AppAbility object that is checked against @CheckAbility decorators.

Roles

RoleWho has it
superadminUsers in the super_admins table — cross-tenant admin
adminUsers in the tenant_admins table for the requested tenant
responsibleUsers who are the responsibleId on at least one project in the tenant
userAll other authenticated tenant members

Role resolution

TenantGuard resolves roles in this priority order:

  1. SuperAdmin — check super_admins table
  2. Admin — check tenant_admins table
  3. Responsible — check if the user is a responsibleId on any project
  4. User — default for all other authenticated tenant members

API key authentication always grants admin access to the key's own tenant.

Permission matrix

SubjectActionsuperadminadminresponsibleuser
Tenantcreate
Tenantread✓ (own)✓ (own)✓ (own)
Tenantupdate✓ (own)
TenantUsermanage
TenantUserread
Entrymanageown only
Invoicemanagecreate + read
Projectmanage
Projectread
TaskListmanage
TaskListread
Vehiclemanage
Vehicleread
Syncmanage
ApiKeymanage
Webhookmanage
Notificationmanageownownown
allmanage

Using @CheckAbility

Apply @CheckAbility on a controller method to require a specific permission:

typescript
import { CheckAbility } from '../../casl/decorators'

@Get()
@CheckAbility({ action: 'read', subject: 'Entry' })
findAll() { ... }

@Post()
@CheckAbility({ action: 'create', subject: 'Invoice' })
create() { ... }

@Delete(':id')
@CheckAbility({ action: 'delete', subject: 'Project' })
remove() { ... }

If the caller's ability does not satisfy the requirement, TenantGuard returns 403 Forbidden.

Available actions and subjects

Actions: manage | create | read | update | delete

(manage implies all other actions)

Subjects: Tenant | TenantUser | TenantAdmin | Entry | Invoice | Project | TaskList | Vehicle | Notification | Sync | ApiKey | Webhook | all

TT Time Tracker — Internal Documentation