rubycas-client 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{CHANGES → CHANGELOG.txt} +13 -0
- data/History.txt +0 -0
- data/{LICENSE → LICENSE.txt} +9 -42
- data/Manifest.txt +16 -0
- data/README.txt +257 -0
- data/Rakefile +48 -14
- data/init.rb +4 -18
- data/lib/casclient.rb +79 -0
- data/lib/casclient/client.rb +209 -0
- data/lib/{cas_proxy_callback_controller.rb → casclient/frameworks/rails/cas_proxy_callback_controller.rb} +1 -1
- data/lib/casclient/frameworks/rails/filter.rb +149 -0
- data/lib/casclient/responses.rb +180 -0
- data/lib/casclient/tickets.rb +38 -0
- data/lib/casclient/version.rb +9 -0
- data/lib/rubycas-client.rb +1 -0
- data/setup.rb +1585 -0
- metadata +67 -47
- data/README +0 -223
- data/install.rb +0 -5
- data/lib/cas.rb +0 -194
- data/lib/cas_auth.rb +0 -553
- data/lib/cas_logger.rb +0 -27
data/lib/cas_auth.rb
DELETED
@@ -1,553 +0,0 @@
|
|
1
|
-
require 'cgi'
|
2
|
-
require 'logger'
|
3
|
-
|
4
|
-
# these requires are needed when outside of a Rails app context (e.g. in unit tests)
|
5
|
-
require 'rubygems'
|
6
|
-
require 'active_support'
|
7
|
-
require 'action_controller'
|
8
|
-
|
9
|
-
require File.dirname(File.expand_path(__FILE__))+'/cas'
|
10
|
-
|
11
|
-
module CAS
|
12
|
-
# The DummyLogger is a class which might pass through to a real Logger
|
13
|
-
# if one is assigned. However, it can gracefully swallow any logging calls
|
14
|
-
# if there is now Logger assigned.
|
15
|
-
class LoggerWrapper
|
16
|
-
def initialize(logger=nil)
|
17
|
-
set_logger(logger)
|
18
|
-
end
|
19
|
-
# Assign the 'real' Logger instance that this dummy instance wraps around.
|
20
|
-
def set_logger(logger)
|
21
|
-
@logger = logger
|
22
|
-
end
|
23
|
-
# log using the appropriate method if we have a logger
|
24
|
-
# if we dont' have a logger, ignore completely.
|
25
|
-
def method_missing(name, *args)
|
26
|
-
if @logger && @logger.respond_to?(name)
|
27
|
-
@logger.send(name, *args)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
LOGGER = CAS::LoggerWrapper.new
|
33
|
-
|
34
|
-
# Allows authentication through a CAS server.
|
35
|
-
# The precondition for this filter to work is that you have an
|
36
|
-
# authentication infrastructure. As such, this is for the enterprise
|
37
|
-
# rather than small shops.
|
38
|
-
#
|
39
|
-
# To use CAS::Filter for authentication, add something like this to
|
40
|
-
# your environment:
|
41
|
-
#
|
42
|
-
# CAS::Filter.cas_base_url = "https://cas.company.com
|
43
|
-
#
|
44
|
-
# The filter will try to use the standard CAS page locations based on this URL.
|
45
|
-
# Or you can explicitly specify the individual URLs:
|
46
|
-
#
|
47
|
-
# CAS::Filter.login_url = "https://cas.company.com/login"
|
48
|
-
# CAS::Filter.validate_url = "https://cas.company.com/proxyValidate"
|
49
|
-
#
|
50
|
-
# The filter will also try to automatically figure out your CAS-protected application's
|
51
|
-
# URL (to send the client back after authenticating on the CAS server), but you can
|
52
|
-
# explicitly override:
|
53
|
-
#
|
54
|
-
# CAS::Filter.service_url = "http://www.my-cas-protected-app.com/
|
55
|
-
#
|
56
|
-
# It is of course possible to use different configurations in development, test
|
57
|
-
# and production by placing the configuration in the appropriate environments file.
|
58
|
-
#
|
59
|
-
# To add CAS protection to a Rails controller:
|
60
|
-
#
|
61
|
-
# before_filter CAS::Filter
|
62
|
-
#
|
63
|
-
# All of the standard Rails filter qualifiers can also be used. For example:
|
64
|
-
#
|
65
|
-
# before_filter CAS::Filter, :only => [:admin, :private]
|
66
|
-
#
|
67
|
-
# By default CAS::Filter saves the logged in user in session[:casfilteruser] but
|
68
|
-
# that name can be changed by setting CAS::Filter.session_username
|
69
|
-
# The username is also available from the request by
|
70
|
-
#
|
71
|
-
# request.username
|
72
|
-
#
|
73
|
-
# This wrapping of the request can be disabled by
|
74
|
-
#
|
75
|
-
# CAS::Filter.wrap_request = false
|
76
|
-
#
|
77
|
-
# Proxying is also possible. Please see the README for examples.
|
78
|
-
#
|
79
|
-
class Filter
|
80
|
-
@@login_url = "https://localhost/login"
|
81
|
-
@@logout_url = nil
|
82
|
-
@@validate_url = "https://localhost/proxyValidate"
|
83
|
-
@@renew = false
|
84
|
-
@@session_username = :casfilteruser
|
85
|
-
@@query_string = {}
|
86
|
-
@@fake = nil
|
87
|
-
@@pgt = nil
|
88
|
-
cattr_accessor :query_string
|
89
|
-
cattr_accessor :login_url, :validate_url, :service_url, :wrap_request, :session_username
|
90
|
-
class_inheritable_accessor :gateway, :renew
|
91
|
-
cattr_accessor :proxy_url, :proxy_callback_url, :proxy_retrieval_url
|
92
|
-
@@authorized_proxies = []
|
93
|
-
cattr_accessor :authorized_proxies
|
94
|
-
|
95
|
-
# gatewaying is disabled by default -- use GatewayFilter if you want gatewaying
|
96
|
-
self.gateway = false
|
97
|
-
|
98
|
-
class << self
|
99
|
-
|
100
|
-
# Retrieves the Logger used by the filter
|
101
|
-
def logger
|
102
|
-
CAS::LOGGER
|
103
|
-
end
|
104
|
-
# Sets the Logger used by the filter
|
105
|
-
def logger=(val)
|
106
|
-
CAS::LOGGER.set_logger(val)
|
107
|
-
end
|
108
|
-
|
109
|
-
alias :log :logger
|
110
|
-
alias :log= :logger=
|
111
|
-
|
112
|
-
# Builds the internal logout URL. The current @@logout_url value will
|
113
|
-
# be used if it is set. Otherwise we will try to figure it out based
|
114
|
-
# on the @@login_url.
|
115
|
-
def create_logout_url
|
116
|
-
if !@@logout_url && @@login_url =~ %r{^(.+?)/[^/]*$}
|
117
|
-
@@logout_url = "#{$1}/logout"
|
118
|
-
end
|
119
|
-
logger.debug "Created logout url: #{@@logout_url}"
|
120
|
-
end
|
121
|
-
|
122
|
-
# Returns the logout URL for the given controller.
|
123
|
-
# This method calls create_logout_url if no logout url has yet
|
124
|
-
# been created or set.
|
125
|
-
#
|
126
|
-
# Additionally a service URL can be provided and will be attached
|
127
|
-
# to the CAS server logout URL. If not provided, the service URL
|
128
|
-
# will be automatically derived using guess_service().
|
129
|
-
def logout_url(controller, service = nil)
|
130
|
-
create_logout_url unless @@logout_url
|
131
|
-
url = redirect_url(controller,@@logout_url,service)
|
132
|
-
logger.debug "Logout url is: #{url}"
|
133
|
-
url
|
134
|
-
end
|
135
|
-
|
136
|
-
# Explicitly sets the logout URL.
|
137
|
-
def logout_url=(url)
|
138
|
-
@@logout_url = url
|
139
|
-
logger.debug "Initialized logout url to: #{url}"
|
140
|
-
end
|
141
|
-
|
142
|
-
# Sets the base CAS url. The login_url, validate_url, and proxy_url
|
143
|
-
# are automagically built on top of this.
|
144
|
-
def cas_base_url=(url)
|
145
|
-
url.gsub!(/\/$/, '')
|
146
|
-
CAS::Filter.login_url = "#{url}/login"
|
147
|
-
CAS::Filter.validate_url = "#{url}/proxyValidate"
|
148
|
-
CAS::Filter.proxy_url = "#{url}/proxy"
|
149
|
-
logger.debug "Initialized CAS base url to: #{url}"
|
150
|
-
end
|
151
|
-
|
152
|
-
# Returns the current @@fake value.
|
153
|
-
# This is used for debugging. See <tt>fake=</tt> and <tt>filter_f</tt>.
|
154
|
-
def fake
|
155
|
-
@@fake
|
156
|
-
end
|
157
|
-
|
158
|
-
# Enables or disables the fake filter.
|
159
|
-
# This is used in debugging.
|
160
|
-
#
|
161
|
-
# The argument can have one of the following values:
|
162
|
-
#
|
163
|
-
# :failure :: The fake filter will always fail.
|
164
|
-
# :param :: The fake filter will use the 'username' request param to set
|
165
|
-
# the username.
|
166
|
-
# Proc :: The fake filter will execute the given proc to determine the
|
167
|
-
# username. The current controller will be fed to the Proc as an
|
168
|
-
# argument.
|
169
|
-
# nil :: Disables the fake filter and enables the real filter.
|
170
|
-
def fake=(val)
|
171
|
-
if val.nil?
|
172
|
-
alias :filter :filter_r
|
173
|
-
else
|
174
|
-
alias :filter :filter_f
|
175
|
-
logger.warn "Will use fake filter"
|
176
|
-
end
|
177
|
-
@@fake = val
|
178
|
-
end
|
179
|
-
|
180
|
-
# This is the fake filter method. It is aliased as 'filter'
|
181
|
-
# when the fake filter is enabled. See <tt>fake=</tt>.
|
182
|
-
def filter_f(controller)
|
183
|
-
logger.break
|
184
|
-
logger.warn "Using fake CAS filter"
|
185
|
-
username = @@fake
|
186
|
-
if :failure == @@fake
|
187
|
-
return false
|
188
|
-
elsif :param == @@fake
|
189
|
-
username = controller.params['username']
|
190
|
-
elsif Proc === @@fake
|
191
|
-
username = @@fake.call(controller)
|
192
|
-
end
|
193
|
-
logger.info("The username set by the fake filter is: #{username}")
|
194
|
-
controller.session[@@session_username] = username
|
195
|
-
return true
|
196
|
-
end
|
197
|
-
|
198
|
-
# This is the real filter method. It is aliased as 'filter'
|
199
|
-
# by default (when the fake filter is disabled).
|
200
|
-
#
|
201
|
-
# The filter method behaves like a standard Rails filter, taking
|
202
|
-
# the current controller as an argument (in order to access the current
|
203
|
-
# request params, the session, etc.). The method returns true
|
204
|
-
# when authentication is successful, false otherwise. Generally,
|
205
|
-
# before returning false the filter will send a HTTP redirect back to the
|
206
|
-
# CAS server.
|
207
|
-
def filter_r(controller)
|
208
|
-
logger.break
|
209
|
-
logger.info("Using real CAS filter in controller: #{controller}")
|
210
|
-
|
211
|
-
# We store the receipt in the session so that we do not have to fetch it again
|
212
|
-
# if we're asked to validate a ticket that has already been validated. This
|
213
|
-
# saves us from unnecessarily hitting the CAS server.
|
214
|
-
session_receipt = controller.session[:casfilterreceipt]
|
215
|
-
session_ticket = controller.session[:caslastticket]
|
216
|
-
ticket = controller.params[:ticket]
|
217
|
-
|
218
|
-
is_valid = false
|
219
|
-
|
220
|
-
if controller.session[:casfiltergateway]
|
221
|
-
log.debug "Coming back from gatewayed request to CAS server..."
|
222
|
-
did_gateway = true
|
223
|
-
controller.session[:casfiltergateway] = false
|
224
|
-
else
|
225
|
-
log.debug "This request is not gatewayed."
|
226
|
-
end
|
227
|
-
|
228
|
-
if ticket and (!session_ticket or session_ticket != ticket)
|
229
|
-
log.info "A ticket parameter was given in the URI: #{ticket} and "+
|
230
|
-
(!session_ticket ? "there is no previous ticket for this session" :
|
231
|
-
"the ticket is different than the previous ticket, which was #{session_ticket}")
|
232
|
-
|
233
|
-
receipt = get_receipt_for_ticket(ticket, controller)
|
234
|
-
|
235
|
-
if receipt && validate_receipt(receipt)
|
236
|
-
logger.info("Receipt for ticket request #{ticket} is valid, belongs to user #{receipt.user_name}, and will be stored in the session.")
|
237
|
-
controller.session[:casfilterreceipt] = receipt
|
238
|
-
controller.session[:caslastticket] = ticket
|
239
|
-
controller.session[@@session_username] = receipt.user_name
|
240
|
-
|
241
|
-
if receipt.pgt_iou
|
242
|
-
logger.info("Receipt has a proxy-granting ticket IOU. Attempting to retrieve the proxy-granting ticket...")
|
243
|
-
pgt = retrieve_pgt(receipt)
|
244
|
-
if pgt
|
245
|
-
log.debug("Got PGT #{pgt} for PGT IOU #{receipt.pgt_iou}. This will be stored in the session.")
|
246
|
-
controller.session[:casfilterpgt] = pgt
|
247
|
-
else
|
248
|
-
log.error("Failed to retrieve a PGT for PGT IOU #{receipt.pgt_iou}!")
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
is_valid = true
|
253
|
-
else
|
254
|
-
if receipt
|
255
|
-
log.warn "Receipt was invalid for ticket #{ticket}!"
|
256
|
-
else
|
257
|
-
log.warn "get_receipt_for_ticket() for ticket #{ticket} did not return a receipt!"
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
elsif session_receipt && controller.session[@@session_username] && !@@renew
|
262
|
-
|
263
|
-
log.info "Validating receipt from the session (instead of checking with the CAS server) because we have a :casfilteruser and the filter is not configured with @@renew."
|
264
|
-
log.debug "The session receipt is: #{session_receipt}"
|
265
|
-
|
266
|
-
is_valid = validate_receipt(session_receipt)
|
267
|
-
|
268
|
-
if is_valid
|
269
|
-
log.info "The session receipt is VALID"
|
270
|
-
else
|
271
|
-
log.warn "The session receipt is NOT VALID!"
|
272
|
-
end
|
273
|
-
|
274
|
-
else
|
275
|
-
|
276
|
-
log.info "No ticket was given and we do not have a receipt in the session."
|
277
|
-
|
278
|
-
|
279
|
-
raise CASException, "Can't redirect without login url" unless @@login_url
|
280
|
-
|
281
|
-
if did_gateway
|
282
|
-
log.info "We gatewayed and came back without authentication."
|
283
|
-
if self.gateway
|
284
|
-
log.info "This filter is configured to allow gatewaying, so we will permit the user to continue without authentication."
|
285
|
-
return true
|
286
|
-
else
|
287
|
-
log.warn "This filter is NOT configured to allow gatewaying, yet this request was gatewayed. Something is not right!"
|
288
|
-
end
|
289
|
-
elsif self.gateway
|
290
|
-
log.debug "We did not gateway, so we will notify the filter that the next request is being gatewayed by setting sesson[:casfiltergateway] to true"
|
291
|
-
controller.session[:casfiltergateway] = true
|
292
|
-
end
|
293
|
-
|
294
|
-
end
|
295
|
-
|
296
|
-
if is_valid
|
297
|
-
logger.info "This request is successfully CAS authenticated for user #{controller.session[@@session_username]}!"
|
298
|
-
return true
|
299
|
-
else
|
300
|
-
controller.session[:service] = service_url(controller)
|
301
|
-
logger.info "This request is NOT CAS authenticated, so we will redirect to the login page at: #{redirect_url(controller)}"
|
302
|
-
controller.send :redirect_to, redirect_url(controller) and return false
|
303
|
-
end
|
304
|
-
end
|
305
|
-
alias :filter :filter_r
|
306
|
-
|
307
|
-
# Requests a proxy ticket from the CAS server and returns it as a ProxyTicketRequest object.
|
308
|
-
#
|
309
|
-
# Note that the ProxyTicketRequest object is returned regardless of whether the request
|
310
|
-
# is successful. You should check the returned object's proxy_ticket field to find out
|
311
|
-
# whether the request resulted in a valid proxy ticket.
|
312
|
-
def request_proxy_ticket(target_service, pgt)
|
313
|
-
r = ProxyTicketRequest.new
|
314
|
-
r.proxy_url = @@proxy_url
|
315
|
-
r.target_service = target_service
|
316
|
-
r.pgt = pgt
|
317
|
-
|
318
|
-
# FIXME: Why is this here? The only way it would get raised is if the supplied pgt was nil/false? This might be a vestige...
|
319
|
-
raise CAS::ProxyGrantingNotAvailable, "Cannot request a proxy ticket for service #{r.target_service} because no proxy granting ticket (PGT) has been set." unless r.pgt
|
320
|
-
|
321
|
-
logger.info("Requesting proxy ticket for service: #{r.target_service} with PGT #{pgt}")
|
322
|
-
r.request
|
323
|
-
|
324
|
-
if r.proxy_ticket
|
325
|
-
logger.info("Got proxy ticket #{r.proxy_ticket} for service #{r.target_service}")
|
326
|
-
else
|
327
|
-
logger.warn("Did not receive a proxy ticket for service #{r.target_service}! Reason: #{r.error_code}: #{r.error_message}")
|
328
|
-
end
|
329
|
-
|
330
|
-
return r
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
private
|
337
|
-
|
338
|
-
# Retrieves a proxy granting ticket corresponding to the given receipt's
|
339
|
-
# proxy granting ticket IOU from the proxy callback server.
|
340
|
-
#
|
341
|
-
# Returns a CAS::ProxyGrantingTicket object.
|
342
|
-
def self.retrieve_pgt(receipt)
|
343
|
-
retrieve_url = "#{@@proxy_retrieval_url}?pgtIou=#{receipt.pgt_iou}"
|
344
|
-
|
345
|
-
logger.debug("Will attempt to retrieve the PGT from: #{retrieve_url}")
|
346
|
-
|
347
|
-
pgt = CAS::ServiceTicketValidator.retrieve(retrieve_url)
|
348
|
-
|
349
|
-
logger.info("Retrieved the PGT: #{pgt}")
|
350
|
-
|
351
|
-
return pgt
|
352
|
-
end
|
353
|
-
|
354
|
-
# Returns true if the given CAS::Receipt is valid; false wotherwise.
|
355
|
-
def self.validate_receipt(receipt)
|
356
|
-
logger.info "Checking that the receipt is valid and coherent..."
|
357
|
-
|
358
|
-
if not receipt
|
359
|
-
logger.info "No receipt given, so the receipt is invalid"
|
360
|
-
return false
|
361
|
-
elsif @@renew && !receipt.primary_authentication?
|
362
|
-
logger.info "The filter is configured to force primary authentication (i.e. the renew options is set to true), but the receipt was not generated by primary authentication so we consider it invalid"
|
363
|
-
return false
|
364
|
-
end
|
365
|
-
|
366
|
-
if receipt.proxied?
|
367
|
-
logger.info "The receipt is proxied by proxying service: #{receipt.proxying_service}"
|
368
|
-
|
369
|
-
if @@authorized_proxies and !@@authorized_proxies.empty?
|
370
|
-
logger.debug "Authorized proxies are: #{@@authorized_proxies.inspect}"
|
371
|
-
|
372
|
-
if !@@authorized_proxies.include? receipt.proxying_service
|
373
|
-
logger.warn "Receipt was proxied by #{receipt_proxying_service} but this proxying service is not in the list of authorized proxies. The receipt is therefore invalid."
|
374
|
-
return false
|
375
|
-
else
|
376
|
-
logger.info "Receipt is proxied by a valid proxying service."
|
377
|
-
end
|
378
|
-
else
|
379
|
-
logger.info "No authorized proxies set, so any proxy will be considered valid"
|
380
|
-
end
|
381
|
-
else
|
382
|
-
logger.info "Receipt is not proxied"
|
383
|
-
end
|
384
|
-
|
385
|
-
return true
|
386
|
-
end
|
387
|
-
|
388
|
-
# Fetches a CAS::Receipt for the given service or proxy ticket
|
389
|
-
# and returns it.
|
390
|
-
#
|
391
|
-
# Takes the current controller as the second argument in order to
|
392
|
-
# guess the current service URL when it is not explicitly set for
|
393
|
-
# the filter.
|
394
|
-
def self.get_receipt_for_ticket(ticket, controller)
|
395
|
-
logger.info "Getting receipt for ticket '#{ticket}'"
|
396
|
-
pv = ProxyTicketValidator.new
|
397
|
-
pv.validate_url = @@validate_url
|
398
|
-
pv.service_ticket = ticket
|
399
|
-
pv.service = controller.session[:service] || service_url(controller)
|
400
|
-
pv.renew = @@renew
|
401
|
-
pv.proxy_callback_url = @@proxy_callback_url
|
402
|
-
receipt = nil
|
403
|
-
logger.debug "ProxyTicketValidator is: #{pv.inspect}"
|
404
|
-
begin
|
405
|
-
receipt = Receipt.new(pv)
|
406
|
-
rescue AuthenticationException => e
|
407
|
-
logger.warn("Getting a receipt for the ProxyTicketValidator threw an exception: #{e}")
|
408
|
-
rescue MalformedServerResponseException => e
|
409
|
-
logger.error("CAS Server returned malformed response:\n\n#{e}")
|
410
|
-
raise e
|
411
|
-
end
|
412
|
-
logger.debug "Receipt is: #{receipt.inspect}"
|
413
|
-
receipt
|
414
|
-
end
|
415
|
-
|
416
|
-
# Returns the service URL for the current service.
|
417
|
-
#
|
418
|
-
# This will return the @@service_url if it has been explicitly
|
419
|
-
# set; otherwise it will try to guess the service URL based
|
420
|
-
# on the given controller parameters (see <tt>guess_service()</tt>).
|
421
|
-
def self.service_url(controller)
|
422
|
-
unclean = @@service_url || guess_service(controller)
|
423
|
-
clean = remove_ticket_from_service_uri(unclean)
|
424
|
-
logger.debug("Service URI without ticket is: #{clean}")
|
425
|
-
clean
|
426
|
-
end
|
427
|
-
|
428
|
-
def self.server_name=(s)
|
429
|
-
puts
|
430
|
-
puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
|
431
|
-
puts "!!! CAS CONFIGURATION WARNING !!!"
|
432
|
-
puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
|
433
|
-
puts
|
434
|
-
puts "CAS::Filter.server_name= no longer does anything."
|
435
|
-
puts
|
436
|
-
puts "If you want to explicitly set the service, try using:"
|
437
|
-
puts "CAS::Filter.service_url = 'http://myservice.com'"
|
438
|
-
puts
|
439
|
-
end
|
440
|
-
|
441
|
-
# Returns the URL to the login page of the CAS server with
|
442
|
-
# additional parameters like 'renew', and 'gateway' tacked
|
443
|
-
# on as appropriate. The <tt>url</tt> parameter can be used
|
444
|
-
# to use something other than the login url as the base.
|
445
|
-
#
|
446
|
-
# An optional <tt>service</tt> parameter can be provided to
|
447
|
-
# override the 'service' part of the URL.
|
448
|
-
#
|
449
|
-
# FIXME: this method is really poorly named :(
|
450
|
-
def self.redirect_url(controller,url=@@login_url,service=nil)
|
451
|
-
if service
|
452
|
-
service = remove_ticket_from_service_uri(service)
|
453
|
-
end
|
454
|
-
|
455
|
-
service = CGI.escape(service || service_url(controller))
|
456
|
-
|
457
|
-
"#{url}?service=#{service}" +
|
458
|
-
((@@renew)? "&renew=true":"") +
|
459
|
-
((gateway)? "&gateway=true":"") +
|
460
|
-
((@@query_string.blank?)? "" : "&" +
|
461
|
-
(@@query_string.collect { |k,v| "#{k}=#{v}"}.join("&")))
|
462
|
-
end
|
463
|
-
|
464
|
-
# Tries to figure out the current service URL.
|
465
|
-
#
|
466
|
-
# This is used when the @@service_url has not been explicitly set.
|
467
|
-
# The guessed URL (generally the current URL stripped of some
|
468
|
-
# CAS-specific parameters) is fed to the CAS server so that the
|
469
|
-
# server knows where to redirect back after authentication.
|
470
|
-
#
|
471
|
-
# Also see <tt>redirect_url</tt>.
|
472
|
-
def self.guess_service(controller)
|
473
|
-
logger.info "Guessing service based on params: #{controller.params.inspect}"
|
474
|
-
|
475
|
-
# we're assuming that controller.params[:service] is url-encoded!
|
476
|
-
if controller.params and controller.params.include? :service
|
477
|
-
service = controller.params[:service]
|
478
|
-
logger.info "We have a :service param, so we will URI-decode it and use this as the service: #{controller.params[:service]}"
|
479
|
-
return service
|
480
|
-
end
|
481
|
-
|
482
|
-
req = controller.request
|
483
|
-
|
484
|
-
if controller.params
|
485
|
-
parms = controller.params.dup
|
486
|
-
else
|
487
|
-
parms = {}
|
488
|
-
end
|
489
|
-
|
490
|
-
parms.delete("ticket")
|
491
|
-
service = controller.url_for(parms)
|
492
|
-
|
493
|
-
logger.info "Guessed service is: #{service}"
|
494
|
-
|
495
|
-
return service
|
496
|
-
end
|
497
|
-
|
498
|
-
# URI-encodes the
|
499
|
-
def self.escape_service_uri(uri)
|
500
|
-
# FIXME: Why aren't we just using CGi.escape?
|
501
|
-
URI.encode(uri, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]", false, 'U').freeze)
|
502
|
-
end
|
503
|
-
|
504
|
-
# The service URI should never have a ticket parameter, but we use this to remove
|
505
|
-
# any parameters named "ticket" just in case, as having a "ticket" parameter in the
|
506
|
-
# service URI will generally cause an infinite redirection loop.
|
507
|
-
def self.remove_ticket_from_service_uri(uri)
|
508
|
-
uri.gsub(/ticket=[^&$]*&?/, '')
|
509
|
-
end
|
510
|
-
end
|
511
|
-
|
512
|
-
# The GatewayFilter is identical to the normal Filter, but has the gateway
|
513
|
-
# option set to true by default. This makes it easier to use in cases where
|
514
|
-
# authentication is optional.
|
515
|
-
#
|
516
|
-
# For example, say your 'index' view is accessible by authenticated and
|
517
|
-
# unauthenticated users, but you want some additional content shown for
|
518
|
-
# authenticated users. You can use the GatewayFilter to check if the user is
|
519
|
-
# already authenticated with CAS and provide them with a service ticket for
|
520
|
-
# the new service. If they are not already authenticated, then they will be
|
521
|
-
# allowed to see the 'index' view without being asked for a login.
|
522
|
-
#
|
523
|
-
# To achieve this in a Rails controller, you should set up your filters as follows:
|
524
|
-
#
|
525
|
-
# before_filter CAS::Filter, :except => [:index]
|
526
|
-
# before_filter CAS::GatewayFilter, :only => [:index]
|
527
|
-
#
|
528
|
-
# Note that you cannot use the 'renew' option with the GatewayFilter since the
|
529
|
-
# 'gateway' and 'renew' options have roughly opposite meanings -- 'renew' forces
|
530
|
-
# re-authentication, while 'gateway' makes authentication optional.
|
531
|
-
class GatewayFilter < Filter
|
532
|
-
self.gateway = true
|
533
|
-
self.renew = false
|
534
|
-
|
535
|
-
def logout_url
|
536
|
-
uri = URI.parse(super)
|
537
|
-
if uri.query?
|
538
|
-
uri.to_s + "&gateway=true"
|
539
|
-
else
|
540
|
-
uri.to_s + "?gateway=true"
|
541
|
-
end
|
542
|
-
end
|
543
|
-
end
|
544
|
-
|
545
|
-
class ProxyGrantingNotAvailable < Exception
|
546
|
-
end
|
547
|
-
end
|
548
|
-
|
549
|
-
class ActionController::AbstractRequest
|
550
|
-
def username
|
551
|
-
session[CAS::Filter.session_username]
|
552
|
-
end
|
553
|
-
end
|