soar_authentication_token 4.0.1 → 5.0.0

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