sso 0.1.2 → 0.1.3
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/lib/sso.rb +3 -3
- data/lib/sso/benchmarking.rb +6 -2
- data/lib/sso/client/authentications/passport.rb +2 -2
- data/lib/sso/client/passport_verifier.rb +9 -5
- data/lib/sso/client/warden/hooks/after_fetch.rb +31 -10
- data/lib/sso/client/warden/strategies/passport.rb +2 -2
- data/lib/sso/{server/configuration.rb → configuration.rb} +13 -1
- data/lib/sso/{server/configure.rb → configure.rb} +0 -0
- data/lib/sso/meter.rb +32 -0
- data/lib/sso/server/authentications/passport.rb +2 -2
- data/lib/sso/server/middleware/passport_destruction.rb +1 -1
- data/lib/sso/server/middleware/passport_exchange.rb +6 -6
- data/lib/sso/server/passport.rb +2 -2
- data/lib/sso/server/warden/strategies/passport.rb +3 -3
- data/spec/dummy/app/controllers/sessions_controller.rb +4 -3
- data/spec/lib/sso/benchmarking_spec.rb +83 -0
- data/spec/lib/sso/client/warden/hooks/after_fetch_spec.rb +190 -1
- data/spec/lib/sso/client/warden/strategies/passport_spec.rb +1 -1
- data/spec/spec_helper.rb +9 -0
- data/spec/support/sso/test/helpers.rb +11 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9660c9f78d14f395529c39a41a4355c7b0141cd
|
4
|
+
data.tar.gz: 56552af9e5cc37cae7ef084c68e80a0ac0a4d4c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b86244b2218c89bdac9433bf12c69ee0119e834cc1ec2c9d24a260e5121c05810a28cfbdb70cb012032383e59c1969e8322e5be21a7ef67b10973de0af66c8a5
|
7
|
+
data.tar.gz: 2823fc5f108f37bda20eaf72c75abaa70db5a270cb5522fa7d2dee388e57d26990d8a2010d2d283946bf3cef28d36a62be6254eea09b7f0fe3af9319185f30f4
|
data/lib/sso.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'operation'
|
2
2
|
|
3
3
|
require 'sso/logging'
|
4
|
+
require 'sso/meter'
|
4
5
|
require 'sso/benchmarking'
|
5
|
-
|
6
|
-
require 'sso/
|
7
|
-
require 'sso/server/configure'
|
6
|
+
require 'sso/configuration'
|
7
|
+
require 'sso/configure'
|
8
8
|
|
9
9
|
require 'sso/client/omniauth/strategies/sso'
|
10
10
|
|
data/lib/sso/benchmarking.rb
CHANGED
@@ -2,13 +2,17 @@ module SSO
|
|
2
2
|
# Helper to log results of benchmarks.
|
3
3
|
module Benchmarking
|
4
4
|
include ::SSO::Logging
|
5
|
+
include ::SSO::Meter
|
5
6
|
|
6
|
-
def benchmark(name, &block)
|
7
|
+
def benchmark(name: nil, metric: nil, &block)
|
8
|
+
return unless block_given?
|
7
9
|
result = nil
|
8
10
|
seconds = Benchmark.realtime do
|
9
11
|
result = block.call
|
10
12
|
end
|
11
|
-
|
13
|
+
milliseconds = (seconds * 1000).round
|
14
|
+
debug { "#{name || metric || 'Benchmark'} took #{milliseconds}ms" }
|
15
|
+
histogram key: metric, value: milliseconds if metric
|
12
16
|
result
|
13
17
|
end
|
14
18
|
end
|
@@ -85,7 +85,7 @@ module SSO
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def failure_rack_array
|
88
|
-
payload = { success:
|
88
|
+
payload = { success: false, code: :passport_verification_failed }
|
89
89
|
[200, { 'Content-Type' => 'application/json' }, [payload.to_json]]
|
90
90
|
end
|
91
91
|
|
@@ -121,7 +121,7 @@ module SSO
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def decrypt_chip!
|
124
|
-
benchmark 'Passport chip decryption' do
|
124
|
+
benchmark(name: 'Passport chip decryption') do
|
125
125
|
decipher = chip_digest
|
126
126
|
decipher.decrypt
|
127
127
|
decipher.key = chip_key
|
@@ -18,18 +18,22 @@ module SSO
|
|
18
18
|
fetch_response { |failure| return failure }
|
19
19
|
interpret_response
|
20
20
|
|
21
|
-
rescue JSON::ParserError
|
21
|
+
rescue ::JSON::ParserError
|
22
22
|
error { 'SSO Server response is not valid JSON.' }
|
23
23
|
error { response.inspect }
|
24
|
+
Operations.failure :server_response_not_parseable, object: response
|
25
|
+
end
|
26
|
+
|
27
|
+
def human_readable_timeout_in_ms
|
28
|
+
"#{timeout_in_milliseconds}ms"
|
24
29
|
end
|
25
30
|
|
26
31
|
private
|
27
32
|
|
28
33
|
def fetch_response
|
29
|
-
yield Operations.failure(:server_unreachable, object: response) unless response.code == 200
|
34
|
+
yield Operations.failure(:server_unreachable, object: response) unless response.code.to_s == '200'
|
30
35
|
yield Operations.failure(:server_response_not_parseable, object: response) unless parsed_response
|
31
36
|
yield Operations.failure(:server_response_missing_success_flag, object: response) unless response_has_success_flag?
|
32
|
-
yield Operations.failure(:server_response_unsuccessful, object: response) unless parsed_response['success'].to_s == 'true'
|
33
37
|
Operations.success :server_response_looks_legit
|
34
38
|
end
|
35
39
|
|
@@ -130,7 +134,7 @@ module SSO
|
|
130
134
|
|
131
135
|
def response!
|
132
136
|
debug { "Fetching Passport from #{endpoint.inspect}" }
|
133
|
-
benchmark 'Passport
|
137
|
+
benchmark(name: 'Passport verification request', metric: 'client.passport.verification.duration') do
|
134
138
|
::HTTParty.get endpoint, timeout: timeout_in_seconds, query: query_params, headers: { 'Accept' => 'application/json' }
|
135
139
|
end
|
136
140
|
end
|
@@ -140,7 +144,7 @@ module SSO
|
|
140
144
|
end
|
141
145
|
|
142
146
|
def response_has_success_flag?
|
143
|
-
parsed_response && parsed_response.respond_to?(:key?) && parsed_response.key?('success')
|
147
|
+
parsed_response && parsed_response.respond_to?(:key?) && (parsed_response.key?('success') || parsed_response.key?(:success))
|
144
148
|
end
|
145
149
|
|
146
150
|
end
|
@@ -12,6 +12,7 @@ module SSO
|
|
12
12
|
class AfterFetch
|
13
13
|
include ::SSO::Logging
|
14
14
|
include ::SSO::Benchmarking
|
15
|
+
include ::SSO::Meter
|
15
16
|
|
16
17
|
attr_reader :passport, :warden, :options
|
17
18
|
delegate :request, to: :warden
|
@@ -31,12 +32,14 @@ module SSO
|
|
31
32
|
return unless passport.is_a?(::SSO::Client::Passport)
|
32
33
|
verify
|
33
34
|
|
34
|
-
rescue Timeout::Error
|
35
|
+
rescue ::Timeout::Error
|
35
36
|
error { 'SSO Server timed out. Continuing with last known authentication/authorization...' }
|
36
|
-
|
37
|
+
meter :timeout, timeout_ms: verifier.human_readable_timeout_in_ms
|
38
|
+
Operations.failure :server_request_timed_out
|
37
39
|
|
38
40
|
rescue => exception
|
39
41
|
::SSO.config.exception_handler.call exception
|
42
|
+
Operations.failure :client_exception_caught
|
40
43
|
end
|
41
44
|
|
42
45
|
private
|
@@ -53,14 +56,17 @@ module SSO
|
|
53
56
|
verification.code
|
54
57
|
end
|
55
58
|
|
59
|
+
def verification_object
|
60
|
+
verification.object
|
61
|
+
end
|
62
|
+
|
56
63
|
def verify
|
57
64
|
debug { "Validating Passport #{passport.id.inspect} of logged in #{passport.user.class} in scope #{warden_scope.inspect}" }
|
58
65
|
|
59
66
|
case verification_code
|
60
67
|
when :server_unreachable then server_unreachable!
|
61
68
|
when :server_response_not_parseable then server_response_not_parseable!
|
62
|
-
when :server_response_missing_success_flag
|
63
|
-
when :server_response_unsuccessful! then server_response_unsuccessful!
|
69
|
+
when :server_response_missing_success_flag then server_response_missing_success_flag!
|
64
70
|
when :passport_valid then passport_valid!
|
65
71
|
when :passport_valid_and_modified then passport_valid_and_modified!(verification.object)
|
66
72
|
when :passport_invalid then passport_invalid!
|
@@ -74,39 +80,54 @@ module SSO
|
|
74
80
|
passport.modified!
|
75
81
|
passport.user = modified_passport.user
|
76
82
|
passport.state = modified_passport.state
|
77
|
-
|
83
|
+
meter :valid_and_modified
|
84
|
+
Operations.success :valid_and_modified
|
78
85
|
end
|
79
86
|
|
80
87
|
def passport_valid!
|
81
88
|
debug { 'Valid passport, no changes' }
|
82
89
|
passport.verified!
|
83
|
-
|
90
|
+
meter :valid
|
91
|
+
Operations.success :valid
|
84
92
|
end
|
85
93
|
|
86
94
|
def passport_invalid!
|
87
95
|
info { 'Your Passport is not valid any more.' }
|
88
96
|
warden.logout warden_scope
|
89
|
-
|
97
|
+
meter :invalid
|
98
|
+
Operations.failure :invalid
|
90
99
|
end
|
91
100
|
|
92
101
|
def server_unreachable!
|
93
|
-
error { "SSO Server responded with an unexpected HTTP status code (#{
|
102
|
+
error { "SSO Server responded with an unexpected HTTP status code (#{verification_code.inspect} instead of 200). #{verification_object.inspect}" }
|
103
|
+
meter :server_unreachable
|
104
|
+
Operations.failure :server_unreachable
|
94
105
|
end
|
95
106
|
|
96
107
|
def server_response_missing_success_flag!
|
97
108
|
error { 'SSO Server response did not include the expected success flag.' }
|
109
|
+
meter :server_response_missing_success_flag
|
110
|
+
Operations.failure :server_response_missing_success_flag
|
98
111
|
end
|
99
112
|
|
100
113
|
def unexpected_server_response_status!
|
101
114
|
error { "SSO Server response did not include a known passport status code. #{verification_code.inspect}" }
|
115
|
+
meter :unexpected_server_response_status
|
116
|
+
Operations.failure :unexpected_server_response_status
|
102
117
|
end
|
103
118
|
|
104
119
|
def server_response_not_parseable!
|
105
120
|
error { 'SSO Server response could not be parsed at all.' }
|
121
|
+
meter :server_response_not_parseable
|
122
|
+
Operations.failure :server_response_not_parseable
|
106
123
|
end
|
107
124
|
|
108
|
-
def meter(
|
109
|
-
|
125
|
+
def meter(key, data = {})
|
126
|
+
options[:key] = "client.warden.hooks.after_fetch.#{key}"
|
127
|
+
options[:tags] = { scope: warden_scope }
|
128
|
+
data[:passport_id] = passport.id
|
129
|
+
options[:data] = data
|
130
|
+
track options
|
110
131
|
end
|
111
132
|
|
112
133
|
# TODO: Use ActionDispatch remote IP or you might get the Load Balancer's IP instead :(
|
@@ -21,7 +21,7 @@ module SSO
|
|
21
21
|
debug { "Persisting trusted Passport #{authentication.object.inspect}" }
|
22
22
|
success! authentication.object
|
23
23
|
else
|
24
|
-
debug { 'Authentication from Passport failed.' }
|
24
|
+
debug { 'Authentication from Passport on Client failed.' }
|
25
25
|
debug { "Responding with #{authentication.object.inspect}" }
|
26
26
|
custom! authentication.object
|
27
27
|
end
|
@@ -31,7 +31,7 @@ module SSO
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def passport_authentication
|
34
|
-
benchmark 'Passport proxy verification' do
|
34
|
+
benchmark(name: 'Passport proxy verification request', metric: 'client.passport.verification') do
|
35
35
|
::SSO::Client::Authentications::Passport.new(request).authenticate
|
36
36
|
end
|
37
37
|
end
|
@@ -2,6 +2,7 @@ require 'logger'
|
|
2
2
|
|
3
3
|
module SSO
|
4
4
|
class Configuration
|
5
|
+
include ::SSO::Logging
|
5
6
|
|
6
7
|
# Server
|
7
8
|
|
@@ -54,6 +55,11 @@ module SSO
|
|
54
55
|
end
|
55
56
|
attr_writer :exception_handler
|
56
57
|
|
58
|
+
def metric
|
59
|
+
@metric || default_metric
|
60
|
+
end
|
61
|
+
attr_writer :metric
|
62
|
+
|
57
63
|
def passport_chip_key
|
58
64
|
@passport_chip_key || fail('You need to configure a secret passport_chip_key, see SSO::Configuration for more info.')
|
59
65
|
end
|
@@ -110,8 +116,14 @@ module SSO
|
|
110
116
|
end
|
111
117
|
end
|
112
118
|
|
119
|
+
def default_metric
|
120
|
+
proc do |type:, key:, value:, tags:, data:|
|
121
|
+
debug { "Measuring #{type.inspect} with key #{key.inspect} and value #{value.inspect} and tags #{tags} and data #{data}" }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
113
125
|
def default_session_backend
|
114
|
-
fail('You need to configure session_backend, see SSO::Configuration for more info.') unless %w(
|
126
|
+
fail('You need to configure session_backend, see SSO::Configuration for more info.') unless %w(development test).include?(environment)
|
115
127
|
end
|
116
128
|
|
117
129
|
def default_passport_verification_timeout_ms
|
File without changes
|
data/lib/sso/meter.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module SSO
|
2
|
+
module Meter
|
3
|
+
include ::SSO::Logging
|
4
|
+
|
5
|
+
def track(key:, value: 1, tags: nil, data: {})
|
6
|
+
data[:caller] = caller_name
|
7
|
+
# info { "Measuring increment #{key.inspect} with value #{value.inspect} and tags #{tags} and data #{data}" }
|
8
|
+
metric.call type: :increment, key: "sso.#{key}", value: value, tags: tags, data: data
|
9
|
+
|
10
|
+
rescue => exception
|
11
|
+
::SSO.config.exception_handler.call exception
|
12
|
+
end
|
13
|
+
|
14
|
+
def histogram(key:, value:, tags: nil, data: {})
|
15
|
+
data[:caller] = caller_name
|
16
|
+
# info { "Measuring histogram #{key.inspect} with value #{value.inspect} and tags #{tags} and data #{data}" }
|
17
|
+
metric.call type: :histogram, key: "sso.#{key}", value: value, tags: tags, data: data
|
18
|
+
|
19
|
+
rescue => exception
|
20
|
+
::SSO.config.exception_handler.call exception
|
21
|
+
end
|
22
|
+
|
23
|
+
def caller_name
|
24
|
+
self.class.name
|
25
|
+
end
|
26
|
+
|
27
|
+
def metric
|
28
|
+
::SSO.config.metric
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -15,7 +15,7 @@ module SSO
|
|
15
15
|
result
|
16
16
|
else
|
17
17
|
# TODO: Prevent Flooding here.
|
18
|
-
debug { "The Passport authentication failed: #{result.code}" }
|
18
|
+
debug { "The Server Passport authentication failed: #{result.code}" }
|
19
19
|
Operations.failure :passport_authentication_failed, object: failure_rack_array
|
20
20
|
end
|
21
21
|
end
|
@@ -73,7 +73,7 @@ module SSO
|
|
73
73
|
# all passports simply because a load balancer is pointing to the wrong Rails application or something.
|
74
74
|
#
|
75
75
|
def failure_rack_array
|
76
|
-
payload = { success:
|
76
|
+
payload = { success: false, code: :passport_invalid }
|
77
77
|
[200, { 'Content-Type' => 'application/json' }, [payload.to_json]]
|
78
78
|
end
|
79
79
|
|
@@ -27,7 +27,7 @@ module SSO
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def json_code(code)
|
30
|
-
[200, { 'Content-Type' => 'application/json' }, [{ success:
|
30
|
+
[200, { 'Content-Type' => 'application/json' }, [{ success: false, code: code }.to_json]]
|
31
31
|
end
|
32
32
|
|
33
33
|
def passports_path
|
@@ -19,16 +19,16 @@ module SSO
|
|
19
19
|
end
|
20
20
|
|
21
21
|
token = request.params['access_token']
|
22
|
-
debug { "Detected incoming Passport
|
22
|
+
debug { "Detected incoming Passport exchange request for access token #{token.inspect}" }
|
23
23
|
access_token = ::Doorkeeper::AccessToken.find_by_token token
|
24
24
|
|
25
|
-
return
|
26
|
-
return
|
25
|
+
return json_error :access_token_not_found unless access_token
|
26
|
+
return json_error :access_token_invalid unless access_token.valid?
|
27
27
|
|
28
28
|
finding = ::SSO::Server::Passports.find_by_access_token_id(access_token.id)
|
29
29
|
if finding.failure?
|
30
30
|
# This should never happen. Every Access Token should be connected to a Passport.
|
31
|
-
return
|
31
|
+
return json_error :passport_not_found
|
32
32
|
end
|
33
33
|
passport = finding.object
|
34
34
|
|
@@ -44,8 +44,8 @@ module SSO
|
|
44
44
|
[200, { 'Content-Type' => 'application/json' }, [payload.to_json]]
|
45
45
|
end
|
46
46
|
|
47
|
-
def
|
48
|
-
[200, { 'Content-Type' => 'application/json' }, [{ success:
|
47
|
+
def json_error(code)
|
48
|
+
[200, { 'Content-Type' => 'application/json' }, [{ success: false, code: code }.to_json]]
|
49
49
|
end
|
50
50
|
|
51
51
|
def passports_path
|
data/lib/sso/server/passport.rb
CHANGED
@@ -46,7 +46,7 @@ module SSO
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def state!
|
49
|
-
result = benchmark 'Passport user state calculation' do
|
49
|
+
result = benchmark(name: 'Passport user state calculation') do
|
50
50
|
OpenSSL::HMAC.hexdigest user_state_digest, user_state_key, user_state_base
|
51
51
|
end
|
52
52
|
debug { "The user state is #{result.inspect}" }
|
@@ -62,7 +62,7 @@ module SSO
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def chip!
|
65
|
-
benchmark 'Passport chip encryption' do
|
65
|
+
benchmark(name: 'Passport chip encryption') do
|
66
66
|
ensure_secret
|
67
67
|
cipher = chip_digest
|
68
68
|
cipher.encrypt
|
@@ -20,8 +20,8 @@ module SSO
|
|
20
20
|
debug { "Responding with #{authentication.object}" }
|
21
21
|
custom! authentication.object
|
22
22
|
else
|
23
|
-
debug { 'Authentication from Passport failed.' }
|
24
|
-
|
23
|
+
debug { 'Authentication from Passport on Server failed.' }
|
24
|
+
custom! authentication.object
|
25
25
|
end
|
26
26
|
|
27
27
|
rescue => exception
|
@@ -29,7 +29,7 @@ module SSO
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def passport_authentication
|
32
|
-
benchmark 'Passport verification' do
|
32
|
+
benchmark(name: 'Passport verification') do
|
33
33
|
::SSO::Server::Authentications::Passport.new(request).authenticate
|
34
34
|
end
|
35
35
|
end
|
@@ -2,7 +2,7 @@ class SessionsController < ApplicationController
|
|
2
2
|
include ::SSO::Logging
|
3
3
|
delegate :logout, to: :warden
|
4
4
|
|
5
|
-
before_action :
|
5
|
+
before_action :prevent_json, only: [:new]
|
6
6
|
|
7
7
|
# POI
|
8
8
|
def new
|
@@ -27,8 +27,9 @@ class SessionsController < ApplicationController
|
|
27
27
|
|
28
28
|
private
|
29
29
|
|
30
|
-
def
|
31
|
-
return unless request.format == :json
|
30
|
+
def prevent_json
|
31
|
+
return unless request.format.to_sym == :json
|
32
|
+
warn { "This request is asking for JSON where it shouldn't" }
|
32
33
|
render status: :unauthorized, json: { status: :error, code: :authentication_failed }
|
33
34
|
end
|
34
35
|
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe ::SSO::Benchmarking do
|
4
|
+
|
5
|
+
let(:instance) { MyTestNamespace::MyClass.new }
|
6
|
+
|
7
|
+
before do
|
8
|
+
stub_const 'MyTestNamespace', Module.new
|
9
|
+
stub_const 'MyTestNamespace::MyClass', Class.new { include SSO::Benchmarking }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#benchmark' do
|
13
|
+
context 'without block' do
|
14
|
+
it 'is nil' do
|
15
|
+
expect(instance.benchmark).to be_nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'block given' do
|
20
|
+
context 'without arguments' do
|
21
|
+
it 'returns what was passed in' do
|
22
|
+
duration = instance.benchmark { :something }
|
23
|
+
expect(duration).to eq :something
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'logs' do
|
27
|
+
expect(instance).to receive(:debug) do |_, &block|
|
28
|
+
expect(block.call).to eq 'Benchmark took 0ms'
|
29
|
+
end
|
30
|
+
instance.benchmark {}
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'does not meter' do
|
34
|
+
expect(::SSO.config).to_not receive(:metric)
|
35
|
+
instance.benchmark {}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'only with name' do
|
40
|
+
it 'logs with the name' do
|
41
|
+
expect(instance).to receive(:debug) do |_, &block|
|
42
|
+
expect(block.call).to eq 'Long calculation took 0ms'
|
43
|
+
end
|
44
|
+
instance.benchmark(name: 'Long calculation') {}
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does not meter' do
|
48
|
+
expect(instance).to_not receive(:histogram)
|
49
|
+
instance.benchmark(name: 'Long calculation') {}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'only with metric' do
|
54
|
+
it 'logs with the metric' do
|
55
|
+
expect(instance).to receive(:debug) do |_, &block|
|
56
|
+
expect(block.call).to eq 'blob.serialization took 0ms'
|
57
|
+
end
|
58
|
+
instance.benchmark(metric: 'blob.serialization') {}
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'meters as histogram with the metric as name' do
|
62
|
+
expect(instance).to receive(:histogram).with key: 'blob.serialization', value: 0
|
63
|
+
instance.benchmark(metric: 'blob.serialization') {}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'with name and metric' do
|
68
|
+
it 'logs with the name' do
|
69
|
+
expect(instance).to receive(:debug) do |_, &block|
|
70
|
+
expect(block.call).to eq 'Synchronous encryption took 0ms'
|
71
|
+
end
|
72
|
+
instance.benchmark(name: 'Synchronous encryption', metric: 'encryption.aes') {}
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'meters as histogram with the metric as name' do
|
76
|
+
expect(instance).to receive(:histogram).with key: 'encryption.aes', value: 0
|
77
|
+
instance.benchmark(name: 'Synchronous encryption', metric: 'encryption.aes') {}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
RSpec.describe SSO::Client::Warden::Hooks::AfterFetch, type: :request, db: true do
|
3
|
+
RSpec.describe SSO::Client::Warden::Hooks::AfterFetch, type: :request, db: true, stub_benchmarks: true do
|
4
4
|
|
5
5
|
# Client side
|
6
6
|
let(:warden_env) { {} }
|
@@ -10,6 +10,7 @@ RSpec.describe SSO::Client::Warden::Hooks::AfterFetch, type: :request, db: true
|
|
10
10
|
let(:hook) { described_class.new passport: client_passport, warden: warden, options: {} }
|
11
11
|
let(:client_user) { double :client_user, name: 'Good old client user' }
|
12
12
|
let(:client_passport) { ::SSO::Client::Passport.new id: passport_id, secret: passport_secret, state: passport_state, user: client_user }
|
13
|
+
let(:operation) { hook.call }
|
13
14
|
|
14
15
|
# Shared
|
15
16
|
let!(:oauth_app) { create :outsider_doorkeeper_application }
|
@@ -34,6 +35,10 @@ RSpec.describe SSO::Client::Warden::Hooks::AfterFetch, type: :request, db: true
|
|
34
35
|
context 'invalid passport' do
|
35
36
|
let(:passport_secret) { SecureRandom.uuid }
|
36
37
|
|
38
|
+
before do
|
39
|
+
expect(warden).to receive :logout
|
40
|
+
end
|
41
|
+
|
37
42
|
it 'does not verify the passport' do
|
38
43
|
expect(client_passport).to_not be_verified
|
39
44
|
hook.call
|
@@ -45,6 +50,20 @@ RSpec.describe SSO::Client::Warden::Hooks::AfterFetch, type: :request, db: true
|
|
45
50
|
hook.call
|
46
51
|
expect(client_passport).to_not be_modified
|
47
52
|
end
|
53
|
+
|
54
|
+
it 'fails' do
|
55
|
+
expect(operation).to be_failure
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'has a useful error code' do
|
59
|
+
expect(operation.code).to eq :invalid
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'meters the invalid passport' do
|
63
|
+
expect(::SSO.config.metric).to receive(:call).with type: :histogram, key: 'sso.client.passport.verification.duration', value: 42_000, tags: nil, data: { caller: 'SSO::Client::PassportVerifier' }
|
64
|
+
expect(::SSO.config.metric).to receive(:call).with type: :increment, key: 'sso.client.warden.hooks.after_fetch.invalid', value: 1, tags: { scope: nil }, data: { passport_id: client_passport.id, caller: 'SSO::Client::Warden::Hooks::AfterFetch' }
|
65
|
+
hook.call
|
66
|
+
end
|
48
67
|
end
|
49
68
|
|
50
69
|
context 'user does not change' do
|
@@ -64,6 +83,20 @@ RSpec.describe SSO::Client::Warden::Hooks::AfterFetch, type: :request, db: true
|
|
64
83
|
hook.call
|
65
84
|
expect(client_passport.user.name).to eq 'Good old client user'
|
66
85
|
end
|
86
|
+
|
87
|
+
it 'succeeds' do
|
88
|
+
expect(operation).to be_success
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'has a useful error code' do
|
92
|
+
expect(operation.code).to eq :valid
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'meters the invalid passport' do
|
96
|
+
expect(::SSO.config.metric).to receive(:call).with type: :histogram, key: 'sso.client.passport.verification.duration', value: 42_000, tags: nil, data: { caller: 'SSO::Client::PassportVerifier' }
|
97
|
+
expect(::SSO.config.metric).to receive(:call).with type: :increment, key: 'sso.client.warden.hooks.after_fetch.valid', value: 1, tags: { scope: nil }, data: { passport_id: client_passport.id, caller: 'SSO::Client::Warden::Hooks::AfterFetch' }
|
98
|
+
hook.call
|
99
|
+
end
|
67
100
|
end
|
68
101
|
|
69
102
|
context 'user attribute changed which is not included in the state digest' do
|
@@ -88,6 +121,20 @@ RSpec.describe SSO::Client::Warden::Hooks::AfterFetch, type: :request, db: true
|
|
88
121
|
hook.call
|
89
122
|
expect(client_passport.user.name).to eq 'Good old client user'
|
90
123
|
end
|
124
|
+
|
125
|
+
it 'succeeds' do
|
126
|
+
expect(operation).to be_success
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'has a useful error code' do
|
130
|
+
expect(operation.code).to eq :valid
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'meters the invalid passport' do
|
134
|
+
expect(::SSO.config.metric).to receive(:call).with type: :histogram, key: 'sso.client.passport.verification.duration', value: 42_000, tags: nil, data: { caller: 'SSO::Client::PassportVerifier' }
|
135
|
+
expect(::SSO.config.metric).to receive(:call).with type: :increment, key: 'sso.client.warden.hooks.after_fetch.valid', value: 1, tags: { scope: nil }, data: { passport_id: client_passport.id, caller: 'SSO::Client::Warden::Hooks::AfterFetch' }
|
136
|
+
hook.call
|
137
|
+
end
|
91
138
|
end
|
92
139
|
|
93
140
|
context 'user attribute changed which results in a new state digest' do
|
@@ -112,6 +159,148 @@ RSpec.describe SSO::Client::Warden::Hooks::AfterFetch, type: :request, db: true
|
|
112
159
|
hook.call
|
113
160
|
expect(client_passport.user['name']).to eq server_user.name
|
114
161
|
end
|
162
|
+
|
163
|
+
it 'succeeds' do
|
164
|
+
expect(operation).to be_success
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'has a useful error code' do
|
168
|
+
expect(operation.code).to eq :valid_and_modified
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'meters the invalid passport' do
|
172
|
+
expect(::SSO.config.metric).to receive(:call).with type: :histogram, key: 'sso.client.passport.verification.duration', value: 42_000, tags: nil, data: { caller: 'SSO::Client::PassportVerifier' }
|
173
|
+
expect(::SSO.config.metric).to receive(:call).with type: :increment, key: 'sso.client.warden.hooks.after_fetch.valid_and_modified', value: 1, tags: { scope: nil }, data: { passport_id: client_passport.id, caller: 'SSO::Client::Warden::Hooks::AfterFetch' }
|
174
|
+
hook.call
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'server request times out' do
|
179
|
+
before do
|
180
|
+
expect(::HTTParty).to receive(:get).and_raise ::Net::ReadTimeout
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'fails' do
|
184
|
+
expect(operation).to be_failure
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'has a useful error code' do
|
188
|
+
expect(operation.code).to eq :server_request_timed_out
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'meters the timeout' do
|
192
|
+
expect(::SSO.config.metric).to receive(:call).with type: :increment, key: 'sso.client.warden.hooks.after_fetch.timeout', value: 1, tags: { scope: nil }, data: { timeout_ms: '100ms', passport_id: client_passport.id, caller: 'SSO::Client::Warden::Hooks::AfterFetch' }
|
193
|
+
hook.call
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'server unreachable' do
|
198
|
+
before do
|
199
|
+
expect(::HTTParty).to receive(:get).and_return double(:response, code: 302)
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'fails' do
|
203
|
+
expect(operation).to be_failure
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'has a useful error code' do
|
207
|
+
expect(operation.code).to eq :server_unreachable
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'meters the timeout' do
|
211
|
+
expect(::SSO.config.metric).to receive(:call).with type: :histogram, key: 'sso.client.passport.verification.duration', value: 42_000, tags: nil, data: { caller: 'SSO::Client::PassportVerifier' }
|
212
|
+
expect(::SSO.config.metric).to receive(:call).with type: :increment, key: 'sso.client.warden.hooks.after_fetch.server_unreachable', value: 1, tags: { scope: nil }, data: { passport_id: client_passport.id, caller: 'SSO::Client::Warden::Hooks::AfterFetch' }
|
213
|
+
hook.call
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'server response not parseable' do
|
218
|
+
let(:response) { double :response, code: 200 }
|
219
|
+
|
220
|
+
before do
|
221
|
+
expect(::HTTParty).to receive(:get).and_return response
|
222
|
+
allow(response).to receive(:parsed_response).and_raise ::JSON::ParserError
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'fails' do
|
226
|
+
expect(operation).to be_failure
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'has a useful error code' do
|
230
|
+
expect(operation.code).to eq :server_response_not_parseable
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'meters the timeout' do
|
234
|
+
expect(::SSO.config.metric).to receive(:call).with type: :histogram, key: 'sso.client.passport.verification.duration', value: 42_000, tags: nil, data: { caller: 'SSO::Client::PassportVerifier' }
|
235
|
+
expect(::SSO.config.metric).to receive(:call).with type: :increment, key: 'sso.client.warden.hooks.after_fetch.server_response_not_parseable', value: 1, tags: { scope: nil }, data: { passport_id: client_passport.id, caller: 'SSO::Client::Warden::Hooks::AfterFetch' }
|
236
|
+
hook.call
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
context 'server response has no success flag at all' do
|
241
|
+
let(:response) { double :response, code: 200, parsed_response: { some: :thing } }
|
242
|
+
|
243
|
+
before do
|
244
|
+
expect(::HTTParty).to receive(:get).and_return response
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'fails' do
|
248
|
+
expect(operation).to be_failure
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'has a useful error code' do
|
252
|
+
expect(operation.code).to eq :server_response_missing_success_flag
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'meters the timeout' do
|
256
|
+
expect(::SSO.config.metric).to receive(:call).with type: :histogram, key: 'sso.client.passport.verification.duration', value: 42_000, tags: nil, data: { caller: 'SSO::Client::PassportVerifier' }
|
257
|
+
expect(::SSO.config.metric).to receive(:call).with type: :increment, key: 'sso.client.warden.hooks.after_fetch.server_response_missing_success_flag', value: 1, tags: { scope: nil }, data: { passport_id: client_passport.id, caller: 'SSO::Client::Warden::Hooks::AfterFetch' }
|
258
|
+
hook.call
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'server behaves weirdly' do
|
263
|
+
let(:response) { double :response, code: 200, parsed_response: { success: true } }
|
264
|
+
|
265
|
+
before do
|
266
|
+
expect(::HTTParty).to receive(:get).and_return response
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'fails' do
|
270
|
+
expect(operation).to be_failure
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'has a useful error code' do
|
274
|
+
expect(operation.code).to eq :unexpected_server_response_status
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'meters the timeout' do
|
278
|
+
expect(::SSO.config.metric).to receive(:call).with type: :histogram, key: 'sso.client.passport.verification.duration', value: 42_000, tags: nil, data: { caller: 'SSO::Client::PassportVerifier' }
|
279
|
+
expect(::SSO.config.metric).to receive(:call).with type: :increment, key: 'sso.client.warden.hooks.after_fetch.unexpected_server_response_status', value: 1, tags: { scope: nil }, data: { passport_id: client_passport.id, caller: 'SSO::Client::Warden::Hooks::AfterFetch' }
|
280
|
+
hook.call
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
context 'client-side exception' do
|
285
|
+
before do
|
286
|
+
expect(::HTTParty).to receive(:get).and_raise ArgumentError
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'fails' do
|
290
|
+
expect(operation).to be_failure
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'has a useful error code' do
|
294
|
+
expect(operation.code).to eq :client_exception_caught
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
describe '.activate' do
|
299
|
+
|
300
|
+
it 'proxies the options to warden' do
|
301
|
+
expect(::Warden::Manager).to receive(:after_fetch).with(scope: :insider).and_yield :passport, :warden, :options
|
302
|
+
described_class.activate scope: :insider
|
303
|
+
end
|
115
304
|
end
|
116
305
|
|
117
306
|
end
|
@@ -52,7 +52,7 @@ RSpec.describe SSO::Client::Warden::Strategies::Passport do
|
|
52
52
|
expect(rack_array.size).to eq 3
|
53
53
|
expect(rack_array[0]).to eq 200
|
54
54
|
expect(rack_array[1]).to eq 'Content-Type' => 'application/json'
|
55
|
-
expect(rack_array[2]).to eq ['{"success":
|
55
|
+
expect(rack_array[2]).to eq ['{"success":false,"code":"passport_verification_failed"}']
|
56
56
|
end
|
57
57
|
strategy.authenticate!
|
58
58
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -34,6 +34,11 @@ RSpec.configure do |config|
|
|
34
34
|
config.before :suite do
|
35
35
|
DatabaseCleaner.strategy = :transaction
|
36
36
|
DatabaseCleaner.clean_with :truncation
|
37
|
+
SSO.config.exception_handler = nil
|
38
|
+
SSO.config.passport_chip_key = nil
|
39
|
+
SSO.config.oauth_client_id = nil
|
40
|
+
SSO.config.oauth_client_secret = nil
|
41
|
+
SSO.config.metric = ::SSO::Test::Helpers.meter
|
37
42
|
end
|
38
43
|
|
39
44
|
config.before :each do
|
@@ -48,6 +53,10 @@ RSpec.configure do |config|
|
|
48
53
|
SSO.config.exception_handler = proc { |exception| fail exception }
|
49
54
|
end
|
50
55
|
|
56
|
+
config.before :each, stub_benchmarks: true do
|
57
|
+
stub_benchmarks
|
58
|
+
end
|
59
|
+
|
51
60
|
config.after :each do
|
52
61
|
Timecop.return
|
53
62
|
SSO.config.exception_handler = nil
|
@@ -4,6 +4,17 @@ module SSO
|
|
4
4
|
module Test
|
5
5
|
module Helpers
|
6
6
|
|
7
|
+
def self.meter
|
8
|
+
proc {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def stub_benchmarks
|
12
|
+
allow(Benchmark).to receive(:realtime) do |&block|
|
13
|
+
block.call
|
14
|
+
42
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
7
18
|
# Inspired by Warden::Spec::Helpers
|
8
19
|
def env_with_params(path = '/', params = {}, env = {})
|
9
20
|
method = params.delete(:method) || 'GET'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sso
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- halo
|
@@ -265,12 +265,13 @@ files:
|
|
265
265
|
- lib/sso/client/passport_verifier.rb
|
266
266
|
- lib/sso/client/warden/hooks/after_fetch.rb
|
267
267
|
- lib/sso/client/warden/strategies/passport.rb
|
268
|
+
- lib/sso/configuration.rb
|
269
|
+
- lib/sso/configure.rb
|
268
270
|
- lib/sso/logging.rb
|
271
|
+
- lib/sso/meter.rb
|
269
272
|
- lib/sso/server.rb
|
270
273
|
- lib/sso/server/README.md
|
271
274
|
- lib/sso/server/authentications/passport.rb
|
272
|
-
- lib/sso/server/configuration.rb
|
273
|
-
- lib/sso/server/configure.rb
|
274
275
|
- lib/sso/server/doorkeeper/access_token_marker.rb
|
275
276
|
- lib/sso/server/doorkeeper/grant_marker.rb
|
276
277
|
- lib/sso/server/doorkeeper/resource_owner_authenticator.rb
|
@@ -324,6 +325,7 @@ files:
|
|
324
325
|
- spec/dummy/db/schema.rb
|
325
326
|
- spec/integration/oauth/authorization_code_spec.rb
|
326
327
|
- spec/integration/oauth/password_spec.rb
|
328
|
+
- spec/lib/sso/benchmarking_spec.rb
|
327
329
|
- spec/lib/sso/client/authentications/passport_spec.rb
|
328
330
|
- spec/lib/sso/client/warden/hooks/after_fetch_spec.rb
|
329
331
|
- spec/lib/sso/client/warden/strategies/passport_spec.rb
|
@@ -403,6 +405,7 @@ test_files:
|
|
403
405
|
- spec/dummy/Rakefile
|
404
406
|
- spec/integration/oauth/authorization_code_spec.rb
|
405
407
|
- spec/integration/oauth/password_spec.rb
|
408
|
+
- spec/lib/sso/benchmarking_spec.rb
|
406
409
|
- spec/lib/sso/client/authentications/passport_spec.rb
|
407
410
|
- spec/lib/sso/client/warden/hooks/after_fetch_spec.rb
|
408
411
|
- spec/lib/sso/client/warden/strategies/passport_spec.rb
|