mirror of
https://github.com/discourse/discourse.git
synced 2026-08-06 00:15:24 +08:00
Followup 5823e4e3b2,
this commit allows the addition of users along with
groups to access control lists, modifying DAccessControl
to support selecting a user or group from the same
search input.
Shown here is a mix of user & group permissions in the
`DAccessControl` component:
<img width="611" height="664" alt="image"
src="https://github.com/user-attachments/assets/c25e13b0-8885-4ce7-972c-5116f3acb094"
/>
When the search opens, we show the site's groups that
the user can see as preloaded values, showing only the
group name for clarity:
<img width="612" height="389" alt="image"
src="https://github.com/user-attachments/assets/df93a94b-918e-4fed-b045-ac72f02aca41"
/>
When searching a GET request is sent and users are included
in search results.
<img width="608" height="404" alt="image"
src="https://github.com/user-attachments/assets/ff17e6c0-2505-480e-ae25-e6f8309555bc"
/>
---------
Co-authored-by: Jordan Vidrine <jordan@jordanvidrine.com>
5.4 KiB
Vendored
5.4 KiB
Vendored
Testing and Review Checklist
Use this reference when adding specs or reviewing ACL-backed features.
Backend Specs
For target models:
- Include
AclTarget. - Cover
mandatory_aclif the target defines one. - Cover domain helper methods such as
anonymous_can_read?orcan_write?that wrappermission_acl. - Cover
.with_acl_permissionand.with_any_acl_permissionswhen index/list actions depend on the shared scopes.
For Guardian methods:
- Cover anonymous, logged-in, direct group membership, writer-implies-reader, and manager cases as applicable.
- Cover target-id list helpers when an index action scopes resources by ACL.
- If pseudo groups are involved, cover
granular_anonymous_and_logged_in_groups_permissionsbehavior when relevant.
For services using AccessControlListManager:
- Creation with omitted
aclpersists mandatory ACLs. - Creation with
acl: []persists mandatory ACLs or fails closed when no mandatory ACL exists. - Update with explicit
acl: []replaces existing ACLs with mandatory ACLs. - Submitted ACLs that match the target class's
banned_aclfail thehas_no_banned_aclpolicy. - Update with a non-empty ACL replaces old rows and logs/records permission history if the feature has history.
- After a successful manager call, the target instance has been reloaded and should not retain a stale
permission_aclcache. - Unauthorized actors fail before the manager mutates ACL rows.
- Invalid group IDs fail at the service/contract boundary when user input can supply IDs.
For cleanup behavior:
- Destroying a group enqueues
:cleanup_acls_for_deleted. Jobs::CleanupAclsForDeletedremoves the group id from ACL rows.- Destroying a user enqueues
:cleanup_acls_for_deleted. Jobs::CleanupAclsForDeletedremoves the user id from ACL rows.- ACL rows with no remaining group or user ids are deleted.
- ACL rows that still have user ids are preserved.
- ACL rows that still have group ids are preserved.
For lookup objects:
Acl::Target#permission_group_idsreturns[]for missing permissions and a defensive copy for present permissions.Acl::Target#group_ids_with_any_permissionreplaces the oldermulti_permission_group_idsname.Acl::Target#permission_user_idsand#user_ids_with_any_permissionreturn[]for missing permissions and defensive array copies for present permissions.Acl::User#target_ids_with_permissionand#target_ids_with_any_permissionsreturn defensive array copies.
For migration specs:
- Blank legacy arrays.
- Existing read/write/manage group values.
- Default setting fallback when no setting row exists.
- Raw setting rows that omit mandatory values.
Frontend Specs
For DAccessControl consumers:
- Rendered permissions match target-specific labels/descriptions.
- Mandatory ACL from
site.access_control.mandatory_acl[targetKey]appears and is locked. - Banned ACL from
site.access_control.banned_acl[targetKey]removes matching permission options for the matching grantee. - Existing rows with the same group as a mandatory row are not duplicated.
onChangewrites updated ACL arrays into parent state.- The final save payload includes the intended ACL array.
Use .skills/discourse-writing-js-tests for QUnit patterns.
Review Checklist
Ask these questions during code review:
- Is every ACL write routed through
AccessControlListManager? - Does the caller authorize the actor before the manager can replace ACL rows?
- Does the service distinguish omitted ACL params from explicit empty ACL params when that matters?
- Are mandatory ACLs enforced in both backend writes and frontend display?
- Are banned ACLs enforced in backend writes and filtered from the frontend for matching grantees?
- Does the UI
@aclTargetstring match the target'sacl_target_key? - Is ACL serialization limited to users who can manage the specific target?
- Are list/index queries using
Target.with_acl_permission,Target.with_any_acl_permissions,guardian.target_ids_with_acl_permission, ortarget_ids_with_any_acl_permissionsinstead of ad hoc SQL? - Are group and user IDs validated before persistence when they come from params?
- Are plugin target classes registered with
DiscoursePluginRegistry.register_acl_target_class? - Do tests cover anonymous users and pseudo-group semantics if
anonymous_users,logged_in_users, oreveryonecan appear in ACLs? - Do tests cover stale group rows or missing plugin target classes if the code serializes ACLs that may outlive their original target or group?
Known Sharp Edges
AccessControlListManagercurrently assumes caller-side authorization.- Backend user ACL support is partial: expansion, flattening, preloading, lookup helpers, matching scopes, and cleanup jobs handle
allowed_user_ids, but shared UI authoring is not complete. DAccessControlremains group-first and does not provide complete user ACL editing yet.DAccessControlinjects mandatory rows for display but does not notify the parent on render.DAccessControlfilters banned permission options for UX, but hidden options are not authorization. The manager policy is the enforcement point.- Mandatory ACLs from site settings need extra care in migrations because raw stored settings may omit
mandatory_values. flattened_listskips stale group IDs, stale user IDs, and unknown target classes, so a missing row can disappear from serialized ACL output while cleanup or validation catches up.