Return inherited members of approval groups as approvers
What does this MR do and why?
- Contributes to #255981 (closed)
- Feature flag: #368117 (closed)
Problem
If the subgroup is an approval group, then indirect members of this group (direct members of parent group) don't show up as approvers.
Solution
- Return group members not only from approval groups but also from their ancestors.
- Add filter for group members to return only users with
developer
or higher permissions
Database
Click to expand
SELECT
"users".*
FROM ((
SELECT
"users".*
FROM
"users"
INNER JOIN "approval_project_rules_users" ON "users"."id" = "approval_project_rules_users"."user_id"
WHERE
"approval_project_rules_users"."approval_project_rule_id" = 72703)
UNION (
SELECT
"users".*
FROM
"users"
WHERE
"users"."id" IN (
SELECT
"members"."user_id"
FROM
"members"
LEFT OUTER JOIN "users" ON "users"."id" = "members"."user_id"
WHERE
"members"."type" = 'GroupMember'
AND "members"."source_type" = 'Namespace'
AND (("members"."user_id" IS NULL
AND "members"."invite_token" IS NOT NULL)
OR "users"."state" = 'active')
AND "members"."requested_at" IS NULL
AND (members.access_level > 5)
AND "members"."access_level" IN (50, 40, 30)
AND "members"."source_id" IN ( WITH RECURSIVE "base_and_ancestors" AS ((
SELECT
"namespaces"."id",
"namespaces"."name",
"namespaces"."path",
"namespaces"."owner_id",
"namespaces"."created_at",
"namespaces"."updated_at",
"namespaces"."type",
"namespaces"."description",
"namespaces"."avatar",
"namespaces"."membership_lock",
"namespaces"."share_with_group_lock",
"namespaces"."visibility_level",
"namespaces"."request_access_enabled",
"namespaces"."ldap_sync_status",
"namespaces"."ldap_sync_error",
"namespaces"."ldap_sync_last_update_at",
"namespaces"."ldap_sync_last_successful_update_at",
"namespaces"."ldap_sync_last_sync_at",
"namespaces"."description_html",
"namespaces"."lfs_enabled",
"namespaces"."parent_id",
"namespaces"."shared_runners_minutes_limit",
"namespaces"."repository_size_limit",
"namespaces"."require_two_factor_authentication",
"namespaces"."two_factor_grace_period",
"namespaces"."cached_markdown_version",
"namespaces"."project_creation_level",
"namespaces"."runners_token",
"namespaces"."file_template_project_id",
"namespaces"."saml_discovery_token",
"namespaces"."runners_token_encrypted",
"namespaces"."custom_project_templates_group_id",
"namespaces"."auto_devops_enabled",
"namespaces"."extra_shared_runners_minutes_limit",
"namespaces"."last_ci_minutes_notification_at",
"namespaces"."last_ci_minutes_usage_notification_level",
"namespaces"."subgroup_creation_level",
"namespaces"."emails_disabled",
"namespaces"."max_pages_size",
"namespaces"."max_artifacts_size",
"namespaces"."mentions_disabled",
"namespaces"."default_branch_protection",
"namespaces"."unlock_membership_to_ldap",
"namespaces"."max_personal_access_token_lifetime",
"namespaces"."push_rule_id",
"namespaces"."shared_runners_enabled",
"namespaces"."allow_descendants_override_disabled_shared_runners",
"namespaces"."traversal_ids"
FROM
"namespaces"
INNER JOIN "approval_project_rules_groups" ON "namespaces"."id" = "approval_project_rules_groups"."group_id"
WHERE
"namespaces"."type" = 'Group'
AND "approval_project_rules_groups"."approval_project_rule_id" = 72703)
UNION (
SELECT
"namespaces"."id",
"namespaces"."name",
"namespaces"."path",
"namespaces"."owner_id",
"namespaces"."created_at",
"namespaces"."updated_at",
"namespaces"."type",
"namespaces"."description",
"namespaces"."avatar",
"namespaces"."membership_lock",
"namespaces"."share_with_group_lock",
"namespaces"."visibility_level",
"namespaces"."request_access_enabled",
"namespaces"."ldap_sync_status",
"namespaces"."ldap_sync_error",
"namespaces"."ldap_sync_last_update_at",
"namespaces"."ldap_sync_last_successful_update_at",
"namespaces"."ldap_sync_last_sync_at",
"namespaces"."description_html",
"namespaces"."lfs_enabled",
"namespaces"."parent_id",
"namespaces"."shared_runners_minutes_limit",
"namespaces"."repository_size_limit",
"namespaces"."require_two_factor_authentication",
"namespaces"."two_factor_grace_period",
"namespaces"."cached_markdown_version",
"namespaces"."project_creation_level",
"namespaces"."runners_token",
"namespaces"."file_template_project_id",
"namespaces"."saml_discovery_token",
"namespaces"."runners_token_encrypted",
"namespaces"."custom_project_templates_group_id",
"namespaces"."auto_devops_enabled",
"namespaces"."extra_shared_runners_minutes_limit",
"namespaces"."last_ci_minutes_notification_at",
"namespaces"."last_ci_minutes_usage_notification_level",
"namespaces"."subgroup_creation_level",
"namespaces"."emails_disabled",
"namespaces"."max_pages_size",
"namespaces"."max_artifacts_size",
"namespaces"."mentions_disabled",
"namespaces"."default_branch_protection",
"namespaces"."unlock_membership_to_ldap",
"namespaces"."max_personal_access_token_lifetime",
"namespaces"."push_rule_id",
"namespaces"."shared_runners_enabled",
"namespaces"."allow_descendants_override_disabled_shared_runners",
"namespaces"."traversal_ids"
FROM
"namespaces",
"base_and_ancestors"
WHERE
"namespaces"."type" = 'Group'
AND "namespaces"."id" = "base_and_ancestors"."parent_id"))
SELECT
"id"
FROM
"base_and_ancestors" AS "namespaces")))) users
https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/11066/commands/39646
Screenshots or screen recordings
Before | After |
---|---|
How to set up and validate locally
-
Enable feature flags
Feature.enable(:subgroups_approval_rules) Feature.enable(:use_inherited_permissions_for_approval_rules)
-
Login as an admin
-
Create "Group A"
-
Create "Group B" as a subgroup of "Group A" ( Group A / Group B )
-
Create "Project" as a project of "Group A" ( Group A / Project )
-
Add "User 1" with Developer permissions to "Group A" (http://127.0.0.1:3000/groups/group-a/-/group_members)
-
Add "Group B" with Developer permissions as a member group of "Project" ( http://127.0.0.1:3000/group-a/project/-/project_members?tab=groups )
-
Create approval rule and select "Group B" as an approval (http://127.0.0.1:3000/group-a/project/edit)
-
Login as a "User 1"
-
You should have access to both groups and the project
-
Visit http://127.0.0.1:3000/api/v4/projects/group-a%2Fproject/approval_rules
-
You should see
User 1
in eligible approvals list in the response
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.