-
Notifications
You must be signed in to change notification settings - Fork 444
/
Copy pathauthenticator.rb
235 lines (190 loc) · 7.27 KB
/
authenticator.rb
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
require 'gssapi' if CONFIG['kerberos_service_principal']
require 'api_error'
class Authenticator
class AuthenticationRequiredError < APIError
setup 401, 'Authentication required'
end
class AnonymousUser < APIError
setup 401
end
class NoPublicAccessError < APIError
setup 401
end
class InactiveUserError < APIError
setup 403
end
class UnconfirmedUserError < APIError
setup 403
end
class AdminUserRequiredError < APIError
setup('put_request_no_permission', 403)
end
attr_reader :request, :session, :user_permissions, :http_user
def initialize(request, session, response)
@response = response
@request = request
@session = session
@http_user = nil
@user_permissions = nil
end
def extract_user
if ::Configuration.proxy_auth_mode_enabled?
extract_proxy_user
else
extract_auth_user
@http_user = User.find_with_credentials(@login, @passwd) if @login && @passwd
end
@http_user = User.find_by_login(session[:login]) if !@http_user && session[:login]
check_extracted_user
end
def extract_user_public
if ::Configuration.anonymous
load_nobody
else
Rails.logger.error 'No public access is configured'
raise NoPublicAccessError, 'No public access is configured'
end
end
# We allow anonymous user only for rare special operations (if configured) but we require
# a valid account for all other operations.
# For this rare special operations we simply skip the require login before filter!
# At the moment these operations are the /public, /trigger and /about controller actions.
def require_login
raise AnonymousUser, 'Anonymous user is not allowed here - please login' unless User.session
end
def require_admin
Rails.logger.debug { "Checking for Admin role for user #{@http_user.login}" }
unless @http_user.is_admin?
Rails.logger.debug 'not granted!'
raise AdminUserRequiredError, 'Requires admin privileges'
end
true
end
def authorization_infos
# 1. try to get it where mod_rewrite might have put it
# 2. for Apache/mod_fastcgi with -pass-header Authorization
# 3. regular location
['X-HTTP_AUTHORIZATION', 'Authorization', 'HTTP_AUTHORIZATION'].each do |header|
return request.env[header].to_s.split if request.env.key?(header)
end
nil
end
private
def initialize_krb_session
principal = CONFIG['kerberos_service_principal']
raise AuthenticationRequiredError, 'Kerberos configuration is broken. Principal is empty.' if principal.blank?
CONFIG['kerberos_realm'] = principal.rpartition('@')[2] unless CONFIG['kerberos_realm']
krb = GSSAPI::Simple.new(
principal.partition('/')[2].rpartition('@')[0],
principal.partition('/')[0],
CONFIG['kerberos_keytab'] || '/etc/krb5.keytab'
)
krb.acquire_credentials
krb
end
def raise_and_invalidate(authorization, message = '')
@response.headers['WWW-Authenticate'] = authorization.join(' ')
raise AuthenticationRequiredError, message
end
def extract_krb_user(authorization)
unless authorization[1]
Rails.logger.debug "Didn't receive any negotiation data."
raise_and_invalidate(authorization, 'GSSAPI negotiation failed.')
end
begin
krb = initialize_krb_session
begin
tok = krb.accept_context(Base64.strict_decode64(authorization[1]))
rescue GSSAPI::GssApiError, ArgumentError
raise_and_invalidate(authorization, 'Received invalid GSSAPI context.')
end
raise_and_invalidate(authorization, 'User authenticated in wrong Kerberos realm.') unless krb.display_name.match?("@#{CONFIG['kerberos_realm']}$")
unless tok == true
tok = Base64.strict_encode64(tok)
@response.headers['WWW-Authenticate'] = "Negotiate #{tok}"
end
@login = krb.display_name.partition('@')[0]
@http_user = User.find_by_login(@login)
unless @http_user
Rails.logger.debug { "Creating account for user '#{@login}'" }
@http_user = User.create_user_with_fake_pw!(login: @login, state: User.default_user_state)
end
rescue GSSAPI::GssApiError => e
raise AuthenticationRequiredError, "Received a GSSAPI exception; #{e.message}."
end
end
def extract_basic_user(authorization)
@login, @passwd = Base64.decode64(authorization[1]).split(':', 2)[0..1]
# set password to the empty string in case no password is transmitted in the auth string
@passwd ||= ''
end
def extract_proxy_user
proxy_user = request.env['HTTP_X_USERNAME']
# we're using a login proxy, there is no need to authenticate the user from the credentials
# However we have to care for the status of the user that must not be unconfirmed or proxy requested
if proxy_user
@http_user = User.find_by_login(proxy_user)
# If we do not find a User here, we need to create a user and wait for
# the confirmation by the user and the BS Admin Team.
unless @http_user
if ::Configuration.registration == 'deny'
Rails.logger.debug('No user found in database, creation disabled')
raise AuthenticationRequiredError, "User '#{proxy_user}' does not exist"
end
@http_user = User.create_user_with_fake_pw!(login: proxy_user, state: User.default_user_state)
end
@http_user.update_login_values(request.env)
else
Rails.logger.error 'No X-username header was sent by login proxy!'
end
end
def extract_auth_user
authorization = authorization_infos
# privacy! logger.debug( "AUTH: #{authorization.inspect}" )
if authorization
# logger.debug( "AUTH2: #{authorization}" )
if authorization[0] == 'Basic'
extract_basic_user(authorization)
elsif authorization[0] == 'Negotiate' && CONFIG['kerberos_mode']
extract_krb_user(authorization)
else
Rails.logger.debug { "Unsupported authentication string '#{authorization[0]}' received." }
end
else
Rails.logger.debug 'No authentication string was received.'
end
end
def check_extracted_user
unless @http_user
if @login.blank?
return true if check_for_anonymous_user
raise AuthenticationRequiredError
end
raise AuthenticationRequiredError, "Unknown user '#{@login}' or invalid password"
end
if @http_user.state == 'unconfirmed'
raise UnconfirmedUserError, 'User is registered but not yet approved. Your account ' \
'is a registered account, but it is not yet approved for the OBS by admin.'
end
User.session = @http_user
if @http_user.state == 'confirmed'
Rails.logger.debug { "USER found: #{@http_user.login}" }
@user_permissions = Suse::Permission.new(@http_user)
return true
end
raise InactiveUserError, 'User is registered but not in confirmed state. Your account ' \
'is a registered account, but it is in a not active state.'
end
# set the nobody user if a user agent is present in anonymous mode
def check_for_anonymous_user
return false unless ::Configuration.anonymous && request.user_agent
load_nobody
true
end
# to become _public_ special user
def load_nobody
@http_user = User.find_nobody!
User.session = @http_user
@user_permissions = Suse::Permission.new(@http_user)
end
end