Class: Gitlab::GitAccess

Inherits:
Object
  • Object
show all
Includes:
Utils::StrongMemoize
Defined in:
lib/gitlab/git_access.rb

Constant Summary collapse

ForbiddenError =
Class.new(StandardError)
NotFoundError =
Class.new(StandardError)
TimeoutError =
Class.new(StandardError)
ANY =

Use the magic string ‘_any’ to indicate we do not know what the changes are. This is also what gitlab-shell does.

'_any'
ERROR_MESSAGES =
{
  upload: 'You are not allowed to upload code for this project.',
  download: 'You are not allowed to download code from this project.',
  auth_upload: 'You are not allowed to upload code.',
  auth_download: 'You are not allowed to download code.',
  deploy_key_upload: 'This deploy key does not have write access to this project.',
  no_repo: 'A repository for this project does not exist yet.',
  project_not_found: "The project you were looking for could not be found or you don't have permission to view it.",
  auth_by_job_token_forbidden: 'Insufficient permissions to pull from the repository of project %{target_project_path}.',
  auth_by_job_token_project_not_in_allowlist: 'Authentication by CI/CD job token not allowed from %{source_project_path} to %{target_project_path}.',
  command_not_allowed: "The command you're trying to execute is not allowed.",
  upload_pack_disabled_over_http: 'Pulling over HTTP is not allowed.',
  receive_pack_disabled_over_http: 'Pushing over HTTP is not allowed.',
  read_only: 'The repository is temporarily read-only. Please try again later.',
  archived: "You can't push code to an archived project.",
  cannot_push_to_read_only: "You can't push code to a read-only GitLab instance.",
  push_code: 'You are not allowed to push code to this project.'
}.freeze
INTERNAL_TIMEOUT =
50.seconds.freeze
LOG_HEADER =
<<~MESSAGE
  Push operation timed out

  Timing information for debugging purposes:
MESSAGE
DOWNLOAD_COMMANDS =
%w[git-upload-pack git-upload-archive].freeze
PUSH_COMMANDS =
%w[git-receive-pack].freeze
ALL_COMMANDS =
DOWNLOAD_COMMANDS + PUSH_COMMANDS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(actor, container, protocol, authentication_abilities:, repository_path: nil, redirected_path: nil, auth_result_type: nil, push_options: nil) ⇒ GitAccess

Returns a new instance of GitAccess.



63
64
65
66
67
68
69
70
71
72
# File 'lib/gitlab/git_access.rb', line 63

def initialize(actor, container, protocol, authentication_abilities:, repository_path: nil, redirected_path: nil, auth_result_type: nil, push_options: nil)
  @actor     = actor
  @container = container
  @protocol  = protocol
  @authentication_abilities = Array(authentication_abilities)
  @repository_path = repository_path
  @redirected_path = redirected_path
  @auth_result_type = auth_result_type
  @push_options = Gitlab::PushOptions.new(push_options)
end

Instance Attribute Details

#actorObject (readonly)

Returns the value of attribute actor.



48
49
50
# File 'lib/gitlab/git_access.rb', line 48

def actor
  @actor
end

#auth_result_typeObject (readonly)

Returns the value of attribute auth_result_type.



48
49
50
# File 'lib/gitlab/git_access.rb', line 48

def auth_result_type
  @auth_result_type
end

#authentication_abilitiesObject (readonly)

Returns the value of attribute authentication_abilities.



48
49
50
# File 'lib/gitlab/git_access.rb', line 48

def authentication_abilities
  @authentication_abilities
end

#changesObject (readonly)

Returns the value of attribute changes.



48
49
50
# File 'lib/gitlab/git_access.rb', line 48

def changes
  @changes
end

#cmdObject (readonly)

Returns the value of attribute cmd.



48
49
50
# File 'lib/gitlab/git_access.rb', line 48

def cmd
  @cmd
end

#containerObject

Returns the value of attribute container.



51
52
53
# File 'lib/gitlab/git_access.rb', line 51

def container
  @container
end

#protocolObject (readonly)

Returns the value of attribute protocol.



48
49
50
# File 'lib/gitlab/git_access.rb', line 48

def protocol
  @protocol
end

#push_optionsObject (readonly)

Returns the value of attribute push_options.



48
49
50
# File 'lib/gitlab/git_access.rb', line 48

def push_options
  @push_options
end

#redirected_pathObject (readonly)

Returns the value of attribute redirected_path.



48
49
50
# File 'lib/gitlab/git_access.rb', line 48

def redirected_path
  @redirected_path
end

#repository_pathObject (readonly)

Returns the value of attribute repository_path.



48
49
50
# File 'lib/gitlab/git_access.rb', line 48

def repository_path
  @repository_path
end

Class Method Details

.error_message(key) ⇒ Object

Raises:

  • (ArgumentError)


53
54
55
56
57
58
59
60
61
# File 'lib/gitlab/git_access.rb', line 53

def self.error_message(key)
  self.ancestors.each do |cls|
    return cls.const_get(:ERROR_MESSAGES, false).fetch(key)
  rescue NameError, KeyError
    next
  end

  raise ArgumentError, "No error message defined for #{key}"
end

Instance Method Details

#check(cmd, changes) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/gitlab/git_access.rb', line 74

def check(cmd, changes)
  @changes = changes
  @cmd = cmd

  check_protocol!
  check_valid_actor!
  check_active_user!
  check_authentication_abilities!
  check_command_disabled!
  check_command_existence!

  custom_action = check_custom_action
  return custom_action if custom_action

  check_db_accessibility!
  check_container!
  check_repository_existence!

  case cmd
  when *DOWNLOAD_COMMANDS
    check_download_access!
  when *PUSH_COMMANDS
    check_push_access!
  end
  check_additional_conditions!

  success_result
end

#deploy_key_can_download_code?Boolean

Returns:

  • (Boolean)


111
112
113
114
115
116
# File 'lib/gitlab/git_access.rb', line 111

def deploy_key_can_download_code?
  authentication_abilities.include?(:download_code) &&
    deploy_key? &&
    deploy_key.has_access_to?(container) &&
    (project? && repository_access_level != ::Featurable::DISABLED)
end

#download_abilitySymbol

Returns the name of a Declarative Policy ability to check.

Returns:

  • (Symbol)

    the name of a Declarative Policy ability to check

Raises:

  • (NotImplementedError)


124
125
126
# File 'lib/gitlab/git_access.rb', line 124

def download_ability
  raise NotImplementedError
end

#guest_can_download?Boolean

Returns:

  • (Boolean)


107
108
109
# File 'lib/gitlab/git_access.rb', line 107

def guest_can_download?
  ::Users::Anonymous.can?(download_ability, container)
end

#loggerObject



103
104
105
# File 'lib/gitlab/git_access.rb', line 103

def logger
  @logger ||= Checks::TimedLogger.new(timeout: INTERNAL_TIMEOUT, header: LOG_HEADER)
end

#protocol_allowed?Boolean

Returns:

  • (Boolean)


139
140
141
# File 'lib/gitlab/git_access.rb', line 139

def protocol_allowed?
  Gitlab::ProtocolAccess.allowed?(protocol, project: project)
end

#push_abilitySymbol

Returns the name of a Declarative Policy ability to check.

Returns:

  • (Symbol)

    the name of a Declarative Policy ability to check

Raises:

  • (NotImplementedError)


129
130
131
# File 'lib/gitlab/git_access.rb', line 129

def push_ability
  raise NotImplementedError
end

#request_from_ci_build?Boolean

Returns:

  • (Boolean)


133
134
135
136
137
# File 'lib/gitlab/git_access.rb', line 133

def request_from_ci_build?
  return false unless protocol == 'http'

  auth_result_type == :build || auth_result_type == :ci
end

#user_can_download?Boolean

Returns:

  • (Boolean)


118
119
120
121
# File 'lib/gitlab/git_access.rb', line 118

def user_can_download?
  authentication_abilities.include?(:download_code) &&
    user_access.can_do_action?(download_ability)
end