rubycas-client 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,3 +1,36 @@
1
+ Copyright (c) 2006 Karolinska Institutet
2
+ (Karolinska Institutet, Stockholm, Sweden).
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions
7
+ are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright
10
+ notice, this list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright
13
+ notice, this list of conditions and the following disclaimer in the
14
+ documentation and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of Karolinska Institutet nor the names of its contributors
17
+ may be used to endorse or promote products derived from this software
18
+ without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY KAROLINSKA INSTITUTET AND CONTRIBUTORS ``AS IS''
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ ARE DISCLAIMED. IN NO EVENT SHALL KAROLINSKA INSTITUTET OR CONTRIBUTORS BE
24
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ POSSIBILITY OF SUCH DAMAGE.
31
+
32
+ ===============================================================================
33
+
1
34
  GNU LESSER GENERAL PUBLIC LICENSE
2
35
  Version 2.1, February 1999
3
36
 
data/README CHANGED
@@ -1,7 +1,7 @@
1
1
  = RubyCAS-Client
2
2
 
3
- Author:: Matt Walker, with modification and docs by Matt Zukowski
4
- Copyright:: Copyright (c) retained by the authors
3
+ Author:: Ola Bini <ola.bini AT ki DOT se>, Matt Zukowski <matt AT roughest DOT net>
4
+ Copyright:: (c) 2006 Karolinska Institutet, portions (c) 2006 Urbacon Ltd.
5
5
  License:: GNU Lesser General Public License v2.1 (LGPL 2.1)
6
6
  Website:: http://rubyforge.org/projects/rubycas-client
7
7
 
@@ -19,10 +19,14 @@ about the CAS protocol here: http://www.ja-sig.org/products/cas
19
19
 
20
20
  This CAS client library is designed to work easily with Rails, but can of course be used elsewhere.
21
21
 
22
- === Installing
22
+ == Installing
23
23
 
24
- You can always download the latest version of RubyCAS-Client from the project's rubyforge page at http://rubyforge.org/projects/rubycas-client.
25
- The library is also available as a gem, which can be installed by:
24
+ You can always download the latest version of RubyCAS-Client from the project's rubyforge page at http://rubyforge.org/projects/rubycas-client,
25
+ however probably the easiest way to install CAS support into your Rails app is via the plugins facility:
26
+
27
+ ./script/plugin install http://rubycas-client.rubyforge.org/plugin/current
28
+
29
+ Alternatively, the library is also available as a gem, which can be installed by:
26
30
 
27
31
  gem install rubycas-client
28
32
 
@@ -30,72 +34,116 @@ The latest development version is availabe via subversion:
30
34
 
31
35
  svn checkout svn://rubyforge.org/var/svn/rubycas-client/trunk/ruby
32
36
 
37
+ Or you can install the latest development version into your Rails app as a plugin:
38
+
39
+ ./script/plugin install -x svn://rubyforge.org/var/svn/rubycas-client/trunk/ruby
40
+
33
41
  Please contact the developers via the {rubyforge.org page}[svn checkout svn://rubyforge.org/var/svn/rubycas-client] if you have bug fixes
34
42
  or enhancements you would like to contribute back.
35
43
 
36
- === Here is an example of how to use the library in your Rails application:
44
+ == Examples
37
45
 
38
- Somewhere in your +config/environment.rb+ file add this:
46
+ ==== Here is an example of how to use the library in your Rails application:
39
47
 
40
- require_gem 'rubycas-client'
48
+ Somewhere in your +config/environment.rb+ file add this (assuming that you have RubyCAS-Client installed as a plugin, otherwise
49
+ you'll need to +require 'cas_auth'+ and +require 'cas_proxy_callback_controller'+):
41
50
 
42
- CAS.cas_server_host = 'login.mycompany.com'
43
- CAS.cas_server_port = '443'
51
+ CAS::Filter.cas_base_url = "https://login.example.com/cas"
44
52
 
45
53
  Then, in your +app/controllers/application.rb+ (or in whatever controller you want to add the CAS filter for):
46
54
 
47
- before_filter CAS::CASFilter
55
+ before_filter CAS::Filter
48
56
 
49
57
  That's it. You should now find that you are redirected to your CAS login page when you try to access any action
50
58
  in your protected controller. You can of course qualify the +before_filter+ as you would with any other ActionController
51
- filter. For example: +before_filter CAS::CASFilter, :except => [ :unprotected_action, :another_unprotected_action ]
59
+ filter. For example: +before_filter CAS::Filter, :except => [ :unprotected_action, :another_unprotected_action ]
52
60
 
53
- <b>Once the user has been authenticated, their authenticated username is available under +session[:user]+</b>
61
+ <b>Once the user has been authenticated, their authenticated username is available under +request.username+
62
+ (and also under +session[:casfilteruser]+).</b> If you want to do something with this username (for example load a
63
+ user record from the database), you can append another filter method that checks for this value and does whatever you need
64
+ it to do.
54
65
 
55
- === A more complicated example:
66
+ ==== A more complicated example:
56
67
 
57
- The CAS client library provides callback methods that can be overridden to inject your user management business logic.
58
- For example, ordinarily after authentication the user's username is placed in +session[:user]+. Rather than storing
59
- the username you may want to store a User object (e.g. a User ActiveRecord model). Here is an example of how to do this:
68
+ Here is a more complicated configuration showing most of the configuration options (this does not show proxy options however,
69
+ which are covered in the next section):
60
70
 
61
- In your +config/environment.rb+ file add this:
71
+ CAS::Filter.login_url = "https://login.example.com/cas/login" # the URI of the CAS login page
72
+ CAS::Filter.validate_url = "https://login.example.com/cas/serviceValidate" # the URI where CAS ticket validation requests are sent
73
+ CAS::Filter.server_name = "yourapplication.example.com:3000" # the server name of your CAS-protected application
74
+ CAS::Filter.renew = false # force re-authentication? see http://www.ja-sig.org/products/cas/overview/protocol
75
+ CAS::Filter.wrap_request = true # make the username available under request.username?
76
+ CAS::Filter.gateway = false # act as cas gateway? see http://www.ja-sig.org/products/cas/overview/protocol
77
+ CAS::Filter.session_username = :casfilteruser # this is the hash in the session where the authenticated username will be stored
62
78
 
63
- require_gem 'rubycas-client'
64
79
 
65
- CAS.cas_server_host = 'login.mycompany.com'
66
- CAS.cas_server_port = '443'
80
+ ==== How to act as a CAS proxy:
67
81
 
68
- module CAS
69
- def self.load_user_data(username, cas_payload)
70
- user = User.find_by_username(username)
71
- user = User.new(:username => username) if user.nil?
72
-
73
- user.last_login = Time.now
74
-
75
- return user
76
- end
82
+ CAS 2.0 has a built-in mechanism that allows a CAS-authenticated application to pass on its authentication to other applications.
83
+ An example where this is useful might be a portal site, where the user logs in to a central website and then gets forwarded to
84
+ various other sites that run independently of the portal system (but are always accessed via the portal). The exact mechanism
85
+ behind this is rather complicated so I won't go over it here. If you wish to learn more about CAS proxying, a great walkthrough
86
+ is available at http://www.ja-sig.org/wiki/display/CAS/Proxy+CAS+Walkthrough.
87
+
88
+ RubyCAS-Client fully supports proxying, so a CAS-protected Rails application can act as a CAS proxy.
89
+
90
+ Additionally, RubyCAS-Client comes with a controller that can act as a CAS proxy callback receiver. This is necessary because
91
+ when your application requests to act as a CAS proxy, the CAS server must contact your application to deposit the proxy-granting-ticket
92
+ (PGT). Note that in this case the CAS server CONTACTS YOU, rather than you contacting the CAS server (as in all other CAS operations).
93
+
94
+ Confused? Don't worry, you don't really have to understand this to use it. To enable your Rails app to act as a CAS proxy,
95
+ all you need to do is this:
96
+
97
+ In your +config/environment.rb+:
98
+
99
+ CAS::Filter.cas_base_url = "https://login.example.com/cas"
100
+ CAS::Filter.proxy_callback_url = "https://yourrailsapp.com/cas_proxy_callback/receive_pgt"
101
+ CAS::Filter.proxy_retrieval_url = "https://yourrailsapp.com/cas_proxy_callback/retrieve_pgt"
77
102
 
78
- def self.save_user_data(user)
79
- user.save
80
- end
81
- end
103
+ In +config/routes.rb+ make sure that you have a route that will allow requests to /cas_proxy_callback/:action to be routed to the
104
+ CasProxyCallbackController. This should work as-is with the standard Rails routes setup, but if you have disabled the default
105
+ route, you should add the following:
106
+
107
+ map.cas_proxy_callback 'cas_proxy_callback/:action', :controller => 'cas_proxy_callback'
108
+
109
+ Once your user logs in to CAS via your application, you can do the following to obtain a service ticket that can then be used
110
+ to authenticate another application:
111
+
112
+ service_uri = "http://some.other.application"
113
+ proxy_granting_ticket = session[:casfilterpgt]
114
+ ticket = CAS::Filter.request_proxy_ticket(service_uri, proxy_granting_ticket)
115
+
116
+ +ticket+ should now contain a valid service ticket. You can use it to authenticate your other by sending it and the service URI
117
+ as query parameters to your target application:
118
+
119
+ http://some.other.application?service=#{ticket.target_service}&ticket=#{ticket.proxy_ticket}
82
120
 
83
- In the above example, +load_user_data+ is overridden to load the user data based on the authenticated username.
84
- +save_user_data+ is also overidden to save any changes made to the user model by the login process (in this case,
85
- the last_login column is updated). See the CAS module documentation for more information on callbacks.
121
+ This is of course assuming that some.other.application is also protected by the CAS filter.
86
122
 
87
- Another feature available in the CAS library is the ability to refresh the authentication ticket with the server
88
- at some specified interval. Normally when the user logs in, their authentication info is stored in the local
89
- web session and no futher queries are made to the CAS server. This may result in a situation where the remote CAS
90
- authentication ticket has expired, but the local session authentication has not. To prevent this from happening
91
- you can specify a +refresh_ticket_interval+ (in minutes) as follows:
123
+ For extra security -- and you will likely want to do this on production machines in the wild -- in the proxied app's configuration
124
+ (some.other.appliction in this example) you can specify the list of authorized proxies. For example, on your proxied app the CAS
125
+ configuration might look something like this:
92
126
 
93
- CAS.refresh_ticket_interval = 15
127
+ CAS::Filter.cas_base_url = "https://login.example.com/cas"
128
+ CAS::Filter.server_name = "some.other.application"
129
+ CAS::Filter.authorized_proxies = ["https://yourrailsapp.com/cas/proxy_callback/receive_pgt"]
94
130
 
95
- The CAS client will now check at every request if the local authentication info is older than 15 minutes. If it is,
96
- it will transparently re-authenticate with the server. Note that because of the way the re-authentication process works,
97
- after re-authenticating the user will have a +ticket+ query variable attached to the end of their current URL for that request
98
- (i.e. http://mycompany.com/somepage/index?ticket=123456789).
131
+ If no authorized proxies are given, the filter will accept receipts from any proxy.
132
+
133
+ ===== Additional notes and caveats:
134
+
135
+ Note that when the CAS filter runs, the PGT is stored in session[:casfilterpgt]. This value must be passed to CAS::Filter#request_proxy_ticket.
136
+ Also, note that CAS::Filter#request_proxy_ticket will URI-encode the service_uri before passing it to the CAS server, and the service
137
+ value must henceforth always be passed as URI-encoded (this can be problematic when your proxied application uses some CAS client other than
138
+ RubyCAS-Client).
139
+
140
+ <b>The proxy url must be an https address.</b> Otherwise CAS will refuse to communicate with it. This means that if you are using
141
+ the bundled cas_proxy_callback controller, you will have to host your application on an https-enabled server. This can be a bit
142
+ tricky with Rails. WEBrick's SSL support is difficult to configure, and Mongrel doesn't support SSL at all. One workaround is to
143
+ use a reverse proxy like Pound[http://www.apsis.ch/pound/], which will accept https connections and locally re-route them
144
+ to your Rails application. Also, note that <i>self-signed SSL certificates likely won't work</i>. You will probably need to use
145
+ a real certificate purchased from a trusted CA authority (there are ways around this, but good luck :)
146
+
99
147
 
100
148
  == License
101
149
 
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the cas_auth plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the cas_auth plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'CasAuth'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
data/init.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'cas_auth'
2
+
3
+ #CAS::Filter.logger = RAILS_DEFAULT_LOGGER if !RAILS_DEFAULT_LOGGER.nil?
4
+ #CAS::Filter.logger = config.logger if !config.logger.nil?
5
+
6
+ CAS::Filter.logger = Logger.new "#{RAILS_ROOT}/log/cas_filter.log"
7
+ CAS::Filter.logger.level = Logger::DEBUG
8
+
9
+ #class ActionController::Base
10
+ # append_before_filter CAS::Filter
11
+ #end
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
data/lib/cas.rb ADDED
@@ -0,0 +1,188 @@
1
+ require 'net/https'
2
+ require 'rexml/document'
3
+
4
+ module CAS
5
+ class CASException < Exception
6
+ end
7
+ class AuthenticationException < CASException
8
+ end
9
+ class ValidationException < CASException
10
+ end
11
+
12
+ class Receipt
13
+ attr_accessor :validate_url, :pgt_iou, :primary_authentication, :proxy_callback_url, :proxy_list, :user_name
14
+
15
+ def primary_authentication?
16
+ primary_authentication
17
+ end
18
+
19
+ def initialize(ptv)
20
+ if !ptv.successful_authentication?
21
+ begin
22
+ ptv.validate
23
+ rescue ValidationException=>vald
24
+ raise AuthenticationException, "Unable to validate ProxyTicketValidator [#{ptv}] [#{vald}]"
25
+ end
26
+ raise AuthenticationException, "Unable to validate ProxyTicketValidator because of no success with validation[#{ptv}]" unless ptv.successful_authentication?
27
+ end
28
+ self.validate_url = ptv.validate_url
29
+ self.pgt_iou = ptv.pgt_iou
30
+ self.user_name = ptv.user
31
+ self.proxy_callback_url = ptv.proxy_callback_url
32
+ self.proxy_list = ptv.proxy_list
33
+ self.primary_authentication = ptv.renewed?
34
+ raise AuthenticationException, "Validation of [#{ptv}] did not result in an internally consistent Receipt" unless validate
35
+ end
36
+
37
+ def proxied?
38
+ !proxy_list.empty?
39
+ end
40
+
41
+ def proxying_service
42
+ proxy_list.first
43
+ end
44
+
45
+ def to_s
46
+ "[#{super} - userName=[#{user_name}] validateUrl=[#{validate_url}] proxyCallbackUrl=[#{proxy_callback_url}] pgtIou=[#{pgt_iou}] proxyList=[#{proxy_list}]"
47
+ end
48
+
49
+ def validate
50
+ user_name &&
51
+ validate_url &&
52
+ proxy_list &&
53
+ !(primary_authentication? && !proxy_list.empty?) # May not be both primary authenitication and proxied.
54
+ end
55
+ end
56
+
57
+ class AbstractCASResponse
58
+
59
+ def self.retrieve(uri_str)
60
+ prs = URI.parse(uri_str)
61
+ # puts prs.inspect
62
+ https = Net::HTTP.new(prs.host,prs.port)
63
+ # puts https.inspect
64
+ https.use_ssl=true
65
+ https.start { |conn|
66
+ # TODO: make sure that HTTP status code in the response is 200... maybe throw exception if is 500?
67
+ conn.get("#{prs.path}?#{prs.query}").body.strip
68
+ }
69
+ end
70
+
71
+ protected
72
+ def parse_unsuccessful(elm)
73
+ # puts "unsuccessful"
74
+ @error_message = elm.text.strip
75
+ @error_code = elm.attributes["code"].strip
76
+ @successful_authentication = false
77
+ end
78
+
79
+ def parse(str)
80
+ # puts "parsing... #{str}"
81
+ doc = REXML::Document.new str
82
+ resp = doc.elements["cas:serviceResponse"].elements[1]
83
+ # puts "resp... #{resp.name}"
84
+ if successful_response? resp
85
+ parse_successful(resp)
86
+ else
87
+ parse_unsuccessful(resp)
88
+ end
89
+ end
90
+ end
91
+
92
+ class ServiceTicketValidator < AbstractCASResponse
93
+ attr_accessor :validate_url, :proxy_callback_url, :renew, :service_ticket, :service
94
+ attr_reader :pgt_iou, :user, :error_code, :error_message, :entire_response, :successful_authentication
95
+
96
+ def renewed?
97
+ renew
98
+ end
99
+
100
+ def successful_authentication?
101
+ successful_authentication
102
+ end
103
+
104
+ def validate
105
+ raise ValidationException, "must set validation URL and ticket" if validate_url.nil? || service_ticket.nil?
106
+ clear!
107
+ @attempted_authentication = true
108
+ url_building = "#{validate_url}#{(url_building =~ /\?/)?'&':'?'}service=#{service}&ticket=#{service_ticket}"
109
+ url_building += "&pgtUrl=#{proxy_callback_url}" if proxy_callback_url
110
+ url_building += "&renew=true" if renew
111
+ @@entire_response = ServiceTicketValidator.retrieve url_building
112
+ parse @@entire_response
113
+ end
114
+
115
+ def clear!
116
+ @user = @pgt_iou = @error_message = nil
117
+ @successful_authentication = @attempted_authentication = false
118
+ end
119
+
120
+ def to_s
121
+ "[#{super} - validateUrl=[#{validate_url}] proxyCallbackUrl=[#{proxy_callback_url}] ticket=[#{service_ticket}] service=[#{service} pgtIou=[#{pgt_iou}] user=[#{user}] errorCode=[#{error_message}] errorMessage=[#{error_message}] renew=[#{renew}] entireResponse=[#{entire_response}]]"
122
+ end
123
+
124
+ protected
125
+ def parse_successful(elm)
126
+ # puts "successful"
127
+ @user = elm.elements["cas:user"] && elm.elements["cas:user"].text.strip
128
+ # puts "user: #{@user}"
129
+ @pgt_iou = elm.elements["cas:proxyGrantingTicket"] && elm.elements["cas:proxyGrantingTicket"].text.strip
130
+ # puts "pgt_iou: #{@pgt_iou}"
131
+ @successful_authentication = true
132
+ end
133
+
134
+ def successful_response?(resp)
135
+ resp.name == "authenticationSuccess"
136
+ end
137
+ end
138
+
139
+ class ProxyTicketValidator < ServiceTicketValidator
140
+ attr_reader :proxy_list
141
+ @@response_prefix = "proxy"
142
+
143
+ def initialize
144
+ super
145
+ @proxy_list = []
146
+ end
147
+
148
+ def clear!
149
+ super
150
+ @proxy_list = []
151
+ end
152
+
153
+ protected
154
+ def parse_successful(elm)
155
+ super(elm)
156
+ # puts "proxy_successful"
157
+ proxies = elm.elements["cas:proxies"]
158
+ if proxies
159
+ proxies.elements.each("cas:proxy") { |prox|
160
+ @proxy_list ||= []
161
+ @proxy_list << prox.text.strip
162
+ }
163
+ end
164
+ end
165
+ end
166
+
167
+ class ProxyTicketRequest < AbstractCASResponse
168
+ attr_accessor :proxy_url, :target_service, :pgt
169
+ attr_reader :proxy_ticket
170
+
171
+ def request
172
+ url_building = "#{proxy_url}#{(url_building =~ /\?/)?'&':'?'}targetService=#{target_service}&pgt=#{pgt}"
173
+ # puts "REQUESTING:"+url_building
174
+ @@entire_response = ServiceTicketValidator.retrieve url_building
175
+ # puts @@entire_response.to_s
176
+ parse @@entire_response
177
+ end
178
+
179
+ protected
180
+ def parse_successful(elm)
181
+ @proxy_ticket = elm.elements["cas:proxyTicket"] && elm.elements["cas:proxyTicket"].text.strip
182
+ end
183
+
184
+ def successful_response?(resp)
185
+ resp.name == "proxySuccess"
186
+ end
187
+ end
188
+ end
data/lib/cas_auth.rb ADDED
@@ -0,0 +1,311 @@
1
+ # RubyCAS-Client is a client and Rails filter for the CAS protocol.
2
+ # Copyright (c) 2006 Karolinska Institutet
3
+ #
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program; if not, write to the Free Software Foundation,
15
+ # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16
+
17
+ require 'uri'
18
+ require 'logger'
19
+
20
+
21
+ require File.dirname(File.expand_path(__FILE__))+'/cas'
22
+
23
+ module CAS
24
+ # The DummyLogger is a class which might pass through to a real Logger
25
+ # if one is assigned. However, it can gracefully swallow any logging calls
26
+ # if there is now Logger assigned.
27
+ class LoggerWrapper
28
+ def initialize(logger=nil)
29
+ set_logger(logger)
30
+ end
31
+ # Assign the 'real' Logger instance that this dummy instance wraps around.
32
+ def set_logger(logger)
33
+ @logger = logger
34
+ end
35
+ # log using the appropriate method if we have a logger
36
+ # if we dont' have a logger, ignore completely.
37
+ def method_missing(name, *args)
38
+ if @logger && @logger.respond_to?(name)
39
+ @logger.send(name, *args)
40
+ end
41
+ end
42
+ end
43
+
44
+ LOGGER = CAS::LoggerWrapper.new
45
+
46
+ # Allows authentication through a CAS server.
47
+ # The precondition for this filter to work is that you have an
48
+ # authentication infrastructure. As such, this is for the enterprise
49
+ # rather than small shops.
50
+ #
51
+ # To use CAS::Filter for authentication, add something like this to
52
+ # your environment:
53
+ #
54
+ # CAS::Filter.server_name = "yourapplication.server.name"
55
+ # CAS::Filter.cas_base_url = "https://cas.company.com
56
+ #
57
+ # The filter will try to use the standard CAS page locations based on this URL.
58
+ # Or you can explicitly specify the individual URLs:
59
+ #
60
+ # CAS::Filter.server_name = "yourapplication.server.name"
61
+ # CAS::Filter.login_url = "https://cas.company.com/login"
62
+ # CAS::Filter.validate_url = "https://cas.company.com/proxyValidate"
63
+ #
64
+ # It is of course possible to use different configurations in development, test
65
+ # and production by placing the configuration in the appropriate environments file.
66
+ #
67
+ # To add CAS protection to a controller:
68
+ #
69
+ # before_filter CAS::Filter
70
+ #
71
+ # All of the standard Rails filter qualifiers can also be used. For example:
72
+ #
73
+ # before_filter CAS::Filter, :only => [:admin, :private]
74
+ #
75
+ # By default CAS::Filter saves the logged in user in session[:casfilteruser] but
76
+ # that name can be changed by setting CAS::Filter.session_username
77
+ # The username is also available from the request by
78
+ #
79
+ # request.username
80
+ #
81
+ # This wrapping of the request can be disabled by
82
+ #
83
+ # CAS::Filter.wrap_request = false
84
+ #
85
+ # Proxying is also possible. Please see the README for examples.
86
+ #
87
+ class Filter
88
+ @@login_url = "https://localhost/login"
89
+ @@logout_url = nil
90
+ @@validate_url = "https://localhost/proxyValidate"
91
+ @@server_name = "localhost"
92
+ @@renew = false
93
+ @@session_username = :casfilteruser
94
+ @@query_string = {}
95
+ @@fake = nil
96
+ @@pgt = nil
97
+ cattr_accessor :query_string
98
+ cattr_accessor :login_url, :validate_url, :service_url, :server_name, :renew, :wrap_request, :gateway, :session_username
99
+ cattr_accessor :proxy_url, :proxy_callback_url, :proxy_retrieval_url
100
+ @@authorized_proxies = []
101
+ cattr_accessor :authorized_proxies
102
+
103
+
104
+ class << self
105
+ # Retrieves the current Logger instance
106
+ def logger
107
+ CAS::LOGGER
108
+ end
109
+ def logger=(val)
110
+ CAS::LOGGER.set_logger(val)
111
+ end
112
+
113
+ alias :log :logger
114
+ alias :log= :logger=
115
+
116
+ def create_logout_url
117
+ if !@@logout_url && @@login_url =~ %r{^(.+?)/[^/]*$}
118
+ @@logout_url = "#{$1}/logout"
119
+ end
120
+ logger.info "Created logout-url: #{@@logout_url}"
121
+ end
122
+
123
+ def logout_url(controller)
124
+ create_logout_url unless @@logout_url
125
+ url = redirect_url(controller,@@logout_url)
126
+ logger.info "Using logout-url #{url}"
127
+ url
128
+ end
129
+
130
+ def logout_url=(val)
131
+ @@logout_url = val
132
+ end
133
+
134
+ def cas_base_url=(url)
135
+ CAS::Filter.login_url = "#{url}/login"
136
+ CAS::Filter.validate_url = "#{url}/proxyValidate"
137
+ CAS::Filter.proxy_url = "#{url}/proxy"
138
+ end
139
+
140
+ def fake
141
+ @@fake
142
+ end
143
+
144
+ def fake=(val)
145
+ if val.nil?
146
+ alias :filter :filter_r
147
+ else
148
+ alias :filter :filter_f
149
+ end
150
+ @@fake = val
151
+ end
152
+
153
+ def filter_f(controller)
154
+ logger.debug("entering fake cas filter")
155
+ username = @@fake
156
+ if :failure == @@fake
157
+ return false
158
+ elsif :param == @@fake
159
+ username = controller.params['username']
160
+ elsif Proc === @@fake
161
+ username = @@fake.call(controller)
162
+ end
163
+ logger.debug("our username is: #{username}")
164
+ controller.session[@@session_username] = username
165
+ return true
166
+ end
167
+
168
+ def filter_r(controller)
169
+ logger.debug("filter of controller: #{controller}")
170
+ receipt = controller.session[:casfilterreceipt]
171
+ logger.info("receipt: #{receipt}")
172
+ valid = false
173
+ if receipt
174
+ valid = validate_receipt(receipt)
175
+ logger.info("valid receipt?: #{valid}")
176
+ else
177
+ reqticket = controller.params["ticket"]
178
+ logger.info("ticket: #{reqticket}")
179
+ if reqticket
180
+ # We temporarily allow ActionController requests to be handled concurrently.
181
+ # Otherwise proxy granting ticket callbacks from CAS wouldn't work, since
182
+ # the Rails server would be deadlocked while it waits for the CAS server to validate
183
+ # the ticket, and the CAS server waits for the Rails server to receive the PGT callback.
184
+ # Note that since the allow_concurrency option is undocumented and considered
185
+ # experimental, what we're doing here may cause unforseen problems. Beware!
186
+ ActionController::Base.allow_concurrency = true
187
+ receipt = authenticated_user(reqticket,controller)
188
+ ActionController::Base.allow_concurrency = false
189
+
190
+ logger.info("new receipt: #{receipt}")
191
+ logger.info("validate_receipt: " + validate_receipt(receipt).to_s)
192
+ if receipt && validate_receipt(receipt)
193
+ controller.session[:casfilterreceipt] = receipt
194
+ controller.session[@@session_username] = receipt.user_name
195
+
196
+ if receipt.pgt_iou
197
+ ActionController::Base.allow_concurrency = true
198
+ retrieve_url = "#{@@proxy_retrieval_url}?pgtIou=#{receipt.pgt_iou}"
199
+ logger.info("retrieving pgt from: #{retrieve_url}")
200
+ controller.session[:casfilterpgt] = CAS::ServiceTicketValidator.retrieve(retrieve_url)
201
+ ActionController::Base.allow_concurrency = false
202
+ end
203
+
204
+ valid = true
205
+ end
206
+ else
207
+ did_gateway = controller.session[:casfiltergateway]
208
+ raise CASException, "Can't redirect without login url" if !@@login_url
209
+ if did_gateway
210
+ if controller.session[@@session_username]
211
+ valid = true
212
+ else
213
+ controller.session[:casfiltergateway] = true
214
+ end
215
+ else
216
+ controller.session[:casfiltergateway] = true
217
+ end
218
+ end
219
+ end
220
+ logger.info("will send redirect #{redirect_url(controller)}") if !valid
221
+ controller.send :redirect_to,redirect_url(controller) if !valid
222
+ return valid
223
+ end
224
+ alias :filter :filter_r
225
+
226
+
227
+ def request_proxy_ticket(target_service, pgt)
228
+ r = ProxyTicketRequest.new
229
+ r.proxy_url = @@proxy_url
230
+ r.target_service = escape_service_uri(target_service)
231
+ r.pgt = pgt
232
+
233
+ raise "Cannot request a proxy ticket for service #{r.target_service} because no proxy granting ticket (PGT) has been set." unless r.pgt
234
+
235
+ logger.info("requesting proxy ticket for service #{r.target_service} with pgt #{pgt}")
236
+ r.request
237
+
238
+ if r.proxy_ticket
239
+ logger.info("got proxy ticket #{r.proxy_ticket} for service #{r.target_service}")
240
+ else
241
+ logger.warn("did not receive a proxy ticket for service #{r.target_service}!")
242
+ end
243
+
244
+ return r
245
+ end
246
+ end
247
+
248
+ private
249
+ def self.validate_receipt(receipt)
250
+ if receipt
251
+ logger.debug "authorized proxies: #{@@authorized_proxies.inspect}"
252
+ logger.debug "proxying service: #{receipt.proxying_service.inspect}"
253
+ end
254
+
255
+ valid = receipt && !(@@renew && !receipt.primary_authentication?)
256
+
257
+ if @@authorized_proxies and !@@authorized_proxies.empty?
258
+ valid = valid && !(receipt.proxied? && !@@authorized_proxies.include?(receipt.proxying_service))
259
+ end
260
+
261
+ return valid
262
+ end
263
+
264
+ def self.authenticated_user(tick, controller)
265
+ pv = ProxyTicketValidator.new
266
+ pv.validate_url = @@validate_url
267
+ pv.service_ticket = tick
268
+ pv.service = service_url(controller)
269
+ pv.renew = @@renew
270
+ pv.proxy_callback_url = @@proxy_callback_url
271
+ receipt = nil
272
+ logger.debug("pv: #{pv.inspect}")
273
+ begin
274
+ receipt = Receipt.new(pv)
275
+ rescue AuthenticationException=>auth
276
+ logger.warn("filter: had an authentication-exception #{auth}")
277
+ end
278
+ receipt
279
+ end
280
+ def self.service_url(controller)
281
+ before = @@service_url || guess_service(controller)
282
+ logger.debug("before: #{before}")
283
+ after = escape_service_uri(before)
284
+ logger.debug("after: #{after}")
285
+ after
286
+ end
287
+ def self.redirect_url(controller,url=@@login_url)
288
+ "#{url}?service=#{service_url(controller)}" + ((@@renew)? "&renew=true":"") + ((@@gateway)? "&gateway=true":"") + ((@@query_string.nil?)? "" : "&"+(@@query_string.collect { |k,v| "#{k}=#{v}"}.join("&")))
289
+ end
290
+ def self.guess_service(controller)
291
+ # we're assuming that controller.params[:service] is url-encoded!
292
+ return controller.params[:service] if controller.params.include? :service
293
+
294
+ req = controller.request
295
+ parms = controller.params.dup
296
+ parms.delete("ticket")
297
+ query = (parms.collect {|key, val| "#{key}=#{val}"}).join("&")
298
+ query = "?" + query unless query.empty?
299
+ "#{req.protocol}#{@@server_name}#{req.request_uri.split(/\?/)[0]}#{query}"
300
+ end
301
+ def self.escape_service_uri(uri)
302
+ URI.encode(uri, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]", false, 'U').freeze)
303
+ end
304
+ end
305
+ end
306
+
307
+ class ActionController::AbstractRequest
308
+ def username
309
+ session[CAS::Filter.session_username]
310
+ end
311
+ end
@@ -0,0 +1,72 @@
1
+ require 'pstore'
2
+
3
+ # Controller that responds to proxy generating ticket callbacks from the CAS server and allows
4
+ # for retrieval of those PGTs.
5
+ class CasProxyCallbackController < ActionController::Base
6
+
7
+ # Receives a proxy granting ticket from the CAS server and stores it in the database.
8
+ # Note that this action should ALWAYS be called via https, otherwise you have a gaping security hole.
9
+ # In fact, the JA-SIG implementation of the CAS server will refuse to send PGTs to non-https URLs.
10
+ def receive_pgt
11
+ render_error "PGTs can be received only via HTTPS or local connections." and return unless
12
+ request.ssl? or request.env['REMOTE_HOST'] == "127.0.0.1"
13
+
14
+ pgtIou = params['pgtIou']
15
+ pgtId = params['pgtId']
16
+
17
+ # We need to render a response with HTTP status code 200 when no pgtIou/pgtId is specified because CAS seems first
18
+ # call the action without any parameters (maybe to check if the server responds correctly) and only then again,
19
+ # this time with the required params.
20
+ render :text => "Okay, the server is up, but please specify a pgtIou and pgtId." and return unless pgtIou and pgtId
21
+
22
+ # TODO: pstore contents should probably be encrypted...
23
+ pstore = open_pstore
24
+
25
+ pstore.transaction do
26
+ pstore[pgtIou] = pgtId
27
+ end
28
+
29
+ render :text => "PGT received. Thank you!" and return
30
+ end
31
+
32
+ # Retreives a proxy granting ticket, sends it to output, and deletes the pgt from session storage.
33
+ # Note that this action should ALWAYS be called via https, otherwise you have a gaping security hole --
34
+ # in fact, the action will not work if the request is not made via SSL or is not local (we allow for local
35
+ # non-SSL requests since this allows for the use of reverse HTTPS proxies like Pound).
36
+ def retrieve_pgt
37
+ render_error "You can only retrieve PGTs via HTTPS or local connections." and return unless
38
+ request.ssl? or request.env['REMOTE_HOST'] == "127.0.0.1"
39
+
40
+ pgtIou = params['pgtIou']
41
+
42
+ render_error "No pgtIou specified. Cannot retreive the pgtId." and return unless pgtIou
43
+
44
+ pstore = open_pstore
45
+
46
+ pgt = nil
47
+ pstore.transaction do
48
+ pgt = pstore[pgtIou]
49
+ end
50
+
51
+ if not pgt
52
+ render_error "Invalid pgtIou specified. Perhaps this pgt has already been retrieved?" and return
53
+ end
54
+
55
+ render :text => pgt
56
+
57
+ # TODO: need to periodically clean the storage, otherwise it will just keep growing
58
+ pstore.transaction do
59
+ pstore.delete pgtIou
60
+ end
61
+ end
62
+
63
+ private
64
+ def render_error(msg)
65
+ # Note that the error messages are mostly just for debugging, since the CAS server never reads them.
66
+ render :text => msg, :status => 500
67
+ end
68
+
69
+ def open_pstore
70
+ PStore.new("#{RAILS_ROOT}/tmp/cas_pgt.pstore")
71
+ end
72
+ end
metadata CHANGED
@@ -3,7 +3,7 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: rubycas-client
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.9.1
6
+ version: 0.10.0
7
7
  date: 2006-09-07 00:00:00 -04:00
8
8
  summary: Client library for the CAS single-sign-on protocol.
9
9
  require_paths:
@@ -12,7 +12,7 @@ email: matt@roughest.net
12
12
  homepage: http://rubycas-client.rubyforge.org
13
13
  rubyforge_project: rubycas-client
14
14
  description: RubyCAS-Client is a Ruby client library for Yale's Central Authentication Service (CAS) single-sign-on protocol for web-based applications.
15
- autorequire: cas-client
15
+ autorequire:
16
16
  default_executable:
17
17
  bindir: bin
18
18
  has_rdoc: true
@@ -29,8 +29,13 @@ authors:
29
29
  - Matt Zukowski
30
30
  - Matt Walker
31
31
  files:
32
- - lib/cas-client.rb
32
+ - install.rb
33
+ - init.rb
34
+ - lib/cas_proxy_callback_controller.rb
35
+ - lib/cas.rb
36
+ - lib/cas_auth.rb
33
37
  - LICENSE
38
+ - Rakefile
34
39
  - README
35
40
  test_files: []
36
41
 
data/lib/cas-client.rb DELETED
@@ -1,289 +0,0 @@
1
- # RubyCAS-Client is a Ruby client library for Yale's Central Authentication Service (CAS) protocol.
2
- #
3
- # Author:: Matt Walker, with modification and docs by Matt Zukowski
4
- # Copyright:: Copyright (c) retained by the authors
5
- # License:: GNU Lesser General Public License v2.1 (LGPL 2.1)
6
- # Website:: http://rubyforge.org/projects/rubycas-client
7
- #
8
- # This program is free software; you can redistribute it and/or
9
- # modify it under the terms of the GNU General Public License
10
- # as published by the Free Software Foundation; either version 2
11
- # of the License, or (at your option) any later version.
12
- #
13
- # This program is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with this program; if not, write to the Free Software
20
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
-
22
-
23
- require 'net/https'
24
- require 'rexml/document'
25
-
26
- require 'rubygems'
27
- require_gem 'actionpack'
28
-
29
- # We must override redirect_to in ActionController::Base to allow this class to
30
- # redirect the user to the CAS server for login, since redirect_to is not public by default.
31
- class ActionController::Base; public :redirect_to; end
32
-
33
- # Module containing the CASFilter and associated constants. This particular
34
- # version is designed to be used with Rails, but you can imagine expanding it
35
- # to work in other settings as well. If you would like to override the default
36
- # values of @@cas_server_host or @@cas_server_port, you may do so as follows:
37
- #
38
- # require 'cas'
39
- #
40
- # CAS.cas_server_host = 'netid.tamu.edu'
41
- # CAS.cas_server_port = 443
42
- #
43
- # By default, the filter will store the authenticated username in session[:user]. You can
44
- # modify this behaviour to have session[:user] contain something like your User ActiveRecord object by
45
- # modifying various callbacks like so (see the documentation for each callback method for details):
46
- #
47
- # module CAS
48
- # def self.load_user_data(username, cas_payload)
49
- # user = User.find_by_username(username)
50
- # user.last_login = Time.now
51
- # return user
52
- # end
53
- #
54
- # def self.save_user_data(user)
55
- # user.save
56
- # end
57
- # end
58
- #
59
- # Author:: Matt Walker, with modification and docs by Matt Zukowski
60
- # Acknowledgements:: James Smith provided me with my first Ruby CAS client, on which this code is based.
61
- module CAS
62
- # The hostname of the CAS server to authenticate against.
63
- @@cas_server_host = 'login.mycompany.com'
64
-
65
- # The port the CAS server is running on.
66
- @@cas_server_port = 443
67
-
68
- # Normally, CAS authentication is only done once -- during the first filter call. After that the user's
69
- # username is stored in the local session and the CAS server is not contacted again until the local (i.e. Rails)
70
- # session expires. By setting this to a value other than nil, the user will be re-authenticated with CAS
71
- # every @@refresh_ticket_interval minutes. If you set this to 0, the CAS ticket will be re-validated on every
72
- # request.
73
- @@refresh_ticket_interval = nil
74
-
75
- # The class for performing authentication filtering in Rails. The most
76
- # important method is +filter+, which is static (thus requiring assisting
77
- # methods to be static as well). This version is simplistic, but you can
78
- # imagine extending it to allow gatewaying or authentication by proxy.
79
- class CASFilter
80
- # Filtering method specific to Rails. In particular, it is meant to be
81
- # used with Rails' +before_filter+ method to add CAS authentication to
82
- # one or more actions, as in the following example:
83
- #
84
- # require 'cas'
85
- #
86
- # class AdminController < ApplicationController
87
- # before_filter CAS::CASFilter, :except => :logout
88
- # # ...
89
- # end
90
- #
91
- # When a user logs in, their user object is stashed in the session.
92
- # This indicates to all future requests that the user is authenticated.
93
- # If there is no user object in the session, the user must provide a
94
- # valid CAS ticket to login. If they have no ticket, they are
95
- # redirected to the CAS server to get one. If they do have a ticket,
96
- # it is validated with the server before creating their user object in
97
- # the session (and possibly database).
98
- #
99
- # Inputs:
100
- # [controller] The ActionController performing filtering. If its session contains a user object, the user must have successfully authenticated against CAS.
101
- #
102
- # Returns a boolean: did user successfully authenticate?
103
- def self.filter(controller)
104
- RAILS_DEFAULT_LOGGER.info "Starting CAS filter..."
105
-
106
- # If we have a user, a successful login was made for this session
107
- if (!controller.session[:user].nil?)
108
- # If the local session auth info is older than @@refresh_ticket_interval, then re-submit the ticket for validation
109
- # to ensure that the ticket hasn't expired on the CAS end
110
- cas_ticket_timestamp = controller.session[:cas_ticket_timestamp]
111
-
112
- if (cas_ticket_timestamp and cas_ticket_timestamp.kind_of? Time)
113
- time_elapsed = Time.now - cas_ticket_timestamp
114
-
115
- if time_elapsed < (CAS.refresh_ticket_interval * 60)
116
- return true # local session info isn't expired so we're okay
117
- end
118
- end
119
- # local session info is expired so we continue with full ticket validation...
120
- end
121
-
122
- RAILS_DEFAULT_LOGGER.debug "No user session present, begin CAS authenticatoin process..."
123
-
124
- # Otherwise, we require a ticket to authenticate the user
125
- service = controller.url_for()
126
- ticket = controller.params[:ticket]
127
-
128
- if ticket.nil? || ticket == ""
129
- self.redirect_to_login(controller, service)
130
- return false
131
- end
132
-
133
- cas_payload = validate_ticket(service, ticket)
134
-
135
- #TODO: redirect to login with appropriate error message (i.e. "your session has expried" or "invalid response from cas", etc.)
136
- if cas_payload.nil? || cas_payload.length != 1
137
- self.redirect_to_login(controller, service)
138
- return false
139
- end
140
-
141
- username = cas_payload[:username]
142
-
143
- user = CAS.load_user_data(username, cas_payload)
144
- CAS.save_user_data(user)
145
-
146
- controller.session[:user] = user
147
- controller.session[:cas_ticket_timestamp] = Time.now
148
- return true
149
- end
150
-
151
- private
152
-
153
- # Validates a CAS ticket with the server.
154
- #
155
- # Inputs:
156
- # [service] The URL of the calling service.
157
- # [ticket] The CAS ticket returned by the server in the URL.
158
- #
159
- # Returns an array: [ NetID, UIN ]
160
- def self.validate_ticket(service, ticket)
161
- RAILS_DEFAULT_LOGGER.debug "Validating ticket #{ticket}..."
162
-
163
- http = Net::HTTP.new(CAS.cas_server_host, CAS.cas_server_port)
164
- http.use_ssl = true
165
- page = http.get("/cas/serviceValidate?service=#{service}&ticket=#{ticket}").body
166
-
167
- RAILS_DEFAULT_LOGGER.debug "Got response:\n"+page
168
-
169
- # Parse XML document returned by CAS server
170
- doc = REXML::Document.new(page)
171
- return unless REXML::XPath.first(doc, 'cas:serviceResponse/cas:authenticationFailure',
172
- 'cas:serviceResponse' => 'http://www.yale.edu/tp/cas').nil?
173
- return if REXML::XPath.first(doc, 'cas:serviceResponse/cas:authenticationSuccess',
174
- 'cas:serviceResponse' => 'http://www.yale.edu/tp/cas').nil?
175
-
176
- cas_payload = CAS.process_cas_xml(doc)
177
-
178
- return cas_payload
179
- end
180
-
181
- def self.redirect_to_login(controller, service)
182
- RAILS_DEFAULT_LOGGER.debug "Redirecting to CAS login..."
183
- controller.redirect_to "https://#{CAS.cas_server_host}:#{CAS.cas_server_port}/cas/login?service=#{service}"
184
- end
185
- end
186
-
187
- # The following callbacks are meant to allow you to inject your business logic into the CAS filter
188
- # without the need to modify the original code.
189
-
190
- # This method is called after we've obtained the authenticated username from CAS.
191
- # It takes the username and returns some data based on that username. The data will be stored
192
- # under session[:user]. Here you may want to load your User model. i.e., something like this:
193
- #
194
- # module CAS
195
- # def self.load_user_data(username, cas_payload)
196
- # return User.find_by_username(username)
197
- # end
198
- # end
199
- #
200
- # The method also takes the cas_payload object containing data returned from CAS. You can use
201
- # this to set some additional data for your user object. By default, however, cas_payload doesn't
202
- # contain anything interesting -- only an array with the username. You can modify this by changing
203
- # the behaviour or CAS#process_cas_xml
204
- #
205
- def self.load_user_data(username, cas_payload)
206
- user = User.find_by_username(username)
207
- if user.nil?
208
- user = User.new(:username => username)
209
- else
210
- user.update_from_directory
211
- end
212
-
213
- user.last_login = Time.now
214
-
215
- return user
216
- end
217
-
218
- # This method is called after load_user_data has done its business and returned the user object.
219
- # Here you will probably want to call user.save (or something similar) to commit any changes
220
- # made to the user object during authentication.
221
- #
222
- # Example:
223
- #
224
- # module CAS
225
- # def self.load_user_data(user)
226
- # user.save
227
- # end
228
- # end
229
- #
230
- def self.save_user_data(user)
231
- user.save
232
- end
233
-
234
- # This method is called to parse the REXML doc returned by a CAS ticket validation request.
235
- # By default, the method returns a hash with a :username key containing the authenticated
236
- # username. However if your CAS server returns other information in its response, you may want
237
- # to modify this to have the cas_payload hash include other data (cas_payload is later fed
238
- # into the CAS#load_user_data callback).
239
- # Note that whatever you do, you almost definetly should return a hash with a :username key
240
- # since load_user_data expects its first parameter to be a username derived from this.
241
- #
242
- # Example:
243
- #
244
- # module CAS
245
- # def self.process_cas_xml(doc)
246
- # cas_payload = {}
247
- # cas_payload[:username] = REXML::XPath.first(doc,
248
- # 'cas:serviceResponse/cas:authenticationSuccess/cas:user',
249
- # 'cas:serviceResponse' => 'http://www.yale.edu/tp/cas').get_text.value
250
- # end
251
- # end
252
- #
253
- def self.process_cas_xml(doc)
254
- cas_payload = {}
255
- cas_payload[:username] = REXML::XPath.first(doc,
256
- 'cas:serviceResponse/cas:authenticationSuccess/cas:user',
257
- 'cas:serviceResponse' => 'http://www.yale.edu/tp/cas').get_text.value
258
- #cas_payload << REXML::XPath.first(doc,
259
- # 'cas:serviceResponse/cas:authenticationSuccess/cas:UIN',
260
- # 'cas:serviceResponse' => 'http://www.yale.edu/tp/cas').get_text.value
261
-
262
- return cas_payload
263
- end
264
-
265
- # Returns the full URI to the CAS logout page.
266
- # +service+ can be a full URI where the user should be re-directed after logging out and logging in again.
267
- # (However it is up to your CAS server's implementation whether to use this parameter for anything)
268
- def self.cas_logout_uri(service = nil)
269
- "https://#{CAS.cas_server_host}:#{CAS.cas_server_port}/cas/logout?service=#{service}"
270
- end
271
-
272
- # Setter for @@cas_server_host.
273
- def self.cas_server_host; @@cas_server_host; end
274
-
275
- # Setter for @@cas_server_port.
276
- def self.cas_server_port; @@cas_server_port; end
277
-
278
- # Setter for @@refresh_ticket_interval.
279
- def self.refresh_ticket_interval; @@refresh_ticket_interval; end
280
-
281
- # Getter for @@cas_server_host.
282
- def self.cas_server_host=(url); @@cas_server_host = url; end
283
-
284
- # Getter for @@cas_server_port.
285
- def self.cas_server_port=(port); @@cas_server_port = port; end
286
-
287
- # Getter for @@refresh_ticket_interval.
288
- def self.refresh_ticket_interval=(interval); @@refresh_ticket_interval = interval; end
289
- end