cassette 1.0.18 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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