gunark-rubycas-server 0.6.99.336

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.
Files changed (72) hide show
  1. data/CHANGELOG.txt +1 -0
  2. data/History.txt +245 -0
  3. data/LICENSE.txt +504 -0
  4. data/Manifest.txt +74 -0
  5. data/PostInstall.txt +3 -0
  6. data/README.txt +25 -0
  7. data/Rakefile +4 -0
  8. data/bin/rubycas-server +26 -0
  9. data/bin/rubycas-server-ctl +22 -0
  10. data/config.example.yml +442 -0
  11. data/config/hoe.rb +76 -0
  12. data/config/requirements.rb +15 -0
  13. data/custom_views.example.rb +11 -0
  14. data/lib/casserver.rb +111 -0
  15. data/lib/casserver/authenticators/active_directory_ldap.rb +11 -0
  16. data/lib/casserver/authenticators/base.rb +48 -0
  17. data/lib/casserver/authenticators/client_certificate.rb +46 -0
  18. data/lib/casserver/authenticators/ldap.rb +138 -0
  19. data/lib/casserver/authenticators/ntlm.rb +88 -0
  20. data/lib/casserver/authenticators/open_id.rb +22 -0
  21. data/lib/casserver/authenticators/sql.rb +102 -0
  22. data/lib/casserver/authenticators/sql_encrypted.rb +75 -0
  23. data/lib/casserver/authenticators/sql_md5.rb +19 -0
  24. data/lib/casserver/authenticators/test.rb +19 -0
  25. data/lib/casserver/cas.rb +308 -0
  26. data/lib/casserver/conf.rb +112 -0
  27. data/lib/casserver/controllers.rb +452 -0
  28. data/lib/casserver/environment.rb +26 -0
  29. data/lib/casserver/models.rb +218 -0
  30. data/lib/casserver/postambles.rb +174 -0
  31. data/lib/casserver/utils.rb +30 -0
  32. data/lib/casserver/version.rb +9 -0
  33. data/lib/casserver/views.rb +243 -0
  34. data/lib/rubycas-server.rb +1 -0
  35. data/lib/rubycas-server/version.rb +1 -0
  36. data/lib/themes/cas.css +121 -0
  37. data/lib/themes/notice.png +0 -0
  38. data/lib/themes/ok.png +0 -0
  39. data/lib/themes/simple/bg.png +0 -0
  40. data/lib/themes/simple/login_box_bg.png +0 -0
  41. data/lib/themes/simple/logo.png +0 -0
  42. data/lib/themes/simple/theme.css +28 -0
  43. data/lib/themes/urbacon/bg.png +0 -0
  44. data/lib/themes/urbacon/login_box_bg.png +0 -0
  45. data/lib/themes/urbacon/logo.png +0 -0
  46. data/lib/themes/urbacon/theme.css +33 -0
  47. data/lib/themes/warning.png +0 -0
  48. data/misc/basic_cas_single_signon_mechanism_diagram.png +0 -0
  49. data/misc/basic_cas_single_signon_mechanism_diagram.svg +652 -0
  50. data/resources/init.d.sh +58 -0
  51. data/script/console +10 -0
  52. data/script/destroy +14 -0
  53. data/script/generate +14 -0
  54. data/script/txt2html +82 -0
  55. data/setup.rb +1585 -0
  56. data/tasks/deployment.rake +34 -0
  57. data/tasks/environment.rake +7 -0
  58. data/tasks/website.rake +17 -0
  59. data/vendor/isaac_0.9.1/LICENSE +26 -0
  60. data/vendor/isaac_0.9.1/README +78 -0
  61. data/vendor/isaac_0.9.1/TODO +3 -0
  62. data/vendor/isaac_0.9.1/VERSIONS +3 -0
  63. data/vendor/isaac_0.9.1/crypt/ISAAC.rb +171 -0
  64. data/vendor/isaac_0.9.1/isaac.gemspec +39 -0
  65. data/vendor/isaac_0.9.1/setup.rb +596 -0
  66. data/vendor/isaac_0.9.1/test/TC_ISAAC.rb +76 -0
  67. data/website/index.html +40 -0
  68. data/website/index.txt +3 -0
  69. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  70. data/website/stylesheets/screen.css +138 -0
  71. data/website/template.html.erb +40 -0
  72. metadata +146 -0
@@ -0,0 +1,112 @@
1
+ # load configuration
2
+
3
+ begin
4
+ if $CONFIG_FILE
5
+ conf_file = $CONFIG_FILE
6
+ else
7
+ conf_file = etc_conf = "/etc/rubycas-server/config.yml"
8
+ unless File.exists? conf_file
9
+ # can use local config.yml file in case we're running non-gem installation
10
+ conf_file = File.dirname(File.expand_path(__FILE__))+"/../../config.yml"
11
+ end
12
+ end
13
+
14
+ unless File.exists? conf_file
15
+ require 'fileutils'
16
+
17
+ example_conf_file = File.expand_path(File.dirname(File.expand_path(__FILE__))+"/../../config.example.yml")
18
+ puts "\nCAS SERVER NOT YET CONFIGURED!!!\n"
19
+ puts "\nAttempting to copy sample configuration from '#{example_conf_file}' to '#{etc_conf}'...\n"
20
+
21
+ begin
22
+ FileUtils.mkdir("/etc/rubycas-server") unless File.exists? "/etc/rubycas-server"
23
+ FileUtils.cp(example_conf_file, etc_conf)
24
+ rescue Errno::EACCES
25
+ puts "\nIt appears that you do not have permissions to create the '#{etc_conf}' file. Try running this command using sudo (as root).\n"
26
+ exit 2
27
+ rescue
28
+ puts "\nFor some reason the '#{etc_conf}' file could not be created. You'll have to copy the file manually." +
29
+ " Use '#{example_conf_file}' as a template.\n"
30
+ exit 2
31
+ end
32
+
33
+ puts "\nA sample configuration has been created for you in '#{etc_conf}'. Please edit this file to" +
34
+ " suit your needs and then run rubycas-server again.\n"
35
+ exit 1
36
+ end
37
+
38
+ loaded_conf = HashWithIndifferentAccess.new(YAML.load_file(conf_file))
39
+
40
+ if $CONF
41
+ $CONF = loaded_conf.merge $CONF
42
+ else
43
+ $CONF = loaded_conf
44
+ end
45
+
46
+ if $CONF[:authenticator].instance_of? Array
47
+ $CONF[:authenticator].each_index { |auth_index| $CONF[:authenticator][auth_index] = HashWithIndifferentAccess.new($CONF[:authenticator][auth_index])}
48
+ end
49
+
50
+ $AUTH = []
51
+ begin
52
+ # attempt to instantiate the authenticator
53
+ if $CONF[:authenticator].instance_of? Array
54
+ $CONF[:authenticator].each { |authenticator| $AUTH << authenticator[:class].constantize.new}
55
+ else
56
+ $AUTH << $CONF[:authenticator][:class].constantize.new
57
+ end
58
+ rescue NameError
59
+ if $CONF[:authenticator].instance_of? Array
60
+ $CONF[:authenticator].each do |authenticator|
61
+ if !authenticator[:source].nil?
62
+ # config.yml explicitly names source file
63
+ require authenticator[:source]
64
+ else
65
+ # the authenticator class hasn't yet been loaded, so lets try to load it from the casserver/authenticators directory
66
+ auth_rb = authenticator[:class].underscore.gsub('cas_server/', '')
67
+ require 'casserver/'+auth_rb
68
+ end
69
+ $AUTH << authenticator[:class].constantize.new
70
+ end
71
+ else
72
+ if !$CONF[:authenticator][:source].nil?
73
+ # config.yml explicitly names source file
74
+ require $CONF[:authenticator][:source]
75
+ else
76
+ # the authenticator class hasn't yet been loaded, so lets try to load it from the casserver/authenticators directory
77
+ auth_rb = $CONF[:authenticator][:class].underscore.gsub('cas_server/', '')
78
+ require 'casserver/'+auth_rb
79
+ end
80
+
81
+ $AUTH << $CONF[:authenticator][:class].constantize.new
82
+ end
83
+ end
84
+ rescue
85
+ raise "Your RubyCAS-Server configuration may be invalid."+
86
+ " Please double-check check your config.yml file."+
87
+ " Make sure that you are using spaces instead of tabs for your indentation!!" +
88
+ "\n\nUNDERLYING EXCEPTION:\n#{$!}"
89
+ end
90
+
91
+ module CASServer
92
+ module Conf
93
+ DEFAULTS = {
94
+ :expire_sessions => false,
95
+ :login_ticket_expiry => 5.minutes,
96
+ :service_ticket_expiry => 5.minutes, # CAS Protocol Spec, sec. 3.2.1 (recommended expiry time)
97
+ :proxy_granting_ticket_expiry => 48.hours,
98
+ :ticket_granting_ticket_expiry => 48.hours,
99
+ :log => {:file => 'casserver.log', :level => 'DEBUG'},
100
+ :uri_path => "/"
101
+ }
102
+
103
+ def [](key)
104
+ $CONF[key] || DEFAULTS[key]
105
+ end
106
+ module_function "[]".intern
107
+
108
+ def self.method_missing(method, *args)
109
+ self[method]
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,452 @@
1
+ # The #.#.# comments (e.g. "2.1.3") refer to section numbers in the CAS protocol spec
2
+ # under http://www.ja-sig.org/products/cas/overview/protocol/index.html
3
+
4
+ module CASServer::Controllers
5
+
6
+ # 2.1
7
+ class Login < R '/', '/login'
8
+ include CASServer::CAS
9
+
10
+ # 2.1.1
11
+ def get
12
+ CASServer::Utils::log_controller_action(self.class, @input)
13
+
14
+ # make sure there's no caching
15
+ headers['Pragma'] = 'no-cache'
16
+ headers['Cache-Control'] = 'no-store'
17
+ headers['Expires'] = (Time.now - 1.year).rfc2822
18
+
19
+ # optional params
20
+ @service = clean_service_url(@input['service'])
21
+ @renew = @input['renew']
22
+ @gateway = @input['gateway'] == 'true' || @input['gateway'] == '1'
23
+
24
+ if tgc = @cookies[:tgt]
25
+ tgt, tgt_error = validate_ticket_granting_ticket(tgc)
26
+ end
27
+
28
+ if tgt and !tgt_error
29
+ @message = {:type => 'notice',
30
+ :message => %{You are currently logged in as "#{tgt.username}". If this is not you, please log in below.}}
31
+ end
32
+
33
+ if @input['redirection_loop_intercepted']
34
+ @message = {:type => 'mistake',
35
+ :message => %{The client and server are unable to negotiate authentication. Please try logging in again later.}}
36
+ end
37
+
38
+ begin
39
+ if @service
40
+ if !@renew && tgt && !tgt_error
41
+ st = generate_service_ticket(@service, tgt.username, tgt)
42
+ service_with_ticket = service_uri_with_ticket(@service, st)
43
+ $LOG.info("User '#{tgt.username}' authenticated based on ticket granting cookie. Redirecting to service '#{@service}'.")
44
+ return redirect(service_with_ticket, :status => 303) # response code 303 means "See Other" (see Appendix B in CAS Protocol spec)
45
+ elsif @gateway
46
+ $LOG.info("Redirecting unauthenticated gateway request to service '#{@service}'.")
47
+ return redirect(@service, :status => 303)
48
+ end
49
+ elsif @gateway
50
+ $LOG.error("This is a gateway request but no service parameter was given!")
51
+ @message = {:type => 'mistake',
52
+ :message => "The server cannot fulfill this gateway request because no service parameter was given."}
53
+ end
54
+ rescue URI::InvalidURIError
55
+ $LOG.error("The service '#{@service}' is not a valid URI!")
56
+ @message = {:type => 'mistake',
57
+ :message => "The target service your browser supplied appears to be invalid. Please contact your system administrator for help."}
58
+ end
59
+
60
+ lt = generate_login_ticket
61
+
62
+ $LOG.debug("Rendering login form with lt: #{lt}, service: #{@service}, renew: #{@renew}, gateway: #{@gateway}")
63
+
64
+ @lt = lt.ticket
65
+
66
+ #$LOG.debug(env)
67
+
68
+ # If the 'onlyLoginForm' parameter is specified, we will only return the
69
+ # login form part of the page. This is useful for when you want to
70
+ # embed the login form in some external page (as an IFRAME, or otherwise).
71
+ # The optional 'submitToURI' parameter can be given to explicitly set the
72
+ # action for the form, otherwise the server will try to guess this for you.
73
+ if @input.has_key? 'onlyLoginForm'
74
+ if env['HTTP_HOST']
75
+ guessed_login_uri = "http#{env['HTTPS'] && env['HTTPS'] == 'on' ? 's' : ''}://#{env['REQUEST_URI']}#{self / '/login'}"
76
+ else
77
+ guessed_login_uri = nil
78
+ end
79
+
80
+ @form_action = @input['submitToURI'] || guessed_login_uri
81
+
82
+ if @form_action
83
+ render :login_form
84
+ else
85
+ @status = 500
86
+ "Could not guess the CAS login URI. Please supply a submitToURI parameter with your request."
87
+ end
88
+ else
89
+ render :login
90
+ end
91
+ end
92
+
93
+ # 2.2
94
+ def post
95
+ CASServer::Utils::log_controller_action(self.class, @input)
96
+
97
+ # 2.2.1 (optional)
98
+ @service = clean_service_url(@input['service'])
99
+
100
+ # 2.2.2 (required)
101
+ @username = @input['username']
102
+ @password = @input['password']
103
+ @lt = @input['lt']
104
+
105
+ # Remove leading and trailing widespace from username.
106
+ @username.strip! if @username
107
+
108
+ if @username && $CONF[:downcase_username]
109
+ $LOG.debug("Converting username #{@username.inspect} to lowercase because 'downcase_username' option is enabled.")
110
+ @username.downcase!
111
+ end
112
+
113
+ if error = validate_login_ticket(@lt)
114
+ @message = {:type => 'mistake', :message => error}
115
+ # generate another login ticket to allow for re-submitting the form
116
+ @lt = generate_login_ticket.ticket
117
+ @status = 401
118
+ return render(:login)
119
+ end
120
+
121
+ # generate another login ticket to allow for re-submitting the form after a post
122
+ @lt = generate_login_ticket.ticket
123
+
124
+ if $CONF[:authenticator].instance_of? Array
125
+ $AUTH.each_index {|auth_index| $AUTH[auth_index].configure(CASServer::Conf.authenticator[auth_index])}
126
+ else
127
+ $AUTH[0].configure(CASServer::Conf.authenticator)
128
+ end
129
+
130
+ $LOG.debug("Logging in with username: #{@username}, lt: #{@lt}, service: #{@service}, auth: #{$AUTH}")
131
+
132
+ credentials_are_valid = false
133
+ extra_attributes = {}
134
+ successful_authenticator = nil
135
+ begin
136
+ $AUTH.each do |auth|
137
+ credentials_are_valid = auth.validate(
138
+ :username => @username,
139
+ :password => @password,
140
+ :service => @service,
141
+ :request => env
142
+ )
143
+ if credentials_are_valid
144
+ extra_attributes.merge!(auth.extra_attributes) unless auth.extra_attributes.blank?
145
+ successful_authenticator = auth
146
+ break
147
+ end
148
+ end
149
+ rescue CASServer::AuthenticatorError => e
150
+ $LOG.error(e)
151
+ @message = {:type => 'mistake', :message => e.to_s}
152
+ return render(:login)
153
+ end
154
+
155
+ if credentials_are_valid
156
+ $LOG.info("Credentials for username '#{@username}' successfully validated using #{successful_authenticator.class.name}.")
157
+ $LOG.debug("Authenticator provided additional user attributes: #{extra_attributes.inspect}") unless extra_attributes.blank?
158
+
159
+ # 3.6 (ticket-granting cookie)
160
+ tgt = generate_ticket_granting_ticket(@username, extra_attributes)
161
+
162
+ if CASServer::Conf.expire_sessions
163
+ expires = CASServer::Conf.ticket_granting_ticket_expiry.to_i.from_now
164
+ expiry_info = " It will expire on #{expires}."
165
+ else
166
+ expiry_info = " It will not expire."
167
+ end
168
+
169
+ if CASServer::Conf.expire_sessions
170
+ @cookies[:tgt] = {
171
+ :value => tgt.to_s,
172
+ :expires => Time.now + CASServer::Conf.ticket_granting_ticket_expiry
173
+ }
174
+ else
175
+ @cookies[:tgt] = tgt.to_s
176
+ end
177
+
178
+ $LOG.debug("Ticket granting cookie '#{@cookies[:tgt].inspect}' granted to '#{@username.inspect}'. #{expiry_info}")
179
+
180
+ if @service.blank?
181
+ $LOG.info("Successfully authenticated user '#{@username}' at '#{tgt.client_hostname}'. No service param was given, so we will not redirect.")
182
+ @message = {:type => 'confirmation', :message => "You have successfully logged in."}
183
+ else
184
+ @st = generate_service_ticket(@service, @username, tgt)
185
+ begin
186
+ service_with_ticket = service_uri_with_ticket(@service, @st)
187
+
188
+ $LOG.info("Redirecting authenticated user '#{@username}' at '#{@st.client_hostname}' to service '#{@service}'")
189
+ return redirect(service_with_ticket, :status => 303) # response code 303 means "See Other" (see Appendix B in CAS Protocol spec)
190
+ rescue URI::InvalidURIError
191
+ $LOG.error("The service '#{@service}' is not a valid URI!")
192
+ @message = {:type => 'mistake', :message => "The target service your browser supplied appears to be invalid. Please contact your system administrator for help."}
193
+ end
194
+ end
195
+ else
196
+ $LOG.warn("Invalid credentials given for user '#{@username}'")
197
+ @message = {:type => 'mistake', :message => "Incorrect username or password."}
198
+ @status = 401
199
+ end
200
+
201
+ render :login
202
+ end
203
+ end
204
+
205
+ # 2.3
206
+ class Logout < R '/logout'
207
+ include CASServer::CAS
208
+
209
+ # 2.3.1
210
+ def get
211
+ CASServer::Utils::log_controller_action(self.class, @input)
212
+
213
+ # The behaviour here is somewhat non-standard. Rather than showing just a blank
214
+ # "logout" page, we take the user back to the login page with a "you have been logged out"
215
+ # message, allowing for an opportunity to immediately log back in. This makes it
216
+ # easier for the user to log out and log in as someone else.
217
+ @service = clean_service_url(@input['service'] || @input['destination'])
218
+ @continue_url = @input['url']
219
+
220
+ @gateway = @input['gateway'] == 'true' || @input['gateway'] == '1'
221
+
222
+ tgt = CASServer::Models::TicketGrantingTicket.find_by_ticket(@cookies[:tgt])
223
+
224
+ @cookies.delete :tgt
225
+
226
+ if tgt
227
+ CASServer::Models::TicketGrantingTicket.transaction do
228
+ pgts = CASServer::Models::ProxyGrantingTicket.find(:all,
229
+ :conditions => [CASServer::Models::Base.connection.quote_table_name(CASServer::Models::ServiceTicket.table_name)+".username = ?", tgt.username],
230
+ :include => :service_ticket)
231
+ pgts.each do |pgt|
232
+ $LOG.debug("Deleting Proxy-Granting Ticket '#{pgt}' for user '#{pgt.service_ticket.username}'")
233
+ pgt.destroy
234
+ end
235
+
236
+ if CASServer::Conf.enable_single_sign_out
237
+ $LOG.debug("Deleting Service/Proxy Tickets for '#{tgt}' for user '#{tgt.username}'")
238
+ tgt.service_tickets.each do |st|
239
+ send_logout_notification_for_service_ticket(st)
240
+ # TODO: Maybe we should do some special handling if send_logout_notification_for_service_ticket fails?
241
+ # Note that the method returns false if the POST results in a non-200 HTTP response.
242
+ $LOG.debug "Deleting #{st.class} #{st.ticket.inspect}."
243
+ st.destroy
244
+ end
245
+ end
246
+
247
+ $LOG.debug("Deleting Ticket-Granting Ticket '#{tgt}' for user '#{tgt.username}'")
248
+ tgt.destroy
249
+ end
250
+
251
+ $LOG.info("User '#{tgt.username}' logged out.")
252
+ else
253
+ $LOG.warn("User tried to log out without a valid ticket-granting ticket.")
254
+ end
255
+
256
+ @message = {:type => 'confirmation', :message => "You have successfully logged out."}
257
+
258
+ @message[:message] <<
259
+ " Please click on the following link to continue:" if @continue_url
260
+
261
+ @lt = generate_login_ticket
262
+
263
+ if @gateway && @service
264
+ redirect(@service, :status => 303)
265
+ elsif @continue_url
266
+ render :logout
267
+ else
268
+ render :login
269
+ end
270
+ end
271
+ end
272
+
273
+ # 2.4
274
+ class Validate < R '/validate'
275
+ include CASServer::CAS
276
+
277
+ # 2.4.1
278
+ def get
279
+ CASServer::Utils::log_controller_action(self.class, @input)
280
+
281
+ # required
282
+ @service = clean_service_url(@input['service'])
283
+ @ticket = @input['ticket']
284
+ # optional
285
+ @renew = @input['renew']
286
+
287
+ st, @error = validate_service_ticket(@service, @ticket)
288
+ @success = st && !@error
289
+
290
+ @username = st.username if @success
291
+
292
+ @status = response_status_from_error(@error) if @error
293
+
294
+ render :validate
295
+ end
296
+ end
297
+
298
+ # 2.5
299
+ class ServiceValidate < R '/serviceValidate'
300
+ include CASServer::CAS
301
+
302
+ # 2.5.1
303
+ def get
304
+ CASServer::Utils::log_controller_action(self.class, @input)
305
+
306
+ # required
307
+ @service = clean_service_url(@input['service'])
308
+ @ticket = @input['ticket']
309
+ # optional
310
+ @pgt_url = @input['pgtUrl']
311
+ @renew = @input['renew']
312
+
313
+ st, @error = validate_service_ticket(@service, @ticket)
314
+ @success = st && !@error
315
+
316
+ if @success
317
+ @username = st.username
318
+ if @pgt_url
319
+ pgt = generate_proxy_granting_ticket(@pgt_url, st)
320
+ @pgtiou = pgt.iou if pgt
321
+ end
322
+ @extra_attributes = st.ticket_granting_ticket.extra_attributes || {}
323
+ end
324
+
325
+ @status = response_status_from_error(@error) if @error
326
+
327
+ render :service_validate
328
+ end
329
+ end
330
+
331
+ # 2.6
332
+ class ProxyValidate < R '/proxyValidate'
333
+ include CASServer::CAS
334
+
335
+ # 2.6.1
336
+ def get
337
+ CASServer::Utils::log_controller_action(self.class, @input)
338
+
339
+ # required
340
+ @service = clean_service_url(@input['service'])
341
+ @ticket = @input['ticket']
342
+ # optional
343
+ @pgt_url = @input['pgtUrl']
344
+ @renew = @input['renew']
345
+
346
+ @proxies = []
347
+
348
+ t, @error = validate_proxy_ticket(@service, @ticket)
349
+ @success = t && !@error
350
+
351
+ @extra_attributes = {}
352
+ if @success
353
+ @username = t.username
354
+
355
+ if t.kind_of? CASServer::Models::ProxyTicket
356
+ @proxies << t.proxy_granting_ticket.service_ticket.service
357
+ end
358
+
359
+ if @pgt_url
360
+ pgt = generate_proxy_granting_ticket(@pgt_url, t)
361
+ @pgtiou = pgt.iou if pgt
362
+ end
363
+
364
+ @extra_attributes = t.ticket_granting_ticket.extra_attributes || {}
365
+ end
366
+ $LOG.error @error
367
+ @status = response_status_from_error(@error) if @error
368
+
369
+ render :proxy_validate
370
+ end
371
+ end
372
+
373
+ class Proxy < R '/proxy'
374
+ include CASServer::CAS
375
+
376
+ # 2.7
377
+ def get
378
+ CASServer::Utils::log_controller_action(self.class, @input)
379
+
380
+ # required
381
+ @ticket = @input['pgt']
382
+ @target_service = @input['targetService']
383
+
384
+ pgt, @error = validate_proxy_granting_ticket(@ticket)
385
+ @success = pgt && !@error
386
+
387
+ if @success
388
+ @pt = generate_proxy_ticket(@target_service, pgt)
389
+ end
390
+
391
+ @status = response_status_from_error(@error) if @error
392
+
393
+ render :proxy
394
+ end
395
+ end
396
+
397
+ # Controller for obtaining login tickets.
398
+ # This is useful when you want to build a custom login form located on a
399
+ # remote server. Your form will have to include a valid login ticket
400
+ # value, and this can be fetched from the CAS server using this controller'
401
+ # POST method.
402
+ class LoginTicketDispenser < R '/loginTicket'
403
+ include CASServer::CAS
404
+
405
+ def get
406
+ CASServer::Utils::log_controller_action(self.class, @input)
407
+ $LOG.error("Tried to use login ticket dispenser with get method!")
408
+ @status = 422
409
+ "To generate a login ticket, you must make a POST request."
410
+ end
411
+
412
+ # Renders a page with a login ticket (and only the login ticket)
413
+ # in the response body.
414
+ def post
415
+ CASServer::Utils::log_controller_action(self.class, @input)
416
+ lt = generate_login_ticket
417
+
418
+ $LOG.debug("Dispensing login ticket #{lt} to host #{(env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_HOST'] || env['REMOTE_ADDR']).inspect}")
419
+
420
+ @lt = lt.ticket
421
+
422
+ @lt
423
+ end
424
+ end
425
+
426
+ class Themes < R '/themes/(.+)'
427
+ MIME_TYPES = {'.css' => 'text/css', '.js' => 'text/javascript',
428
+ '.jpg' => 'image/jpeg'}
429
+ PATH = CASServer::Conf.themes_dir || File.expand_path(File.dirname(__FILE__))+'/../themes'
430
+
431
+ def get(path)@headers['Content-Type'] = MIME_TYPES[path[/\.\w+$/, 0]] || "text/plain"
432
+ unless path.include? ".." # prevent directory traversal attacks
433
+ @headers['X-Sendfile'] = "#{PATH}/#{path}"
434
+ else
435
+ @status = "403"
436
+ "403 - Invalid path"
437
+ end
438
+ end
439
+ end
440
+
441
+ def response_status_from_error(error)
442
+ case error.code.to_s
443
+ when /^INVALID_/, 'BAD_PGT'
444
+ 422
445
+ when 'INTERNAL_ERROR'
446
+ 500
447
+ else
448
+ 500
449
+ end
450
+ end
451
+ module_function :response_status_from_error
452
+ end