From 62c882adbcb06d3244286d8af0bdea0a0d0c5d8d Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Fri, 15 Aug 2025 09:19:21 +0200 Subject: [PATCH 01/12] Step-up auth: Add database mode and setting in UI In a previous MR, we introduced step-up authentication for the admin mode, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171643. Now, we want to extend this functionality to namespaces (groups) in GitLab, see https://gitlab.com/gitlab-org/gitlab/-/issues/556943. The implementation plan for this feature includes several key changes that will be implemented in several MRs. Future MRs will implement the enforcement logic and inheritance logic (considering settings from parent groups). This MR adds foundational database and UI support for step-up authentication at the namespace level: - Add `step_up_auth_required_oauth_provider` column to namespace_settings with validation against enabled OAuth providers - Extend group settings UI with step-up authentication configuration section including inheritance indicators - Implement feature flag protection for gradual rollout **Feature Flag:** `omniauth_step_up_auth_for_namespace` (disabled by default) Changelog: added --- app/controllers/concerns/groups/params.rb | 3 +- app/helpers/groups_helper.rb | 14 ++++ app/models/group.rb | 2 + app/models/namespace_setting.rb | 8 ++ .../groups/settings/_permissions.html.haml | 1 + .../_step_up_authentication.html.haml | 19 +++++ .../omniauth_step_up_auth_for_namespace.yml | 9 ++ ...ed_oauth_provider_to_namespace_settings.rb | 17 ++++ db/schema_migrations/20250729162745 | 1 + db/structure.sql | 2 + .../auth/oidc/step_up_authentication.rb | 22 +++++ locale/gitlab.pot | 15 ++++ .../settings/step_up_authentication_spec.rb | 61 ++++++++++++++ spec/helpers/groups_helper_spec.rb | 59 +++++++++++++ .../auth/oidc/step_up_authentication_spec.rb | 64 ++++++++++++++ spec/models/namespace_setting_spec.rb | 32 +++++++ spec/services/groups/update_service_spec.rb | 84 +++++++++++++++++++ 17 files changed, 412 insertions(+), 1 deletion(-) create mode 100644 app/views/groups/settings/_step_up_authentication.html.haml create mode 100644 config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml create mode 100644 db/migrate/20250729162745_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb create mode 100644 db/schema_migrations/20250729162745 create mode 100644 spec/features/groups/settings/step_up_authentication_spec.rb diff --git a/app/controllers/concerns/groups/params.rb b/app/controllers/concerns/groups/params.rb index 95cf0571fd7e92..77a59a1ac2addc 100644 --- a/app/controllers/concerns/groups/params.rb +++ b/app/controllers/concerns/groups/params.rb @@ -56,7 +56,8 @@ def group_params_attributes :crm_enabled, :crm_source_group_id, :force_pages_access_control, - :enable_namespace_descendants_cache + :enable_namespace_descendants_cache, + :step_up_auth_required_oauth_provider ] + [group_feature_attributes: group_feature_attributes] end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index da9c8c031d8c50..24421a89a5ee03 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -254,6 +254,14 @@ def group_merge_requests(group) MergeRequestsFinder.new(current_user, group_id: group.id, include_subgroups: true, non_archived: true).execute end + def step_up_auth_provider_options_for_select + available_step_up_auth_providers_for_namespace.map do |provider| + provider_config = Gitlab::Auth::OAuth::Provider.config_for(provider.to_s) + provider_label = provider_config[:label].presence || provider.to_s.humanize + [provider_label, provider.to_s] + end + end + private def group_title_link(group, hidable: false, show_avatar: false) @@ -331,6 +339,12 @@ def localized_jobs_to_be_done_choices other: _('A different reason') }.with_indifferent_access.freeze end + + def available_step_up_auth_providers_for_namespace + Gitlab::Auth::Oidc::StepUpAuthentication.enabled_providers( + scope: Gitlab::Auth::Oidc::StepUpAuthentication::STEP_UP_AUTH_SCOPE_NAMESPACE + ) + end end GroupsHelper.prepend_mod_with('GroupsHelper') diff --git a/app/models/group.rb b/app/models/group.rb index b64ef3ba9fc546..149ecb09e89d20 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -182,6 +182,8 @@ def self.supported_keyset_orderings :require_dpop_for_manage_api_endpoints=, :seat_control, :setup_for_company, + :step_up_auth_required_oauth_provider, + :step_up_auth_required_oauth_provider=, to: :namespace_settings ) diff --git a/app/models/namespace_setting.rb b/app/models/namespace_setting.rb index 89d4dc2c35482d..e925c1d625bca4 100644 --- a/app/models/namespace_setting.rb +++ b/app/models/namespace_setting.rb @@ -58,10 +58,18 @@ class NamespaceSetting < ApplicationRecord validate :validate_enterprise_bypass_expires_at, if: ->(record) { record.allow_enterprise_bypass_placeholder_confirmation? && (record.new_record? || record.will_save_change_to_enterprise_bypass_expires_at?) } + validates :step_up_auth_required_oauth_provider, presence: true, allow_nil: true + validates :step_up_auth_required_oauth_provider, inclusion: { in: ->(_) { + Gitlab::Auth::Oidc::StepUpAuthentication + .enabled_providers(scope: Gitlab::Auth::Oidc::StepUpAuthentication::STEP_UP_AUTH_SCOPE_NAMESPACE) + .map(&:to_s) + } }, allow_nil: true sanitizes! :default_branch_name nullify_if_blank :default_branch_name + nullify_if_blank :step_up_auth_required_oauth_provider + before_validation :set_pipeline_variables_default_role, on: :create after_update :invalidate_namespace_descendants_cache, if: -> { saved_change_to_archived? } diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml index d5abcfab34247c..147f47ab59b777 100644 --- a/app/views/groups/settings/_permissions.html.haml +++ b/app/views/groups/settings/_permissions.html.haml @@ -51,6 +51,7 @@ = render_if_exists 'groups/settings/extended_grat_expiry_webhook_execute', f: f, group: @group = render_if_exists 'groups/settings/enforce_ssh_certificates', f: f, group: @group = render 'groups/settings/two_factor_auth', f: f, group: @group + = render 'groups/settings/step_up_authentication', f: f, group: @group = render 'groups/settings/membership', f: f, group: @group = render_if_exists 'groups/settings/placeholder_confirmation_bypass', f: f, group: @group = render_if_exists 'groups/settings/remove_dormant_members', f: f, group: @group diff --git a/app/views/groups/settings/_step_up_authentication.html.haml b/app/views/groups/settings/_step_up_authentication.html.haml new file mode 100644 index 00000000000000..f1c194492a2d38 --- /dev/null +++ b/app/views/groups/settings/_step_up_authentication.html.haml @@ -0,0 +1,19 @@ +- return if Feature.disabled?(:omniauth_step_up_auth_for_namespace, current_user) + +%h5= s_('GroupSettings|Step-up Authentication') +%p + = link_to s_('GroupSettings|Learn more about step-up authentication.'), help_page_path('administration/auth/oidc.md'), target: '_blank', rel: 'noopener noreferrer' + +.form-group + = f.select :step_up_auth_required_oauth_provider, + step_up_auth_provider_options_for_select, + { include_blank: s_('GroupSettings|Disabled') }, + { class: 'form-control gl-form-select' } + + .form-text.gl-text-subtle + = s_('GroupSettings|When enabled, users must complete step-up authentication to access this group and its resources.') + + - if group.namespace_settings.step_up_auth_required_oauth_provider.present? + .form-text.gl-text-success.gl-mt-2 + = sprite_icon('check-circle', css_class: 'gl-mr-2') + = s_('GroupSettings|Step-up authentication is currently enabled for this group.') diff --git a/config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml b/config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml new file mode 100644 index 00000000000000..4331be3a45a6a4 --- /dev/null +++ b/config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml @@ -0,0 +1,9 @@ +--- +name: omniauth_step_up_auth_for_namespace +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/474650 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176295 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/510951 +milestone: '18.3' +group: group::authorization +type: gitlab_com_derisk +default_enabled: false diff --git a/db/migrate/20250729162745_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb b/db/migrate/20250729162745_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb new file mode 100644 index 00000000000000..f30ca2a97927e6 --- /dev/null +++ b/db/migrate/20250729162745_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddStepUpAuthRequiredOauthProviderToNamespaceSettings < Gitlab::Database::Migration[2.3] + milestone '18.3' + + disable_ddl_transaction! + + def up + add_column :namespace_settings, :step_up_auth_required_oauth_provider, :text + add_text_limit :namespace_settings, :step_up_auth_required_oauth_provider, 255 + end + + def down + remove_text_limit :namespace_settings, :step_up_auth_required_oauth_provider + remove_column :namespace_settings, :step_up_auth_required_oauth_provider + end +end diff --git a/db/schema_migrations/20250729162745 b/db/schema_migrations/20250729162745 new file mode 100644 index 00000000000000..3ed94fdbd09b87 --- /dev/null +++ b/db/schema_migrations/20250729162745 @@ -0,0 +1 @@ +757c6e9a42e4fb0f238961528d171834b70944e55293d403e4278b732d3555cf \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 739ba0424b4f4a..8e1bd9253eff7b 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -19511,7 +19511,9 @@ CREATE TABLE namespace_settings ( allow_personal_snippets boolean DEFAULT true NOT NULL, auto_duo_code_review_enabled boolean, lock_auto_duo_code_review_enabled boolean DEFAULT false NOT NULL, + step_up_auth_required_oauth_provider text, CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)), + CONSTRAINT check_d9644d516f CHECK ((char_length(step_up_auth_required_oauth_provider) <= 255)), CONSTRAINT check_namespace_settings_security_policies_is_hash CHECK ((jsonb_typeof(security_policies) = 'object'::text)), CONSTRAINT namespace_settings_unique_project_download_limit_alertlist_size CHECK ((cardinality(unique_project_download_limit_alertlist) <= 100)), CONSTRAINT namespace_settings_unique_project_download_limit_allowlist_size CHECK ((cardinality(unique_project_download_limit_allowlist) <= 100)) diff --git a/lib/gitlab/auth/oidc/step_up_authentication.rb b/lib/gitlab/auth/oidc/step_up_authentication.rb index 833dd12ba577f4..5b0dc74aa05d0e 100644 --- a/lib/gitlab/auth/oidc/step_up_authentication.rb +++ b/lib/gitlab/auth/oidc/step_up_authentication.rb @@ -11,6 +11,12 @@ module StepUpAuthentication SESSION_STORE_KEY = 'omniauth_step_up_auth' STEP_UP_AUTH_SCOPE_ADMIN_MODE = :admin_mode + STEP_UP_AUTH_SCOPE_NAMESPACE = :namespace + + ALLOWED_SCOPES = [ + STEP_UP_AUTH_SCOPE_ADMIN_MODE, + STEP_UP_AUTH_SCOPE_NAMESPACE + ].freeze class << self # Checks if step-up authentication is enabled for the step-up auth scope 'admin_mode' @@ -32,6 +38,12 @@ def enabled_for_provider?(provider_name:, scope: STEP_UP_AUTH_SCOPE_ADMIN_MODE) has_included_claims?(provider_name, scope) end + def enabled_providers(scope: STEP_UP_AUTH_SCOPE_ADMIN_MODE) + oauth_providers.select do |provider| + enabled_for_provider?(provider_name: provider, scope: scope) + end + end + # Verifies if step-up authentication has succeeded for any provider # with the step-up auth scope 'admin_mode' # @@ -75,6 +87,16 @@ def conditions_fulfilled?(oauth_extra_metadata:, provider:, scope: STEP_UP_AUTH_ conditions.present? && conditions.all? end + def step_up_auth_flows(session) + omniauth_step_up_auth_session_data(session) + &.to_h + &.flat_map do |provider, step_up_auth_object| + step_up_auth_object.map do |step_up_auth_scope, _| + build_flow(provider: provider, session: session, scope: step_up_auth_scope) + end + end + end + def build_flow(provider:, session:, scope: STEP_UP_AUTH_SCOPE_ADMIN_MODE) Gitlab::Auth::Oidc::StepUpAuthenticationFlow.new(provider: provider, scope: scope, session: session) end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 0a28f572b1ee4a..0e05df20f23bae 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -31901,6 +31901,9 @@ msgstr "" msgid "GroupSettings|Disable user invitations to groups and projects within %{group}" msgstr "" +msgid "GroupSettings|Disabled" +msgstr "" + msgid "GroupSettings|Emails are not encrypted. Concerned administrators may want to disable diff previews." msgstr "" @@ -31985,6 +31988,9 @@ msgstr "" msgid "GroupSettings|Include diff previews" msgstr "" +msgid "GroupSettings|Learn more about step-up authentication." +msgstr "" + msgid "GroupSettings|Members cannot invite groups outside of %{group} and its subgroups" msgstr "" @@ -32045,6 +32051,12 @@ msgstr "" msgid "GroupSettings|Settings that apply only to enterprise users associated with this group." msgstr "" +msgid "GroupSettings|Step-up Authentication" +msgstr "" + +msgid "GroupSettings|Step-up authentication is currently enabled for this group." +msgstr "" + msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found." msgstr "" @@ -32093,6 +32105,9 @@ msgstr "" msgid "GroupSettings|When disabled, members cannot use runner registration tokens to register runners. Members can use runner authentication tokens instead as the more secure registration method." msgstr "" +msgid "GroupSettings|When enabled, users must complete step-up authentication to access this group and its resources." +msgstr "" + msgid "GroupSettings|You must have the Owner role in the target group" msgstr "" diff --git a/spec/features/groups/settings/step_up_authentication_spec.rb b/spec/features/groups/settings/step_up_authentication_spec.rb new file mode 100644 index 00000000000000..66eddbf580b80b --- /dev/null +++ b/spec/features/groups/settings/step_up_authentication_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Group Step-up Authentication Settings', :js, feature_category: :groups_and_projects do + let_it_be(:group) { create(:group) } + let_it_be(:user) { create(:user, owner_of: group) } + + let(:ommiauth_provider_config_oidc) do + GitlabSettings::Options.new( + name: 'openid_connect', + label: 'OpenID Connect', + step_up_auth: { + namespace: { + id_token: { + required: { + acr: 'gold' + } + } + } + } + ) + end + + before do + sign_in(user) + + stub_omniauth_setting(enabled: true, providers: [ommiauth_provider_config_oidc]) + end + + it 'displays step-up authentication settings in group permissions' do + visit edit_group_path(group, anchor: 'js-permissions-settings') + + expect(page).to have_content('Step-up Authentication') + expect(page).to have_select('group_step_up_auth_required_oauth_provider') + end + + it 'allows enabling step-up authentication' do + visit edit_group_path(group, anchor: 'js-permissions-settings') + + select 'OpenID Connect', from: 'group_step_up_auth_required_oauth_provider' + click_button 'Save changes' + + expect(page).to have_content("Group 'group1' was successfully updated.") + + expect(group.reload.namespace_settings.step_up_auth_required_oauth_provider).to eq('openid_connect') + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(omniauth_step_up_auth_for_namespace: false) + end + + it 'does not display step-up authentication settings' do + visit edit_group_path(group, anchor: 'js-permissions-settings') + + expect(page).not_to have_content('Step-up Authentication') + expect(page).not_to have_select('group_step_up_auth_required_oauth_provider') + end + end +end diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index 31f14a62c6e7cc..ca24b51226fd96 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -829,4 +829,63 @@ expect(helper.group_merge_requests(group)).to contain_exactly(merge_request) end end + + describe '#step_up_auth_provider_options_for_select' do + using RSpec::Parameterized::TableSyntax + + let_it_be(:group) { create(:group) } + let_it_be(:current_user) { create(:user, owner_of: group) } + + let(:omniauth_provider_oidc) do + GitlabSettings::Options.new( + name: "openid_connect", + step_up_auth: { + namespace: { + id_token: { + required: { + acr: 'gold' + } + } + } + } + ) + end + + let(:omniauth_provider_oidc_only_namespace) do + GitlabSettings::Options.new( + name: "openid_connect_only_namespace", + label: "OpenID Connect (Only namespace)", + args: { + strategy_class: 'OmniAuth::Strategies::OpenIDConnect' + }, + step_up_auth: { + namespace: { + id_token: { + required: { + acr: 'gold' + } + } + } + } + ) + end + + subject { helper.step_up_auth_provider_options_for_select } + + before do + allow(helper).to receive(:current_user).and_return(current_user) + + stub_omniauth_setting(enabled: true, providers: providers) + allow(Devise).to receive(:omniauth_providers).and_return(providers.map(&:name)) + end + + where(:providers, :expected_options) do + [ref(:omniauth_provider_oidc), ref(:omniauth_provider_oidc_only_namespace)] | [['Openid connect', 'openid_connect'], ['OpenID Connect (Only namespace)', 'openid_connect_only_namespace']] + [ref(:omniauth_provider_oidc)] | [['Openid connect', 'openid_connect']] + [] | [] + end + with_them do + it { is_expected.to match_array(expected_options) } + end + end end diff --git a/spec/lib/gitlab/auth/oidc/step_up_authentication_spec.rb b/spec/lib/gitlab/auth/oidc/step_up_authentication_spec.rb index e73ce036caa216..9a734f610878d2 100644 --- a/spec/lib/gitlab/auth/oidc/step_up_authentication_spec.rb +++ b/spec/lib/gitlab/auth/oidc/step_up_authentication_spec.rb @@ -302,4 +302,68 @@ it { is_expected.to all be_failed.and(be_enabled_by_config) } end end + + describe '.enabled_providers' do + subject { described_class.enabled_providers(scope: scope) } + + let(:omniauth_provider_oidc) do + GitlabSettings::Options.new( + name: "openid_connect", + step_up_auth: { + admin_mode: { + id_token: { + required: { + acr: 'gold' + } + } + }, + namespace: { + id_token: { + required: { + acr: 'gold' + } + } + } + } + ) + end + + let(:omniauth_provider_oidc_only_namespace) do + GitlabSettings::Options.new( + name: "openid_connect_aad", + step_up_auth: { + namespace: { + id_token: { + required: { + acr: 'gold' + } + } + } + } + ) + end + + before do + stub_omniauth_setting(enabled: true, providers: provider_configs) + allow(Devise).to receive(:omniauth_providers).and_return(provider_configs.map(&:name)) + end + + # rubocop:disable Layout/LineLength -- Avoid formatting to ensure one-line table syntax + where(:scope, :provider_configs, :expected_result) do + :admin_mode | [ref(:omniauth_provider_oidc)] | ['openid_connect'] + 'admin_mode' | [ref(:omniauth_provider_oidc)] | ['openid_connect'] + :admin_mode | [ref(:omniauth_provider_oidc_only_namespace)] | [] + :namespace | [ref(:omniauth_provider_oidc), ref(:omniauth_provider_oidc_only_namespace)] | %w[openid_connect openid_connect_aad] + 'namespace' | [ref(:omniauth_provider_oidc)] | ['openid_connect'] + :namespace | [ref(:omniauth_provider_oidc)] | ['openid_connect'] + :namespace | [] | [] + :unknown_scope | [ref(:omniauth_provider_oidc), ref(:omniauth_provider_oidc_only_namespace)] | [] + nil | [ref(:omniauth_provider_oidc), ref(:omniauth_provider_oidc_only_namespace)] | [] + end + # rubocop:enable Layout/LineLength + + with_them do + it { is_expected.to match_array(expected_result) } + end + end end diff --git a/spec/models/namespace_setting_spec.rb b/spec/models/namespace_setting_spec.rb index 53aff3ac435615..1463d1fa250640 100644 --- a/spec/models/namespace_setting_spec.rb +++ b/spec/models/namespace_setting_spec.rb @@ -702,4 +702,36 @@ end end end + + describe '#step_up_auth_required_oauth_provider' do + subject { namespace_settings } + + it { is_expected.to validate_presence_of(:step_up_auth_required_oauth_provider).allow_nil } + it { is_expected.to validate_inclusion_of(:step_up_auth_required_oauth_provider).in_array([]).allow_nil } + + context 'with oauth providers configured for step up authentication' do + let(:ommiauth_provider_config_with_step_up_auth) do + GitlabSettings::Options.new( + name: "openid_connect", + step_up_auth: { + namespace: { + id_token: { + required: { acr: 'gold' } + } + } + } + ) + end + + before do + stub_omniauth_setting(enabled: true, providers: [ommiauth_provider_config_with_step_up_auth]) + end + + it { is_expected.to validate_inclusion_of(:step_up_auth_required_oauth_provider).in_array([ommiauth_provider_config_with_step_up_auth.name]) } + + it { is_expected.to allow_value(ommiauth_provider_config_with_step_up_auth.name).for(:step_up_auth_required_oauth_provider) } + it { is_expected.to allow_value('').for(:step_up_auth_required_oauth_provider) } + it { is_expected.not_to allow_value('google_oauth2').for(:step_up_auth_required_oauth_provider).with_message('is not included in the list') } + end + end end diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index d4eeddcdb3504b..a2042023a71282 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -573,6 +573,90 @@ end end + describe 'when updating namespace setting #step_up_auth_required_oauth_provider' do + let!(:group) { private_group } + + let(:ommiauth_provider_config_oidc) do + GitlabSettings::Options.new( + name: 'openid_connect', + step_up_auth: { + namespace: { + id_token: { + required: { + acr: 'gold' + } + } + } + } + ) + end + + let(:ommiauth_provider_config_oidc_aad) do + GitlabSettings::Options.new( + name: 'openid_connect_aad', + step_up_auth: { + namespace: { + id_token: { + required: { + acr: 'gold' + } + } + } + } + ) + end + + before do + group.add_owner(user) + + stub_omniauth_setting(enabled: true, providers: [ommiauth_provider_config_oidc, ommiauth_provider_config_oidc_aad]) + allow(Devise).to receive(:omniauth_providers).and_return([ommiauth_provider_config_oidc.name, ommiauth_provider_config_oidc_aad.name]) + end + + context 'when updating with valid provider' do + it 'successfully updates the setting' do + result = update_group(group, user, { step_up_auth_required_oauth_provider: 'openid_connect' }) + + expect(result).to be_truthy + expect(group.reload.namespace_settings.step_up_auth_required_oauth_provider).to eq('openid_connect') + end + end + + context 'when updating to disabled (nil)' do + before do + group.namespace_settings.update!(step_up_auth_required_oauth_provider: 'openid_connect') + end + + it 'successfully disables step-up auth' do + result = update_group(group, user, { step_up_auth_required_oauth_provider: nil }) + + expect(result).to be_truthy + expect(group.reload.namespace_settings.step_up_auth_required_oauth_provider).to be_nil + end + end + + context 'when updating to disabled (empty string)' do + before do + group.namespace_settings.update!(step_up_auth_required_oauth_provider: 'openid_connect') + end + + it 'successfully disables step-up auth' do + result = update_group(group, user, { step_up_auth_required_oauth_provider: '' }) + + expect(result).to be_truthy + expect(group.reload.namespace_settings.step_up_auth_required_oauth_provider).to be_nil + end + end + + context 'when updating with invalid provider' do + it 'fails to update with validation error' do + result = update_group(group, user, { step_up_auth_required_oauth_provider: 'invalid_provider' }) + + expect(result).to be_falsey + end + end + end + context 'updating default_branch_protection' do let(:service) do described_class.new(internal_group, user, default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH) -- GitLab From b0973ea27d9d7afe60421d73d0c96144143720ff Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Mon, 4 Aug 2025 17:45:26 +0200 Subject: [PATCH 02/12] refactor: Fixing test in ci pipeline - https://gitlab.com/gitlab-community/gitlab-org/gitlab/-/jobs/10897334209 --- .../step_up_auth_required_oauth_provider_updated.yml | 10 ++++++++++ doc/user/compliance/audit_event_types.md | 1 + ee/lib/namespaces/namespace_setting_changes_auditor.rb | 3 ++- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 config/audit_events/types/step_up_auth_required_oauth_provider_updated.yml diff --git a/config/audit_events/types/step_up_auth_required_oauth_provider_updated.yml b/config/audit_events/types/step_up_auth_required_oauth_provider_updated.yml new file mode 100644 index 00000000000000..2c4876409fa6a7 --- /dev/null +++ b/config/audit_events/types/step_up_auth_required_oauth_provider_updated.yml @@ -0,0 +1,10 @@ +--- +name: step_up_auth_required_oauth_provider_updated +description: Step-up authentication OAuth provider requirement is updated +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/556943 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423 +feature_category: system_access +milestone: '18.3' +saved_to_database: true +streamed: true +scope: [Group] \ No newline at end of file diff --git a/doc/user/compliance/audit_event_types.md b/doc/user/compliance/audit_event_types.md index 0c6697106d519a..b839fbd4edc072 100644 --- a/doc/user/compliance/audit_event_types.md +++ b/doc/user/compliance/audit_event_types.md @@ -655,6 +655,7 @@ Audit event types belong to the following product categories. | [`authenticated_with_password`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/198216) | User successfully signed in with password | {{< icon name="check-circle" >}} Yes | GitLab [18.3](https://gitlab.com/gitlab-org/gitlab/-/issues/555101) | User | | [`authenticated_with_two_factor`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/198216) | User successfully signed in with two-factor authentication | {{< icon name="check-circle" >}} Yes | GitLab [18.3](https://gitlab.com/gitlab-org/gitlab/-/issues/555101) | User | | [`authenticated_with_webauthn`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/198216) | User successfully signed in with WebAuthn device | {{< icon name="check-circle" >}} Yes | GitLab [18.3](https://gitlab.com/gitlab-org/gitlab/-/issues/555101) | User | +| [`step_up_auth_required_oauth_provider_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423) | Step-up authentication OAuth provider requirement is updated | {{< icon name="check-circle" >}} Yes | GitLab [18.3](https://gitlab.com/gitlab-org/gitlab/-/issues/556943) | Group | ### Team planning diff --git a/ee/lib/namespaces/namespace_setting_changes_auditor.rb b/ee/lib/namespaces/namespace_setting_changes_auditor.rb index a3462123aa4345..a9f0a663fe0366 100644 --- a/ee/lib/namespaces/namespace_setting_changes_auditor.rb +++ b/ee/lib/namespaces/namespace_setting_changes_auditor.rb @@ -21,7 +21,8 @@ class NamespaceSettingChangesAuditor < ::AuditEvents::BaseChangesAuditor remove_dormant_members: 'remove_dormant_members_updated', remove_dormant_members_period: 'remove_dormant_members_period_updated', prevent_sharing_groups_outside_hierarchy: 'prevent_sharing_groups_outside_hierarchy_updated', - seat_control: 'seat_control_updated' + seat_control: 'seat_control_updated', + step_up_auth_required_oauth_provider: 'step_up_auth_required_oauth_provider_updated' }.freeze def initialize(current_user, namespace_setting, group) -- GitLab From 36ad798cb23136e1261b41c003556f76c4634c8b Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Mon, 4 Aug 2025 17:53:36 +0200 Subject: [PATCH 03/12] refactor: Apply suggestion from @sam.figueroa https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2663825417 --- app/services/groups/update_service.rb | 4 ++++ spec/services/groups/update_service_spec.rb | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb index 33f5a0578186f7..bc3fa91be392ca 100644 --- a/app/services/groups/update_service.rb +++ b/app/services/groups/update_service.rb @@ -109,6 +109,10 @@ def reject_parent_id! # overridden in EE def remove_unallowed_params + if Feature.disabled?(:omniauth_step_up_auth_for_namespace, current_user) + params.delete(:step_up_auth_required_oauth_provider) + end + params.delete(:emails_enabled) unless can?(current_user, :set_emails_disabled, group) params.delete(:max_artifacts_size) unless can?(current_user, :update_max_artifacts_size, group) diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index a2042023a71282..9775cb66d8001e 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -620,6 +620,19 @@ expect(result).to be_truthy expect(group.reload.namespace_settings.step_up_auth_required_oauth_provider).to eq('openid_connect') end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(omniauth_step_up_auth_for_namespace: false) + end + + it 'successfully updates the setting' do + result = update_group(group, user, { step_up_auth_required_oauth_provider: 'openid_connect' }) + + expect(result).to be_truthy + expect(group.reload.namespace_settings.step_up_auth_required_oauth_provider).to be_nil + end + end end context 'when updating to disabled (nil)' do -- GitLab From 1a3e81393e7652108cc84cc994fdc25bb864c8f3 Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Wed, 6 Aug 2025 11:03:14 +0200 Subject: [PATCH 04/12] refactor: Fix undercoverage job in CI/CD pipeline - Fixing this failed pipeline: https://gitlab.com/gitlab-community/gitlab-org/gitlab/-/jobs/10914082185 - Moving method `step_up_auth_flows` to private scope because it is not used outside the class. Therefore, we do not need to test the method explicitly and the undercoverage job will not be triggered. --- .../auth/oidc/step_up_authentication.rb | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/lib/gitlab/auth/oidc/step_up_authentication.rb b/lib/gitlab/auth/oidc/step_up_authentication.rb index 5b0dc74aa05d0e..cc36795933c51a 100644 --- a/lib/gitlab/auth/oidc/step_up_authentication.rb +++ b/lib/gitlab/auth/oidc/step_up_authentication.rb @@ -50,15 +50,7 @@ def enabled_providers(scope: STEP_UP_AUTH_SCOPE_ADMIN_MODE) # @param session [Hash] the session hash containing authentication state # @return [Boolean] true if step-up authentication is authenticated def succeeded?(session, scope: STEP_UP_AUTH_SCOPE_ADMIN_MODE) - step_up_auth_flows = - omniauth_step_up_auth_session_data(session) - &.to_h - &.flat_map do |provider, step_up_auth_object| - step_up_auth_object.map do |step_up_auth_scope, _| - build_flow(provider: provider, session: session, scope: step_up_auth_scope) - end - end - step_up_auth_flows + step_up_auth_flows(session) .select do |step_up_auth_flow| step_up_auth_flow.scope.to_s == scope.to_s end @@ -87,16 +79,6 @@ def conditions_fulfilled?(oauth_extra_metadata:, provider:, scope: STEP_UP_AUTH_ conditions.present? && conditions.all? end - def step_up_auth_flows(session) - omniauth_step_up_auth_session_data(session) - &.to_h - &.flat_map do |provider, step_up_auth_object| - step_up_auth_object.map do |step_up_auth_scope, _| - build_flow(provider: provider, session: session, scope: step_up_auth_scope) - end - end - end - def build_flow(provider:, session:, scope: STEP_UP_AUTH_SCOPE_ADMIN_MODE) Gitlab::Auth::Oidc::StepUpAuthenticationFlow.new(provider: provider, scope: scope, session: session) end -- GitLab From d224754adb2428c7fd28bf958528768a905562b6 Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Wed, 13 Aug 2025 17:07:53 +0200 Subject: [PATCH 05/12] refactor: Apply suggestions from @dmeshcharakou - Use let_it_be_with_reload instead of let! - Test nullify_if_blank for :step_up_auth_required_oauth_provider - Add delegate tests in group_spec.rb - Resolve conflicts in db/structure.sql - Rollout issue for feature flag - Fix feature_issue_url - Fix introduced_by_url - Analytics review needed - Skip partial rendering when feature flag disabled - Use group as actor for feature flag --- app/services/groups/update_service.rb | 2 +- .../groups/settings/_permissions.html.haml | 3 +- .../_step_up_authentication.html.haml | 2 - .../omniauth_step_up_auth_for_namespace.yml | 6 +-- spec/models/group_spec.rb | 5 +++ spec/models/namespace_setting_spec.rb | 9 +++-- spec/services/groups/update_service_spec.rb | 39 ++++++++++--------- 7 files changed, 37 insertions(+), 29 deletions(-) diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb index bc3fa91be392ca..4d4a1411617a77 100644 --- a/app/services/groups/update_service.rb +++ b/app/services/groups/update_service.rb @@ -109,7 +109,7 @@ def reject_parent_id! # overridden in EE def remove_unallowed_params - if Feature.disabled?(:omniauth_step_up_auth_for_namespace, current_user) + if Feature.disabled?(:omniauth_step_up_auth_for_namespace, group) params.delete(:step_up_auth_required_oauth_provider) end diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml index 147f47ab59b777..237ce0dd951898 100644 --- a/app/views/groups/settings/_permissions.html.haml +++ b/app/views/groups/settings/_permissions.html.haml @@ -51,7 +51,8 @@ = render_if_exists 'groups/settings/extended_grat_expiry_webhook_execute', f: f, group: @group = render_if_exists 'groups/settings/enforce_ssh_certificates', f: f, group: @group = render 'groups/settings/two_factor_auth', f: f, group: @group - = render 'groups/settings/step_up_authentication', f: f, group: @group + - if Feature.enabled?(:omniauth_step_up_auth_for_namespace, @group) + = render 'groups/settings/step_up_authentication', f: f, group: @group = render 'groups/settings/membership', f: f, group: @group = render_if_exists 'groups/settings/placeholder_confirmation_bypass', f: f, group: @group = render_if_exists 'groups/settings/remove_dormant_members', f: f, group: @group diff --git a/app/views/groups/settings/_step_up_authentication.html.haml b/app/views/groups/settings/_step_up_authentication.html.haml index f1c194492a2d38..286e396430abe1 100644 --- a/app/views/groups/settings/_step_up_authentication.html.haml +++ b/app/views/groups/settings/_step_up_authentication.html.haml @@ -1,5 +1,3 @@ -- return if Feature.disabled?(:omniauth_step_up_auth_for_namespace, current_user) - %h5= s_('GroupSettings|Step-up Authentication') %p = link_to s_('GroupSettings|Learn more about step-up authentication.'), help_page_path('administration/auth/oidc.md'), target: '_blank', rel: 'noopener noreferrer' diff --git a/config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml b/config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml index 4331be3a45a6a4..b5ace00605ac7c 100644 --- a/config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml +++ b/config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml @@ -1,9 +1,9 @@ --- name: omniauth_step_up_auth_for_namespace -feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/474650 -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176295 +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/556943 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/510951 -milestone: '18.3' +milestone: '18.4' group: group::authorization type: gitlab_com_derisk default_enabled: false diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 3543a2c51958b9..f89868d9a3f5cb 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -262,6 +262,11 @@ it { is_expected.to include_module(Referable) } end + describe 'delegations' do + it { is_expected.to delegate_method(:step_up_auth_required_oauth_provider).to(:namespace_settings) } + it { is_expected.to delegate_method(:step_up_auth_required_oauth_provider=).to(:namespace_settings).with_arguments(:args) } + end + describe 'validations' do let_it_be(:private_organization) { create(:organization, :private) } let_it_be(:public_organization) { create(:organization, :public) } diff --git a/spec/models/namespace_setting_spec.rb b/spec/models/namespace_setting_spec.rb index 1463d1fa250640..f0b902acd333a3 100644 --- a/spec/models/namespace_setting_spec.rb +++ b/spec/models/namespace_setting_spec.rb @@ -706,10 +706,13 @@ describe '#step_up_auth_required_oauth_provider' do subject { namespace_settings } - it { is_expected.to validate_presence_of(:step_up_auth_required_oauth_provider).allow_nil } - it { is_expected.to validate_inclusion_of(:step_up_auth_required_oauth_provider).in_array([]).allow_nil } + context 'without omniauth provider configured for step-up authentication' do + it { is_expected.to validate_presence_of(:step_up_auth_required_oauth_provider).allow_nil } + it { is_expected.to validate_inclusion_of(:step_up_auth_required_oauth_provider).in_array([]).allow_nil } + it { is_expected.to nullify_if_blank(:step_up_auth_required_oauth_provider) } + end - context 'with oauth providers configured for step up authentication' do + context 'with omniauth providers configured for step-up authentication' do let(:ommiauth_provider_config_with_step_up_auth) do GitlabSettings::Options.new( name: "openid_connect", diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index 9775cb66d8001e..5b27f610d189b5 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -574,7 +574,8 @@ end describe 'when updating namespace setting #step_up_auth_required_oauth_provider' do - let!(:group) { private_group } + let_it_be_with_reload(:group) { create(:group, :private) } + let_it_be(:user) { create(:user, owner_of: group) } let(:ommiauth_provider_config_oidc) do GitlabSettings::Options.new( @@ -606,18 +607,20 @@ ) end - before do - group.add_owner(user) + let(:params) { {} } + subject(:execute_update) { update_group(group, user, params) } + + before do stub_omniauth_setting(enabled: true, providers: [ommiauth_provider_config_oidc, ommiauth_provider_config_oidc_aad]) allow(Devise).to receive(:omniauth_providers).and_return([ommiauth_provider_config_oidc.name, ommiauth_provider_config_oidc_aad.name]) end context 'when updating with valid provider' do - it 'successfully updates the setting' do - result = update_group(group, user, { step_up_auth_required_oauth_provider: 'openid_connect' }) + let(:params) { { step_up_auth_required_oauth_provider: 'openid_connect' } } - expect(result).to be_truthy + it 'successfully updates the setting' do + expect(execute_update).to be_truthy expect(group.reload.namespace_settings.step_up_auth_required_oauth_provider).to eq('openid_connect') end @@ -626,46 +629,44 @@ stub_feature_flags(omniauth_step_up_auth_for_namespace: false) end - it 'successfully updates the setting' do - result = update_group(group, user, { step_up_auth_required_oauth_provider: 'openid_connect' }) - - expect(result).to be_truthy + it 'does not update the setting when feature flag is disabled' do + expect(execute_update).to be_truthy expect(group.reload.namespace_settings.step_up_auth_required_oauth_provider).to be_nil end end end context 'when updating to disabled (nil)' do + let(:params) { { step_up_auth_required_oauth_provider: nil } } + before do group.namespace_settings.update!(step_up_auth_required_oauth_provider: 'openid_connect') end it 'successfully disables step-up auth' do - result = update_group(group, user, { step_up_auth_required_oauth_provider: nil }) - - expect(result).to be_truthy + expect(execute_update).to be_truthy expect(group.reload.namespace_settings.step_up_auth_required_oauth_provider).to be_nil end end context 'when updating to disabled (empty string)' do + let(:params) { { step_up_auth_required_oauth_provider: '' } } + before do group.namespace_settings.update!(step_up_auth_required_oauth_provider: 'openid_connect') end it 'successfully disables step-up auth' do - result = update_group(group, user, { step_up_auth_required_oauth_provider: '' }) - - expect(result).to be_truthy + expect(execute_update).to be_truthy expect(group.reload.namespace_settings.step_up_auth_required_oauth_provider).to be_nil end end context 'when updating with invalid provider' do - it 'fails to update with validation error' do - result = update_group(group, user, { step_up_auth_required_oauth_provider: 'invalid_provider' }) + let(:params) { { step_up_auth_required_oauth_provider: 'invalid_provider' } } - expect(result).to be_falsey + it 'fails to update with validation error' do + expect(execute_update).to be_falsey end end end -- GitLab From d010c6c27b97db40deade0d54860cd306ca1fa98 Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Fri, 15 Aug 2025 08:03:54 +0200 Subject: [PATCH 06/12] refactor: Apply suggestion from @dmeshcharakou - Update group for feature flag, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2688387433 - Bump migrations timestamp, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2688387519 --- .../gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml | 2 +- ...ep_up_auth_required_oauth_provider_to_namespace_settings.rb} | 0 db/schema_migrations/20250729162745 | 1 - db/schema_migrations/20250815073031 | 1 + 4 files changed, 2 insertions(+), 2 deletions(-) rename db/migrate/{20250729162745_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb => 20250815073031_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb} (100%) delete mode 100644 db/schema_migrations/20250729162745 create mode 100644 db/schema_migrations/20250815073031 diff --git a/config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml b/config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml index b5ace00605ac7c..f37c56ffd3a303 100644 --- a/config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml +++ b/config/feature_flags/gitlab_com_derisk/omniauth_step_up_auth_for_namespace.yml @@ -4,6 +4,6 @@ feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/556943 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/510951 milestone: '18.4' -group: group::authorization +group: group::authentication type: gitlab_com_derisk default_enabled: false diff --git a/db/migrate/20250729162745_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb b/db/migrate/20250815073031_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb similarity index 100% rename from db/migrate/20250729162745_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb rename to db/migrate/20250815073031_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb diff --git a/db/schema_migrations/20250729162745 b/db/schema_migrations/20250729162745 deleted file mode 100644 index 3ed94fdbd09b87..00000000000000 --- a/db/schema_migrations/20250729162745 +++ /dev/null @@ -1 +0,0 @@ -757c6e9a42e4fb0f238961528d171834b70944e55293d403e4278b732d3555cf \ No newline at end of file diff --git a/db/schema_migrations/20250815073031 b/db/schema_migrations/20250815073031 new file mode 100644 index 00000000000000..ab814c0bb732be --- /dev/null +++ b/db/schema_migrations/20250815073031 @@ -0,0 +1 @@ +36d8e64868c5c74aad0a1b8869a1ca3deaa319ae452b8dc2049c790cac751e42 \ No newline at end of file -- GitLab From 0ee59036ff78fbb5224a7314951025f4ae9a72d3 Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Fri, 15 Aug 2025 10:54:30 +0200 Subject: [PATCH 07/12] refactor: Apply suggestion from @sayobittencourt - Adjust link text, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2683539394 --- .../_step_up_authentication.html.haml | 10 +-- db/structure.sql | 25 +++--- doc/administration/auth/oidc.md | 89 +++++++++++++++++-- locale/gitlab.pot | 9 +- 4 files changed, 102 insertions(+), 31 deletions(-) diff --git a/app/views/groups/settings/_step_up_authentication.html.haml b/app/views/groups/settings/_step_up_authentication.html.haml index 286e396430abe1..a8205acf28df1f 100644 --- a/app/views/groups/settings/_step_up_authentication.html.haml +++ b/app/views/groups/settings/_step_up_authentication.html.haml @@ -1,6 +1,9 @@ %h5= s_('GroupSettings|Step-up Authentication') %p - = link_to s_('GroupSettings|Learn more about step-up authentication.'), help_page_path('administration/auth/oidc.md'), target: '_blank', rel: 'noopener noreferrer' + = link_to s_('GroupSettings|What is step-up authentication?'), + help_page_path('administration/auth/oidc.md', anchor: 'enable-step-up-authentication-for-groups'), + target: '_blank', + rel: 'noopener noreferrer' .form-group = f.select :step_up_auth_required_oauth_provider, @@ -10,8 +13,3 @@ .form-text.gl-text-subtle = s_('GroupSettings|When enabled, users must complete step-up authentication to access this group and its resources.') - - - if group.namespace_settings.step_up_auth_required_oauth_provider.present? - .form-text.gl-text-success.gl-mt-2 - = sprite_icon('check-circle', css_class: 'gl-mr-2') - = s_('GroupSettings|Step-up authentication is currently enabled for this group.') diff --git a/db/structure.sql b/db/structure.sql index 8e1bd9253eff7b..31461570c7e85a 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -5077,7 +5077,7 @@ PARTITION BY LIST (partition_id); CREATE TABLE p_ci_finished_build_ch_sync_events ( build_id bigint NOT NULL, - partition bigint DEFAULT 1 NOT NULL, + partition bigint DEFAULT 2 NOT NULL, build_finished_at timestamp without time zone NOT NULL, processed boolean DEFAULT false NOT NULL, project_id bigint NOT NULL @@ -5087,7 +5087,7 @@ PARTITION BY LIST (partition); CREATE TABLE p_ci_finished_pipeline_ch_sync_events ( pipeline_id bigint NOT NULL, project_namespace_id bigint NOT NULL, - partition bigint DEFAULT 1 NOT NULL, + partition bigint DEFAULT 2 NOT NULL, pipeline_finished_at timestamp without time zone NOT NULL, processed boolean DEFAULT false NOT NULL ) @@ -10538,16 +10538,16 @@ CREATE TABLE application_settings ( database_reindexing jsonb DEFAULT '{}'::jsonb NOT NULL, duo_chat jsonb DEFAULT '{}'::jsonb NOT NULL, group_settings jsonb DEFAULT '{}'::jsonb NOT NULL, + web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, + lock_web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, model_prompt_cache_enabled boolean DEFAULT true NOT NULL, lock_model_prompt_cache_enabled boolean DEFAULT false NOT NULL, response_limits jsonb DEFAULT '{}'::jsonb NOT NULL, - web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, - lock_web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, tmp_asset_proxy_secret_key jsonb, editor_extensions jsonb DEFAULT '{}'::jsonb NOT NULL, security_and_compliance_settings jsonb DEFAULT '{}'::jsonb NOT NULL, - sdrs_url text, default_profile_preferences jsonb DEFAULT '{}'::jsonb NOT NULL, + sdrs_url text, sdrs_enabled boolean DEFAULT false NOT NULL, sdrs_jwt_signing_key jsonb, resource_access_tokens_settings jsonb DEFAULT '{}'::jsonb NOT NULL, @@ -14942,7 +14942,8 @@ CREATE TABLE deploy_tokens ( write_virtual_registry boolean DEFAULT false NOT NULL, seven_days_notification_sent_at timestamp with time zone, thirty_days_notification_sent_at timestamp with time zone, - sixty_days_notification_sent_at timestamp with time zone + sixty_days_notification_sent_at timestamp with time zone, + CONSTRAINT check_e2ab92a2f6 CHECK ((num_nonnulls(group_id, project_id) = 1)) ); CREATE SEQUENCE deploy_tokens_id_seq @@ -15421,8 +15422,8 @@ CREATE TABLE duo_workflows_workflows ( allow_agent_to_request_user boolean DEFAULT true NOT NULL, pre_approved_agent_privileges smallint[] DEFAULT '{1,2}'::smallint[] NOT NULL, image text, - environment smallint, namespace_id bigint, + environment smallint, CONSTRAINT check_30ca07a4ef CHECK ((char_length(goal) <= 16384)), CONSTRAINT check_3a9162f1ae CHECK ((char_length(image) <= 2048)), CONSTRAINT check_73884a5839 CHECK ((num_nonnulls(namespace_id, project_id) = 1)), @@ -19500,11 +19501,11 @@ CREATE TABLE namespace_settings ( job_token_policies_enabled boolean DEFAULT false NOT NULL, security_policies jsonb DEFAULT '{}'::jsonb NOT NULL, duo_nano_features_enabled boolean, + web_based_commit_signing_enabled boolean, + lock_web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, model_prompt_cache_enabled boolean, lock_model_prompt_cache_enabled boolean DEFAULT false NOT NULL, disable_invite_members boolean DEFAULT false NOT NULL, - web_based_commit_signing_enabled boolean, - lock_web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, allow_enterprise_bypass_placeholder_confirmation boolean DEFAULT false NOT NULL, enterprise_bypass_expires_at timestamp with time zone, hide_email_on_profile boolean DEFAULT false NOT NULL, @@ -23016,8 +23017,8 @@ CREATE TABLE project_settings ( merge_request_title_regex text, protect_merge_request_pipelines boolean DEFAULT true NOT NULL, auto_duo_code_review_enabled boolean, - model_prompt_cache_enabled boolean, web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, + model_prompt_cache_enabled boolean, duo_context_exclusion_settings jsonb DEFAULT '{}'::jsonb NOT NULL, merge_request_title_regex_description text, duo_remote_flows_enabled boolean DEFAULT false NOT NULL, @@ -26262,12 +26263,12 @@ CREATE TABLE user_preferences ( dpop_enabled boolean DEFAULT false NOT NULL, use_work_items_view boolean DEFAULT false NOT NULL, text_editor_type smallint DEFAULT 2 NOT NULL, - merge_request_dashboard_list_type smallint DEFAULT 0 NOT NULL, extensions_marketplace_opt_in_url text, + merge_request_dashboard_list_type smallint DEFAULT 0 NOT NULL, dark_color_scheme_id smallint, work_items_display_settings jsonb DEFAULT '{}'::jsonb NOT NULL, - default_duo_add_on_assignment_id bigint, markdown_maintain_indentation boolean DEFAULT false NOT NULL, + default_duo_add_on_assignment_id bigint, CONSTRAINT check_1d670edc68 CHECK ((time_display_relative IS NOT NULL)), CONSTRAINT check_89bf269f41 CHECK ((char_length(diffs_deletion_color) <= 7)), CONSTRAINT check_9b50d9f942 CHECK ((char_length(extensions_marketplace_opt_in_url) <= 512)), diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md index 7b1ea34582682d..38a63a9e338e6a 100644 --- a/doc/administration/auth/oidc.md +++ b/doc/administration/auth/oidc.md @@ -1409,7 +1409,7 @@ To configure a custom duration for your ID tokens: {{< /tabs >}} -## Step-up authentication for Admin Mode +## Step-up authentication {{< details >}} @@ -1419,12 +1419,6 @@ To configure a custom duration for your ID tokens: {{< /details >}} -{{< history >}} - -- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/474650) in GitLab 17.11 [with a flag](../feature_flags/_index.md) named `omniauth_step_up_auth_for_admin_mode`. Disabled by default. - -{{< /history >}} - {{< alert type="flag" >}} The availability of this feature is controlled by a feature flag. @@ -1448,6 +1442,11 @@ This feature is an [experiment](../../policy/development_stages_support.md) and ### Enable step-up authentication for Admin Mode +{{< history >}} + +- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/474650) in GitLab 17.11 [with a flag](../feature_flags/_index.md) named `omniauth_step_up_auth_for_admin_mode`. Disabled by default. + +{{< /history >}} To enable step-up authentication for Admin Mode: 1. Edit your GitLab configuration file (`gitlab.yml` or `/etc/gitlab/gitlab.rb`) to enable @@ -1638,6 +1637,82 @@ To require step-up authentication for Admin Mode with Microsoft Entra ID: 1. Save the configuration file and restart GitLab for the changes to take effect. +### Enable step-up authentication for groups + +{{< history >}} + +- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/556943) in GitLab 18.4 [with a flag](../feature_flags/_index.md) named `omniauth_step_up_auth_for_namespace`. Disabled by default. + +{{< /history >}} + +You can also enable step-up authentication for every group in your instance. + +To enable step-up authentication for groups: + +1. Edit your GitLab configuration file (`gitlab.yml` or `/etc/gitlab/gitlab.rb`) to enable + step-up authentication for an specific OmniAuth provider. + + In contrast to [the step-up authentication configuration for Admin Mode](#enable-step-up-authentication-for-admin-mode), + the step-up authentication configuration for groups expects the `namespace` object to be defined + in the `step_up_auth` configuration. This is because the step-up authentication for groups + is not limited to Admin Mode, but applies to all groups in your instance. The rest of the configuration + is similar to the Admin Mode configuration. + + ```yaml + production: &base + omniauth: + providers: + - { name: 'openid_connect', + label: 'Provider name', + args: { + name: 'openid_connect', + # ... + allow_authorize_params: ["claims"], # Match this to the parameters defined in `step_up_auth => admin_mode => params` + }, + step_up_auth: { + namespace : { + # The `id_token` field defines the claims that must be included with the token. + # You can specify claims in one or both of the `required` or `included` fields. + # The token must include matching values for every claim you define in these fields. + id_token: { + # The `required` field defines key-value pairs that must be included with the ID token. + # The values must match exactly what is defined. + # In this example, the 'acr' (Authentication Context Class Reference) claim + # must have the value 'gold' to pass the step-up authentication challenge. + # This ensures a specific level of authentication assurance. + required: { + acr: 'gold' + }, + # The `included` field also defines key-value pairs that must be included with the ID token. + # Multiple accepted values can be defined in an array. If an array is not used, the value must match exactly. + # In this example, the 'amr' (Authentication Method References) claim + # must have a value of either 'mfa' or 'fpt' to pass the step-up authentication challenge. + # This is useful for scenarios where the user must provide additional authentication factors. + included: { + amr: ['mfa', 'fpt'] + }, + }, + # The `params` field defines any additional parameters that are sent during the authentication process. + # In this example, the `claims` parameter is added to the authorization request and instructs the + # identity provider to include an 'acr' claim with the value 'gold' in the ID token. + # The 'essential: true' indicates that this claim is required for successful authentication. + params: { + claims: { + id_token: { + acr: { + essential: true, + values: ['gold'] + } + } + } + } + }, + } + } + ``` + +1. Save the configuration file and restart GitLab for the changes to take effect. + ### Add custom documentation links for step-up authentication When step-up authentication fails, GitLab can display custom documentation links to help users understand diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 0e05df20f23bae..b7543d327d4cfc 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -31988,9 +31988,6 @@ msgstr "" msgid "GroupSettings|Include diff previews" msgstr "" -msgid "GroupSettings|Learn more about step-up authentication." -msgstr "" - msgid "GroupSettings|Members cannot invite groups outside of %{group} and its subgroups" msgstr "" @@ -32054,9 +32051,6 @@ msgstr "" msgid "GroupSettings|Step-up Authentication" msgstr "" -msgid "GroupSettings|Step-up authentication is currently enabled for this group." -msgstr "" - msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found." msgstr "" @@ -32102,6 +32096,9 @@ msgstr "" msgid "GroupSettings|What is Insights?" msgstr "" +msgid "GroupSettings|What is step-up authentication?" +msgstr "" + msgid "GroupSettings|When disabled, members cannot use runner registration tokens to register runners. Members can use runner authentication tokens instead as the more secure registration method." msgstr "" -- GitLab From 262465d6ef4abd8c5394da486c8699eb8fcac79f Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Tue, 19 Aug 2025 10:59:16 +0200 Subject: [PATCH 08/12] reafctor: Apply suggestions from @dmeshcharakou - Finalization of the MR changes - Rolling over to next milestone, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2694416138 - Remove wrong changes in db/structure.sql --- ...p_auth_required_oauth_provider_updated.yml | 2 +- ...ed_oauth_provider_to_namespace_settings.rb | 2 +- db/structure.sql | 25 +++++++++---------- doc/user/compliance/audit_event_types.md | 2 +- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/config/audit_events/types/step_up_auth_required_oauth_provider_updated.yml b/config/audit_events/types/step_up_auth_required_oauth_provider_updated.yml index 2c4876409fa6a7..4b8b59c3696e0c 100644 --- a/config/audit_events/types/step_up_auth_required_oauth_provider_updated.yml +++ b/config/audit_events/types/step_up_auth_required_oauth_provider_updated.yml @@ -4,7 +4,7 @@ description: Step-up authentication OAuth provider requirement is updated introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/556943 introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423 feature_category: system_access -milestone: '18.3' +milestone: '18.4' saved_to_database: true streamed: true scope: [Group] \ No newline at end of file diff --git a/db/migrate/20250815073031_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb b/db/migrate/20250815073031_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb index f30ca2a97927e6..a91b614b9c6fca 100644 --- a/db/migrate/20250815073031_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb +++ b/db/migrate/20250815073031_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class AddStepUpAuthRequiredOauthProviderToNamespaceSettings < Gitlab::Database::Migration[2.3] - milestone '18.3' + milestone '18.4' disable_ddl_transaction! diff --git a/db/structure.sql b/db/structure.sql index 31461570c7e85a..8e1bd9253eff7b 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -5077,7 +5077,7 @@ PARTITION BY LIST (partition_id); CREATE TABLE p_ci_finished_build_ch_sync_events ( build_id bigint NOT NULL, - partition bigint DEFAULT 2 NOT NULL, + partition bigint DEFAULT 1 NOT NULL, build_finished_at timestamp without time zone NOT NULL, processed boolean DEFAULT false NOT NULL, project_id bigint NOT NULL @@ -5087,7 +5087,7 @@ PARTITION BY LIST (partition); CREATE TABLE p_ci_finished_pipeline_ch_sync_events ( pipeline_id bigint NOT NULL, project_namespace_id bigint NOT NULL, - partition bigint DEFAULT 2 NOT NULL, + partition bigint DEFAULT 1 NOT NULL, pipeline_finished_at timestamp without time zone NOT NULL, processed boolean DEFAULT false NOT NULL ) @@ -10538,16 +10538,16 @@ CREATE TABLE application_settings ( database_reindexing jsonb DEFAULT '{}'::jsonb NOT NULL, duo_chat jsonb DEFAULT '{}'::jsonb NOT NULL, group_settings jsonb DEFAULT '{}'::jsonb NOT NULL, - web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, - lock_web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, model_prompt_cache_enabled boolean DEFAULT true NOT NULL, lock_model_prompt_cache_enabled boolean DEFAULT false NOT NULL, response_limits jsonb DEFAULT '{}'::jsonb NOT NULL, + web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, + lock_web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, tmp_asset_proxy_secret_key jsonb, editor_extensions jsonb DEFAULT '{}'::jsonb NOT NULL, security_and_compliance_settings jsonb DEFAULT '{}'::jsonb NOT NULL, - default_profile_preferences jsonb DEFAULT '{}'::jsonb NOT NULL, sdrs_url text, + default_profile_preferences jsonb DEFAULT '{}'::jsonb NOT NULL, sdrs_enabled boolean DEFAULT false NOT NULL, sdrs_jwt_signing_key jsonb, resource_access_tokens_settings jsonb DEFAULT '{}'::jsonb NOT NULL, @@ -14942,8 +14942,7 @@ CREATE TABLE deploy_tokens ( write_virtual_registry boolean DEFAULT false NOT NULL, seven_days_notification_sent_at timestamp with time zone, thirty_days_notification_sent_at timestamp with time zone, - sixty_days_notification_sent_at timestamp with time zone, - CONSTRAINT check_e2ab92a2f6 CHECK ((num_nonnulls(group_id, project_id) = 1)) + sixty_days_notification_sent_at timestamp with time zone ); CREATE SEQUENCE deploy_tokens_id_seq @@ -15422,8 +15421,8 @@ CREATE TABLE duo_workflows_workflows ( allow_agent_to_request_user boolean DEFAULT true NOT NULL, pre_approved_agent_privileges smallint[] DEFAULT '{1,2}'::smallint[] NOT NULL, image text, - namespace_id bigint, environment smallint, + namespace_id bigint, CONSTRAINT check_30ca07a4ef CHECK ((char_length(goal) <= 16384)), CONSTRAINT check_3a9162f1ae CHECK ((char_length(image) <= 2048)), CONSTRAINT check_73884a5839 CHECK ((num_nonnulls(namespace_id, project_id) = 1)), @@ -19501,11 +19500,11 @@ CREATE TABLE namespace_settings ( job_token_policies_enabled boolean DEFAULT false NOT NULL, security_policies jsonb DEFAULT '{}'::jsonb NOT NULL, duo_nano_features_enabled boolean, - web_based_commit_signing_enabled boolean, - lock_web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, model_prompt_cache_enabled boolean, lock_model_prompt_cache_enabled boolean DEFAULT false NOT NULL, disable_invite_members boolean DEFAULT false NOT NULL, + web_based_commit_signing_enabled boolean, + lock_web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, allow_enterprise_bypass_placeholder_confirmation boolean DEFAULT false NOT NULL, enterprise_bypass_expires_at timestamp with time zone, hide_email_on_profile boolean DEFAULT false NOT NULL, @@ -23017,8 +23016,8 @@ CREATE TABLE project_settings ( merge_request_title_regex text, protect_merge_request_pipelines boolean DEFAULT true NOT NULL, auto_duo_code_review_enabled boolean, - web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, model_prompt_cache_enabled boolean, + web_based_commit_signing_enabled boolean DEFAULT false NOT NULL, duo_context_exclusion_settings jsonb DEFAULT '{}'::jsonb NOT NULL, merge_request_title_regex_description text, duo_remote_flows_enabled boolean DEFAULT false NOT NULL, @@ -26263,12 +26262,12 @@ CREATE TABLE user_preferences ( dpop_enabled boolean DEFAULT false NOT NULL, use_work_items_view boolean DEFAULT false NOT NULL, text_editor_type smallint DEFAULT 2 NOT NULL, - extensions_marketplace_opt_in_url text, merge_request_dashboard_list_type smallint DEFAULT 0 NOT NULL, + extensions_marketplace_opt_in_url text, dark_color_scheme_id smallint, work_items_display_settings jsonb DEFAULT '{}'::jsonb NOT NULL, - markdown_maintain_indentation boolean DEFAULT false NOT NULL, default_duo_add_on_assignment_id bigint, + markdown_maintain_indentation boolean DEFAULT false NOT NULL, CONSTRAINT check_1d670edc68 CHECK ((time_display_relative IS NOT NULL)), CONSTRAINT check_89bf269f41 CHECK ((char_length(diffs_deletion_color) <= 7)), CONSTRAINT check_9b50d9f942 CHECK ((char_length(extensions_marketplace_opt_in_url) <= 512)), diff --git a/doc/user/compliance/audit_event_types.md b/doc/user/compliance/audit_event_types.md index b839fbd4edc072..e4a45b39e7ffba 100644 --- a/doc/user/compliance/audit_event_types.md +++ b/doc/user/compliance/audit_event_types.md @@ -655,7 +655,7 @@ Audit event types belong to the following product categories. | [`authenticated_with_password`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/198216) | User successfully signed in with password | {{< icon name="check-circle" >}} Yes | GitLab [18.3](https://gitlab.com/gitlab-org/gitlab/-/issues/555101) | User | | [`authenticated_with_two_factor`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/198216) | User successfully signed in with two-factor authentication | {{< icon name="check-circle" >}} Yes | GitLab [18.3](https://gitlab.com/gitlab-org/gitlab/-/issues/555101) | User | | [`authenticated_with_webauthn`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/198216) | User successfully signed in with WebAuthn device | {{< icon name="check-circle" >}} Yes | GitLab [18.3](https://gitlab.com/gitlab-org/gitlab/-/issues/555101) | User | -| [`step_up_auth_required_oauth_provider_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423) | Step-up authentication OAuth provider requirement is updated | {{< icon name="check-circle" >}} Yes | GitLab [18.3](https://gitlab.com/gitlab-org/gitlab/-/issues/556943) | Group | +| [`step_up_auth_required_oauth_provider_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423) | Step-up authentication OAuth provider requirement is updated | {{< icon name="check-circle" >}} Yes | GitLab [18.4](https://gitlab.com/gitlab-org/gitlab/-/issues/556943) | Group | ### Team planning -- GitLab From e2bd377cc5986e3dfff01c073a8b86401f28fc76 Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Wed, 20 Aug 2025 09:07:24 +0200 Subject: [PATCH 09/12] db: Refreshing timestamp of migration to fix ci pipeline Fingers crossed that it will work. - See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2696845194 --- ...tep_up_auth_required_oauth_provider_to_namespace_settings.rb} | 0 db/schema_migrations/20250815073031 | 1 - db/schema_migrations/20250820070256 | 1 + 3 files changed, 1 insertion(+), 1 deletion(-) rename db/migrate/{20250815073031_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb => 20250820070256_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb} (100%) delete mode 100644 db/schema_migrations/20250815073031 create mode 100644 db/schema_migrations/20250820070256 diff --git a/db/migrate/20250815073031_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb b/db/migrate/20250820070256_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb similarity index 100% rename from db/migrate/20250815073031_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb rename to db/migrate/20250820070256_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb diff --git a/db/schema_migrations/20250815073031 b/db/schema_migrations/20250815073031 deleted file mode 100644 index ab814c0bb732be..00000000000000 --- a/db/schema_migrations/20250815073031 +++ /dev/null @@ -1 +0,0 @@ -36d8e64868c5c74aad0a1b8869a1ca3deaa319ae452b8dc2049c790cac751e42 \ No newline at end of file diff --git a/db/schema_migrations/20250820070256 b/db/schema_migrations/20250820070256 new file mode 100644 index 00000000000000..3c0463ea243f88 --- /dev/null +++ b/db/schema_migrations/20250820070256 @@ -0,0 +1 @@ +72a1b0ec5efc1c04c2deb6af2d28ee4511cadfdfe74b36ab41322a873cdfa346 \ No newline at end of file -- GitLab From 695891253d711d8da27a535e63d7242a52ae1e78 Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Sat, 23 Aug 2025 22:29:29 +0200 Subject: [PATCH 10/12] db: Split text column migration per database guidelines Splits the migration for step_up_auth_required_oauth_provider into two separate migrations following GitLab's database best practices. The column addition now runs in a transaction with lock retries, while the text limit constraint runs separately with DDL disabled. Addresses https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2703347253 . Split text column into two migrations The split adds text column and limit in separate migrations to follow database guidelines around concurrent indexes and column additions. --- ...quired_oauth_provider_to_namespace_settings.rb | 6 ++---- ...mit_to_step_up_auth_required_oauth_provider.rb | 15 +++++++++++++++ db/schema_migrations/20250820070257 | 1 + 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20250820070257_add_text_limit_to_step_up_auth_required_oauth_provider.rb create mode 100644 db/schema_migrations/20250820070257 diff --git a/db/migrate/20250820070256_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb b/db/migrate/20250820070256_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb index a91b614b9c6fca..074c5ebeec3ba3 100644 --- a/db/migrate/20250820070256_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb +++ b/db/migrate/20250820070256_add_step_up_auth_required_oauth_provider_to_namespace_settings.rb @@ -3,15 +3,13 @@ class AddStepUpAuthRequiredOauthProviderToNamespaceSettings < Gitlab::Database::Migration[2.3] milestone '18.4' - disable_ddl_transaction! - + # rubocop:disable Migration/AddLimitToTextColumns -- Limit is added in 20250820070257_add_text_limit_to_step_up_auth_required_oauth_provider def up add_column :namespace_settings, :step_up_auth_required_oauth_provider, :text - add_text_limit :namespace_settings, :step_up_auth_required_oauth_provider, 255 end + # rubocop:enable Migration/AddLimitToTextColumns def down - remove_text_limit :namespace_settings, :step_up_auth_required_oauth_provider remove_column :namespace_settings, :step_up_auth_required_oauth_provider end end diff --git a/db/migrate/20250820070257_add_text_limit_to_step_up_auth_required_oauth_provider.rb b/db/migrate/20250820070257_add_text_limit_to_step_up_auth_required_oauth_provider.rb new file mode 100644 index 00000000000000..082a5d2018e201 --- /dev/null +++ b/db/migrate/20250820070257_add_text_limit_to_step_up_auth_required_oauth_provider.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddTextLimitToStepUpAuthRequiredOauthProvider < Gitlab::Database::Migration[2.3] + milestone '18.4' + + disable_ddl_transaction! + + def up + add_text_limit :namespace_settings, :step_up_auth_required_oauth_provider, 255 + end + + def down + remove_text_limit :namespace_settings, :step_up_auth_required_oauth_provider + end +end diff --git a/db/schema_migrations/20250820070257 b/db/schema_migrations/20250820070257 new file mode 100644 index 00000000000000..565e59590d9695 --- /dev/null +++ b/db/schema_migrations/20250820070257 @@ -0,0 +1 @@ +1a9e7379e005315bbfe02e13b0e996d76e8a8ddc42ade169846acdfb5e8f4326 \ No newline at end of file -- GitLab From 59224fd01dffb6fb719c5c41309b872dae99076d Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Wed, 27 Aug 2025 09:25:00 +0200 Subject: [PATCH 11/12] docs: Apply suggestions from @idurham - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2710622633 - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2710622622 - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2710622616 - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2710622599 - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2710622574 - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/199423#note_2710622562 --- .../_step_up_authentication.html.haml | 6 ++-- doc/administration/auth/oidc.md | 32 +++++++++++++------ locale/gitlab.pot | 8 ++--- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/app/views/groups/settings/_step_up_authentication.html.haml b/app/views/groups/settings/_step_up_authentication.html.haml index a8205acf28df1f..470d1db15d5aa5 100644 --- a/app/views/groups/settings/_step_up_authentication.html.haml +++ b/app/views/groups/settings/_step_up_authentication.html.haml @@ -1,7 +1,7 @@ -%h5= s_('GroupSettings|Step-up Authentication') +%h5= s_('GroupSettings|Step-up authentication') %p = link_to s_('GroupSettings|What is step-up authentication?'), - help_page_path('administration/auth/oidc.md', anchor: 'enable-step-up-authentication-for-groups'), + help_page_path('administration/auth/oidc.md', anchor: 'force-step-up-authentication-for-a-group'), target: '_blank', rel: 'noopener noreferrer' @@ -12,4 +12,4 @@ { class: 'form-control gl-form-select' } .form-text.gl-text-subtle - = s_('GroupSettings|When enabled, users must complete step-up authentication to access this group and its resources.') + = s_('GroupSettings|Forces users to complete step-up authentication before they can access this group.') diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md index 38a63a9e338e6a..b15b67b03fcc18 100644 --- a/doc/administration/auth/oidc.md +++ b/doc/administration/auth/oidc.md @@ -1637,7 +1637,7 @@ To require step-up authentication for Admin Mode with Microsoft Entra ID: 1. Save the configuration file and restart GitLab for the changes to take effect. -### Enable step-up authentication for groups +### Add a step-up authentication provider for groups {{< history >}} @@ -1645,19 +1645,13 @@ To require step-up authentication for Admin Mode with Microsoft Entra ID: {{< /history >}} -You can also enable step-up authentication for every group in your instance. +You can also add step-up authentication providers available to all groups in your instance. This does not force groups to use step-up authentication, each group must still [set up](#force-step-up-authentication-for-a-group) this feature individually. -To enable step-up authentication for groups: +To add a step-up authentication provider for groups: 1. Edit your GitLab configuration file (`gitlab.yml` or `/etc/gitlab/gitlab.rb`) to enable step-up authentication for an specific OmniAuth provider. - In contrast to [the step-up authentication configuration for Admin Mode](#enable-step-up-authentication-for-admin-mode), - the step-up authentication configuration for groups expects the `namespace` object to be defined - in the `step_up_auth` configuration. This is because the step-up authentication for groups - is not limited to Admin Mode, but applies to all groups in your instance. The rest of the configuration - is similar to the Admin Mode configuration. - ```yaml production: &base omniauth: @@ -1670,6 +1664,9 @@ To enable step-up authentication for groups: allow_authorize_params: ["claims"], # Match this to the parameters defined in `step_up_auth => admin_mode => params` }, step_up_auth: { + # Unlike step-up authentication configuration for Admin Mode, you use the `namespace` + # object. This is because you're adding step-up authentication to access the entire + # group, not just Admin Mode. namespace : { # The `id_token` field defines the claims that must be included with the token. # You can specify claims in one or both of the `required` or `included` fields. @@ -1713,6 +1710,23 @@ To enable step-up authentication for groups: 1. Save the configuration file and restart GitLab for the changes to take effect. +### Force step-up authentication for a group + +You can force users to complete step-up authentication before they access a group. This setting is managed for each group individually, but requires a step-up authentication provider that was previously added for the entire instance. + +Prerequisites: + +- [A step-up authentication provider for groups in your instance](#add-a-step-up-authentication-provider-for-groups). +- You must have the Owner role. + +To force step-up authentication for a group: + +1. On the left sidebar, select **Search or go to** and find your group. +1. Select **Settings > General**. +1. Expand the **Permissions and group features** section. +1. Under Step-up authentication, select an available authentication provider. +1. Select **Save changes**. + ### Add custom documentation links for step-up authentication When step-up authentication fails, GitLab can display custom documentation links to help users understand diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b7543d327d4cfc..71292b582aa914 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -31943,6 +31943,9 @@ msgstr "" msgid "GroupSettings|Failed to save changes." msgstr "" +msgid "GroupSettings|Forces users to complete step-up authentication before they can access this group." +msgstr "" + msgid "GroupSettings|Git abuse rate limit" msgstr "" @@ -32048,7 +32051,7 @@ msgstr "" msgid "GroupSettings|Settings that apply only to enterprise users associated with this group." msgstr "" -msgid "GroupSettings|Step-up Authentication" +msgid "GroupSettings|Step-up authentication" msgstr "" msgid "GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found." @@ -32102,9 +32105,6 @@ msgstr "" msgid "GroupSettings|When disabled, members cannot use runner registration tokens to register runners. Members can use runner authentication tokens instead as the more secure registration method." msgstr "" -msgid "GroupSettings|When enabled, users must complete step-up authentication to access this group and its resources." -msgstr "" - msgid "GroupSettings|You must have the Owner role in the target group" msgstr "" -- GitLab From be86d1131103545d7ba9c77696f56c15ad569d2e Mon Sep 17 00:00:00 2001 From: Gerardo Navarro Date: Wed, 27 Aug 2025 10:18:32 +0200 Subject: [PATCH 12/12] test: Fix failing spec in ci pipeline - https://gitlab.com/gitlab-org/gitlab/-/jobs/11139593843#L1447 - Consolidated feature specs into one for better performance --- .../groups/settings/step_up_authentication_spec.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/spec/features/groups/settings/step_up_authentication_spec.rb b/spec/features/groups/settings/step_up_authentication_spec.rb index 66eddbf580b80b..89db69e64be9ab 100644 --- a/spec/features/groups/settings/step_up_authentication_spec.rb +++ b/spec/features/groups/settings/step_up_authentication_spec.rb @@ -28,20 +28,16 @@ stub_omniauth_setting(enabled: true, providers: [ommiauth_provider_config_oidc]) end - it 'displays step-up authentication settings in group permissions' do + it 'displays step-up authentication settings in group permissions and allows enabling step-up authentication' do visit edit_group_path(group, anchor: 'js-permissions-settings') - expect(page).to have_content('Step-up Authentication') + expect(page).to have_content('Step-up authentication') expect(page).to have_select('group_step_up_auth_required_oauth_provider') - end - - it 'allows enabling step-up authentication' do - visit edit_group_path(group, anchor: 'js-permissions-settings') select 'OpenID Connect', from: 'group_step_up_auth_required_oauth_provider' click_button 'Save changes' - expect(page).to have_content("Group 'group1' was successfully updated.") + expect(page).to have_content("Group '#{group.name}' was successfully updated.") expect(group.reload.namespace_settings.step_up_auth_required_oauth_provider).to eq('openid_connect') end @@ -54,7 +50,7 @@ it 'does not display step-up authentication settings' do visit edit_group_path(group, anchor: 'js-permissions-settings') - expect(page).not_to have_content('Step-up Authentication') + expect(page).not_to have_content('Step-up authentication') expect(page).not_to have_select('group_step_up_auth_required_oauth_provider') end end -- GitLab