rubycas-client 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{CHANGES → CHANGELOG.txt} +13 -0
- data/History.txt +0 -0
- data/{LICENSE → LICENSE.txt} +9 -42
- data/Manifest.txt +16 -0
- data/README.txt +257 -0
- data/Rakefile +48 -14
- data/init.rb +4 -18
- data/lib/casclient.rb +79 -0
- data/lib/casclient/client.rb +209 -0
- data/lib/{cas_proxy_callback_controller.rb → casclient/frameworks/rails/cas_proxy_callback_controller.rb} +1 -1
- data/lib/casclient/frameworks/rails/filter.rb +149 -0
- data/lib/casclient/responses.rb +180 -0
- data/lib/casclient/tickets.rb +38 -0
- data/lib/casclient/version.rb +9 -0
- data/lib/rubycas-client.rb +1 -0
- data/setup.rb +1585 -0
- metadata +67 -47
- data/README +0 -223
- data/install.rb +0 -5
- data/lib/cas.rb +0 -194
- data/lib/cas_auth.rb +0 -553
- data/lib/cas_logger.rb +0 -27
metadata
CHANGED
@@ -1,62 +1,82 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.2
|
3
|
-
specification_version: 1
|
4
2
|
name: rubycas-client
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version:
|
7
|
-
date: 2007-12-21 12:37:15.306028 -05:00
|
8
|
-
summary: Client library for the CAS single-sign-on protocol.
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: matt@roughest.net
|
12
|
-
homepage: http://rubycas-client.rubyforge.org
|
13
|
-
rubyforge_project: rubycas-client
|
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:
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
4
|
+
version: 2.0.0
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
7
|
- Matt Zukowski
|
31
|
-
- Ola Bini
|
32
8
|
- Matt Walker
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
- lib/cas.rb
|
37
|
-
- lib/cas_proxy_callback_controller.rb
|
38
|
-
- lib/cas_auth.rb
|
39
|
-
- lib/cas_logger.rb
|
40
|
-
- CHANGES
|
41
|
-
- Rakefile
|
42
|
-
- README
|
43
|
-
- LICENSE
|
44
|
-
test_files: []
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
45
12
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
-
|
13
|
+
date: 2008-02-19 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: activesupport
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: Client library for the Central Authentication Service (CAS) protocol.
|
26
|
+
email: matt at roughest dot net
|
55
27
|
executables: []
|
56
28
|
|
57
29
|
extensions: []
|
58
30
|
|
31
|
+
extra_rdoc_files:
|
32
|
+
- CHANGELOG.txt
|
33
|
+
- History.txt
|
34
|
+
- LICENSE.txt
|
35
|
+
- Manifest.txt
|
36
|
+
- README.txt
|
37
|
+
files:
|
38
|
+
- CHANGELOG.txt
|
39
|
+
- History.txt
|
40
|
+
- LICENSE.txt
|
41
|
+
- Manifest.txt
|
42
|
+
- README.txt
|
43
|
+
- Rakefile
|
44
|
+
- init.rb
|
45
|
+
- lib/casclient.rb
|
46
|
+
- lib/casclient/client.rb
|
47
|
+
- lib/casclient/frameworks/rails/cas_proxy_callback_controller.rb
|
48
|
+
- lib/casclient/frameworks/rails/filter.rb
|
49
|
+
- lib/casclient/responses.rb
|
50
|
+
- lib/casclient/tickets.rb
|
51
|
+
- lib/casclient/version.rb
|
52
|
+
- lib/rubycas-client.rb
|
53
|
+
- setup.rb
|
54
|
+
has_rdoc: true
|
55
|
+
homepage: http://rubycas-client.rubyforge.org
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options:
|
58
|
+
- --main
|
59
|
+
- README.txt
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: "0"
|
73
|
+
version:
|
59
74
|
requirements: []
|
60
75
|
|
61
|
-
|
76
|
+
rubyforge_project: rubycas-client
|
77
|
+
rubygems_version: 1.0.1
|
78
|
+
signing_key:
|
79
|
+
specification_version: 2
|
80
|
+
summary: Client library for the Central Authentication Service (CAS) protocol.
|
81
|
+
test_files: []
|
62
82
|
|
data/README
DELETED
@@ -1,223 +0,0 @@
|
|
1
|
-
= RubyCAS-Client
|
2
|
-
|
3
|
-
Author:: Matt Zukowski <matt AT roughest DOT net>, Ola Bini <ola.bini AT ki DOT se>, Matt Walker <mwalker AT tamu DOT edu>
|
4
|
-
Copyright:: (c) 2006 Karolinska Institutet, portions (c) 2006 Urbacon Ltd.
|
5
|
-
License:: GNU Lesser General Public License v2.1 (LGPL 2.1)
|
6
|
-
Website:: http://rubyforge.org/projects/rubycas-client and http://code.google.com/p/rubycas-client
|
7
|
-
|
8
|
-
=== RubyCAS-Client is a Ruby client library for Yale's Central Authentication Service (CAS) protocol.
|
9
|
-
|
10
|
-
CAS provides a solid and secure single sign on solution for web-based applications. When a user logs on to your
|
11
|
-
CAS-enabled website, the CAS client checks with the CAS server to see if the user has been centrally authenticated.
|
12
|
-
If not, the user is redirected to your CAS server's web-based login page where they enter their credentials, and upon
|
13
|
-
successful authentication are redirected back to your client web application. If the user has been previously
|
14
|
-
authenticated with the CAS server (with their "ticket" being held as a session cookie), they are transparently
|
15
|
-
allowed to go about their business.
|
16
|
-
|
17
|
-
This client requires a CAS server to talk to. You can obtain a Java implementation of such a server as well as more info
|
18
|
-
about the CAS protocol here: http://www.ja-sig.org/products/cas
|
19
|
-
|
20
|
-
This CAS client library is designed to work easily with Rails, but can of course be used elsewhere.
|
21
|
-
|
22
|
-
== Installing
|
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
|
-
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.googlecode.com/svn/trunk/rubycas-client
|
28
|
-
|
29
|
-
Alternatively, the library is also available as a gem, which can be installed by:
|
30
|
-
|
31
|
-
gem install rubycas-client
|
32
|
-
|
33
|
-
If your Rails application is under Subversion control, you can also install the plugin as an svn:external, which will ensure that
|
34
|
-
you always have the latest version of RubyCAS-Client:
|
35
|
-
|
36
|
-
./script/plugin install -x http://rubycas-client.googlecode.com/svn/trunk/rubycas-client
|
37
|
-
|
38
|
-
Please contact the developers via the {RubyForge page}[http://rubyforge.org/projects/rubycas-client] if you have bug fixes
|
39
|
-
or enhancements you would like to contribute back.
|
40
|
-
|
41
|
-
== Examples
|
42
|
-
|
43
|
-
==== Using RubyCAS-Client in Rails controllers
|
44
|
-
|
45
|
-
<i>Note that from this point on we are assuming that you have a working CAS server up and running at an https URI.</i>
|
46
|
-
|
47
|
-
Somewhere in your <tt>config/environment.rb</tt> file add this:
|
48
|
-
|
49
|
-
require 'cas_auth'
|
50
|
-
CAS::Filter.cas_base_url = "https://login.example.com/cas"
|
51
|
-
|
52
|
-
You will also need to specify the server name where your CAS-protected app is running (i.e. the hostname of the app you are
|
53
|
-
adding CAS protection to, not the CAS server):
|
54
|
-
|
55
|
-
CAS::Filter.server_name = "yourapplication.example.com:3000"
|
56
|
-
|
57
|
-
Then, in your <tt>app/controllers/application.rb</tt> (or in whatever controller you want to add the CAS filter for):
|
58
|
-
|
59
|
-
before_filter CAS::Filter
|
60
|
-
|
61
|
-
That's it. You should now find that you are redirected to your CAS login page whenever you try to access any action
|
62
|
-
in your protected controller. You can of course qualify the <tt>before_filter</tt> as you would with any other ActionController
|
63
|
-
filter. For example:
|
64
|
-
|
65
|
-
before_filter CAS::Filter, :except => [ :unprotected_action, :another_unprotected_action ]
|
66
|
-
|
67
|
-
<b>Once the user has been authenticated, their authenticated username is available under <tt>request.username</tt></b>
|
68
|
-
(and also under <tt>session[:casfilteruser]</tt>). If you want to do something with this username (for example load a
|
69
|
-
user record from the database), you can append another filter method that checks for this value and does whatever you need
|
70
|
-
it to do.
|
71
|
-
|
72
|
-
==== A more complicated example
|
73
|
-
|
74
|
-
Here is a more complicated configuration showing most of the configuration options (this does not show proxy options however,
|
75
|
-
which are covered in the next section):
|
76
|
-
|
77
|
-
CAS::Filter.login_url = "https://login.example.com/cas/login" # the URI of the CAS login page
|
78
|
-
CAS::Filter.validate_url = "https://login.example.com/cas/serviceValidate" # the URI where CAS ticket validation requests are sent
|
79
|
-
CAS::Filter.server_name = "yourapplication.example.com:3000" # the server name of your CAS-protected application
|
80
|
-
CAS::Filter.renew = false # force re-authentication? see http://www.ja-sig.org/products/cas/overview/protocol
|
81
|
-
CAS::Filter.wrap_request = true # make the username available under request.username?
|
82
|
-
CAS::Filter.gateway = false # act as cas gateway? see http://www.ja-sig.org/products/cas/overview/protocol
|
83
|
-
CAS::Filter.session_username = :casfilteruser # this is the hash in the session where the authenticated username will be stored
|
84
|
-
|
85
|
-
Note that in this example we explicitly specified the login and validate URLs instead of letting RubyCAS-Client figure them out
|
86
|
-
based on <tt>CAS::Filter.cas_base_url</tt>.
|
87
|
-
|
88
|
-
==== Defining a 'logout' action
|
89
|
-
|
90
|
-
Your Rails application's controller(s) will probably have some sort of logout function. In it you will likely want reset the
|
91
|
-
user's session for your application, and then redirect to the CAS server's logout URL. Here's an example of how to do this:
|
92
|
-
|
93
|
-
def logout
|
94
|
-
reset_session
|
95
|
-
redirect_to CAS::Filter.logout_url(self, request.referer)
|
96
|
-
end
|
97
|
-
|
98
|
-
==== Gatewayed authentication
|
99
|
-
|
100
|
-
RubyCAS-Client supports gatewaying as of version 1.1.0. Gatewaying allows for optional CAS authentication, so that users who
|
101
|
-
already have a pre-existing CAS SSO session will be automatically authenticated for the gatewayed service, while those who
|
102
|
-
do not, will be allowed to access the service without authentication. This is useful for example when you want to show
|
103
|
-
some additional private content on a homepage to authenticated users, but also want unauthenticated users to be able to
|
104
|
-
access the page without first logging in.
|
105
|
-
|
106
|
-
For more information on using gatewaying, see CAS::GatewayFilter.
|
107
|
-
|
108
|
-
==== How to act as a CAS proxy
|
109
|
-
|
110
|
-
CAS 2.0 has a built-in mechanism that allows a CAS-authenticated application to pass on its authentication to other applications.
|
111
|
-
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
|
112
|
-
various other sites that run independently of the portal system (but are always accessed via the portal). The exact mechanism
|
113
|
-
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
|
114
|
-
is available at http://www.ja-sig.org/wiki/display/CAS/Proxy+CAS+Walkthrough.
|
115
|
-
|
116
|
-
RubyCAS-Client fully supports proxying, so a CAS-protected Rails application can act as a CAS proxy.
|
117
|
-
|
118
|
-
Additionally, RubyCAS-Client comes with a controller that can act as a CAS proxy callback receiver. This is necessary because
|
119
|
-
when your application requests to act as a CAS proxy, the CAS server must contact your application to deposit the proxy-granting-ticket
|
120
|
-
(PGT). Note that in this case the CAS server CONTACTS YOU, rather than you contacting the CAS server (as in all other CAS operations).
|
121
|
-
|
122
|
-
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,
|
123
|
-
all you need to do is this:
|
124
|
-
|
125
|
-
In your <tt>config/environment.rb</tt>:
|
126
|
-
|
127
|
-
CAS::Filter.cas_base_url = "https://login.example.com/cas"
|
128
|
-
CAS::Filter.proxy_callback_url = "https://yourrailsapp.com/cas_proxy_callback/receive_pgt"
|
129
|
-
CAS::Filter.proxy_retrieval_url = "https://yourrailsapp.com/cas_proxy_callback/retrieve_pgt"
|
130
|
-
|
131
|
-
In <tt>config/routes.rb</tt> make sure that you have a route that will allow requests to /cas_proxy_callback/:action to be routed to the
|
132
|
-
CasProxyCallbackController. This should work as-is with the standard Rails routes setup, but if you have disabled the default
|
133
|
-
route, you should add the following:
|
134
|
-
|
135
|
-
map.cas_proxy_callback 'cas_proxy_callback/:action', :controller => 'cas_proxy_callback'
|
136
|
-
|
137
|
-
Now here's a big giant caveat: <b>your CAS callback application and your CAS proxy application must run on separate Rails servers</b>.
|
138
|
-
In other words, if you want a Rails app to act as a CAS ticket-granting proxy, the cas_proxy_callback controller
|
139
|
-
must run on a different server. This is because Rails does not properly support handling of concurrent requests. The CAS proxy mechanism
|
140
|
-
acts in such a way that if your proxy application and your callback controller were on the same server
|
141
|
-
you would end up with a deadlock (the CAS server would be waiting for its callback to be accepted by your Rails server,
|
142
|
-
but your Rails server wouldn't respond to the CAS server's callback until the CAS server responded back first).
|
143
|
-
|
144
|
-
The simplest workaround is this:
|
145
|
-
|
146
|
-
1. Create an empty rails app (i.e. something like <tt>rails cas_proxy_callback</tt>)
|
147
|
-
2. Make sure that you have the CAS plugin installed. If you installed it as a gem, you don't have to do anything since
|
148
|
-
it is already installed. If you want to install as a plugin, see the instructions in the "Installing" section above.
|
149
|
-
3. Make sure that the server is up and running, and configure your proxy_callback_url and proxy_retrieval_url to point
|
150
|
-
to the new server as described above (or rather, make Pound point to the new server, if that's how you're handling https).
|
151
|
-
|
152
|
-
That's it. The proxy_callback_controller doesn't require any additional configuration. It doesn't access the database
|
153
|
-
or anything of that sort.
|
154
|
-
|
155
|
-
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
|
156
|
-
to authenticate another application:
|
157
|
-
|
158
|
-
service_uri = "http://some.other.application"
|
159
|
-
proxy_granting_ticket = session[:casfilterpgt]
|
160
|
-
ticket = CAS::Filter.request_proxy_ticket(service_uri, proxy_granting_ticket).proxy_ticket
|
161
|
-
|
162
|
-
<tt>ticket</tt> should now contain a valid service ticket. You can use it to authenticate by sending it and the service URI
|
163
|
-
as query parameters to your target application:
|
164
|
-
|
165
|
-
http://some.other.application?service=#{CGI.encode(ticket.target_service)}&ticket=#{ticket.proxy_ticket}
|
166
|
-
|
167
|
-
This is of course assuming that some.other.application is also protected by the CAS filter.
|
168
|
-
Note that you should always URI-encode your service parameter inside URIs!
|
169
|
-
|
170
|
-
Note that CAS::Filter#request_proxy_ticket actually returns a CAS::ProxyTicketRequest object, which is why we need to call
|
171
|
-
#proxy_ticket on it to retrieve the actual service ticket.
|
172
|
-
|
173
|
-
For extra security -- and you will likely want to do this on production machines in the wild -- in the proxied app's configuration
|
174
|
-
(some.other.appliction in this example) you can specify the list of authorized proxies. For example, on your proxied app the CAS
|
175
|
-
configuration might look something like this:
|
176
|
-
|
177
|
-
CAS::Filter.cas_base_url = "https://login.example.com/cas"
|
178
|
-
CAS::Filter.server_name = "some.other.application"
|
179
|
-
CAS::Filter.authorized_proxies = ["https://yourrailsapp.com/cas/proxy_callback/receive_pgt"]
|
180
|
-
|
181
|
-
If no authorized proxies are given, the filter will accept receipts from any proxy.
|
182
|
-
|
183
|
-
===== Additional notes and caveats:
|
184
|
-
|
185
|
-
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.
|
186
|
-
Also, note that CAS::Filter#request_proxy_ticket will URI-encode the service_uri before passing it to the CAS server, and the service
|
187
|
-
value must henceforth always be passed as URI-encoded (this can be problematic when your proxied application uses some CAS client other than
|
188
|
-
RubyCAS-Client).
|
189
|
-
|
190
|
-
<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
|
191
|
-
the bundled cas_proxy_callback controller, you will have to host your application on an https-enabled server. This can be a bit
|
192
|
-
tricky with Rails. WEBrick's SSL support is difficult to configure, and Mongrel doesn't support SSL at all. One workaround is to
|
193
|
-
use a reverse proxy like Pound[http://www.apsis.ch/pound/], which will accept https connections and locally re-route them
|
194
|
-
to your Rails application. Also, note that <i>self-signed SSL certificates likely won't work</i>. You will probably need to use
|
195
|
-
a real certificate purchased from a trusted CA authority (there are ways around this, but good luck :)
|
196
|
-
|
197
|
-
|
198
|
-
== SSL Support
|
199
|
-
|
200
|
-
If you are getting an error on net/https -- something like this:
|
201
|
-
|
202
|
-
no such file to load -- net/https
|
203
|
-
|
204
|
-
Then make sure the library for open SSL is installed. For example, on an Debian/Ubuntu system issue the following:
|
205
|
-
|
206
|
-
sudo apt-get install libopenssl-ruby
|
207
|
-
|
208
|
-
|
209
|
-
== License
|
210
|
-
|
211
|
-
This program is free software; you can redistribute it and/or modify
|
212
|
-
it under the terms of the GNU Lesser General Public License as published by
|
213
|
-
the Free Software Foundation; either version 2 of the License, or
|
214
|
-
(at your option) any later version.
|
215
|
-
|
216
|
-
This program is distributed in the hope that it will be useful,
|
217
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
218
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
219
|
-
GNU General Public License for more details.
|
220
|
-
|
221
|
-
You should have received a copy of the GNU Lesser General Public License
|
222
|
-
along with this program (see the file called LICENSE); if not, write to the
|
223
|
-
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
data/install.rb
DELETED
data/lib/cas.rb
DELETED
@@ -1,194 +0,0 @@
|
|
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
|
-
class MalformedServerResponseException < CASException
|
12
|
-
end
|
13
|
-
|
14
|
-
class Receipt
|
15
|
-
attr_accessor :validate_url, :pgt_iou, :primary_authentication, :proxy_callback_url, :proxy_list, :user_name
|
16
|
-
|
17
|
-
def primary_authentication?
|
18
|
-
primary_authentication
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize(ptv)
|
22
|
-
if !ptv.successful_authentication?
|
23
|
-
begin
|
24
|
-
ptv.validate
|
25
|
-
rescue ValidationException=>vald
|
26
|
-
raise AuthenticationException, "Unable to validate ProxyTicketValidator [#{ptv}] [#{vald}]"
|
27
|
-
end
|
28
|
-
raise AuthenticationException, "Unable to validate ProxyTicketValidator because of no success with validation[#{ptv}]" unless ptv.successful_authentication?
|
29
|
-
end
|
30
|
-
self.validate_url = ptv.validate_url
|
31
|
-
self.pgt_iou = ptv.pgt_iou
|
32
|
-
self.user_name = ptv.user
|
33
|
-
self.proxy_callback_url = ptv.proxy_callback_url
|
34
|
-
self.proxy_list = ptv.proxy_list
|
35
|
-
self.primary_authentication = ptv.renewed?
|
36
|
-
raise AuthenticationException, "Validation of [#{ptv}] did not result in an internally consistent Receipt" unless validate
|
37
|
-
end
|
38
|
-
|
39
|
-
def proxied?
|
40
|
-
!proxy_list.empty?
|
41
|
-
end
|
42
|
-
|
43
|
-
def proxying_service
|
44
|
-
proxy_list.first
|
45
|
-
end
|
46
|
-
|
47
|
-
def to_s
|
48
|
-
"[#{super} - userName=[#{user_name}] validateUrl=[#{validate_url}] proxyCallbackUrl=[#{proxy_callback_url}] pgtIou=[#{pgt_iou}] proxyList=[#{proxy_list}]"
|
49
|
-
end
|
50
|
-
|
51
|
-
def validate
|
52
|
-
user_name &&
|
53
|
-
validate_url &&
|
54
|
-
proxy_list &&
|
55
|
-
!(primary_authentication? && !proxy_list.empty?) # May not be both primary authenitication and proxied.
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
class AbstractCASResponse
|
60
|
-
attr_reader :error_code, :error_message, :successful_authentication
|
61
|
-
|
62
|
-
def self.retrieve(uri_str)
|
63
|
-
prs = URI.parse(uri_str)
|
64
|
-
https = Net::HTTP.new(prs.host,prs.port)
|
65
|
-
https.use_ssl = (prs.scheme == 'https') # patch by rubywmg to allow non-SSL URLs for demo purposes
|
66
|
-
https.start { |conn|
|
67
|
-
# TODO: make sure that HTTP status code in the response is 200... maybe throw exception if is 500?
|
68
|
-
conn.get("#{prs.path}?#{prs.query}").body.strip
|
69
|
-
}
|
70
|
-
end
|
71
|
-
|
72
|
-
protected
|
73
|
-
def parse_unsuccessful(elm)
|
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
|
-
begin
|
81
|
-
doc = REXML::Document.new str
|
82
|
-
rescue REXML::ParseException => e
|
83
|
-
raise MalformedServerResponseException, "BAD RESPONSE FROM CAS SERVER:\n#{str.inspect}\n\nEXCEPTION:\n#{e}"
|
84
|
-
end
|
85
|
-
|
86
|
-
unless doc.elements && doc.elements["cas:serviceResponse"]
|
87
|
-
raise MalformedServerResponseException, "BAD RESPONSE FROM CAS SERVER:\n#{str.inspect}\n\nXML DOC:\n#{doc.inspect}"
|
88
|
-
end
|
89
|
-
|
90
|
-
resp = doc.elements["cas:serviceResponse"].elements[1]
|
91
|
-
|
92
|
-
if successful_response? resp
|
93
|
-
parse_successful(resp)
|
94
|
-
else
|
95
|
-
parse_unsuccessful(resp)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
class ServiceTicketValidator < AbstractCASResponse
|
101
|
-
attr_accessor :validate_url, :proxy_callback_url, :renew, :service_ticket, :service
|
102
|
-
attr_reader :pgt_iou, :user, :entire_response
|
103
|
-
|
104
|
-
def renewed?
|
105
|
-
renew
|
106
|
-
end
|
107
|
-
|
108
|
-
def successful_authentication?
|
109
|
-
successful_authentication
|
110
|
-
end
|
111
|
-
|
112
|
-
def validate
|
113
|
-
raise ValidationException, "must set validation URL and ticket" if validate_url.nil? || service_ticket.nil?
|
114
|
-
clear!
|
115
|
-
@attempted_authentication = true
|
116
|
-
url_building = "#{validate_url}#{(url_building =~ /\?/)?'&':'?'}service=#{CGI.escape(service)}&ticket=#{service_ticket}"
|
117
|
-
url_building += "&pgtUrl=#{proxy_callback_url}" if proxy_callback_url
|
118
|
-
url_building += "&renew=true" if renew
|
119
|
-
@@entire_response = ServiceTicketValidator.retrieve url_building
|
120
|
-
parse @@entire_response
|
121
|
-
end
|
122
|
-
|
123
|
-
def clear!
|
124
|
-
@user = @pgt_iou = @error_message = nil
|
125
|
-
@successful_authentication = @attempted_authentication = false
|
126
|
-
end
|
127
|
-
|
128
|
-
def to_s
|
129
|
-
"[#{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}]]"
|
130
|
-
end
|
131
|
-
|
132
|
-
protected
|
133
|
-
def parse_successful(elm)
|
134
|
-
# puts "successful"
|
135
|
-
@user = elm.elements["cas:user"] && elm.elements["cas:user"].text.strip
|
136
|
-
# puts "user: #{@user}"
|
137
|
-
@pgt_iou = elm.elements["cas:proxyGrantingTicket"] && elm.elements["cas:proxyGrantingTicket"].text.strip
|
138
|
-
# puts "pgt_iou: #{@pgt_iou}"
|
139
|
-
@successful_authentication = true
|
140
|
-
end
|
141
|
-
|
142
|
-
def successful_response?(resp)
|
143
|
-
resp.name == "authenticationSuccess"
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
class ProxyTicketValidator < ServiceTicketValidator
|
148
|
-
attr_reader :proxy_list
|
149
|
-
@@response_prefix = "proxy"
|
150
|
-
|
151
|
-
def initialize
|
152
|
-
super
|
153
|
-
@proxy_list = []
|
154
|
-
end
|
155
|
-
|
156
|
-
def clear!
|
157
|
-
super
|
158
|
-
@proxy_list = []
|
159
|
-
end
|
160
|
-
|
161
|
-
protected
|
162
|
-
def parse_successful(elm)
|
163
|
-
super(elm)
|
164
|
-
|
165
|
-
proxies = elm.elements["cas:proxies"]
|
166
|
-
if proxies
|
167
|
-
proxies.elements.each("cas:proxy") { |prox|
|
168
|
-
@proxy_list ||= []
|
169
|
-
@proxy_list << prox.text.strip
|
170
|
-
}
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
class ProxyTicketRequest < AbstractCASResponse
|
176
|
-
attr_accessor :proxy_url, :target_service, :pgt
|
177
|
-
attr_reader :proxy_ticket
|
178
|
-
|
179
|
-
def request
|
180
|
-
url_building = "#{proxy_url}#{(url_building =~ /\?/)?'&':'?'}pgt=#{pgt}&targetService=#{CGI.escape(target_service)}"
|
181
|
-
@@entire_response = ServiceTicketValidator.retrieve url_building
|
182
|
-
parse @@entire_response
|
183
|
-
end
|
184
|
-
|
185
|
-
protected
|
186
|
-
def parse_successful(elm)
|
187
|
-
@proxy_ticket = elm.elements["cas:proxyTicket"] && elm.elements["cas:proxyTicket"].text.strip
|
188
|
-
end
|
189
|
-
|
190
|
-
def successful_response?(resp)
|
191
|
-
resp.name == "proxySuccess"
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|