synapse-rubycas-server 1.1.4.pre → 1.1.4

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 (118) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +2 -21
  3. data/Rakefile +0 -6
  4. data/bin/rubycas-server +26 -12
  5. data/config.ru +1 -2
  6. data/config/unicorn.rb +88 -0
  7. data/lib/casserver.rb +1 -2
  8. data/lib/casserver/cas.rb +4 -330
  9. data/lib/casserver/server.rb +8 -4
  10. data/lib/casserver/views/_login_form.erb +36 -15
  11. data/lib/casserver/views/layout.erb +4 -40
  12. data/lib/casserver/views/login.erb +27 -13
  13. data/locales/en.yml +3 -17
  14. data/public/themes/cas.css +0 -2
  15. data/public/themes/simple/theme.css +28 -0
  16. data/spec/casserver_spec.rb +1 -1
  17. data/spec/config/default_config.yml +1 -1
  18. metadata +12 -146
  19. data/bin/cap +0 -16
  20. data/bin/capify +0 -16
  21. data/bin/foreman +0 -16
  22. data/bin/lessc +0 -16
  23. data/bin/rackup +0 -16
  24. data/bin/rake2thor +0 -16
  25. data/bin/therubyracer +0 -16
  26. data/bin/thor +0 -16
  27. data/bin/tilt +0 -16
  28. data/bin/unicorn +0 -16
  29. data/bin/unicorn_rails +0 -16
  30. data/config/deploy.rb +0 -36
  31. data/config/deploy/production.rb +0 -4
  32. data/config/deploy/staging.rb +0 -4
  33. data/config/recipes/base.rb +0 -8
  34. data/config/recipes/git.rb +0 -10
  35. data/config/recipes/nginx.rb +0 -28
  36. data/config/recipes/puma.rb +0 -38
  37. data/config/recipes/rubycas.rb +0 -11
  38. data/config/recipes/templates/nginx.erb +0 -43
  39. data/config/recipes/templates/puma.erb +0 -13
  40. data/config/recipes/templates/rubycas.erb +0 -114
  41. data/config/unicorn/development.rb +0 -14
  42. data/config/unicorn/production.rb +0 -14
  43. data/config/unicorn/staging.rb +0 -14
  44. data/public/app.css +0 -9641
  45. data/public/assets/fontawesome-webfont.eot +0 -0
  46. data/public/assets/fontawesome-webfont.svg +0 -255
  47. data/public/assets/fontawesome-webfont.ttf +0 -0
  48. data/public/assets/fontawesome-webfont.woff +0 -0
  49. data/public/assets/gothamhtf-black-webfont.eot +0 -0
  50. data/public/assets/gothamhtf-black-webfont.svg +0 -241
  51. data/public/assets/gothamhtf-black-webfont.ttf +0 -0
  52. data/public/assets/gothamhtf-black-webfont.woff +0 -0
  53. data/public/assets/gothamhtf-blackitalic-webfont.eot +0 -0
  54. data/public/assets/gothamhtf-blackitalic-webfont.svg +0 -241
  55. data/public/assets/gothamhtf-blackitalic-webfont.ttf +0 -0
  56. data/public/assets/gothamhtf-blackitalic-webfont.woff +0 -0
  57. data/public/assets/gothamhtf-bold-webfont.eot +0 -0
  58. data/public/assets/gothamhtf-bold-webfont.svg +0 -241
  59. data/public/assets/gothamhtf-bold-webfont.ttf +0 -0
  60. data/public/assets/gothamhtf-bold-webfont.woff +0 -0
  61. data/public/assets/gothamhtf-bolditalic-webfont.eot +0 -0
  62. data/public/assets/gothamhtf-bolditalic-webfont.svg +0 -241
  63. data/public/assets/gothamhtf-bolditalic-webfont.ttf +0 -0
  64. data/public/assets/gothamhtf-bolditalic-webfont.woff +0 -0
  65. data/public/assets/gothamhtf-book-webfont.eot +0 -0
  66. data/public/assets/gothamhtf-book-webfont.svg +0 -241
  67. data/public/assets/gothamhtf-book-webfont.ttf +0 -0
  68. data/public/assets/gothamhtf-book-webfont.woff +0 -0
  69. data/public/assets/gothamhtf-bookitalic-webfont.eot +0 -0
  70. data/public/assets/gothamhtf-bookitalic-webfont.svg +0 -241
  71. data/public/assets/gothamhtf-bookitalic-webfont.ttf +0 -0
  72. data/public/assets/gothamhtf-bookitalic-webfont.woff +0 -0
  73. data/public/assets/gothamhtf-light-webfont.eot +0 -0
  74. data/public/assets/gothamhtf-light-webfont.svg +0 -241
  75. data/public/assets/gothamhtf-light-webfont.ttf +0 -0
  76. data/public/assets/gothamhtf-light-webfont.woff +0 -0
  77. data/public/assets/gothamhtf-lightitalic-webfont.eot +0 -0
  78. data/public/assets/gothamhtf-lightitalic-webfont.svg +0 -241
  79. data/public/assets/gothamhtf-lightitalic-webfont.ttf +0 -0
  80. data/public/assets/gothamhtf-lightitalic-webfont.woff +0 -0
  81. data/public/assets/gothamhtf-medium-webfont.eot +0 -0
  82. data/public/assets/gothamhtf-medium-webfont.svg +0 -241
  83. data/public/assets/gothamhtf-medium-webfont.ttf +0 -0
  84. data/public/assets/gothamhtf-medium-webfont.woff +0 -0
  85. data/public/assets/gothamhtf-thin-webfont.eot +0 -0
  86. data/public/assets/gothamhtf-thin-webfont.svg +0 -241
  87. data/public/assets/gothamhtf-thin-webfont.ttf +0 -0
  88. data/public/assets/gothamhtf-thin-webfont.woff +0 -0
  89. data/public/assets/gothamhtf-thinitalic-webfont.eot +0 -0
  90. data/public/assets/gothamhtf-thinitalic-webfont.svg +0 -241
  91. data/public/assets/gothamhtf-thinitalic-webfont.ttf +0 -0
  92. data/public/assets/gothamhtf-thinitalic-webfont.woff +0 -0
  93. data/public/assets/gothamhtf-ultra-webfont.eot +0 -0
  94. data/public/assets/gothamhtf-ultra-webfont.svg +0 -241
  95. data/public/assets/gothamhtf-ultra-webfont.ttf +0 -0
  96. data/public/assets/gothamhtf-ultra-webfont.woff +0 -0
  97. data/public/assets/gothamhtf-ultraitalic-webfont.eot +0 -0
  98. data/public/assets/gothamhtf-ultraitalic-webfont.svg +0 -241
  99. data/public/assets/gothamhtf-ultraitalic-webfont.ttf +0 -0
  100. data/public/assets/gothamhtf-ultraitalic-webfont.woff +0 -0
  101. data/public/assets/gothamhtf-xlight-webfont.eot +0 -0
  102. data/public/assets/gothamhtf-xlight-webfont.svg +0 -241
  103. data/public/assets/gothamhtf-xlight-webfont.ttf +0 -0
  104. data/public/assets/gothamhtf-xlight-webfont.woff +0 -0
  105. data/public/assets/gothamhtf-xlightitalic-webfont.eot +0 -0
  106. data/public/assets/gothamhtf-xlightitalic-webfont.svg +0 -241
  107. data/public/assets/gothamhtf-xlightitalic-webfont.ttf +0 -0
  108. data/public/assets/gothamhtf-xlightitalic-webfont.woff +0 -0
  109. data/public/css/app.css +0 -190
  110. data/public/css/bootstrap-responsive.min.css +0 -9
  111. data/public/css/bootstrap.min.css +0 -9
  112. data/public/img/glyphicons-halflings-white.png +0 -0
  113. data/public/img/glyphicons-halflings.png +0 -0
  114. data/public/js/app.js +0 -0
  115. data/public/js/bootstrap.min.js +0 -6
  116. data/public/js/jquery-1.8.0.js +0 -9227
  117. data/public/themes/app.css +0 -4652
  118. data/rubycas-server.gemspec +0 -62
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ODQxZTBlYWM3YTJkNWIwMTYyNTc3OGM4OGNjMmJkYjE5YzA5MjVkZQ==
5
+ data.tar.gz: !binary |-
6
+ OGY5NmJjMGIyOGNjOGQ1NDMxODE0YTRjMTljNTRkMTM3NmU5Y2VkZg==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ ZmQ3MjNmNGE0MjY1NGU4ZDk1ZTBkNDg0Nzk3YjNmODRhNTc3ODQxMWQyNTU1
10
+ NjgyNGQ2NDI1YzM3YTIzODAxZjZjY2Q5NmVkNDBhNjUyNTJiYWQ1NWY3NmM5
11
+ Yjg1N2FjNTMyNDIxMTM2ZDA3M2M4ZmE0ZWNkNTRlY2I4N2U5MDE=
12
+ data.tar.gz: !binary |-
13
+ NzQzMjMyN2YyMjI0MTEyNGRkM2U3YzRkNTRjOGFiNDI0Y2YxMTNjZTYxMmYy
14
+ NDlmMjAyYjUxODc5ZjVlZjhjYmNlMTQ0MjI0ZDJlNmI1OGI3NjgzYjUzMWJl
15
+ ZDZhMzEwNTlhOGI2ODAwOWEyZGI0ZGE0OWUzNTM3NmRmNzNiYzk=
data/Gemfile CHANGED
@@ -1,29 +1,10 @@
1
1
  source "http://rubygems.org"
2
+ gemspec
2
3
 
3
- gem 'appraisal'
4
- gem 'synapse-rubycas-server', "1.1.4"
5
- gem "mysql2"
6
- gem 'activerecord-mysql2-adapter'
7
- gem "activerecord"
8
- gem "activesupport"
9
- gem "sinatra"
10
- gem "sinatra-r18n"
11
- gem 'puma', "~> 2.1.1"
12
- gem "newrelic_rpm"
13
- gem "rake", "~> 10.0.4"
14
-
15
- group :development do
16
- gem 'capistrano-ext'
17
- gem 'capistrano_colors'
18
- gem 'capistrano-rbenv'
19
- gem 'capistrano-unicorn', :require => false
20
- gem 'foreman'
21
- gem 'hipchat'
22
- end
23
4
 
24
5
  # Gems for authenticators
25
6
  group :ldap do
26
- gem "net-ldap", "~> 0.1.1"
7
+ gem "net-ldap", "~> 0.1.1"
27
8
  end
28
9
 
29
10
  group :active_resource do
data/Rakefile CHANGED
@@ -1,9 +1,3 @@
1
1
  require 'appraisal'
2
2
  Dir['tasks/**/*.rake'].each { |rake| load rake }
3
3
  task :default => :spec
4
-
5
-
6
- desc "Open an irb session preloaded with this library"
7
- task :console do
8
- sh "irb -rubygems -I lib -r casserver.rb"
9
- end
data/bin/rubycas-server CHANGED
@@ -1,16 +1,30 @@
1
- #!/usr/bin/env ruby-local-exec
2
- #
3
- # This file was generated by Bundler.
4
- #
5
- # The application 'rubycas-server' is installed as part of a gem, and
6
- # this file is here to facilitate running it.
7
- #
1
+ #!/usr/bin/env ruby
8
2
 
9
- require 'pathname'
10
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
- Pathname.new(__FILE__).realpath)
3
+ # Enables UTF-8 compatibility in ruby 1.8.
4
+ $KCODE = 'u' if RUBY_VERSION < '1.9'
12
5
 
13
6
  require 'rubygems'
14
- require 'bundler/setup'
15
7
 
16
- load Gem.bin_path('rubycas-server', 'rubycas-server')
8
+ $:.unshift File.dirname(__FILE__) + "/../lib"
9
+
10
+ if ARGV.join.match('--debugger')
11
+ require 'ruby-debug'
12
+ puts
13
+ puts "=> Debugger Enabled"
14
+ end
15
+
16
+ if ARGV.join.match('-c')
17
+ c = ARGV.join.match(/-c\s*([^\s]+)/)
18
+ if (c && c[1])
19
+ ENV['CONFIG_FILE'] = c[1]
20
+ puts
21
+ puts "=> Using custom config file #{ENV['CONFIG_FILE'].inspect}"
22
+ else
23
+ $stderr.puts("To specify a custom config file use `rubycas-server -c path/to/config_file_name.yml`.")
24
+ exit
25
+ end
26
+ end
27
+
28
+ require 'casserver'
29
+
30
+ CASServer::Server.run!
data/config.ru CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'rubygems'
2
- require 'bundler'
3
- Bundler.require
2
+ require 'bundler/setup'
4
3
 
5
4
  $:.unshift "#{File.dirname(__FILE__)}/lib"
6
5
  require "casserver"
data/config/unicorn.rb ADDED
@@ -0,0 +1,88 @@
1
+ # Sample configuration file for Unicorn (not Rack)
2
+ #
3
+ # See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
4
+ # documentation.
5
+ SINATRA_ROOT = `pwd`.strip
6
+
7
+ # Use at least one worker per core if you're on a dedicated server,
8
+ # more will usually help for _short_ waits on databases/caches.
9
+ worker_processes 3
10
+
11
+ # Help ensure your application will always spawn in the symlinked
12
+ # "current" directory that Capistrano sets up.
13
+ working_directory SINATRA_ROOT # available in 0.94.0+
14
+
15
+ # listen on both a Unix domain socket and a TCP port,
16
+ # we use a shorter backlog for quicker failover when busy
17
+ # listen "/tmp/.sock", :backlog => 64
18
+ listen 18889, :tcp_nopush => true
19
+
20
+ # nuke workers after 30 seconds instead of 60 seconds (the default)
21
+ timeout 30
22
+
23
+ # feel free to point this anywhere accessible on the filesystem
24
+
25
+ pid "#{SINATRA_ROOT}/tmp/pids/unicorn.pid"
26
+
27
+ # relative_path "/test_platform"
28
+ # some applications/frameworks log to stderr or stdout, so prevent
29
+ # them from going to /dev/null when daemonized here:
30
+ stderr_path "#{SINATRA_ROOT}/log/unicorn.stderr.log"
31
+ stdout_path "#{SINATRA_ROOT}/log/unicorn.stdout.log"
32
+
33
+ # combine REE with "preload_app true" for memory savings
34
+ # http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
35
+ preload_app false
36
+ GC.respond_to?(:copy_on_write_friendly=) and
37
+ GC.copy_on_write_friendly = true
38
+
39
+ before_fork do |server, worker|
40
+ # the following is highly recomended for Rails + "preload_app true"
41
+ # as there's no need for the master process to hold a connection
42
+ # defined?(ActiveRecord::Base) and
43
+ # ActiveRecord::Base.connection.disconnect!
44
+
45
+ # The following is only recommended for memory/DB-constrained
46
+ # installations. It is not needed if your system can house
47
+ # twice as many worker_processes as you have configured.
48
+ #
49
+ # # This allows a new master process to incrementally
50
+ # # phase out the old master process with SIGTTOU to avoid a
51
+ # # thundering herd (especially in the "preload_app false" case)
52
+ # # when doing a transparent upgrade. The last worker spawned
53
+ # # will then kill off the old master process with a SIGQUIT.
54
+ old_pid = "#{server.config[:pid]}.oldbin"
55
+
56
+ puts 'pid:'
57
+ puts '-------------------'
58
+ puts server.pid
59
+ puts old_pid
60
+ puts '---------------------'
61
+
62
+ if old_pid != server.pid
63
+ begin
64
+ sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
65
+ Process.kill(sig, File.read(old_pid).to_i)
66
+ rescue Errno::ENOENT, Errno::ESRCH
67
+ end
68
+ end
69
+ #
70
+ # # *optionally* throttle the master from forking too quickly by sleeping
71
+ sleep 1
72
+ end
73
+
74
+ after_fork do |server, worker|
75
+ # per-process listener ports for debugging/admin/migrations
76
+ # addr = "127.0.0.1:#{9293 + worker.nr}"
77
+ # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
78
+
79
+ # the following is *required* for Rails + "preload_app true",
80
+ # defined?(ActiveRecord::Base) and
81
+ # ActiveRecord::Base.establish_connection
82
+
83
+ # if preload_app is true, then you may also want to check and
84
+ # restart any other shared sockets/descriptors such as Memcached,
85
+ # and Redis. TokyoCabinet file handles are safe to reuse
86
+ # between any number of forked children (assuming your kernel
87
+ # correctly implements pread()/pwrite() system calls)
88
+ end
data/lib/casserver.rb CHANGED
@@ -3,10 +3,9 @@ module CASServer; end
3
3
  require 'active_record'
4
4
  require 'active_support'
5
5
  require 'sinatra/base'
6
+ require 'casserver/core_ext/directory_user'
6
7
  require 'builder' # for XML views
7
8
  require 'logger'
8
- require 'net/ldap'
9
- require 'casserver/core_ext/directory_user'
10
9
  $LOG = Logger.new(STDOUT)
11
10
 
12
11
  require 'casserver/authenticators/base'
data/lib/casserver/cas.rb CHANGED
@@ -34,332 +34,6 @@ module CASServer::CAS
34
34
  tgt.ticket = "TGC-" + String.random
35
35
  tgt.username = username
36
36
  tgt.extra_attributes = extra_attributes
37
- tgt.expires = (Time.now + 2.weeks).strftime("%a, %d-%b-%Y %H:%M:%S GMT")
38
- tgt.client_hostname = @env['HTTP_X_FORWARDED_FOR'] || @env['REMOTE_HOST'] || @env['REMOTE_ADDR']
39
- tgt.save!
40
- $LOG.debug("Generated ticket granting ticket '#{tgt.ticket}' for user" +
41
- " '#{tgt.username}' at '#{tgt.client_hostname}' with expiration of '#{tgt.expires}'" +
42
- (extra_attributes.blank? ? "" : " with extra attributes #{extra_attributes.inspect}"))
43
- tgt
44
- end
45
-
46
- def generate_service_ticket(service, username, tgt)
47
- # 3.1 (service ticket)
48
- st = ServiceTicket.new
49
- st.ticket = "ST-" + String.random
50
- st.service = service
51
- st.username = username
52
- st.granted_by_tgt_id = tgt.id
53
- st.client_hostname = @env['HTTP_X_FORWARDED_FOR'] || @env['REMOTE_HOST'] || @env['REMOTE_ADDR']
54
- st.save!
55
- $LOG.debug("Generated service ticket '#{st.ticket}' for service '#{st.service}'" +
56
- " for user '#{st.username}' at '#{st.client_hostname}'")
57
- st
58
- end
59
-
60
- def generate_proxy_ticket(target_service, pgt)
61
- # 3.2 (proxy ticket)
62
- pt = ProxyTicket.new
63
- pt.ticket = "PT-" + String.random
64
- pt.service = target_service
65
- pt.username = pgt.service_ticket.username
66
- pt.granted_by_pgt_id = pgt.id
67
- pt.granted_by_tgt_id = pgt.service_ticket.granted_by_tgt_id
68
- pt.client_hostname = @env['HTTP_X_FORWARDED_FOR'] || @env['REMOTE_HOST'] || @env['REMOTE_ADDR']
69
- pt.save!
70
- $LOG.debug("Generated proxy ticket '#{pt.ticket}' for target service '#{pt.service}'" +
71
- " for user '#{pt.username}' at '#{pt.client_hostname}' using proxy-granting" +
72
- " ticket '#{pgt.ticket}'")
73
- pt
74
- end
75
-
76
- def generate_proxy_granting_ticket(pgt_url, st)
77
- uri = URI.parse(pgt_url)
78
- https = Net::HTTP.new(uri.host,uri.port)
79
- https.use_ssl = true
80
-
81
- # Here's what's going on here:
82
- #
83
- # 1. We generate a ProxyGrantingTicket (but don't store it in the database just yet)
84
- # 2. Deposit the PGT and it's associated IOU at the proxy callback URL.
85
- # 3. If the proxy callback URL responds with HTTP code 200, store the PGT and return it;
86
- # otherwise don't save it and return nothing.
87
- #
88
- https.start do |conn|
89
- path = uri.path.empty? ? '/' : uri.path
90
- path += '?' + uri.query unless (uri.query.nil? || uri.query.empty?)
91
-
92
- pgt = ProxyGrantingTicket.new
93
- pgt.ticket = "PGT-" + String.random(60)
94
- pgt.iou = "PGTIOU-" + String.random(57)
95
- pgt.service_ticket_id = st.id
96
- pgt.client_hostname = @env['HTTP_X_FORWARDED_FOR'] || @env['REMOTE_HOST'] || @env['REMOTE_ADDR']
97
-
98
- # FIXME: The CAS protocol spec says to use 'pgt' as the parameter, but in practice
99
- # the JA-SIG and Yale server implementations use pgtId. We'll go with the
100
- # in-practice standard.
101
- path += (uri.query.nil? || uri.query.empty? ? '?' : '&') + "pgtId=#{pgt.ticket}&pgtIou=#{pgt.iou}"
102
-
103
- response = conn.request_get(path)
104
- # TODO: follow redirects... 2.5.4 says that redirects MAY be followed
105
- # NOTE: The following response codes are valid according to the JA-SIG implementation even without following redirects
106
-
107
- if %w(200 202 301 302 304).include?(response.code)
108
- # 3.4 (proxy-granting ticket IOU)
109
- pgt.save!
110
- $LOG.debug "PGT generated for pgt_url '#{pgt_url}': #{pgt.inspect}"
111
- pgt
112
- else
113
- $LOG.warn "PGT callback server responded with a bad result code '#{response.code}'. PGT will not be stored."
114
- nil
115
- end
116
- end
117
- end
118
-
119
- def validate_login_ticket(ticket)
120
- $LOG.debug("Validating login ticket '#{ticket}'")
121
-
122
- success = false
123
- if ticket.nil?
124
- error = t.error.no_login_ticket
125
- $LOG.warn "Missing login ticket."
126
- elsif lt = LoginTicket.find_by_ticket(ticket)
127
- if lt.consumed?
128
- error = t.error.login_ticket_already_used
129
- $LOG.warn "Login ticket '#{ticket}' previously used up"
130
- elsif Time.now - lt.created_on < settings.config[:maximum_unused_login_ticket_lifetime]
131
- $LOG.info "Login ticket '#{ticket}' successfully validated"
132
- else
133
- error = t.error.login_timeout
134
- $LOG.warn "Expired login ticket '#{ticket}'"
135
- end
136
- else
137
- error = t.error.invalid_login_ticket
138
- $LOG.warn "Invalid login ticket '#{ticket}'"
139
- end
140
-
141
- lt.consume! if lt
142
-
143
- error
144
- end
145
-
146
- def validate_ticket_granting_ticket(ticket)
147
- $LOG.debug("Validating ticket granting ticket '#{ticket}'")
148
-
149
- if ticket.nil?
150
- error = "No ticket granting ticket given."
151
- $LOG.debug error
152
- elsif tgt = TicketGrantingTicket.find_by_ticket(ticket)
153
- if settings.config[:maximum_session_lifetime] && Time.now - tgt.created_on > settings.config[:maximum_session_lifetime]
154
- tgt.destroy
155
- error = "Your session has expired. Please log in again."
156
- $LOG.info "Ticket granting ticket '#{ticket}' for user '#{tgt.username}' expired."
157
- else
158
- $LOG.info "Ticket granting ticket '#{ticket}' for user '#{tgt.username}' successfully validated."
159
- end
160
- else
161
- error = "Invalid ticket granting ticket '#{ticket}' (no matching ticket found in the database)."
162
- $LOG.warn(error)
163
- end
164
-
165
- [tgt, error]
166
- end
167
-
168
- def validate_service_ticket(service, ticket, allow_proxy_tickets = false)
169
- $LOG.debug "Validating service/proxy ticket '#{ticket}' for service '#{service}'"
170
-
171
- if service.nil? or ticket.nil?
172
- error = Error.new(:INVALID_REQUEST, "Ticket or service parameter was missing in the request.")
173
- $LOG.warn "#{error.code} - #{error.message}"
174
- elsif st = ServiceTicket.find_by_ticket(ticket)
175
- if st.consumed?
176
- error = Error.new(:INVALID_TICKET, "Ticket '#{ticket}' has already been used up.")
177
- $LOG.warn "#{error.code} - #{error.message}"
178
- elsif st.kind_of?(CASServer::Model::ProxyTicket) && !allow_proxy_tickets
179
- error = Error.new(:INVALID_TICKET, "Ticket '#{ticket}' is a proxy ticket, but only service tickets are allowed here.")
180
- $LOG.warn "#{error.code} - #{error.message}"
181
- elsif Time.now - st.created_on > settings.config[:maximum_unused_service_ticket_lifetime]
182
- error = Error.new(:INVALID_TICKET, "Ticket '#{ticket}' has expired.")
183
- $LOG.warn "Ticket '#{ticket}' has expired."
184
- elsif !st.matches_service? service
185
- error = Error.new(:INVALID_SERVICE, "The ticket '#{ticket}' belonging to user '#{st.username}' is valid,"+
186
- " but the requested service '#{service}' does not match the service '#{st.service}' associated with this ticket.")
187
- $LOG.warn "#{error.code} - #{error.message}"
188
- else
189
- $LOG.info("Ticket '#{ticket}' for service '#{service}' for user '#{st.username}' successfully validated.")
190
- end
191
- else
192
- error = Error.new(:INVALID_TICKET, "Ticket '#{ticket}' not recognized.")
193
- $LOG.warn("#{error.code} - #{error.message}")
194
- end
195
-
196
- if st
197
- st.consume!
198
- end
199
-
200
-
201
- [st, error]
202
- end
203
-
204
- def validate_proxy_ticket(service, ticket)
205
- pt, error = validate_service_ticket(service, ticket, true)
206
-
207
- if pt.kind_of?(CASServer::Model::ProxyTicket) && !error
208
- if not pt.granted_by_pgt
209
- error = Error.new(:INTERNAL_ERROR, "Proxy ticket '#{pt}' belonging to user '#{pt.username}' is not associated with a proxy granting ticket.")
210
- elsif not pt.granted_by_pgt.service_ticket
211
- error = Error.new(:INTERNAL_ERROR, "Proxy granting ticket '#{pt.granted_by_pgt}'"+
212
- " (associated with proxy ticket '#{pt}' and belonging to user '#{pt.username}' is not associated with a service ticket.")
213
- end
214
- end
215
-
216
- [pt, error]
217
- end
218
-
219
- def validate_proxy_granting_ticket(ticket)
220
- if ticket.nil?
221
- error = Error.new(:INVALID_REQUEST, "pgt parameter was missing in the request.")
222
- $LOG.warn("#{error.code} - #{error.message}")
223
- elsif pgt = ProxyGrantingTicket.find_by_ticket(ticket)
224
- if pgt.service_ticket
225
- $LOG.info("Proxy granting ticket '#{ticket}' belonging to user '#{pgt.service_ticket.username}' successfully validated.")
226
- else
227
- error = Error.new(:INTERNAL_ERROR, "Proxy granting ticket '#{ticket}' is not associated with a service ticket.")
228
- $LOG.error("#{error.code} - #{error.message}")
229
- end
230
- else
231
- error = Error.new(:BAD_PGT, "Invalid proxy granting ticket '#{ticket}' (no matching ticket found in the database).")
232
- $LOG.warn("#{error.code} - #{error.message}")
233
- end
234
-
235
- [pgt, error]
236
- end
237
-
238
- # Takes an existing ServiceTicket object (presumably pulled from the database)
239
- # and sends a POST with logout information to the service that the ticket
240
- # was generated for.
241
- #
242
- # This makes possible the "single sign-out" functionality added in CAS 3.1.
243
- # See http://www.ja-sig.org/wiki/display/CASUM/Single+Sign+Out
244
- def send_logout_notification_for_service_ticket(st)
245
- uri = URI.parse(st.service)
246
- uri.path = '/' if uri.path.empty?
247
- time = Time.now
248
- rand = String.random
249
- path = uri.path
250
- req = Net::HTTP::Post.new(path)
251
- req.set_form_data('logoutRequest' => %{<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="#{rand}" Version="2.0" IssueInstant="#{time.rfc2822}">
252
- <saml:NameID></saml:NameID>
253
- <samlp:SessionIndex>#{st.ticket}</samlp:SessionIndex>
254
- </samlp:LogoutRequest>})
255
-
256
- begin
257
- http = Net::HTTP.new(uri.host, uri.port)
258
- http.use_ssl = true if uri.scheme =='https'
259
-
260
- http.start do |conn|
261
- response = conn.request(req)
262
- if response.kind_of? Net::HTTPSuccess
263
- $LOG.info "Logout notification successfully posted to #{st.service.inspect}."
264
- return true
265
- else
266
- $LOG.error "Service #{st.service.inspect} responed to logout notification with code '#{response.code}'!"
267
- return false
268
- end
269
- end
270
- rescue Exception => e
271
- $LOG.error "Failed to send logout notification to service #{st.service.inspect} due to #{e}"
272
- return false
273
- end
274
- end
275
-
276
- def service_uri_with_ticket(service, st)
277
- raise ArgumentError, "Second argument must be a ServiceTicket!" unless st.kind_of? CASServer::Model::ServiceTicket
278
-
279
- # This will choke with a URI::InvalidURIError if service URI is not properly URI-escaped...
280
- # This exception is handled further upstream (i.e. in the controller).
281
- service_uri = URI.parse(service)
282
-
283
- if service.include? "?"
284
- if service_uri.query.empty?
285
- query_separator = ""
286
- else
287
- query_separator = "&"
288
- end
289
- else
290
- query_separator = "?"
291
- end
292
-
293
- service_with_ticket = service + query_separator + "ticket=" + st.ticket
294
- service_with_ticket
295
- end
296
-
297
- # Strips CAS-related parameters from a service URL and normalizes it,
298
- # removing trailing / and ?. Also converts any spaces to +.
299
- #
300
- # For example, "http://google.com?ticket=12345" will be returned as
301
- # "http://google.com". Also, "http://google.com/" would be returned as
302
- # "http://google.com".
303
- #
304
- # Note that only the first occurance of each CAS-related parameter is
305
- # removed, so that "http://google.com?ticket=12345&ticket=abcd" would be
306
- # returned as "http://google.com?ticket=abcd".
307
- def clean_service_url(dirty_service)
308
- return dirty_service if dirty_service.blank?
309
- clean_service = dirty_service.dup
310
- ['service', 'ticket', 'gateway', 'renew'].each do |p|
311
- clean_service.sub!(Regexp.new("&?#{p}=[^&]*"), '')
312
- end
313
-
314
- clean_service.gsub!(/[\/\?&]$/, '') # remove trailing ?, /, or &
315
- clean_service.gsub!('?&', '?')
316
- clean_service.gsub!(' ', '+')
317
-
318
- $LOG.debug("Cleaned dirty service URL #{dirty_service.inspect} to #{clean_service.inspect}") if
319
- dirty_service != clean_service
320
-
321
- return clean_service
322
- end
323
- module_function :clean_service_url
324
-
325
- end
326
- require 'uri'
327
- require 'net/https'
328
-
329
- require 'casserver/model'
330
- require 'casserver/core_ext'
331
-
332
- # Encapsulates CAS functionality. This module is meant to be included in
333
- # the CASServer::Controllers module.
334
- module CASServer::CAS
335
-
336
- include CASServer::Model
337
-
338
- def generate_login_ticket
339
- # 3.5 (login ticket)
340
- lt = LoginTicket.new
341
- lt.ticket = "LT-" + String.random
342
-
343
- lt.client_hostname = @env['HTTP_X_FORWARDED_FOR'] || @env['REMOTE_HOST'] || @env['REMOTE_ADDR']
344
- lt.save!
345
- $LOG.debug("Generated login ticket '#{lt.ticket}' for client" +
346
- " at '#{lt.client_hostname}'")
347
- lt
348
- end
349
-
350
- # Creates a TicketGrantingTicket for the given username. This is done when the user logs in
351
- # for the first time to establish their SSO session (after their credentials have been validated).
352
- #
353
- # The optional 'extra_attributes' parameter takes a hash of additional attributes
354
- # that will be sent along with the username in the CAS response to subsequent
355
- # validation requests from clients.
356
- def generate_ticket_granting_ticket(username, extra_attributes = {})
357
- # 3.6 (ticket granting cookie/ticket)
358
- tgt = TicketGrantingTicket.new
359
- tgt.ticket = "TGC-" + String.random
360
- tgt.username = username
361
- tgt.extra_attributes = extra_attributes
362
- # tgt.expires = (Time.now + 2.weeks).strftime("%a, %d-%b-%Y %H:%M:%S GMT")
363
37
  tgt.client_hostname = @env['HTTP_X_FORWARDED_FOR'] || @env['REMOTE_HOST'] || @env['REMOTE_ADDR']
364
38
  tgt.save!
365
39
  $LOG.debug("Generated ticket granting ticket '#{tgt.ticket}' for user" +
@@ -413,7 +87,7 @@ module CASServer::CAS
413
87
  https.start do |conn|
414
88
  path = uri.path.empty? ? '/' : uri.path
415
89
  path += '?' + uri.query unless (uri.query.nil? || uri.query.empty?)
416
-
90
+
417
91
  pgt = ProxyGrantingTicket.new
418
92
  pgt.ticket = "PGT-" + String.random(60)
419
93
  pgt.iou = "PGTIOU-" + String.random(57)
@@ -428,7 +102,7 @@ module CASServer::CAS
428
102
  response = conn.request_get(path)
429
103
  # TODO: follow redirects... 2.5.4 says that redirects MAY be followed
430
104
  # NOTE: The following response codes are valid according to the JA-SIG implementation even without following redirects
431
-
105
+
432
106
  if %w(200 202 301 302 304).include?(response.code)
433
107
  # 3.4 (proxy-granting ticket IOU)
434
108
  pgt.save!
@@ -577,11 +251,11 @@ module CASServer::CAS
577
251
  <saml:NameID></saml:NameID>
578
252
  <samlp:SessionIndex>#{st.ticket}</samlp:SessionIndex>
579
253
  </samlp:LogoutRequest>})
580
-
254
+
581
255
  begin
582
256
  http = Net::HTTP.new(uri.host, uri.port)
583
257
  http.use_ssl = true if uri.scheme =='https'
584
-
258
+
585
259
  http.start do |conn|
586
260
  response = conn.request(req)
587
261
  if response.kind_of? Net::HTTPSuccess