Commit 9e6466c78442075e0f8fb8fe317ffe53dfcb5a95
Exists in
spb-stable
and in
2 other branches
Merge branch 'api-remove-branch' into 'master'
New rules for protected branches This MR change permissions logic for branch removal. Changes listed below: Before * developer can remove branch with terminal but not in UI * masters can remove any branch with UI even protected one * force-push to protected branch is not allowed but remove is allowed After * none can force push or remove protected branches * developers and masters can remove normal branches with console and UI
Showing
7 changed files
with
90 additions
and
22 deletions
Show diff stats
app/controllers/projects/branches_controller.rb
| @@ -4,8 +4,7 @@ class Projects::BranchesController < Projects::ApplicationController | @@ -4,8 +4,7 @@ class Projects::BranchesController < Projects::ApplicationController | ||
| 4 | before_filter :require_non_empty_project | 4 | before_filter :require_non_empty_project |
| 5 | 5 | ||
| 6 | before_filter :authorize_code_access! | 6 | before_filter :authorize_code_access! |
| 7 | - before_filter :authorize_push!, only: [:create] | ||
| 8 | - before_filter :authorize_admin_project!, only: [:destroy] | 7 | + before_filter :authorize_push!, only: [:create, :destroy] |
| 9 | 8 | ||
| 10 | def index | 9 | def index |
| 11 | @branches = Kaminari.paginate_array(@repository.branches).page(params[:page]).per(30) | 10 | @branches = Kaminari.paginate_array(@repository.branches).page(params[:page]).per(30) |
| @@ -22,11 +21,7 @@ class Projects::BranchesController < Projects::ApplicationController | @@ -22,11 +21,7 @@ class Projects::BranchesController < Projects::ApplicationController | ||
| 22 | end | 21 | end |
| 23 | 22 | ||
| 24 | def destroy | 23 | def destroy |
| 25 | - branch = @repository.find_branch(params[:id]) | ||
| 26 | - | ||
| 27 | - if branch && @repository.rm_branch(branch.name) | ||
| 28 | - Event.create_ref_event(@project, current_user, branch, 'rm') | ||
| 29 | - end | 24 | + DeleteBranchService.new.execute(project, params[:id], current_user) |
| 30 | 25 | ||
| 31 | respond_to do |format| | 26 | respond_to do |format| |
| 32 | format.html { redirect_to project_branches_path(@project) } | 27 | format.html { redirect_to project_branches_path(@project) } |
| @@ -0,0 +1,11 @@ | @@ -0,0 +1,11 @@ | ||
| 1 | +module BranchesHelper | ||
| 2 | + def can_remove_branch?(project, branch_name) | ||
| 3 | + if project.protected_branch? branch_name | ||
| 4 | + false | ||
| 5 | + elsif branch_name == project.repository.root_ref | ||
| 6 | + false | ||
| 7 | + else | ||
| 8 | + can?(current_user, :push_code, project) | ||
| 9 | + end | ||
| 10 | + end | ||
| 11 | +end |
| @@ -0,0 +1,46 @@ | @@ -0,0 +1,46 @@ | ||
| 1 | +class DeleteBranchService | ||
| 2 | + def execute(project, branch_name, current_user) | ||
| 3 | + repository = project.repository | ||
| 4 | + branch = repository.find_branch(branch_name) | ||
| 5 | + | ||
| 6 | + # No such branch | ||
| 7 | + unless branch | ||
| 8 | + return error('No such branch') | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + if branch_name == repository.root_ref | ||
| 12 | + return error('Cannot remove HEAD branch') | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + # Dont allow remove of protected branch | ||
| 16 | + if project.protected_branch?(branch_name) | ||
| 17 | + return error('Protected branch cant be removed') | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | + # Dont allow user to remove branch if he is not allowed to push | ||
| 21 | + unless current_user.can?(:push_code, project) | ||
| 22 | + return error('You dont have push access to repo') | ||
| 23 | + end | ||
| 24 | + | ||
| 25 | + if repository.rm_branch(branch_name) | ||
| 26 | + Event.create_ref_event(project, current_user, branch, 'rm') | ||
| 27 | + success('Branch was removed') | ||
| 28 | + else | ||
| 29 | + return error('Failed to remove branch') | ||
| 30 | + end | ||
| 31 | + end | ||
| 32 | + | ||
| 33 | + def error(message) | ||
| 34 | + { | ||
| 35 | + message: message, | ||
| 36 | + state: :error | ||
| 37 | + } | ||
| 38 | + end | ||
| 39 | + | ||
| 40 | + def success(message) | ||
| 41 | + { | ||
| 42 | + message: message, | ||
| 43 | + state: :success | ||
| 44 | + } | ||
| 45 | + end | ||
| 46 | +end |
app/views/projects/branches/_branch.html.haml
| @@ -16,8 +16,8 @@ | @@ -16,8 +16,8 @@ | ||
| 16 | %i.icon-copy | 16 | %i.icon-copy |
| 17 | Compare | 17 | Compare |
| 18 | 18 | ||
| 19 | - - if can?(current_user, :admin_project, @project) && branch.name != @repository.root_ref | ||
| 20 | - = link_to project_branch_path(@project, branch.name), class: 'btn btn-grouped btn-small remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do | 19 | + - if can_remove_branch?(@project, branch.name) |
| 20 | + = link_to project_branch_path(@project, branch.name), class: 'btn btn-grouped btn-small btn-remove remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do | ||
| 21 | %i.icon-trash | 21 | %i.icon-trash |
| 22 | 22 | ||
| 23 | - if commit | 23 | - if commit |
app/views/projects/protected_branches/index.html.haml
| @@ -4,12 +4,12 @@ | @@ -4,12 +4,12 @@ | ||
| 4 | = render "projects/branches/filter" | 4 | = render "projects/branches/filter" |
| 5 | .col-md-9 | 5 | .col-md-9 |
| 6 | .bs-callout.bs-callout-info | 6 | .bs-callout.bs-callout-info |
| 7 | - %p Protected branches designed to prevent push for all except #{link_to "masters", help_permissions_path, class: "vlink"}. | ||
| 8 | - %p This ability allows: | 7 | + %p Protected branches designed to |
| 9 | %ul | 8 | %ul |
| 10 | - %li keep stable branches secured | ||
| 11 | - %li forced code review before merge to protected branches | ||
| 12 | - %li prevents branch from force push | 9 | + %li prevent push for all except #{link_to "masters", help_permissions_path, class: "vlink"}. |
| 10 | + %li prevent branch from force push | ||
| 11 | + %li prevent branch from removal | ||
| 12 | + %p This ability allows to keep stable branches secured and force code review before merge to protected branches | ||
| 13 | %p Read more about project permissions #{link_to "here", help_permissions_path, class: "underlined-link"} | 13 | %p Read more about project permissions #{link_to "here", help_permissions_path, class: "underlined-link"} |
| 14 | 14 | ||
| 15 | - if can? current_user, :admin_project, @project | 15 | - if can? current_user, :admin_project, @project |
lib/api/branches.rb
| @@ -84,6 +84,18 @@ module API | @@ -84,6 +84,18 @@ module API | ||
| 84 | 84 | ||
| 85 | present @branch, with: Entities::RepoObject, project: user_project | 85 | present @branch, with: Entities::RepoObject, project: user_project |
| 86 | end | 86 | end |
| 87 | + | ||
| 88 | + # Delete branch | ||
| 89 | + # | ||
| 90 | + # Parameters: | ||
| 91 | + # id (required) - The ID of a project | ||
| 92 | + # branch (required) - The name of the branch | ||
| 93 | + # Example Request: | ||
| 94 | + # DELETE /projects/:id/repository/branches/:branch | ||
| 95 | + delete ":id/repository/branches/:branch" do | ||
| 96 | + authorize_push_project | ||
| 97 | + DeleteBranchService.new.execute(user_project, params[:branch], current_user) | ||
| 98 | + end | ||
| 87 | end | 99 | end |
| 88 | end | 100 | end |
| 89 | end | 101 | end |
lib/gitlab/git_access.rb
| @@ -44,14 +44,18 @@ module Gitlab | @@ -44,14 +44,18 @@ module Gitlab | ||
| 44 | def push_allowed?(user, project, ref, oldrev, newrev, forced_push) | 44 | def push_allowed?(user, project, ref, oldrev, newrev, forced_push) |
| 45 | if user && user_allowed?(user) | 45 | if user && user_allowed?(user) |
| 46 | action = if project.protected_branch?(ref) | 46 | action = if project.protected_branch?(ref) |
| 47 | - if forced_push.to_s == 'true' | ||
| 48 | - :force_push_code_to_protected_branches | ||
| 49 | - else | ||
| 50 | - :push_code_to_protected_branches | ||
| 51 | - end | ||
| 52 | - else | ||
| 53 | - :push_code | ||
| 54 | - end | 47 | + # we dont allow force push to protected branch |
| 48 | + if forced_push.to_s == 'true' | ||
| 49 | + :force_push_code_to_protected_branches | ||
| 50 | + # and we dont allow remove of protected branch | ||
| 51 | + elsif newrev =~ /0000000/ | ||
| 52 | + :remove_protected_branches | ||
| 53 | + else | ||
| 54 | + :push_code_to_protected_branches | ||
| 55 | + end | ||
| 56 | + else | ||
| 57 | + :push_code | ||
| 58 | + end | ||
| 55 | user.can?(action, project) | 59 | user.can?(action, project) |
| 56 | else | 60 | else |
| 57 | false | 61 | false |