soar_authentication_token 4.0.1 → 5.0.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.
@@ -0,0 +1,291 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+
4
+ describe SoarAuthenticationToken::JwtTokenValidator do
5
+ subject { SoarAuthenticationToken::JwtTokenValidator }
6
+ before :all do
7
+ @test_store = AuthTokenStoreProvider::StubClient.new
8
+ keypair_generator = SoarAuthenticationToken::KeypairGenerator.new
9
+ @valid_private_key, @valid_public_key = keypair_generator.generate
10
+ @invalid_private_key, @invalid_public_key = keypair_generator.generate
11
+ @test_identifier = 'a@b.co.za'
12
+ @local_valid_generator_configuration = {
13
+ 'provider' => 'SoarAuthenticationToken::JwtTokenGenerator',
14
+ 'private_key' => @valid_private_key
15
+ }
16
+ @local_invalid_generator_configuration = {
17
+ 'provider' => 'SoarAuthenticationToken::JwtTokenGenerator',
18
+ 'private_key' => @invalid_private_key
19
+ }
20
+
21
+ @local_validator_configuration_single_key = {
22
+ 'provider' => 'SoarAuthenticationToken::JwtTokenValidator',
23
+ 'keys' => {
24
+ 'keyA' => {
25
+ 'public_key' => @valid_public_key
26
+ }
27
+ }
28
+ }
29
+
30
+ @private_key_order_1, @public_key_order_1 = keypair_generator.generate
31
+ @private_key_order_2, @public_key_order_2 = keypair_generator.generate
32
+ @private_key_order_3, @public_key_order_3 = keypair_generator.generate
33
+ @multiple_key_configuration = {
34
+ 'provider' => 'SoarAuthenticationToken::JwtTokenValidator',
35
+ 'keys' => {
36
+
37
+ 'KEYPAIR_20160107T230001' => { #Specifically add this key name out of alphabetical order
38
+ 'public_key' => @public_key_order_3
39
+ },
40
+ 'KEYPAIR_20160108T200001' => {
41
+ 'public_key' => @public_key_order_1
42
+ },
43
+ 'KEYPAIR_20160108T190001' => {
44
+ 'public_key' => @public_key_order_2
45
+ }
46
+ }
47
+ }
48
+
49
+ @remote_generator_configuration = {
50
+ 'provider' => 'SoarAuthenticationToken::RemoteTokenGenerator',
51
+ 'generator-url' => 'http://authentication-token-generator-service:9393/generate',
52
+ 'generator-client-auth-token' => 'test_ecosystem_token_for_auth_token_aaapi_authenticator_service'
53
+ }
54
+ @remote_validator_configuration = {
55
+ 'provider' => 'SoarAuthenticationToken::RemoteTokenValidator',
56
+ 'validator-url' => 'http://authentication-token-validator-service:9393/validate',
57
+ 'generator-client-auth-token' => 'test_ecosystem_token_for_auth_token_aaapi_authenticator_service'
58
+ }
59
+ @local_valid_generator = SoarAuthenticationToken::TokenGenerator.new(@local_valid_generator_configuration)
60
+ @local_valid_generator.inject_store_provider(@test_store)
61
+ @local_invalid_generator = SoarAuthenticationToken::TokenGenerator.new(@local_invalid_generator_configuration)
62
+ @local_invalid_generator.inject_store_provider(@test_store)
63
+ @remote_generator = SoarAuthenticationToken::TokenGenerator.new(@remote_generator_configuration)
64
+ end
65
+
66
+ it 'has a version number' do
67
+ expect(SoarAuthenticationToken::VERSION).not_to be nil
68
+ end
69
+
70
+ describe "#validate" do
71
+ context "given single key" do
72
+ let!(:iut) {
73
+ iut = SoarAuthenticationToken::TokenValidator.new(@local_validator_configuration_single_key)
74
+ iut.inject_store_provider(@test_store)
75
+ iut
76
+ }
77
+ context 'given valid token' do
78
+ let!(:token_validation_result) {
79
+ token, token_generator_meta = @local_valid_generator.generate(authenticated_identifier: @test_identifier)
80
+ iut.validate(authentication_token: token)
81
+ }
82
+ let!(:token_validity) { token_validation_result[0] }
83
+ let!(:token_meta) { token_validation_result[1] }
84
+ let!(:message) { token_validation_result[2] }
85
+
86
+ it 'indicate token is valid' do
87
+ expect(token_validity).to eq true
88
+ end
89
+
90
+ it 'provide the token meta' do
91
+ expect(token_meta['authenticated_identifier']).to eq @test_identifier
92
+ end
93
+
94
+ it 'provide a message indicating that the token is valid' do
95
+ expect(message).to match /Valid token for/
96
+ end
97
+ end
98
+
99
+ context 'given expired token' do
100
+ let!(:token_validation_result) {
101
+ token, token_generator_meta = @local_valid_generator.generate(authenticated_identifier: @test_identifier)
102
+ allow(Time).to receive(:now).and_return(Time.now+one_year_in_seconds)
103
+ iut.validate(authentication_token: token)
104
+ }
105
+ let!(:token_validity) { token_validation_result[0] }
106
+ let!(:token_meta) { token_validation_result[1] }
107
+ let!(:message) { token_validation_result[2] }
108
+
109
+ it 'indicate token is invalid' do
110
+ expect(token_validity).to eq false
111
+ end
112
+
113
+ it 'does not provide the token meta' do
114
+ expect(token_meta).to eq nil
115
+ end
116
+
117
+ it 'provide a message indicating that the token is invalid' do
118
+ expect(message).to match /Expired token/
119
+ end
120
+ end
121
+
122
+ context 'given unknown token (not in store)' do
123
+ let!(:token_validation_result) {
124
+ token, token_generator_meta = @local_valid_generator.generate(authenticated_identifier: @test_identifier)
125
+ @test_store.instance_variable_set("@store", []) #clear store
126
+ iut.validate(authentication_token: token)
127
+ }
128
+ let!(:token_validity) { token_validation_result[0] }
129
+ let!(:token_meta) { token_validation_result[1] }
130
+ let!(:message) { token_validation_result[2] }
131
+
132
+ it 'indicate that token is invalid' do
133
+ expect(token_validity).to eq false
134
+ end
135
+
136
+ it 'does not provide the token meta' do
137
+ expect(token_meta).to eq nil
138
+ end
139
+
140
+ it 'provide a message indicating that the token is invalid' do
141
+ expect(message).to match /Unknown token/
142
+ end
143
+ end
144
+
145
+ context 'given invalid token (garbage or different key)' do
146
+ let!(:token_validation_result) {
147
+ token, token_generator_meta = @remote_generator.generate(authenticated_identifier: @test_identifier)
148
+ iut.validate(authentication_token: token)
149
+ }
150
+ let!(:token_validity) { token_validation_result[0] }
151
+ let!(:token_meta) { token_validation_result[1] }
152
+ let!(:message) { token_validation_result[2] }
153
+
154
+
155
+ it 'indicate token is invalid' do
156
+ expect(token_validity).to eq false
157
+ end
158
+
159
+ it 'does not provide the token meta' do
160
+ expect(token_meta).to eq nil
161
+ end
162
+
163
+ it 'provide a message indicating that the token is invalid' do
164
+ expect(message).to match /Token decode\/verification failure/
165
+ end
166
+ end
167
+ end
168
+
169
+
170
+ context 'given multiple keys' do
171
+ let!(:iut) {
172
+ iut = SoarAuthenticationToken::TokenValidator.new(@multiple_key_configuration)
173
+ iut.inject_store_provider(@test_store)
174
+ iut
175
+ }
176
+ context 'given a token that cannot be verified with any of the public keys' do
177
+ let!(:token) {
178
+ generator_configuration = {
179
+ 'provider' => 'SoarAuthenticationToken::JwtTokenGenerator',
180
+ 'private_key' => @invalid_private_key
181
+ }
182
+ generator = SoarAuthenticationToken::TokenGenerator.new(generator_configuration)
183
+ generator.inject_store_provider(@test_store)
184
+ token, token_generator_meta = generator.generate(authenticated_identifier: @test_identifier)
185
+ token
186
+ }
187
+ let!(:token_validation_result) {
188
+ iut.validate(authentication_token: token)
189
+ }
190
+ let!(:token_validity) { token_validation_result[0] }
191
+ let!(:token_meta) { token_validation_result[1] }
192
+ let!(:message) { token_validation_result[2] }
193
+ it 'responds indicating the token is invalid' do
194
+ expect(token_validity).to eq false
195
+ end
196
+
197
+ it 'attempts to use the public keys in alphabetical order' do
198
+ iut = SoarAuthenticationToken::JwtTokenValidator.new(@multiple_key_configuration)
199
+ iut.inject_store_provider(@test_store)
200
+
201
+ expect(iut).to receive(:attempt_decode_using_a_key).once.ordered.with(token, @multiple_key_configuration['keys']['KEYPAIR_20160108T200001']).and_return(nil)
202
+ expect(iut).to receive(:attempt_decode_using_a_key).once.ordered.with(token, @multiple_key_configuration['keys']['KEYPAIR_20160108T190001']).and_return(nil)
203
+ expect(iut).to receive(:attempt_decode_using_a_key).once.ordered.with(token, @multiple_key_configuration['keys']['KEYPAIR_20160107T230001']).and_return(nil)
204
+
205
+ iut.validate(authentication_token: token)
206
+ end
207
+
208
+ it 'attempts to use all the public keys' do
209
+ iut = SoarAuthenticationToken::JwtTokenValidator.new(@multiple_key_configuration)
210
+ iut.inject_store_provider(@test_store)
211
+
212
+ expect(iut).to receive(:attempt_decode_using_a_key).exactly(3).times
213
+
214
+ iut.validate(authentication_token: token)
215
+ end
216
+ end
217
+
218
+ context 'given a token that can be verified with the first/latest public key in the list' do
219
+ let!(:token) {
220
+ generator_configuration = {
221
+ 'provider' => 'SoarAuthenticationToken::JwtTokenGenerator',
222
+ 'private_key' => @private_key_order_1
223
+ }
224
+ generator = SoarAuthenticationToken::TokenGenerator.new(generator_configuration)
225
+ generator.inject_store_provider(@test_store)
226
+ token, token_generator_meta = generator.generate(authenticated_identifier: @test_identifier)
227
+ token
228
+ }
229
+ it 'responds indicating the token is valid' do
230
+ iut = SoarAuthenticationToken::JwtTokenValidator.new(@multiple_key_configuration)
231
+ iut.inject_store_provider(@test_store)
232
+ token_validity, token_meta, message = iut.validate(authentication_token: token)
233
+
234
+ expect(token_validity).to eq true
235
+ end
236
+
237
+ it 'attempts to use the first public key (and only the first key)' do
238
+ #Do a bit of work to get fake token meta
239
+ iut = SoarAuthenticationToken::TokenValidator.new(@multiple_key_configuration)
240
+ iut.inject_store_provider(@test_store)
241
+ validity, token_meta, messages = iut.validate(authentication_token: token)
242
+
243
+ #Now create iut again for test
244
+ iut = SoarAuthenticationToken::JwtTokenValidator.new(@multiple_key_configuration)
245
+ iut.inject_store_provider(@test_store)
246
+
247
+ expect(iut).to receive(:attempt_decode_using_a_key).once.with(token, @multiple_key_configuration['keys']['KEYPAIR_20160108T200001']).and_return([token_meta])
248
+ iut.validate(authentication_token: token)
249
+ end
250
+ end
251
+
252
+ context 'given a token that can be verified with the second public key in the list' do
253
+ let!(:token) {
254
+ generator_configuration = {
255
+ 'provider' => 'SoarAuthenticationToken::JwtTokenGenerator',
256
+ 'private_key' => @private_key_order_2
257
+ }
258
+ generator = SoarAuthenticationToken::TokenGenerator.new(generator_configuration)
259
+ generator.inject_store_provider(@test_store)
260
+ token, token_generator_meta = generator.generate(authenticated_identifier: @test_identifier)
261
+ token
262
+ }
263
+ let!(:token_validation_result) {
264
+ iut.validate(authentication_token: token)
265
+ }
266
+ let!(:token_validity) { token_validation_result[0] }
267
+ let!(:token_meta) { token_validation_result[1] }
268
+ let!(:message) { token_validation_result[2] }
269
+ it 'responds indicating the token is valid' do
270
+ expect(token_validity).to eq true
271
+ end
272
+
273
+ it 'attempts to use public keys in order (first the first key, second the second key, not the third key)' do
274
+ #Do a bit of work to get fake token meta
275
+ iut = SoarAuthenticationToken::TokenValidator.new(@multiple_key_configuration)
276
+ iut.inject_store_provider(@test_store)
277
+ validity, token_meta, messages = iut.validate(authentication_token: token)
278
+
279
+ #Now create iut again for test
280
+ iut = SoarAuthenticationToken::JwtTokenValidator.new(@multiple_key_configuration)
281
+ iut.inject_store_provider(@test_store)
282
+
283
+ expect(iut).to receive(:attempt_decode_using_a_key).once.with(token, @multiple_key_configuration['keys']['KEYPAIR_20160108T200001']).and_return(nil)
284
+ expect(iut).to receive(:attempt_decode_using_a_key).once.with(token, @multiple_key_configuration['keys']['KEYPAIR_20160108T190001']).and_return([token_meta])
285
+ expect(iut).not_to receive(:attempt_decode_using_a_key).with(token, @multiple_key_configuration['keys']['KEYPAIR_20160107T230001'])
286
+ iut.validate(authentication_token: token)
287
+ end
288
+ end
289
+ end
290
+ end
291
+ end
@@ -9,7 +9,7 @@ describe SoarAuthenticationToken::RackMiddleware do
9
9
  keypair_generator = SoarAuthenticationToken::KeypairGenerator.new
10
10
  private_key, public_key = keypair_generator.generate
11
11
  configuration = {
12
- 'provider' => 'RemoteTokenGenerator',
12
+ 'provider' => 'SoarAuthenticationToken::RemoteTokenGenerator',
13
13
  'generator-url' => 'http://authentication-token-generator-service:9393/generate',
14
14
  'generator-client-auth-token' => 'test_ecosystem_token_for_auth_token_aaapi_authenticator_service'
15
15
  }
@@ -22,7 +22,7 @@ describe SoarAuthenticationToken::RackMiddleware do
22
22
  keypair_generator = SoarAuthenticationToken::KeypairGenerator.new
23
23
  private_key, public_key = keypair_generator.generate
24
24
  configuration = {
25
- 'provider' => 'JwtTokenGenerator',
25
+ 'provider' => 'SoarAuthenticationToken::JwtTokenGenerator',
26
26
  'private_key' => private_key,
27
27
  'public_key' => public_key
28
28
  }
@@ -52,7 +52,7 @@ describe SoarAuthenticationToken::RackMiddleware do
52
52
  [200, {"Content-Type"=>"text/html"}, test_app_response_data ]
53
53
  end
54
54
  @iut_configuration = {
55
- 'provider' => 'RemoteTokenValidator',
55
+ 'provider' => 'SoarAuthenticationToken::RemoteTokenValidator',
56
56
  'validator-url' => 'http://authentication-token-validator-service:9393/validate'
57
57
  }
58
58
  @iut = SoarAuthenticationToken::RackMiddleware.new(@test_app, @iut_configuration)
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+
4
+ describe SoarAuthenticationToken::RemoteTokenValidator do
5
+ subject { SoarAuthenticationToken::RemoteTokenValidator }
6
+ before :all do
7
+ @test_store = AuthTokenStoreProvider::StubClient.new
8
+ keypair_generator = SoarAuthenticationToken::KeypairGenerator.new
9
+ @valid_private_key, @valid_public_key = keypair_generator.generate
10
+ @invalid_private_key, @invalid_public_key = keypair_generator.generate
11
+ @test_identifier = 'a@b.co.za'
12
+ @local_invalid_generator_configuration = {
13
+ 'provider' => 'SoarAuthenticationToken::JwtTokenGenerator',
14
+ 'private_key' => @invalid_private_key
15
+ }
16
+
17
+ @remote_generator_configuration = {
18
+ 'provider' => 'SoarAuthenticationToken::RemoteTokenGenerator',
19
+ 'generator-url' => 'http://authentication-token-generator-service:9393/generate',
20
+ 'generator-client-auth-token' => 'test_ecosystem_token_for_auth_token_aaapi_authenticator_service'
21
+ }
22
+ @remote_validator_configuration = {
23
+ 'provider' => 'SoarAuthenticationToken::RemoteTokenValidator',
24
+ 'validator-url' => 'http://authentication-token-validator-service:9393/validate',
25
+ 'generator-client-auth-token' => 'test_ecosystem_token_for_auth_token_aaapi_authenticator_service'
26
+ }
27
+ @local_invalid_generator = SoarAuthenticationToken::TokenGenerator.new(@local_invalid_generator_configuration)
28
+ @local_invalid_generator.inject_store_provider(@test_store)
29
+ @remote_generator = SoarAuthenticationToken::TokenGenerator.new(@remote_generator_configuration)
30
+ end
31
+
32
+ it 'has a version number' do
33
+ expect(SoarAuthenticationToken::VERSION).not_to be nil
34
+ end
35
+
36
+ describe "#validate" do
37
+ let!(:iut) { subject.new(@remote_validator_configuration) }
38
+ context 'given valid token' do
39
+ let!(:token_validation_result) {
40
+ token, token_generator_meta = @remote_generator.generate(authenticated_identifier: @test_identifier)
41
+ iut.validate(authentication_token: token)
42
+ }
43
+ let!(:token_validity) { token_validation_result[0] }
44
+ let!(:token_meta) { token_validation_result[1] }
45
+ let!(:message) { token_validation_result[2] }
46
+
47
+ it 'should indicate valid if the token is valid' do
48
+ expect(token_validity).to eq true
49
+ end
50
+
51
+ it 'should provide the authenticated_identifier if the token is valid' do
52
+ expect(token_meta['authenticated_identifier']).to eq @test_identifier
53
+ end
54
+ end
55
+
56
+ context 'given invalid (generalized) token' do
57
+ let!(:token_validation_result) {
58
+ token, token_generator_meta = @local_invalid_generator.generate(authenticated_identifier: @test_identifier)
59
+ iut.validate(authentication_token: token)
60
+ }
61
+ let!(:token_validity) { token_validation_result[0] }
62
+ let!(:token_meta) { token_validation_result[1] }
63
+ let!(:message) { token_validation_result[2] }
64
+
65
+ it 'indicate token is invalid' do
66
+ expect(token_validity).to eq false
67
+ end
68
+
69
+ it 'does not provide the token meta' do
70
+ expect(token_meta).to eq nil
71
+ end
72
+
73
+ it 'provides a message indicating the token is invalid' do
74
+ expect(message).to match /Token decode\/verification failure/
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+
4
+ describe SoarAuthenticationToken::StaticTokenValidator do
5
+ subject { SoarAuthenticationToken::StaticTokenValidator }
6
+ before :all do
7
+ @token_for_client_service_1 = 'some_secret_token_string_1111'
8
+ @token_for_client_service_2 = 'some_secret_token_string_2222'
9
+ @token_for_client_service_3_expired = 'some_secret_token_string_3333_expired'
10
+ @token_for_client_service_3_unknown = 'some_secret_token_string_3333_unknown'
11
+ end
12
+
13
+ it 'has a version number' do
14
+ expect(SoarAuthenticationToken::VERSION).not_to be nil
15
+ end
16
+
17
+ describe "#validate" do
18
+ context "given that the validator is configured for static tokens" do
19
+ let(:configuration) {
20
+ current_time = Time.now
21
+ {
22
+ 'provider' => 'SoarAuthenticationToken::StaticTokenValidator',
23
+ 'static_tokens' => [
24
+ {
25
+ 'token' => 'some_secret_token_string_1111',
26
+ 'authenticated_identifier' => 'calling_client_service_1',
27
+ 'token_issue_time' => current_time.utc.iso8601(3),
28
+ 'token_expiry_time' => (current_time + 100).utc.iso8601(3)
29
+ },
30
+ {
31
+ 'token' => 'some_secret_token_string_2222',
32
+ 'authenticated_identifier' => 'calling_client_service_2',
33
+ 'token_issue_time' => current_time.utc.iso8601(3),
34
+ 'token_expiry_time' => (current_time + 100).utc.iso8601(3)
35
+ },
36
+ {
37
+ 'token' => 'some_secret_token_string_3333_expired',
38
+ 'authenticated_identifier' => 'calling_client_service_3',
39
+ 'token_issue_time' => (current_time - 100).utc.iso8601(3),
40
+ 'token_expiry_time' => (current_time - 50).utc.iso8601(3)
41
+ }
42
+ ]
43
+ }
44
+ }
45
+ let(:iut) { subject.new(configuration) }
46
+ context "given a token that is in the list of static tokens and not expired" do
47
+ let(:response) { iut.validate(authentication_token: @token_for_client_service_1) }
48
+ let(:token_validity) { response[0] }
49
+ let(:token_meta) { response[1] }
50
+ let(:message) { response[2] }
51
+
52
+ it 'should respond with true indicating token is valid' do
53
+ expect(token_validity).to eq true
54
+ end
55
+
56
+ it 'should respond with token meta' do
57
+ expect(token_meta).to_not eq nil
58
+ end
59
+
60
+ it 'should respond with a message indicating that the token is valid' do
61
+ expect(message).to eq 'Valid token for <calling_client_service_1>'
62
+ end
63
+ end
64
+
65
+ context "the token is in the list of static tokens and expired" do
66
+ let(:response) { iut.validate(authentication_token: @token_for_client_service_3_expired) }
67
+ let(:token_validity) { response[0] }
68
+ let(:token_meta) { response[1] }
69
+ let(:message) { response[2] }
70
+
71
+ it 'should respond with false indicating token is invalid' do
72
+ expect(token_validity).to eq false
73
+ end
74
+
75
+ it 'should respond with nil token meta' do
76
+ expect(token_meta).to eq nil
77
+ end
78
+
79
+ it 'should respond with a message indicating that the token is expired' do
80
+ expect(message).to match /Expired token/
81
+ end
82
+ end
83
+
84
+ context "given a token that is not in the list of static tokens" do
85
+ let(:response) { iut.validate(authentication_token: @token_for_client_service_3_unknown) }
86
+ let(:token_validity) { response[0] }
87
+ let(:token_meta) { response[1] }
88
+ let(:message) { response[2] }
89
+
90
+ it 'should respond with false indicating token is invalid' do
91
+ expect(token_validity).to eq false
92
+ end
93
+
94
+ it 'should respond with no token meta' do
95
+ expect(token_meta).to eq nil
96
+ end
97
+
98
+ it 'should respond with a message indicating that the token is valid' do
99
+ expect(message).to match /Unknown static token/
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end