Back to Discourse

Discourse ACL Authoring

.skills/discourse-acl-authoring/SKILL.md

2026.7.0-latest3.5 KB
Original Source

Discourse ACL Authoring

Use this skill before adding or reviewing ACL-backed resource permissions in Discourse core or plugins.

Workflow

  1. Identify the target resource and desired permission vocabulary. Prefer simple strings such as view, edit, and manage, but confirm the target's domain semantics.
  2. Read references/backend-model.md when touching AccessControlList, AclTarget, Acl::Target, Acl::User, Guardian, User#permission_acl, or target visibility queries.
  3. Read references/manager-and-plugin-integration.md when writing ACLs, creating target models, registering plugin target classes, serializing ACLs, or integrating services/controllers.
  4. Read references/frontend-d-access-control.md when using or customizing DAccessControl.
  5. Read references/testing-and-review.md when adding specs or reviewing an ACL feature.
  6. Also load .skills/discourse-service-authoring for Service::Base changes, .skills/discourse-writing-rspec-tests for RSpec, and .skills/discourse-writing-js-tests for QUnit.

Non-Negotiables

  • Put authorization at the caller boundary before AccessControlListManager.call; the manager is a destructive replacement service and currently assumes the caller already authorized the actor.
  • Always call AccessControlListManager for writes, including empty submitted ACL arrays, so mandatory ACLs are injected and old rows are replaced intentionally.
  • Treat AccessControlList.flattened_list as the API shape for UI/client payloads and AccessControlList.expand_list_for_bulk_insert as the DB insert shape.
  • Use Guardian ACL helpers or AclTarget visibility scopes for checks and target scopes instead of hand-querying ACL tables in controllers.
  • Register plugin ACL targets with DiscoursePluginRegistry.register_acl_target_class so Site#access_control exposes mandatory and banned ACL metadata to the frontend.
  • Treat banned_acl as a server-enforced restriction. DAccessControl hides banned permission choices for UX, but AccessControlListManager must still reject submitted banned entries.
  • Do not claim user ACL support is complete. Current authoring and UI flows are group-first; allowed_user_ids and user entries exist in lower layers but are not fully wired by expand_list, preload_allowed, cleanup jobs, or DAccessControl.

Local Anchors

  • Model and relation API: app/models/access_control_list.rb
  • Target concern: app/models/concerns/acl_target.rb
  • Permission lookup objects: lib/acl/target.rb, lib/acl/user.rb
  • Guardian helpers: lib/guardian.rb
  • User ACL cache: app/models/user.rb
  • Write manager: app/services/access_control_list_manager.rb
  • Deleted group cleanup: app/jobs/regular/cleanup_acls_for_deleted.rb, app/models/group.rb
  • Site metadata: app/models/site.rb, app/serializers/site_serializer.rb
  • Frontend component: frontend/discourse/app/ui-kit/d-access-control.gjs
  • Core specs: spec/models/access_control_list_spec.rb, spec/models/concerns/acl_target_spec.rb, spec/lib/acl/target_spec.rb, spec/jobs/regular/cleanup_acls_for_deleted_spec.rb, spec/services/access_control_list_manager_spec.rb, spec/serializers/site_serializer_spec.rb, frontend/discourse/tests/integration/components/d-access-control-test.gjs
  • Plugin consumer example: plugins/discourse-kanban or the external discourse-kanban checkout when present.