.ai/principles/distilled/permissions-fundamentals.md
current_user, ensure it is responsible for authorization.push_frontend_ability (available on controllers inheriting from ApplicationController) to expose abilities to JavaScript; access them via gon.abilities.<camelCaseName> (DO NOT use gon.abilities.<snake_case_name>).action_resource(_subresource) for all permissions (both assignable and raw).create, read, update, delete — unless a documented exception applies; obtain Authorization team approval for any action outside this set.admin, change, configure, destroy, edit, list, manage, modify, set, view, or write.read_project, not read_projects).project, group, user) into the permission name; pass the subject in the can? call to determine scope instead (e.g., use read_insights_dashboard not read_project_insights_dashboard).archive_project), a relationship change (transfer_project), or a high-impact/irreversible operation (purge_maven_virtual_registry_cache) already established in GitLab domain language._) following the pattern _<action>_<qualifier>_<resource> (e.g., _read_authored_issue).prevent rules instead.config/authz/permissions/<resource>/_<action>_<qualifier>.yml.confidential, authored, assigned); DO NOT use qualifiers that describe the actor.config/authz/roles/ with the fields name, description, inherits_from, and optionally raw_permissions and permissions.raw_permissions, expanded permissions (assignable groups), and all permissions inherited recursively from parent roles.raw_permissions only for existing permissions that need to be moved into the role definition.GITLAB_DEBUG_POLICIES=true to trace where a permission is checked../ee/bin/custom-ability <ABILITY_NAME> to generate the YAML configuration file for a new custom ability under ee/config/custom_abilities/.bundle exec rails generate gitlab:custom_roles:code --ability <ABILITY_NAME> to update the permissions validation schema and create a spec file.project_permissions and group_permissions fields; DO NOT manually add policy rules to ProjectPolicy or GroupPolicy for custom abilities.wip: true in its YAML when implementing across multiple MRs; remove the wip: key when the ability is ready to ship.read_* for all view-related actions and admin_* for object updates; avoid introducing additional abilities unless necessary.admin_* custom abilities declare read_* as a requirement in the YAML requirements field.MemberRoles factory (ee/spec/factories/member_roles.rb) and add request specs under ee/spec/requests/custom_roles/<ABILITY_NAME>/request_spec.rb.bundle exec rake gitlab:custom_roles:compile_docs and bundle exec rake gitlab:graphql:compile_docs to update documentation after adding a custom ability.config/authz/permission_groups/internal/<resource>/<name>.yml with description and permissions fields.DeclarativePolicy policies by calling Authz::PermissionGroups::Internal.get('<identifier>') and splatting the result into a prevent rule.project_authorizations table; the highest permission across group and project membership is the effective access level.spec/policies/ (CE) or ee/spec/policies/ (EE).describe block per permission; DO NOT group multiple permissions into a single block.where table syntax to test each role explicitly, and include every role in the table — DO NOT omit roles expected to be disallowed.describe block using let_it_be; DO NOT rely on a subject defined at a higher scope.private_project, archived_project) rather than a generic project.For the full picture, see: