rubycas-client 2.3.8 → 2.3.9.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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|