rubycas-client 2.3.8 → 2.3.9.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ SimpleCov.start do
2
+ add_filter "/spec/.*_spec\.rb"
3
+ add_filter "/spec/.*/shared_examples.*"
4
+ add_filter "/spec/.*/.*helper(s?).rb"
5
+ end
6
+
7
+ # vim: filetype=ruby
@@ -1,5 +1,7 @@
1
+ language: ruby
1
2
  rvm:
2
3
  - 1.8.7
3
4
  - 1.9.2
4
5
  - 1.9.3
5
6
  - ree
7
+ - jruby-18mode
data/Gemfile CHANGED
@@ -1,13 +1,26 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  group :development do
4
- gem "json", "~> 1.6.1"
5
- gem "rspec", "~> 2.7.0"
4
+ gem "json"
5
+ gem "rspec"
6
6
  gem "bundler", ">= 1.0"
7
- gem "jeweler", "~> 1.6.2"
8
- gem "actionpack"
7
+ gem "jeweler"
8
+ gem "actionpack", :require => 'action_pack'
9
+ gem "activerecord", :require => 'active_record'
9
10
  gem "rake"
10
- gem "rcov"
11
+ gem "simplecov", :require => false
12
+ gem "guard"
13
+ gem "guard-rspec"
14
+ gem "database_cleaner"
15
+
16
+ platforms :ruby do
17
+ gem "sqlite3"
18
+ end
19
+
20
+ platforms :jruby do
21
+ gem "jruby-openssl"
22
+ gem "activerecord-jdbcsqlite3-adapter"
23
+ end
11
24
  end
12
25
 
13
26
  gem "activesupport", :require => "active_support"
@@ -4,35 +4,68 @@ GEM
4
4
  actionpack (2.3.11)
5
5
  activesupport (= 2.3.11)
6
6
  rack (~> 1.1.0)
7
+ activerecord (2.3.11)
8
+ activesupport (= 2.3.11)
9
+ activerecord-jdbc-adapter (1.2.2)
10
+ activerecord-jdbcsqlite3-adapter (1.2.2)
11
+ activerecord-jdbc-adapter (~> 1.2.2)
12
+ jdbc-sqlite3 (~> 3.7.2)
7
13
  activesupport (2.3.11)
14
+ bouncy-castle-java (1.5.0146.1)
15
+ database_cleaner (0.7.1)
8
16
  diff-lcs (1.1.3)
17
+ ffi (1.0.11)
18
+ ffi (1.0.11-java)
9
19
  git (1.2.5)
20
+ guard (1.0.0)
21
+ ffi (>= 0.5.0)
22
+ thor (~> 0.14.6)
23
+ guard-rspec (0.6.0)
24
+ guard (>= 0.10.0)
25
+ jdbc-sqlite3 (3.7.2)
10
26
  jeweler (1.6.4)
11
27
  bundler (~> 1.0)
12
28
  git (>= 1.2.5)
13
29
  rake
14
- json (1.6.1)
30
+ jruby-openssl (0.7.5)
31
+ bouncy-castle-java (>= 1.5.0146.1)
32
+ json (1.6.5)
33
+ json (1.6.5-java)
34
+ multi_json (1.0.4)
15
35
  rack (1.1.2)
16
36
  rake (0.9.2.2)
17
- rcov (0.9.11)
18
- rspec (2.7.0)
19
- rspec-core (~> 2.7.0)
20
- rspec-expectations (~> 2.7.0)
21
- rspec-mocks (~> 2.7.0)
22
- rspec-core (2.7.1)
23
- rspec-expectations (2.7.0)
37
+ rspec (2.8.0)
38
+ rspec-core (~> 2.8.0)
39
+ rspec-expectations (~> 2.8.0)
40
+ rspec-mocks (~> 2.8.0)
41
+ rspec-core (2.8.0)
42
+ rspec-expectations (2.8.0)
24
43
  diff-lcs (~> 1.1.2)
25
- rspec-mocks (2.7.0)
44
+ rspec-mocks (2.8.0)
45
+ simplecov (0.5.4)
46
+ multi_json (~> 1.0.3)
47
+ simplecov-html (~> 0.5.3)
48
+ simplecov-html (0.5.3)
49
+ sqlite3 (1.3.5)
50
+ thor (0.14.6)
26
51
 
27
52
  PLATFORMS
53
+ java
28
54
  ruby
29
55
 
30
56
  DEPENDENCIES
31
57
  actionpack
58
+ activerecord
59
+ activerecord-jdbcsqlite3-adapter
32
60
  activesupport
33
61
  bundler (>= 1.0)
34
- jeweler (~> 1.6.2)
35
- json (~> 1.6.1)
62
+ database_cleaner
63
+ guard
64
+ guard-rspec
65
+ jeweler
66
+ jruby-openssl
67
+ json
36
68
  rake
37
- rcov
38
- rspec (~> 2.7.0)
69
+ rspec
70
+ simplecov
71
+ sqlite3
@@ -0,0 +1,11 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2, :cli => '-c -f doc' do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ watch(%r{^spec/support/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
9
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
10
+ end
11
+
@@ -1,5 +1,27 @@
1
1
  = RubyCAS-Client Changelog
2
2
 
3
+ == Version 2.3.9rc1 :: 2012-03-24
4
+
5
+ * Bug Fixes
6
+ * Fixed issue that caused Single Sign Out to fail (@bryanlarsen and @soorajb)
7
+ * Fixed issue in Filter#unauthorized! (@mscottford)
8
+ * Fixed #38, boolean values are now preserved in extra attribute yaml
9
+ parsing
10
+
11
+ * New functionality
12
+ * Tweak the CasProxyCallbackController so it can be used with
13
+ rubycas-client-rails in Rails 3 (@bryanlarsen)
14
+ * Add support for calling the CAS Server through an HTTP proxy (@shevaun)
15
+ * Add support for specifying the service url to be added to the
16
+ logout url (@dyson)
17
+ * add support for extra attributes as xml attributes (@bhenderson)
18
+ * Add :raw mode to extra attribute parsing
19
+
20
+ * Other
21
+ * Made writing and running rspec tests much easier
22
+ * Added tests for Ticket Stores
23
+ * Official support for jruby 1.6
24
+
3
25
  == Version 2.3.8 :: 2011-12-19
4
26
 
5
27
  * Bug Fixes
@@ -1,4 +1,4 @@
1
- = RubyCAS-Client
1
+ = RubyCAS-Client
2
2
 
3
3
  Authors:: Matt Zukowski <matt AT roughest DOT net> and Matt Campbell <matt AT soupmatt DOT com>; inspired by code by Ola Bini <ola.bini AT ki DOT se> and Matt Walker <mwalker AT tamu DOT edu>
4
4
  Copyright:: Portions contributed by Matt Zukowski are copyright (c) 2009 Urbacon Ltd.
@@ -8,7 +8,7 @@ License:: MIT License
8
8
  Websites:: http://github.com/rubycas/rubycas-client
9
9
  http://github.com/rubycas/rubycas-client/wiki
10
10
  http://rubydoc.info/github/rubycas/rubycas-client/master/frames
11
-
11
+ Build Status:: {<img src="http://travis-ci.org/rubycas/rubycas-client.png" />}[http://travis-ci.org/rubycas/rubycas-client]
12
12
 
13
13
  === RubyCAS-Client is a Ruby client library for Yale's Central Authentication Service (CAS) protocol.
14
14
 
data/Rakefile CHANGED
@@ -24,18 +24,6 @@ Jeweler::Tasks.new do |gem|
24
24
  end
25
25
  Jeweler::RubygemsDotOrgTasks.new
26
26
 
27
- begin
28
- require 'rcov/rcovtask'
29
- Rcov::RcovTask.new do |test|
30
- test.libs << 'test'
31
- test.pattern = 'test/**/test_*.rb'
32
- test.verbose = true
33
- test.rcov_opts << '--exclude "gems/*"'
34
- end
35
- rescue LoadError
36
- puts "Hiding rcov tasks because rcov is not available"
37
- end
38
-
39
27
  begin
40
28
  require 'rspec/core/rake_task'
41
29
  desc 'Run RSpecs to confirm that all functionality is working as expected'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.3.8
1
+ 2.3.9.rc1
@@ -56,7 +56,7 @@ module CASClient
56
56
  # Log using the appropriate method if we have a logger
57
57
  # if we dont' have a logger, gracefully ignore.
58
58
  def method_missing(name, *args)
59
- if @real_logger && @real_logger.respond_to?(name)
59
+ if !@real_logger.nil? && @real_logger.respond_to?(name)
60
60
  @real_logger.send(name, *args)
61
61
  end
62
62
  end
@@ -4,27 +4,28 @@ module CASClient
4
4
  attr_reader :cas_base_url, :cas_destination_logout_param_name
5
5
  attr_reader :log, :username_session_key, :extra_attributes_session_key
6
6
  attr_reader :ticket_store
7
+ attr_reader :proxy_host, :proxy_port
7
8
  attr_writer :login_url, :validate_url, :proxy_url, :logout_url, :service_url
8
9
  attr_accessor :proxy_callback_url, :proxy_retrieval_url
9
-
10
+
10
11
  def initialize(conf = nil)
11
12
  configure(conf) if conf
12
13
  end
13
-
14
+
14
15
  def configure(conf)
15
16
  #TODO: raise error if conf contains unrecognized cas options (this would help detect user typos in the config)
16
17
 
17
18
  raise ArgumentError, "Missing :cas_base_url parameter!" unless conf[:cas_base_url]
18
-
19
+
19
20
  if conf.has_key?("encode_extra_attributes_as")
20
21
  unless (conf[:encode_extra_attributes_as] == :json || conf[:encode_extra_attributes_as] == :yaml)
21
22
  raise ArgumentError, "Unkown Value for :encode_extra_attributes_as parameter! Allowed options are json or yaml - #{conf[:encode_extra_attributes_as]}"
22
23
  end
23
24
  end
24
-
25
- @cas_base_url = conf[:cas_base_url].gsub(/\/$/, '')
25
+
26
+ @cas_base_url = conf[:cas_base_url].gsub(/\/$/, '')
26
27
  @cas_destination_logout_param_name = conf[:cas_destination_logout_param_name]
27
-
28
+
28
29
  @login_url = conf[:login_url]
29
30
  @logout_url = conf[:logout_url]
30
31
  @validate_url = conf[:validate_url]
@@ -32,7 +33,11 @@ module CASClient
32
33
  @service_url = conf[:service_url]
33
34
  @force_ssl_verification = conf[:force_ssl_verification]
34
35
  @proxy_callback_url = conf[:proxy_callback_url]
35
-
36
+
37
+ #proxy server settings
38
+ @proxy_host = conf[:proxy_host]
39
+ @proxy_port = conf[:proxy_port]
40
+
36
41
  @username_session_key = conf[:username_session_key] || :cas_user
37
42
  @extra_attributes_session_key = conf[:extra_attributes_session_key] || :cas_extra_attributes
38
43
  @ticket_store_class = case conf[:ticket_store]
@@ -45,13 +50,13 @@ module CASClient
45
50
  end
46
51
  @ticket_store = @ticket_store_class.new conf[:ticket_store_config]
47
52
  raise CASException, "The Ticket Store is not a subclass of AbstractTicketStore, it is a #{@ticket_store_class}" unless @ticket_store.kind_of? CASClient::Tickets::Storage::AbstractTicketStore
48
-
53
+
49
54
  @log = CASClient::LoggerWrapper.new
50
55
  @log.set_real_logger(conf[:logger]) if conf[:logger]
51
56
  @ticket_store.log = @log
52
57
  @conf_options = conf
53
58
  end
54
-
59
+
55
60
  def cas_destination_logout_param_name
56
61
  @cas_destination_logout_param_name || "destination"
57
62
  end
@@ -59,11 +64,11 @@ module CASClient
59
64
  def login_url
60
65
  @login_url || (cas_base_url + "/login")
61
66
  end
62
-
67
+
63
68
  def validate_url
64
69
  @validate_url || (cas_base_url + "/proxyValidate")
65
70
  end
66
-
71
+
67
72
  # Returns the CAS server's logout url.
68
73
  #
69
74
  # If a logout_url has not been explicitly configured,
@@ -72,39 +77,40 @@ module CASClient
72
77
  # destination_url:: Set this if you want the user to be
73
78
  # able to immediately log back in. Generally
74
79
  # you'll want to use something like <tt>request.referer</tt>.
75
- # Note that the above behaviour describes RubyCAS-Server
80
+ # Note that the above behaviour describes RubyCAS-Server
76
81
  # -- other CAS server implementations might use this
77
82
  # parameter differently (or not at all).
78
83
  # follow_url:: This satisfies section 2.3.1 of the CAS protocol spec.
79
84
  # See http://www.ja-sig.org/products/cas/overview/protocol
80
- def logout_url(destination_url = nil, follow_url = nil)
85
+ def logout_url(destination_url = nil, follow_url = nil, service_url = nil)
81
86
  url = @logout_url || (cas_base_url + "/logout")
82
-
87
+ uri = URI.parse(url)
88
+ service_url = (service_url if service_url) || @service_url
89
+ h = uri.query ? query_to_hash(uri.query) : {}
90
+
83
91
  if destination_url
84
92
  # if present, remove the 'ticket' parameter from the destination_url
85
93
  duri = URI.parse(destination_url)
86
- h = duri.query ? query_to_hash(duri.query) : {}
87
- h.delete('ticket')
88
- duri.query = hash_to_query(h)
94
+ dh = duri.query ? query_to_hash(duri.query) : {}
95
+ dh.delete('ticket')
96
+ duri.query = hash_to_query(dh)
89
97
  destination_url = duri.to_s.gsub(/\?$/, '')
90
- end
91
-
92
- if destination_url || follow_url
93
- uri = URI.parse(url)
94
- h = uri.query ? query_to_hash(uri.query) : {}
95
98
  h[cas_destination_logout_param_name] = destination_url if destination_url
99
+ h['gateway'] = 'true'
100
+ elsif follow_url
96
101
  h['url'] = follow_url if follow_url
97
- uri.query = hash_to_query(h)
98
- uri.to_s
102
+ h['service'] = service_url
99
103
  else
100
- url
104
+ h['service'] = service_url
101
105
  end
106
+ uri.query = hash_to_query(h)
107
+ uri.to_s
102
108
  end
103
-
109
+
104
110
  def proxy_url
105
111
  @proxy_url || (cas_base_url + "/proxy")
106
112
  end
107
-
113
+
108
114
  def validate_service_ticket(st)
109
115
  uri = URI.parse(validate_url)
110
116
  h = uri.query ? query_to_hash(uri.query) : {}
@@ -113,7 +119,7 @@ module CASClient
113
119
  h['renew'] = "1" if st.renew
114
120
  h['pgtUrl'] = proxy_callback_url if proxy_callback_url
115
121
  uri.query = hash_to_query(h)
116
-
122
+
117
123
  response = request_cas_response(uri, ValidationResponse)
118
124
  st.user = response.user
119
125
  st.extra_attributes = response.extra_attributes
@@ -121,22 +127,20 @@ module CASClient
121
127
  st.success = response.is_success?
122
128
  st.failure_code = response.failure_code
123
129
  st.failure_message = response.failure_message
124
-
130
+
125
131
  return st
126
132
  end
127
133
  alias validate_proxy_ticket validate_service_ticket
128
-
134
+
129
135
  # Returns true if the configured CAS server is up and responding;
130
136
  # false otherwise.
131
137
  def cas_server_is_up?
132
138
  uri = URI.parse(login_url)
133
-
139
+
134
140
  log.debug "Checking if CAS server at URI '#{uri}' is up..."
135
-
136
- https = Net::HTTP.new(uri.host, uri.port)
137
- https.use_ssl = (uri.scheme == 'https')
138
- https.verify_mode = (@force_ssl_verification ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE)
139
-
141
+
142
+ https = https_connection(uri)
143
+
140
144
  begin
141
145
  raw_res = https.start do |conn|
142
146
  conn.get("#{uri.path}?#{uri.query}")
@@ -145,22 +149,22 @@ module CASClient
145
149
  log.warn "CAS server did not respond! (#{e.inspect})"
146
150
  return false
147
151
  end
148
-
152
+
149
153
  log.debug "CAS server responded with #{raw_res.inspect}:\n#{raw_res.body}"
150
-
154
+
151
155
  return raw_res.kind_of?(Net::HTTPSuccess)
152
156
  end
153
-
154
- # Requests a login using the given credentials for the given service;
157
+
158
+ # Requests a login using the given credentials for the given service;
155
159
  # returns a LoginResponse object.
156
160
  def login_to_service(credentials, service)
157
161
  lt = request_login_ticket
158
-
162
+
159
163
  data = credentials.merge(
160
164
  :lt => lt,
161
- :service => service
165
+ :service => service
162
166
  )
163
-
167
+
164
168
  res = submit_data_to_cas(login_url, data)
165
169
  response = CASClient::LoginResponse.new(res)
166
170
 
@@ -170,7 +174,7 @@ module CASClient
170
174
 
171
175
  return response
172
176
  end
173
-
177
+
174
178
  # Requests a login ticket from the CAS server for use in a login request;
175
179
  # returns a LoginTicket object.
176
180
  #
@@ -178,18 +182,16 @@ module CASClient
178
182
  # tickets in this manner is not part of the official CAS spec.
179
183
  def request_login_ticket
180
184
  uri = URI.parse(login_url+'Ticket')
181
- https = Net::HTTP.new(uri.host, uri.port)
182
- https.use_ssl = (uri.scheme == 'https')
183
- https.verify_mode = (@force_ssl_verification ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE)
185
+ https = https_connection(uri)
184
186
  res = https.post(uri.path, ';')
185
-
187
+
186
188
  raise CASException, res.body unless res.kind_of? Net::HTTPSuccess
187
-
189
+
188
190
  res.body.strip
189
191
  end
190
-
192
+
191
193
  # Requests a proxy ticket from the CAS server for the given service
192
- # using the given pgt (proxy granting ticket); returns a ProxyTicket
194
+ # using the given pgt (proxy granting ticket); returns a ProxyTicket
193
195
  # object.
194
196
  #
195
197
  # The pgt required to request a proxy ticket is obtained as part of
@@ -200,17 +202,17 @@ module CASClient
200
202
  h['pgt'] = pgt.ticket
201
203
  h['targetService'] = target_service
202
204
  uri.query = hash_to_query(h)
203
-
205
+
204
206
  response = request_cas_response(uri, ProxyResponse)
205
-
207
+
206
208
  pt = ProxyTicket.new(response.proxy_ticket, target_service)
207
209
  pt.success = response.is_success?
208
210
  pt.failure_code = response.failure_code
209
211
  pt.failure_message = response.failure_message
210
-
212
+
211
213
  return pt
212
214
  end
213
-
215
+
214
216
  def retrieve_proxy_granting_ticket(pgt_iou)
215
217
  pgt = @ticket_store.retrieve_pgt(pgt_iou)
216
218
 
@@ -218,24 +220,29 @@ module CASClient
218
220
 
219
221
  ProxyGrantingTicket.new(pgt, pgt_iou)
220
222
  end
221
-
223
+
222
224
  def add_service_to_login_url(service_url)
223
225
  uri = URI.parse(login_url)
224
226
  uri.query = (uri.query ? uri.query + "&" : "") + "service=#{CGI.escape(service_url)}"
225
227
  uri.to_s
226
228
  end
227
-
229
+
228
230
  private
231
+
232
+ def https_connection(uri)
233
+ https = Net::HTTP::Proxy(proxy_host, proxy_port).new(uri.host, uri.port)
234
+ https.use_ssl = (uri.scheme == 'https')
235
+ https.verify_mode = (@force_ssl_verification ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE)
236
+ https
237
+ end
238
+
229
239
  # Fetches a CAS response of the given type from the given URI.
230
240
  # Type should be either ValidationResponse or ProxyResponse.
231
241
  def request_cas_response(uri, type, options={})
232
242
  log.debug "Requesting CAS response for URI #{uri}"
233
-
243
+
234
244
  uri = URI.parse(uri) unless uri.kind_of? URI
235
- https = Net::HTTP.new(uri.host, uri.port)
236
- https.use_ssl = (uri.scheme == 'https')
237
- https.verify_mode = (@force_ssl_verification ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE)
238
-
245
+ https = https_connection(uri)
239
246
  begin
240
247
  raw_res = https.start do |conn|
241
248
  conn.get("#{uri.path}?#{uri.query}")
@@ -244,7 +251,7 @@ module CASClient
244
251
  log.error "CAS server did not respond! (#{e.inspect})"
245
252
  raise "The CAS authentication server at #{uri} is not responding!"
246
253
  end
247
-
254
+
248
255
  # We accept responses of type 422 since RubyCAS-Server generates these
249
256
  # in response to requests from the client that are processable but contain
250
257
  # invalid CAS data (for example an invalid service ticket).
@@ -254,25 +261,23 @@ module CASClient
254
261
  log.error "CAS server responded with an error! (#{raw_res.inspect})"
255
262
  raise "The CAS authentication server at #{uri} responded with an error (#{raw_res.inspect})!"
256
263
  end
257
-
264
+
258
265
  type.new(raw_res.body, @conf_options)
259
266
  end
260
-
267
+
261
268
  # Submits some data to the given URI and returns a Net::HTTPResponse.
262
269
  def submit_data_to_cas(uri, data)
263
270
  uri = URI.parse(uri) unless uri.kind_of? URI
264
271
  req = Net::HTTP::Post.new(uri.path)
265
272
  req.set_form_data(data, ';')
266
- https = Net::HTTP.new(uri.host, uri.port)
267
- https.use_ssl = (uri.scheme == 'https')
268
- https.verify_mode = (@force_ssl_verification ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE)
273
+ https = https_connection(uri)
269
274
  https.start {|conn| conn.request(req) }
270
275
  end
271
-
276
+
272
277
  def query_to_hash(query)
273
278
  CGI.parse(query)
274
279
  end
275
-
280
+
276
281
  def hash_to_query(hash)
277
282
  pairs = []
278
283
  hash.each do |k, vals|