rubycas-client-rails 0.1.0

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.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 University of Toronto
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,43 @@
1
+ RubyCAS-Client-Rails
2
+ ====================
3
+
4
+ Rails plugin for using the RubyCAS-Client as a controller filter.
5
+
6
+ This uses a Railtie, so will only work with Rails 3.0 and up.
7
+
8
+
9
+ Example
10
+ =======
11
+
12
+ First, install the `rubycas-client` gem:
13
+
14
+ gem install rubycas-client
15
+
16
+ Next, clone the `rubycas-client-rails` code from Github (this will eventually
17
+ be released as a gem too).
18
+
19
+ Now add both to your `Gemfile`:
20
+
21
+ gem 'rubycas-client'
22
+ gem 'rubycas-client-rails', :path => '/path/where/you/cloned/rubycas-client-rails'
23
+
24
+ In your `application.rb` add:
25
+
26
+ config.rubycas.cas_base_url = 'https://cas.example.com/'
27
+
28
+ Finally, to enable the CAS filter for a controller:
29
+
30
+ class MyController < ApplicationController
31
+
32
+ before_filter RubyCAS::Filter
33
+
34
+ Many other configuration options are available. For example you can instruct
35
+ the client to log its actions to the default Rails logger using:
36
+
37
+ config.rubycas.logger = Rails.logger
38
+
39
+ See the (outdated) documentation at [http://rubycas-client.rubyforge.org/](http://rubycas-client.rubyforge.org/)
40
+ for a full list of config options.
41
+
42
+
43
+ Copyright (c) 2011 University of Toronto, released under the MIT license
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the rubycas_client_rails plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the rubycas_client_rails plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'RubycasClientRails'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ # Include hook code here
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,424 @@
1
+
2
+ require 'casclient'
3
+
4
+ module RubyCAS
5
+ class Railtie < Rails::Railtie
6
+ config.rubycas = ActiveSupport::OrderedOptions.new
7
+
8
+ initializer 'rubycas.initialize' do |app|
9
+ RubyCAS::Filter.setup(config.rubycas)
10
+ end
11
+ end
12
+
13
+ class Filter
14
+ cattr_reader :config, :log, :client
15
+
16
+ # These are initialized when you call setup.
17
+ @@client = nil
18
+ @@log = nil
19
+ @@fake_user = nil
20
+ @@fake_extra_attributes = nil
21
+
22
+ class << self
23
+ def setup(config)
24
+ @@config = config
25
+ @@config[:logger] = Rails.logger unless @@config[:logger]
26
+ @@client = CASClient::Client.new(@@config)
27
+ @@log = @@client.log
28
+ end
29
+
30
+ def filter(controller)
31
+ raise "Cannot use the CASClient filter because it has not yet been configured." if config.nil?
32
+
33
+ if @@fake_user
34
+ controller.session[client.username_session_key] = @@fake_user
35
+ controller.session[:casfilteruser] = @@fake_user
36
+ controller.session[client.extra_attributes_session_key] = @@fake_extra_attributes
37
+ return true
38
+ end
39
+
40
+
41
+ last_st = controller.session[:cas_last_valid_ticket]
42
+
43
+ if single_sign_out(controller)
44
+ controller.send(:render, :text => "CAS Single-Sign-Out request intercepted.")
45
+ return false
46
+ end
47
+
48
+ st = read_ticket(controller)
49
+
50
+ is_new_session = true
51
+
52
+ if st && last_st &&
53
+ last_st.ticket == st.ticket &&
54
+ last_st.service == st.service
55
+ # warn() rather than info() because we really shouldn't be re-validating the same ticket.
56
+ # The only situation where this is acceptable is if the user manually does a refresh and
57
+ # the same ticket happens to be in the URL.
58
+ log.warn("Re-using previously validated ticket since the ticket id and service are the same.")
59
+ st = last_st
60
+ is_new_session = false
61
+ elsif last_st &&
62
+ !config[:authenticate_on_every_request] &&
63
+ controller.session[client.username_session_key]
64
+ # Re-use the previous ticket if the user already has a local CAS session (i.e. if they were already
65
+ # previously authenticated for this service). This is to prevent redirection to the CAS server on every
66
+ # request.
67
+ #
68
+ # This behaviour can be disabled (so that every request is routed through the CAS server) by setting
69
+ # the :authenticate_on_every_request config option to true. However, this is not desirable since
70
+ # it will almost certainly break POST request, AJAX calls, etc.
71
+ log.debug "Existing local CAS session detected for #{controller.session[client.username_session_key].inspect}. "+
72
+ "Previous ticket #{last_st.ticket.inspect} will be re-used."
73
+ st = last_st
74
+ is_new_session = false
75
+ end
76
+
77
+ if st
78
+ client.validate_service_ticket(st) unless st.has_been_validated?
79
+ vr = st.response
80
+
81
+ if st.is_valid?
82
+ if is_new_session
83
+ log.info("Ticket #{st.ticket.inspect} for service #{st.service.inspect} belonging to user #{vr.user.inspect} is VALID.")
84
+ controller.session[client.username_session_key] = vr.user.dup
85
+ controller.session[client.extra_attributes_session_key] = HashWithIndifferentAccess.new(vr.extra_attributes) if vr.extra_attributes
86
+
87
+ if vr.extra_attributes
88
+ log.debug("Extra user attributes provided along with ticket #{st.ticket.inspect}: #{vr.extra_attributes.inspect}.")
89
+ end
90
+
91
+ # RubyCAS-Client 1.x used :casfilteruser as it's username session key,
92
+ # so we need to set this here to ensure compatibility with configurations
93
+ # built around the old client.
94
+ controller.session[:casfilteruser] = vr.user
95
+
96
+ if config[:enable_single_sign_out]
97
+ f = store_service_session_lookup(st, controller.request.session_options[:id] || controller.session.session_id)
98
+ log.debug("Wrote service session lookup file to #{f.inspect} with session id #{controller.request.session_options[:id] || controller.session.session_id.inspect}.")
99
+ end
100
+ end
101
+
102
+ # Store the ticket in the session to avoid re-validating the same service
103
+ # ticket with the CAS server.
104
+ controller.session[:cas_last_valid_ticket] = st
105
+
106
+ if vr.pgt_iou
107
+ unless controller.session[:cas_pgt] && controller.session[:cas_pgt].ticket && controller.session[:cas_pgt].iou == vr.pgt_iou
108
+ log.info("Receipt has a proxy-granting ticket IOU. Attempting to retrieve the proxy-granting ticket...")
109
+ pgt = client.retrieve_proxy_granting_ticket(vr.pgt_iou)
110
+
111
+ if pgt
112
+ log.debug("Got PGT #{pgt.ticket.inspect} for PGT IOU #{pgt.iou.inspect}. This will be stored in the session.")
113
+ controller.session[:cas_pgt] = pgt
114
+ # For backwards compatibility with RubyCAS-Client 1.x configurations...
115
+ controller.session[:casfilterpgt] = pgt
116
+ else
117
+ log.error("Failed to retrieve a PGT for PGT IOU #{vr.pgt_iou}!")
118
+ end
119
+ else
120
+ log.info("PGT is present in session and PGT IOU #{vr.pgt_iou} matches the saved PGT IOU. Not retrieving new PGT.")
121
+ end
122
+
123
+ end
124
+
125
+ return true
126
+ else
127
+ log.warn("Ticket #{st.ticket.inspect} failed validation -- #{vr.failure_code}: #{vr.failure_message}")
128
+ unauthorized!(controller, vr)
129
+ return false
130
+ end
131
+ else # no service ticket was present in the request
132
+ if returning_from_gateway?(controller)
133
+ log.info "Returning from CAS gateway without authentication."
134
+
135
+ # unset, to allow for the next request to be authenticated if necessary
136
+ controller.session[:cas_sent_to_gateway] = false
137
+
138
+ if use_gatewaying?
139
+ log.info "This CAS client is configured to use gatewaying, so we will permit the user to continue without authentication."
140
+ controller.session[client.username_session_key] = nil
141
+ return true
142
+ else
143
+ log.warn "The CAS client is NOT configured to allow gatewaying, yet this request was gatewayed. Something is not right!"
144
+ end
145
+ end
146
+
147
+ unauthorized!(controller)
148
+ return false
149
+ end
150
+ rescue OpenSSL::SSL::SSLError
151
+ log.error("SSL Error: hostname was not match with the server certificate. You can try to disable the ssl verification with a :force_ssl_verification => false in your configurations file.")
152
+ unauthorized!(controller)
153
+ return false
154
+ end
155
+
156
+ # used to allow faking for testing
157
+ # with cucumber and other tools.
158
+ # use like
159
+ # CASClient::Frameworks::Rails::Filter.fake("homer")
160
+ # you can also fake extra attributes by including a second parameter
161
+ # CASClient::Frameworks::Rails::Filter.fake("homer", {:roles => ['dad', 'husband']})
162
+ def fake(username, extra_attributes = nil)
163
+ @@fake_user = username
164
+ @@fake_extra_attributes = extra_attributes
165
+ end
166
+
167
+ def use_gatewaying?
168
+ @@config[:use_gatewaying]
169
+ end
170
+
171
+ # Returns the login URL for the current controller.
172
+ # Useful when you want to provide a "Login" link in a GatewayFilter'ed
173
+ # action.
174
+ def login_url(controller)
175
+ service_url = read_service_url(controller)
176
+ url = client.add_service_to_login_url(service_url)
177
+ log.debug("Generated login url: #{url}")
178
+ return url
179
+ end
180
+
181
+ # allow controllers to reuse the existing config to auto-login to
182
+ # the service
183
+ #
184
+ # Use this from within a controller. Pass the controller, the
185
+ # login-credentials and the path that you want the user
186
+ # resdirected to on success.
187
+ #
188
+ # When writing a login-action you must check the return-value of
189
+ # the response to see if it failed!
190
+ #
191
+ # If it worked - you need to redirect the user to the service -
192
+ # path, because that has the ticket that will *actually* log them
193
+ # into your system
194
+ #
195
+ # example:
196
+ # def autologin
197
+ # resp = CASClient::Frameworks::Rails::Filter.login_to_service(self, credentials, dashboard_url)
198
+ # if resp.is_faiulure?
199
+ # flash[:error] = 'Login failed'
200
+ # render :action => 'login'
201
+ # else
202
+ # return redirect_to(@resp.service_redirect_url)
203
+ # end
204
+ # end
205
+ def login_to_service(controller, credentials, return_path)
206
+ resp = @@client.login_to_service(credentials, return_path)
207
+ if resp.is_failure?
208
+ log.info("Validation failed for service #{return_path.inspect} reason: '#{resp.failure_message}'")
209
+ else
210
+ log.info("Ticket #{resp.ticket.inspect} for service #{return_path.inspect} is VALID.")
211
+ end
212
+
213
+ resp
214
+ end
215
+
216
+ # Clears the given controller's local Rails session, does some local
217
+ # CAS cleanup, and redirects to the CAS logout page. Additionally, the
218
+ # <tt>request.referer</tt> value from the <tt>controller</tt> instance
219
+ # is passed to the CAS server as a 'destination' parameter. This
220
+ # allows RubyCAS server to provide a follow-up login page allowing
221
+ # the user to log back in to the service they just logged out from
222
+ # using a different username and password. Other CAS server
223
+ # implemenations may use this 'destination' parameter in different
224
+ # ways.
225
+ # If given, the optional <tt>service</tt> URL overrides
226
+ # <tt>request.referer</tt>.
227
+ def logout(controller, service = nil)
228
+ referer = service || controller.request.referer
229
+ st = controller.session[:cas_last_valid_ticket]
230
+ delete_service_session_lookup(st) if st
231
+ controller.send(:reset_session)
232
+ controller.send(:redirect_to, client.logout_url(referer))
233
+ end
234
+
235
+ def unauthorized!(controller, vr = nil)
236
+ if controller.params[:format] == "xml"
237
+ if vr
238
+ controller.send(:render, :xml => "<errors><error>#{vr.failure_message}</error></errors>", :status => 401)
239
+ else
240
+ controller.send(:head, 401)
241
+ end
242
+ else
243
+ redirect_to_cas_for_authentication(controller)
244
+ end
245
+ end
246
+
247
+ def redirect_to_cas_for_authentication(controller)
248
+ redirect_url = login_url(controller)
249
+
250
+ if use_gatewaying?
251
+ controller.session[:cas_sent_to_gateway] = true
252
+ redirect_url << "&gateway=true"
253
+ else
254
+ controller.session[:cas_sent_to_gateway] = false
255
+ end
256
+
257
+ if controller.session[:previous_redirect_to_cas] &&
258
+ controller.session[:previous_redirect_to_cas] > (Time.now - 1.second)
259
+ log.warn("Previous redirect to the CAS server was less than a second ago. The client at #{controller.request.remote_ip.inspect} may be stuck in a redirection loop!")
260
+ controller.session[:cas_validation_retry_count] ||= 0
261
+
262
+ if controller.session[:cas_validation_retry_count] > 3
263
+ log.error("Redirection loop intercepted. Client at #{controller.request.remote_ip.inspect} will be redirected back to login page and forced to renew authentication.")
264
+ redirect_url += "&renew=1&redirection_loop_intercepted=1"
265
+ end
266
+
267
+ controller.session[:cas_validation_retry_count] += 1
268
+ else
269
+ controller.session[:cas_validation_retry_count] = 0
270
+ end
271
+ controller.session[:previous_redirect_to_cas] = Time.now
272
+
273
+ log.debug("Redirecting to #{redirect_url.inspect}")
274
+ controller.send(:redirect_to, redirect_url)
275
+ end
276
+
277
+ private
278
+ def single_sign_out(controller)
279
+
280
+ # Avoid calling raw_post (which may consume the post body) if
281
+ # this seems to be a file upload
282
+ if content_type = controller.request.headers["CONTENT_TYPE"] &&
283
+ content_type =~ %r{^multipart/}
284
+ return false
285
+ end
286
+
287
+ if controller.request.post? &&
288
+ controller.params['logoutRequest'] &&
289
+ controller.params['logoutRequest'] =~
290
+ %r{^<samlp:LogoutRequest.*?<samlp:SessionIndex>(.*)</samlp:SessionIndex>}m
291
+ # TODO: Maybe check that the request came from the registered CAS server? Although this might be
292
+ # pointless since it's easily spoofable...
293
+ si = $~[1]
294
+
295
+ unless config[:enable_single_sign_out]
296
+ log.warn "Ignoring single-sign-out request for CAS session #{si.inspect} because ssout functionality is not enabled (see the :enable_single_sign_out config option)."
297
+ return false
298
+ end
299
+
300
+ log.debug "Intercepted single-sign-out request for CAS session #{si.inspect}."
301
+
302
+ begin
303
+ required_sess_store = ActiveRecord::SessionStore
304
+ current_sess_store = ActionController::Base.session_store
305
+ rescue NameError
306
+ # for older versions of Rails (prior to 2.3)
307
+ required_sess_store = CGI::Session::ActiveRecordStore
308
+ current_sess_store = ActionController::Base.session_options[:database_manager]
309
+ end
310
+
311
+
312
+ if current_sess_store == required_sess_store
313
+ session_id = read_service_session_lookup(si)
314
+
315
+ if session_id
316
+ session = current_sess_store::Session.find_by_session_id(session_id)
317
+ if session
318
+ session.destroy
319
+ log.debug("Destroyed #{session.inspect} for session #{session_id.inspect} corresponding to service ticket #{si.inspect}.")
320
+ else
321
+ log.debug("Data for session #{session_id.inspect} was not found. It may have already been cleared by a local CAS logout request.")
322
+ end
323
+
324
+ log.info("Single-sign-out for session #{session_id.inspect} completed successfuly.")
325
+ else
326
+ log.warn("Couldn't destroy session with SessionIndex #{si} because no corresponding session id could be looked up.")
327
+ end
328
+ else
329
+ log.error "Cannot process logout request because this Rails application's session store is "+
330
+ " #{current_sess_store.name.inspect}. Single Sign-Out only works with the "+
331
+ " #{required_sess_store.name.inspect} session store."
332
+ end
333
+
334
+ # Return true to indicate that a single-sign-out request was detected
335
+ # and that further processing of the request is unnecessary.
336
+ return true
337
+ end
338
+
339
+ # This is not a single-sign-out request.
340
+ return false
341
+ end
342
+
343
+ def read_ticket(controller)
344
+ # Note that we are now deleting the 'ticket' and 'renew' parameters, since they really
345
+ # have no business getting passed on to the controller action.
346
+
347
+ ticket = controller.params.delete(:ticket)
348
+
349
+ return nil unless ticket
350
+
351
+ log.debug("Request contains ticket #{ticket.inspect}.")
352
+
353
+ if ticket =~ /^PT-/
354
+ CASClient::ProxyTicket.new(ticket, read_service_url(controller), controller.params.delete(:renew))
355
+ else
356
+ CASClient::ServiceTicket.new(ticket, read_service_url(controller), controller.params.delete(:renew))
357
+ end
358
+ end
359
+
360
+ def returning_from_gateway?(controller)
361
+ controller.session[:cas_sent_to_gateway]
362
+ end
363
+
364
+ def read_service_url(controller)
365
+ if config[:service_url]
366
+ log.debug("Using explicitly set service url: #{config[:service_url]}")
367
+ return config[:service_url]
368
+ end
369
+
370
+ params = controller.params.dup
371
+ params.delete(:ticket)
372
+ service_url = controller.url_for(params)
373
+ log.debug("Guessed service url: #{service_url.inspect}")
374
+ return service_url
375
+ end
376
+
377
+ # Creates a file in tmp/sessions linking a SessionTicket
378
+ # with the local Rails session id. The file is named
379
+ # cas_sess.<session ticket> and its text contents is the corresponding
380
+ # Rails session id.
381
+ # Returns the filename of the lookup file created.
382
+ def store_service_session_lookup(st, sid)
383
+ st = st.ticket if st.kind_of? CASClient::ServiceTicket
384
+ f = File.new(filename_of_service_session_lookup(st), 'w')
385
+ f.write(sid)
386
+ f.close
387
+ return f.path
388
+ end
389
+
390
+ # Returns the local Rails session ID corresponding to the given
391
+ # ServiceTicket. This is done by reading the contents of the
392
+ # cas_sess.<session ticket> file created in a prior call to
393
+ # #store_service_session_lookup.
394
+ def read_service_session_lookup(st)
395
+ st = st.ticket if st.kind_of? CASClient::ServiceTicket
396
+ ssl_filename = filename_of_service_session_lookup(st)
397
+ return File.exists?(ssl_filename) && IO.read(ssl_filename)
398
+ end
399
+
400
+ # Removes a stored relationship between a ServiceTicket and a local
401
+ # Rails session id. This should be called when the session is being
402
+ # closed.
403
+ #
404
+ # See #store_service_session_lookup.
405
+ def delete_service_session_lookup(st)
406
+ st = st.ticket if st.kind_of? CASClient::ServiceTicket
407
+ ssl_filename = filename_of_service_session_lookup(st)
408
+ File.delete(ssl_filename) if File.exists?(ssl_filename)
409
+ end
410
+
411
+ # Returns the path and filename of the service session lookup file.
412
+ def filename_of_service_session_lookup(st)
413
+ st = st.ticket if st.kind_of? CASClient::ServiceTicket
414
+ return "#{Rails.root}/tmp/sessions/cas_sess.#{st}"
415
+ end
416
+ end
417
+
418
+ class GatewayFilter < Filter
419
+ def self.use_gatewaying?
420
+ return true unless @@config[:use_gatewaying] == false
421
+ end
422
+ end
423
+ end
424
+ end
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{rubycas-client-rails}
3
+ s.version = "0.1.0"
4
+
5
+ s.authors = ["Matt Zukowski"]
6
+ s.date = %q{2011-08-13}
7
+ s.description = %q{Rails plugin for using the RubyCAS-Client as a controller filter.}
8
+ s.summary = %q{RubyCAS-Client Railtie for Rails 3.0.}
9
+ s.email = %q{matt dot zukowski at utoronto dot ca}
10
+ s.files = `git ls-files`.split("\n")
11
+ s.homepage = %q{http://rubycas-client.rubyforge.org}
12
+ s.rdoc_options = ["--main", "README.txt"]
13
+ s.require_paths = ["lib"]
14
+ s.rubyforge_project = %q{rubycas-client}
15
+
16
+ s.add_dependency('rails', '>= 3.0.0')
17
+ s.add_dependency('rubycas-client', '>= 2.2.0')
18
+ end
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubycas-client-rails
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Matt Zukowski
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-13 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rails
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 3
32
+ - 0
33
+ - 0
34
+ version: 3.0.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rubycas-client
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 7
46
+ segments:
47
+ - 2
48
+ - 2
49
+ - 0
50
+ version: 2.2.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ description: Rails plugin for using the RubyCAS-Client as a controller filter.
54
+ email: matt dot zukowski at utoronto dot ca
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ extra_rdoc_files: []
60
+
61
+ files:
62
+ - MIT-LICENSE
63
+ - README.markdown
64
+ - Rakefile
65
+ - init.rb
66
+ - install.rb
67
+ - lib/rubycas-client-rails.rb
68
+ - rubycas-client-rails.gemspec
69
+ - uninstall.rb
70
+ has_rdoc: true
71
+ homepage: http://rubycas-client.rubyforge.org
72
+ licenses: []
73
+
74
+ post_install_message:
75
+ rdoc_options:
76
+ - --main
77
+ - README.txt
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 3
95
+ segments:
96
+ - 0
97
+ version: "0"
98
+ requirements: []
99
+
100
+ rubyforge_project: rubycas-client
101
+ rubygems_version: 1.4.2
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: RubyCAS-Client Railtie for Rails 3.0.
105
+ test_files: []
106
+