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
|
require_relative 'shell_env'
module Grack
class Auth < Rack::Auth::Basic
attr_accessor :user, :project, :env
def call(env)
@env = env
@request = Rack::Request.new(env)
@auth = Request.new(env)
# Need this patch due to the rails mount
# Need this if under RELATIVE_URL_ROOT
unless Gitlab.config.gitlab.relative_url_root.empty?
# If website is mounted using relative_url_root need to remove it first
@env['PATH_INFO'] = @request.path.sub(Gitlab.config.gitlab.relative_url_root,'')
else
@env['PATH_INFO'] = @request.path
end
@env['SCRIPT_NAME'] = ""
if project
auth!
else
render_not_found
end
end
private
def auth!
if @auth.provided?
return bad_request unless @auth.basic?
# Authentication with username and password
login, password = @auth.credentials
# Allow authentication for GitLab CI service
# if valid token passed
if gitlab_ci_request?(login, password)
return @app.call(env)
end
@user = authenticate_user(login, password)
if @user
Gitlab::ShellEnv.set_env(@user)
@env['REMOTE_USER'] = @auth.username
end
end
if authorized_request?
@app.call(env)
else
unauthorized
end
end
def gitlab_ci_request?(login, password)
if login == "gitlab-ci-token" && project.gitlab_ci?
token = project.gitlab_ci_service.token
if token.present? && token == password && git_cmd == 'git-upload-pack'
true
end
end
false
end
def authenticate_user(login, password)
auth = Gitlab::Auth.new
auth.find(login, password)
end
def authorized_request?
case git_cmd
when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
if user
Gitlab::GitAccess.new.download_allowed?(user, project)
elsif project.public?
# Allow clone/fetch for public projects
true
else
false
end
when *Gitlab::GitAccess::PUSH_COMMANDS
if user
# Skip user authorization on upload request.
# It will be serverd by update hook in repository
true
else
false
end
else
false
end
end
def git_cmd
if @request.get?
@request.params['service']
elsif @request.post?
File.basename(@request.path)
else
nil
end
end
def project
@project ||= project_by_path(@request.path_info)
end
def project_by_path(path)
if m = /^([\w\.\/-]+)\.git/.match(path).to_a
path_with_namespace = m.last
path_with_namespace.gsub!(/\.wiki$/, '')
Project.find_with_namespace(path_with_namespace)
end
end
def render_not_found
[404, {"Content-Type" => "text/plain"}, ["Not Found"]]
end
end
end
|