1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
# frozen_string_literal: true
module Repositories
class GitHttpClientController < Repositories::ApplicationController
include ActionController::HttpAuthentication::Basic
include KerberosSpnegoHelper
include Gitlab::Utils::StrongMemoize
attr_reader :authentication_result, :redirected_path
delegate :authentication_abilities, to: :authentication_result, allow_nil: true
delegate :type, to: :authentication_result, allow_nil: true, prefix: :auth_result
# Git clients will not know what authenticity token to send along
skip_around_action :set_session_storage
skip_before_action :verify_authenticity_token
prepend_before_action :authenticate_user, :parse_repo_path
skip_around_action :sessionless_bypass_admin_mode!
around_action :bypass_admin_mode!, if: :authenticated_user
feature_category :source_code_management
def authenticated_user
authentication_result&.user || authentication_result&.deploy_token
end
private
def user
authenticated_user
end
def download_request?
raise NotImplementedError
end
def upload_request?
raise NotImplementedError
end
def authenticate_user
@authentication_result = Gitlab::Auth::Result::EMPTY
if allow_basic_auth? && basic_auth_provided?
login, password = user_name_and_password(request)
if handle_basic_authentication(login, password)
return # Allow access
end
elsif allow_kerberos_spnego_auth? && spnego_provided?
kerberos_user = find_kerberos_user
if kerberos_user
@authentication_result = Gitlab::Auth::Result.new(
kerberos_user, nil, :kerberos, Gitlab::Auth.full_authentication_abilities)
send_final_spnego_response
return # Allow access
end
elsif http_download_allowed?
@authentication_result = Gitlab::Auth::Result.new(nil, project, :none, [:download_code])
return # Allow access
end
send_challenges
render_access_denied
rescue Gitlab::Auth::MissingPersonalAccessTokenError
render_access_denied
end
def render_access_denied
help_page = help_page_url(
'topics/git/troubleshooting_git',
anchor: 'error-on-git-fetch-http-basic-access-denied'
)
render(
plain: format(_("HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"), help_page_url: help_page),
status: :unauthorized
)
end
def basic_auth_provided?
has_basic_credentials?(request)
end
def send_challenges
challenges = []
challenges << 'Basic realm="GitLab"' if allow_basic_auth?
challenges << spnego_challenge if allow_kerberos_spnego_auth?
headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
end
def container
parse_repo_path unless defined?(@container)
@container
end
def project
parse_repo_path unless defined?(@project)
@project
end
def repository_path
@repository_path ||= params[:repository_path]
end
def parse_repo_path
@container, @project, @repo_type, @redirected_path = Gitlab::RepoPath.parse(repository_path)
end
def repository
strong_memoize(:repository) do
repo_type.repository_for(container)
end
end
def repo_type
parse_repo_path unless defined?(@repo_type)
@repo_type
end
def handle_basic_authentication(login, password)
@authentication_result = Gitlab::Auth.find_for_git_client(
login, password, project: project, ip: request.ip)
@authentication_result.success?
end
def ci?
authentication_result.ci?(project)
end
def http_download_allowed?
Gitlab::ProtocolAccess.allowed?('http') &&
download_request? &&
container &&
Guest.can?(repo_type.guest_read_ability, container)
end
def bypass_admin_mode!(&block)
return yield unless Gitlab::CurrentSettings.admin_mode
Gitlab::Auth::CurrentUserMode.bypass_session!(authenticated_user.id, &block)
end
end
end
Repositories::GitHttpClientController.prepend_mod_with('Repositories::GitHttpClientController')
|