wmernagh-rubycas-server 0.6.99.336

Sign up to get free protection for your applications and to get access to all the features.
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/hoe.rb +76 -0
  11. data/config/requirements.rb +15 -0
  12. data/config.example.yml +442 -0
  13. data/custom_views.example.rb +11 -0
  14. data/lib/casserver/authenticators/active_directory_ldap.rb +11 -0
  15. data/lib/casserver/authenticators/base.rb +48 -0
  16. data/lib/casserver/authenticators/client_certificate.rb +46 -0
  17. data/lib/casserver/authenticators/ldap.rb +138 -0
  18. data/lib/casserver/authenticators/ntlm.rb +88 -0
  19. data/lib/casserver/authenticators/open_id.rb +22 -0
  20. data/lib/casserver/authenticators/sql.rb +102 -0
  21. data/lib/casserver/authenticators/sql_encrypted.rb +75 -0
  22. data/lib/casserver/authenticators/sql_md5.rb +19 -0
  23. data/lib/casserver/authenticators/test.rb +19 -0
  24. data/lib/casserver/cas.rb +308 -0
  25. data/lib/casserver/conf.rb +112 -0
  26. data/lib/casserver/controllers.rb +452 -0
  27. data/lib/casserver/environment.rb +26 -0
  28. data/lib/casserver/models.rb +218 -0
  29. data/lib/casserver/postambles.rb +174 -0
  30. data/lib/casserver/utils.rb +30 -0
  31. data/lib/casserver/version.rb +9 -0
  32. data/lib/casserver/views.rb +243 -0
  33. data/lib/casserver.rb +111 -0
  34. data/lib/rubycas-server/version.rb +1 -0
  35. data/lib/rubycas-server.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,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
@@ -0,0 +1,26 @@
1
+ $: << File.dirname(File.expand_path(__FILE__))
2
+
3
+ # Try to load local version of Picnic if possible (for development purposes)
4
+ $: << File.dirname(File.expand_path(__FILE__))+"/../../../picnic/lib"
5
+ $: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/picnic/lib"
6
+
7
+ begin
8
+ require 'picnic'
9
+ rescue LoadError => e
10
+ # make sure that the LoadError was about picnic and not something else
11
+ raise e unless e.to_s =~ /picnic/
12
+
13
+ require 'rubygems'
14
+
15
+ # make things backwards-compatible for rubygems < 0.9.0
16
+ unless Object.method_defined? :gem
17
+ alias gem require_gem
18
+ end
19
+
20
+ gem 'picnic'
21
+
22
+ require 'picnic'
23
+ end
24
+
25
+ # used for serializing user extra_attributes (see #service_validate in views.rb)
26
+ require 'yaml'
@@ -0,0 +1,218 @@
1
+ require 'camping/db'
2
+
3
+ module CASServer::Models
4
+
5
+ module Consumable
6
+ def consume!
7
+ self.consumed = Time.now
8
+ self.save!
9
+ end
10
+ end
11
+
12
+ class Ticket < Base
13
+ def to_s
14
+ ticket
15
+ end
16
+
17
+ def self.cleanup_expired(expiry_time)
18
+ transaction do
19
+ conditions = ["created_on < ?", Time.now - expiry_time]
20
+ expired_tickets_count = count(:conditions => conditions)
21
+
22
+ $LOG.debug("Destroying #{expired_tickets_count} expired #{self.name.split('::').last}"+
23
+ "#{'s' if expired_tickets_count > 1}.") if expired_tickets_count > 0
24
+
25
+ destroy_all(conditions)
26
+ end
27
+ end
28
+ end
29
+
30
+ class LoginTicket < Ticket
31
+ set_table_name 'casserver_lt'
32
+ include Consumable
33
+ end
34
+
35
+ class ServiceTicket < Ticket
36
+ set_table_name 'casserver_st'
37
+ include Consumable
38
+
39
+ belongs_to :ticket_granting_ticket, :foreign_key => :tgt_id
40
+
41
+ def matches_service?(service)
42
+ CASServer::CAS.clean_service_url(self.service) ==
43
+ CASServer::CAS.clean_service_url(service)
44
+ end
45
+ end
46
+
47
+ class ProxyTicket < ServiceTicket
48
+ belongs_to :proxy_granting_ticket
49
+ end
50
+
51
+ class TicketGrantingTicket < Ticket
52
+ set_table_name 'casserver_tgt'
53
+
54
+ serialize :extra_attributes
55
+
56
+ has_many :service_tickets, :foreign_key => :tgt_id
57
+ end
58
+
59
+ class ProxyGrantingTicket < Ticket
60
+ set_table_name 'casserver_pgt'
61
+ belongs_to :service_ticket
62
+ has_many :proxy_tickets, :dependent => :destroy
63
+ end
64
+
65
+ class Error
66
+ attr_reader :code, :message
67
+
68
+ def initialize(code, message)
69
+ @code = code
70
+ @message = message
71
+ end
72
+
73
+ def to_s
74
+ message
75
+ end
76
+ end
77
+
78
+ class CreateCASServer < V 0.1
79
+ def self.up
80
+ if ActiveRecord::Base.connection.table_alias_length > 30
81
+ $LOG.info("Creating database with long table names...")
82
+
83
+ create_table :casserver_login_tickets, :force => true do |t|
84
+ t.column :ticket, :string, :null => false
85
+ t.column :created_on, :timestamp, :null => false
86
+ t.column :consumed, :datetime, :null => true
87
+ t.column :client_hostname, :string, :null => false
88
+ end
89
+
90
+ create_table :casserver_service_tickets, :force => true do |t|
91
+ t.column :ticket, :string, :null => false
92
+ t.column :service, :string, :null => false
93
+ t.column :created_on, :timestamp, :null => false
94
+ t.column :consumed, :datetime, :null => true
95
+ t.column :client_hostname, :string, :null => false
96
+ t.column :username, :string, :null => false
97
+ t.column :type, :string, :null => false
98
+ t.column :proxy_granting_ticket_id, :integer, :null => true
99
+ end
100
+
101
+ create_table :casserver_ticket_granting_tickets, :force => true do |t|
102
+ t.column :ticket, :string, :null => false
103
+ t.column :created_on, :timestamp, :null => false
104
+ t.column :client_hostname, :string, :null => false
105
+ t.column :username, :string, :null => false
106
+ end
107
+
108
+ create_table :casserver_proxy_granting_tickets, :force => true do |t|
109
+ t.column :ticket, :string, :null => false
110
+ t.column :created_on, :timestamp, :null => false
111
+ t.column :client_hostname, :string, :null => false
112
+ t.column :iou, :string, :null => false
113
+ t.column :service_ticket_id, :integer, :null => false
114
+ end
115
+ end
116
+ end
117
+
118
+ def self.down
119
+ if ActiveRecord::Base.connection.table_alias_length > 30
120
+ drop_table :casserver_proxy_granting_tickets
121
+ drop_table :casserver_ticket_granting_tickets
122
+ drop_table :casserver_service_tickets
123
+ drop_table :casserver_login_tickets
124
+ end
125
+ end
126
+ end
127
+
128
+ # Oracle table names cannot exceed 30 chars...
129
+ # See http://code.google.com/p/rubycas-server/issues/detail?id=15
130
+ class ShortenTableNames < V 0.5
131
+ def self.up
132
+ if ActiveRecord::Base.connection.table_alias_length > 30
133
+ $LOG.info("Shortening table names")
134
+ rename_table :casserver_login_tickets, :casserver_lt
135
+ rename_table :casserver_service_tickets, :casserver_st
136
+ rename_table :casserver_ticket_granting_tickets, :casserver_tgt
137
+ rename_table :casserver_proxy_granting_tickets, :casserver_pgt
138
+ else
139
+ create_table :casserver_lt, :force => true do |t|
140
+ t.column :ticket, :string, :null => false
141
+ t.column :created_on, :timestamp, :null => false
142
+ t.column :consumed, :datetime, :null => true
143
+ t.column :client_hostname, :string, :null => false
144
+ end
145
+
146
+ create_table :casserver_st, :force => true do |t|
147
+ t.column :ticket, :string, :null => false
148
+ t.column :service, :string, :null => false
149
+ t.column :created_on, :timestamp, :null => false
150
+ t.column :consumed, :datetime, :null => true
151
+ t.column :client_hostname, :string, :null => false
152
+ t.column :username, :string, :null => false
153
+ t.column :type, :string, :null => false
154
+ t.column :proxy_granting_ticket_id, :integer, :null => true
155
+ end
156
+
157
+ create_table :casserver_tgt, :force => true do |t|
158
+ t.column :ticket, :string, :null => false
159
+ t.column :created_on, :timestamp, :null => false
160
+ t.column :client_hostname, :string, :null => false
161
+ t.column :username, :string, :null => false
162
+ end
163
+
164
+ create_table :casserver_pgt, :force => true do |t|
165
+ t.column :ticket, :string, :null => false
166
+ t.column :created_on, :timestamp, :null => false
167
+ t.column :client_hostname, :string, :null => false
168
+ t.column :iou, :string, :null => false
169
+ t.column :service_ticket_id, :integer, :null => false
170
+ end
171
+ end
172
+ end
173
+
174
+ def self.down
175
+ if ActiveRecord::Base.connection.table_alias_length > 30
176
+ rename_table :casserver_lt, :cassserver_login_tickets
177
+ rename_table :casserver_st, :casserver_service_tickets
178
+ rename_table :casserver_tgt, :casserver_ticket_granting_tickets
179
+ rename_table :casserver_pgt, :casserver_proxy_granting_tickets
180
+ else
181
+ drop_table :casserver_pgt
182
+ drop_table :casserver_tgt
183
+ drop_table :casserver_st
184
+ drop_table :casserver_lt
185
+ end
186
+ end
187
+ end
188
+
189
+ class AddTgtToSt < V 0.7
190
+ def self.up
191
+ add_column :casserver_st, :tgt_id, :integer, :null => true
192
+ end
193
+
194
+ def self.down
195
+ remove_column :casserver_st, :tgt_id, :integer
196
+ end
197
+ end
198
+
199
+ class ChangeServiceToText < V 0.71
200
+ def self.up
201
+ change_column :casserver_st, :service, :text
202
+ end
203
+
204
+ def self.down
205
+ change_column :casserver_st, :service, :string
206
+ end
207
+ end
208
+
209
+ class AddExtraAttributes < V 0.72
210
+ def self.up
211
+ add_column :casserver_tgt, :extra_attributes, :text
212
+ end
213
+
214
+ def self.down
215
+ remove_column :casserver_tgt, :extra_attributes
216
+ end
217
+ end
218
+ end