diff --git a/app/assets/javascripts/members/index.js b/app/assets/javascripts/members/index.js index d707aa543f1b0a9b9ad188c2ba785636e6a20066..cb19c51ee6b7ac05a8933ee22c5c5f24eee00df6 100644 --- a/app/assets/javascripts/members/index.js +++ b/app/assets/javascripts/members/index.js @@ -4,6 +4,7 @@ import Vue from 'vue'; import Vuex from 'vuex'; import VueApollo from 'vue-apollo'; import { parseDataAttributes } from '~/members/utils'; +import { parseBoolean } from '~/lib/utils/common_utils'; import { TABS } from 'ee_else_ce/members/tabs_metadata'; import MembersTabs from './components/members_tabs.vue'; import membersStore from './store'; @@ -39,6 +40,7 @@ export const initMembersApp = (el, context, options) => { namespaceUserLimit, availableRoles, reassignmentCsvPath, + allowInactivePlaceholderReassignment, ...vuexStoreAttributes } = parseDataAttributes(el); @@ -84,6 +86,7 @@ export const initMembersApp = (el, context, options) => { availableRoles, context, reassignmentCsvPath, + allowInactivePlaceholderReassignment: parseBoolean(allowInactivePlaceholderReassignment), group: { id: isGroup ? sourceId : null, name: groupName, diff --git a/app/assets/javascripts/members/placeholders/components/placeholder_actions.vue b/app/assets/javascripts/members/placeholders/components/placeholder_actions.vue index 268ac0590c5943145432c430aa6198fc106970f0..53670d30f734650e66d3a24cb4520ad4003db7e9 100644 --- a/app/assets/javascripts/members/placeholders/components/placeholder_actions.vue +++ b/app/assets/javascripts/members/placeholders/components/placeholder_actions.vue @@ -33,6 +33,11 @@ export default { GlButton, GlCollapsibleListbox, }, + inject: { + allowInactivePlaceholderReassignment: { + default: false, + }, + }, props: { sourceUser: { type: Object, @@ -74,12 +79,14 @@ export default { computed: { queryVariables() { - return { + const query = { first: USERS_PER_PAGE, - active: true, + ...(this.allowInactivePlaceholderReassignment ? {} : { active: true }), humans: true, search: this.search, }; + + return query; }, hasNextPage() { diff --git a/app/helpers/groups/group_members_helper.rb b/app/helpers/groups/group_members_helper.rb index fe44a1b3a3033b678da9e93e5c4759f402886542..e20b7a984f65fa2c622f9bb05caf6acb3f6bb499 100644 --- a/app/helpers/groups/group_members_helper.rb +++ b/app/helpers/groups/group_members_helper.rb @@ -30,7 +30,8 @@ def group_members_app_data( can_approve_access_requests: true, # true for CE, overridden in EE placeholder: placeholder_users, available_roles: available_group_roles(group), - reassignment_csv_path: group_bulk_reassignment_file_path(group) + reassignment_csv_path: group_bulk_reassignment_file_path(group), + allow_inactive_placeholder_reassignment: allow_inactive_placeholder_reassignment?.to_s } end # rubocop:enable Metrics/ParameterLists @@ -92,6 +93,10 @@ def available_group_roles(group) { title: name, value: "static-#{access_level}" } end end + + def allow_inactive_placeholder_reassignment? + Import::UserMapping::BypassConfirmationAuthorizer.new(current_user).allow_mapping_to_inactive_users? + end end Groups::GroupMembersHelper.prepend_mod_with('Groups::GroupMembersHelper') diff --git a/app/services/import/source_users/base_service.rb b/app/services/import/source_users/base_service.rb index 4dbde352bd666dc02b1da98ffcd29df68719c415..bb844d49cb90832a23119a21b4581e853454c078 100644 --- a/app/services/import/source_users/base_service.rb +++ b/app/services/import/source_users/base_service.rb @@ -36,7 +36,8 @@ def track_reassignment_event(event_name, reassign_to_user: import_source_user.re additional_properties: { label: Gitlab::GlobalAnonymousId.user_id(import_source_user.placeholder_user), property: Gitlab::GlobalAnonymousId.user_id(reassign_to_user), - import_type: import_source_user.import_type + import_type: import_source_user.import_type, + reassign_to_user_state: reassign_to_user&.state } ) end diff --git a/app/services/import/source_users/reassign_service.rb b/app/services/import/source_users/reassign_service.rb index 951cf94ffe5984c527ce527bf28535f576b4a270..6c09dfbf07174aafcbccbaaf2b1738d534fad71a 100644 --- a/app/services/import/source_users/reassign_service.rb +++ b/app/services/import/source_users/reassign_service.rb @@ -3,6 +3,8 @@ module Import module SourceUsers class ReassignService < BaseService + include Gitlab::Utils::StrongMemoize + def initialize(import_source_user, assignee_user, current_user:) @import_source_user = import_source_user @current_user = current_user @@ -64,7 +66,7 @@ def run_validations return error_invalid_permissions unless current_user.can?(:admin_import_source_user, import_source_user) return error_namespace_type if root_namespace.user_namespace? - error_invalid_assignee unless valid_assignee?(assignee_user) + error_invalid_assignee unless valid_assignee? end def error_invalid_assignee @@ -76,28 +78,39 @@ def error_invalid_assignee end def invalid_assignee_message - if allow_mapping_to_admins? + if allow_mapping_to_inactive_users? && allow_mapping_to_admins? s_('UserMapping|You can assign users with regular, auditor, or administrator access only.') + elsif allow_mapping_to_admins? + s_('UserMapping|You can assign active users with regular, auditor, or administrator access only.') + elsif allow_mapping_to_inactive_users? + s_('UserMapping|You can assign users with regular or auditor access only.') else - s_('UserMapping|You can assign only active users with regular or auditor access. ' \ - 'To assign users with administrator access, ask your GitLab administrator to ' \ - 'enable the "Allow contribution mapping to administrators" setting.') + s_('UserMapping|You can assign active users with regular or auditor access only.') end end - def valid_assignee?(user) - user.present? && - user.human? && - user.active? && + def valid_assignee? + assignee_user.present? && + assignee_user.human? && + (allow_mapping_to_inactive_users? || assignee_user.active?) && # rubocop:disable Cop/UserAdmin -- This should not be affected by admin mode. # We just want to know whether the user CAN have admin privileges or not. - (allow_mapping_to_admins? ? true : !user.admin?) + (allow_mapping_to_admins? || !assignee_user.admin?) # rubocop:enable Cop/UserAdmin end + def allow_mapping_to_inactive_users? + bypass_confirmation_authorizer.allow_mapping_to_inactive_users? + end + def allow_mapping_to_admins? ::Gitlab::CurrentSettings.allow_contribution_mapping_to_admins? end + + def bypass_confirmation_authorizer + Import::UserMapping::BypassConfirmationAuthorizer.new(current_user) + end + strong_memoize_attr :bypass_confirmation_authorizer end end end diff --git a/config/events/accept_placeholder_user_reassignment.yml b/config/events/accept_placeholder_user_reassignment.yml index 2377654922c3628cddc63d4df9333164c71b818e..027429380db604f54906de564877acb1f5234989 100644 --- a/config/events/accept_placeholder_user_reassignment.yml +++ b/config/events/accept_placeholder_user_reassignment.yml @@ -12,6 +12,8 @@ additional_properties: description: id of the user that is replacing the placeholder import_type: description: the import source e.g. gitlab_migration, github, bitbucket + reassign_to_user_state: + description: the state of the user that is replacing the placeholder e.g. active, blocked product_group: import_and_integrate product_categories: - importers diff --git a/config/events/cancel_placeholder_user_reassignment.yml b/config/events/cancel_placeholder_user_reassignment.yml index 8e31a044289fa09ff5ae1a43d57d526c8566e871..37e1c3d703e7e48de716c828524d2ca68c2491a4 100644 --- a/config/events/cancel_placeholder_user_reassignment.yml +++ b/config/events/cancel_placeholder_user_reassignment.yml @@ -12,6 +12,8 @@ additional_properties: description: id of the user that was to replace the placeholder import_type: description: the import source e.g. gitlab_migration, github, bitbucket + reassign_to_user_state: + description: the state of the user that was to replace the placeholder e.g. active, blocked product_group: import_and_integrate product_categories: - importers diff --git a/config/events/fail_placeholder_user_reassignment.yml b/config/events/fail_placeholder_user_reassignment.yml index 75da6a526805264149e341fc8c14f1eb17641cbe..9f407c47eb43321e41715871dffcc9afd805dc77 100644 --- a/config/events/fail_placeholder_user_reassignment.yml +++ b/config/events/fail_placeholder_user_reassignment.yml @@ -12,6 +12,8 @@ additional_properties: description: id of the user that did not replace the placeholder import_type: description: the import source e.g. gitlab_migration, github, bitbucket + reassign_to_user_state: + description: the state of the user that did not replace the placeholder e.g. active, blocked product_group: import_and_integrate product_categories: - importers diff --git a/config/events/propose_placeholder_user_reassignment.yml b/config/events/propose_placeholder_user_reassignment.yml index 81d14803607f4e84625a88fe09bb6ce11d192360..f4c8f92d24c52bab20405ee16a787325b66a328c 100644 --- a/config/events/propose_placeholder_user_reassignment.yml +++ b/config/events/propose_placeholder_user_reassignment.yml @@ -12,6 +12,8 @@ additional_properties: description: id of the user that is replacing the placeholder import_type: description: the import source e.g. gitlab_migration, github, bitbucket + reassign_to_user_state: + description: the state of the user that is replacing the placeholder e.g. active, blocked product_group: import_and_integrate product_categories: - importers diff --git a/config/events/reject_placeholder_user_reassignment.yml b/config/events/reject_placeholder_user_reassignment.yml index 164da7dc5aab2d034067d6ab920cce95c8d63be3..27cadc9d844266a0fdab40d06f257957948c45cd 100644 --- a/config/events/reject_placeholder_user_reassignment.yml +++ b/config/events/reject_placeholder_user_reassignment.yml @@ -11,7 +11,9 @@ additional_properties: property: description: id of the user that was to replace the placeholder import_type: - description: the import source e.g. gitlab_migration, github, bitbucket, gitea + description: the import source e.g. gitlab_migration, github, bitbucket, gitea + reassign_to_user_state: + description: the state of the user that was to replace the placeholder e.g. active, blocked product_group: import_and_integrate product_categories: - importers diff --git a/lib/import/user_mapping/bypass_confirmation_authorizer.rb b/lib/import/user_mapping/bypass_confirmation_authorizer.rb new file mode 100644 index 0000000000000000000000000000000000000000..ecfcf83f1d4b279e30259e0ae46cd4243802723c --- /dev/null +++ b/lib/import/user_mapping/bypass_confirmation_authorizer.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Import + module UserMapping + class BypassConfirmationAuthorizer + def initialize(reassigning_user) + @reassigning_user = reassigning_user + end + + def allow_mapping_to_inactive_users? + allow_admin_bypass_placeholder_confirmation? + end + + private + + attr_reader :reassigning_user + + def allow_admin_bypass_placeholder_confirmation? + return false unless reassigning_user + return false unless Feature.enabled?(:importer_user_mapping_allow_bypass_of_confirmation, reassigning_user) + + ::Gitlab::CurrentSettings.allow_bypass_placeholder_confirmation && + reassigning_user.can_admin_all_resources? && + Gitlab.config.gitlab.impersonation_enabled + end + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 5ecdbc5f9b6975bd9233fceb32014669c5a6ceb4..bc9041a8f4fd4a44d081e0a31311ff2c6805a246 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -65604,12 +65604,18 @@ msgstr "" msgid "UserMapping|User ID" msgstr "" -msgid "UserMapping|You can assign only active users with regular or auditor access. To assign users with administrator access, ask your GitLab administrator to enable the \"Allow contribution mapping to administrators\" setting." +msgid "UserMapping|You can assign active users with regular or auditor access only." +msgstr "" + +msgid "UserMapping|You can assign active users with regular, auditor, or administrator access only." msgstr "" msgid "UserMapping|You can assign only users with linked SAML and SCIM identities. Ensure the user has signed into GitLab through your SAML SSO provider and has an active SCIM identity for this group." msgstr "" +msgid "UserMapping|You can assign users with regular or auditor access only." +msgstr "" + msgid "UserMapping|You can assign users with regular, auditor, or administrator access only." msgstr "" diff --git a/spec/frontend/members/placeholders/components/placeholder_actions_spec.js b/spec/frontend/members/placeholders/components/placeholder_actions_spec.js index 0e35acb78c7a8ddc22d1bb2bcad746e9272c6904..b97abad27ce3e805a5fb1a5a97f550bab51ac18d 100644 --- a/spec/frontend/members/placeholders/components/placeholder_actions_spec.js +++ b/spec/frontend/members/placeholders/components/placeholder_actions_spec.js @@ -53,7 +53,11 @@ describe('PlaceholderActions', () => { show: jest.fn(), }; - const createComponent = ({ seachUsersQueryHandler = usersQueryHandler, props = {} } = {}) => { + const createComponent = ({ + seachUsersQueryHandler = usersQueryHandler, + props = {}, + provide = {}, + } = {}) => { mockApollo = createMockApollo([ [searchUsersQuery, seachUsersQueryHandler], [importSourceUsersQuery, sourceUsersQueryHandler], @@ -79,6 +83,9 @@ describe('PlaceholderActions', () => { ...defaultProps, ...props, }, + provide: { + ...provide, + }, mocks: { $toast }, }); }; @@ -434,4 +441,31 @@ describe('PlaceholderActions', () => { expect(wrapper.findByText(reassignmentError).exists()).toBe(true); }); }); + + describe('when placeholders are not allowed to be reassigned to inactive users', () => { + it('includes active: true in searchUsersQuery variables', () => { + createComponent({ provide: { allowInactivePlaceholderReassignment: false } }); + + expect(usersQueryHandler).toHaveBeenCalledWith({ + active: true, + first: 20, + humans: true, + search: '', + after: '', + }); + }); + }); + + describe('when placeholders are allowed to be reassigned to inactive users', () => { + it('does not include active in searchUsersQuery variables', () => { + createComponent({ provide: { allowInactivePlaceholderReassignment: true } }); + + expect(usersQueryHandler).toHaveBeenCalledWith({ + first: 20, + humans: true, + search: '', + after: '', + }); + }); + }); }); diff --git a/spec/helpers/groups/group_members_helper_spec.rb b/spec/helpers/groups/group_members_helper_spec.rb index 413b5cbe442709e9d397ab00e5b1cae107a08a1f..c95d70806c049d09ef89182b12783ee15c2f5ab3 100644 --- a/spec/helpers/groups/group_members_helper_spec.rb +++ b/spec/helpers/groups/group_members_helper_spec.rb @@ -65,7 +65,8 @@ group_name: shared_group.name, group_path: shared_group.full_path, can_approve_access_requests: true, - available_roles: available_roles + available_roles: available_roles, + allow_inactive_placeholder_reassignment: 'false' } expect(subject).to include(expected) @@ -183,6 +184,30 @@ ) end end + + context 'when allow_bypass_placeholder_confirmation application setting is disabled' do + before do + expect_next_instance_of(Import::UserMapping::BypassConfirmationAuthorizer, current_user) do |authorizer| + allow(authorizer).to receive(:allow_mapping_to_inactive_users?).and_return(false) + end + end + + it 'returns false' do + expect(subject[:allow_inactive_placeholder_reassignment]).to eq('false') + end + end + + context 'when allow_bypass_placeholder_confirmation application setting is enabled' do + before do + expect_next_instance_of(Import::UserMapping::BypassConfirmationAuthorizer, current_user) do |authorizer| + allow(authorizer).to receive(:allow_mapping_to_inactive_users?).and_return(true) + end + end + + it 'returns true' do + expect(subject[:allow_inactive_placeholder_reassignment]).to eq('true') + end + end end describe '#group_member_header_subtext' do diff --git a/spec/lib/import/user_mapping/bypass_confirmation_authorizer_spec.rb b/spec/lib/import/user_mapping/bypass_confirmation_authorizer_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..f5a62ad7a9eb836a6f7612dd2ef76c97263a14f4 --- /dev/null +++ b/spec/lib/import/user_mapping/bypass_confirmation_authorizer_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Import::UserMapping::BypassConfirmationAuthorizer, feature_category: :importers do + subject(:authorizer) { described_class.new(reassigning_user) } + + describe '#allow_mapping_to_inactive_users?' do + let_it_be(:reassigning_user) { create(:user, :admin) } + + subject(:allow_mapping_to_inactive_users) { authorizer.allow_mapping_to_inactive_users? } + + before do + stub_application_setting(allow_bypass_placeholder_confirmation: true) + stub_config_setting(impersonation_enabled: true) + end + + it 'returns true for admins with bypass application setting and impersonation enabled', :enable_admin_mode do + expect(allow_mapping_to_inactive_users).to be(true) + end + + context 'when the importer_user_mapping_allow_bypass_of_confirmation flag is disabled', :enable_admin_mode do + before do + stub_feature_flags(importer_user_mapping_allow_bypass_of_confirmation: false) + end + + it { is_expected.to be(false) } + end + + context 'when the allow_bypass_placeholder_confirmation application setting is disabled', :enable_admin_mode do + before do + stub_application_setting(allow_bypass_placeholder_confirmation: false) + end + + it { is_expected.to be(false) } + end + + context 'when admin mode is disabled for the admin user' do + it { is_expected.to be(false) } + end + + context 'when the reassigning user is not an admin', :enable_admin_mode do + let!(:reassigning_user) { create(:user) } + + it { is_expected.to be(false) } + end + + context 'when the reassigning user is nil', :enable_admin_mode do + let!(:reassigning_user) { nil } + + it { is_expected.to be(false) } + end + + context 'when user impersonation is disabled', :enable_admin_mode do + before do + stub_config_setting(impersonation_enabled: false) + end + + it { is_expected.to be(false) } + end + end +end diff --git a/spec/services/import/source_users/accept_reassignment_service_spec.rb b/spec/services/import/source_users/accept_reassignment_service_spec.rb index 764f5aa433e3db5264f7a942b4328e1958f4d227..9bfab09c2294e51dec6ec69d9773e8a81e51eee2 100644 --- a/spec/services/import/source_users/accept_reassignment_service_spec.rb +++ b/spec/services/import/source_users/accept_reassignment_service_spec.rb @@ -36,7 +36,8 @@ additional_properties: { label: Gitlab::GlobalAnonymousId.user_id(import_source_user.placeholder_user), property: Gitlab::GlobalAnonymousId.user_id(import_source_user.reassign_to_user), - import_type: import_source_user.import_type + import_type: import_source_user.import_type, + reassign_to_user_state: import_source_user.reassign_to_user.state } ) end @@ -94,7 +95,8 @@ additional_properties: { label: Gitlab::GlobalAnonymousId.user_id(import_source_user.placeholder_user), property: Gitlab::GlobalAnonymousId.user_id(import_source_user.reassign_to_user), - import_type: import_source_user.import_type + import_type: import_source_user.import_type, + reassign_to_user_state: import_source_user.reassign_to_user.state } ) diff --git a/spec/services/import/source_users/cancel_reassignment_service_spec.rb b/spec/services/import/source_users/cancel_reassignment_service_spec.rb index 7a3dd82ef508496f6ccb4799065f380878742f5c..a337c0e1a64935584fa80192caa58d006d195375 100644 --- a/spec/services/import/source_users/cancel_reassignment_service_spec.rb +++ b/spec/services/import/source_users/cancel_reassignment_service_spec.rb @@ -25,7 +25,8 @@ additional_properties: { label: Gitlab::GlobalAnonymousId.user_id(import_source_user.placeholder_user), property: Gitlab::GlobalAnonymousId.user_id(user), - import_type: import_source_user.import_type + import_type: import_source_user.import_type, + reassign_to_user_state: user.state } ) diff --git a/spec/services/import/source_users/reassign_service_spec.rb b/spec/services/import/source_users/reassign_service_spec.rb index b00be0b13a22a2db2b5a0fb32d91320d4d0890e5..e03452ebc03d94c551e3930a025509d8626859a1 100644 --- a/spec/services/import/source_users/reassign_service_spec.rb +++ b/spec/services/import/source_users/reassign_service_spec.rb @@ -15,8 +15,9 @@ import_source_user.namespace.add_owner(user) end - context 'when reassignment is successful' do - it 'returns success' do + shared_examples 'a success response' do + it 'returns success', :aggregate_failures do + expect(Import::ReassignPlaceholderUserRecordsWorker).not_to receive(:perform_async) expect(Notify).to receive_message_chain(:import_source_user_reassign, :deliver_later) expect { result } .to trigger_internal_events('propose_placeholder_user_reassignment') @@ -26,7 +27,8 @@ additional_properties: { label: Gitlab::GlobalAnonymousId.user_id(import_source_user.placeholder_user), property: Gitlab::GlobalAnonymousId.user_id(assignee_user), - import_type: import_source_user.import_type + import_type: import_source_user.import_type, + reassign_to_user_state: assignee_user.state } ) @@ -38,6 +40,10 @@ end end + context 'when the reassigned by and reassigning user are valid' do + it_behaves_like 'a success response' + end + shared_examples 'an error response' do |desc, error:| it "returns #{desc} error" do expect(Notify).not_to receive(:import_source_user_reassign) @@ -68,36 +74,28 @@ let(:assignee_user) { nil } it_behaves_like 'an error response', 'invalid assignee', - error: s_('UserMapping|You can assign only active users with regular or auditor access. ' \ - 'To assign users with administrator access, ask your GitLab administrator to ' \ - 'enable the "Allow contribution mapping to administrators" setting.') + error: s_('UserMapping|You can assign active users with regular or auditor access only.') end context 'when assignee user is not a human' do let(:assignee_user) { create(:user, :bot) } it_behaves_like 'an error response', 'invalid assignee', - error: s_('UserMapping|You can assign only active users with regular or auditor access. ' \ - 'To assign users with administrator access, ask your GitLab administrator to ' \ - 'enable the "Allow contribution mapping to administrators" setting.') + error: s_('UserMapping|You can assign active users with regular or auditor access only.') end context 'when assignee user is not active' do let(:assignee_user) { create(:user, :deactivated) } it_behaves_like 'an error response', 'invalid assignee', - error: s_('UserMapping|You can assign only active users with regular or auditor access. ' \ - 'To assign users with administrator access, ask your GitLab administrator to ' \ - 'enable the "Allow contribution mapping to administrators" setting.') + error: s_('UserMapping|You can assign active users with regular or auditor access only.') end context 'when assignee user is an admin' do let(:assignee_user) { create(:user, :admin) } it_behaves_like 'an error response', 'invalid assignee', - error: s_('UserMapping|You can assign only active users with regular or auditor access. ' \ - 'To assign users with administrator access, ask your GitLab administrator to ' \ - 'enable the "Allow contribution mapping to administrators" setting.') + error: s_('UserMapping|You can assign active users with regular or auditor access only.') end context 'when allow_contribution_mapping_to_admins setting is enabled' do @@ -105,33 +103,66 @@ stub_application_setting(allow_contribution_mapping_to_admins: true) end - context 'and the assignee user is invalid' do + context 'and the assignee user is inactive' do let(:assignee_user) { create(:user, :deactivated) } it_behaves_like 'an error response', 'invalid assignee', - error: s_('UserMapping|You can assign users with regular, auditor, or administrator access only.') + error: s_('UserMapping|You can assign active users with regular, auditor, or administrator access only.') end context 'and the assignee user is an admin' do let(:assignee_user) { create(:user, :admin) } - it 'assigns the user' do - expect(Notify).to receive_message_chain(:import_source_user_reassign, :deliver_later) - expect { result } - .to trigger_internal_events('propose_placeholder_user_reassignment') - .with( - namespace: import_source_user.namespace, - user: current_user, - additional_properties: { - label: Gitlab::GlobalAnonymousId.user_id(import_source_user.placeholder_user), - property: Gitlab::GlobalAnonymousId.user_id(assignee_user), - import_type: import_source_user.import_type - } - ) + it_behaves_like 'a success response' + end + + context 'and mapping to inactive users is allowed' do + before do + expect_next_instance_of(Import::UserMapping::BypassConfirmationAuthorizer, current_user) do |authorizer| + allow(authorizer).to receive(:allow_mapping_to_inactive_users?).and_return(true) + end + end + + context 'and assignee user is not a human' do + let(:assignee_user) { create(:user, :bot) } + + it_behaves_like 'an error response', 'invalid assignee', + error: s_('UserMapping|You can assign users with regular, auditor, or administrator access only.') + end + end + end - expect(result).to be_success + context 'when mapping to inactive users is allowed' do + before do + expect_next_instance_of(Import::UserMapping::BypassConfirmationAuthorizer, current_user) do |authorizer| + allow(authorizer).to receive(:allow_mapping_to_inactive_users?).and_return(true) end end + + context 'and the assignee user is an admin' do + let(:assignee_user) { create(:user, :admin) } + + it_behaves_like 'an error response', 'invalid assignee', + error: s_('UserMapping|You can assign users with regular or auditor access only.') + end + + context 'and the assignee user is blocked' do + let(:assignee_user) { create(:user, :blocked) } + + it_behaves_like 'a success response' + end + + context 'and the assignee user is banned' do + let(:assignee_user) { create(:user, :banned) } + + it_behaves_like 'a success response' + end + + context 'and the assignee user is deactivated' do + let(:assignee_user) { create(:user, :deactivated) } + + it_behaves_like 'a success response' + end end context 'when an error occurs' do @@ -152,7 +183,8 @@ additional_properties: { label: Gitlab::GlobalAnonymousId.user_id(import_source_user.placeholder_user), property: Gitlab::GlobalAnonymousId.user_id(assignee_user), - import_type: import_source_user.import_type + import_type: import_source_user.import_type, + reassign_to_user_state: assignee_user.state } ) end diff --git a/spec/services/import/source_users/reject_reassignment_service_spec.rb b/spec/services/import/source_users/reject_reassignment_service_spec.rb index 7828c6dab310a4fe646ea08cda85a699d78307b8..7de65fd55973298b48ab2f2cb089638a1bc67e10 100644 --- a/spec/services/import/source_users/reject_reassignment_service_spec.rb +++ b/spec/services/import/source_users/reject_reassignment_service_spec.rb @@ -30,7 +30,8 @@ additional_properties: { label: Gitlab::GlobalAnonymousId.user_id(import_source_user.placeholder_user), property: Gitlab::GlobalAnonymousId.user_id(import_source_user.reassign_to_user), - import_type: import_source_user.import_type + import_type: import_source_user.import_type, + reassign_to_user_state: import_source_user.reassign_to_user.state } ) expect(result).to be_success