rack-cas-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+ gem 'rubycas-client', "~> 2.2.1"
3
+ gem 'activesupport', '>= 2'
4
+
5
+ # Include everything needed to run rake, tests, features, etc.
6
+ group :development do
7
+ gem "rspec", "~> 2.3.0"
8
+ gem "bundler", "~> 1.0.0"
9
+ gem "jeweler", "~> 1.6.4"
10
+ gem "rcov", ">= 0"
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,35 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.1.0)
5
+ multi_json (~> 1.0)
6
+ diff-lcs (1.1.3)
7
+ git (1.2.5)
8
+ jeweler (1.6.4)
9
+ bundler (~> 1.0)
10
+ git (>= 1.2.5)
11
+ rake
12
+ multi_json (1.0.3)
13
+ rake (0.9.2)
14
+ rcov (0.9.10)
15
+ rspec (2.3.0)
16
+ rspec-core (~> 2.3.0)
17
+ rspec-expectations (~> 2.3.0)
18
+ rspec-mocks (~> 2.3.0)
19
+ rspec-core (2.3.1)
20
+ rspec-expectations (2.3.0)
21
+ diff-lcs (~> 1.1.2)
22
+ rspec-mocks (2.3.0)
23
+ rubycas-client (2.2.1)
24
+ activesupport
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ activesupport (>= 2)
31
+ bundler (~> 1.0.0)
32
+ jeweler (~> 1.6.4)
33
+ rcov
34
+ rspec (~> 2.3.0)
35
+ rubycas-client (~> 2.2.1)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Jerome Riga
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.
data/README.rdoc ADDED
@@ -0,0 +1,58 @@
1
+ = rack-cas-client
2
+ This middleware will redirect all unauthenticated requests to the specified cas server. After a successful authentication the cas server will redirect the user back to your application with a service ticket. This ticket is used to check the validity of the user's session on subsequent calls.
3
+
4
+ By default the user's login username is store in the session (session['cas']['user']). If your cas server is configured to send extra attributes (http://code.google.com/p/rubycas-server/wiki/HowToSendExtraUserAttributes), then they are accessible at (session['cas']['user_extra']).
5
+
6
+ To log user out, send a DELETE to /logout and the middleware will delete the user's session.
7
+
8
+ This middleware was inspired by the 'rubycas-client-rails' gem
9
+
10
+ == Basic Usage
11
+ require 'rack/cas_client'
12
+
13
+ app = Rack::Builder.new {
14
+ use Rack::Session::Cookie
15
+ use Rack::Cas::Client, :cas_base_url => 'https://cas.myserver:443/', :session_dir => './tmp/sessions'
16
+ run lambda {|env|
17
+ request = Rack::Request.new(env)
18
+ [200, {'Content-Type' => 'text/html'}, ['<p>You are in '+"#{request.session['cas']['user']}"+' </p><p>Extra '+ "#{request.session['cas']['user_extra'].inspect}" +'</p><form action="/logout" method="post"><input type="hidden" name="_method" value="delete" /><input type="submit" value="out"/></form> ']]
19
+ }
20
+ }
21
+
22
+ run app
23
+
24
+ == Rails
25
+ In config/application.rb or in an environment file
26
+
27
+ config.middleware.insert_after ActionDispatch::Session::CookieStore, Rack::Cas::Client, :cas_base_url => 'https://cas.server:443/', :session_dir => './tmp/sessions'
28
+
29
+ == Sinatra
30
+ require 'sinatra/base'
31
+
32
+ class App < Sinatra::Base
33
+ use Rack::Session::Cookie
34
+ use Rack::Cas::Client, :cas_base_url => 'https://cas.myserver:443/', :session_dir => './tmp/sessions'
35
+ end
36
+
37
+ run App.new
38
+
39
+
40
+ == Todo
41
+ * Add tests
42
+ * cleanup code
43
+
44
+ == Contributing to rack-cas-client
45
+
46
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
47
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
48
+ * Fork the project
49
+ * Start a feature/bugfix branch
50
+ * Commit and push until you are happy with your contribution
51
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
52
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
53
+
54
+ == Copyright
55
+
56
+ Copyright (c) 2011 Jerome Riga. See LICENSE.txt for
57
+ further details.
58
+
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "rack-cas-client"
18
+ gem.homepage = "http://github.com/zemis/rack-cas-client"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Rack middleware for authentication with CAS server}
21
+ gem.description = %Q{Rack middleware that handles user authentication against a cas server}
22
+ gem.email = "jriga@zemis.co.uk"
23
+ gem.authors = ["Jerome Riga"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rake/rdoctask'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "rack-cas-client #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/config.ru ADDED
@@ -0,0 +1,14 @@
1
+ require 'rack'
2
+ require './lib/rack/cas_client.rb'
3
+
4
+
5
+ app = Rack::Builder.new {
6
+ use Rack::Session::Cookie
7
+ use Rack::Cas::Client, :cas_base_url => 'https://localhost:44300/', :session_dir => './tmp/sessions', :enable_single_sign_out => true
8
+ run lambda {|env|
9
+ request = Rack::Request.new(env)
10
+ [200, {'Content-Type' => 'text/html'}, ['<p>You are in '+"#{request.session['cas']['user']}"+' </p><p>Extra '+ "#{request.session['cas']['user_extra'].inspect}" +'</p><form action="/logout" method="post"><input type="hidden" name="_method" value="delete" /><input type="submit" value="out"/></form> ']]
11
+ }
12
+ }
13
+
14
+ run app
@@ -0,0 +1,437 @@
1
+ require 'logger'
2
+ require 'uri'
3
+ require 'casclient'
4
+ require 'ostruct'
5
+
6
+ # TODO: remove dependency on activesuppor/core_ext
7
+ # rubycas-client-2.2.1/lib/casclient/responses.rb:40 => String.blank?
8
+ # rubycas-client-2.2.1/lib/casclient/responses.rb:68 => Hash.from_xml(xml)
9
+ require 'active_support/core_ext'
10
+
11
+ module Rack
12
+ module Cas
13
+
14
+ module SessionStore
15
+ module FileSystem
16
+ # Creates a file in tmp/sessions linking a SessionTicket
17
+ # with the local Rails session id. The file is named
18
+ # cas_sess.<session ticket> and its text contents is the corresponding
19
+ # Rails session id.
20
+ # Returns the filename of the lookup file created.
21
+ def store_service_session_lookup(st, sid)
22
+ st = st.ticket if st.kind_of? CASClient::ServiceTicket
23
+ f = ::File.new(filename_of_service_session_lookup(st), 'w')
24
+ f.write(sid)
25
+ f.close
26
+ return f.path
27
+ end
28
+
29
+ # Returns the local Rails session ID corresponding to the given
30
+ # ServiceTicket. This is done by reading the contents of the
31
+ # cas_sess.<session ticket> file created in a prior call to
32
+ # #store_service_session_lookup.
33
+ def read_service_session_lookup(st)
34
+ st = st.ticket if st.kind_of? CASClient::ServiceTicket
35
+ ssl_filename = filename_of_service_session_lookup(st)
36
+ return ::File.exists?(ssl_filename) && IO.read(ssl_filename)
37
+ end
38
+
39
+ # Removes a stored relationship between a ServiceTicket and a local
40
+ # Rails session id. This should be called when the session is being
41
+ # closed.
42
+ #
43
+ # See #store_service_session_lookup.
44
+ def delete_service_session_lookup(st)
45
+ st = st.ticket if st.kind_of? CASClient::ServiceTicket
46
+ ssl_filename = filename_of_service_session_lookup(st)
47
+ ::File.delete(ssl_filename) if ::File.exists?(ssl_filename)
48
+ end
49
+
50
+ # Returns the path and filename of the service session lookup file.
51
+ def filename_of_service_session_lookup(st)
52
+ st = st.ticket if st.kind_of? CASClient::ServiceTicket
53
+ return "#{config[:session_dir]}/cas_sess.#{st}"
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ class Client < Struct.new :app, :options
60
+ include SessionStore::FileSystem
61
+ attr_reader :mem
62
+
63
+ def call(env)
64
+ if assets_request?(env); return app.call(env); end
65
+ if logout_options = logout_request?(env); return logout(*logout_options) end
66
+ if request = sso_request?(env); return single_sign_out(request) end
67
+ if valid_session_options = authenticated?(env); return valid_session(*valid_session_options) end
68
+ if xml_request?(env); return unauthorized_request end
69
+
70
+ redirect_to_cas_for_authentication(env)
71
+ end
72
+
73
+ protected
74
+ def client
75
+ @client ||= CASClient::Client.new(config)
76
+ end
77
+
78
+ def log
79
+ @logger ||= Rails.logger rescue ::Logger.new(STDOUT)
80
+ end
81
+
82
+ def config
83
+ @config ||= {
84
+ :cas_base_url => nil,
85
+ :cas_destination_logout_param_name => nil,
86
+ :logger => log,
87
+ :username_session_key => nil,
88
+ :extra_attributes_session_key => nil,
89
+ :ticket_store => nil,
90
+ :login_url => nil,
91
+ :validate_url => nil,
92
+ :proxy_url => nil,
93
+ :logout_url => nil,
94
+ :service_url => nil,
95
+ :proxy_callback_url => nil,
96
+ :proxy_retrieval_url => nil,
97
+ :tmp_dir => nil
98
+ }.merge(options)
99
+ raise "You must provide the location " if @config[:enable_single_sign_out] && @config[:session_dir].nil?
100
+ @config
101
+ end
102
+
103
+ def assets_request?(env)
104
+ Rack::Request.new(env).path =~ /.*\.(js|css|png|jpg|jpeg|gif|ico)$/i
105
+ end
106
+
107
+ def logout_request?(env)
108
+ request = Rack::Request.new(env)
109
+ if request.path == '/logout' && request.delete?
110
+ # st = last_service_ticket
111
+ st = request.session['cas']['last_valid_ticket']
112
+ [st, request]
113
+ end
114
+ end
115
+
116
+ def logout(st, request)
117
+ log.debug("Logging out!!")
118
+ log.debug("looking up st for deletion #{st.inspect}")
119
+
120
+ delete_service_session_lookup(st) if st
121
+ request.session.delete('cas')
122
+
123
+ response = Rack::Response.new
124
+ response.redirect(client.logout_url(request.referer))
125
+ response.finish
126
+ end
127
+
128
+ def sso_request?(env)
129
+ request = Rack::Request.new(env)
130
+ request if request.post? && request.params['logoutRequest']
131
+ end
132
+
133
+ def single_sign_out(request)
134
+ log.debug("SINGLE SIGN OUT")
135
+ logoutRequest = URI.unescape(request.params['logoutRequest'])
136
+
137
+ md = logoutRequest.match( %r{^<samlp:LogoutRequest.*?<samlp:SessionIndex>(.*)</samlp:SessionIndex>}m )
138
+ if md && md[1]
139
+ ticket = md[1]
140
+ done = delete_service_session_lookup(ticket)
141
+ if done
142
+ [200,{'Content-type' => 'text/plain'},['session deleted']]
143
+ else
144
+ [404,{'Content-type' => 'text/plain'},['session not found']]
145
+ end
146
+ else
147
+ [400,{'Content-type' => 'text/plain'},['missing service ticket in request']]
148
+ end
149
+ end
150
+
151
+ def unauthorized_request
152
+ log.debug("Unauthorized request")
153
+ if vr
154
+ [401, {'Content-type' => 'application/xml'}, ["<errors><error>#{vr.failure_message}</error></errors>"]]
155
+ else
156
+ [401, {'Content-type' => 'text/html'}, [vr.failure_message]]
157
+ end
158
+ end
159
+
160
+ def xml_request?(env)
161
+ Rack::Request.new(env).params[:format] == "xml"
162
+ end
163
+
164
+
165
+ def authenticated?(env)
166
+ request = Rack::Request.new(env)
167
+ @mem = request.session['cas'] || {}
168
+
169
+ current_service_ticket = nil
170
+ user = nil
171
+ user_extra = nil
172
+ new_session = true
173
+
174
+ case check_service_ticket(env)
175
+ when :identical
176
+ log.warn("Re-using previously validated ticket since the ticket id and service are the same.")
177
+ new_session = false
178
+ current_service_ticket = last_service_ticket
179
+
180
+ when :different
181
+ log.debug "Existing local CAS session detected for #{client_username_session_key.inspect}. "+"Previous ticket #{last_service_ticket.ticket.inspect} will be re-used."
182
+ new_session = false
183
+ current_service_ticket = last_service_ticket
184
+
185
+ else
186
+ log.debug("New session!")
187
+ current_service_ticket = service_ticket(env)
188
+ end
189
+
190
+ if current_service_ticket
191
+ unless current_service_ticket.has_been_validated?
192
+ log.debug("VALIDATING SERVICE TICKET")
193
+ client.validate_service_ticket(current_service_ticket)
194
+ end
195
+ vr = current_service_ticket.response
196
+
197
+ if current_service_ticket.is_valid?
198
+ work_for_vr_pgt_iou(vr,env) if vr.pgt_iou
199
+
200
+ return [env, request, new_session, current_service_ticket]
201
+ else
202
+
203
+ log.warn("Ticket #{current_service_ticket.ticket.inspect} failed validation -- #{vr.failure_code}: #{vr.failure_message}")
204
+ return false
205
+ end
206
+ else
207
+
208
+ # no service ticket was present in the request
209
+ if gateway
210
+ log.info "Returning from CAS gateway without authentication."
211
+
212
+ # unset, to allow for the next request to be authenticated if necessary
213
+ sent_to_gateway = false
214
+
215
+ if config[:use_gatewaying]
216
+ log.info "This CAS client is configured to use gatewaying, so we will permit the user to continue without authentication."
217
+ client_username_session_key = nil
218
+ return true
219
+ else
220
+ log.warn "The CAS client is NOT configured to allow gatewaying, yet this request was gatewayed. Something is not right!"
221
+ end
222
+ end
223
+
224
+
225
+ return false
226
+
227
+ end
228
+
229
+ rescue OpenSSL::SSL::SSLError
230
+ 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.")
231
+
232
+ return false
233
+ end
234
+
235
+ def valid_session(env, request, new_session, current_service_ticket)
236
+ cas_resp = current_service_ticket.response
237
+ log.info("Ticket #{current_service_ticket.ticket.inspect} for service #{current_service_ticket.service.inspect} belonging to user #{cas_resp.user.inspect} is VALID.")
238
+ env['rack.cas.client.user'] = cas_resp.user
239
+ env['rack.cas.client.user_extra'] = cas_resp.extra_attributes.dup
240
+
241
+ # TODO: remove ticket params from env
242
+
243
+ status, headers, body = app.call(env)
244
+
245
+ response = Rack::Response.new(body, status, headers)
246
+ # only modify the session when it's a new_session
247
+ if new_session
248
+ session = request.session
249
+ session['cas'] = {'last_valid_ticket' => current_service_ticket, 'filteruser' => cas_resp.user, 'username_session_key' => cas_resp.user}
250
+
251
+ if config[:enable_single_sign_out]
252
+ f = store_service_session_lookup(current_service_ticket, session)
253
+ log.debug("Wrote service session lookup file to #{f.inspect} with session id #{session.inspect}.")
254
+ end
255
+
256
+ response.delete_cookie(request.session_options[:key], {})
257
+ response.set_cookie(request.session_options[:key], session)
258
+ end
259
+ response.finish
260
+ end
261
+
262
+ def work_for_vr_pgt_iou(vr,env)
263
+ log.debug("CALLING work_for_vr_pgt_iou with vr #{vr.inspect}")
264
+ request = Rack::Request.new(env)
265
+ unless request.session[:cas_pgt] && request.session[:cas_pgt].ticket && request.session[:cas_pgt].iou == vr.pgt_iou
266
+ log.info("Receipt has a proxy-granting ticket IOU. Attempting to retrieve the proxy-granting ticket...")
267
+ pgt = client.retrieve_proxy_granting_ticket(vr.pgt_iou)
268
+
269
+ if pgt
270
+ log.debug("Got PGT #{pgt.ticket.inspect} for PGT IOU #{pgt.iou.inspect}. This will be stored in the session.")
271
+ request.session[:cas_pgt] = pgt
272
+ # For backwards compatibility with RubyCAS-Client 1.x configurations...
273
+ request.session[:casfilterpgt] = pgt
274
+ else
275
+ log.error("Failed to retrieve a PGT for PGT IOU #{vr.pgt_iou}!")
276
+ end
277
+ else
278
+ log.info("PGT is present in session and PGT IOU #{vr.pgt_iou} matches the saved PGT IOU. Not retrieving new PGT.")
279
+ end
280
+
281
+ end
282
+
283
+ def check_service_ticket(env)
284
+ st, last_st = [service_ticket(env), last_service_ticket]
285
+
286
+ return :identical if st && last_st && last_st.ticket == st.ticket && last_st.service == st.service
287
+ return :different if last_st && !config[:authenticate_on_every_request] && client_username_session_key
288
+ end
289
+
290
+ def service_ticket(env)
291
+ request = Rack::Request.new(env)
292
+
293
+ ticket = request.params['ticket']
294
+ return unless ticket
295
+
296
+ if ticket =~ /^PT-/
297
+ CASClient::ProxyTicket.new(ticket, service_url(env), request.params.delete('renew'))
298
+ else
299
+ CASClient::ServiceTicket.new(ticket, service_url(env), request.params.delete('renew'))
300
+ end
301
+ end
302
+
303
+ def last_service_ticket
304
+ @mem['last_valid_ticket']
305
+ end
306
+ def last_service_ticket=(value)
307
+ @mem['last_valid_ticket'] = value
308
+ end
309
+
310
+ def client_username_session_key
311
+ @mem['username_session_key']
312
+ end
313
+ def client_username_session_key=(value)
314
+ @mem['username_session_key'] = value
315
+ end
316
+
317
+ def client_extra_attributes_session_key
318
+ @mem['user_extra']
319
+ end
320
+ def client_extra_attributes_session_key=(value)
321
+ @mem['user_extra'] = value
322
+ end
323
+ def casfilteruser=(value)
324
+ @mem['filteruser'] = value
325
+ end
326
+ def gateway=(value)
327
+ @mem['sent_to_gateway'] = value
328
+ end
329
+ def gateway
330
+ @mem['sent_to_gateway']
331
+ end
332
+ def previous_redirect_to_cas
333
+ @mem['previous_redirect']
334
+ end
335
+ def previous_redirect_to_cas=(value)
336
+ @mem['cas']['previous_redirect'] = value
337
+ end
338
+ def validation_retry
339
+ @mem['validation_retry'] || 0
340
+ end
341
+ def validation_retry=(value)
342
+ @mem['validation_retry'] = value
343
+ end
344
+
345
+ def vr
346
+ @vr
347
+ end
348
+
349
+ def vr=(value)
350
+ @vr = value
351
+ end
352
+
353
+ def service_url(env)
354
+ return @service_url if @service_url
355
+
356
+ if config[:service_url]
357
+ log.debug("Using explicitly set service url: #{config[:service_url]}")
358
+ return @service_url = config[:service_url]
359
+ end
360
+ request = Rack::Request.new(env)
361
+
362
+ params = request.params.dup
363
+ params.delete(:ticket)
364
+ url = URI.const_get(request.scheme.upcase).build(:host => request.host, :port => request.port, :path => request.path, :query => request.query_string)
365
+ @service_url = url.to_s
366
+ log.debug("Guessed service url: #{@service_url}")
367
+ @service_url
368
+ end
369
+
370
+
371
+ def redirect_to_cas_for_authentication(env)
372
+ redirect_url = login_url(env)
373
+
374
+ if config[:use_gatewaying]
375
+ gateway = true
376
+ redirect_url << "&gateway=true"
377
+ else
378
+ gateway = false
379
+ end
380
+
381
+ if previous_redirect_to_cas && previous_redirect_to_cas > (Time.now - 1.second)
382
+ 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!")
383
+
384
+ if validation_retry > 3
385
+ log.error("Redirection loop intercepted. Client at #{controller.request.remote_ip.inspect} will be redirected back to login page and forced to renew authentication.")
386
+ redirect_url += "&renew=1&redirection_loop_intercepted=1"
387
+ end
388
+
389
+ validation_retry = validation_retry + 1
390
+ else
391
+ validation_retry = 0
392
+ end
393
+ previous_redirect_to_cas = Time.now
394
+
395
+ request = Rack::Request.new(env)
396
+ response = Rack::Response.new(["redirect to #{redirect_url}"],302, {'Location' => redirect_url, 'Content-Type' => 'text/plain'})
397
+
398
+ return response.finish
399
+ end
400
+
401
+
402
+ # Returns the login URL for the current controller.
403
+ # Useful when you want to provide a "Login" link in a GatewayFilter'ed
404
+ # action.
405
+ def login_url(env)
406
+ url = client.add_service_to_login_url(service_url(env))
407
+ log.debug("Generated login url: #{url}")
408
+ return url
409
+ end
410
+
411
+ end
412
+
413
+
414
+ module ClientHelpers
415
+
416
+ module Sinatra
417
+ def current_user
418
+ return @current_user if @current_user
419
+ user_data = {:username => request.env['rack.cas.client.user']}
420
+ user_data.merge!(request.env['rack.cas.client.user_extra'])
421
+ @current_user = OpenStruct.new(user_data)
422
+ end
423
+ end
424
+
425
+ module Rails
426
+ def current_user
427
+ return @current_user if @current_user
428
+ user_data = {:username => request.env['rack.cas.client.user']}
429
+ user_data.merge!(request.env['rack.cas.client.user_extra'])
430
+ @current_user = OpenStruct.new(user_data)
431
+ end
432
+ end
433
+
434
+ end
435
+
436
+ end
437
+ end
File without changes
@@ -0,0 +1,68 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rack-cas-client}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{Jerome Riga}]
12
+ s.date = %q{2011-10-10}
13
+ s.description = %q{Rack middleware that handles user authentication against a cas server}
14
+ s.email = %q{jriga@zemis.co.uk}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "config.ru",
29
+ "lib/rack-cas-client.rb",
30
+ "lib/rack/cas_client.rb",
31
+ "rack-cas-client.gemspec",
32
+ "spec/rack-cas-client_spec.rb",
33
+ "spec/spec_helper.rb"
34
+ ]
35
+ s.homepage = %q{http://github.com/zemis/rack-cas-client}
36
+ s.licenses = [%q{MIT}]
37
+ s.require_paths = [%q{lib}]
38
+ s.rubygems_version = %q{1.8.6}
39
+ s.summary = %q{Rack middleware for authentication with CAS server}
40
+
41
+ if s.respond_to? :specification_version then
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_runtime_dependency(%q<rubycas-client>, ["~> 2.2.1"])
46
+ s.add_runtime_dependency(%q<activesupport>, [">= 2"])
47
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
48
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
49
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
50
+ s.add_development_dependency(%q<rcov>, [">= 0"])
51
+ else
52
+ s.add_dependency(%q<rubycas-client>, ["~> 2.2.1"])
53
+ s.add_dependency(%q<activesupport>, [">= 2"])
54
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
55
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
56
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
57
+ s.add_dependency(%q<rcov>, [">= 0"])
58
+ end
59
+ else
60
+ s.add_dependency(%q<rubycas-client>, ["~> 2.2.1"])
61
+ s.add_dependency(%q<activesupport>, [">= 2"])
62
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
63
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
64
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
65
+ s.add_dependency(%q<rcov>, [">= 0"])
66
+ end
67
+ end
68
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "RackCasClient" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'rack-cas-client'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-cas-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jerome Riga
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-10 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rubycas-client
16
+ requirement: &2156465080 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.2.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2156465080
25
+ - !ruby/object:Gem::Dependency
26
+ name: activesupport
27
+ requirement: &2156464560 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '2'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2156464560
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &2156464080 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 2.3.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2156464080
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: &2156463600 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2156463600
58
+ - !ruby/object:Gem::Dependency
59
+ name: jeweler
60
+ requirement: &2156463120 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 1.6.4
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2156463120
69
+ - !ruby/object:Gem::Dependency
70
+ name: rcov
71
+ requirement: &2156462640 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *2156462640
80
+ description: Rack middleware that handles user authentication against a cas server
81
+ email: jriga@zemis.co.uk
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files:
85
+ - LICENSE.txt
86
+ - README.rdoc
87
+ files:
88
+ - .document
89
+ - .rspec
90
+ - Gemfile
91
+ - Gemfile.lock
92
+ - LICENSE.txt
93
+ - README.rdoc
94
+ - Rakefile
95
+ - VERSION
96
+ - config.ru
97
+ - lib/rack-cas-client.rb
98
+ - lib/rack/cas_client.rb
99
+ - rack-cas-client.gemspec
100
+ - spec/rack-cas-client_spec.rb
101
+ - spec/spec_helper.rb
102
+ homepage: http://github.com/zemis/rack-cas-client
103
+ licenses:
104
+ - MIT
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ segments:
116
+ - 0
117
+ hash: -4063731964327134772
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 1.8.6
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: Rack middleware for authentication with CAS server
130
+ test_files: []