summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToon Claes <toon@gitlab.com>2018-03-30 14:16:24 +0200
committerToon Claes <toon@gitlab.com>2018-08-20 16:25:02 +0200
commit7c9983c721ab47d25c9d437035e04b385e8af7c9 (patch)
tree283e3de3d64f1c3a61ceee4dbed332096790f760
parent3c80adf5c8486315fa84ac237177c38b9ae625c9 (diff)
downloadgitlab-ce-7c9983c721ab47d25c9d437035e04b385e8af7c9.tar.gz
Allow project owners to set up forking relation through API
Before this change only GitLab admins where allowed to set up forking relation through the API. This changes that so project owners can do this too. Closes gitlab-org/gitlab-ce#40550.
-rw-r--r--changelogs/unreleased/tc-api-fork-owners.yml5
-rw-r--r--doc/api/projects.md4
-rw-r--r--lib/api/projects.rb2
-rw-r--r--spec/requests/api/projects_spec.rb108
4 files changed, 82 insertions, 37 deletions
diff --git a/changelogs/unreleased/tc-api-fork-owners.yml b/changelogs/unreleased/tc-api-fork-owners.yml
new file mode 100644
index 00000000000..feaa3c1705e
--- /dev/null
+++ b/changelogs/unreleased/tc-api-fork-owners.yml
@@ -0,0 +1,5 @@
+---
+title: Allow project owners to set up forking relation through API
+merge_request: 18104
+author:
+type: changed
diff --git a/doc/api/projects.md b/doc/api/projects.md
index bda4164ee92..448562b8dec 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -1413,9 +1413,9 @@ DELETE /projects/:id/hooks/:hook_id
Note the JSON response differs if the hook is available or not. If the project hook
is available before it is returned in the JSON response or an empty response is returned.
-## Admin fork relation
+## Fork relationship
-Allows modification of the forked relationship between existing projects. Available only for admins.
+Allows modification of the forked relationship between existing projects. Available only for project owners and admins.
### Create a forked from/to relation between existing projects
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 5738bf220c6..2801ae918c6 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -386,7 +386,7 @@ module API
requires :forked_from_id, type: String, desc: 'The ID of the project it was forked from'
end
post ":id/fork/:forked_from_id" do
- authenticated_as_admin!
+ authorize! :admin_project, user_project
fork_from_project = find_project!(params[:forked_from_id])
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index c249c881db5..881eff5d09d 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -402,7 +402,7 @@ describe API::Projects do
context 'and with min_access_level' do
before do
- project2.add_master(user2)
+ project2.add_maintainer(user2)
project3.add_developer(user2)
project4.add_reporter(user2)
end
@@ -1121,47 +1121,87 @@ describe API::Projects do
describe 'fork management' do
let(:project_fork_target) { create(:project) }
let(:project_fork_source) { create(:project, :public) }
+ let(:private_project_fork_source) { create(:project, :private) }
describe 'POST /projects/:id/fork/:forked_from_id' do
- let(:new_project_fork_source) { create(:project, :public) }
+ context 'user is a developer' do
+ before do
+ project_fork_target.add_developer(user)
+ end
- it "is not available for non admin users" do
- post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
- expect(response).to have_gitlab_http_status(403)
- end
+ it 'denies project to be forked from an existing project' do
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
- it 'allows project to be forked from an existing project' do
- expect(project_fork_target.forked?).not_to be_truthy
- post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
- expect(response).to have_gitlab_http_status(201)
- project_fork_target.reload
- expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
- expect(project_fork_target.forked_project_link).not_to be_nil
- expect(project_fork_target.forked?).to be_truthy
+ expect(response).to have_gitlab_http_status(403)
+ end
end
it 'refreshes the forks count cache' do
expect(project_fork_source.forks_count).to be_zero
+ end
+
+ context 'user is maintainer' do
+ before do
+ project_fork_target.add_maintainer(user)
+ end
- post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+ it 'allows project to be forked from an existing project' do
+ expect(project_fork_target).not_to be_forked
- expect(project_fork_source.forks_count).to eq(1)
- end
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
+ project_fork_target.reload
- it 'fails if forked_from project which does not exist' do
- post api("/projects/#{project_fork_target.id}/fork/9999", admin)
- expect(response).to have_gitlab_http_status(404)
+ expect(response).to have_gitlab_http_status(201)
+ expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
+ expect(project_fork_target.forked_project_link).to be_present
+ expect(project_fork_target).to be_forked
+ end
+
+ it 'denies project to be forked from a private project' do
+ post api("/projects/#{project_fork_target.id}/fork/#{private_project_fork_source.id}", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
end
- it 'fails with 409 if already forked' do
- post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
- project_fork_target.reload
- expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
- post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin)
- expect(response).to have_gitlab_http_status(409)
- project_fork_target.reload
- expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
- expect(project_fork_target.forked?).to be_truthy
+ context 'user is admin' do
+ it 'allows project to be forked from an existing project' do
+ expect(project_fork_target).not_to be_forked
+
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+
+ expect(response).to have_gitlab_http_status(201)
+ end
+
+ it 'allows project to be forked from a private project' do
+ post api("/projects/#{project_fork_target.id}/fork/#{private_project_fork_source.id}", admin)
+
+ expect(response).to have_gitlab_http_status(201)
+ end
+
+ it 'refreshes the forks count cachce' do
+ expect do
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+ end.to change(project_fork_source, :forks_count).by(1)
+ end
+
+ it 'fails if forked_from project which does not exist' do
+ post api("/projects/#{project_fork_target.id}/fork/9999", admin)
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'fails with 409 if already forked' do
+ other_project_fork_source = create(:project, :public)
+
+ Projects::ForkService.new(project_fork_source, admin).execute(project_fork_target)
+
+ post api("/projects/#{project_fork_target.id}/fork/#{other_project_fork_source.id}", admin)
+ project_fork_target.reload
+
+ expect(response).to have_gitlab_http_status(409)
+ expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
+ expect(project_fork_target).to be_forked
+ end
end
end
@@ -1183,8 +1223,8 @@ describe API::Projects do
before do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
project_fork_target.reload
- expect(project_fork_target.forked_from_project).not_to be_nil
- expect(project_fork_target.forked?).to be_truthy
+ expect(project_fork_target.forked_from_project).to be_present
+ expect(project_fork_target).to be_forked
end
it 'makes forked project unforked' do
@@ -1193,7 +1233,7 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(204)
project_fork_target.reload
expect(project_fork_target.forked_from_project).to be_nil
- expect(project_fork_target.forked?).not_to be_truthy
+ expect(project_fork_target).not_to be_forked
end
it_behaves_like '412 response' do
@@ -1228,8 +1268,8 @@ describe API::Projects do
before do
post api("/projects/#{private_fork.id}/fork/#{project_fork_source.id}", admin)
private_fork.reload
- expect(private_fork.forked_from_project).not_to be_nil
- expect(private_fork.forked?).to be_truthy
+ expect(private_fork.forked_from_project).to be_present
+ expect(private_fork).to be_forked
project_fork_source.reload
expect(project_fork_source.forks.length).to eq(1)
expect(project_fork_source.forks).to include(private_fork)