soar_authentication_token 3.0.5 → 3.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +116 -23
- data/lib/soar_authentication_token/keypair_generator.rb +0 -6
- data/lib/soar_authentication_token/rack_middleware.rb +17 -7
- data/lib/soar_authentication_token/token_generator.rb +14 -9
- data/lib/soar_authentication_token/token_validator.rb +2 -2
- data/lib/soar_authentication_token/version.rb +1 -1
- data/soar_authentication_token.gemspec +3 -1
- data/spec/rack_middleware_spec.rb +37 -14
- data/spec/spec_helper.rb +12 -0
- data/spec/token_validator_spec.rb +127 -71
- metadata +32 -5
- data/sanity/sanity_benchmark.rb +0 -83
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 275c14010dd496f6d91233ac79a73fd9755b628a
|
4
|
+
data.tar.gz: 190b1794277f7916ba2cb551946777c5bc98af66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a622e8ab76aae89f5578a0d30051351d2d481d56cbb1804ad2369d45886dbf54fe9c4abe36251225c2f99feffdb09b57d1e36b7451125c7ab5e70790ff474ec7
|
7
|
+
data.tar.gz: fe4d4a1a62f2705632d9e5a1839d5839ddd3552956616533e817761a4b17e1a89190332d1e4ff1dfdd0a777cc61f374a56cfa07b8f55f0e2fd1288da8ac6bc96
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# SoarAuthenticationToken
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/soar_authentication_token.png)](https://badge.fury.io/rb/soar_authentication_token)
|
4
4
|
|
@@ -13,25 +13,28 @@ gem 'soar_authentication_token'
|
|
13
13
|
```
|
14
14
|
|
15
15
|
And then execute:
|
16
|
-
|
17
|
-
|
16
|
+
```bash
|
17
|
+
bundle
|
18
|
+
```
|
18
19
|
|
19
20
|
Or install it yourself as:
|
21
|
+
```bash
|
22
|
+
gem install soar_authentication_token
|
23
|
+
```
|
20
24
|
|
21
|
-
|
25
|
+
## Configuration of generators and validators
|
22
26
|
|
23
|
-
|
27
|
+
There are three modes of operation: local, remote or static
|
24
28
|
|
25
|
-
There are three modes of operation.
|
26
29
|
### Local
|
27
|
-
In local mode the tokens are decoded, verified and meta extracted locally using configured key material.
|
30
|
+
In local mode the tokens are decoded, verified and meta extracted locally using configured key material. In practice the the token generation and validation services run in this mode since their roles are to generate and validate tokens.
|
28
31
|
|
29
32
|
### Remote
|
30
|
-
In remote mode the tokens are passed to a validation service for
|
33
|
+
In remote mode the tokens are passed to a token validation service for validation. This allows for a centralized management of tokens. The key material are therefore managed on the validation service and . In this mode you only have to provide the url of the validation service.
|
31
34
|
|
32
35
|
### Static
|
33
|
-
In this mode the validator
|
34
|
-
* Between the various authentication token services that requires authentication between themselves. These services do not
|
36
|
+
In this mode the validator is configured with a list of preconfigured static tokens. Incoming tokens are simply checked against this list. No extraction of meta is performed on the tokens but retrieved from the configuration. Rotation of tokens is simple since many tokens can be configured. This mode is to be used in only two scenarios:
|
37
|
+
* Between the various authentication token services that requires authentication between themselves. These services themselves do not the benefit of another centralized authentication service to rely on.
|
35
38
|
* In test scenarios where you do not want to pull in the authentication services to perform testing of your services.
|
36
39
|
|
37
40
|
|
@@ -39,33 +42,123 @@ In this mode the validator are configured with a list of preconfigured static to
|
|
39
42
|
|
40
43
|
Run the rspec test tests using docker compose:
|
41
44
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
$ docker-compose down
|
48
|
-
|
49
|
-
Locally run a subset:
|
50
|
-
|
51
|
-
$ bundle exec rspec -cfd spec/rack_middleware_spec.rb
|
52
|
-
|
45
|
+
```bash
|
46
|
+
docker-compose build
|
47
|
+
docker-compose run --rm soar-authentication-token
|
48
|
+
docker-compose down
|
49
|
+
```
|
53
50
|
|
54
51
|
## Updating
|
55
52
|
|
56
|
-
In order to pull the latest from the referenced projects, simply the following
|
53
|
+
For test purposes this repo relies on various other repos with services. This is to test the interaction between the generator and validation clients and these services. In order to pull the latest from the referenced projects and build fresh docker images, simply the following commands:
|
57
54
|
|
58
55
|
```bash
|
59
56
|
git pull && git submodule foreach 'git fetch origin --tags; git checkout master; git pull'
|
57
|
+
docker-compose down
|
60
58
|
docker-compose build
|
61
59
|
```
|
62
60
|
|
63
61
|
## Usage
|
64
62
|
|
63
|
+
### KeypairGenerator
|
64
|
+
|
65
|
+
In support of generating JWT tokens this class is responsible for generating an EC keypairs in PEM format. Use this when rotating keypairs in production or for testing the generators/validators.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
generator = SoarAuthenticationToken::KeypairGenerator.new
|
69
|
+
private_key, public_key = generator.generate
|
70
|
+
```
|
71
|
+
|
72
|
+
### RackMiddleware
|
73
|
+
|
74
|
+
The rack middleware allows you to perform validation of all requests before it even hits your resource. The middleware also populates the 'user' key in the rack session with the authenticated identifier and the 'auth_token_meta' key with meta regarding the authentication that might be useful to more sensitive services.
|
75
|
+
|
76
|
+
First step is to configure the middleware. Actually you are not configuring the middleware but the TokenValidator instance that will be used in the validation. Most of the time you will be reaching out to a remote validation service and configure it as such:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
@iut_configuration = {
|
80
|
+
'mode' => 'remote',
|
81
|
+
'validator-url' => 'http://authentication-token-validator-service:9393/validate'
|
82
|
+
}
|
83
|
+
@iut = SoarAuthenticationToken::RackMiddleware.new(@test_app, @iut_configuration)
|
84
|
+
```
|
85
|
+
|
86
|
+
The middleware will look for the authentication token in the HTTP header 'AUTHORIZATION'.
|
87
|
+
|
88
|
+
Please refer to the rack middleware spec for detailed examples on how to use the middleware.
|
65
89
|
|
90
|
+
### TokenGenerator
|
66
91
|
|
67
|
-
|
92
|
+
The class generates tokens or requests a token from a generator service as configured.
|
68
93
|
|
94
|
+
For remote token generation, configure as such:
|
95
|
+
```ruby
|
96
|
+
@configuration_remote = {
|
97
|
+
'mode' => 'remote',
|
98
|
+
'generator-url' => 'http://authentication-token-generator-service:9393/generate',
|
99
|
+
'generator-client-auth-token' => 'xxxx'
|
100
|
+
}
|
101
|
+
```
|
102
|
+
|
103
|
+
For local token generation, configure as such:
|
104
|
+
```ruby
|
105
|
+
generator = SoarAuthenticationToken::KeypairGenerator.new
|
106
|
+
private_key, public_key = generator.generate
|
107
|
+
@configuration_local = {
|
108
|
+
'mode' => 'local',
|
109
|
+
'private_key' => private_key
|
110
|
+
}
|
111
|
+
```
|
112
|
+
|
113
|
+
Create storage client. The example here uses a local stub client that uses a in-memory store.
|
114
|
+
```ruby
|
115
|
+
gem 'auth_token_store_provider', "~> 1.0.1"
|
116
|
+
require 'auth_token_store_provider'
|
117
|
+
store = AuthTokenStoreProvider::StubClient.new
|
118
|
+
```
|
119
|
+
|
120
|
+
After configuration you simply generate a token like this:
|
121
|
+
```ruby
|
122
|
+
generator = SoarAuthenticationToken::TokenGenerator.new(configuration)
|
123
|
+
generator.inject_store_provider(store)
|
124
|
+
token, token_generator_meta = generator.generate(authenticated_identifier: 'someone@hetzner.co.za', flow_identifier: 'test-flow-id')
|
125
|
+
```
|
126
|
+
|
127
|
+
### TokenValidator
|
128
|
+
|
129
|
+
The class validates tokens or requests validation of a token from a validation service as configured.
|
130
|
+
|
131
|
+
For remote token validation, configure as such:
|
132
|
+
```ruby
|
133
|
+
@configuration_remote = {
|
134
|
+
'mode' => 'remote',
|
135
|
+
'validator-url' => 'http://authentication-token-validator-service:9393/validate',
|
136
|
+
}
|
137
|
+
```
|
138
|
+
|
139
|
+
For local token validation, configure as such:
|
140
|
+
```ruby
|
141
|
+
generator = SoarAuthenticationToken::KeypairGenerator.new
|
142
|
+
private_key, public_key = generator.generate
|
143
|
+
@configuration_local = {
|
144
|
+
'mode' => 'local',
|
145
|
+
'public_key' => public_key
|
146
|
+
}
|
147
|
+
```
|
148
|
+
|
149
|
+
Create storage client. The example here uses a local stub client that uses a in-memory store.
|
150
|
+
```ruby
|
151
|
+
gem 'auth_token_store_provider', "~> 1.0.1"
|
152
|
+
require 'auth_token_store_provider'
|
153
|
+
store = AuthTokenStoreProvider::StubClient.new
|
154
|
+
```
|
155
|
+
|
156
|
+
After configuration you simply valiate a token like this:
|
157
|
+
```ruby
|
158
|
+
validator = SoarAuthenticationToken::TokenValidator.new(configuration)
|
159
|
+
validator.inject_store_provider(store)
|
160
|
+
token_validity, token_validator_meta, messages = validator.validate(authentication_token: token, flow_identifier: 'test-flow-id')
|
161
|
+
```
|
69
162
|
|
70
163
|
|
71
164
|
## Contributing
|
@@ -2,9 +2,6 @@ require 'openssl'
|
|
2
2
|
|
3
3
|
module SoarAuthenticationToken
|
4
4
|
class KeypairGenerator
|
5
|
-
def initialize
|
6
|
-
end
|
7
|
-
|
8
5
|
def generate
|
9
6
|
private_key = OpenSSL::PKey::EC.new 'secp521r1'
|
10
7
|
private_key.generate_key
|
@@ -12,8 +9,5 @@ module SoarAuthenticationToken
|
|
12
9
|
public_key.private_key = nil
|
13
10
|
[private_key.to_pem, public_key.to_pem]
|
14
11
|
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
12
|
end
|
19
13
|
end
|
@@ -9,21 +9,27 @@ module SoarAuthenticationToken
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def call(env)
|
12
|
-
|
13
|
-
|
14
|
-
token_valid, token_meta, message = validate_and_resolve_token(request.env['HTTP_AUTHORIZATION'],params['flow_identifier'])
|
12
|
+
session, params, token, flow_id = get_request_information(env)
|
13
|
+
token_valid, token_meta, message = validate_and_resolve_token(token,flow_id)
|
15
14
|
if token_valid
|
16
15
|
session['user'] = token_meta['authenticated_identifier']
|
17
16
|
session['auth_token_meta'] = token_meta
|
18
|
-
@app.call env
|
19
|
-
else
|
20
|
-
audit_token_rejection("Token rejected due to #{message}",params['flow_identifier'])
|
21
|
-
[401, {"Content-Type" => "text/html"}, ["401 - Not authenticated"]]
|
17
|
+
return @app.call env
|
22
18
|
end
|
19
|
+
audit_token_rejection("Token rejected due to #{message}",flow_id)
|
20
|
+
rejection
|
23
21
|
end
|
24
22
|
|
25
23
|
private
|
26
24
|
|
25
|
+
def get_request_information(env)
|
26
|
+
request = Rack::Request.new env
|
27
|
+
[ request.session,
|
28
|
+
request.params,
|
29
|
+
request.env['HTTP_AUTHORIZATION'],
|
30
|
+
request.params['flow_identifier'] ]
|
31
|
+
end
|
32
|
+
|
27
33
|
def validate_and_resolve_token(authentication_token,flow_identifier)
|
28
34
|
token_validator = SoarAuthenticationToken::TokenValidator.new(@configuration)
|
29
35
|
token_validator.validate(authentication_token: authentication_token,flow_identifier: flow_identifier)
|
@@ -32,5 +38,9 @@ module SoarAuthenticationToken
|
|
32
38
|
def audit_token_rejection(message, flow_id)
|
33
39
|
@auditing.warn(message,flow_id) if @auditing
|
34
40
|
end
|
41
|
+
|
42
|
+
def rejection
|
43
|
+
[401, {"Content-Type" => "text/html"}, ["401 - Not authenticated"]]
|
44
|
+
end
|
35
45
|
end
|
36
46
|
end
|
@@ -31,7 +31,7 @@ module SoarAuthenticationToken
|
|
31
31
|
private
|
32
32
|
|
33
33
|
def generate_locally(authenticated_identifier)
|
34
|
-
token_meta =
|
34
|
+
token_meta = generate_meta(authenticated_identifier)
|
35
35
|
token = encode(token_meta)
|
36
36
|
add_token_to_store(token_meta)
|
37
37
|
[token, token_meta]
|
@@ -60,7 +60,7 @@ module SoarAuthenticationToken
|
|
60
60
|
body['data']['token']
|
61
61
|
end
|
62
62
|
|
63
|
-
def
|
63
|
+
def generate_meta(authenticated_identifier)
|
64
64
|
current_time = Time.now
|
65
65
|
{ 'authenticated_identifier' => authenticated_identifier,
|
66
66
|
'token_issue_time' => current_time.utc.iso8601(3),
|
@@ -76,13 +76,18 @@ module SoarAuthenticationToken
|
|
76
76
|
def validate_configuration
|
77
77
|
raise "'mode' must be configured" unless @configuration['mode']
|
78
78
|
raise "'mode' must be configured as either 'local' or 'remote'" unless ['local','remote'].include?(@configuration['mode'])
|
79
|
-
if '
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
79
|
+
validate_local_mode_configuration if 'local' == @configuration['mode']
|
80
|
+
validate_remote_mode_configuration if 'remote' == @configuration['mode']
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate_remote_mode_configuration
|
84
|
+
raise "'generator-url' must be configured in remote mode" if @configuration['generator-url'].nil?
|
85
|
+
end
|
86
|
+
|
87
|
+
def validate_local_mode_configuration
|
88
|
+
raise "'private_key' must be configured in local mode" unless @configuration['private_key']
|
89
|
+
raise "'expiry' must be configured in local mode" unless @configuration['expiry']
|
90
|
+
raise "'expiry' must be an integer" unless Integer(@configuration['expiry'])
|
86
91
|
end
|
87
92
|
|
88
93
|
def merge_with_default_configuration(configuration)
|
@@ -22,7 +22,8 @@ module SoarAuthenticationToken
|
|
22
22
|
def validate(authentication_token:,flow_identifier: nil)
|
23
23
|
return validate_locally(authentication_token) if 'local' == @configuration['mode']
|
24
24
|
return validate_statically(authentication_token) if 'static' == @configuration['mode']
|
25
|
-
return validate_remotely(authentication_token,flow_identifier)
|
25
|
+
return validate_remotely(authentication_token,flow_identifier) if 'remote' == @configuration['mode']
|
26
|
+
raise 'invalid validation mode configured'
|
26
27
|
end
|
27
28
|
|
28
29
|
private
|
@@ -36,7 +37,6 @@ module SoarAuthenticationToken
|
|
36
37
|
token_expiry_time: found_static_token['token_expiry_time'])
|
37
38
|
return rejection_result(reason: "Expired token <#{meta['token_expiry_time']}> for <#{meta['authenticated_identifier']}>") if token_expired?(meta)
|
38
39
|
return success_result(token_meta: meta)
|
39
|
-
|
40
40
|
end
|
41
41
|
|
42
42
|
def find_configured_static_token(authentication_token)
|
@@ -24,10 +24,12 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_dependency "rack", '~> 1.6', '>= 1.6.4'
|
25
25
|
spec.add_dependency 'authenticated_client', '~> 0.0.2'
|
26
26
|
|
27
|
-
spec.add_development_dependency 'auth_token_store_provider', "~> 1.0
|
27
|
+
spec.add_development_dependency 'auth_token_store_provider', "~> 1.0"
|
28
28
|
spec.add_development_dependency 'pry', '~> 0'
|
29
29
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
30
30
|
spec.add_development_dependency 'rake', '~> 10.0'
|
31
31
|
spec.add_development_dependency 'rspec', '~> 2.13'
|
32
32
|
spec.add_development_dependency "capybara", '~> 2.1', '>= 2.1.0'
|
33
|
+
spec.add_development_dependency "simplecov", '~> 0'
|
34
|
+
spec.add_development_dependency "simplecov-rcov", '~> 0'
|
33
35
|
end
|
@@ -44,7 +44,12 @@ describe SoarAuthenticationToken::RackMiddleware do
|
|
44
44
|
@test_app = lambda do |env|
|
45
45
|
request = Rack::Request.new env
|
46
46
|
session = request.session
|
47
|
-
|
47
|
+
test_app_response_data = {
|
48
|
+
'message' => "tested with authenticated user #{session['user']}",
|
49
|
+
'user' => session['user'],
|
50
|
+
'auth_token_meta' => session['auth_token_meta']
|
51
|
+
}
|
52
|
+
[200, {"Content-Type"=>"text/html"}, test_app_response_data ]
|
48
53
|
end
|
49
54
|
@iut_configuration = {
|
50
55
|
'mode' => 'remote',
|
@@ -68,23 +73,41 @@ describe SoarAuthenticationToken::RackMiddleware do
|
|
68
73
|
end
|
69
74
|
end
|
70
75
|
|
71
|
-
context "when called with
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
+
context "when called with a request environment" do
|
77
|
+
context 'with no authentication token' do
|
78
|
+
it "return with 401" do
|
79
|
+
opts = { }
|
80
|
+
code, env, body = @iut.call Rack::MockRequest.env_for('http://service', opts)
|
81
|
+
expect([code, env, body]).to eq([401, {"Content-Type" => "text/html"}, ["401 - Not authenticated"]])
|
82
|
+
end
|
76
83
|
end
|
77
84
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
85
|
+
context 'with an invalid authentication token' do
|
86
|
+
it "return with 401" do
|
87
|
+
opts = { 'HTTP_AUTHORIZATION' => @local_invalid_generator.generate(authenticated_identifier: 'a@b.com') }
|
88
|
+
code, env, body = @iut.call Rack::MockRequest.env_for('http://service', opts)
|
89
|
+
expect([code, env, body]).to eq([401, {"Content-Type" => "text/html"}, ["401 - Not authenticated"]])
|
90
|
+
end
|
82
91
|
end
|
83
92
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
93
|
+
context 'with a valid authentiation token' do
|
94
|
+
it "pass requests to the application" do
|
95
|
+
opts = { 'HTTP_AUTHORIZATION' => @local_valid_generator.generate(authenticated_identifier: 'a@b.com') }
|
96
|
+
code, env, body = @iut.call Rack::MockRequest.env_for('http://service', opts)
|
97
|
+
expect([code, env, body['message']]).to eq([200, {"Content-Type"=>"text/html"}, "tested with authenticated user a@b.com" ])
|
98
|
+
end
|
99
|
+
|
100
|
+
it "populate the 'user' key in the rack session with the authenticated user" do
|
101
|
+
opts = { 'HTTP_AUTHORIZATION' => @local_valid_generator.generate(authenticated_identifier: 'a@b.com') }
|
102
|
+
code, env, body = @iut.call Rack::MockRequest.env_for('http://service', opts)
|
103
|
+
expect(body['user']).to eq('a@b.com')
|
104
|
+
end
|
105
|
+
|
106
|
+
it "populate the 'auth_token_meta' key in the rack session with the hash containing the token meta" do
|
107
|
+
opts = { 'HTTP_AUTHORIZATION' => @local_valid_generator.generate(authenticated_identifier: 'a@b.com') }
|
108
|
+
code, env, body = @iut.call Rack::MockRequest.env_for('http://service', opts)
|
109
|
+
expect(body['auth_token_meta']['authenticated_identifier']).to eq('a@b.com')
|
110
|
+
end
|
88
111
|
end
|
89
112
|
end
|
90
113
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,18 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'simplecov-rcov'
|
3
|
+
|
4
|
+
SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
|
5
|
+
SimpleCov.start do
|
6
|
+
add_filter "/spec/"
|
7
|
+
end
|
8
|
+
|
1
9
|
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
10
|
$LOAD_PATH.unshift File.expand_path('../../spec/support', __FILE__)
|
3
11
|
|
4
12
|
require 'soar_authentication_token'
|
5
13
|
require 'pry'
|
6
14
|
require 'auth_token_store_provider'
|
15
|
+
|
16
|
+
def one_year_in_seconds
|
17
|
+
31536000
|
18
|
+
end
|
@@ -83,51 +83,144 @@ describe SoarAuthenticationToken::TokenValidator do
|
|
83
83
|
expect(SoarAuthenticationToken::VERSION).not_to be nil
|
84
84
|
end
|
85
85
|
|
86
|
+
describe "#validate" do
|
87
|
+
context "given that the validator is configured for local validation" do
|
88
|
+
context 'given valid token' do
|
89
|
+
let!(:token_validation_result) {
|
90
|
+
token, token_generator_meta = @local_valid_generator.generate(authenticated_identifier: @test_identifier)
|
91
|
+
@iut_local.validate(authentication_token: token)
|
92
|
+
}
|
93
|
+
let!(:token_validity) { token_validation_result[0] }
|
94
|
+
let!(:token_meta) { token_validation_result[1] }
|
95
|
+
let!(:message) { token_validation_result[2] }
|
86
96
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
97
|
+
it 'indicate token is valid' do
|
98
|
+
expect(token_validity).to eq true
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'provide the token meta' do
|
102
|
+
expect(token_meta['authenticated_identifier']).to eq @test_identifier
|
103
|
+
end
|
93
104
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
105
|
+
it 'provide a message indicating that the token is valid' do
|
106
|
+
expect(message).to match /Valid token for/
|
107
|
+
end
|
108
|
+
end
|
99
109
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
110
|
+
context 'given expired token' do
|
111
|
+
let!(:token_validation_result) {
|
112
|
+
token, token_generator_meta = @local_valid_generator.generate(authenticated_identifier: @test_identifier)
|
113
|
+
allow(Time).to receive(:now).and_return(Time.now+one_year_in_seconds)
|
114
|
+
@iut_local.validate(authentication_token: token)
|
115
|
+
}
|
116
|
+
let!(:token_validity) { token_validation_result[0] }
|
117
|
+
let!(:token_meta) { token_validation_result[1] }
|
118
|
+
let!(:message) { token_validation_result[2] }
|
105
119
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
expect(token_meta).to eq nil
|
110
|
-
end
|
120
|
+
it 'indicate token is invalid' do
|
121
|
+
expect(token_validity).to eq false
|
122
|
+
end
|
111
123
|
|
112
|
-
|
113
|
-
|
124
|
+
it 'does not provide the token meta' do
|
125
|
+
expect(token_meta).to eq nil
|
126
|
+
end
|
114
127
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
128
|
+
it 'provide a message indicating that the token is invalid' do
|
129
|
+
expect(message).to match /Expired token/
|
130
|
+
end
|
131
|
+
end
|
119
132
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
133
|
+
context 'given unknown token (not in store)' do
|
134
|
+
let!(:token_validation_result) {
|
135
|
+
token, token_generator_meta = @local_valid_generator.generate(authenticated_identifier: @test_identifier)
|
136
|
+
@test_store.instance_variable_set("@store", []) #clear store
|
137
|
+
@iut_local.validate(authentication_token: token)
|
138
|
+
}
|
139
|
+
let!(:token_validity) { token_validation_result[0] }
|
140
|
+
let!(:token_meta) { token_validation_result[1] }
|
141
|
+
let!(:message) { token_validation_result[2] }
|
142
|
+
|
143
|
+
it 'indicate that token is invalid' do
|
144
|
+
expect(token_validity).to eq false
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'does not provide the token meta' do
|
148
|
+
expect(token_meta).to eq nil
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'provide a message indicating that the token is invalid' do
|
152
|
+
expect(message).to match /Unknown token/
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'given invalid token (garbage or different key)' do
|
157
|
+
let!(:token_validation_result) {
|
158
|
+
token, token_generator_meta = @remote_generator.generate(authenticated_identifier: @test_identifier)
|
159
|
+
@iut_local.validate(authentication_token: token)
|
160
|
+
}
|
161
|
+
let!(:token_validity) { token_validation_result[0] }
|
162
|
+
let!(:token_meta) { token_validation_result[1] }
|
163
|
+
let!(:message) { token_validation_result[2] }
|
164
|
+
|
165
|
+
it 'indicate token is invalid' do
|
166
|
+
expect(token_validity).to eq false
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'does not provide the token meta' do
|
170
|
+
expect(token_meta).to eq nil
|
171
|
+
end
|
124
172
|
|
125
|
-
|
173
|
+
it 'provide a message indicating that the token is invalid' do
|
174
|
+
expect(message).to match /Token decode\/verification failure/
|
175
|
+
end
|
176
|
+
end
|
126
177
|
end
|
127
|
-
end
|
128
178
|
|
129
179
|
|
130
|
-
|
180
|
+
|
181
|
+
context "given that the validator is configured for remote validation" do
|
182
|
+
context 'given valid token' do
|
183
|
+
let!(:token_validation_result) {
|
184
|
+
token, token_generator_meta = @remote_generator.generate(authenticated_identifier: @test_identifier)
|
185
|
+
@iut_remote.validate(authentication_token: token)
|
186
|
+
}
|
187
|
+
let!(:token_validity) { token_validation_result[0] }
|
188
|
+
let!(:token_meta) { token_validation_result[1] }
|
189
|
+
let!(:message) { token_validation_result[2] }
|
190
|
+
|
191
|
+
it 'should indicate valid if the token is valid' do
|
192
|
+
expect(token_validity).to eq true
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'should provide the authenticated_identifier if the token is valid' do
|
196
|
+
expect(token_meta['authenticated_identifier']).to eq @test_identifier
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context 'given invalid (generalized) token' do
|
201
|
+
let!(:token_validation_result) {
|
202
|
+
token, token_generator_meta = @local_invalid_generator.generate(authenticated_identifier: @test_identifier)
|
203
|
+
@iut_remote.validate(authentication_token: token)
|
204
|
+
}
|
205
|
+
let!(:token_validity) { token_validation_result[0] }
|
206
|
+
let!(:token_meta) { token_validation_result[1] }
|
207
|
+
let!(:message) { token_validation_result[2] }
|
208
|
+
|
209
|
+
it 'indicate token is invalid' do
|
210
|
+
expect(token_validity).to eq false
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'does not provide the token meta' do
|
214
|
+
expect(token_meta).to eq nil
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'provides a message indicating the token is invalid' do
|
218
|
+
expect(message).to match /Token decode\/verification failure/
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
end
|
131
224
|
context "given that the validator is configured for static tokens" do
|
132
225
|
let(:iut) { subject.new(@static_validator_configuration) }
|
133
226
|
context "given a token that is in the list of static tokens and not expired" do
|
@@ -188,41 +281,4 @@ describe SoarAuthenticationToken::TokenValidator do
|
|
188
281
|
end
|
189
282
|
end
|
190
283
|
end
|
191
|
-
|
192
|
-
|
193
|
-
context "when validating a token remotely using the configured url" do
|
194
|
-
it 'should indicate valid if the token is valid' do
|
195
|
-
token, token_generator_meta = @remote_generator.generate(authenticated_identifier: @test_identifier)
|
196
|
-
token_validity, token_meta, message = @iut_remote.validate(authentication_token: token)
|
197
|
-
expect(token_validity).to eq true
|
198
|
-
end
|
199
|
-
|
200
|
-
it 'should indicate invalid if the token is invalid' do
|
201
|
-
token, token_generator_meta = @local_invalid_generator.generate(authenticated_identifier: @test_identifier)
|
202
|
-
token_validity, token_meta, message = @iut_remote.validate(authentication_token: token)
|
203
|
-
expect(token_validity).to eq false
|
204
|
-
end
|
205
|
-
|
206
|
-
it 'should provide the authenticated_identifier if the token is valid' do
|
207
|
-
token, token_generator_meta = @remote_generator.generate(authenticated_identifier: @test_identifier)
|
208
|
-
token_validity, token_meta, message = @iut_remote.validate(authentication_token: token)
|
209
|
-
expect(token_meta['authenticated_identifier']).to eq @test_identifier
|
210
|
-
end
|
211
|
-
|
212
|
-
it 'should not provide the authenticated_identifier if the token is invalid' do
|
213
|
-
token, token_generator_meta = @local_invalid_generator.generate(authenticated_identifier: @test_identifier)
|
214
|
-
token_validity, token_meta, message = @iut_remote.validate(authentication_token: token)
|
215
|
-
expect(token_meta).to eq nil
|
216
|
-
end
|
217
|
-
|
218
|
-
it 'should indicate as invalid tokens that are older than the configured expiry time' do
|
219
|
-
#TODO
|
220
|
-
#expect(true).to eq false
|
221
|
-
end
|
222
|
-
|
223
|
-
it 'should indicate as valid tokens that are not older than the configured expiry time' do
|
224
|
-
#TODO
|
225
|
-
#expect(true).to eq false
|
226
|
-
end
|
227
|
-
end
|
228
284
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: soar_authentication_token
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Barney de Villiers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: soar_xt
|
@@ -84,14 +84,14 @@ dependencies:
|
|
84
84
|
requirements:
|
85
85
|
- - "~>"
|
86
86
|
- !ruby/object:Gem::Version
|
87
|
-
version: 1.0
|
87
|
+
version: '1.0'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
90
|
version_requirements: !ruby/object:Gem::Requirement
|
91
91
|
requirements:
|
92
92
|
- - "~>"
|
93
93
|
- !ruby/object:Gem::Version
|
94
|
-
version: 1.0
|
94
|
+
version: '1.0'
|
95
95
|
- !ruby/object:Gem::Dependency
|
96
96
|
name: pry
|
97
97
|
requirement: !ruby/object:Gem::Requirement
|
@@ -168,6 +168,34 @@ dependencies:
|
|
168
168
|
- - ">="
|
169
169
|
- !ruby/object:Gem::Version
|
170
170
|
version: 2.1.0
|
171
|
+
- !ruby/object:Gem::Dependency
|
172
|
+
name: simplecov
|
173
|
+
requirement: !ruby/object:Gem::Requirement
|
174
|
+
requirements:
|
175
|
+
- - "~>"
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
version: '0'
|
178
|
+
type: :development
|
179
|
+
prerelease: false
|
180
|
+
version_requirements: !ruby/object:Gem::Requirement
|
181
|
+
requirements:
|
182
|
+
- - "~>"
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
- !ruby/object:Gem::Dependency
|
186
|
+
name: simplecov-rcov
|
187
|
+
requirement: !ruby/object:Gem::Requirement
|
188
|
+
requirements:
|
189
|
+
- - "~>"
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
192
|
+
type: :development
|
193
|
+
prerelease: false
|
194
|
+
version_requirements: !ruby/object:Gem::Requirement
|
195
|
+
requirements:
|
196
|
+
- - "~>"
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: '0'
|
171
199
|
description: Interface to the authentication token service
|
172
200
|
email:
|
173
201
|
- barney.de.villiers@hetzner.co.za
|
@@ -204,7 +232,6 @@ files:
|
|
204
232
|
- sanity/.ruby-version
|
205
233
|
- sanity/Gemfile
|
206
234
|
- sanity/sanity.rb
|
207
|
-
- sanity/sanity_benchmark.rb
|
208
235
|
- soar_authentication_token.gemspec
|
209
236
|
- spec/keypair_generator_spec.rb
|
210
237
|
- spec/rack_middleware_spec.rb
|
data/sanity/sanity_benchmark.rb
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
require 'soar_auditing_provider'
|
2
|
-
require 'log4r_auditor'
|
3
|
-
require 'soar_flow'
|
4
|
-
require 'benchmark'
|
5
|
-
require 'byebug'
|
6
|
-
|
7
|
-
class Main
|
8
|
-
|
9
|
-
AUDITING_CONFIGURATION = {
|
10
|
-
'auditing' => {
|
11
|
-
'level' => 'debug',
|
12
|
-
'install_exit_handler' => 'false',
|
13
|
-
'add_caller_source_location' => 'false',
|
14
|
-
'queue_worker' => {
|
15
|
-
'queue_size' => 1000000,
|
16
|
-
'initial_back_off_in_seconds' => 1,
|
17
|
-
'back_off_multiplier' => 2,
|
18
|
-
'back_off_attempts' => 5
|
19
|
-
},
|
20
|
-
'default_nfrs' => {
|
21
|
-
'accessibility' => 'local',
|
22
|
-
'privacy' => 'not encrypted',
|
23
|
-
'reliability' => 'instance',
|
24
|
-
'performance' => 'high'
|
25
|
-
},
|
26
|
-
'auditors' => {
|
27
|
-
'log4r' => {
|
28
|
-
'adaptor' => 'Log4rAuditor::Log4rAuditor',
|
29
|
-
'file_name' => 'soar_sc.log',
|
30
|
-
'standard_stream' => 'none',
|
31
|
-
'nfrs' => {
|
32
|
-
'accessibility' => 'local',
|
33
|
-
'privacy' => 'not encrypted',
|
34
|
-
'reliability' => 'instance',
|
35
|
-
'performance' => 'high'
|
36
|
-
}
|
37
|
-
}
|
38
|
-
}
|
39
|
-
}
|
40
|
-
}
|
41
|
-
|
42
|
-
def test_sanity
|
43
|
-
iterations = 1000000
|
44
|
-
|
45
|
-
#create and configure auditing instance
|
46
|
-
myauditing = SoarAuditingProvider::AuditingProvider.new( AUDITING_CONFIGURATION['auditing'] )
|
47
|
-
myauditing.startup_flow_id = SoarFlow::ID::generate_flow_id
|
48
|
-
myauditing.service_identifier = 'my-test-service.com'
|
49
|
-
|
50
|
-
#associate a set of auditing entries with a flow by generating a flow identifiers
|
51
|
-
flow_id = SoarFlow::ID::generate_flow_id
|
52
|
-
|
53
|
-
Benchmark.bm do |x|
|
54
|
-
myauditing = SoarAuditingProvider::AuditingProvider.new( AUDITING_CONFIGURATION['auditing'].dup.merge("level" => "warn") )
|
55
|
-
myauditing.startup_flow_id = SoarFlow::ID::generate_flow_id
|
56
|
-
myauditing.service_identifier = 'my-test-service.com'
|
57
|
-
x.report ("audit_call_below_audit_threshold:") {
|
58
|
-
iterations.times {
|
59
|
-
myauditing.info("Benchmarking test",flow_id)
|
60
|
-
}
|
61
|
-
}
|
62
|
-
myauditing = SoarAuditingProvider::AuditingProvider.new( AUDITING_CONFIGURATION['auditing'].dup.merge("add_caller_source_location" => "false") )
|
63
|
-
myauditing.startup_flow_id = SoarFlow::ID::generate_flow_id
|
64
|
-
myauditing.service_identifier = 'my-test-service.com'
|
65
|
-
x.report ("audit_call_without_caller_info :") {
|
66
|
-
iterations.times {
|
67
|
-
myauditing.info("Benchmarking test",flow_id)
|
68
|
-
}
|
69
|
-
}
|
70
|
-
myauditing = SoarAuditingProvider::AuditingProvider.new( AUDITING_CONFIGURATION['auditing'].dup.merge("add_caller_source_location" => "true") )
|
71
|
-
myauditing.startup_flow_id = SoarFlow::ID::generate_flow_id
|
72
|
-
myauditing.service_identifier = 'my-test-service.com'
|
73
|
-
x.report ("audit_call_with_caller_info :") {
|
74
|
-
iterations.times {
|
75
|
-
myauditing.info("Benchmarking test",flow_id)
|
76
|
-
}
|
77
|
-
}
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
main = Main.new
|
83
|
-
main.test_sanity
|