Permissions & Authorization Guide
This skill helps you work with Lightdash's CASL-based permissions system, including scopes, custom roles, and authorization enforcement.
What do you need help with?
-
Add a new scope/permission - Step-by-step guide to add a new permission
-
Debug a permission issue - Troubleshoot why a user can't access something
-
Understand the permission flow - Learn how permissions work end-to-end
-
Work with custom roles - Create or modify custom roles with specific scopes
Quick Reference
Key Files
Purpose Location
Scope definitions packages/common/src/authorization/scopes.ts
CASL types packages/common/src/authorization/types.ts
Ability builder packages/common/src/authorization/index.ts
System role abilities packages/common/src/authorization/projectMemberAbility.ts
Role-to-scope mapping packages/common/src/authorization/roleToScopeMapping.ts
Scope-to-CASL conversion packages/common/src/authorization/scopeAbilityBuilder.ts
Common Patterns
Backend permission check:
import { subject } from '@casl/ability'; import { ForbiddenError } from '@lightdash/common';
if (user.ability.cannot('manage', subject('Dashboard', { projectUuid }))) { throw new ForbiddenError('You do not have permission'); }
Frontend permission check:
const { user } = useUser();
if (user?.ability.can('manage', 'Dashboard')) { return <EditButton />; }
or wrap in a CASL component:
import { Can } from '../../providers/Ability';
<Can I="manage" a="Dashboard"> <EditButton /> </Can>
Full Documentation
For comprehensive documentation, read: .context/PERMISSIONS.md
This includes:
-
Architecture diagram showing the complete permission flow
-
All scope groups and modifiers (@self, @public, @space, etc.)
-
Database schema for custom roles
-
Step-by-step guide to add new scopes
-
Troubleshooting guide
Adding a New Scope (Quick Guide)
- Define scope in packages/common/src/authorization/scopes.ts :
{ name: 'manage:NewFeature', description: 'Description for custom role UI', isEnterprise: false, group: ScopeGroup.PROJECT_MANAGEMENT, getConditions: (context) => [addUuidCondition(context)], }
Add subject (if new) in packages/common/src/authorization/types.ts
Add to system role in packages/common/src/authorization/roleToScopeMapping.ts
Update ability builder in packages/common/src/authorization/projectMemberAbility.ts
Enforce in service with user.ability.cannot() check
Add frontend check with user?.ability.can()
Debugging Permission Issues
When a user gets "ForbiddenError":
-
Check scope exists - Is the scope defined in scopes.ts ?
-
Check role assignment - Does the user's role include this scope?
-
Check conditions - Do the CASL conditions match the resource?
-
Check enterprise flag - Is isEnterprise: true but deployment isn't enterprise?
-
Check subject name - Case-sensitive match in CaslSubjectNames ?
Use grep to find where the permission is checked:
grep -r "ability.cannot.'manage'.'YourSubject'" packages/backend/src/services/
Please describe what you're trying to accomplish, or ask me to explain any aspect of the permissions system.