CE port Refactor Gitlab::Checks::ChangeAccess class
This commit is contained in:
parent
87186cbc92
commit
6ed50b62e7
17 changed files with 698 additions and 510 deletions
38
lib/gitlab/checks/base_checker.rb
Normal file
38
lib/gitlab/checks/base_checker.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Checks
|
||||
class BaseChecker
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
attr_reader :change_access
|
||||
delegate(*ChangeAccess::ATTRIBUTES, to: :change_access)
|
||||
|
||||
def initialize(change_access)
|
||||
@change_access = change_access
|
||||
end
|
||||
|
||||
def validate!
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def deletion?
|
||||
Gitlab::Git.blank_ref?(newrev)
|
||||
end
|
||||
|
||||
def update?
|
||||
!Gitlab::Git.blank_ref?(oldrev) && !deletion?
|
||||
end
|
||||
|
||||
def updated_from_web?
|
||||
protocol == 'web'
|
||||
end
|
||||
|
||||
def tag_exists?
|
||||
project.repository.tag_exists?(tag_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
110
lib/gitlab/checks/branch_check.rb
Normal file
110
lib/gitlab/checks/branch_check.rb
Normal file
|
@ -0,0 +1,110 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Checks
|
||||
class BranchCheck < BaseChecker
|
||||
ERROR_MESSAGES = {
|
||||
delete_default_branch: 'The default branch of a project cannot be deleted.',
|
||||
force_push_protected_branch: 'You are not allowed to force push code to a protected branch on this project.',
|
||||
non_master_delete_protected_branch: 'You are not allowed to delete protected branches from this project. Only a project maintainer or owner can delete a protected branch.',
|
||||
non_web_delete_protected_branch: 'You can only delete protected branches using the web interface.',
|
||||
merge_protected_branch: 'You are not allowed to merge code into protected branches on this project.',
|
||||
push_protected_branch: 'You are not allowed to push code to protected branches on this project.'
|
||||
}.freeze
|
||||
|
||||
LOG_MESSAGES = {
|
||||
delete_default_branch_check: "Checking if default branch is being deleted...",
|
||||
protected_branch_checks: "Checking if you are force pushing to a protected branch...",
|
||||
protected_branch_push_checks: "Checking if you are allowed to push to the protected branch...",
|
||||
protected_branch_deletion_checks: "Checking if you are allowed to delete the protected branch..."
|
||||
}.freeze
|
||||
|
||||
def validate!
|
||||
return unless branch_name
|
||||
|
||||
logger.log_timed(LOG_MESSAGES[:delete_default_branch_check]) do
|
||||
if deletion? && branch_name == project.default_branch
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:delete_default_branch]
|
||||
end
|
||||
end
|
||||
|
||||
protected_branch_checks
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def protected_branch_checks
|
||||
logger.log_timed(LOG_MESSAGES[:protected_branch_checks]) do
|
||||
return unless ProtectedBranch.protected?(project, branch_name) # rubocop:disable Cop/AvoidReturnFromBlocks
|
||||
|
||||
if forced_push?
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:force_push_protected_branch]
|
||||
end
|
||||
end
|
||||
|
||||
if deletion?
|
||||
protected_branch_deletion_checks
|
||||
else
|
||||
protected_branch_push_checks
|
||||
end
|
||||
end
|
||||
|
||||
def protected_branch_deletion_checks
|
||||
logger.log_timed(LOG_MESSAGES[:protected_branch_deletion_checks]) do
|
||||
unless user_access.can_delete_branch?(branch_name)
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:non_master_delete_protected_branch]
|
||||
end
|
||||
|
||||
unless updated_from_web?
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:non_web_delete_protected_branch]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def protected_branch_push_checks
|
||||
logger.log_timed(LOG_MESSAGES[:protected_branch_push_checks]) do
|
||||
if matching_merge_request?
|
||||
unless user_access.can_merge_to_branch?(branch_name) || user_access.can_push_to_branch?(branch_name)
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:merge_protected_branch]
|
||||
end
|
||||
else
|
||||
unless user_access.can_push_to_branch?(branch_name)
|
||||
raise GitAccess::UnauthorizedError, push_to_protected_branch_rejected_message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def push_to_protected_branch_rejected_message
|
||||
if project.empty_repo?
|
||||
empty_project_push_message
|
||||
else
|
||||
ERROR_MESSAGES[:push_protected_branch]
|
||||
end
|
||||
end
|
||||
|
||||
def empty_project_push_message
|
||||
<<~MESSAGE
|
||||
|
||||
A default branch (e.g. master) does not yet exist for #{project.full_path}
|
||||
Ask a project Owner or Maintainer to create a default branch:
|
||||
|
||||
#{project_members_url}
|
||||
|
||||
MESSAGE
|
||||
end
|
||||
|
||||
def project_members_url
|
||||
Gitlab::Routing.url_helpers.project_project_members_url(project)
|
||||
end
|
||||
|
||||
def matching_merge_request?
|
||||
Checks::MatchingMergeRequest.new(newrev, branch_name, project).match?
|
||||
end
|
||||
|
||||
def forced_push?
|
||||
Gitlab::Checks::ForcePush.force_push?(project, oldrev, newrev)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,35 +3,11 @@
|
|||
module Gitlab
|
||||
module Checks
|
||||
class ChangeAccess
|
||||
ERROR_MESSAGES = {
|
||||
push_code: 'You are not allowed to push code to this project.',
|
||||
delete_default_branch: 'The default branch of a project cannot be deleted.',
|
||||
force_push_protected_branch: 'You are not allowed to force push code to a protected branch on this project.',
|
||||
non_master_delete_protected_branch: 'You are not allowed to delete protected branches from this project. Only a project maintainer or owner can delete a protected branch.',
|
||||
non_web_delete_protected_branch: 'You can only delete protected branches using the web interface.',
|
||||
merge_protected_branch: 'You are not allowed to merge code into protected branches on this project.',
|
||||
push_protected_branch: 'You are not allowed to push code to protected branches on this project.',
|
||||
change_existing_tags: 'You are not allowed to change existing tags on this project.',
|
||||
update_protected_tag: 'Protected tags cannot be updated.',
|
||||
delete_protected_tag: 'Protected tags cannot be deleted.',
|
||||
create_protected_tag: 'You are not allowed to create this tag as it is protected.',
|
||||
lfs_objects_missing: 'LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".'
|
||||
}.freeze
|
||||
ATTRIBUTES = %i[user_access project skip_authorization
|
||||
skip_lfs_integrity_check protocol oldrev newrev ref
|
||||
branch_name tag_name logger commits].freeze
|
||||
|
||||
LOG_MESSAGES = {
|
||||
push_checks: "Checking if you are allowed to push...",
|
||||
delete_default_branch_check: "Checking if default branch is being deleted...",
|
||||
protected_branch_checks: "Checking if you are force pushing to a protected branch...",
|
||||
protected_branch_push_checks: "Checking if you are allowed to push to the protected branch...",
|
||||
protected_branch_deletion_checks: "Checking if you are allowed to delete the protected branch...",
|
||||
tag_checks: "Checking if you are allowed to change existing tags...",
|
||||
protected_tag_checks: "Checking if you are creating, updating or deleting a protected tag...",
|
||||
lfs_objects_exist_check: "Scanning repository for blobs stored in LFS and verifying their files have been uploaded to GitLab...",
|
||||
commits_check_file_paths_validation: "Validating commits' file paths...",
|
||||
commits_check: "Validating commit contents..."
|
||||
}.freeze
|
||||
|
||||
attr_reader :user_access, :project, :skip_authorization, :skip_lfs_integrity_check, :protocol, :oldrev, :newrev, :ref, :branch_name, :tag_name, :logger
|
||||
attr_reader(*ATTRIBUTES)
|
||||
|
||||
def initialize(
|
||||
change, user_access:, project:, skip_authorization: false,
|
||||
|
@ -50,206 +26,32 @@ module Gitlab
|
|||
@logger.append_message("Running checks for ref: #{@branch_name || @tag_name}")
|
||||
end
|
||||
|
||||
def exec(skip_commits_check: false)
|
||||
def exec
|
||||
return true if skip_authorization
|
||||
|
||||
push_checks
|
||||
branch_checks
|
||||
tag_checks
|
||||
lfs_objects_exist_check unless skip_lfs_integrity_check
|
||||
commits_check unless skip_commits_check
|
||||
ref_level_checks
|
||||
# Check of commits should happen as the last step
|
||||
# given they're expensive in terms of performance
|
||||
commits_check
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def push_checks
|
||||
logger.log_timed(LOG_MESSAGES[__method__]) do
|
||||
unless can_push?
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:push_code]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def branch_checks
|
||||
return unless branch_name
|
||||
|
||||
logger.log_timed(LOG_MESSAGES[:delete_default_branch_check]) do
|
||||
if deletion? && branch_name == project.default_branch
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:delete_default_branch]
|
||||
end
|
||||
end
|
||||
|
||||
protected_branch_checks
|
||||
end
|
||||
|
||||
def protected_branch_checks
|
||||
logger.log_timed(LOG_MESSAGES[__method__]) do
|
||||
return unless ProtectedBranch.protected?(project, branch_name) # rubocop:disable Cop/AvoidReturnFromBlocks
|
||||
|
||||
if forced_push?
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:force_push_protected_branch]
|
||||
end
|
||||
end
|
||||
|
||||
if deletion?
|
||||
protected_branch_deletion_checks
|
||||
else
|
||||
protected_branch_push_checks
|
||||
end
|
||||
end
|
||||
|
||||
def protected_branch_deletion_checks
|
||||
logger.log_timed(LOG_MESSAGES[__method__]) do
|
||||
unless user_access.can_delete_branch?(branch_name)
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:non_master_delete_protected_branch]
|
||||
end
|
||||
|
||||
unless updated_from_web?
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:non_web_delete_protected_branch]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def protected_branch_push_checks
|
||||
logger.log_timed(LOG_MESSAGES[__method__]) do
|
||||
if matching_merge_request?
|
||||
unless user_access.can_merge_to_branch?(branch_name) || user_access.can_push_to_branch?(branch_name)
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:merge_protected_branch]
|
||||
end
|
||||
else
|
||||
unless user_access.can_push_to_branch?(branch_name)
|
||||
raise GitAccess::UnauthorizedError, push_to_protected_branch_rejected_message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def tag_checks
|
||||
return unless tag_name
|
||||
|
||||
logger.log_timed(LOG_MESSAGES[__method__]) do
|
||||
if tag_exists? && user_access.cannot_do_action?(:admin_project)
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:change_existing_tags]
|
||||
end
|
||||
end
|
||||
|
||||
protected_tag_checks
|
||||
end
|
||||
|
||||
def protected_tag_checks
|
||||
logger.log_timed(LOG_MESSAGES[__method__]) do
|
||||
return unless ProtectedTag.protected?(project, tag_name) # rubocop:disable Cop/AvoidReturnFromBlocks
|
||||
|
||||
raise(GitAccess::UnauthorizedError, ERROR_MESSAGES[:update_protected_tag]) if update?
|
||||
raise(GitAccess::UnauthorizedError, ERROR_MESSAGES[:delete_protected_tag]) if deletion?
|
||||
|
||||
unless user_access.can_create_tag?(tag_name)
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:create_protected_tag]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def commits_check
|
||||
return if deletion? || newrev.nil?
|
||||
return unless should_run_commit_validations?
|
||||
|
||||
logger.log_timed(LOG_MESSAGES[__method__]) do
|
||||
# n+1: https://gitlab.com/gitlab-org/gitlab-ee/issues/3593
|
||||
::Gitlab::GitalyClient.allow_n_plus_1_calls do
|
||||
commits.each do |commit|
|
||||
logger.check_timeout_reached
|
||||
|
||||
commit_check.validate(commit, validations_for_commit(commit))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
logger.log_timed(LOG_MESSAGES[:commits_check_file_paths_validation]) do
|
||||
commit_check.validate_file_paths
|
||||
end
|
||||
end
|
||||
|
||||
# Method overwritten in EE to inject custom validations
|
||||
def validations_for_commit(_)
|
||||
[]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def push_to_protected_branch_rejected_message
|
||||
if project.empty_repo?
|
||||
empty_project_push_message
|
||||
else
|
||||
ERROR_MESSAGES[:push_protected_branch]
|
||||
end
|
||||
end
|
||||
|
||||
def empty_project_push_message
|
||||
<<~MESSAGE
|
||||
|
||||
A default branch (e.g. master) does not yet exist for #{project.full_path}
|
||||
Ask a project Owner or Maintainer to create a default branch:
|
||||
|
||||
#{project_members_url}
|
||||
|
||||
MESSAGE
|
||||
end
|
||||
|
||||
def project_members_url
|
||||
Gitlab::Routing.url_helpers.project_project_members_url(project)
|
||||
end
|
||||
|
||||
def should_run_commit_validations?
|
||||
commit_check.validate_lfs_file_locks?
|
||||
end
|
||||
|
||||
def updated_from_web?
|
||||
protocol == 'web'
|
||||
end
|
||||
|
||||
def tag_exists?
|
||||
project.repository.tag_exists?(tag_name)
|
||||
end
|
||||
|
||||
def forced_push?
|
||||
Gitlab::Checks::ForcePush.force_push?(project, oldrev, newrev)
|
||||
end
|
||||
|
||||
def update?
|
||||
!Gitlab::Git.blank_ref?(oldrev) && !deletion?
|
||||
end
|
||||
|
||||
def deletion?
|
||||
Gitlab::Git.blank_ref?(newrev)
|
||||
end
|
||||
|
||||
def matching_merge_request?
|
||||
Checks::MatchingMergeRequest.new(newrev, branch_name, project).match?
|
||||
end
|
||||
|
||||
def lfs_objects_exist_check
|
||||
logger.log_timed(LOG_MESSAGES[__method__]) do
|
||||
lfs_check = Checks::LfsIntegrity.new(project, newrev, logger.time_left)
|
||||
|
||||
if lfs_check.objects_missing?
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:lfs_objects_missing]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def commit_check
|
||||
@commit_check ||= Gitlab::Checks::CommitCheck.new(project, user_access.user, newrev, oldrev)
|
||||
end
|
||||
|
||||
def commits
|
||||
@commits ||= project.repository.new_commits(newrev)
|
||||
end
|
||||
|
||||
def can_push?
|
||||
user_access.can_do_action?(:push_code) ||
|
||||
user_access.can_push_to_branch?(branch_name)
|
||||
protected
|
||||
|
||||
def ref_level_checks
|
||||
Gitlab::Checks::PushCheck.new(self).validate!
|
||||
Gitlab::Checks::BranchCheck.new(self).validate!
|
||||
Gitlab::Checks::TagCheck.new(self).validate!
|
||||
Gitlab::Checks::LfsCheck.new(self).validate!
|
||||
end
|
||||
|
||||
def commits_check
|
||||
Gitlab::Checks::DiffCheck.new(self).validate!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Checks
|
||||
class CommitCheck
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
attr_reader :project, :user, :newrev, :oldrev
|
||||
|
||||
def initialize(project, user, newrev, oldrev)
|
||||
@project = project
|
||||
@user = user
|
||||
@newrev = newrev
|
||||
@oldrev = oldrev
|
||||
@file_paths = []
|
||||
end
|
||||
|
||||
def validate(commit, validations)
|
||||
return if validations.empty? && path_validations.empty?
|
||||
|
||||
commit.raw_deltas.each do |diff|
|
||||
@file_paths << (diff.new_path || diff.old_path)
|
||||
|
||||
validations.each do |validation|
|
||||
if error = validation.call(diff)
|
||||
raise ::Gitlab::GitAccess::UnauthorizedError, error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate_file_paths
|
||||
path_validations.each do |validation|
|
||||
if error = validation.call(@file_paths)
|
||||
raise ::Gitlab::GitAccess::UnauthorizedError, error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate_lfs_file_locks?
|
||||
strong_memoize(:validate_lfs_file_locks) do
|
||||
project.lfs_enabled? && newrev && oldrev && project.any_lfs_file_locks?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def lfs_file_locks_validation
|
||||
lambda do |paths|
|
||||
lfs_lock = project.lfs_file_locks.where(path: paths).where.not(user_id: user.id).first
|
||||
|
||||
if lfs_lock
|
||||
return "The path '#{lfs_lock.path}' is locked in Git LFS by #{lfs_lock.user.name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def path_validations
|
||||
validate_lfs_file_locks? ? [lfs_file_locks_validation] : []
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
99
lib/gitlab/checks/diff_check.rb
Normal file
99
lib/gitlab/checks/diff_check.rb
Normal file
|
@ -0,0 +1,99 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Checks
|
||||
class DiffCheck < BaseChecker
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
LOG_MESSAGES = {
|
||||
validate_file_paths: "Validating diffs' file paths...",
|
||||
diff_content_check: "Validating diff contents..."
|
||||
}.freeze
|
||||
|
||||
def validate!
|
||||
return unless should_run_diff_validations?
|
||||
return if commits.empty?
|
||||
return unless uses_raw_delta_validations?
|
||||
|
||||
file_paths = []
|
||||
process_raw_deltas do |diff|
|
||||
file_paths << (diff.new_path || diff.old_path)
|
||||
|
||||
validate_diff(diff)
|
||||
end
|
||||
|
||||
validate_file_paths(file_paths)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def should_run_diff_validations?
|
||||
newrev && oldrev && !deletion? && validate_lfs_file_locks?
|
||||
end
|
||||
|
||||
def validate_lfs_file_locks?
|
||||
strong_memoize(:validate_lfs_file_locks) do
|
||||
project.lfs_enabled? && project.any_lfs_file_locks?
|
||||
end
|
||||
end
|
||||
|
||||
def uses_raw_delta_validations?
|
||||
validations_for_diff.present? || path_validations.present?
|
||||
end
|
||||
|
||||
def validate_diff(diff)
|
||||
validations_for_diff.each do |validation|
|
||||
if error = validation.call(diff)
|
||||
raise ::Gitlab::GitAccess::UnauthorizedError, error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Method overwritten in EE to inject custom validations
|
||||
def validations_for_diff
|
||||
[]
|
||||
end
|
||||
|
||||
def path_validations
|
||||
validate_lfs_file_locks? ? [lfs_file_locks_validation] : []
|
||||
end
|
||||
|
||||
def process_raw_deltas
|
||||
logger.log_timed(LOG_MESSAGES[:diff_content_check]) do
|
||||
# n+1: https://gitlab.com/gitlab-org/gitlab-ee/issues/3593
|
||||
::Gitlab::GitalyClient.allow_n_plus_1_calls do
|
||||
commits.each do |commit|
|
||||
logger.check_timeout_reached
|
||||
|
||||
commit.raw_deltas.each do |diff|
|
||||
yield(diff)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate_file_paths(file_paths)
|
||||
logger.log_timed(LOG_MESSAGES[__method__]) do
|
||||
path_validations.each do |validation|
|
||||
if error = validation.call(file_paths)
|
||||
raise ::Gitlab::GitAccess::UnauthorizedError, error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def lfs_file_locks_validation
|
||||
lambda do |paths|
|
||||
lfs_lock = project.lfs_file_locks.where(path: paths).where.not(user_id: user_access.user.id).take
|
||||
|
||||
if lfs_lock
|
||||
return "The path '#{lfs_lock.path}' is locked in Git LFS by #{lfs_lock.user.name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
end
|
22
lib/gitlab/checks/lfs_check.rb
Normal file
22
lib/gitlab/checks/lfs_check.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Checks
|
||||
class LfsCheck < BaseChecker
|
||||
LOG_MESSAGE = "Scanning repository for blobs stored in LFS and verifying their files have been uploaded to GitLab...".freeze
|
||||
ERROR_MESSAGE = 'LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".'.freeze
|
||||
|
||||
def validate!
|
||||
return if skip_lfs_integrity_check
|
||||
|
||||
logger.log_timed(LOG_MESSAGE) do
|
||||
lfs_check = Checks::LfsIntegrity.new(project, newrev, logger.time_left)
|
||||
|
||||
if lfs_check.objects_missing?
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGE
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
22
lib/gitlab/checks/push_check.rb
Normal file
22
lib/gitlab/checks/push_check.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Checks
|
||||
class PushCheck < BaseChecker
|
||||
def validate!
|
||||
logger.log_timed("Checking if you are allowed to push...") do
|
||||
unless can_push?
|
||||
raise GitAccess::UnauthorizedError, 'You are not allowed to push code to this project.'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_push?
|
||||
user_access.can_do_action?(:push_code) ||
|
||||
user_access.can_push_to_branch?(branch_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
46
lib/gitlab/checks/tag_check.rb
Normal file
46
lib/gitlab/checks/tag_check.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Checks
|
||||
class TagCheck < BaseChecker
|
||||
ERROR_MESSAGES = {
|
||||
change_existing_tags: 'You are not allowed to change existing tags on this project.',
|
||||
update_protected_tag: 'Protected tags cannot be updated.',
|
||||
delete_protected_tag: 'Protected tags cannot be deleted.',
|
||||
create_protected_tag: 'You are not allowed to create this tag as it is protected.'
|
||||
}.freeze
|
||||
|
||||
LOG_MESSAGES = {
|
||||
tag_checks: "Checking if you are allowed to change existing tags...",
|
||||
protected_tag_checks: "Checking if you are creating, updating or deleting a protected tag..."
|
||||
}.freeze
|
||||
|
||||
def validate!
|
||||
return unless tag_name
|
||||
|
||||
logger.log_timed(LOG_MESSAGES[:tag_checks]) do
|
||||
if tag_exists? && user_access.cannot_do_action?(:admin_project)
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:change_existing_tags]
|
||||
end
|
||||
end
|
||||
|
||||
protected_tag_checks
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def protected_tag_checks
|
||||
logger.log_timed(LOG_MESSAGES[__method__]) do
|
||||
return unless ProtectedTag.protected?(project, tag_name) # rubocop:disable Cop/AvoidReturnFromBlocks
|
||||
|
||||
raise(GitAccess::UnauthorizedError, ERROR_MESSAGES[:update_protected_tag]) if update?
|
||||
raise(GitAccess::UnauthorizedError, ERROR_MESSAGES[:delete_protected_tag]) if deletion?
|
||||
|
||||
unless user_access.can_create_tag?(tag_name)
|
||||
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:create_protected_tag]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
90
spec/lib/gitlab/checks/branch_check_spec.rb
Normal file
90
spec/lib/gitlab/checks/branch_check_spec.rb
Normal file
|
@ -0,0 +1,90 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Checks::BranchCheck do
|
||||
include_context 'change access checks context'
|
||||
|
||||
describe '#validate!' do
|
||||
it 'does not raise any error' do
|
||||
expect { subject.validate! }.not_to raise_error
|
||||
end
|
||||
|
||||
context 'trying to delete the default branch' do
|
||||
let(:newrev) { '0000000000000000000000000000000000000000' }
|
||||
let(:ref) { 'refs/heads/master' }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'The default branch of a project cannot be deleted.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'protected branches check' do
|
||||
before do
|
||||
allow(ProtectedBranch).to receive(:protected?).with(project, 'master').and_return(true)
|
||||
allow(ProtectedBranch).to receive(:protected?).with(project, 'feature').and_return(true)
|
||||
end
|
||||
|
||||
it 'raises an error if the user is not allowed to do forced pushes to protected branches' do
|
||||
expect(Gitlab::Checks::ForcePush).to receive(:force_push?).and_return(true)
|
||||
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to force push code to a protected branch on this project.')
|
||||
end
|
||||
|
||||
it 'raises an error if the user is not allowed to merge to protected branches' do
|
||||
expect_any_instance_of(Gitlab::Checks::MatchingMergeRequest).to receive(:match?).and_return(true)
|
||||
expect(user_access).to receive(:can_merge_to_branch?).and_return(false)
|
||||
expect(user_access).to receive(:can_push_to_branch?).and_return(false)
|
||||
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to merge code into protected branches on this project.')
|
||||
end
|
||||
|
||||
it 'raises an error if the user is not allowed to push to protected branches' do
|
||||
expect(user_access).to receive(:can_push_to_branch?).and_return(false)
|
||||
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to push code to protected branches on this project.')
|
||||
end
|
||||
|
||||
context 'when project repository is empty' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
it 'raises an error if the user is not allowed to push to protected branches' do
|
||||
expect(user_access).to receive(:can_push_to_branch?).and_return(false)
|
||||
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /Ask a project Owner or Maintainer to create a default branch/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'branch deletion' do
|
||||
let(:newrev) { '0000000000000000000000000000000000000000' }
|
||||
let(:ref) { 'refs/heads/feature' }
|
||||
|
||||
context 'if the user is not allowed to delete protected branches' do
|
||||
it 'raises an error' do
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to delete protected branches from this project. Only a project maintainer or owner can delete a protected branch.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'if the user is allowed to delete protected branches' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'through the web interface' do
|
||||
let(:protocol) { 'web' }
|
||||
|
||||
it 'allows branch deletion' do
|
||||
expect { subject.validate! }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'over SSH or HTTP' do
|
||||
it 'raises an error' do
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only delete protected branches using the web interface.')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,35 +2,44 @@ require 'spec_helper'
|
|||
|
||||
describe Gitlab::Checks::ChangeAccess do
|
||||
describe '#exec' do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:user_access) { Gitlab::UserAccess.new(user, project: project) }
|
||||
let(:oldrev) { 'be93687618e4b132087f430a4d8fc3a609c9b77c' }
|
||||
let(:newrev) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' }
|
||||
let(:ref) { 'refs/heads/master' }
|
||||
let(:changes) { { oldrev: oldrev, newrev: newrev, ref: ref } }
|
||||
let(:protocol) { 'ssh' }
|
||||
let(:timeout) { Gitlab::GitAccess::INTERNAL_TIMEOUT }
|
||||
let(:logger) { Gitlab::Checks::TimedLogger.new(timeout: timeout) }
|
||||
include_context 'change access checks context'
|
||||
|
||||
subject(:change_access) do
|
||||
described_class.new(
|
||||
changes,
|
||||
project: project,
|
||||
user_access: user_access,
|
||||
protocol: protocol,
|
||||
logger: logger
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
subject { change_access }
|
||||
|
||||
context 'without failed checks' do
|
||||
it "doesn't raise an error" do
|
||||
expect { subject.exec }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'calls pushes checks' do
|
||||
expect_any_instance_of(Gitlab::Checks::PushCheck).to receive(:validate!)
|
||||
|
||||
subject.exec
|
||||
end
|
||||
|
||||
it 'calls branches checks' do
|
||||
expect_any_instance_of(Gitlab::Checks::BranchCheck).to receive(:validate!)
|
||||
|
||||
subject.exec
|
||||
end
|
||||
|
||||
it 'calls tags checks' do
|
||||
expect_any_instance_of(Gitlab::Checks::TagCheck).to receive(:validate!)
|
||||
|
||||
subject.exec
|
||||
end
|
||||
|
||||
it 'calls lfs checks' do
|
||||
expect_any_instance_of(Gitlab::Checks::LfsCheck).to receive(:validate!)
|
||||
|
||||
subject.exec
|
||||
end
|
||||
|
||||
it 'calls diff checks' do
|
||||
expect_any_instance_of(Gitlab::Checks::DiffCheck).to receive(:validate!)
|
||||
|
||||
subject.exec
|
||||
end
|
||||
end
|
||||
|
||||
context 'when time limit was reached' do
|
||||
|
@ -45,203 +54,5 @@ describe Gitlab::Checks::ChangeAccess do
|
|||
expect { access.exec }.to raise_error(Gitlab::Checks::TimedLogger::TimeoutError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is not allowed to push to the repo' do
|
||||
it 'raises an error' do
|
||||
expect(user_access).to receive(:can_do_action?).with(:push_code).and_return(false)
|
||||
expect(user_access).to receive(:can_push_to_branch?).with('master').and_return(false)
|
||||
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to push code to this project.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'tags check' do
|
||||
let(:ref) { 'refs/tags/v1.0.0' }
|
||||
|
||||
it 'raises an error if the user is not allowed to update tags' do
|
||||
allow(user_access).to receive(:can_do_action?).with(:push_code).and_return(true)
|
||||
expect(user_access).to receive(:can_do_action?).with(:admin_project).and_return(false)
|
||||
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to change existing tags on this project.')
|
||||
end
|
||||
|
||||
context 'with protected tag' do
|
||||
let!(:protected_tag) { create(:protected_tag, project: project, name: 'v*') }
|
||||
|
||||
context 'as maintainer' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'deletion' do
|
||||
let(:oldrev) { 'be93687618e4b132087f430a4d8fc3a609c9b77c' }
|
||||
let(:newrev) { '0000000000000000000000000000000000000000' }
|
||||
|
||||
it 'is prevented' do
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /cannot be deleted/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'update' do
|
||||
let(:oldrev) { 'be93687618e4b132087f430a4d8fc3a609c9b77c' }
|
||||
let(:newrev) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' }
|
||||
|
||||
it 'is prevented' do
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /cannot be updated/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'creation' do
|
||||
let(:oldrev) { '0000000000000000000000000000000000000000' }
|
||||
let(:newrev) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' }
|
||||
let(:ref) { 'refs/tags/v9.1.0' }
|
||||
|
||||
it 'prevents creation below access level' do
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /allowed to create this tag as it is protected/)
|
||||
end
|
||||
|
||||
context 'when user has access' do
|
||||
let!(:protected_tag) { create(:protected_tag, :developers_can_create, project: project, name: 'v*') }
|
||||
|
||||
it 'allows tag creation' do
|
||||
expect { subject.exec }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'branches check' do
|
||||
context 'trying to delete the default branch' do
|
||||
let(:newrev) { '0000000000000000000000000000000000000000' }
|
||||
let(:ref) { 'refs/heads/master' }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'The default branch of a project cannot be deleted.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'protected branches check' do
|
||||
before do
|
||||
allow(ProtectedBranch).to receive(:protected?).with(project, 'master').and_return(true)
|
||||
allow(ProtectedBranch).to receive(:protected?).with(project, 'feature').and_return(true)
|
||||
end
|
||||
|
||||
it 'raises an error if the user is not allowed to do forced pushes to protected branches' do
|
||||
expect(Gitlab::Checks::ForcePush).to receive(:force_push?).and_return(true)
|
||||
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to force push code to a protected branch on this project.')
|
||||
end
|
||||
|
||||
it 'raises an error if the user is not allowed to merge to protected branches' do
|
||||
expect_any_instance_of(Gitlab::Checks::MatchingMergeRequest).to receive(:match?).and_return(true)
|
||||
expect(user_access).to receive(:can_merge_to_branch?).and_return(false)
|
||||
expect(user_access).to receive(:can_push_to_branch?).and_return(false)
|
||||
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to merge code into protected branches on this project.')
|
||||
end
|
||||
|
||||
it 'raises an error if the user is not allowed to push to protected branches' do
|
||||
expect(user_access).to receive(:can_push_to_branch?).and_return(false)
|
||||
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to push code to protected branches on this project.')
|
||||
end
|
||||
|
||||
context 'when project repository is empty' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
it 'raises an error if the user is not allowed to push to protected branches' do
|
||||
expect(user_access).to receive(:can_push_to_branch?).and_return(false)
|
||||
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /Ask a project Owner or Maintainer to create a default branch/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'branch deletion' do
|
||||
let(:newrev) { '0000000000000000000000000000000000000000' }
|
||||
let(:ref) { 'refs/heads/feature' }
|
||||
|
||||
context 'if the user is not allowed to delete protected branches' do
|
||||
it 'raises an error' do
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to delete protected branches from this project. Only a project maintainer or owner can delete a protected branch.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'if the user is allowed to delete protected branches' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'through the web interface' do
|
||||
let(:protocol) { 'web' }
|
||||
|
||||
it 'allows branch deletion' do
|
||||
expect { subject.exec }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'over SSH or HTTP' do
|
||||
it 'raises an error' do
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only delete protected branches using the web interface.')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'LFS integrity check' do
|
||||
it 'fails if any LFS blobs are missing' do
|
||||
allow_any_instance_of(Gitlab::Checks::LfsIntegrity).to receive(:objects_missing?).and_return(true)
|
||||
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /LFS objects are missing/)
|
||||
end
|
||||
|
||||
it 'succeeds if LFS objects have already been uploaded' do
|
||||
allow_any_instance_of(Gitlab::Checks::LfsIntegrity).to receive(:objects_missing?).and_return(false)
|
||||
|
||||
expect { subject.exec }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'LFS file lock check' do
|
||||
let(:owner) { create(:user) }
|
||||
let!(:lock) { create(:lfs_file_lock, user: owner, project: project, path: 'README') }
|
||||
|
||||
before do
|
||||
allow(project.repository).to receive(:new_commits).and_return(
|
||||
project.repository.commits_between('be93687618e4b132087f430a4d8fc3a609c9b77c', '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51')
|
||||
)
|
||||
end
|
||||
|
||||
context 'with LFS not enabled' do
|
||||
it 'skips the validation' do
|
||||
expect_any_instance_of(Gitlab::Checks::CommitCheck).not_to receive(:validate)
|
||||
|
||||
subject.exec
|
||||
end
|
||||
end
|
||||
|
||||
context 'with LFS enabled' do
|
||||
before do
|
||||
allow(project).to receive(:lfs_enabled?).and_return(true)
|
||||
end
|
||||
|
||||
context 'when change is sent by a different user' do
|
||||
it 'raises an error if the user is not allowed to update the file' do
|
||||
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, "The path 'README' is locked in Git LFS by #{lock.user.name}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when change is sent by the author of the lock' do
|
||||
let(:user) { owner }
|
||||
|
||||
it "doesn't raise any error" do
|
||||
expect { subject.exec }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
51
spec/lib/gitlab/checks/diff_check_spec.rb
Normal file
51
spec/lib/gitlab/checks/diff_check_spec.rb
Normal file
|
@ -0,0 +1,51 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Checks::DiffCheck do
|
||||
include_context 'change access checks context'
|
||||
|
||||
describe '#validate!' do
|
||||
let(:owner) { create(:user) }
|
||||
let!(:lock) { create(:lfs_file_lock, user: owner, project: project, path: 'README') }
|
||||
|
||||
before do
|
||||
allow(project.repository).to receive(:new_commits).and_return(
|
||||
project.repository.commits_between('be93687618e4b132087f430a4d8fc3a609c9b77c', '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51')
|
||||
)
|
||||
end
|
||||
|
||||
context 'with LFS not enabled' do
|
||||
before do
|
||||
allow(project).to receive(:lfs_enabled?).and_return(false)
|
||||
end
|
||||
|
||||
it 'skips the validation' do
|
||||
expect(subject).not_to receive(:validate_diff)
|
||||
expect(subject).not_to receive(:validate_file_paths)
|
||||
|
||||
subject.validate!
|
||||
end
|
||||
end
|
||||
|
||||
context 'with LFS enabled' do
|
||||
before do
|
||||
allow(project).to receive(:lfs_enabled?).and_return(true)
|
||||
end
|
||||
|
||||
context 'when change is sent by a different user' do
|
||||
it 'raises an error if the user is not allowed to update the file' do
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, "The path 'README' is locked in Git LFS by #{lock.user.name}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when change is sent by the author of the lock' do
|
||||
let(:user) { owner }
|
||||
|
||||
it "doesn't raise any error" do
|
||||
expect { subject.validate! }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
52
spec/lib/gitlab/checks/lfs_check_spec.rb
Normal file
52
spec/lib/gitlab/checks/lfs_check_spec.rb
Normal file
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Checks::LfsCheck do
|
||||
include_context 'change access checks context'
|
||||
|
||||
let(:blob_object) { project.repository.blob_at_branch('lfs', 'files/lfs/lfs_object.iso') }
|
||||
|
||||
before do
|
||||
allow_any_instance_of(Gitlab::Git::LfsChanges).to receive(:new_pointers) do
|
||||
[blob_object]
|
||||
end
|
||||
end
|
||||
|
||||
describe '#validate!' do
|
||||
context 'with LFS not enabled' do
|
||||
it 'skips integrity check' do
|
||||
expect_any_instance_of(Gitlab::Git::LfsChanges).not_to receive(:new_pointers)
|
||||
|
||||
subject.validate!
|
||||
end
|
||||
end
|
||||
|
||||
context 'with LFS enabled' do
|
||||
before do
|
||||
allow(project).to receive(:lfs_enabled?).and_return(true)
|
||||
end
|
||||
|
||||
context 'deletion' do
|
||||
let(:changes) { { oldrev: oldrev, ref: ref } }
|
||||
|
||||
it 'skips integrity check' do
|
||||
expect(project.repository).not_to receive(:new_objects)
|
||||
|
||||
subject.validate!
|
||||
end
|
||||
end
|
||||
|
||||
it 'fails if any LFS blobs are missing' do
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /LFS objects are missing/)
|
||||
end
|
||||
|
||||
it 'succeeds if LFS objects have already been uploaded' do
|
||||
lfs_object = create(:lfs_object, oid: blob_object.lfs_oid)
|
||||
create(:lfs_objects_project, project: project, lfs_object: lfs_object)
|
||||
|
||||
expect { subject.validate! }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
22
spec/lib/gitlab/checks/push_check_spec.rb
Normal file
22
spec/lib/gitlab/checks/push_check_spec.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Checks::PushCheck do
|
||||
include_context 'change access checks context'
|
||||
|
||||
describe '#validate!' do
|
||||
it 'does not raise any error' do
|
||||
expect { subject.validate! }.not_to raise_error
|
||||
end
|
||||
|
||||
context 'when the user is not allowed to push to the repo' do
|
||||
it 'raises an error' do
|
||||
expect(user_access).to receive(:can_do_action?).with(:push_code).and_return(false)
|
||||
expect(user_access).to receive(:can_push_to_branch?).with('master').and_return(false)
|
||||
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to push code to this project.')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
64
spec/lib/gitlab/checks/tag_check_spec.rb
Normal file
64
spec/lib/gitlab/checks/tag_check_spec.rb
Normal file
|
@ -0,0 +1,64 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Checks::TagCheck do
|
||||
include_context 'change access checks context'
|
||||
|
||||
describe '#validate!' do
|
||||
let(:ref) { 'refs/tags/v1.0.0' }
|
||||
|
||||
it 'raises an error' do
|
||||
allow(user_access).to receive(:can_do_action?).with(:push_code).and_return(true)
|
||||
expect(user_access).to receive(:can_do_action?).with(:admin_project).and_return(false)
|
||||
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to change existing tags on this project.')
|
||||
end
|
||||
|
||||
context 'with protected tag' do
|
||||
let!(:protected_tag) { create(:protected_tag, project: project, name: 'v*') }
|
||||
|
||||
context 'as maintainer' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'deletion' do
|
||||
let(:oldrev) { 'be93687618e4b132087f430a4d8fc3a609c9b77c' }
|
||||
let(:newrev) { '0000000000000000000000000000000000000000' }
|
||||
|
||||
it 'is prevented' do
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /cannot be deleted/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'update' do
|
||||
let(:oldrev) { 'be93687618e4b132087f430a4d8fc3a609c9b77c' }
|
||||
let(:newrev) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' }
|
||||
|
||||
it 'is prevented' do
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /cannot be updated/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'creation' do
|
||||
let(:oldrev) { '0000000000000000000000000000000000000000' }
|
||||
let(:newrev) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' }
|
||||
let(:ref) { 'refs/tags/v9.1.0' }
|
||||
|
||||
it 'prevents creation below access level' do
|
||||
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /allowed to create this tag as it is protected/)
|
||||
end
|
||||
|
||||
context 'when user has access' do
|
||||
let!(:protected_tag) { create(:protected_tag, :developers_can_create, project: project, name: 'v*') }
|
||||
|
||||
it 'allows tag creation' do
|
||||
expect { subject.validate! }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -302,7 +302,7 @@ describe 'Git HTTP requests' do
|
|||
it 'rejects pushes with 403 Forbidden' do
|
||||
upload(path, env) do |response|
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(response.body).to eq(change_access_error(:push_code))
|
||||
expect(response.body).to eq('You are not allowed to push code to this project.')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -60,9 +60,4 @@ module GitHttpHelpers
|
|||
message = Gitlab::GitAccessWiki::ERROR_MESSAGES[error_key]
|
||||
message || raise("GitAccessWiki error message key '#{error_key}' not found")
|
||||
end
|
||||
|
||||
def change_access_error(error_key)
|
||||
message = Gitlab::Checks::ChangeAccess::ERROR_MESSAGES[error_key]
|
||||
message || raise("ChangeAccess error message key '#{error_key}' not found")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
shared_context 'change access checks context' do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:user_access) { Gitlab::UserAccess.new(user, project: project) }
|
||||
let(:oldrev) { 'be93687618e4b132087f430a4d8fc3a609c9b77c' }
|
||||
let(:newrev) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' }
|
||||
let(:ref) { 'refs/heads/master' }
|
||||
let(:changes) { { oldrev: oldrev, newrev: newrev, ref: ref } }
|
||||
let(:protocol) { 'ssh' }
|
||||
let(:timeout) { Gitlab::GitAccess::INTERNAL_TIMEOUT }
|
||||
let(:logger) { Gitlab::Checks::TimedLogger.new(timeout: timeout) }
|
||||
let(:change_access) do
|
||||
Gitlab::Checks::ChangeAccess.new(
|
||||
changes,
|
||||
project: project,
|
||||
user_access: user_access,
|
||||
protocol: protocol,
|
||||
logger: logger
|
||||
)
|
||||
end
|
||||
|
||||
subject { described_class.new(change_access) }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue