agskills.dev
MARKETPLACE

ld-permissions

Guide for Lightdash's CASL-based authorization system. Use when working with scopes, custom roles, abilities, permissions, ForbiddenError, authorization, or access control. Helps with adding new scopes, debugging permission issues, understanding the permission flow, and creating custom roles.

lightdash5.5k674

Vista Previa

SKILL.md
Metadata
name
ld-permissions
description
Guide for Lightdash's CASL-based authorization system. Use when working with scopes, custom roles, abilities, permissions, ForbiddenError, authorization, or access control. Helps with adding new scopes, debugging permission issues, understanding the permission flow, and creating custom roles.
allowed-tools
Read, Grep, Glob, Task

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?

  1. Add a new scope/permission - Step-by-step guide to add a new permission
  2. Debug a permission issue - Troubleshoot why a user can't access something
  3. Understand the permission flow - Learn how permissions work end-to-end
  4. Work with custom roles - Create or modify custom roles with specific scopes

Quick Reference

Key Files

PurposeLocation
Scope definitionspackages/common/src/authorization/scopes.ts
CASL typespackages/common/src/authorization/types.ts
Ability builderpackages/common/src/authorization/index.ts
System role abilitiespackages/common/src/authorization/projectMemberAbility.ts
Role-to-scope mappingpackages/common/src/authorization/roleToScopeMapping.ts
Scope-to-CASL conversionpackages/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)

  1. 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)], }
  1. Add subject (if new) in packages/common/src/authorization/types.ts

  2. Add to system role in packages/common/src/authorization/roleToScopeMapping.ts

  3. Update ability builder in packages/common/src/authorization/projectMemberAbility.ts

  4. Enforce in service with user.ability.cannot() check

  5. Add frontend check with user?.ability.can()

Debugging Permission Issues

When a user gets "ForbiddenError":

  1. Check scope exists - Is the scope defined in scopes.ts?
  2. Check role assignment - Does the user's role include this scope?
  3. Check conditions - Do the CASL conditions match the resource?
  4. Check enterprise flag - Is isEnterprise: true but deployment isn't enterprise?
  5. 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.