Restrict feature flag management by minimum role
What does this MR do and why?
Today, any project member with the Developer role or above can create, toggle, and delete feature flags, and there is no way to restrict this. This is a long-standing ask (see the References below) from teams with change-control or compliance requirements, particularly for production flags.
This MR is Phase 1 (backend gate) of adding feature flag management permissions. It introduces a project setting — the minimum role required to manage feature flags — and enforces it in ProjectPolicy.
Behavior
- New project setting
feature_flags_minimum_role:developer(default) ·maintainer·owner·no_one_allowed. - Members below the configured role can still read feature flags and their on/off state, but cannot create, update (toggle), or delete them, and cannot manage feature flag user lists (a strategy can target a user list, so editing one changes which users a flag applies to).
- Admins bypass the restriction, except when it is set to
no_one_allowed. - A meta-permission controls who can change the setting:
update_feature_flags_minimum_role_settingis granted to Maintainers, but loosening the setting away from a privileged state (owner/no_one_allowed) requires_update_privileged_feature_flags_minimum_role_setting, granted to Owners. This prevents a Maintainer from quietly weakening an Owner-set restriction.
Why no_one_allowed
no_one_allowed is a true freeze: it blocks feature flag management for everyone, including instance administrators. This is deliberate. It exists for change-control and compliance needs — locking down all feature flag changes during a change-freeze window, an active incident, or an audit.
It is not a lockout risk, because changing the setting is gated separately from managing flags. An Owner lifts the freeze via update_feature_flags_minimum_role_setting first, then manages flags. That separation is also why the value sits at the top of the scale (developer → maintainer → owner → no_one_allowed) rather than being a distinct boolean.
Design
This mirrors the shipped ci_pipeline_variables_minimum_override_role pattern. All feature flag write surfaces (REST API, controllers, services, serializers) already authorize through the create_feature_flag / update_feature_flag / admin_feature_flag / destroy_feature_flag abilities, so a single policy gate covers every surface.
ProjectSetting#feature_flags_management_allowed?(access_level, user)performs the role comparison.ProjectPolicyprevents the write abilities when the member is below the threshold (behind thefeature_flag_management_permissionsfeature flag).
Not a breaking change
The behavior is guarded by the default-off feature_flag_management_permissions feature flag, and the setting's default value (developer) preserves today's behavior. No project changes behavior until an Owner/Maintainer opts in.
Scope of this MR
Backend gate only. Exposing the setting via GraphQL/REST (with UpdateService enforcement of the meta-permission) and a settings UI will follow in subsequent MRs. There are no user-facing docs in this MR because the setting is not yet writable through the API or UI; docs will accompany those MRs.
Screenshots or screen recordings
N/A — backend only, no UI in this MR.
How to set up and validate locally
- Create a project and add a Developer and a Maintainer.
- Enable the flag:
Feature.enable(:feature_flag_management_permissions). - Set
project.project_setting.update!(feature_flags_minimum_role: :maintainer). - As the Developer,
Ability.allowed?(developer, :update_feature_flag, project)isfalse; as the Maintainer it istrue.:read_feature_flagremainstruefor both.
Covered by specs:
spec/models/project_setting_spec.rb— role threshold logic and privileged-state helper.spec/policies/project_policy_spec.rb— full role × minimum-role matrix, feature-flag-off short-circuit, read-still-allowed, admin bypass, and the meta-permission.
References
- Related to #8239
MR acceptance checklist
This change touches authorization; a review of the policy changes is appreciated.