-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
Copy pathopenid_controller.rb
295 lines (245 loc) · 8.23 KB
/
openid_controller.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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
require 'pathname'
require 'openid'
require 'openid/consumer/discovery'
require 'openid/extensions/sreg'
require 'openid/extensions/pape'
require 'openid/store/filesystem'
class OpenidController < ApplicationController
# protect_from_forgery :except => [:index]
include OpenidHelper
include OpenID::Server
layout nil
def index
begin
if params['openid.mode']
oidreq = server.decode_request(params)
else
oidreq = server.decode_request(Rack::Utils.parse_query(request.env['ORIGINAL_FULLPATH'].split('?')[1]))
end
rescue ProtocolError => e
# invalid openid request, so just display a page with an error message
render text: e.to_s, status: 500
return
end
# no openid.mode was given
unless oidreq
render text: 'This is an OpenID server endpoint.'
return
end
if current_user.nil? && !params['openid.mode']
session[:openid_return_to] = request.env['ORIGINAL_FULLPATH']
flash[:warning] = 'Please log in first.'
redirect_to '/login'
return
else
if oidreq
requested_username = ''
if request.env['ORIGINAL_FULLPATH']&.split('?')[1]
request.env['ORIGINAL_FULLPATH'].split('?')[1].split('&').each do |param|
requested_username = param.split('=')[1].split('%2F').last if param.split('=')[0] == 'openid.claimed_id'
end
end
if current_user && !requested_username.casecmp(current_user.username.downcase).zero?
flash[:error] = "You are requesting access to an account that's not yours. Please <a href='/logout'>log out</a> and use the correct account, or <a href='" + oidreq.trust_root + "'>try to login with the correct username</a>"
redirect_to '/dashboard'
else
oidresp = nil
if oidreq.is_a?(CheckIDRequest)
identity = oidreq.identity
if oidreq.id_select
if oidreq.immediate
oidresp = oidreq.answer(false)
elsif session[:username]
# The user hasn't logged in.
# show_decision_page(oidreq) # this doesnt make sense... it was in the example though
session[:openid_return_to] = request.env['ORIGINAL_FULLPATH']
redirect_to '/login'
else
# Else, set the identity to the one the user is using.
identity = url_for_user
end
end
if oidresp
nil
elsif is_authorized(identity, oidreq.trust_root)
oidresp = oidreq.answer(true, nil, identity)
# add the sreg response if requested
add_sreg(oidreq, oidresp)
# ditto pape
add_pape(oidreq, oidresp)
elsif oidreq.immediate
server_url = url_for action: 'index'
oidresp = oidreq.answer(false, server_url)
else
show_decision_page(oidreq)
return
end
else
oidresp = server.handle_request(oidreq)
end
render_response(oidresp)
end
else
session[:openid_return_to] = request.env['ORIGINAL_FULLPATH']
redirect_to '/login'
end
end
end
def resume
if session[:openid_return_to] # for openid login, redirects back to openid auth process
return_to = session[:openid_return_to]
session[:openid_return_to] = nil
session[:openid_requester] = nil
redirect_to return_to
end
end
def show_decision_page(oidreq, message = 'The site shown below is asking to use your PublicLab.org account to log you in. Do you trust this site?')
session[:last_oidreq] = oidreq
@oidreq = oidreq
flash[:notice] = message if message
render template: 'openid/decide'
end
def user_page
# Yadis content-negotiation: we want to return the xrds if asked for.
accept = request.env['HTTP_ACCEPT']
# This is not technically correct, and should eventually be updated
# to do real Accept header parsing and logic. Though I expect it will work
# 99% of the time.
if accept&.include?('application/xrds+xml')
user_xrds
return
end
# content negotiation failed, so just render the user page
xrds_url = url_for(controller: 'user', action: params[:username]) + '/xrds'
identity_page = <<EOS
<html><head>
<meta http-equiv="X-XRDS-Location" content="#{xrds_url}" />
<link rel="openid.server" href="#{url_for action: 'index'}" />
</head><body><p>OpenID identity page for #{params[:username]}</p>
</body></html>
EOS
# Also add the Yadis location header, so that they don't have
# to parse the html unless absolutely necessary.
response.headers['X-XRDS-Location'] = xrds_url
render text: identity_page
end
def user_xrds
types = [
OpenID::OPENID_2_0_TYPE,
OpenID::OPENID_1_0_TYPE,
OpenID::SREG_URI
]
render_xrds(types)
end
def idp_xrds
types = [
OpenID::OPENID_IDP_2_0_TYPE
]
render_xrds(types)
end
def decision
oidreq = session[:last_oidreq]
session[:last_oidreq] = nil
if params[:yes].nil?
redirect_to oidreq.cancel_url
return
else
id_to_send = params[:id_to_send]
identity = oidreq.identity
if oidreq.id_select
if id_to_send && (id_to_send != '')
session[:username] = id_to_send
session[:approvals] = []
identity = url_for_user
else
msg = 'You must enter a username to in order to send ' \
'an identifier to the Relying Party.'
show_decision_page(oidreq, msg)
return
end
else
session[:username] = current_user.username
end
if session[:approvals]
session[:approvals] << oidreq.trust_root
else
session[:approvals] = [oidreq.trust_root]
end
oidresp = oidreq.answer(true, nil, identity)
add_sreg(oidreq, oidresp)
add_pape(oidreq, oidresp)
return render_response(oidresp)
end
end
protected
def server
if @server.nil?
server_url = url_for action: 'index', only_path: false
dir = Pathname.new(request.host).join('db').join('openid-store')
store = OpenID::Store::Filesystem.new(dir)
@server = Server.new(store, server_url)
end
@server
end
def approved(trust_root)
return false if session[:approvals].nil?
session[:approvals].member?(trust_root)
end
def is_authorized(identity_url, trust_root)
(session[:username] && (identity_url == url_for_user) && approved(trust_root))
end
def render_xrds(types)
type_str = ''
types.each do |uri|
type_str += "<Type>#{uri}</Type>\n "
end
yadis = <<EOS
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="0">
#{type_str}
<URI>#{url_for(controller: 'openid', only_path: false)}</URI>
</Service>
</XRD>
</xrds:XRDS>
EOS
response.headers['content-type'] = 'application/xrds+xml'
render text: yadis
end
def add_sreg(oidreq, oidresp)
# check for Simple Registration arguments and respond
sregreq = OpenID::SReg::Request.from_openid_request(oidreq)
return if sregreq.nil?
# In a real application, this data would be user-specific,
# and the user should be asked for permission to release
# it.
sreg_data = {
'nickname' => current_user.username, # session[:username],
'email' => current_user.email
}
sregresp = OpenID::SReg::Response.extract_response(sregreq, sreg_data)
oidresp.add_extension(sregresp)
end
def add_pape(oidreq, oidresp)
papereq = OpenID::PAPE::Request.from_openid_request(oidreq)
return if papereq.nil?
paperesp = OpenID::PAPE::Response.new
paperesp.nist_auth_level = 0 # we don't even do auth at all!
oidresp.add_extension(paperesp)
end
def render_response(oidresp)
signed_response = server.signatory.sign(oidresp) if oidresp.needs_signing
web_response = server.encode_response(oidresp)
case web_response.code
when HTTP_OK
render text: web_response.body, status: 200
when HTTP_REDIRECT
redirect_to web_response.headers['location']
else
render text: web_response.body, status: 400
end
end
end