summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKerri Miller <kerrizor@kerrizor.com>2019-04-29 12:08:57 -0700
committerKerri Miller <kerrizor@kerrizor.com>2019-04-29 14:09:33 -0700
commit3191159a3261b0667976b93a702c2ca9aae2966f (patch)
tree8a3e76df202f75d12281559e836b48dcef490a0a
parent7ae2107d9ebca0adecc8a21cacd1bfb6e89ee3ab (diff)
downloadgitlab-ce-kerrizor/api-support-for-changes-to-forks-50850.tar.gz
Fork the target project if user has read-only access to itkerrizor/api-support-for-changes-to-forks-50850
If the `current_user` doesn't have push access to the target branch, find or create a fork of the target project and create a new branch for the commit(s).
-rw-r--r--lib/api/commits.rb39
-rw-r--r--spec/requests/api/commits_spec.rb28
2 files changed, 64 insertions, 3 deletions
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 65eb9bfb87e..7e237ccbb4b 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -18,6 +18,25 @@ module API
forbidden!("You are not allowed to push into this branch")
end
end
+
+ def find_or_create_fork
+ if fork = user_owned_forks(user_project)
+ fork
+ else
+ ::Projects::ForkService.new(
+ user_project,
+ current_user,
+ namespace: current_user.namespace
+ ).execute
+ end
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def user_owned_forks(base_project)
+ current_user.projects.joins(:fork_network_member)
+ .detect { |p| p.forked_from? base_project }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
end
params do
@@ -102,13 +121,29 @@ module API
optional :force, type: Boolean, default: false, desc: 'When `true` overwrites the target branch with a new commit based on the `start_branch`'
end
post ':id/repository/commits' do
- authorize_push_to_branch!(params[:branch])
+ unless user_access.can_push_to_branch?(params[:branch])
+ unless forked_project = find_or_create_fork
+ forbidden!(
+ "You are not allowed to push to this branch, and we are unable \
+ to find or create a fork"
+ )
+ end
+ end
attrs = declared_params
attrs[:branch_name] = attrs.delete(:branch)
attrs[:start_branch] ||= attrs[:branch_name]
- result = ::Files::MultiService.new(user_project, current_user, attrs).execute
+ if forked_project
+ attrs[:start_project] = user_project
+ attrs[:branch_name] = "#{current_user.name.parameterize}/patch_#{Time.now.to_i}"
+ end
+
+ result = ::Files::MultiService.new(
+ forked_project ? forked_project : user_project,
+ current_user,
+ attrs
+ ).execute
if result[:status] == :success
commit_detail = user_project.repository.commit(result[:result])
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index a132b85b878..ec74593fc8f 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -2,6 +2,8 @@ require 'spec_helper'
require 'mime/types'
describe API::Commits do
+ include ProjectForksHelper
+
let(:user) { create(:user) }
let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
@@ -441,6 +443,30 @@ describe API::Commits do
expect(json_response['title']).to eq(message)
end
+ context 'when a guest only has read access' do
+ let(:project) { create(:project, :public, :repository) }
+
+ context 'but they have a fork of the project' do
+ let!(:forked_project) { fork_project(project, guest, namespace: guest.namespace, repository: true) }
+
+ it 'returns a 201' do
+ expect { post api(url, guest), params: valid_u_params }
+ .not_to change { Project.all.count }
+
+ expect(response).to have_gitlab_http_status(201)
+ end
+ end
+
+ context 'and they do not have a fork of the project' do
+ it 'forks the project and returns a 201' do
+ expect { post api(url, guest), params: valid_u_params }
+ .to change { Project.all.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(201)
+ end
+ end
+ end
+
it 'returns a 400 bad request if file does not exist' do
post api(url, user), params: invalid_u_params
@@ -621,7 +647,7 @@ describe API::Commits do
it 'denies pushing to another branch' do
post api(url, user), params: push_params('other-branch')
- expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response).to have_gitlab_http_status(:bad_request)
end
end
end