rubycas-client 0.10.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +46 -0
- data/README +19 -8
- data/init.rb +11 -3
- data/lib/cas.rb +3 -2
- data/lib/cas_auth.rb +221 -113
- data/lib/cas_logger.rb +27 -0
- data/lib/cas_proxy_callback_controller.rb +6 -4
- metadata +8 -5
data/CHANGES
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
= RubyCAS-Client Changelog
|
2
|
+
|
3
|
+
== Version 0.11.0
|
4
|
+
|
5
|
+
* Added this changelog to advise users of major changes to the library.
|
6
|
+
|
7
|
+
* Large chunks of the library have been re-written. Beware of the possibility
|
8
|
+
of new bugs (although the re-write was meant to fix a whole slew of existing
|
9
|
+
bugs, so you're almost certainly better off upgrading).
|
10
|
+
|
11
|
+
* service and targetService parameters in requests are now properly URI-encoded,
|
12
|
+
so the filter should behave properly when your service has query parameters.
|
13
|
+
Thanks sakazuki for pointing out the problem.
|
14
|
+
|
15
|
+
* You can now force the CAS client to re-authenticate itself with the CAS server
|
16
|
+
(i.e. override the authentication stored in the session) by providing a new
|
17
|
+
service ticket in the URI. In other words, the client will authenticate with
|
18
|
+
CAS if: a) you have a 'ticket' parameter in the URI, and there is currently no
|
19
|
+
authentication info in the session, or b) you have a 'ticket' parameter in the
|
20
|
+
URI and this ticket is different than the ticket that was used to authenticat
|
21
|
+
the existing session. This is especially useful when you are using CAS proxying,
|
22
|
+
since it allows you to force re-authentication in proxied applications (for
|
23
|
+
example, when the user has logged out and a new user has logged in in the parent
|
24
|
+
proxy-granting application).
|
25
|
+
|
26
|
+
* If your service URI has a 'ticket' parameter, it will now be automatically
|
27
|
+
removed when passing the service as a parameter in any CAS request. This is
|
28
|
+
done because at least some CAS servers will happily accept a service URI with
|
29
|
+
a 'ticket' parameter, which will result in a URI with multiple 'ticket'
|
30
|
+
parameters once you are redirected back to CAS (and that in turn can result
|
31
|
+
in an endless redirection loop).
|
32
|
+
|
33
|
+
* Logging has been greatly improved, which should make debugging your CAS
|
34
|
+
installation much easier. Look for the logs under log/cas_client_RAILS_ENV.log
|
35
|
+
|
36
|
+
* When you install RubyCAS-Client as a Rails plugin, it will now by default
|
37
|
+
use a custom logger. You can change this by explicitly setting your own
|
38
|
+
logger in your environment.rb, or by modifying the plugin's init.rb.
|
39
|
+
|
40
|
+
* CasProxyCallbackController no longer checks to make sure that the incoming
|
41
|
+
request is secure. The check is impossible since the secure header is not
|
42
|
+
passed on by at least some reverse proxies (like Pound), and if you are using
|
43
|
+
the callback controller then you are almost certainly also using a reverse
|
44
|
+
proxy.
|
45
|
+
|
46
|
+
* Cleaned up and updated documentation, fixed some example code.
|
data/README
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= RubyCAS-Client
|
2
2
|
|
3
|
-
Author::
|
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
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 and http://code.google.com/p/rubycas-client
|
@@ -22,15 +22,16 @@ This CAS client library is designed to work easily with Rails, but can of course
|
|
22
22
|
== Installing
|
23
23
|
|
24
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:
|
25
|
+
However, probably the easiest way to install CAS support into your Rails app is via the plugins facility:
|
26
26
|
|
27
|
-
./script/plugin install http://rubycas-client.
|
27
|
+
./script/plugin install http://rubycas-client.googlecode.com/svn/trunk/rubycas-client
|
28
28
|
|
29
29
|
Alternatively, the library is also available as a gem, which can be installed by:
|
30
30
|
|
31
31
|
gem install rubycas-client
|
32
32
|
|
33
|
-
|
33
|
+
If your Rails application is under subversion control, you can also install the plugin as an external, which will ensure that
|
34
|
+
you are always up to date:
|
34
35
|
|
35
36
|
./script/plugin install -x http://rubycas-client.googlecode.com/svn/trunk/rubycas-client
|
36
37
|
|
@@ -45,6 +46,12 @@ Somewhere in your <tt>config/environment.rb</tt> file add this (assuming that yo
|
|
45
46
|
you'll need to <tt>require 'cas_auth'</tt> and <tt>require 'cas_proxy_callback_controller'</tt>):
|
46
47
|
|
47
48
|
CAS::Filter.cas_base_url = "https://login.example.com/cas"
|
49
|
+
|
50
|
+
You will also probably (but not necessarily) need to specify the server name where your CAS-protected app is running:
|
51
|
+
|
52
|
+
CAS::Filter.server_name = "yourapplication.example.com:3000"
|
53
|
+
|
54
|
+
The above setting might not be necessary if your application is running on the standard port 80.
|
48
55
|
|
49
56
|
Then, in your <tt>app/controllers/application.rb</tt> (or in whatever controller you want to add the CAS filter for):
|
50
57
|
|
@@ -127,14 +134,18 @@ to authenticate another application:
|
|
127
134
|
|
128
135
|
service_uri = "http://some.other.application"
|
129
136
|
proxy_granting_ticket = session[:casfilterpgt]
|
130
|
-
ticket = CAS::Filter.request_proxy_ticket(service_uri, proxy_granting_ticket)
|
137
|
+
ticket = CAS::Filter.request_proxy_ticket(service_uri, proxy_granting_ticket).proxy_ticket
|
131
138
|
|
132
|
-
<tt>ticket</tt> should now contain a valid service ticket. You can use it to authenticate
|
139
|
+
<tt>ticket</tt> should now contain a valid service ticket. You can use it to authenticate by sending it and the service URI
|
133
140
|
as query parameters to your target application:
|
134
141
|
|
135
|
-
http://some.other.application?service=#{ticket.target_service}&ticket=#{ticket.proxy_ticket}
|
142
|
+
http://some.other.application?service=#{CGI.encode(ticket.target_service)}&ticket=#{ticket.proxy_ticket}
|
136
143
|
|
137
|
-
This is of course assuming that some.other.application is also protected by the CAS filter.
|
144
|
+
This is of course assuming that some.other.application is also protected by the CAS filter.
|
145
|
+
Note that you should always URI-encode your service parameter inside URIs!
|
146
|
+
|
147
|
+
Note that CAS::Filter#request_proxy_ticket actually returns a CAS::ProxyTicketRequest object, which is why we need to call
|
148
|
+
#proxy_ticket on it to retrieve the actual service ticket.
|
138
149
|
|
139
150
|
For extra security -- and you will likely want to do this on production machines in the wild -- in the proxied app's configuration
|
140
151
|
(some.other.appliction in this example) you can specify the list of authorized proxies. For example, on your proxied app the CAS
|
data/init.rb
CHANGED
@@ -1,12 +1,20 @@
|
|
1
1
|
require 'cas_auth'
|
2
|
+
require 'cas_logger'
|
2
3
|
require 'cas_proxy_callback_controller'
|
3
4
|
|
4
5
|
#CAS::Filter.logger = RAILS_DEFAULT_LOGGER if !RAILS_DEFAULT_LOGGER.nil?
|
5
6
|
#CAS::Filter.logger = config.logger if !config.logger.nil?
|
6
7
|
|
7
|
-
CAS::Filter.logger = Logger.new
|
8
|
-
CAS::Filter.logger.
|
8
|
+
CAS::Filter.logger = CAS::Logger.new("#{RAILS_ROOT}/log/cas_client_#{RAILS_ENV}.log", 1024000)
|
9
|
+
CAS::Filter.logger.formatter = CAS::Logger::Formatter.new
|
10
|
+
|
11
|
+
#if RAILS_ENV == "production"
|
12
|
+
# CAS::Filter.logger.level = Logger::WARN
|
13
|
+
#else
|
14
|
+
CAS::Filter.logger.level = Logger::DEBUG
|
15
|
+
#end
|
16
|
+
|
9
17
|
|
10
18
|
#class ActionController::Base
|
11
19
|
# append_before_filter CAS::Filter
|
12
|
-
#end
|
20
|
+
#end
|
data/lib/cas.rb
CHANGED
@@ -57,6 +57,7 @@ module CAS
|
|
57
57
|
class AbstractCASResponse
|
58
58
|
|
59
59
|
def self.retrieve(uri_str)
|
60
|
+
# puts uri_str
|
60
61
|
prs = URI.parse(uri_str)
|
61
62
|
# puts prs.inspect
|
62
63
|
https = Net::HTTP.new(prs.host,prs.port)
|
@@ -105,7 +106,7 @@ module CAS
|
|
105
106
|
raise ValidationException, "must set validation URL and ticket" if validate_url.nil? || service_ticket.nil?
|
106
107
|
clear!
|
107
108
|
@attempted_authentication = true
|
108
|
-
url_building = "#{validate_url}#{(url_building =~ /\?/)?'&':'?'}service=#{service}&ticket=#{service_ticket}"
|
109
|
+
url_building = "#{validate_url}#{(url_building =~ /\?/)?'&':'?'}service=#{CGI.escape(service)}&ticket=#{service_ticket}"
|
109
110
|
url_building += "&pgtUrl=#{proxy_callback_url}" if proxy_callback_url
|
110
111
|
url_building += "&renew=true" if renew
|
111
112
|
@@entire_response = ServiceTicketValidator.retrieve url_building
|
@@ -169,7 +170,7 @@ module CAS
|
|
169
170
|
attr_reader :proxy_ticket
|
170
171
|
|
171
172
|
def request
|
172
|
-
url_building = "#{proxy_url}#{(url_building =~ /\?/)?'&':'?'}
|
173
|
+
url_building = "#{proxy_url}#{(url_building =~ /\?/)?'&':'?'}pgt=#{pgt}&targetService=#{CGI.escape(target_service)}"
|
173
174
|
# puts "REQUESTING:"+url_building
|
174
175
|
@@entire_response = ServiceTicketValidator.retrieve url_building
|
175
176
|
# puts @@entire_response.to_s
|
data/lib/cas_auth.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
-
require '
|
1
|
+
require 'cgi'
|
2
2
|
require 'logger'
|
3
3
|
|
4
|
+
# these requires are needed when outside of a Rails app context (e.g. in unit tests)
|
5
|
+
require 'rubygems'
|
6
|
+
require 'active_support'
|
7
|
+
require 'action_controller'
|
8
|
+
|
4
9
|
require File.dirname(File.expand_path(__FILE__))+'/cas'
|
5
10
|
|
6
11
|
module CAS
|
@@ -85,6 +90,7 @@ module CAS
|
|
85
90
|
|
86
91
|
|
87
92
|
class << self
|
93
|
+
|
88
94
|
# Retrieves the current Logger instance
|
89
95
|
def logger
|
90
96
|
CAS::LOGGER
|
@@ -92,32 +98,34 @@ module CAS
|
|
92
98
|
def logger=(val)
|
93
99
|
CAS::LOGGER.set_logger(val)
|
94
100
|
end
|
95
|
-
|
101
|
+
|
96
102
|
alias :log :logger
|
97
103
|
alias :log= :logger=
|
98
|
-
|
104
|
+
|
99
105
|
def create_logout_url
|
100
106
|
if !@@logout_url && @@login_url =~ %r{^(.+?)/[^/]*$}
|
101
107
|
@@logout_url = "#{$1}/logout"
|
102
108
|
end
|
103
|
-
logger.info "Created logout
|
109
|
+
logger.info "Created logout url: #{@@logout_url}"
|
104
110
|
end
|
105
111
|
|
106
112
|
def logout_url(controller)
|
107
113
|
create_logout_url unless @@logout_url
|
108
114
|
url = redirect_url(controller,@@logout_url)
|
109
|
-
logger.info "
|
115
|
+
logger.info "Logout url is: #{url}"
|
110
116
|
url
|
111
117
|
end
|
112
118
|
|
113
|
-
def logout_url=(
|
114
|
-
@@logout_url =
|
119
|
+
def logout_url=(url)
|
120
|
+
@@logout_url = url
|
121
|
+
logger.info "Set logout url to: #{url}"
|
115
122
|
end
|
116
123
|
|
117
124
|
def cas_base_url=(url)
|
118
125
|
CAS::Filter.login_url = "#{url}/login"
|
119
126
|
CAS::Filter.validate_url = "#{url}/proxyValidate"
|
120
127
|
CAS::Filter.proxy_url = "#{url}/proxy"
|
128
|
+
logger.info "Set CAS base url to: #{url}"
|
121
129
|
end
|
122
130
|
|
123
131
|
def fake
|
@@ -127,14 +135,17 @@ module CAS
|
|
127
135
|
def fake=(val)
|
128
136
|
if val.nil?
|
129
137
|
alias :filter :filter_r
|
138
|
+
logger.info "Will use real filter"
|
130
139
|
else
|
131
140
|
alias :filter :filter_f
|
141
|
+
logger.warn "Will use fake filter"
|
142
|
+
end
|
143
|
+
@@fake = val
|
132
144
|
end
|
133
|
-
|
134
|
-
end
|
135
|
-
|
145
|
+
|
136
146
|
def filter_f(controller)
|
137
|
-
|
147
|
+
logger.break
|
148
|
+
logger.warn("Using fake CAS filter")
|
138
149
|
username = @@fake
|
139
150
|
if :failure == @@fake
|
140
151
|
return false
|
@@ -143,147 +154,244 @@ module CAS
|
|
143
154
|
elsif Proc === @@fake
|
144
155
|
username = @@fake.call(controller)
|
145
156
|
end
|
146
|
-
logger.
|
157
|
+
logger.info("The username set by the fake filter is: #{username}")
|
147
158
|
controller.session[@@session_username] = username
|
148
159
|
return true
|
149
160
|
end
|
150
161
|
|
151
162
|
def filter_r(controller)
|
152
|
-
logger.
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
#
|
169
|
-
|
170
|
-
|
171
|
-
|
163
|
+
logger.break
|
164
|
+
logger.info("Using real CAS filter in controller: #{controller}")
|
165
|
+
session_receipt = controller.session[:casfilterreceipt]
|
166
|
+
session_ticket = controller.session[:caslastticket]
|
167
|
+
ticket = controller.params[:ticket]
|
168
|
+
|
169
|
+
is_valid = false
|
170
|
+
|
171
|
+
if ticket and (!session_ticket or session_ticket != ticket)
|
172
|
+
log.info "A ticket parameter was given in the URI: #{ticket} and "+
|
173
|
+
(!session_ticket ? "there is no previous ticket for this session" :
|
174
|
+
"the ticket is different than the previous ticket, which was #{session_ticket}")
|
175
|
+
|
176
|
+
receipt = get_receipt_for_ticket(ticket, controller)
|
177
|
+
|
178
|
+
if receipt && validate_receipt(receipt)
|
179
|
+
logger.info("Receipt for ticket request #{ticket} is valid, belongs to user #{receipt.user_name}, and will be stored in the session.")
|
180
|
+
controller.session[:casfilterreceipt] = receipt
|
181
|
+
controller.session[:caslastticket] = ticket
|
182
|
+
controller.session[@@session_username] = receipt.user_name
|
172
183
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
retrieve_url = "#{@@proxy_retrieval_url}?pgtIou=#{receipt.pgt_iou}"
|
182
|
-
logger.info("retrieving pgt from: #{retrieve_url}")
|
183
|
-
controller.session[:casfilterpgt] = CAS::ServiceTicketValidator.retrieve(retrieve_url)
|
184
|
-
ActionController::Base.allow_concurrency = false
|
184
|
+
if receipt.pgt_iou
|
185
|
+
logger.info("Receipt has a proxy-granting ticket IOU. Attempting to retrieve the proxy-granting ticket...")
|
186
|
+
pgt = retrieve_pgt(receipt)
|
187
|
+
if pgt
|
188
|
+
log.debug("Got PGT #{pgt} for PGT IOU #{receipt.pgt_iou}. This will be stored in the session.")
|
189
|
+
controller.session[:casfilterpgt] = pgt
|
190
|
+
else
|
191
|
+
log.warning("Failed to retrieve a PGT for PGT IOU #{receipt.pgt_iou}!")
|
185
192
|
end
|
186
|
-
|
187
|
-
valid = true
|
188
193
|
end
|
194
|
+
|
195
|
+
is_valid = true
|
189
196
|
else
|
190
|
-
|
191
|
-
|
192
|
-
if did_gateway
|
193
|
-
if controller.session[@@session_username]
|
194
|
-
valid = true
|
195
|
-
else
|
196
|
-
controller.session[:casfiltergateway] = true
|
197
|
-
end
|
197
|
+
if receipt
|
198
|
+
log.warn "get_receipt_for_ticket() for ticket #{ticket} did not return a receipt!"
|
198
199
|
else
|
200
|
+
log.warn "Receipt was invalid for ticket #{ticket}!"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
elsif session_receipt
|
205
|
+
|
206
|
+
log.info "Validating receipt from the session because " +
|
207
|
+
(ticket ? "the given ticket #{ticket} is the same as the old ticket" : "there was no ticket given in the URI") + "."
|
208
|
+
log.debug "The session receipt is: #{session_receipt}"
|
209
|
+
|
210
|
+
is_valid = validate_receipt(session_receipt)
|
211
|
+
|
212
|
+
if is_valid
|
213
|
+
log.info "The session receipt is VALID"
|
214
|
+
else
|
215
|
+
log.warn "The session receipt is NOT VALID!"
|
216
|
+
end
|
217
|
+
|
218
|
+
else
|
219
|
+
|
220
|
+
log.info "No ticket was given and we do not have a receipt in the session."
|
221
|
+
|
222
|
+
did_gateway = controller.session[:casfiltergateway]
|
223
|
+
raise CASException, "Can't redirect without login url" unless @@login_url
|
224
|
+
|
225
|
+
if did_gateway
|
226
|
+
if controller.session[@@session_username]
|
227
|
+
log.info "We gatewayed and have a username stored in the session. The gateway was therefore successful."
|
228
|
+
is_valid = true
|
229
|
+
else
|
230
|
+
log.debug "We gatewayed but do not have a username stored in the session, so we will keep session[:casfiltergateway] true"
|
199
231
|
controller.session[:casfiltergateway] = true
|
200
232
|
end
|
233
|
+
else
|
234
|
+
log.info "We did not gateway, so we will notify the filter that the next request is being gatewayed by setting sesson[:casfiltergateway} to true"
|
235
|
+
controller.session[:casfiltergateway] = true
|
201
236
|
end
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
if is_valid
|
241
|
+
logger.info "This request is successfully CAS authenticated for user #{controller.session[@@session_username]}!"
|
242
|
+
return true
|
243
|
+
else
|
244
|
+
logger.info "This request is NOT CAS authenticated, so we will redirect to the login page at: #{redirect_url(controller)}"
|
245
|
+
controller.send :redirect_to, redirect_url(controller) and return false
|
202
246
|
end
|
203
|
-
logger.info("will send redirect #{redirect_url(controller)}") if !valid
|
204
|
-
controller.send :redirect_to,redirect_url(controller) if !valid
|
205
|
-
return valid
|
206
247
|
end
|
207
248
|
alias :filter :filter_r
|
208
|
-
|
209
|
-
|
249
|
+
|
250
|
+
|
210
251
|
def request_proxy_ticket(target_service, pgt)
|
211
252
|
r = ProxyTicketRequest.new
|
212
253
|
r.proxy_url = @@proxy_url
|
213
|
-
r.target_service =
|
254
|
+
r.target_service = target_service
|
214
255
|
r.pgt = pgt
|
215
|
-
|
216
|
-
raise "Cannot request a proxy ticket for service #{r.target_service} because no proxy granting ticket (PGT) has been set." unless r.pgt
|
256
|
+
|
257
|
+
raise CAS::ProxyGrantingNotAvailable, "Cannot request a proxy ticket for service #{r.target_service} because no proxy granting ticket (PGT) has been set." unless r.pgt
|
217
258
|
|
218
|
-
logger.info("
|
259
|
+
logger.info("Requesting proxy ticket for service: #{r.target_service} with PGT #{pgt}")
|
219
260
|
r.request
|
220
261
|
|
221
262
|
if r.proxy_ticket
|
222
|
-
logger.info("
|
263
|
+
logger.info("Got proxy ticket #{r.proxy_ticket} for service #{r.target_service}")
|
223
264
|
else
|
224
|
-
logger.warn("
|
265
|
+
logger.warn("Did not receive a proxy ticket for service #{r.target_service}!")
|
225
266
|
end
|
226
267
|
|
227
268
|
return r
|
228
269
|
end
|
229
270
|
end
|
230
271
|
|
272
|
+
|
273
|
+
|
231
274
|
private
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
275
|
+
|
276
|
+
def self.retrieve_pgt(receipt)
|
277
|
+
retrieve_url = "#{@@proxy_retrieval_url}?pgtIou=#{receipt.pgt_iou}"
|
278
|
+
|
279
|
+
logger.debug("Will attempt to retrieve the PGT from: #{retrieve_url}")
|
280
|
+
|
281
|
+
pgt = CAS::ServiceTicketValidator.retrieve(retrieve_url)
|
282
|
+
|
283
|
+
logger.info("Retrieved the PGT: #{pgt}")
|
284
|
+
|
285
|
+
return pgt
|
286
|
+
end
|
287
|
+
|
288
|
+
def self.validate_receipt(receipt)
|
289
|
+
logger.info "Checking that the receipt is valid and coherent..."
|
290
|
+
|
291
|
+
if not receipt
|
292
|
+
logger.info "No receipt given, so the receipt is invalid"
|
293
|
+
return false
|
294
|
+
elsif @@renew && !receipt.primary_authentication?
|
295
|
+
logger.info "The filter is configured to force primary authentication (i.e. the renew options is set to true), but the receipt was not generated by primary authentication so we consider it invalid"
|
296
|
+
return false
|
236
297
|
end
|
237
298
|
|
238
|
-
|
299
|
+
if receipt.proxied?
|
300
|
+
logger.info "The receipt is proxied by proxying service: #{receipt.proxying_service}"
|
301
|
+
|
302
|
+
if @@authorized_proxies and !@@authorized_proxies.empty?
|
303
|
+
logger.debug "Authorized proxies are: #{@@authorized_proxies.inspect}"
|
304
|
+
|
305
|
+
if !@@authorized_proxies.include? receipt.proxying_service
|
306
|
+
logger.warn "Receipt was proxied by #{receipt_proxying_service} but this proxying service is not in the list of authorized proxies. The receipt is therefore invalid."
|
307
|
+
return false
|
308
|
+
else
|
309
|
+
logger.info "Receipt is proxied by a valid proxying service."
|
310
|
+
end
|
311
|
+
else
|
312
|
+
logger.info "No authorized proxies set, so any proxy will be considered valid"
|
313
|
+
end
|
314
|
+
else
|
315
|
+
logger.info "Receipt is not proxied"
|
316
|
+
end
|
239
317
|
|
240
|
-
|
241
|
-
|
318
|
+
return true
|
319
|
+
end
|
320
|
+
|
321
|
+
def self.get_receipt_for_ticket(ticket, controller)
|
322
|
+
logger.info "Getting receipt for ticket '#{ticket}'"
|
323
|
+
pv = ProxyTicketValidator.new
|
324
|
+
pv.validate_url = @@validate_url
|
325
|
+
pv.service_ticket = ticket
|
326
|
+
pv.service = service_url(controller)
|
327
|
+
pv.renew = @@renew
|
328
|
+
pv.proxy_callback_url = @@proxy_callback_url
|
329
|
+
receipt = nil
|
330
|
+
logger.debug "ProxyTicketValidator is: #{pv.inspect}"
|
331
|
+
begin
|
332
|
+
receipt = Receipt.new(pv)
|
333
|
+
rescue AuthenticationException => e
|
334
|
+
logger.warn("Getting a receipt for the ProxyTicketValidator threw an exception: #{e}")
|
242
335
|
end
|
336
|
+
logger.debug "Receipt is: #{receipt.inspect}"
|
337
|
+
receipt
|
338
|
+
end
|
339
|
+
|
340
|
+
def self.service_url(controller)
|
341
|
+
unclean = @@service_url || guess_service(controller)
|
342
|
+
clean = remove_ticket_from_service_uri(unclean)
|
343
|
+
logger.debug("Service URI with ticket removed is: #{clean}")
|
344
|
+
clean
|
345
|
+
end
|
346
|
+
|
347
|
+
# FIXME: this method is really poorly named :(
|
348
|
+
def self.redirect_url(controller,url=@@login_url)
|
349
|
+
"#{url}?service=#{CGI.escape(service_url(controller))}" + ((@@renew)? "&renew=true":"") + ((@@gateway)? "&gateway=true":"") + ((@@query_string.blank?)? "" : "&"+(@@query_string.collect { |k,v| "#{k}=#{v}"}.join("&")))
|
350
|
+
end
|
351
|
+
|
352
|
+
def self.guess_service(controller)
|
353
|
+
logger.info "Guessing service based on params: #{controller.params.inspect}"
|
243
354
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
355
|
+
# we're assuming that controller.params[:service] is url-encoded!
|
356
|
+
if controller.params and controller.params.include? :service
|
357
|
+
service = controller.params[:service]
|
358
|
+
logger.info "We have a :service param, so we will URI-decode it and use this as the service: #{controller.params[:service]}"
|
359
|
+
return service
|
360
|
+
end
|
361
|
+
|
362
|
+
req = controller.request
|
363
|
+
|
364
|
+
if controller.params
|
365
|
+
parms = controller.params.dup
|
366
|
+
else
|
367
|
+
parms = {}
|
368
|
+
end
|
369
|
+
|
370
|
+
parms.delete("ticket")
|
371
|
+
|
372
|
+
query = (parms.collect {|key, val| "#{key}=#{val}"}).join("&")
|
373
|
+
query = "?" + query unless query.empty?
|
374
|
+
|
375
|
+
service = "#{req.protocol}#{@@server_name}#{req.request_uri.split(/\?/)[0]}#{query}"
|
376
|
+
|
377
|
+
logger.info "Guessed service is: #{service}"
|
378
|
+
|
379
|
+
return service
|
260
380
|
end
|
261
|
-
receipt
|
262
|
-
end
|
263
|
-
def self.service_url(controller)
|
264
|
-
before = @@service_url || guess_service(controller)
|
265
|
-
logger.debug("before: #{before}")
|
266
|
-
after = escape_service_uri(before)
|
267
|
-
logger.debug("after: #{after}")
|
268
|
-
after
|
269
|
-
end
|
270
|
-
def self.redirect_url(controller,url=@@login_url)
|
271
|
-
"#{url}?service=#{service_url(controller)}" + ((@@renew)? "&renew=true":"") + ((@@gateway)? "&gateway=true":"") + ((@@query_string.nil?)? "" : "&"+(@@query_string.collect { |k,v| "#{k}=#{v}"}.join("&")))
|
272
|
-
end
|
273
|
-
def self.guess_service(controller)
|
274
|
-
# we're assuming that controller.params[:service] is url-encoded!
|
275
|
-
return controller.params[:service] if controller.params.include? :service
|
276
381
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
382
|
+
def self.escape_service_uri(uri)
|
383
|
+
URI.encode(uri, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]", false, 'U').freeze)
|
384
|
+
end
|
385
|
+
|
386
|
+
# The service URI should never have a ticket parameter, but we use this to remove
|
387
|
+
# any parameters named "ticket" just in case, as having a "ticket" parameter in the
|
388
|
+
# service URI will generally cause an infinite redirection loop.
|
389
|
+
def self.remove_ticket_from_service_uri(uri)
|
390
|
+
uri.gsub(/ticket=[^&$]*&?/, '')
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
class ProxyGrantingNotAvailable < Exception
|
287
395
|
end
|
288
396
|
end
|
289
397
|
|
data/lib/cas_logger.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
# this overrides clean_logger.rb in Rails that pretty much completely breaks logging #!@%*(#@*$&%!!...
|
4
|
+
module CAS
|
5
|
+
class Logger < ::Logger
|
6
|
+
def initialize(logdev, shift_age = 0, shift_size = 1048576)
|
7
|
+
@default_formatter = CAS::Logger::Formatter.new
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def format_message(severity, datetime, progrname, msg)
|
12
|
+
(@formatter || @default_formatter).call(severity, datetime, progname, msg)
|
13
|
+
end
|
14
|
+
|
15
|
+
def break
|
16
|
+
self << "\n"
|
17
|
+
end
|
18
|
+
|
19
|
+
class Formatter < ::Logger::Formatter
|
20
|
+
Format = "[%s#%d] %5s -- %s: %s\n"
|
21
|
+
|
22
|
+
def call(severity, time, progname, msg)
|
23
|
+
Format % [format_datetime(time), $$, severity, progname, msg2str(msg)]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -8,8 +8,10 @@ class CasProxyCallbackController < ActionController::Base
|
|
8
8
|
# Note that this action should ALWAYS be called via https, otherwise you have a gaping security hole.
|
9
9
|
# In fact, the JA-SIG implementation of the CAS server will refuse to send PGTs to non-https URLs.
|
10
10
|
def receive_pgt
|
11
|
-
|
12
|
-
|
11
|
+
#FIXME: these checks don't work because REMOTE_HOST doesn't work consistently under all web servers (for example it doesn't work at all under mongrel)
|
12
|
+
# ... need to find a reliable way to check if the request came through from a reverse HTTPS proxy -- until then I'm disabling this check
|
13
|
+
#render_error "PGTs can be received only via HTTPS or local connections." and return unless
|
14
|
+
# request.ssl? or request.env['REMOTE_HOST'] == "127.0.0.1"
|
13
15
|
|
14
16
|
pgtIou = params['pgtIou']
|
15
17
|
pgtId = params['pgtId']
|
@@ -34,8 +36,8 @@ class CasProxyCallbackController < ActionController::Base
|
|
34
36
|
# in fact, the action will not work if the request is not made via SSL or is not local (we allow for local
|
35
37
|
# non-SSL requests since this allows for the use of reverse HTTPS proxies like Pound).
|
36
38
|
def retrieve_pgt
|
37
|
-
render_error "You can only retrieve PGTs via HTTPS or local connections." and return unless
|
38
|
-
|
39
|
+
#render_error "You can only retrieve PGTs via HTTPS or local connections." and return unless
|
40
|
+
# request.ssl? or request.env['REMOTE_HOST'] == "127.0.0.1"
|
39
41
|
|
40
42
|
pgtIou = params['pgtIou']
|
41
43
|
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.
|
2
|
+
rubygems_version: 0.9.0
|
3
3
|
specification_version: 1
|
4
4
|
name: rubycas-client
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date:
|
6
|
+
version: 0.11.0
|
7
|
+
date: 2007-01-18 00:00:00 -05:00
|
8
8
|
summary: Client library for the CAS single-sign-on protocol.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -25,6 +25,7 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
|
|
25
25
|
platform: ruby
|
26
26
|
signing_key:
|
27
27
|
cert_chain:
|
28
|
+
post_install_message:
|
28
29
|
authors:
|
29
30
|
- Matt Zukowski
|
30
31
|
- Ola Bini
|
@@ -32,12 +33,14 @@ authors:
|
|
32
33
|
files:
|
33
34
|
- install.rb
|
34
35
|
- init.rb
|
35
|
-
- lib/cas_proxy_callback_controller.rb
|
36
36
|
- lib/cas.rb
|
37
|
+
- lib/cas_proxy_callback_controller.rb
|
37
38
|
- lib/cas_auth.rb
|
38
|
-
-
|
39
|
+
- lib/cas_logger.rb
|
40
|
+
- CHANGES
|
39
41
|
- Rakefile
|
40
42
|
- README
|
43
|
+
- LICENSE
|
41
44
|
test_files: []
|
42
45
|
|
43
46
|
rdoc_options:
|