diff --git a/CHANGELOG b/CHANGELOG index 0250b4a23c0..410863d3f99 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ v 7.4.0 - Do not delete tmp/repositories itself during clean-up, only its contents - Support for backup uploads to remote storage - Prevent notes polling when there are not notes + - Internal ForkService: Prepare support for fork to a given namespace - API: Add support for forking a project via the API (Bernhard Kaindl) - API: filter project issues by milestone (Julien Bianchi) - Fail harder in the backup script diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index a59311bf942..c4f2d08efe9 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -2,11 +2,9 @@ module Projects class ForkService < BaseService include Gitlab::ShellAdapter - def initialize(project, user) - @from_project, @current_user = project, user - end - def execute + @from_project = @project + project_params = { visibility_level: @from_project.visibility_level, description: @from_project.description, @@ -15,8 +13,15 @@ module Projects project = Project.new(project_params) project.name = @from_project.name project.path = @from_project.path - project.namespace = current_user.namespace - project.creator = current_user + project.namespace = @current_user.namespace + if namespace = @params[:namespace] + project.namespace = namespace + end + project.creator = @current_user + unless @current_user.can?(:create_projects, project.namespace) + project.errors.add(:namespace, 'insufficient access rights') + return project + end # If the project cannot save, we do not want to trigger the project destroy # as this can have the side effect of deleting a repo attached to an existing @@ -27,7 +32,7 @@ module Projects #First save the DB entries as they can be rolled back if the repo fork fails project.build_forked_project_link(forked_to_project_id: project.id, forked_from_project_id: @from_project.id) if project.save - project.team << [current_user, :master] + project.team << [@current_user, :master] end #Now fork the repo unless gitlab_shell.fork_repository(@from_project.path_with_namespace, project.namespace.path) diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index 0edc3a8e807..5c80345c2b3 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -42,10 +42,54 @@ describe Projects::ForkService do end end - def fork_project(from_project, user, fork_success = true) - context = Projects::ForkService.new(from_project, user) - shell = double("gitlab_shell") - shell.stub(fork_repository: fork_success) + describe :fork_to_namespace do + before do + @group_owner = create(:user) + @developer = create(:user) + @project = create(:project, creator_id: @group_owner.id, + star_count: 777, + description: 'Wow, such a cool project!') + @group = create(:group) + @group.add_user(@group_owner, GroupMember::OWNER) + @group.add_user(@developer, GroupMember::DEVELOPER) + @opts = { namespace: @group } + end + + context 'fork project for group' do + it 'group owner successfully forks project into the group' do + to_project = fork_project(@project, @group_owner, true, @opts) + to_project.owner.should == @group + to_project.namespace.should == @group + to_project.name.should == @project.name + to_project.path.should == @project.path + to_project.description.should == @project.description + to_project.star_count.should be_zero + end + end + + context 'fork project for group when user not owner' do + it 'group developer should fail to fork project into the group' do + to_project = fork_project(@project, @developer, true, @opts) + to_project.errors[:namespace].should == ['insufficient access rights'] + end + end + + context 'project already exists in group' do + it 'should fail due to validation, not transaction failure' do + existing_project = create(:project, name: @project.name, + namespace: @group) + to_project = fork_project(@project, @group_owner, true, @opts) + existing_project.persisted?.should be_true + to_project.errors[:base].should == ['Invalid fork destination'] + to_project.errors[:name].should == ['has already been taken'] + to_project.errors[:path].should == ['has already been taken'] + end + end + end + + def fork_project(from_project, user, fork_success = true, params = {}) + context = Projects::ForkService.new(from_project, user, params) + shell = double('gitlab_shell').stub(fork_repository: fork_success) context.stub(gitlab_shell: shell) context.execute end