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 ""