2018-12-04 06:55:49 -05:00
# frozen_string_literal: true
module Gitlab
module Checks
2021-06-15 14:09:57 -04:00
class BranchCheck < BaseSingleChecker
2018-12-04 06:55:49 -05:00
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.' ,
2019-03-06 07:20:27 -05:00
push_protected_branch : 'You are not allowed to push code to protected branches on this project.' ,
create_protected_branch : 'You are not allowed to create protected branches on this project.' ,
invalid_commit_create_protected_branch : 'You can only use an existing protected branch ref as the basis of a new protected branch.' ,
2020-08-05 17:09:40 -04:00
non_web_create_protected_branch : 'You can only create protected branches using the web interface and API.' ,
prohibited_hex_branch_name : 'You cannot create a branch with a 40-character hexadecimal branch name.'
2018-12-04 06:55:49 -05:00
} . 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... " ,
2019-03-06 07:20:27 -05:00
protected_branch_creation_checks : " Checking if you are allowed to create a protected branch... " ,
2018-12-04 06:55:49 -05:00
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
2020-02-26 13:09:24 -05:00
raise GitAccess :: ForbiddenError , ERROR_MESSAGES [ :delete_default_branch ]
2018-12-04 06:55:49 -05:00
end
end
2020-08-05 17:09:40 -04:00
prohibited_branch_checks
2018-12-04 06:55:49 -05:00
protected_branch_checks
end
private
2020-08-05 17:09:40 -04:00
def prohibited_branch_checks
2021-11-26 07:12:49 -05:00
return if deletion?
2020-08-05 17:09:40 -04:00
if branch_name =~ / \ A \ h{40} \ z /
raise GitAccess :: ForbiddenError , ERROR_MESSAGES [ :prohibited_hex_branch_name ]
end
end
2018-12-04 06:55:49 -05:00
def protected_branch_checks
logger . log_timed ( LOG_MESSAGES [ :protected_branch_checks ] ) do
return unless ProtectedBranch . protected? ( project , branch_name ) # rubocop:disable Cop/AvoidReturnFromBlocks
2021-03-11 13:09:23 -05:00
if forced_push? && ! ProtectedBranch . allow_force_push? ( project , branch_name )
2020-02-26 13:09:24 -05:00
raise GitAccess :: ForbiddenError , ERROR_MESSAGES [ :force_push_protected_branch ]
2018-12-04 06:55:49 -05:00
end
end
2019-03-07 03:51:58 -05:00
if project . empty_repo?
protected_branch_push_checks
2019-04-21 23:25:01 -04:00
elsif creation?
2019-03-06 07:20:27 -05:00
protected_branch_creation_checks
elsif deletion?
2018-12-04 06:55:49 -05:00
protected_branch_deletion_checks
else
protected_branch_push_checks
end
end
2019-03-06 07:20:27 -05:00
def protected_branch_creation_checks
logger . log_timed ( LOG_MESSAGES [ :protected_branch_creation_checks ] ) do
2019-03-21 07:11:06 -04:00
break if user_access . can_push_to_branch? ( branch_name )
2019-03-06 07:20:27 -05:00
unless user_access . can_merge_to_branch? ( branch_name )
2020-02-26 13:09:24 -05:00
raise GitAccess :: ForbiddenError , ERROR_MESSAGES [ :create_protected_branch ]
2019-03-06 07:20:27 -05:00
end
unless safe_commit_for_new_protected_branch?
2020-02-26 13:09:24 -05:00
raise GitAccess :: ForbiddenError , ERROR_MESSAGES [ :invalid_commit_create_protected_branch ]
2019-03-06 07:20:27 -05:00
end
unless updated_from_web?
2020-02-26 13:09:24 -05:00
raise GitAccess :: ForbiddenError , ERROR_MESSAGES [ :non_web_create_protected_branch ]
2019-03-06 07:20:27 -05:00
end
end
end
2018-12-04 06:55:49 -05:00
def protected_branch_deletion_checks
logger . log_timed ( LOG_MESSAGES [ :protected_branch_deletion_checks ] ) do
unless user_access . can_delete_branch? ( branch_name )
2020-02-26 13:09:24 -05:00
raise GitAccess :: ForbiddenError , ERROR_MESSAGES [ :non_master_delete_protected_branch ]
2018-12-04 06:55:49 -05:00
end
unless updated_from_web?
2020-02-26 13:09:24 -05:00
raise GitAccess :: ForbiddenError , ERROR_MESSAGES [ :non_web_delete_protected_branch ]
2018-12-04 06:55:49 -05:00
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 )
2020-02-26 13:09:24 -05:00
raise GitAccess :: ForbiddenError , ERROR_MESSAGES [ :merge_protected_branch ]
2018-12-04 06:55:49 -05:00
end
else
unless user_access . can_push_to_branch? ( branch_name )
2020-02-26 13:09:24 -05:00
raise GitAccess :: ForbiddenError , push_to_protected_branch_rejected_message
2018-12-04 06:55:49 -05:00
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
2021-07-23 08:09:05 -04:00
A default branch ( e . g . main ) does not yet exist for #{project.full_path}
2018-12-04 06:55:49 -05:00
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
2019-03-06 07:20:27 -05:00
def safe_commit_for_new_protected_branch?
ProtectedBranch . any_protected? ( project , project . repository . branch_names_contains_sha ( newrev ) )
end
2018-12-04 06:55:49 -05:00
end
end
end