diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 5bef5ba4af3a9f310eb70a7a12dd38eebd4b28ab..5be96032491ac05eff8f5de4fa466a81188eeb3d 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -19,7 +19,6 @@ def self.admin_not_required_endpoints before_action only: [:index] do push_frontend_feature_flag(:importer_user_mapping, current_user) - push_frontend_feature_flag(:importer_user_mapping_allow_bypass_of_confirmation, @group) end skip_before_action :check_two_factor_requirement, only: :leave diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml index dfc244825a213792fade3a6a34289e4448d94553..f3ccb7f28828adcb8988bf761eefbdae8c930cea 100644 --- a/app/views/groups/settings/_permissions.html.haml +++ b/app/views/groups/settings/_permissions.html.haml @@ -42,6 +42,7 @@ = 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/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 = render_if_exists 'groups/settings/disable_invite_members', f: f, group: @group = render_if_exists 'groups/settings/extensions_marketplace', f: f, group: @group diff --git a/config/feature_flags/wip/group_owner_placeholder_confirmation_bypass.yml b/config/feature_flags/wip/group_owner_placeholder_confirmation_bypass.yml new file mode 100644 index 0000000000000000000000000000000000000000..c8325d2fb0117fafdb77e5121b73e9d691dec083 --- /dev/null +++ b/config/feature_flags/wip/group_owner_placeholder_confirmation_bypass.yml @@ -0,0 +1,10 @@ +--- +name: group_owner_placeholder_confirmation_bypass +description: 'Enables ability for owners of top-level enterprise groups to skip placeholder user confirmation during direct transfer' +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/544024 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189546 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/548946 +milestone: '18.1' +group: group::import +type: wip +default_enabled: false diff --git a/ee/app/assets/javascripts/groups/settings/permissions/components/placeholder_bypass_date_picker.vue b/ee/app/assets/javascripts/groups/settings/permissions/components/placeholder_bypass_date_picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..1374d67eeea6753c0588d51b4f5700bcc6fc405e --- /dev/null +++ b/ee/app/assets/javascripts/groups/settings/permissions/components/placeholder_bypass_date_picker.vue @@ -0,0 +1,52 @@ + + + diff --git a/ee/app/controllers/concerns/ee/groups/params.rb b/ee/app/controllers/concerns/ee/groups/params.rb index f366b8f3d2841ce73884b42cd8af0ff9ebfd5448..af5611d12a058a2bc9798fec331cbee274653189 100644 --- a/ee/app/controllers/concerns/ee/groups/params.rb +++ b/ee/app/controllers/concerns/ee/groups/params.rb @@ -123,7 +123,7 @@ def current_group end def enterprise_bypass_placeholders_allowed? - ::Feature.enabled?(:importer_user_mapping_allow_bypass_of_confirmation, current_group) && + ::Feature.enabled?(:group_owner_placeholder_confirmation_bypass, current_group) && current_group&.domain_verification_available? end end diff --git a/ee/app/views/groups/settings/_placeholder_confirmation_bypass.html.haml b/ee/app/views/groups/settings/_placeholder_confirmation_bypass.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..ce2f6b635ec614983aaf00c4b32fdf5a3b014b17 --- /dev/null +++ b/ee/app/views/groups/settings/_placeholder_confirmation_bypass.html.haml @@ -0,0 +1,10 @@ +- return unless Feature.enabled?(:group_owner_placeholder_confirmation_bypass, @group) +- return unless @group.root? && Gitlab.com? + +- domain_verification_available = @group.domain_verification_available? + +%h5= s_('BulkImports|Placeholder user confirmation') +.form-group.gl-mt-4.gl-mb-6{ role: 'group' } + = f.gitlab_ui_checkbox_component :allow_enterprise_bypass_placeholder_confirmation, + "#{s_('UserMapping|Placeholders can be reassigned to enterprise users without user confirmation.')} #{link_to _('Learn more'), help_page_path('user/project/import/_index.md', anchor: 'reassign-contributions-and-memberships'), target: '_blank', rel: 'noopener noreferrer'}".html_safe, + checkbox_options: { disabled: !domain_verification_available } diff --git a/ee/spec/frontend/groups/settings/permissions/placeholder_bypass_date_picker_spec.js b/ee/spec/frontend/groups/settings/permissions/placeholder_bypass_date_picker_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..2e1ed5437404110b4dc9cb611d39eb46b04c0691 --- /dev/null +++ b/ee/spec/frontend/groups/settings/permissions/placeholder_bypass_date_picker_spec.js @@ -0,0 +1,51 @@ +import { GlDatepicker, GlFormGroup } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import PlaceholderBypassDatePicker from 'ee/groups/settings/permissions/components/placeholder_bypass_date_picker.vue'; + +describe('PlaceholderBypassDatePicker', () => { + let wrapper; + + const defaultProps = { + minDate: new Date('2025-05-28'), + inputName: 'placeholder-user-bypass-datepicker', + disabled: false, + }; + + const createComponent = (props = {}) => { + wrapper = shallowMountExtended(PlaceholderBypassDatePicker, { + propsData: { + ...defaultProps, + ...props, + }, + }); + }; + + it('renders the correct label', () => { + createComponent(); + + expect(wrapper.findComponent(GlFormGroup).attributes('label')).toEqual('Expiry date'); + }); + + it('sets the inputName on GlDatepicker', () => { + createComponent(); + expect(wrapper.findComponent(GlDatepicker).props('inputName')).toEqual(defaultProps.inputName); + }); + + it('sets the minDate on GlDatepicker', () => { + createComponent(); + + expect(wrapper.findComponent(GlDatepicker).props('minDate')).toEqual(defaultProps.minDate); + }); + + it('sets the expiry date on GlDatepicker when currentExpiryDate is provided', () => { + const expiryDate = '2025-06-15'; + createComponent({ currentExpiryDate: expiryDate }); + + expect(wrapper.findComponent(GlDatepicker).props('value')).toEqual(new Date(expiryDate)); + }); + + it('allows disabled state to be overridden', () => { + createComponent({ disabled: false }); + expect(wrapper.findComponent(GlDatepicker).props('disabled')).toBe(false); + }); +}); diff --git a/ee/spec/requests/groups_controller_spec.rb b/ee/spec/requests/groups_controller_spec.rb index 331c94666cd2596fc17ccbe189b1b42d7b2ac0b7..ea151a1488e4e00580ec11cb4a9f99b006cd71b5 100644 --- a/ee/spec/requests/groups_controller_spec.rb +++ b/ee/spec/requests/groups_controller_spec.rb @@ -690,9 +690,9 @@ end end - context 'when the importer_user_mapping_allow_bypass_of_confirmation feature flag is disabled' do + context 'when the group_owner_placeholder_confirmation_bypass feature flag is disabled' do before do - stub_feature_flags(importer_user_mapping_allow_bypass_of_confirmation: false) + stub_feature_flags(group_owner_placeholder_confirmation_bypass: false) end it 'does not change the setting' do diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 76c2424bcaa730c3d9683abe802dba6122561486..49689bbdfcb086dfc4e5691e8a669f5a6228caef 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -11375,6 +11375,9 @@ msgstr "" msgid "Bulk update" msgstr "" +msgid "BulkImports|Placeholder user confirmation" +msgstr "" + msgid "BulkImport|%{count} placeholder user has been matched to a user." msgid_plural "BulkImport|%{count} placeholder users have been matched to users." msgstr[0] "" @@ -25776,6 +25779,9 @@ msgstr "" msgid "Expiring soon" msgstr "" +msgid "Expiry date" +msgstr "" + msgid "Explain current vulnerability." msgstr "" @@ -66884,6 +66890,9 @@ msgstr "" msgid "UserMapping|Placeholders" msgstr "" +msgid "UserMapping|Placeholders can be reassigned to enterprise users without user confirmation." +msgstr "" + msgid "UserMapping|Please upload a valid CSV file." msgstr ""