vibes-rubycas-client 2.3.0.alpha → 2.3.0.alpha2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +3 -2
- data/Gemfile.lock +5 -2
- data/History.txt +21 -0
- data/README.rdoc +14 -12
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/lib/casclient/client.rb +19 -26
- data/lib/casclient/frameworks/rails/cas_proxy_callback_controller.rb +5 -31
- data/lib/casclient/frameworks/rails/filter.rb +15 -48
- data/lib/casclient/responses.rb +12 -13
- data/lib/casclient/tickets/storage.rb +131 -0
- data/lib/casclient.rb +1 -0
- data/test/teststrap.rb +7 -0
- data/test/units/casclient/frameworks/rails/filter_test.rb +32 -0
- data/vibes-rubycas-client.gemspec +11 -8
- metadata +27 -12
- data/.source_index +0 -0
- data/lib/casclient/frameworks/merb/filter.rb +0 -105
- data/lib/casclient/frameworks/merb/strategy.rb +0 -110
data/Gemfile
CHANGED
@@ -6,10 +6,11 @@ source "http://rubygems.org"
|
|
6
6
|
# Add dependencies to develop your gem here.
|
7
7
|
# Include everything needed to run rake, tests, features, etc.
|
8
8
|
group :development do
|
9
|
-
gem "
|
9
|
+
gem "riot"
|
10
|
+
gem "rr"
|
10
11
|
gem "bundler", "~> 1.0.0"
|
11
12
|
gem "jeweler", "~> 1.6.2"
|
12
|
-
gem "rcov"
|
13
|
+
gem "rcov"
|
13
14
|
end
|
14
15
|
|
15
16
|
gem "activesupport", "~> 2.3.11"
|
data/Gemfile.lock
CHANGED
data/History.txt
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
= RubyCAS-Client Changelog
|
2
2
|
|
3
|
+
== Version 2.3.0 :: Prerelease
|
4
|
+
|
5
|
+
* New Functionality
|
6
|
+
* Add configuration option to expect complex extra attributes to be encoded
|
7
|
+
in json instead of yaml
|
8
|
+
* Split out storage mechanism for single sign out and proxy ticket storage so
|
9
|
+
that it is modular
|
10
|
+
|
11
|
+
* Changes to existing functionality
|
12
|
+
* Change gem building from hoe to jeweler
|
13
|
+
* expect extra attributes to be nested under a cas:attributes elemenet to
|
14
|
+
improve compatibility with other extra attribute implementations
|
15
|
+
* Unauthorized requests to URLs ending in .json now show an JSON formatted
|
16
|
+
response
|
17
|
+
|
18
|
+
* Bug Fixes
|
19
|
+
* Fixed bug introduced by upstream patch that broke proxy ticket validation
|
20
|
+
when using extra attributes
|
21
|
+
* Fixed bug where extra attributes key was set on the session with a null
|
22
|
+
value when faking with no extra attributes
|
23
|
+
|
3
24
|
== Version 2.2.1 :: 2010-06-24
|
4
25
|
|
5
26
|
* Removed a 3rd party patch to the logging mechanism that broke the client under
|
data/README.rdoc
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
= RubyCAS-Client
|
2
2
|
|
3
|
-
|
3
|
+
Authors:: Matt Campbell and Rahul Joshi, forked from original project by Matt Zukowski <matt AT roughest DOT net>; inspired by code by Ola Bini <ola.bini AT ki DOT se> and Matt Walker <mwalker AT tamu DOT edu>
|
4
4
|
Copyright:: Portions contributed by Matt Zukowski are copyright (c) 2009 Urbacon Ltd.
|
5
|
+
Protions contributed by Matt Campbell and Rahul Joshi are copyright (c) 2011 Vibes Media LLC.
|
5
6
|
Other portions are copyright of their respective authors.
|
6
7
|
License:: MIT License
|
7
|
-
Websites:: http://github.com/
|
8
|
-
http://
|
9
|
-
http://
|
10
|
-
|
8
|
+
Websites:: http://github.com/vibes/rubycas-client
|
9
|
+
http://github.com/vibes/rubycas-client/wiki
|
10
|
+
http://rubydoc.info/github/vibes/rubycas-client/master/frames
|
11
11
|
|
12
12
|
|
13
13
|
=== RubyCAS-Client is a Ruby client library for Yale's Central Authentication Service (CAS) protocol.
|
@@ -234,7 +234,6 @@ In your <tt>config/environment.rb</tt>:
|
|
234
234
|
|
235
235
|
CASClient::Frameworks::Rails::Filter.configure(
|
236
236
|
:cas_base_url => "https://cas.example.foo/",
|
237
|
-
:proxy_retrieval_url => "https://cas-proxy-callback.example.foo/cas_proxy_callback/retrieve_pgt",
|
238
237
|
:proxy_callback_url => "https://cas-proxy-callback.example.foo/cas_proxy_callback/receive_pgt",
|
239
238
|
:logger => cas_logger
|
240
239
|
)
|
@@ -254,12 +253,9 @@ but your Rails server wouldn't respond to the CAS server's callback until the CA
|
|
254
253
|
|
255
254
|
The simplest workaround is this:
|
256
255
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
3. Make sure that the server is up and running, and configure your proxy_callback_url and proxy_retrieval_url to point
|
261
|
-
to the new server as described above (or rather, make Pound point to the new server, if that's how you're handling https).
|
262
|
-
|
256
|
+
Run rails using a server that handles multiple concurrent requests. In development, you can use Phusion Passenger Standalone,
|
257
|
+
POW (http://pow.cx/), unicorn and many others. In production, I imagine you already support multiple concurrent requests.
|
258
|
+
|
263
259
|
That's it. The proxy_callback_controller doesn't require any additional configuration. It doesn't access the database
|
264
260
|
or anything of that sort.
|
265
261
|
|
@@ -315,6 +311,12 @@ In your test or Cucumber step definition, simply fake out CAS.
|
|
315
311
|
This functionality was present in the original version of this plugin.
|
316
312
|
The value of the username is stored in session[:cas_user] (or the user specified field) and session[:casfilteruser] for backwards-compatibility.
|
317
313
|
|
314
|
+
If you need to fake out extra attributes, you can do so like this:
|
315
|
+
|
316
|
+
CASClient::Frameworks::Rails::Filter.fake("homer", {:role => "user", :email => "homer@test.foo"})
|
317
|
+
|
318
|
+
And the extra attributes will get put in the proper place in the session.
|
319
|
+
|
318
320
|
== License
|
319
321
|
|
320
322
|
RubyCAS-Client is licensed for use under the terms of the MIT License.
|
data/Rakefile
CHANGED
@@ -27,8 +27,8 @@ Jeweler::RubygemsDotOrgTasks.new
|
|
27
27
|
|
28
28
|
require 'rake/testtask'
|
29
29
|
Rake::TestTask.new(:test) do |test|
|
30
|
-
test.libs << '
|
31
|
-
test.pattern = 'test
|
30
|
+
test.libs << 'test'
|
31
|
+
test.pattern = 'test/**/*_test.rb'
|
32
32
|
test.verbose = true
|
33
33
|
end
|
34
34
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.3.0.
|
1
|
+
2.3.0.alpha2
|
data/lib/casclient/client.rb
CHANGED
@@ -3,6 +3,7 @@ module CASClient
|
|
3
3
|
class Client
|
4
4
|
attr_reader :cas_base_url
|
5
5
|
attr_reader :log, :username_session_key, :extra_attributes_session_key
|
6
|
+
attr_reader :ticket_store
|
6
7
|
attr_writer :login_url, :validate_url, :proxy_url, :logout_url, :service_url
|
7
8
|
attr_accessor :proxy_callback_url, :proxy_retrieval_url
|
8
9
|
|
@@ -15,6 +16,12 @@ module CASClient
|
|
15
16
|
|
16
17
|
raise ArgumentError, "Missing :cas_base_url parameter!" unless conf[:cas_base_url]
|
17
18
|
|
19
|
+
if conf.has_key?("encode_extra_attributes_as")
|
20
|
+
unless (conf[:encode_extra_attributes_as] == :json || conf[:encode_extra_attributes_as] == :yaml)
|
21
|
+
raise ArgumentError, "Unkown Value for :encode_extra_attributes_as parameter! Allowed options are json or yaml - #{conf[:encode_extra_attributes_as]}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
18
25
|
@cas_base_url = conf[:cas_base_url].gsub(/\/$/, '')
|
19
26
|
|
20
27
|
@login_url = conf[:login_url]
|
@@ -24,13 +31,16 @@ module CASClient
|
|
24
31
|
@service_url = conf[:service_url]
|
25
32
|
@force_ssl_verification = conf[:force_ssl_verification]
|
26
33
|
@proxy_callback_url = conf[:proxy_callback_url]
|
27
|
-
@proxy_retrieval_url = conf[:proxy_retrieval_url]
|
28
34
|
|
29
35
|
@username_session_key = conf[:username_session_key] || :cas_user
|
30
36
|
@extra_attributes_session_key = conf[:extra_attributes_session_key] || :cas_extra_attributes
|
37
|
+
@ticket_store_class = conf[:ticket_store] || CASClient::Tickets::Storage::LocalDirTicketStore
|
38
|
+
@ticket_store = @ticket_store_class.new conf[:ticket_store_config]
|
39
|
+
raise CASException, "The Ticket Store is not a subclass of AbstractTicketStore, it is a #{@ticket_store_class}" unless @ticket_store.kind_of? CASClient::Tickets::Storage::AbstractTicketStore
|
31
40
|
|
32
41
|
@log = CASClient::LoggerWrapper.new
|
33
42
|
@log.set_real_logger(conf[:logger]) if conf[:logger]
|
43
|
+
@conf_options = conf
|
34
44
|
end
|
35
45
|
|
36
46
|
def login_url
|
@@ -181,28 +191,11 @@ module CASClient
|
|
181
191
|
end
|
182
192
|
|
183
193
|
def retrieve_proxy_granting_ticket(pgt_iou)
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
# https = Net::HTTP.new(uri.host, uri.port)
|
191
|
-
# https.use_ssl = (uri.scheme == 'https')
|
192
|
-
# res = https.post(uri.path, ';')
|
193
|
-
uri = URI.parse(uri) unless uri.kind_of? URI
|
194
|
-
https = Net::HTTP.new(uri.host, uri.port)
|
195
|
-
https.use_ssl = (uri.scheme == 'https')
|
196
|
-
https.verify_mode = (@force_ssl_verification ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE)
|
197
|
-
|
198
|
-
res = https.start do |conn|
|
199
|
-
conn.get("#{uri.path}?#{uri.query}")
|
200
|
-
end
|
201
|
-
|
202
|
-
|
203
|
-
raise CASException, res.body unless res.kind_of? Net::HTTPSuccess
|
204
|
-
|
205
|
-
ProxyGrantingTicket.new(res.body.strip, pgt_iou)
|
194
|
+
pgt = @ticket_store.retrieve_pgt(pgt_iou)
|
195
|
+
|
196
|
+
raise CASException, "Couldn't find pgt for pgt_iou #{pgt_iou}" unless pgt
|
197
|
+
|
198
|
+
ProxyGrantingTicket.new(pgt, pgt_iou)
|
206
199
|
end
|
207
200
|
|
208
201
|
def add_service_to_login_url(service_url)
|
@@ -214,7 +207,7 @@ module CASClient
|
|
214
207
|
private
|
215
208
|
# Fetches a CAS response of the given type from the given URI.
|
216
209
|
# Type should be either ValidationResponse or ProxyResponse.
|
217
|
-
def request_cas_response(uri, type)
|
210
|
+
def request_cas_response(uri, type, options={})
|
218
211
|
log.debug "Requesting CAS response for URI #{uri}"
|
219
212
|
|
220
213
|
uri = URI.parse(uri) unless uri.kind_of? URI
|
@@ -240,8 +233,8 @@ module CASClient
|
|
240
233
|
log.error "CAS server responded with an error! (#{raw_res.inspect})"
|
241
234
|
raise "The CAS authentication server at #{uri} responded with an error (#{raw_res.inspect})!"
|
242
235
|
end
|
243
|
-
|
244
|
-
type.new(raw_res.body)
|
236
|
+
|
237
|
+
type.new(raw_res.body, @conf_options)
|
245
238
|
end
|
246
239
|
|
247
240
|
# Submits some data to the given URI and returns a Net::HTTPResponse.
|
@@ -24,6 +24,11 @@ class CasProxyCallbackController < ActionController::Base
|
|
24
24
|
render :text => "Okay, the server is up, but please specify a pgtIou and pgtId." and return unless pgtIou and pgtId
|
25
25
|
|
26
26
|
# TODO: pstore contents should probably be encrypted...
|
27
|
+
|
28
|
+
casclient = CASClient::Frameworks::Rails::Filter.client
|
29
|
+
|
30
|
+
casclient.ticket_store.save_pgt_iou(pgtIou, pgtId)
|
31
|
+
|
27
32
|
pstore = open_pstore
|
28
33
|
|
29
34
|
pstore.transaction do
|
@@ -33,37 +38,6 @@ class CasProxyCallbackController < ActionController::Base
|
|
33
38
|
render :text => "PGT received. Thank you!" and return
|
34
39
|
end
|
35
40
|
|
36
|
-
# Retreives a proxy granting ticket, sends it to output, and deletes the pgt from session storage.
|
37
|
-
# Note that this action should ALWAYS be called via https, otherwise you have a gaping security hole --
|
38
|
-
# in fact, the action will not work if the request is not made via SSL or is not local (we allow for local
|
39
|
-
# non-SSL requests since this allows for the use of reverse HTTPS proxies like Pound).
|
40
|
-
def retrieve_pgt
|
41
|
-
#render_error "You can only retrieve PGTs via HTTPS or local connections." and return unless
|
42
|
-
# request.ssl? or request.env['REMOTE_HOST'] == "127.0.0.1"
|
43
|
-
|
44
|
-
pgtIou = params['pgtIou']
|
45
|
-
|
46
|
-
render_error "No pgtIou specified. Cannot retreive the pgtId." and return unless pgtIou
|
47
|
-
|
48
|
-
pstore = open_pstore
|
49
|
-
|
50
|
-
pgt = nil
|
51
|
-
pstore.transaction do
|
52
|
-
pgt = pstore[pgtIou]
|
53
|
-
end
|
54
|
-
|
55
|
-
if not pgt
|
56
|
-
render_error "Invalid pgtIou specified. Perhaps this pgt has already been retrieved?" and return
|
57
|
-
end
|
58
|
-
|
59
|
-
render :text => pgt
|
60
|
-
|
61
|
-
# TODO: need to periodically clean the storage, otherwise it will just keep growing
|
62
|
-
pstore.transaction do
|
63
|
-
pstore.delete pgtIou
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
41
|
private
|
68
42
|
def render_error(msg)
|
69
43
|
# Note that the error messages are mostly just for debugging, since the CAS server never reads them.
|
@@ -18,11 +18,10 @@ module CASClient
|
|
18
18
|
if @@fake_user
|
19
19
|
controller.session[client.username_session_key] = @@fake_user
|
20
20
|
controller.session[:casfilteruser] = @@fake_user
|
21
|
-
controller.session[client.extra_attributes_session_key] = @@fake_extra_attributes
|
21
|
+
controller.session[client.extra_attributes_session_key] = @@fake_extra_attributes if @@fake_extra_attributes
|
22
22
|
return true
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
25
|
last_st = controller.session[:cas_last_valid_ticket]
|
27
26
|
|
28
27
|
if single_sign_out(controller)
|
@@ -79,7 +78,7 @@ module CASClient
|
|
79
78
|
controller.session[:casfilteruser] = vr.user
|
80
79
|
|
81
80
|
if config[:enable_single_sign_out]
|
82
|
-
f = store_service_session_lookup(st, controller.request.session_options[:id] || controller.session.session_id)
|
81
|
+
f = @@client.ticket_store.store_service_session_lookup(st, controller.request.session_options[:id] || controller.session.session_id)
|
83
82
|
log.debug("Wrote service session lookup file to #{f.inspect} with session id #{controller.request.session_options[:id] || controller.session.session_id.inspect}.")
|
84
83
|
end
|
85
84
|
end
|
@@ -219,17 +218,25 @@ module CASClient
|
|
219
218
|
def logout(controller, service = nil)
|
220
219
|
referer = service || controller.request.referer
|
221
220
|
st = controller.session[:cas_last_valid_ticket]
|
222
|
-
delete_service_session_lookup(st) if st
|
221
|
+
@@client.ticket_store.delete_service_session_lookup(st) if st
|
223
222
|
controller.send(:reset_session)
|
224
223
|
controller.send(:redirect_to, client.logout_url(referer))
|
225
224
|
end
|
226
225
|
|
227
226
|
def unauthorized!(controller, vr = nil)
|
228
|
-
|
227
|
+
format = controller.request.format.to_sym
|
228
|
+
format = (format == :js ? :json : format)
|
229
|
+
case format
|
230
|
+
when :xml, :json
|
229
231
|
if vr
|
230
|
-
|
232
|
+
case format
|
233
|
+
when :xml
|
234
|
+
controller.send(:render, :xml => { :error => vr.failure_message }.to_xml(:root => 'errors'), :status => :unauthorized)
|
235
|
+
when :json
|
236
|
+
controller.send(:render, :json => { :errors => { :error => vr.failure_message }}, :status => :unauthorized)
|
237
|
+
end
|
231
238
|
else
|
232
|
-
controller.send(:head,
|
239
|
+
controller.send(:head, :unauthorized)
|
233
240
|
end
|
234
241
|
else
|
235
242
|
redirect_to_cas_for_authentication(controller)
|
@@ -302,7 +309,7 @@ module CASClient
|
|
302
309
|
|
303
310
|
|
304
311
|
if current_sess_store == required_sess_store
|
305
|
-
session_id = read_service_session_lookup(si)
|
312
|
+
session_id = @@client.ticket_store.read_service_session_lookup(si)
|
306
313
|
|
307
314
|
if session_id
|
308
315
|
session = current_sess_store::Session.find_by_session_id(session_id)
|
@@ -362,46 +369,6 @@ module CASClient
|
|
362
369
|
log.debug("Guessed service url: #{service_url.inspect}")
|
363
370
|
return service_url
|
364
371
|
end
|
365
|
-
|
366
|
-
# Creates a file in tmp/sessions linking a SessionTicket
|
367
|
-
# with the local Rails session id. The file is named
|
368
|
-
# cas_sess.<session ticket> and its text contents is the corresponding
|
369
|
-
# Rails session id.
|
370
|
-
# Returns the filename of the lookup file created.
|
371
|
-
def store_service_session_lookup(st, sid)
|
372
|
-
st = st.ticket if st.kind_of? ServiceTicket
|
373
|
-
f = File.new(filename_of_service_session_lookup(st), 'w')
|
374
|
-
f.write(sid)
|
375
|
-
f.close
|
376
|
-
return f.path
|
377
|
-
end
|
378
|
-
|
379
|
-
# Returns the local Rails session ID corresponding to the given
|
380
|
-
# ServiceTicket. This is done by reading the contents of the
|
381
|
-
# cas_sess.<session ticket> file created in a prior call to
|
382
|
-
# #store_service_session_lookup.
|
383
|
-
def read_service_session_lookup(st)
|
384
|
-
st = st.ticket if st.kind_of? ServiceTicket
|
385
|
-
ssl_filename = filename_of_service_session_lookup(st)
|
386
|
-
return File.exists?(ssl_filename) && IO.read(ssl_filename)
|
387
|
-
end
|
388
|
-
|
389
|
-
# Removes a stored relationship between a ServiceTicket and a local
|
390
|
-
# Rails session id. This should be called when the session is being
|
391
|
-
# closed.
|
392
|
-
#
|
393
|
-
# See #store_service_session_lookup.
|
394
|
-
def delete_service_session_lookup(st)
|
395
|
-
st = st.ticket if st.kind_of? ServiceTicket
|
396
|
-
ssl_filename = filename_of_service_session_lookup(st)
|
397
|
-
File.delete(ssl_filename) if File.exists?(ssl_filename)
|
398
|
-
end
|
399
|
-
|
400
|
-
# Returns the path and filename of the service session lookup file.
|
401
|
-
def filename_of_service_session_lookup(st)
|
402
|
-
st = st.ticket if st.kind_of? ServiceTicket
|
403
|
-
return "#{RAILS_ROOT}/tmp/sessions/cas_sess.#{st}"
|
404
|
-
end
|
405
372
|
end
|
406
373
|
end
|
407
374
|
|
data/lib/casclient/responses.rb
CHANGED
@@ -31,7 +31,8 @@ module CASClient
|
|
31
31
|
|
32
32
|
attr_reader :protocol, :user, :pgt_iou, :proxies, :extra_attributes
|
33
33
|
|
34
|
-
def initialize(raw_text)
|
34
|
+
def initialize(raw_text, options={})
|
35
|
+
conf_options = options
|
35
36
|
parse(raw_text)
|
36
37
|
end
|
37
38
|
|
@@ -66,23 +67,22 @@ module CASClient
|
|
66
67
|
end
|
67
68
|
|
68
69
|
@extra_attributes = {}
|
69
|
-
@xml.elements.to_a('//cas:authenticationSuccess/*').each do |el|
|
70
|
+
@xml.elements.to_a('//cas:authenticationSuccess/cas:attributes/*').each do |el|
|
70
71
|
# generating the hash requires prefixes to be defined, so add all of the namespaces
|
71
72
|
el.namespaces.each {|k,v| el.add_namespace(k,v)}
|
72
|
-
@extra_attributes.merge!(Hash.from_xml(el.to_s))
|
73
|
+
@extra_attributes.merge!(Hash.from_xml(el.to_s))
|
73
74
|
end
|
74
75
|
|
75
76
|
# unserialize extra attributes
|
76
77
|
@extra_attributes.each do |k, v|
|
77
|
-
Rails.logger.debug "#{k.inspect} => #{v.inspect}"
|
78
78
|
if v.blank?
|
79
79
|
@extra_attributes[k] = nil
|
80
|
+
elsif v.kind_of?(String)
|
81
|
+
@extra_attributes[k] = v
|
82
|
+
elsif conf.has_key?('encode_extra_attributes_as') && conf_options[:encode_extra_attributes_as] == :json
|
83
|
+
@extra_attributes[k] = JSON.parse(v)
|
80
84
|
else
|
81
|
-
|
82
|
-
@extra_attributes[k] = JSON.parse(v)
|
83
|
-
rescue JSON::ParserError => e
|
84
|
-
@extra_attributes[k] = v
|
85
|
-
end
|
85
|
+
@extra_attributes[k] = YAML.load(v)
|
86
86
|
end
|
87
87
|
end
|
88
88
|
elsif is_failure?
|
@@ -92,9 +92,8 @@ module CASClient
|
|
92
92
|
# this should never happen, since the response should already have been recognized as invalid
|
93
93
|
raise BadResponseException, "BAD CAS RESPONSE:\n#{raw_text.inspect}\n\nXML DOC:\n#{doc.inspect}"
|
94
94
|
end
|
95
|
-
|
96
95
|
end
|
97
|
-
|
96
|
+
|
98
97
|
def is_success?
|
99
98
|
(instance_variable_defined?(:@valid) && @valid) || (protocol > 1.0 && xml.name == "authenticationSuccess")
|
100
99
|
end
|
@@ -111,7 +110,7 @@ module CASClient
|
|
111
110
|
|
112
111
|
attr_reader :proxy_ticket
|
113
112
|
|
114
|
-
def initialize(raw_text)
|
113
|
+
def initialize(raw_text, options={})
|
115
114
|
parse(raw_text)
|
116
115
|
end
|
117
116
|
|
@@ -149,7 +148,7 @@ module CASClient
|
|
149
148
|
attr_reader :tgt, :ticket, :service_redirect_url
|
150
149
|
attr_reader :failure_message
|
151
150
|
|
152
|
-
def initialize(http_response = nil)
|
151
|
+
def initialize(http_response = nil, options={})
|
153
152
|
parse_http_response(http_response) if http_response
|
154
153
|
end
|
155
154
|
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module CASClient
|
2
|
+
module Tickets
|
3
|
+
module Storage
|
4
|
+
class AbstractTicketStore
|
5
|
+
def store_service_session_lookup(st, sid)
|
6
|
+
raise 'Implement this in a subclass!'
|
7
|
+
end
|
8
|
+
|
9
|
+
def read_servcie_session_lookup(st)
|
10
|
+
raise 'Implement this in a subclass!'
|
11
|
+
end
|
12
|
+
|
13
|
+
def delete_service_session_lookup(st)
|
14
|
+
raise 'Implement this in a subclass!'
|
15
|
+
end
|
16
|
+
|
17
|
+
def save_pgt_iou(pgt_iou, pgt)
|
18
|
+
raise 'Implement this in a subclass!'
|
19
|
+
end
|
20
|
+
|
21
|
+
def retrieve_pgt(pgt_iou)
|
22
|
+
raise 'Implement this in a subclass!'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# A Ticket Store that keeps it's tickets in a directory on the local filesystem.
|
27
|
+
# Service tickets are stored under tmp/sessions by default
|
28
|
+
# and Proxy Granting Tickets and their IOUs are stored in tmp/cas_pgt.pstore
|
29
|
+
# This Ticket Store works fine for small sites but will most likely have
|
30
|
+
# concurrency problems under heavy load. It also requires that all your
|
31
|
+
# worker processes have access to a shared file system.
|
32
|
+
#
|
33
|
+
# This ticket store takes the following config parameters
|
34
|
+
# :storage_dir - The directory to store data in. Defaults to RAILS_ROOT/tmp
|
35
|
+
# :service_session_lookup_dir - The directory to store Service Ticket/Session ID files in. Defaults to :storage_dir/sessions
|
36
|
+
# :pgt_store_path - The location to store the pgt PStore file. Defaults to :storage_dir/cas_pgt.pstore
|
37
|
+
class LocalDirTicketStore < AbstractTicketStore
|
38
|
+
require 'pstore'
|
39
|
+
|
40
|
+
def initialize(config={})
|
41
|
+
config ||= {}
|
42
|
+
@tmp_dir = config[:storage_dir] || "#{RAILS_ROOT}/tmp"
|
43
|
+
@service_session_lookup_dir = config[:service_session_lookup_dir] || "#{@tmp_dir}/sessions"
|
44
|
+
@pgt_store_path = config[:pgt_store_path] || "#{@tmp_dir}/cas_pgt.pstore"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Creates a file in tmp/sessions linking a SessionTicket
|
48
|
+
# with the local Rails session id. The file is named
|
49
|
+
# cas_sess.<session ticket> and its text contents is the corresponding
|
50
|
+
# Rails session id.
|
51
|
+
# Returns the filename of the lookup file created.
|
52
|
+
def store_service_session_lookup(st, sid)
|
53
|
+
raise CASException, "No service_ticket specified." unless st
|
54
|
+
raise CASException, "No session_id specified." unless sid
|
55
|
+
|
56
|
+
st = st.ticket if st.kind_of? ServiceTicket
|
57
|
+
f = File.new(filename_of_service_session_lookup(st), 'w')
|
58
|
+
f.write(sid)
|
59
|
+
f.close
|
60
|
+
return f.path
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the local Rails session ID corresponding to the given
|
64
|
+
# ServiceTicket. This is done by reading the contents of the
|
65
|
+
# cas_sess.<session ticket> file created in a prior call to
|
66
|
+
# #store_service_session_lookup.
|
67
|
+
def read_service_session_lookup(st)
|
68
|
+
raise CASException, "No service_ticket specified." unless st
|
69
|
+
|
70
|
+
st = st.ticket if st.kind_of? ServiceTicket
|
71
|
+
ssl_filename = filename_of_service_session_lookup(st)
|
72
|
+
return File.exists?(ssl_filename) && IO.read(ssl_filename)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Removes a stored relationship between a ServiceTicket and a local
|
76
|
+
# Rails session id. This should be called when the session is being
|
77
|
+
# closed.
|
78
|
+
#
|
79
|
+
# See #store_service_session_lookup.
|
80
|
+
def delete_service_session_lookup(st)
|
81
|
+
raise CASException, "No service_ticket specified." unless st
|
82
|
+
|
83
|
+
st = st.ticket if st.kind_of? ServiceTicket
|
84
|
+
ssl_filename = filename_of_service_session_lookup(st)
|
85
|
+
File.delete(ssl_filename) if File.exists?(ssl_filename)
|
86
|
+
end
|
87
|
+
|
88
|
+
def save_pgt_iou(pgt_iou, pgt)
|
89
|
+
# TODO: pstore contents should probably be encrypted...
|
90
|
+
pstore = open_pstore
|
91
|
+
|
92
|
+
pstore.transaction do
|
93
|
+
pstore[pgt_iou] = pgt
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def retrieve_pgt(pgt_iou)
|
98
|
+
raise CASException, "No pgt_iou specified. Cannot retrieve the pgt." unless pgt_iou
|
99
|
+
|
100
|
+
pstore = open_pstore
|
101
|
+
|
102
|
+
pgt = nil
|
103
|
+
pstore.transaction do
|
104
|
+
pgt = pstore[pgt_iou]
|
105
|
+
end
|
106
|
+
|
107
|
+
raise CASException, "Invalid pgt_iou specified. Perhaps this pgt has already been retrieved?" unless pgt
|
108
|
+
|
109
|
+
# TODO: need to periodically clean the storage, otherwise it will just keep growing
|
110
|
+
pstore.transaction do
|
111
|
+
pstore.delete pgt_iou
|
112
|
+
end
|
113
|
+
|
114
|
+
pgt
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
# Returns the path and filename of the service session lookup file.
|
120
|
+
def filename_of_service_session_lookup(st)
|
121
|
+
st = st.ticket if st.kind_of? ServiceTicket
|
122
|
+
return "#{@service_session_lookup_dir}/cas_sess.#{st}"
|
123
|
+
end
|
124
|
+
|
125
|
+
def open_pstore
|
126
|
+
PStore.new(@pgt_store_path)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/lib/casclient.rb
CHANGED
data/test/teststrap.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'teststrap'
|
2
|
+
require 'casclient/frameworks/rails/filter'
|
3
|
+
|
4
|
+
context CASClient::Frameworks::Rails::Filter do
|
5
|
+
helper(:controller_with_session) do |session|
|
6
|
+
controller = Object.new
|
7
|
+
stub(controller).session {session}
|
8
|
+
controller
|
9
|
+
end
|
10
|
+
setup do
|
11
|
+
CASClient::Frameworks::Rails::Filter.configure(
|
12
|
+
:cas_base_url => 'http://test.local/',
|
13
|
+
:logger => stub!
|
14
|
+
)
|
15
|
+
end
|
16
|
+
context "that has fake called with a username" do
|
17
|
+
setup { CASClient::Frameworks::Rails::Filter.fake('tester@test.com') }
|
18
|
+
should 'set the session user on #filter' do
|
19
|
+
setup { Hash.new }
|
20
|
+
CASClient::Frameworks::Rails::Filter.filter(controller_with_session(topic))
|
21
|
+
topic
|
22
|
+
end.equals :cas_user => 'tester@test.com', :casfilteruser => 'tester@test.com'
|
23
|
+
end
|
24
|
+
context "that has fake called with a username and attributes" do
|
25
|
+
setup { CASClient::Frameworks::Rails::Filter.fake('tester@test.com', {:test => 'stuff', :this => 'that'}) }
|
26
|
+
should 'set the session user and attributes on #filter' do
|
27
|
+
setup { Hash.new }
|
28
|
+
CASClient::Frameworks::Rails::Filter.filter(controller_with_session(topic))
|
29
|
+
topic
|
30
|
+
end.equals :cas_user => 'tester@test.com', :casfilteruser => 'tester@test.com', :cas_extra_attributes => {:test => 'stuff', :this => 'that' }
|
31
|
+
end
|
32
|
+
end
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{vibes-rubycas-client}
|
8
|
-
s.version = "2.3.0.
|
8
|
+
s.version = "2.3.0.alpha2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Matt Campbell", "Rahul Joshi", "Matt Zukowski", "Matt Walker"]
|
12
|
-
s.date = %q{2011-06-
|
12
|
+
s.date = %q{2011-06-11}
|
13
13
|
s.description = %q{We've taken the rubycas-client and added some enterprisey features and improved compatibility with JASIG's CAS server}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"LICENSE.txt",
|
@@ -17,7 +17,6 @@ Gem::Specification.new do |s|
|
|
17
17
|
]
|
18
18
|
s.files = [
|
19
19
|
".rvmrc",
|
20
|
-
".source_index",
|
21
20
|
"CHANGELOG.txt",
|
22
21
|
"Gemfile",
|
23
22
|
"Gemfile.lock",
|
@@ -57,13 +56,14 @@ Gem::Specification.new do |s|
|
|
57
56
|
"examples/rails/script/server",
|
58
57
|
"lib/casclient.rb",
|
59
58
|
"lib/casclient/client.rb",
|
60
|
-
"lib/casclient/frameworks/merb/filter.rb",
|
61
|
-
"lib/casclient/frameworks/merb/strategy.rb",
|
62
59
|
"lib/casclient/frameworks/rails/cas_proxy_callback_controller.rb",
|
63
60
|
"lib/casclient/frameworks/rails/filter.rb",
|
64
61
|
"lib/casclient/responses.rb",
|
65
62
|
"lib/casclient/tickets.rb",
|
63
|
+
"lib/casclient/tickets/storage.rb",
|
66
64
|
"lib/vibes-rubycas-client.rb",
|
65
|
+
"test/teststrap.rb",
|
66
|
+
"test/units/casclient/frameworks/rails/filter_test.rb",
|
67
67
|
"vibes-rubycas-client.gemspec"
|
68
68
|
]
|
69
69
|
s.homepage = %q{http://github.com/vibes/rubycas-client}
|
@@ -78,20 +78,23 @@ Gem::Specification.new do |s|
|
|
78
78
|
|
79
79
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
80
80
|
s.add_runtime_dependency(%q<activesupport>, ["~> 2.3.11"])
|
81
|
-
s.add_development_dependency(%q<
|
81
|
+
s.add_development_dependency(%q<riot>, [">= 0"])
|
82
|
+
s.add_development_dependency(%q<rr>, [">= 0"])
|
82
83
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
83
84
|
s.add_development_dependency(%q<jeweler>, ["~> 1.6.2"])
|
84
85
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
85
86
|
else
|
86
87
|
s.add_dependency(%q<activesupport>, ["~> 2.3.11"])
|
87
|
-
s.add_dependency(%q<
|
88
|
+
s.add_dependency(%q<riot>, [">= 0"])
|
89
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
88
90
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
89
91
|
s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
|
90
92
|
s.add_dependency(%q<rcov>, [">= 0"])
|
91
93
|
end
|
92
94
|
else
|
93
95
|
s.add_dependency(%q<activesupport>, ["~> 2.3.11"])
|
94
|
-
s.add_dependency(%q<
|
96
|
+
s.add_dependency(%q<riot>, [">= 0"])
|
97
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
95
98
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
96
99
|
s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
|
97
100
|
s.add_dependency(%q<rcov>, [">= 0"])
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vibes-rubycas-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: -
|
4
|
+
hash: -3702664408
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 3
|
9
9
|
- 0
|
10
10
|
- alpha
|
11
|
-
|
11
|
+
- 2
|
12
|
+
version: 2.3.0.alpha2
|
12
13
|
platform: ruby
|
13
14
|
authors:
|
14
15
|
- Matt Campbell
|
@@ -19,7 +20,7 @@ autorequire:
|
|
19
20
|
bindir: bin
|
20
21
|
cert_chain: []
|
21
22
|
|
22
|
-
date: 2011-06-
|
23
|
+
date: 2011-06-11 00:00:00 -05:00
|
23
24
|
default_executable:
|
24
25
|
dependencies:
|
25
26
|
- !ruby/object:Gem::Dependency
|
@@ -49,12 +50,26 @@ dependencies:
|
|
49
50
|
segments:
|
50
51
|
- 0
|
51
52
|
version: "0"
|
52
|
-
name:
|
53
|
+
name: riot
|
53
54
|
version_requirements: *id002
|
54
55
|
prerelease: false
|
55
56
|
- !ruby/object:Gem::Dependency
|
56
57
|
type: :development
|
57
58
|
requirement: &id003 !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 3
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
name: rr
|
68
|
+
version_requirements: *id003
|
69
|
+
prerelease: false
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
type: :development
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
58
73
|
none: false
|
59
74
|
requirements:
|
60
75
|
- - ~>
|
@@ -66,11 +81,11 @@ dependencies:
|
|
66
81
|
- 0
|
67
82
|
version: 1.0.0
|
68
83
|
name: bundler
|
69
|
-
version_requirements: *
|
84
|
+
version_requirements: *id004
|
70
85
|
prerelease: false
|
71
86
|
- !ruby/object:Gem::Dependency
|
72
87
|
type: :development
|
73
|
-
requirement: &
|
88
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
74
89
|
none: false
|
75
90
|
requirements:
|
76
91
|
- - ~>
|
@@ -82,11 +97,11 @@ dependencies:
|
|
82
97
|
- 2
|
83
98
|
version: 1.6.2
|
84
99
|
name: jeweler
|
85
|
-
version_requirements: *
|
100
|
+
version_requirements: *id005
|
86
101
|
prerelease: false
|
87
102
|
- !ruby/object:Gem::Dependency
|
88
103
|
type: :development
|
89
|
-
requirement: &
|
104
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
90
105
|
none: false
|
91
106
|
requirements:
|
92
107
|
- - ">="
|
@@ -96,7 +111,7 @@ dependencies:
|
|
96
111
|
- 0
|
97
112
|
version: "0"
|
98
113
|
name: rcov
|
99
|
-
version_requirements: *
|
114
|
+
version_requirements: *id006
|
100
115
|
prerelease: false
|
101
116
|
description: We've taken the rubycas-client and added some enterprisey features and improved compatibility with JASIG's CAS server
|
102
117
|
email:
|
@@ -109,7 +124,6 @@ extra_rdoc_files:
|
|
109
124
|
- README.rdoc
|
110
125
|
files:
|
111
126
|
- .rvmrc
|
112
|
-
- .source_index
|
113
127
|
- CHANGELOG.txt
|
114
128
|
- Gemfile
|
115
129
|
- Gemfile.lock
|
@@ -149,13 +163,14 @@ files:
|
|
149
163
|
- examples/rails/script/server
|
150
164
|
- lib/casclient.rb
|
151
165
|
- lib/casclient/client.rb
|
152
|
-
- lib/casclient/frameworks/merb/filter.rb
|
153
|
-
- lib/casclient/frameworks/merb/strategy.rb
|
154
166
|
- lib/casclient/frameworks/rails/cas_proxy_callback_controller.rb
|
155
167
|
- lib/casclient/frameworks/rails/filter.rb
|
156
168
|
- lib/casclient/responses.rb
|
157
169
|
- lib/casclient/tickets.rb
|
170
|
+
- lib/casclient/tickets/storage.rb
|
158
171
|
- lib/vibes-rubycas-client.rb
|
172
|
+
- test/teststrap.rb
|
173
|
+
- test/units/casclient/frameworks/rails/filter_test.rb
|
159
174
|
- vibes-rubycas-client.gemspec
|
160
175
|
has_rdoc: true
|
161
176
|
homepage: http://github.com/vibes/rubycas-client
|
data/.source_index
DELETED
Binary file
|
@@ -1,105 +0,0 @@
|
|
1
|
-
module CASClient
|
2
|
-
module Frameworks
|
3
|
-
module Merb
|
4
|
-
module Filter
|
5
|
-
attr_reader :client
|
6
|
-
|
7
|
-
def cas_filter
|
8
|
-
@client ||= CASClient::Client.new(config)
|
9
|
-
|
10
|
-
service_ticket = read_ticket(self)
|
11
|
-
|
12
|
-
cas_login_url = client.add_service_to_login_url(read_service_url(self))
|
13
|
-
|
14
|
-
last_service_ticket = session[:cas_last_valid_ticket]
|
15
|
-
if (service_ticket && last_service_ticket &&
|
16
|
-
last_service_ticket.ticket == service_ticket.ticket &&
|
17
|
-
last_service_ticket.service == service_ticket.service)
|
18
|
-
|
19
|
-
# warn() rather than info() because we really shouldn't be re-validating the same ticket.
|
20
|
-
# The only time when this is acceptable is if the user manually does a refresh and the ticket
|
21
|
-
# happens to be in the URL.
|
22
|
-
log.warn("Reusing previously validated ticket since the new ticket and service are the same.")
|
23
|
-
service_ticket = last_service_ticket
|
24
|
-
elsif last_service_ticket &&
|
25
|
-
!config[:authenticate_on_every_request] &&
|
26
|
-
session[client.username_session_key]
|
27
|
-
# Re-use the previous ticket if the user already has a local CAS session (i.e. if they were already
|
28
|
-
# previously authenticated for this service). This is to prevent redirection to the CAS server on every
|
29
|
-
# request.
|
30
|
-
# This behaviour can be disabled (so that every request is routed through the CAS server) by setting
|
31
|
-
# the :authenticate_on_every_request config option to false.
|
32
|
-
log.debug "Existing local CAS session detected for #{session[client.username_session_key].inspect}. "+
|
33
|
-
"Previous ticket #{last_service_ticket.ticket.inspect} will be re-used."
|
34
|
-
service_ticket = last_service_ticket
|
35
|
-
end
|
36
|
-
|
37
|
-
if service_ticket
|
38
|
-
client.validate_service_ticket(service_ticket) unless service_ticket.has_been_validated?
|
39
|
-
validation_response = service_ticket.response
|
40
|
-
|
41
|
-
if service_ticket.is_valid?
|
42
|
-
log.info("Ticket #{service_ticket.inspect} for service #{service_ticket.service.inspect} " +
|
43
|
-
"belonging to user #{validation_response.user.inspect} is VALID.")
|
44
|
-
|
45
|
-
session[client.username_session_key] = validation_response.user
|
46
|
-
session[client.extra_attributes_session_key] = validation_response.extra_attributes
|
47
|
-
|
48
|
-
# Store the ticket in the session to avoid re-validating the same service
|
49
|
-
# ticket with the CAS server.
|
50
|
-
session[:cas_last_valid_ticket] = service_ticket
|
51
|
-
return true
|
52
|
-
else
|
53
|
-
log.warn("Ticket #{service_ticket.ticket.inspect} failed validation -- " +
|
54
|
-
"#{validation_response.failure_code}: #{validation_response.failure_message}")
|
55
|
-
redirect cas_login_url
|
56
|
-
throw :halt
|
57
|
-
end
|
58
|
-
else
|
59
|
-
log.warn("No ticket -- redirecting to #{cas_login_url}")
|
60
|
-
redirect cas_login_url
|
61
|
-
throw :halt
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
# Copied from Rails adapter
|
67
|
-
def read_ticket(controller)
|
68
|
-
ticket = controller.params[:ticket]
|
69
|
-
|
70
|
-
return nil unless ticket
|
71
|
-
|
72
|
-
log.debug("Request contains ticket #{ticket.inspect}.")
|
73
|
-
|
74
|
-
if ticket =~ /^PT-/
|
75
|
-
ProxyTicket.new(ticket, read_service_url(controller), controller.params[:renew])
|
76
|
-
else
|
77
|
-
ServiceTicket.new(ticket, read_service_url(controller), controller.params[:renew])
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# Also copied from Rails adapter
|
82
|
-
def read_service_url(controller)
|
83
|
-
if config[:service_url]
|
84
|
-
log.debug("Using explicitly set service url: #{config[:service_url]}")
|
85
|
-
return config[:service_url]
|
86
|
-
end
|
87
|
-
|
88
|
-
params = controller.params.dup
|
89
|
-
params.delete(:ticket)
|
90
|
-
service_url = request.protocol + '://' + request.host / controller.url(params.to_hash.symbolize_keys!)
|
91
|
-
log.debug("Guessed service url: #{service_url.inspect}")
|
92
|
-
return service_url
|
93
|
-
end
|
94
|
-
|
95
|
-
def log
|
96
|
-
::Merb.logger
|
97
|
-
end
|
98
|
-
|
99
|
-
def config
|
100
|
-
::Merb::Plugins.config[:"rubycas-client"]
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
@@ -1,110 +0,0 @@
|
|
1
|
-
# The 'cas' strategy attempts to login users based on the CAS protocol
|
2
|
-
# http://www.ja-sig.org/products/cas/overview/background/index.html
|
3
|
-
#
|
4
|
-
# install the rubycas-client gem
|
5
|
-
# http://rubyforge.org/projects/rubycas-client/
|
6
|
-
#
|
7
|
-
require 'casclient'
|
8
|
-
class Merb::Authentication
|
9
|
-
module Strategies
|
10
|
-
class CAS < Merb::Authentication::Strategy
|
11
|
-
|
12
|
-
include CASClient
|
13
|
-
|
14
|
-
def run!
|
15
|
-
@client ||= Client.new(config)
|
16
|
-
|
17
|
-
service_ticket = read_ticket
|
18
|
-
|
19
|
-
cas_login_url = @client.add_service_to_login_url(service_url)
|
20
|
-
|
21
|
-
last_service_ticket = session[:cas_last_valid_ticket]
|
22
|
-
if (service_ticket && last_service_ticket &&
|
23
|
-
last_service_ticket.ticket == service_ticket.ticket &&
|
24
|
-
last_service_ticket.service == service_ticket.service)
|
25
|
-
|
26
|
-
# warn() rather than info() because we really shouldn't be re-validating the same ticket.
|
27
|
-
# The only time when this is acceptable is if the user manually does a refresh and the ticket
|
28
|
-
# happens to be in the URL.
|
29
|
-
log.warn("Reusing previously validated ticket since the new ticket and service are the same.")
|
30
|
-
service_ticket = last_service_ticket
|
31
|
-
elsif last_service_ticket &&
|
32
|
-
!config[:authenticate_on_every_request] &&
|
33
|
-
session[@client.username_session_key]
|
34
|
-
# Re-use the previous ticket if the user already has a local CAS session (i.e. if they were already
|
35
|
-
# previously authenticated for this service). This is to prevent redirection to the CAS server on every
|
36
|
-
# request.
|
37
|
-
# This behaviour can be disabled (so that every request is routed through the CAS server) by setting
|
38
|
-
# the :authenticate_on_every_request config option to false.
|
39
|
-
log.debug "Existing local CAS session detected for #{session[@client.username_session_key].inspect}. "+
|
40
|
-
"Previous ticket #{last_service_ticket.ticket.inspect} will be re-used."
|
41
|
-
service_ticket = last_service_ticket
|
42
|
-
end
|
43
|
-
|
44
|
-
if service_ticket
|
45
|
-
@client.validate_service_ticket(service_ticket) unless service_ticket.has_been_validated?
|
46
|
-
validation_response = service_ticket.response
|
47
|
-
|
48
|
-
if service_ticket.is_valid?
|
49
|
-
log.info("Ticket #{service_ticket.inspect} for service #{service_ticket.service.inspect} " +
|
50
|
-
"belonging to user #{validation_response.user.inspect} is VALID.")
|
51
|
-
|
52
|
-
session[@client.username_session_key] = validation_response.user
|
53
|
-
session[@client.extra_attributes_session_key] = validation_response.extra_attributes
|
54
|
-
|
55
|
-
# Store the ticket in the session to avoid re-validating the same service
|
56
|
-
# ticket with the CAS server.
|
57
|
-
session[:cas_last_valid_ticket] = service_ticket
|
58
|
-
return true
|
59
|
-
else
|
60
|
-
log.warn("Ticket #{service_ticket.ticket.inspect} failed validation -- " +
|
61
|
-
"#{validation_response.failure_code}: #{validation_response.failure_message}")
|
62
|
-
redirect!(cas_login_url)
|
63
|
-
return false
|
64
|
-
end
|
65
|
-
else
|
66
|
-
log.warn("No ticket -- redirecting to #{cas_login_url}")
|
67
|
-
redirect!(cas_login_url)
|
68
|
-
return false
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def read_ticket
|
73
|
-
ticket = request.params[:ticket]
|
74
|
-
|
75
|
-
return nil unless ticket
|
76
|
-
|
77
|
-
log.debug("Request contains ticket #{ticket.inspect}.")
|
78
|
-
|
79
|
-
if ticket =~ /^PT-/
|
80
|
-
ProxyTicket.new(ticket, service_url, request.params[:renew])
|
81
|
-
else
|
82
|
-
ServiceTicket.new(ticket, service_url, request.params[:renew])
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def service_url
|
87
|
-
if config[:service_url]
|
88
|
-
log.debug("Using explicitly set service url: #{config[:service_url]}")
|
89
|
-
return config[:service_url]
|
90
|
-
end
|
91
|
-
|
92
|
-
params = request.params.dup
|
93
|
-
params.delete(:ticket)
|
94
|
-
service_url = "#{request.protocol}://#{request.host}" + request.path
|
95
|
-
log.debug("Guessed service url: #{service_url.inspect}")
|
96
|
-
return service_url
|
97
|
-
end
|
98
|
-
|
99
|
-
def config
|
100
|
-
::Merb::Plugins.config[:"rubycas-client"]
|
101
|
-
end
|
102
|
-
|
103
|
-
def log
|
104
|
-
::Merb.logger
|
105
|
-
end
|
106
|
-
|
107
|
-
end # CAS
|
108
|
-
end # Strategies
|
109
|
-
end
|
110
|
-
|