rubycas-server 0.7.1.1 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. data/CHANGELOG +292 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE +26 -0
  4. data/README.md +36 -0
  5. data/Rakefile +0 -3
  6. data/bin/rubycas-server +24 -19
  7. data/lib/casserver.rb +7 -110
  8. data/lib/casserver/authenticators/active_directory_ldap.rb +8 -0
  9. data/lib/casserver/authenticators/active_resource.rb +125 -0
  10. data/lib/casserver/authenticators/authlogic_crypto_providers/aes256.rb +43 -0
  11. data/lib/casserver/authenticators/authlogic_crypto_providers/bcrypt.rb +92 -0
  12. data/lib/casserver/authenticators/authlogic_crypto_providers/md5.rb +34 -0
  13. data/lib/casserver/authenticators/authlogic_crypto_providers/sha1.rb +59 -0
  14. data/lib/casserver/authenticators/authlogic_crypto_providers/sha512.rb +50 -0
  15. data/lib/casserver/authenticators/base.rb +30 -11
  16. data/lib/casserver/authenticators/client_certificate.rb +7 -6
  17. data/lib/casserver/authenticators/google.rb +13 -9
  18. data/lib/casserver/authenticators/ldap.rb +37 -28
  19. data/lib/casserver/authenticators/ntlm.rb +9 -9
  20. data/lib/casserver/authenticators/open_id.rb +3 -3
  21. data/lib/casserver/authenticators/sql.rb +65 -34
  22. data/lib/casserver/authenticators/sql_authlogic.rb +93 -0
  23. data/lib/casserver/authenticators/sql_encrypted.rb +44 -44
  24. data/lib/casserver/authenticators/sql_md5.rb +2 -2
  25. data/lib/casserver/authenticators/sql_rest_auth.rb +82 -0
  26. data/lib/casserver/authenticators/test.rb +10 -7
  27. data/lib/casserver/cas.rb +94 -94
  28. data/lib/casserver/localization.rb +91 -0
  29. data/lib/casserver/model.rb +270 -0
  30. data/lib/casserver/server.rb +745 -0
  31. data/lib/casserver/utils.rb +9 -7
  32. data/lib/casserver/views/_login_form.erb +42 -0
  33. data/lib/casserver/views/layout.erb +18 -0
  34. data/lib/casserver/views/login.erb +30 -0
  35. data/lib/casserver/views/proxy.builder +12 -0
  36. data/lib/casserver/views/proxy_validate.builder +25 -0
  37. data/lib/casserver/views/service_validate.builder +18 -0
  38. data/lib/casserver/views/validate.erb +2 -0
  39. data/po/de_DE/rubycas-server.po +127 -0
  40. data/po/es_ES/rubycas-server.po +123 -0
  41. data/po/fr_FR/rubycas-server.po +128 -0
  42. data/po/ja_JP/rubycas-server.po +126 -0
  43. data/po/pl_PL/rubycas-server.po +123 -0
  44. data/po/pt_BR/rubycas-server.po +123 -0
  45. data/po/ru_RU/rubycas-server.po +118 -0
  46. data/po/rubycas-server.pot +112 -0
  47. data/po/zh_CN/rubycas-server.po +113 -0
  48. data/po/zh_TW/rubycas-server.po +113 -0
  49. data/public/themes/cas.css +121 -0
  50. data/{lib → public}/themes/notice.png +0 -0
  51. data/{lib → public}/themes/ok.png +0 -0
  52. data/{lib → public}/themes/simple/bg.png +0 -0
  53. data/public/themes/simple/favicon.png +0 -0
  54. data/{lib → public}/themes/simple/login_box_bg.png +0 -0
  55. data/{lib → public}/themes/simple/logo.png +0 -0
  56. data/public/themes/simple/theme.css +28 -0
  57. data/{lib → public}/themes/urbacon/bg.png +0 -0
  58. data/{lib → public}/themes/urbacon/login_box_bg.png +0 -0
  59. data/{lib → public}/themes/urbacon/logo.png +0 -0
  60. data/public/themes/urbacon/theme.css +33 -0
  61. data/{lib → public}/themes/warning.png +0 -0
  62. data/resources/init.d.sh +1 -1
  63. data/rubycas-server.gemspec +57 -0
  64. data/setup.rb +4 -4
  65. data/spec/alt_config.yml +50 -0
  66. data/spec/authenticators/active_resource_spec.rb +109 -0
  67. data/spec/authenticators/ldap_spec.rb +53 -0
  68. data/spec/casserver_spec.rb +149 -0
  69. data/spec/default_config.yml +50 -0
  70. data/spec/model_spec.rb +42 -0
  71. data/spec/spec.opts +4 -0
  72. data/spec/spec_helper.rb +88 -0
  73. data/spec/utils_spec.rb +53 -0
  74. data/tasks/bundler.rake +4 -0
  75. data/tasks/db/migrate.rake +12 -0
  76. data/tasks/localization.rake +13 -0
  77. data/tasks/spec.rake +10 -0
  78. metadata +294 -91
  79. data/CHANGELOG.txt +0 -1
  80. data/History.txt +0 -252
  81. data/LICENSE.txt +0 -504
  82. data/Manifest.txt +0 -72
  83. data/PostInstall.txt +0 -3
  84. data/README.txt +0 -25
  85. data/bin/rubycas-server-ctl +0 -22
  86. data/config.example.yml +0 -442
  87. data/config/hoe.rb +0 -76
  88. data/config/requirements.rb +0 -15
  89. data/custom_views.example.rb +0 -11
  90. data/lib/casserver/conf.rb +0 -112
  91. data/lib/casserver/controllers.rb +0 -452
  92. data/lib/casserver/environment.rb +0 -30
  93. data/lib/casserver/models.rb +0 -218
  94. data/lib/casserver/postambles.rb +0 -174
  95. data/lib/casserver/version.rb +0 -9
  96. data/lib/casserver/views.rb +0 -243
  97. data/lib/rubycas-server.rb +0 -1
  98. data/lib/rubycas-server/version.rb +0 -1
  99. data/lib/themes/cas.css +0 -121
  100. data/lib/themes/simple/theme.css +0 -28
  101. data/lib/themes/urbacon/theme.css +0 -33
  102. data/misc/basic_cas_single_signon_mechanism_diagram.png +0 -0
  103. data/misc/basic_cas_single_signon_mechanism_diagram.svg +0 -652
  104. data/script/console +0 -10
  105. data/script/destroy +0 -14
  106. data/script/generate +0 -14
  107. data/script/txt2html +0 -82
  108. data/tasks/deployment.rake +0 -34
  109. data/tasks/environment.rake +0 -7
  110. data/tasks/website.rake +0 -17
  111. data/vendor/isaac_0.9.1/LICENSE +0 -26
  112. data/vendor/isaac_0.9.1/README +0 -78
  113. data/vendor/isaac_0.9.1/TODO +0 -3
  114. data/vendor/isaac_0.9.1/VERSIONS +0 -3
  115. data/vendor/isaac_0.9.1/crypt/ISAAC.rb +0 -171
  116. data/vendor/isaac_0.9.1/isaac.gemspec +0 -39
  117. data/vendor/isaac_0.9.1/setup.rb +0 -596
  118. data/vendor/isaac_0.9.1/test/TC_ISAAC.rb +0 -76
  119. data/website/index.html +0 -40
  120. data/website/index.txt +0 -3
  121. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  122. data/website/stylesheets/screen.css +0 -138
  123. data/website/template.html.erb +0 -40
@@ -1,76 +0,0 @@
1
- require 'rubycas-server/version'
2
-
3
- AUTHOR = 'Matt Zukowski' # can also be an array of Authors
4
- EMAIL = "matt@zukowski.ca"
5
- DESCRIPTION = "Provides single sign-on authentication for web applications using the CAS protocol."
6
- GEM_NAME = 'rubycas-server' # what ppl will type to install your gem
7
- RUBYFORGE_PROJECT = 'rubycas-server' # The unix name for your project
8
- HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
- DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
- EXTRA_DEPENDENCIES = [
11
- 'activesupport',
12
- 'activerecord',
13
- ['picnic', '= 0.7.0']
14
- ] # An array of rubygem dependencies [name, version]
15
-
16
- @config_file = "~/.rubyforge/user-config.yml"
17
- @config = nil
18
- RUBYFORGE_USERNAME = "unknown"
19
- def rubyforge_username
20
- unless @config
21
- begin
22
- @config = YAML.load(File.read(File.expand_path(@config_file)))
23
- rescue
24
- puts <<-EOS
25
- ERROR: No rubyforge config file found: #{@config_file}
26
- Run 'rubyforge setup' to prepare your env for access to Rubyforge
27
- - See http://newgem.rubyforge.org/rubyforge.html for more details
28
- EOS
29
- exit
30
- end
31
- end
32
- RUBYFORGE_USERNAME.replace @config["username"]
33
- end
34
-
35
- ENV['NODOT'] = '1'
36
-
37
- REV = nil
38
- # UNCOMMENT IF REQUIRED:
39
- #REV = YAML.load(`svn info`)['Revision']
40
- VERS = CASServer::VERSION::STRING + (REV ? ".#{REV}" : "")
41
- RDOC_OPTS = ['--quiet', '--title', 'rubycas-server documentation',
42
- "--opname", "index.html",
43
- "--line-numbers",
44
- "--main", "README",
45
- "--inline-source"]
46
-
47
- class Hoe
48
- def extra_deps
49
- @extra_deps.reject! { |x| Array(x).first == 'hoe' }
50
- @extra_deps
51
- end
52
- end
53
-
54
- # Generate all the Rake tasks
55
- # Run 'rake -T' to see list of generated tasks (from gem root directory)
56
- $hoe = Hoe.new(GEM_NAME, VERS) do |p|
57
- p.developer(AUTHOR, EMAIL)
58
- p.description = DESCRIPTION
59
- p.summary = DESCRIPTION
60
- p.url = HOMEPATH
61
- p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
62
- p.test_globs = ["test/**/test_*.rb"]
63
- p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
64
-
65
- # == Optional
66
- p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
67
- #p.extra_deps = EXTRA_DEPENDENCIES
68
-
69
- p.spec_extras = {:executables => ['rubycas-server', 'rubycas-server-ctl']} # A hash of extra values to set in the gemspec.
70
- end
71
-
72
- CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
73
- PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}"
74
- $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
75
- $hoe.rsync_args = '-av --delete --ignore-errors'
76
- $hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
@@ -1,15 +0,0 @@
1
- require 'fileutils'
2
- include FileUtils
3
-
4
- require 'rubygems'
5
- %w[rake hoe newgem rubigen].each do |req_gem|
6
- begin
7
- require req_gem
8
- rescue LoadError
9
- puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
- puts "Installation: gem install #{req_gem} -y"
11
- exit
12
- end
13
- end
14
-
15
- $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
@@ -1,11 +0,0 @@
1
- # Custom views file; add methods to the module definition below
2
-
3
- module CASServer::Views
4
-
5
- # Override views here, for example, a custom login form:
6
- def login_form
7
- # Add your custom login form here, using Markaby
8
- # See the original views.rb file at lib/casserver/views.rb for method names and usage
9
- end
10
-
11
- end
@@ -1,112 +0,0 @@
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
@@ -1,452 +0,0 @@
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
- # (the above method returns false if the POST results in a non-200 HTTP response).
242
- $LOG.debug "Deleting #{st.class.name.demodulize} #{st.ticket.inspect}."
243
- st.destroy
244
- end
245
- end
246
-
247
- $LOG.debug("Deleting #{tgt.class.name.demodulize} '#{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
-
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