2018-11-18 11:00:15 +05:30
# frozen_string_literal: true
2018-03-17 18:26:18 +05:30
module Groups
class TransferService < Groups :: BaseService
TransferError = Class . new ( StandardError )
2019-12-26 22:10:19 +05:30
attr_reader :error , :new_parent_group
2018-03-17 18:26:18 +05:30
def initialize ( group , user , params = { } )
super
@error = nil
end
def execute ( new_parent_group )
@new_parent_group = new_parent_group
ensure_allowed_transfer
proceed_to_transfer
rescue TransferError , ActiveRecord :: RecordInvalid , Gitlab :: UpdatePathError = > e
@group . errors . clear
2019-07-31 22:56:46 +05:30
@error = s_ ( " TransferGroup|Transfer failed: %{error_message} " ) % { error_message : e . message }
2018-03-17 18:26:18 +05:30
false
end
private
def proceed_to_transfer
Group . transaction do
update_group_attributes
2019-07-07 11:18:12 +05:30
ensure_ownership
2021-02-22 17:27:13 +05:30
update_integrations
2018-03-17 18:26:18 +05:30
end
2019-07-07 11:18:12 +05:30
2019-12-20 00:11:08 +05:30
post_update_hooks ( @updated_project_ids )
2021-02-22 17:27:13 +05:30
propagate_integrations
2019-12-20 00:11:08 +05:30
2019-07-07 11:18:12 +05:30
true
2018-03-17 18:26:18 +05:30
end
2019-12-20 00:11:08 +05:30
# Overridden in EE
def post_update_hooks ( updated_project_ids )
2020-08-18 19:51:02 +05:30
refresh_project_authorizations
2021-01-03 14:25:43 +05:30
refresh_descendant_groups if @new_parent_group
2019-12-20 00:11:08 +05:30
end
2018-03-17 18:26:18 +05:30
def ensure_allowed_transfer
raise_transfer_error ( :group_is_already_root ) if group_is_already_root?
raise_transfer_error ( :same_parent_as_current ) if same_parent?
raise_transfer_error ( :invalid_policies ) unless valid_policies?
raise_transfer_error ( :namespace_with_same_path ) if namespace_with_same_path?
2019-12-21 20:55:43 +05:30
raise_transfer_error ( :group_contains_images ) if group_projects_contain_registry_images?
2020-06-23 00:09:42 +05:30
raise_transfer_error ( :cannot_transfer_to_subgroup ) if transfer_to_subgroup?
2020-10-24 23:57:45 +05:30
raise_transfer_error ( :group_contains_npm_packages ) if group_with_npm_packages?
end
def group_with_npm_packages?
return false unless group . packages_feature_enabled?
npm_packages = :: Packages :: GroupPackagesFinder . new ( current_user , group , package_type : :npm ) . execute
different_root_ancestor? && npm_packages . exists?
end
def different_root_ancestor?
group . root_ancestor != new_parent_group & . root_ancestor
2018-03-17 18:26:18 +05:30
end
def group_is_already_root?
! @new_parent_group && ! @group . has_parent?
end
def same_parent?
@new_parent_group && @new_parent_group . id == @group . parent_id
end
2020-06-23 00:09:42 +05:30
def transfer_to_subgroup?
@new_parent_group && \
@group . self_and_descendants . pluck_primary_key . include? ( @new_parent_group . id )
end
2018-03-17 18:26:18 +05:30
def valid_policies?
return false unless can? ( current_user , :admin_group , @group )
if @new_parent_group
can? ( current_user , :create_subgroup , @new_parent_group )
else
can? ( current_user , :create_group )
end
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
def namespace_with_same_path?
Namespace . exists? ( path : @group . path , parent : @new_parent_group )
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
2019-12-21 20:55:43 +05:30
def group_projects_contain_registry_images?
2019-12-26 22:10:19 +05:30
@group . has_container_repository_including_subgroups?
2019-12-21 20:55:43 +05:30
end
2018-03-17 18:26:18 +05:30
def update_group_attributes
if @new_parent_group && @new_parent_group . visibility_level < @group . visibility_level
update_children_and_projects_visibility
@group . visibility_level = @new_parent_group . visibility_level
end
2021-01-03 14:25:43 +05:30
update_two_factor_authentication if @new_parent_group
2018-03-17 18:26:18 +05:30
@group . parent = @new_parent_group
2020-06-23 00:09:42 +05:30
@group . clear_memoization ( :self_and_ancestors_ids )
2021-09-04 01:27:46 +05:30
@group . clear_memoization ( :root_ancestor ) if different_root_ancestor?
2021-01-03 14:25:43 +05:30
inherit_group_shared_runners_settings
2018-03-17 18:26:18 +05:30
@group . save!
2021-09-04 01:27:46 +05:30
# #reload is called to make sure traversal_ids are reloaded
@group . reload # rubocop:disable Cop/ActiveRecordAssociationReload
2018-03-17 18:26:18 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
def update_children_and_projects_visibility
descendants = @group . descendants . where ( " visibility_level > ? " , @new_parent_group . visibility_level )
Group
. where ( id : descendants . select ( :id ) )
. update_all ( visibility_level : @new_parent_group . visibility_level )
2019-12-20 00:11:08 +05:30
projects_to_update = @group
2018-03-17 18:26:18 +05:30
. all_projects
. where ( " visibility_level > ? " , @new_parent_group . visibility_level )
2019-12-20 00:11:08 +05:30
# Used in post_update_hooks in EE. Must use pluck (and not select)
# here as after we perform the update below we won't be able to find
# these records again.
@updated_project_ids = projects_to_update . pluck ( :id )
projects_to_update
2018-03-17 18:26:18 +05:30
. update_all ( visibility_level : @new_parent_group . visibility_level )
end
2021-01-03 14:25:43 +05:30
def update_two_factor_authentication
return if namespace_parent_allows_two_factor_auth
@group . require_two_factor_authentication = false
end
def refresh_descendant_groups
return if namespace_parent_allows_two_factor_auth
if @group . descendants . where ( require_two_factor_authentication : true ) . any?
DisallowTwoFactorForSubgroupsWorker . perform_async ( @group . id )
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
2021-01-03 14:25:43 +05:30
def namespace_parent_allows_two_factor_auth
@new_parent_group . namespace_settings . allow_mfa_for_subgroups
end
2019-07-07 11:18:12 +05:30
def ensure_ownership
return if @new_parent_group
return unless @group . owners . empty?
@group . add_owner ( current_user )
end
2020-08-18 19:51:02 +05:30
def refresh_project_authorizations
ProjectAuthorization . where ( project_id : @group . all_projects . select ( :id ) ) . delete_all # rubocop: disable CodeReuse/ActiveRecord
# refresh authorized projects for current_user immediately
current_user . refresh_authorized_projects
# schedule refreshing projects for all the members of the group
@group . refresh_members_authorized_projects
end
2018-03-17 18:26:18 +05:30
def raise_transfer_error ( message )
2020-04-22 19:07:51 +05:30
raise TransferError , localized_error_messages [ message ]
end
def localized_error_messages
{
database_not_supported : s_ ( 'TransferGroup|Database is not supported.' ) ,
namespace_with_same_path : s_ ( 'TransferGroup|The parent group already has a subgroup with the same path.' ) ,
group_is_already_root : s_ ( 'TransferGroup|Group is already a root group.' ) ,
same_parent_as_current : s_ ( 'TransferGroup|Group is already associated to the parent group.' ) ,
invalid_policies : s_ ( " TransferGroup|You don't have enough permissions. " ) ,
2020-06-23 00:09:42 +05:30
group_contains_images : s_ ( 'TransferGroup|Cannot update the path because there are projects under this group that contain Docker images in their Container Registry. Please remove the images from your projects first and try again.' ) ,
2020-10-24 23:57:45 +05:30
cannot_transfer_to_subgroup : s_ ( 'TransferGroup|Cannot transfer group to one of its subgroup.' ) ,
group_contains_npm_packages : s_ ( 'TransferGroup|Group contains projects with NPM packages.' )
2020-04-22 19:07:51 +05:30
} . freeze
2018-03-17 18:26:18 +05:30
end
2021-01-03 14:25:43 +05:30
def inherit_group_shared_runners_settings
parent_setting = @group . parent & . shared_runners_setting
return unless parent_setting
if @group . shared_runners_setting_higher_than? ( parent_setting )
result = Groups :: UpdateSharedRunnersService . new ( @group , current_user , shared_runners_setting : parent_setting ) . execute
raise TransferError , result [ :message ] unless result [ :status ] == :success
end
end
2021-02-22 17:27:13 +05:30
def update_integrations
2021-06-08 01:23:25 +05:30
@group . integrations . inherit . delete_all
Integration . create_from_active_default_integrations ( @group , :group_id )
2021-02-22 17:27:13 +05:30
end
def propagate_integrations
2021-06-08 01:23:25 +05:30
@group . integrations . inherit . each do | integration |
2021-02-22 17:27:13 +05:30
PropagateIntegrationWorker . perform_async ( integration . id )
end
end
2018-03-17 18:26:18 +05:30
end
end
2019-12-20 00:11:08 +05:30
2021-06-08 01:23:25 +05:30
Groups :: TransferService . prepend_mod_with ( 'Groups::TransferService' )