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