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.
- checksums.yaml +4 -4
- data/README.md +89 -12
- data/lib/cassette/authentication/authorities.rb +34 -30
- data/lib/cassette/authentication/cache.rb +22 -18
- data/lib/cassette/authentication/filter.rb +52 -33
- data/lib/cassette/authentication/user.rb +20 -16
- data/lib/cassette/authentication.rb +39 -27
- data/lib/cassette/cache.rb +2 -2
- data/lib/cassette/client/cache.rb +39 -35
- data/lib/cassette/client.rb +5 -4
- data/lib/cassette/http/parsed_response.rb +20 -0
- data/lib/cassette/http/request.rb +44 -0
- data/lib/cassette/http/ticket_response.rb +48 -0
- data/lib/cassette/http.rb +8 -0
- data/lib/cassette/rubycas/helper.rb +2 -8
- data/lib/cassette/rubycas/routing_constraint.rb +23 -0
- data/lib/cassette/rubycas/single_sign_out_constraint.rb +8 -8
- data/lib/cassette/rubycas/user_factory.rb +14 -0
- data/lib/cassette/rubycas.rb +2 -0
- data/lib/cassette/version.rb +2 -2
- data/lib/cassette.rb +12 -50
- data/spec/cassette/authentication/authorities_spec.rb +1 -1
- data/spec/cassette/authentication/cache_spec.rb +40 -4
- data/spec/cassette/authentication/filter_spec.rb +106 -36
- data/spec/cassette/authentication/user_factory_spec.rb +42 -0
- data/spec/cassette/authentication/user_spec.rb +4 -3
- data/spec/cassette/authentication_spec.rb +24 -12
- data/spec/cassette/cache_spec.rb +0 -2
- data/spec/cassette/client/cache_spec.rb +1 -1
- data/spec/cassette/client_spec.rb +319 -0
- data/spec/cassette/errors_spec.rb +1 -1
- data/spec/cassette/http/parsed_response_spec.rb +27 -0
- data/spec/cassette/http/request_spec.rb +41 -0
- data/spec/cassette/http/ticket_response_spec.rb +41 -0
- data/spec/cassette/rubycas/routing_constraint_spec.rb +84 -0
- data/spec/cassette_spec.rb +36 -0
- data/spec/integration/cas/client_spec.rb +0 -3
- data/spec/spec_helper.rb +5 -0
- data/spec/support/controllers/controller_mock.rb +19 -0
- metadata +98 -36
- data/spec/cas_spec.rb +0 -78
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4fc103e95f0afcbec1f342bda0bf6c26c16aaab
|
4
|
+
data.tar.gz: 4fec669e36c9fc02dac165f203f23635fb60c9f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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'
|
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 "
|
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
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
7
|
-
|
6
|
+
module Cassette
|
7
|
+
class Authentication
|
8
|
+
class Cache
|
9
|
+
include Cassette::Cache
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
11
|
+
def initialize(logger)
|
12
|
+
self.logger = logger
|
13
|
+
end
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
28
|
+
protected
|
27
29
|
|
28
|
-
|
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
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
10
|
-
|
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.
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
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
|
data/lib/cassette/cache.rb
CHANGED
@@ -3,41 +3,45 @@
|
|
3
3
|
require 'cassette/client'
|
4
4
|
require 'cassette/cache'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
data/lib/cassette/client.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
module Cassette
|
4
4
|
class Client
|
5
5
|
def self.method_missing(name, *args)
|
6
|
-
|
7
|
-
|
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(
|
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
|