cassette 1.0.18 → 1.1.0

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -12
  3. data/lib/cassette/authentication/authorities.rb +34 -30
  4. data/lib/cassette/authentication/cache.rb +22 -18
  5. data/lib/cassette/authentication/filter.rb +52 -33
  6. data/lib/cassette/authentication/user.rb +20 -16
  7. data/lib/cassette/authentication.rb +39 -27
  8. data/lib/cassette/cache.rb +2 -2
  9. data/lib/cassette/client/cache.rb +39 -35
  10. data/lib/cassette/client.rb +5 -4
  11. data/lib/cassette/http/parsed_response.rb +20 -0
  12. data/lib/cassette/http/request.rb +44 -0
  13. data/lib/cassette/http/ticket_response.rb +48 -0
  14. data/lib/cassette/http.rb +8 -0
  15. data/lib/cassette/rubycas/helper.rb +2 -8
  16. data/lib/cassette/rubycas/routing_constraint.rb +23 -0
  17. data/lib/cassette/rubycas/single_sign_out_constraint.rb +8 -8
  18. data/lib/cassette/rubycas/user_factory.rb +14 -0
  19. data/lib/cassette/rubycas.rb +2 -0
  20. data/lib/cassette/version.rb +2 -2
  21. data/lib/cassette.rb +12 -50
  22. data/spec/cassette/authentication/authorities_spec.rb +1 -1
  23. data/spec/cassette/authentication/cache_spec.rb +40 -4
  24. data/spec/cassette/authentication/filter_spec.rb +106 -36
  25. data/spec/cassette/authentication/user_factory_spec.rb +42 -0
  26. data/spec/cassette/authentication/user_spec.rb +4 -3
  27. data/spec/cassette/authentication_spec.rb +24 -12
  28. data/spec/cassette/cache_spec.rb +0 -2
  29. data/spec/cassette/client/cache_spec.rb +1 -1
  30. data/spec/cassette/client_spec.rb +319 -0
  31. data/spec/cassette/errors_spec.rb +1 -1
  32. data/spec/cassette/http/parsed_response_spec.rb +27 -0
  33. data/spec/cassette/http/request_spec.rb +41 -0
  34. data/spec/cassette/http/ticket_response_spec.rb +41 -0
  35. data/spec/cassette/rubycas/routing_constraint_spec.rb +84 -0
  36. data/spec/cassette_spec.rb +36 -0
  37. data/spec/integration/cas/client_spec.rb +0 -3
  38. data/spec/spec_helper.rb +5 -0
  39. data/spec/support/controllers/controller_mock.rb +19 -0
  40. metadata +98 -36
  41. data/spec/cas_spec.rb +0 -78
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef167b410ab8edd7eddb9cbc2905273b103fb65d
4
- data.tar.gz: 3fe842b14b10a50e5493cbfc6b021b554c1732fe
3
+ metadata.gz: e4fc103e95f0afcbec1f342bda0bf6c26c16aaab
4
+ data.tar.gz: 4fec669e36c9fc02dac165f203f23635fb60c9f0
5
5
  SHA512:
6
- metadata.gz: e624b4e33054308e07ff1b8f4966ad1d22dad86a207d0274bb8d93fefa4ad2bc95837478a4b653be64f4a79d07d57bb704f847003bde4953727a0f024628144a
7
- data.tar.gz: ac20ab03689bc234dda217951dc362c35c3425add96691ad016a93587cd005a64533b26c548969d746e26a5ab5d074a76fcb249bcbc334899ac7ad3c4e194222
6
+ metadata.gz: 1a11d5a6a757a705913b3b1510bba8b34bed024401bdd3fbdca64a57e36dad8ca5a5bec19af39f332a899d3046a0fffd9b9c2eb719905896cf856b8dbc0727c5
7
+ data.tar.gz: 1ad976704ed09a02b776710bddb836f0fbee7f1dc9edd6675a029ac9bc1a73ba6ba1efd234d5322a4b111f002652b8907cf92f3c41380b199893819673bf5fd2
data/README.md CHANGED
@@ -24,10 +24,7 @@ Require this library and create an intializer to set its configuration:
24
24
  Cassette.config = config
25
25
  ```
26
26
 
27
- where config is an object that responds to the methods #base for the base CAS uri, #username and #password
28
- if you are authenticating on other systems and #service and #base\_authority if you are using the authentication filter
29
- to authenticate your app
30
-
27
+ where config is an object that responds to the methods `base` for the base CAS uri, `username` and `password` if you are authenticating on other systems and `service` and `base_authority` if you are using the authentication filter to authenticate your app.
31
28
 
32
29
  You may also set the caching backend using the .backend= module method:
33
30
 
@@ -35,10 +32,9 @@ You may also set the caching backend using the .backend= module method:
35
32
  Cassette::Cache.backend = ActiveSupport::Cache::MemcacheStorage.new
36
33
  ```
37
34
 
38
- By default, Cassette::Cache will check if you have Rails.cache defined or instantiate a new ActiveSupport::Cache::MemoryStore
39
-
35
+ By default, `Cassette::Cache` will check if you have `Rails.cache` defined or instantiate a new `ActiveSupport::Cache::MemoryStore`
40
36
 
41
- To authenticate your Rails app, add to your ApplicationController (or any authenticated controller):
37
+ To authenticate your Rails app, add to your `ApplicationController` (or any authenticated controller):
42
38
 
43
39
  ```ruby
44
40
  class ApplicationController < ActionController::Base
@@ -48,7 +44,7 @@ class ApplicationController < ActionController::Base
48
44
  end
49
45
  ```
50
46
 
51
- You should also rescue from Cassette::Errors::Forbidden with more friendly errors
47
+ You should also rescue from `Cassette::Errors::Forbidden` with more friendly errors
52
48
 
53
49
  If you wish to have actions that skip the authentication filter, add to your controller:
54
50
 
@@ -60,7 +56,68 @@ class SomeController < ApplicationController
60
56
  end
61
57
  ```
62
58
 
63
- Where options are the same options you can pass to Rails' __skip_before_filter__ method
59
+ Where options are the same options you can pass to Rails' `skip_before_filter` method
60
+
61
+ ### Overriding the authenticated service
62
+
63
+ You can the service being authenticated in a controller (or group of controllers). To do this, override the instance method `authentication_service`:
64
+
65
+ ```ruby
66
+ class ApiController < ApplicationController
67
+ def authentication_service
68
+ "api.#{super}"
69
+
70
+ # or maybe a hardcoded:
71
+ # "api.example.org"
72
+
73
+ # looking like regular RubyCAS, using the url
74
+ # request.url
75
+ end
76
+ end
77
+ ```
78
+
79
+ ### Accepting multiple services (restricting from a list)
80
+
81
+ Your config object must respond to `services` and the filter will check your controller `authentication_service` against the list or the configured service.
82
+
83
+ In your initializer:
84
+
85
+ ```ruby
86
+ Cassete.config = OpenStruct.new(
87
+ # omitted
88
+ service: "example.org",
89
+ services: ["api.example.org", "www.example.org", "subdomain.example.org"]
90
+ )
91
+ ```
92
+
93
+ And in your controller:
94
+
95
+ ```ruby
96
+ class ApplicationController < ActionController::Base
97
+ def authentication_service
98
+ request.host
99
+ end
100
+ end
101
+ ```
102
+
103
+ In this example, only tickets generated for __api.example.org__, __www.example.org__, __subdomain.example.org__ or __example.org__ will be accepted others will raise a `Cassette::Errors::Forbidden`.
104
+
105
+ ### Accepting multiple services (customized)
106
+
107
+ If whitelisting services is not enough for your application, you can override the `accepts_authentication_service?` in your controller.
108
+ This method receives the service and returns a boolean if the service is ok or not.
109
+
110
+ ```ruby
111
+ class ApplicationController < ActionController::Base
112
+ def accepts_authentication_service?(service)
113
+ service.ends_with?('my-domain.com')
114
+ end
115
+
116
+ def authentication_service
117
+ request.host
118
+ end
119
+ end
120
+ ```
64
121
 
65
122
  ## RubyCAS client helpers
66
123
 
@@ -68,10 +125,10 @@ Where options are the same options you can pass to Rails' __skip_before_filter__
68
125
  If you are authenticating users with RubyCAS and want role checking, in your rubycas initializer:
69
126
 
70
127
  ```ruby
71
- require "cas/rubycas"
128
+ require "cassette/rubycas"
72
129
  ```
73
130
 
74
- And in your ApplicationController (or any authenticated controller):
131
+ And in your `ApplicationController` (or any authenticated controller):
75
132
 
76
133
  ```ruby
77
134
  class SomeController < ApplicationController
@@ -95,9 +152,29 @@ class SomeController < ApplicationController
95
152
  end
96
153
  ```
97
154
 
155
+ ### Constraining routes for roles
156
+
157
+ This is useful if you want to mount an unauthenticated Rack app (like Resque)
158
+
159
+ Add to your `config/routes.rb`:
160
+
161
+ ```ruby
162
+ mount Resque::Server.new, at: '/resque', constraints: Cassette::Rubycas::RoutingConstraint.new(:admin)
163
+ ```
164
+
165
+ This will make your /resque route require your `BASEAUTHORITY_ADMIN` role.
166
+
167
+ You can also use raw roles:
168
+
169
+ ```ruby
170
+ mount Resque::Server.new, at: '/resque', constraints: Cassette::Rubycas::RoutingConstraint.new('OTHERAPP_ROLE', raw: true)
171
+ ```
172
+
173
+ And your /resque route will require the `OTHERAPP_ROLE` role.
174
+
98
175
  ## Instantiating Cassette::Client and Cassette::Authentication
99
176
 
100
- You can create your own instances of __Cassette::Client__ (st/tgt generator) and __Cassette::Authentication__ (st validator).
177
+ You can create your own instances of `Cassette::Client` (st/tgt generator) and `Cassette::Authentication` (st validator).
101
178
 
102
179
  The constructor accepts a hash with keys (as symbols) for the values of cache, logger, http_client and configuration.
103
180
 
@@ -2,36 +2,40 @@
2
2
 
3
3
  require 'cassette/authentication'
4
4
 
5
- class Cassette::Authentication::Authorities
6
- def self.parse(authorities, base_authority = nil)
7
- new(authorities, base_authority)
8
- end
9
-
10
- def base
11
- @base_authority.to_s.upcase
12
- end
13
-
14
- def has_raw_role?(role)
15
- return true if ENV['NOAUTH']
16
- @authorities.include?(role)
17
- end
18
-
19
- def has_role?(role)
20
- return true if ENV['NOAUTH']
21
- has_raw_role?("#{base}_#{role.to_s.upcase.gsub('_', '-')}")
22
- end
23
-
24
- def initialize(authorities, base_authority = nil)
25
- @base_authority = base_authority || Cassette.config.base_authority
26
-
27
- if authorities.is_a?(String)
28
- @authorities = authorities.gsub(/^\[(.*)\]$/, '\\1').split(',').map(&:strip)
29
- else
30
- @authorities = Array(authorities).map(&:strip)
5
+ module Cassette
6
+ class Authentication
7
+ class Authorities
8
+ def self.parse(authorities, base_authority = nil)
9
+ new(authorities, base_authority)
10
+ end
11
+
12
+ def base
13
+ @base_authority.to_s.upcase
14
+ end
15
+
16
+ def has_raw_role?(role)
17
+ return true if ENV['NOAUTH']
18
+ @authorities.include?(role)
19
+ end
20
+
21
+ def has_role?(role)
22
+ return true if ENV['NOAUTH']
23
+ has_raw_role?("#{base}_#{role.to_s.upcase.gsub('_', '-')}")
24
+ end
25
+
26
+ def initialize(authorities, base_authority = nil)
27
+ @base_authority = base_authority || Cassette.config.base_authority
28
+
29
+ if authorities.is_a?(String)
30
+ @authorities = authorities.gsub(/^\[(.*)\]$/, '\\1').split(',').map(&:strip)
31
+ else
32
+ @authorities = Array(authorities).map(&:strip)
33
+ end
34
+ end
35
+
36
+ def authorities
37
+ @authorities.dup
38
+ end
31
39
  end
32
40
  end
33
-
34
- def authorities
35
- @authorities.dup
36
- end
37
41
  end
@@ -3,27 +3,31 @@
3
3
  require 'cassette/authentication'
4
4
  require 'cassette/cache'
5
5
 
6
- class Cassette::Authentication::Cache
7
- include Cassette::Cache
6
+ module Cassette
7
+ class Authentication
8
+ class Cache
9
+ include Cassette::Cache
8
10
 
9
- def initialize(logger)
10
- self.logger = logger
11
- end
11
+ def initialize(logger)
12
+ self.logger = logger
13
+ end
12
14
 
13
- def fetch_authentication(ticket, options = {}, &block)
14
- options = { expires_in: 5 * 60, max_uses: 5000, force: false }.merge(options)
15
- fetch("Cassette::Authentication.validate_ticket(#{ticket})", options) do
16
- logger.info("Authentication for #{ticket} is not cached")
17
- block.call
18
- end
19
- end
15
+ def fetch_authentication(ticket, service, options = {}, &block)
16
+ options = { expires_in: 5 * 60, max_uses: 5000, force: false }.merge(options)
17
+ fetch("Cassette::Authentication.validate_ticket(#{ticket}, #{service})", options) do
18
+ logger.info("Authentication for #{ticket}, #{service} is not cached")
19
+ block.call
20
+ end
21
+ end
20
22
 
21
- def clear_authentication_cache!
22
- backend.delete_matched('Cassette::Authentication.validate_ticket*')
23
- backend.delete_matched("#{uses_key('Cassette::Authentication.validate_ticket')}*")
24
- end
23
+ def clear_authentication_cache!
24
+ backend.delete_matched('Cassette::Authentication.validate_ticket*')
25
+ backend.delete_matched("#{uses_key('Cassette::Authentication.validate_ticket')}*")
26
+ end
25
27
 
26
- protected
28
+ protected
27
29
 
28
- attr_accessor :logger
30
+ attr_accessor :logger
31
+ end
32
+ end
29
33
  end
@@ -3,39 +3,58 @@
3
3
  require 'active_support/concern'
4
4
  require 'cassette/authentication/user'
5
5
 
6
- module Cassette::Authentication::Filter
7
- extend ActiveSupport::Concern
8
-
9
- included do |controller|
10
- controller.before_filter(:validate_authentication_ticket)
11
- controller.send(:attr_accessor, :current_user)
12
- end
13
-
14
- module ClassMethods
15
- def skip_authentication(*options)
16
- skip_before_filter :validate_authentication_ticket, *options
17
- end
18
- end
19
-
20
- def validate_authentication_ticket(service = Cassette.config.service)
21
- ticket = request.headers['Service-Ticket'] || params[:ticket]
22
-
23
- if ENV['NOAUTH']
24
- Cassette.logger.debug 'NOAUTH set and no Service Ticket, skipping authentication'
25
- self.current_user = Cassette::Authentication::User.new
26
- return
6
+ module Cassette
7
+ class Authentication
8
+ module Filter
9
+ extend ActiveSupport::Concern
10
+
11
+ included do |controller|
12
+ controller.before_filter(:validate_authentication_ticket)
13
+ controller.send(:attr_accessor, :current_user)
14
+ end
15
+
16
+ module ClassMethods
17
+ def skip_authentication(*options)
18
+ skip_before_filter :validate_authentication_ticket, *options
19
+ end
20
+ end
21
+
22
+ def accepts_authentication_service?(service)
23
+ config = Cassette.config
24
+
25
+ if config.respond_to?(:services)
26
+ config.services.member?(service) || config.service == service
27
+ else
28
+ config.service == service
29
+ end
30
+ end
31
+
32
+ def validate_authentication_ticket(service = authentication_service)
33
+ ticket = request.headers['Service-Ticket'] || params[:ticket]
34
+
35
+ if ENV['NOAUTH']
36
+ Cassette.logger.debug 'NOAUTH set and no Service Ticket, skipping authentication'
37
+ self.current_user = Cassette::Authentication::User.new
38
+ return
39
+ end
40
+
41
+ fail Cassette::Errors::Forbidden unless accepts_authentication_service?(authentication_service)
42
+ self.current_user = Cassette::Authentication.validate_ticket(ticket, service)
43
+ end
44
+
45
+ def authentication_service
46
+ Cassette.config.service
47
+ end
48
+
49
+ def validate_role!(role)
50
+ return if ENV['NOAUTH']
51
+ fail Cassette::Errors::Forbidden unless current_user.has_role?(role)
52
+ end
53
+
54
+ def validate_raw_role!(role)
55
+ return if ENV['NOAUTH']
56
+ fail Cassette::Errors::Forbidden unless current_user.has_raw_role?(role)
57
+ end
27
58
  end
28
-
29
- self.current_user = Cassette::Authentication.validate_ticket(ticket, service)
30
- end
31
-
32
- def validate_role!(role)
33
- return if ENV['NOAUTH']
34
- fail Cassette::Errors::Forbidden unless current_user.has_role?(role)
35
- end
36
-
37
- def validate_raw_role!(role)
38
- return if ENV['NOAUTH']
39
- fail Cassette::Errors::Forbidden unless current_user.has_raw_role?(role)
40
59
  end
41
60
  end
@@ -4,24 +4,28 @@ require 'cassette/authentication'
4
4
  require 'cassette/authentication/authorities'
5
5
  require 'delegate'
6
6
 
7
- class Cassette::Authentication::User
8
- attr_accessor :login, :name, :authorities, :email, :ticket
9
- delegate :has_role?, :has_raw_role?, to: :@authorities
7
+ module Cassette
8
+ class Authentication
9
+ class User
10
+ attr_accessor :login, :name, :authorities, :email, :ticket, :type
11
+ delegate :has_role?, :has_raw_role?, to: :@authorities
10
12
 
11
- def initialize(attrs = {})
12
- config = attrs[:config]
13
- @login = attrs[:login]
14
- @name = attrs[:name]
15
- @type = attrs[:type]
16
- @email = attrs[:email]
17
- @ticket = attrs[:ticket]
18
- @authorities = Cassette::Authentication::Authorities
19
- .parse(attrs.fetch(:authorities, '[]'), config && config.base_authority)
20
- end
13
+ def initialize(attrs = {})
14
+ config = attrs[:config]
15
+ @login = attrs[:login]
16
+ @name = attrs[:name]
17
+ @type = attrs[:type]
18
+ @email = attrs[:email]
19
+ @ticket = attrs[:ticket]
20
+ @authorities = Cassette::Authentication::Authorities
21
+ .parse(attrs.fetch(:authorities, '[]'), config && config.base_authority)
22
+ end
21
23
 
22
- %w(customer employee).each do |type|
23
- define_method :"#{type}?" do
24
- !@type.nil? && @type.to_s.downcase == type.to_s
24
+ %w(customer employee).each do |type|
25
+ define_method :"#{type}?" do
26
+ !@type.nil? && @type.to_s.downcase == type.to_s
27
+ end
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -1,25 +1,22 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'active_support/xml_mini'
4
- ActiveSupport::XmlMini.backend = 'LibXML'
5
-
6
3
  module Cassette
7
4
  class Authentication
8
5
  def self.method_missing(name, *args)
9
- @@default_authentication ||= new
10
- @@default_authentication.send(name, *args)
6
+ @default_authentication ||= new
7
+ @default_authentication.send(name, *args)
11
8
  end
12
9
 
13
10
  def initialize(opts = {})
14
11
  self.config = opts.fetch(:config, Cassette.config)
15
12
  self.logger = opts.fetch(:logger, Cassette.logger)
16
- self.http = opts.fetch(:http_client, Cassette)
13
+ self.http = opts.fetch(:http_client, Cassette::Http)
17
14
  self.cache = opts.fetch(:cache, Cassette::Authentication::Cache.new(logger))
18
15
  end
19
16
 
20
17
  def validate_ticket(ticket, service = config.service)
21
- logger.debug "Cassette::Authentication validating ticket: #{ticket}"
22
- fail Cassette::Errors::AuthorizationRequired if ticket.nil? || ticket.blank?
18
+ logger.debug "Cassette::Authentication validating ticket: #{ticket}, #{service}"
19
+ fail Cassette::Errors::AuthorizationRequired if ticket.blank?
23
20
 
24
21
  user = ticket_user(ticket, service)
25
22
  logger.info "Cassette::Authentication user: #{user.inspect}"
@@ -30,33 +27,25 @@ module Cassette
30
27
  end
31
28
 
32
29
  def ticket_user(ticket, service = config.service)
33
- cache.fetch_authentication(ticket) do
30
+ cache.fetch_authentication(ticket, service) do
34
31
  begin
35
32
  logger.info("Validating #{ticket} on #{validate_uri}")
33
+
36
34
  response = http.post(validate_uri, ticket: ticket, service: service).body
35
+ ticket_response = Http::TicketResponse.new(response)
37
36
 
38
37
  logger.info("Validation resut: #{response.inspect}")
39
38
 
40
- user = nil
41
-
42
- ActiveSupport::XmlMini.with_backend('LibXML') do
43
- result = ActiveSupport::XmlMini.parse(response)
44
-
45
- login = result.try(:[], 'serviceResponse').try(:[], 'authenticationSuccess').try(:[], 'user').try(:[], '__content__')
46
-
47
- if login
48
- attributes = result['serviceResponse']['authenticationSuccess']['attributes']
49
- name = attributes.try(:[], 'cn').try(:[], '__content__')
50
- authorities = attributes.try(:[], 'authorities').try(:[], '__content__')
51
-
52
- user = Cassette::Authentication::User.new(login: login, name: name, authorities: authorities, ticket: ticket, config: config)
53
- end
54
- end
55
-
56
- user
39
+ Cassette::Authentication::User.new(
40
+ login: ticket_response.login,
41
+ name: ticket_response.name,
42
+ authorities: ticket_response.authorities,
43
+ ticket: ticket,
44
+ config: config
45
+ ) if ticket_response.login
57
46
  rescue => exception
58
47
  logger.error "Error while authenticating ticket #{ticket}: #{exception.message}"
59
- raise Cassette::Errors::Forbidden.new(exception.message)
48
+ raise Cassette::Errors::Forbidden, exception.message
60
49
  end
61
50
  end
62
51
  end
@@ -65,6 +54,29 @@ module Cassette
65
54
 
66
55
  attr_accessor :cache, :logger, :http, :config
67
56
 
57
+ def try_content(node, *keys)
58
+ keys.inject(node) do |a, e|
59
+ a.try(:[], e)
60
+ end.try(:[], '__content__')
61
+ end
62
+
63
+ def extract_user(xml, ticket)
64
+ ActiveSupport::XmlMini.with_backend('LibXML') do
65
+ result = ActiveSupport::XmlMini.parse(xml)
66
+
67
+ login = try_content(result, 'serviceResponse', 'authenticationSuccess', 'user')
68
+
69
+ if login
70
+ attributes = result['serviceResponse']['authenticationSuccess']['attributes']
71
+ name = try_content(attributes, 'cn')
72
+ authorities = try_content(attributes, 'authorities')
73
+
74
+ Cassette::Authentication::User.new(login: login, name: name, authorities: authorities,
75
+ ticket: ticket, config: config)
76
+ end
77
+ end
78
+ end
79
+
68
80
  def validate_uri
69
81
  "#{config.base.gsub(/\/?$/, '')}/serviceValidate"
70
82
  end
@@ -6,8 +6,8 @@ module Cassette
6
6
  module Cache
7
7
  def backend
8
8
  @backend ||= begin
9
- if defined?(Rails) && Rails.cache
10
- Rails.cache
9
+ if defined?(::Rails) && ::Rails.cache
10
+ ::Rails.cache
11
11
  else
12
12
  ActiveSupport::Cache::MemoryStore.new
13
13
  end
@@ -3,41 +3,45 @@
3
3
  require 'cassette/client'
4
4
  require 'cassette/cache'
5
5
 
6
- class Cassette::Client::Cache
7
- include Cassette::Cache
8
-
9
- def initialize(logger)
10
- self.logger = logger
11
- end
12
-
13
- def fetch_tgt(options = {}, &_block)
14
- options = { expires_in: 4 * 3600, max_uses: 5000, force: false }.merge(options)
15
- fetch('Cassette::Client.tgt', options) do
16
- self.clear_st_cache!
17
- logger.info 'TGT is not cached'
18
- yield
19
- end
20
- end
21
-
22
- def fetch_st(service, options = {}, &_block)
23
- options = { max_uses: 2000, expires_in: 252, force: false }.merge(options)
24
- fetch("Cassette::Client.st(#{service})", options) do
25
- logger.info "ST for #{service} is not cached"
26
- yield
6
+ module Cassette
7
+ class Client
8
+ class Cache
9
+ include Cassette::Cache
10
+
11
+ def initialize(logger)
12
+ self.logger = logger
13
+ end
14
+
15
+ def fetch_tgt(options = {}, &_block)
16
+ options = { expires_in: 4 * 3600, max_uses: 5000, force: false }.merge(options)
17
+ fetch('Cassette::Client.tgt', options) do
18
+ self.clear_st_cache!
19
+ logger.info 'TGT is not cached'
20
+ yield
21
+ end
22
+ end
23
+
24
+ def fetch_st(service, options = {}, &_block)
25
+ options = { max_uses: 2000, expires_in: 252, force: false }.merge(options)
26
+ fetch("Cassette::Client.st(#{service})", options) do
27
+ logger.info "ST for #{service} is not cached"
28
+ yield
29
+ end
30
+ end
31
+
32
+ def clear_tgt_cache!
33
+ backend.delete('Cassette::Client.tgt')
34
+ backend.delete("#{uses_key('Cassette::Client.tgt')}")
35
+ end
36
+
37
+ def clear_st_cache!
38
+ backend.delete_matched('Cassette::Client.st*')
39
+ backend.delete_matched("#{uses_key('Cassette::Client.st')}*")
40
+ end
41
+
42
+ protected
43
+
44
+ attr_accessor :logger
27
45
  end
28
46
  end
29
-
30
- def clear_tgt_cache!
31
- backend.delete('Cassette::Client.tgt')
32
- backend.delete("#{uses_key('Cassette::Client.tgt')}")
33
- end
34
-
35
- def clear_st_cache!
36
- backend.delete_matched('Cassette::Client.st*')
37
- backend.delete_matched("#{uses_key('Cassette::Client.st')}*")
38
- end
39
-
40
- protected
41
-
42
- attr_accessor :logger
43
47
  end
@@ -3,8 +3,8 @@
3
3
  module Cassette
4
4
  class Client
5
5
  def self.method_missing(name, *args)
6
- @@default_client ||= new
7
- @@default_client.send(name, *args)
6
+ @default_client ||= new
7
+ @default_client.send(name, *args)
8
8
  end
9
9
 
10
10
  def initialize(opts = {})
@@ -28,9 +28,10 @@ module Cassette
28
28
  end
29
29
  end
30
30
 
31
- def st(tgt, service, force = false)
31
+ def st(tgt_param, service, force = false)
32
32
  logger.info "Requesting ST for #{service}"
33
33
  cache.fetch_st(service, force: force) do
34
+ tgt = tgt_param.respond_to?(:call) ? tgt_param[] : tgt_param
34
35
  response = http.post("#{tickets_uri}/#{tgt}", service: service)
35
36
  response.body.tap do |st|
36
37
  logger.info "ST is #{st}"
@@ -47,7 +48,7 @@ module Cassette
47
48
  attr_accessor :cache, :logger, :http, :config
48
49
 
49
50
  def st_with_retry(user, pass, service, retrying = false)
50
- st(tgt(user, pass, retrying), service)
51
+ st(->{ tgt(user, pass, retrying) }, service)
51
52
  rescue Cassette::Errors::NotFound => e
52
53
  raise e if retrying
53
54
 
@@ -0,0 +1,20 @@
1
+ require 'delegate'
2
+ require 'active_support/xml_mini'
3
+
4
+ module Cassette
5
+ module Http
6
+ class ParsedResponse < SimpleDelegator
7
+ def initialize(raw_content, parser = XMLParser)
8
+ super(parser.call(raw_content))
9
+ end
10
+
11
+ XMLParser = lambda do |raw_content|
12
+ ActiveSupport::XmlMini.with_backend('LibXML') do
13
+ ActiveSupport::XmlMini.parse(raw_content)
14
+ end
15
+ end
16
+
17
+ private_constant :XMLParser
18
+ end
19
+ end
20
+ end