grack_auth.rb
3.88 KB
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
require_relative 'shell_env'
require 'omniauth-ldap'
module Grack
class Auth < Rack::Auth::Basic
attr_accessor :user, :project
def call(env)
@env = env
@request = Rack::Request.new(env)
@auth = Request.new(env)
# Need this patch due to the rails mount
@env['PATH_INFO'] = @request.path
@env['SCRIPT_NAME'] = ""
return render_not_found unless project
return unauthorized unless project.public || @auth.provided?
return bad_request if @auth.provided? && !@auth.basic?
if valid?
if @auth.provided?
@env['REMOTE_USER'] = @auth.username
end
return @app.call(env)
else
unauthorized
end
end
def valid?
if @auth.provided?
# Authentication with username and password
login, password = @auth.credentials
@user = authenticate(login, password)
return false unless @user
Gitlab::ShellEnv.set_env(@user)
end
# Git upload and receive
if @request.get?
validate_get_request
elsif @request.post?
validate_post_request
else
false
end
end
def authenticate(login, password)
user = User.find_by_email(login) || User.find_by_username(login)
# If the provided login was not a known email or username
# then user is nil
if user.nil? || user.ldap_user?
# Second chance - try LDAP authentication
return nil unless ldap_conf.enabled
auth = Gitlab::Auth.new
auth.ldap_auth(login, password)
else
return user if user.valid_password?(password)
end
end
def ldap_auth(login, password)
# Check user against LDAP backend if user is not authenticated
# Only check with valid login and password to prevent anonymous bind results
return nil unless ldap_conf.enabled && !login.blank? && !password.blank?
ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
ldap_user = ldap.bind_as(
filter: Net::LDAP::Filter.eq(ldap.uid, login),
size: 1,
password: password
)
User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user
end
def validate_get_request
validate_request(@request.params['service'])
end
def validate_post_request
validate_request(File.basename(@request.path))
end
def validate_request(service)
if service == 'git-upload-pack'
project.public || can?(user, :download_code, project)
elsif service == 'git-receive-pack'
action = if project.protected_branch?(current_ref)
:push_code_to_protected_branches
else
:push_code
end
can?(user, action, project)
else
false
end
end
def can?(object, action, subject)
abilities.allowed?(object, action, subject)
end
def current_ref
if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
input = Zlib::GzipReader.new(@request.body).read
else
input = @request.body.read
end
# Need to reset seek point
@request.body.rewind
/refs\/heads\/([\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last
end
def project
unless instance_variable_defined? :@project
# Find project by PATH_INFO from env
if m = /^\/([\w\.\/-]+)\.git/.match(@request.path_info).to_a
@project = Project.find_with_namespace(m.last)
end
end
return @project
end
PLAIN_TYPE = {"Content-Type" => "text/plain"}
def render_not_found
[404, PLAIN_TYPE, ["Not Found"]]
end
protected
def abilities
@abilities ||= begin
abilities = Six.new
abilities << Ability
abilities
end
end
def ldap_conf
@ldap_conf ||= Gitlab.config.ldap
end
end# Auth
end# Grack