External users can't set default_branch_protection when creating subgroup.

Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.

Summary

When a service account (or, more generally, an external user) creates a new subgroup, the default_branch_protection and default_branch_protection_default attributes are not respected. This was first observed specifically while setting default_branch_protection = 0 on the gitlab_group resource of the GitLab Terraform Provider. In this case, the initial resource was created with default_branch_protection = 2 (the default) and then a second apply was required to update to default_branch_protection = 0.

The only other discussion I've seen of this behavior was on an old, closed GitLab Terraform Provider issue, which was closed as not reproducible. I believe that I have identified the critical piece of information missing from that report - the problematic behavior only happens with external users, due to a permission check that is too strict, as described below.

Steps to Reproduce

  1. Obtain a personal access token for an external user (e.g. service account).
  2. Use the (sub)groups API to create a new subgroup, specifying a non-default value (e.g. 0) for the default_branch_protection attribute.
  3. Use the groups API to check the default_branch_protection attribute.
  4. Use the groups API to update the default_branch_protection attribute of the group to the same value (e.g. 0) specified in the initial create (sub)group request.
  5. Use the groups API to check the default_branch_protection attribute.

What is the current bug behavior?

After the initial creation of the subgroup, the value of default_branch_protection will incorrectly be the default value (2, typically), not the value specified in the API request (0, in the steps above). After the update request, default_branch_protection is correctly updated to the specified value. While the end result is the same, this behavior complicates tooling such as Terraform.

What is the expected correct behavior?

The value of default_branch_protection (and default_branch_protection_options) should be respected in the initial (sub)group creation, given that the user has permission to update the attribute after creation. Of note, the correct behavior is observed when the personal access token belongs to a non-external user.

This bug happens on GitLab.com.

Possible Fixes (implementation plan)

I am nearly certain that the issue is caused by an overly-strict permission check in the group create_service.rb:

      unless can?(current_user, :create_group_with_default_branch_protection)
        params.delete(:default_branch_protection)
        params.delete(:default_branch_protection_defaults)
      end

The :create_group_with_default_branch_protection permission is defined in global_policy.rb as:

  rule { can?(:create_group) }.policy do
    enable :create_group_with_default_branch_protection
  end

I've confirmed in user.rb that :create_group is always false for external users.

The reason that updating the attribute post-creation works is that the policy there is scoped to the specific group, as is the check in the group update_service.rb:

      unless can?(current_user, :update_default_branch_protection, group)
        params.delete(:default_branch_protection)
        params.delete(:default_branch_protection_defaults)
      end

Given the above, I believe the fix for this issue to involve introducing a similar, group-scoped permission for creating subgroups with default branch protection (e.g. :create_subgroup_with_default_branch_protection). For consistency, this permission should be, by default, granted whenever the current :create_subgroup and :update_default_branch_protection permissions are granted. The above check in remove_unallowed_params in create_service.rb will then need to bifurcate to handle both the top-level and subgroup cases.

Expectations

Edited by 🤖 GitLab Bot 🤖