summaryrefslogtreecommitdiff
path: root/lib/gitlab/git_access.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/git_access.rb')
-rw-r--r--lib/gitlab/git_access.rb143
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