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
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 )
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 )
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?
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
@group . parent = @new_parent_group
2020-06-23 00:09:42 +05:30
@group . clear_memoization ( :self_and_ancestors_ids )
2018-03-17 18:26:18 +05:30
@group . save!
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
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-03-17 18:26:18 +05:30
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
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.' ) ,
cannot_transfer_to_subgroup : s_ ( 'TransferGroup|Cannot transfer group to one of its subgroup.' )
2020-04-22 19:07:51 +05:30
} . freeze
2018-03-17 18:26:18 +05:30
end
end
end
2019-12-20 00:11:08 +05:30
Groups :: TransferService . prepend_if_ee ( 'EE::Groups::TransferService' )