2018-07-16 12:31:01 -04:00
# frozen_string_literal: true
2018-02-05 19:10:58 -05:00
module Groups
class TransferService < Groups :: BaseService
TransferError = Class . new ( StandardError )
2019-10-31 11:06:41 -04:00
attr_reader :error , :new_parent_group
2018-02-05 19:10:58 -05:00
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-04-15 08:25:48 -04:00
@error = s_ ( " TransferGroup|Transfer failed: %{error_message} " ) % { error_message : e . message }
2018-02-05 19:10:58 -05:00
false
end
private
def proceed_to_transfer
Group . transaction do
update_group_attributes
2019-02-25 18:27:16 -05:00
ensure_ownership
2018-02-05 19:10:58 -05:00
end
2019-02-25 18:27:16 -05:00
2019-12-10 16:08:01 -05:00
post_update_hooks ( @updated_project_ids )
2019-02-25 18:27:16 -05:00
true
2018-02-05 19:10:58 -05:00
end
2019-12-10 16:08:01 -05:00
# Overridden in EE
def post_update_hooks ( updated_project_ids )
2020-08-05 17:09:40 -04:00
refresh_project_authorizations
2019-12-10 16:08:01 -05:00
end
2018-02-05 19:10:58 -05:00
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-10-16 05:07:51 -04:00
raise_transfer_error ( :group_contains_images ) if group_projects_contain_registry_images?
2020-05-22 05:08:09 -04:00
raise_transfer_error ( :cannot_transfer_to_subgroup ) if transfer_to_subgroup?
2020-07-19 23:09:39 -04:00
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
2020-08-06 02:09:38 -04:00
different_root_ancestor? && npm_packages . exists?
2020-07-19 23:09:39 -04:00
end
def different_root_ancestor?
group . root_ancestor != new_parent_group & . root_ancestor
2018-02-05 19:10:58 -05:00
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-05-22 05:08:09 -04:00
def transfer_to_subgroup?
@new_parent_group && \
@group . self_and_descendants . pluck_primary_key . include? ( @new_parent_group . id )
end
2018-02-05 19:10:58 -05:00
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-08-27 11:31:01 -04:00
# rubocop: disable CodeReuse/ActiveRecord
2018-02-05 19:10:58 -05:00
def namespace_with_same_path?
Namespace . exists? ( path : @group . path , parent : @new_parent_group )
end
2018-08-27 11:31:01 -04:00
# rubocop: enable CodeReuse/ActiveRecord
2018-02-05 19:10:58 -05:00
2019-10-16 05:07:51 -04:00
def group_projects_contain_registry_images?
2019-11-08 04:06:07 -05:00
@group . has_container_repository_including_subgroups?
2019-10-16 05:07:51 -04:00
end
2018-02-05 19:10:58 -05:00
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
@group . parent = @new_parent_group
2020-06-04 11:08:21 -04:00
@group . clear_memoization ( :self_and_ancestors_ids )
2018-02-05 19:10:58 -05:00
@group . save!
end
2018-08-27 11:31:01 -04:00
# rubocop: disable CodeReuse/ActiveRecord
2018-02-05 19:10:58 -05:00
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-10 16:08:01 -05:00
projects_to_update = @group
2018-02-05 19:10:58 -05:00
. all_projects
. where ( " visibility_level > ? " , @new_parent_group . visibility_level )
2019-12-10 16:08:01 -05:00
# 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-02-05 19:10:58 -05:00
. update_all ( visibility_level : @new_parent_group . visibility_level )
end
2018-08-27 11:31:01 -04:00
# rubocop: enable CodeReuse/ActiveRecord
2018-02-05 19:10:58 -05:00
2019-02-25 18:27:16 -05:00
def ensure_ownership
return if @new_parent_group
return unless @group . owners . empty?
@group . add_owner ( current_user )
end
2020-08-05 17:09:40 -04:00
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-02-05 19:10:58 -05:00
def raise_transfer_error ( message )
2020-04-21 11:21:10 -04:00
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-05-22 05:08:09 -04:00
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-07-19 23:09:39 -04:00
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-21 11:21:10 -04:00
} . freeze
2018-02-05 19:10:58 -05:00
end
end
end
2019-10-31 11:06:41 -04:00
Groups :: TransferService . prepend_if_ee ( 'EE::Groups::TransferService' )