diff options
Diffstat (limited to 'lib/gitlab/git_access.rb')
-rw-r--r-- | lib/gitlab/git_access.rb | 136 |
1 files changed, 88 insertions, 48 deletions
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index b768a99a0e8..cb69e4b13d3 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -5,95 +5,129 @@ module Gitlab attr_reader :params, :project, :git_cmd, :user - def allowed?(actor, cmd, project, changes = nil) + def self.can_push_to_branch?(user, project, ref) + return false unless user + + if project.protected_branch?(ref) && + !(project.developers_can_push_to_protected_branch?(ref) && project.team.developer?(user)) + user.can?(:push_code_to_protected_branches, project) + else + user.can?(:push_code, project) + end + end + + def check(actor, cmd, project, changes = nil) case cmd when *DOWNLOAD_COMMANDS + download_access_check(actor, project) + when *PUSH_COMMANDS if actor.is_a? User - download_allowed?(actor, project) + push_access_check(actor, project, changes) elsif actor.is_a? DeployKey - actor.projects.include?(project) + return build_status_object(false, "Deploy key not allowed to push") elsif actor.is_a? Key - download_allowed?(actor.user, project) + push_access_check(actor.user, project, changes) else raise 'Wrong actor' end - when *PUSH_COMMANDS - if actor.is_a? User - push_allowed?(actor, project, changes) - elsif actor.is_a? DeployKey - # Deploy key not allowed to push - return false - elsif actor.is_a? Key - push_allowed?(actor.user, project, changes) + else + return build_status_object(false, "Wrong command") + end + end + + def download_access_check(actor, project) + if actor.is_a?(User) + user_download_access_check(actor, project) + elsif actor.is_a?(DeployKey) + if actor.projects.include?(project) + build_status_object(true) else - raise 'Wrong actor' + build_status_object(false, "Deploy key not allowed to access this project") end + elsif actor.is_a? Key + user_download_access_check(actor.user, project) else - false + raise 'Wrong actor' end end - def download_allowed?(user, project) - if user && user_allowed?(user) - user.can?(:download_code, project) + def user_download_access_check(user, project) + if user && user_allowed?(user) && user.can?(:download_code, project) + build_status_object(true) else - false + build_status_object(false, "You don't have access") end end - def push_allowed?(user, project, changes) - return false unless user && user_allowed?(user) - return true if changes.blank? + def push_access_check(user, project, changes) + unless user && user_allowed?(user) + return build_status_object(false, "You don't have access") + end + + if changes.blank? + return build_status_object(true) + end + + unless project.repository.exists? + return build_status_object(false, "Repository does not exist") + end changes = changes.lines if changes.kind_of?(String) # Iterate over all changes to find if user allowed all of them to be applied - changes.each do |change| - unless change_allowed?(user, project, change) + changes.map(&:strip).reject(&:blank?).each do |change| + status = change_access_check(user, project, change) + unless status.allowed? # If user does not have access to make at least one change - cancel all push - return false + return status end end - # If user has access to make all changes - true + return build_status_object(true) end - def change_allowed?(user, project, change) + def change_access_check(user, project, change) oldrev, newrev, ref = change.split(' ') action = if project.protected_branch?(branch_name(ref)) - # we dont allow force push to protected branch - if forced_push?(project, oldrev, newrev) - :force_push_code_to_protected_branches - # and we dont allow remove of protected branch - elsif newrev =~ /0000000/ - :remove_protected_branches - else - :push_code_to_protected_branches - end - elsif project.repository && project.repository.tag_names.include?(tag_name(ref)) + protected_branch_action(project, oldrev, newrev, branch_name(ref)) + elsif protected_tag?(project, tag_name(ref)) # Prevent any changes to existing git tag unless user has permissions :admin_project else :push_code end - user.can?(action, project) + if user.can?(action, project) + build_status_object(true) + else + build_status_object(false, "You don't have permission") + end end def forced_push?(project, oldrev, newrev) - return false if project.empty_repo? + Gitlab::ForcePushCheck.force_push?(project, oldrev, newrev) + end - if oldrev !~ /00000000/ && newrev !~ /00000000/ - missed_refs = IO.popen(%W(git --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev})).read - missed_refs.split("\n").size > 0 + private + + def protected_branch_action(project, oldrev, newrev, branch_name) + # we dont allow force push to protected branch + if forced_push?(project, oldrev, newrev) + :force_push_code_to_protected_branches + elsif Gitlab::Git.blank_ref?(newrev) + # and we dont allow remove of protected branch + :remove_protected_branches + elsif project.developers_can_push_to_protected_branch?(branch_name) + :push_code else - false + :push_code_to_protected_branches end end - private + def protected_tag?(project, tag_name) + project.repository.tag_names.include?(tag_name) + end def user_allowed?(user) Gitlab::UserAccess.allowed?(user) @@ -101,8 +135,8 @@ module Gitlab def branch_name(ref) ref = ref.to_s - if ref.start_with?('refs/heads') - ref.sub(%r{\Arefs/heads/}, '') + if Gitlab::Git.branch_ref?(ref) + Gitlab::Git.ref_name(ref) else nil end @@ -110,11 +144,17 @@ module Gitlab def tag_name(ref) ref = ref.to_s - if ref.start_with?('refs/tags') - ref.sub(%r{\Arefs/tags/}, '') + if Gitlab::Git.tag_ref?(ref) + Gitlab::Git.ref_name(ref) else nil end end + + protected + + def build_status_object(status, message = '') + GitAccessStatus.new(status, message) + end end end |