diff options
author | Kerri Miller <kerrizor@kerrizor.com> | 2019-04-29 22:08:57 +0300 |
---|---|---|
committer | Kerri Miller <kerrizor@kerrizor.com> | 2019-04-30 00:09:33 +0300 |
commit | 3191159a3261b0667976b93a702c2ca9aae2966f (patch) | |
tree | 8a3e76df202f75d12281559e836b48dcef490a0a | |
parent | 7ae2107d9ebca0adecc8a21cacd1bfb6e89ee3ab (diff) |
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.rb | 39 | ||||
-rw-r--r-- | spec/requests/api/commits_spec.rb | 28 |
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 |