diff options
Diffstat (limited to 'lib/gitlab/git_access.rb')
-rw-r--r-- | lib/gitlab/git_access.rb | 143 |
1 files changed, 113 insertions, 30 deletions
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 308f23bc9bc..7679c7e4bb8 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -1,17 +1,52 @@ -# Check a user's access to perform a git action. All public methods in this -# class return an instance of `GitlabAccessStatus` module Gitlab class GitAccess DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive } PUSH_COMMANDS = %w{ git-receive-pack } - attr_reader :actor, :project, :protocol, :user_access + attr_reader :actor, :project, :protocol def initialize(actor, project, protocol) @actor = actor @project = project @protocol = protocol - @user_access = UserAccess.new(user, project: project) + end + + def user + return @user if defined?(@user) + + @user = + case actor + when User + actor + when DeployKey + nil + when Key + actor.user + end + end + + def deploy_key + actor if actor.is_a?(DeployKey) + end + + def can_push_to_branch?(ref) + return false unless user + + if project.protected_branch?(ref) && !project.developers_can_push_to_protected_branch?(ref) + user.can?(:push_code_to_protected_branches, project) + else + user.can?(:push_code, project) + end + end + + def can_read_project? + if user + user.can?(:read_project, project) + elsif deploy_key + deploy_key.projects.include?(project) + else + false + end end def check(cmd, changes = nil) @@ -21,11 +56,11 @@ module Gitlab return build_status_object(false, "No user or key was provided.") end - if user && !user_access.allowed? + if user && !user_allowed? return build_status_object(false, "Your account has been blocked.") end - unless project && (user_access.can_read_project? || deploy_key_can_read_project?) + unless project && can_read_project? return build_status_object(false, 'The project you were looking for could not be found.') end @@ -60,7 +95,7 @@ module Gitlab end def user_download_access_check - unless user_access.can_do_action?(:download_code) + unless user.can?(:download_code, project) return build_status_object(false, "You are not allowed to download code from this project.") end @@ -90,8 +125,46 @@ module Gitlab build_status_object(true) end + def can_user_do_action?(action) + @permission_cache ||= {} + @permission_cache[action] ||= user.can?(action, project) + end + def change_access_check(change) - Checks::ChangeAccess.new(change, user_access: user_access, project: project).exec + oldrev, newrev, ref = change.split(' ') + + action = + if project.protected_branch?(branch_name(ref)) + protected_branch_action(oldrev, newrev, branch_name(ref)) + elsif (tag_ref = tag_name(ref)) && protected_tag?(tag_ref) + # Prevent any changes to existing git tag unless user has permissions + :admin_project + else + :push_code + end + + unless can_user_do_action?(action) + status = + case action + when :force_push_code_to_protected_branches + build_status_object(false, "You are not allowed to force push code to a protected branch on this project.") + when :remove_protected_branches + build_status_object(false, "You are not allowed to deleted protected branches from this project.") + when :push_code_to_protected_branches + build_status_object(false, "You are not allowed to push code to protected branches on this project.") + when :admin_project + build_status_object(false, "You are not allowed to change existing tags on this project.") + else # :push_code + build_status_object(false, "You are not allowed to push code to this project.") + end + return status + end + + build_status_object(true) + end + + def forced_push?(oldrev, newrev) + Gitlab::ForcePushCheck.force_push?(project, oldrev, newrev) end def protocol_allowed? @@ -100,38 +173,48 @@ module Gitlab private - def matching_merge_request?(newrev, branch_name) - Checks::MatchingMergeRequest.new(newrev, branch_name, project).match? + def protected_branch_action(oldrev, newrev, branch_name) + # we dont allow force push to protected branch + if forced_push?(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 + :push_code_to_protected_branches + end end - def deploy_key - actor if actor.is_a?(DeployKey) + def protected_tag?(tag_name) + project.repository.tag_exists?(tag_name) end - def deploy_key_can_read_project? - if deploy_key - deploy_key.projects.include?(project) + def user_allowed? + Gitlab::UserAccess.allowed?(user) + end + + def branch_name(ref) + ref = ref.to_s + if Gitlab::Git.branch_ref?(ref) + Gitlab::Git.ref_name(ref) else - false + nil end end - protected - - def user - return @user if defined?(@user) - - @user = - case actor - when User - actor - when DeployKey - nil - when Key - actor.user - end + def tag_name(ref) + ref = ref.to_s + 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 |