soar_authentication_token 3.0.9 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 943763e03572585973f8183ec30b991724885a5b
4
- data.tar.gz: bc1494d9165b7b8fb09a16707be3c799afa562ed
3
+ metadata.gz: 4020cefab79d1ffcef0ea47c4787412cf4c520e4
4
+ data.tar.gz: dff3cc2d0e722cfbfbd9ae761694417350bfe9ba
5
5
  SHA512:
6
- metadata.gz: 7de47b8886a50cb1b39710af98a80ea8f1e748aa6ba233f2859cbd30dccf8065b341c633f60f8bbd81e9ede872854be13e725d8b5559059e20d78df06cdd6041
7
- data.tar.gz: 768f6724ca6f208abd04af1d27ff3918b3ed668e623e32505a2099e09d1b4c15a368207a389572f5cdd11e31c6d01dce23709f823ec2c1b8415983a70329fe29
6
+ metadata.gz: a9f8caae8f97bc3b7954b9c70848f605b6b7c6ba32b90800e42955811ccdfb34cc2d9bc7632aca2ec1c5b0872146a4a2ca761340fd228afe44a238f37b2998fd
7
+ data.tar.gz: 2a9aaf075ccae76090499d7bca7a7d88b663f75aecbd40393c783071a48591c2bcf842c64b86e9b04c436e6c5c36947442eadd34b0314ced9660030a5f5dc55b
data/README.md CHANGED
@@ -24,16 +24,16 @@ gem install soar_authentication_token
24
24
 
25
25
  ## Configuration of generators and validators
26
26
 
27
- There are three modes of operation: local, remote or static
27
+ There are three provider groups for operation: JwtToken, RemoteToken or StaticToken
28
28
 
29
- ### Local
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.
29
+ ### JwtTokenGenerator/JwtTokenValidator
30
+ With the JwtTokenValidator provider 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.
31
31
 
32
- ### Remote
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.
32
+ ### RemoteTokenGenerator/RemoteTokenValidator
33
+ With the RemoteTokenValidator provider 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.
34
34
 
35
- ### Static
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:
35
+ ### StaticTokenValidator
36
+ In this mode the StaticTokenValidator 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
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.
38
38
  * In test scenarios where you do not want to pull in the authentication services to perform testing of your services.
39
39
 
@@ -77,7 +77,7 @@ First step is to configure the middleware. Actually you are not configuring the
77
77
 
78
78
  ```ruby
79
79
  @iut_configuration = {
80
- 'mode' => 'remote',
80
+ 'provider' => 'RemoteTokenValidator',
81
81
  'validator-url' => 'http://authentication-token-validator-service:9393/validate'
82
82
  }
83
83
  @iut = SoarAuthenticationToken::RackMiddleware.new(@test_app, @iut_configuration)
@@ -94,7 +94,7 @@ The class generates tokens or requests a token from a generator service as confi
94
94
  For remote token generation, configure as such:
95
95
  ```ruby
96
96
  @configuration_remote = {
97
- 'mode' => 'remote',
97
+ 'provider' => 'RemoteTokenGenerator',
98
98
  'generator-url' => 'http://authentication-token-generator-service:9393/generate',
99
99
  'generator-client-auth-token' => 'xxxx'
100
100
  }
@@ -105,7 +105,7 @@ For local token generation, configure as such:
105
105
  generator = SoarAuthenticationToken::KeypairGenerator.new
106
106
  private_key, public_key = generator.generate
107
107
  @configuration_local = {
108
- 'mode' => 'local',
108
+ 'provider' => 'JwtTokenGenerator',
109
109
  'private_key' => private_key
110
110
  }
111
111
  ```
@@ -131,7 +131,7 @@ The class validates tokens or requests validation of a token from a validation s
131
131
  For remote token validation, configure as such:
132
132
  ```ruby
133
133
  @configuration_remote = {
134
- 'mode' => 'remote',
134
+ 'provider' => 'RemoteTokenGenerator',
135
135
  'validator-url' => 'http://authentication-token-validator-service:9393/validate',
136
136
  }
137
137
  ```
@@ -141,7 +141,7 @@ For local token validation, configure as such:
141
141
  generator = SoarAuthenticationToken::KeypairGenerator.new
142
142
  private_key, public_key = generator.generate
143
143
  @configuration_local = {
144
- 'mode' => 'local',
144
+ 'provider' => 'JwtTokenValidator',
145
145
  'public_key' => public_key
146
146
  }
147
147
  ```
@@ -1,6 +1,11 @@
1
1
  module SoarAuthenticationToken
2
2
  end
3
3
 
4
+ require 'soar_authentication_token/providers/jwt_token_generator'
5
+ require 'soar_authentication_token/providers/jwt_token_validator'
6
+ require 'soar_authentication_token/providers/remote_token_generator'
7
+ require 'soar_authentication_token/providers/remote_token_validator'
8
+ require 'soar_authentication_token/providers/static_token_validator'
4
9
  require 'soar_authentication_token/keypair_generator'
5
10
  require 'soar_authentication_token/token_generator'
6
11
  require 'soar_authentication_token/token_validator'
@@ -0,0 +1,66 @@
1
+ require 'soar_xt'
2
+ require 'jwt'
3
+ require 'securerandom'
4
+ require 'time'
5
+ require 'json'
6
+ require 'authenticated_client'
7
+
8
+ module SoarAuthenticationToken
9
+ class JwtTokenGenerator
10
+ DEFAULT_CONFIGURATION = {
11
+ 'expiry' => 604800 #a days worth of seconds
12
+ } unless defined? DEFAULT_CONFIGURATION; DEFAULT_CONFIGURATION.freeze
13
+
14
+ def initialize(configuration)
15
+ @configuration = merge_with_default_configuration(configuration)
16
+ validate_local_mode_configuration
17
+ @private_key = OpenSSL::PKey::EC.new(@configuration['private_key'])
18
+ end
19
+
20
+ def inject_store_provider(store_provider)
21
+ @store_provider = store_provider
22
+ end
23
+
24
+ def generate(authenticated_identifier:, flow_identifier: nil)
25
+ token_meta = generate_meta(authenticated_identifier)
26
+ token = encode(token_meta)
27
+ add_token_to_store(token_meta,flow_identifier)
28
+ [token, token_meta]
29
+ end
30
+
31
+ private
32
+
33
+ def generate_meta(authenticated_identifier)
34
+ current_time = Time.now
35
+ { 'authenticated_identifier' => authenticated_identifier,
36
+ 'token_issue_time' => current_time.utc.iso8601(3),
37
+ 'token_expiry_time' => (current_time + @configuration['expiry']).utc.iso8601(3),
38
+ 'token_identifier' => SecureRandom.hex(32)
39
+ }
40
+ end
41
+
42
+ def encode(meta)
43
+ JWT.encode(meta, @private_key, 'ES512')
44
+ end
45
+
46
+ def validate_local_mode_configuration
47
+ raise "'private_key' must be configured in local mode" unless @configuration['private_key']
48
+ raise "'expiry' must be configured in local mode" unless @configuration['expiry']
49
+ raise "'expiry' must be an integer" unless Integer(@configuration['expiry'])
50
+ end
51
+
52
+ def merge_with_default_configuration(configuration)
53
+ configuration = {} unless configuration
54
+ Hash.deep_merge(DEFAULT_CONFIGURATION,configuration)
55
+ end
56
+
57
+ def add_token_to_store(meta,flow_identifier)
58
+ @store_provider.add(
59
+ token_identifier: meta['token_identifier'],
60
+ authenticated_identifier: meta['authenticated_identifier'],
61
+ token_issue_time: meta['token_issue_time'],
62
+ token_expiry_time: meta['token_expiry_time'],
63
+ flow_identifier: flow_identifier)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,89 @@
1
+ require 'jwt'
2
+ require 'authenticated_client'
3
+
4
+ module SoarAuthenticationToken
5
+ class JwtTokenValidator
6
+ def initialize(configuration)
7
+ @configuration = configuration
8
+ set_configuration_defaults
9
+ validate_configuration
10
+ @public_key = OpenSSL::PKey::EC.new(@configuration['public_key'])
11
+ @public_key.private_key = nil
12
+ end
13
+
14
+ def inject_store_provider(store_provider)
15
+ @store_provider = store_provider
16
+ end
17
+
18
+ def validate(authentication_token:,flow_identifier: nil)
19
+ meta = decode_token_meta(authentication_token)
20
+ return rejection_result(reason: "Expired token <#{meta['token_expiry_time']}> for <#{meta['authenticated_identifier']}>") if token_expired?(meta)
21
+ return rejection_result(reason: "Unknown token for <#{meta['authenticated_identifier']}>") unless token_exist_in_store?(meta,flow_identifier)
22
+ success_result(token_meta: meta)
23
+ rescue JWT::VerificationError, JWT::DecodeError
24
+ rejection_result(reason: 'Token decode/verification failure')
25
+ end
26
+
27
+ private
28
+
29
+ def set_configuration_defaults
30
+ @configuration['expiry'] ||= 86400
31
+ end
32
+
33
+ def decode_token_meta(authentication_token)
34
+ decoded_token_payload = decode(authentication_token)
35
+ compile_meta(token_identifier: decoded_token_payload[0]['token_identifier'],
36
+ authenticated_identifier: decoded_token_payload[0]['authenticated_identifier'],
37
+ token_issue_time: decoded_token_payload[0]['token_issue_time'],
38
+ token_expiry_time: decoded_token_payload[0]['token_expiry_time'])
39
+ end
40
+
41
+ def compile_meta(token_identifier:,
42
+ authenticated_identifier:,
43
+ token_issue_time:,
44
+ token_expiry_time:)
45
+ {
46
+ 'token_identifier' => token_identifier,
47
+ 'authenticated_identifier' => authenticated_identifier,
48
+ 'token_issue_time' => token_issue_time,
49
+ 'token_expiry_time' => token_expiry_time,
50
+ 'token_age' => token_age(token_issue_time)
51
+ }
52
+ end
53
+
54
+ def token_age(token_issue_time)
55
+ Time.now - Time.parse(token_issue_time.to_s)
56
+ end
57
+
58
+ def validate_configuration
59
+ raise "'public_key' must be configured in local mode" unless @configuration['public_key']
60
+ raise "'expiry' must be configured in local mode" unless @configuration['expiry']
61
+ raise "'expiry' must be an integer" unless Integer(@configuration['expiry'])
62
+ end
63
+
64
+ def decode(authentication_token)
65
+ JWT.decode(authentication_token, @public_key, true, { :algorithm => 'ES512' })
66
+ end
67
+
68
+ def token_expired?(meta)
69
+ Time.parse(meta['token_expiry_time'].to_s) < Time.now
70
+ end
71
+
72
+ def token_exist_in_store?(meta,flow_identifier)
73
+ @store_provider.token_exist?(
74
+ token_identifier: meta['token_identifier'],
75
+ authenticated_identifier: meta['authenticated_identifier'],
76
+ token_issue_time: meta['token_issue_time'],
77
+ token_expiry_time: meta['token_expiry_time'],
78
+ flow_identifier: flow_identifier)
79
+ end
80
+
81
+ def rejection_result(reason:)
82
+ [false, nil, reason]
83
+ end
84
+
85
+ def success_result(token_meta:)
86
+ [true, token_meta, "Valid token for <#{token_meta['authenticated_identifier']}>" ]
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,44 @@
1
+ require 'json'
2
+ require 'authenticated_client'
3
+
4
+ module SoarAuthenticationToken
5
+ class RemoteTokenGenerator
6
+ def initialize(configuration)
7
+ @configuration = configuration
8
+ validate_configuration
9
+ end
10
+
11
+ def inject_store_provider(store_provider)
12
+ #ignore the store provider since this generator does not use a store
13
+ end
14
+
15
+ def generate(authenticated_identifier:, flow_identifier: nil)
16
+ client = authenticated_client(authenticated_identifier,flow_identifier)
17
+ validate_and_extract_token_from_response(client.request)
18
+ end
19
+
20
+ private
21
+
22
+ def authenticated_client(authenticated_identifier,flow_identifier)
23
+ client = AuthenticatedClient::Client.new
24
+ client.url = @configuration['generator-url']
25
+ client.token = @configuration['generator-client-auth-token']
26
+ client.verb = :post
27
+ client.parameters = {'flow_identifier' => flow_identifier}
28
+ client.body = { 'authenticated_identifier' => authenticated_identifier }
29
+ client
30
+ end
31
+
32
+ def validate_and_extract_token_from_response(response)
33
+ raise "Failure generating token with token generation service. Code #{response.code}" if '200' != response.code
34
+ body = JSON.parse(response.body)
35
+ raise 'Failure generating token by token service' if 'success' != body['status']
36
+ raise 'Token service did not provide token' if body['data'].nil? or body['data']['token'].nil?
37
+ body['data']['token']
38
+ end
39
+
40
+ def validate_configuration
41
+ raise "'generator-url' must be configured" if @configuration['generator-url'].nil?
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,57 @@
1
+ require 'authenticated_client'
2
+
3
+ module SoarAuthenticationToken
4
+ class RemoteTokenValidator
5
+ def initialize(configuration)
6
+ @configuration = configuration
7
+ validate_configuration
8
+ end
9
+
10
+ def inject_store_provider(store_provider)
11
+ #ignore the store provider since this validator does not use a store
12
+ end
13
+
14
+ def validate(authentication_token:,flow_identifier: nil)
15
+ response = send_request(authentication_token,flow_identifier)
16
+ validate_and_extract_information_from_response(response)
17
+ end
18
+
19
+ private
20
+
21
+ def send_request(authentication_token,flow_identifier)
22
+ uri = URI.parse(@configuration['validator-url'])
23
+ uri.query = URI.encode_www_form( {'flow_identifier' => flow_identifier} )
24
+ http = Net::HTTP.new(uri.host, uri.port)
25
+ http.use_ssl = true if uri.is_a?(URI::HTTPS)
26
+ request = Net::HTTP::Post.new(uri.request_uri)
27
+ request.body = { 'authentication_token' => authentication_token }.to_json
28
+ http.request(request)
29
+ end
30
+
31
+ def validate_and_extract_information_from_response(response)
32
+ raise "Failure validating token with token validation service. Code #{response.code} received" if '200' != response.code
33
+ body = JSON.parse(response.body)
34
+ if ('success' == body['status']) and body['data']
35
+ token_validity = body['data']['token_validity']
36
+ token_meta = body['data']['token_meta']
37
+ message = body['data']['message']
38
+ raise 'Token validation service did not provide token_validity' if token_validity.nil?
39
+ raise 'Token validation service did not provide token_meta' if token_validity and token_meta.nil?
40
+ raise 'Token validation service did not provide message' if message.nil?
41
+ return [token_validity, token_meta, message]
42
+ end
43
+ if 'fail' == body['status']
44
+ return rejection_result(reason: 'remote validation failed')
45
+ end
46
+ raise "Failure validating token with token validation service. Status '#{body['status']}' received"
47
+ end
48
+
49
+ def validate_configuration
50
+ raise "'validator-url' must be configured in remote mode" unless @configuration['validator-url']
51
+ end
52
+
53
+ def rejection_result(reason:)
54
+ [false, nil, reason]
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,64 @@
1
+ module SoarAuthenticationToken
2
+ class StaticTokenValidator
3
+ def initialize(configuration)
4
+ @configuration = configuration
5
+ validate_configuration
6
+ end
7
+
8
+ def inject_store_provider(store_provider)
9
+ #ignore the store provider since this validator does not use a store
10
+ end
11
+
12
+ def validate(authentication_token:,flow_identifier: nil)
13
+ found_static_token = find_configured_static_token(authentication_token)
14
+ return rejection_result(reason: 'Unknown static token') if found_static_token.nil?
15
+ meta = compile_meta(token_identifier: 'static_token',
16
+ authenticated_identifier: found_static_token['authenticated_identifier'],
17
+ token_issue_time: found_static_token['token_issue_time'],
18
+ token_expiry_time: found_static_token['token_expiry_time'])
19
+ return rejection_result(reason: "Expired token <#{meta['token_expiry_time']}> for <#{meta['authenticated_identifier']}>") if token_expired?(meta)
20
+ return success_result(token_meta: meta)
21
+ end
22
+
23
+ private
24
+
25
+ def find_configured_static_token(authentication_token)
26
+ @configuration['static_tokens'].each { |static_token|
27
+ return static_token if authentication_token == static_token['token']
28
+ }
29
+ nil
30
+ end
31
+
32
+ def compile_meta(token_identifier:,
33
+ authenticated_identifier:,
34
+ token_issue_time:,
35
+ token_expiry_time:)
36
+ {
37
+ 'token_identifier' => token_identifier,
38
+ 'authenticated_identifier' => authenticated_identifier,
39
+ 'token_issue_time' => token_issue_time,
40
+ 'token_expiry_time' => token_expiry_time,
41
+ 'token_age' => token_age(token_issue_time)
42
+ }
43
+ end
44
+
45
+ def token_age(token_issue_time)
46
+ Time.now - Time.parse(token_issue_time.to_s)
47
+ end
48
+
49
+ def validate_configuration
50
+ raise "array of 'static_tokens' must be configured" unless @configuration['static_tokens']
51
+ end
52
+
53
+ def token_expired?(meta)
54
+ Time.parse(meta['token_expiry_time'].to_s) < Time.now
55
+ end
56
+
57
+ def rejection_result(reason:)
58
+ [false, nil, reason]
59
+ end
60
+ def success_result(token_meta:)
61
+ [true, token_meta, "Valid token for <#{token_meta['authenticated_identifier']}>" ]
62
+ end
63
+ end
64
+ end
@@ -1,107 +1,28 @@
1
- require 'soar_xt'
2
- require 'jwt'
3
- require 'securerandom'
4
- require 'time'
5
- require 'net/http'
6
- require 'uri'
7
- require 'json'
8
- require 'authenticated_client'
9
-
10
1
  module SoarAuthenticationToken
11
2
  class TokenGenerator
12
- DEFAULT_CONFIGURATION = {
13
- 'expiry' => 604800 #a days worth of seconds
14
- } unless defined? DEFAULT_CONFIGURATION; DEFAULT_CONFIGURATION.freeze
15
-
16
3
  def initialize(configuration)
17
- @configuration = merge_with_default_configuration(configuration)
4
+ @configuration = configuration
18
5
  validate_configuration
19
- @private_key = OpenSSL::PKey::EC.new(@configuration['private_key'])
6
+ instantiate_provider
20
7
  end
21
8
 
22
9
  def inject_store_provider(store_provider)
23
- @store_provider = store_provider
10
+ @provider.inject_store_provider(store_provider)
24
11
  end
25
12
 
26
13
  def generate(authenticated_identifier:, flow_identifier: nil)
27
- return generate_locally(authenticated_identifier,flow_identifier) if 'local' == @configuration['mode']
28
- return generate_remotely(authenticated_identifier,flow_identifier)
14
+ @provider.generate(authenticated_identifier: authenticated_identifier,
15
+ flow_identifier: flow_identifier)
29
16
  end
30
17
 
31
18
  private
32
19
 
33
- def generate_locally(authenticated_identifier,flow_identifier)
34
- token_meta = generate_meta(authenticated_identifier)
35
- token = encode(token_meta)
36
- add_token_to_store(token_meta,flow_identifier)
37
- [token, token_meta]
38
- end
39
-
40
- def generate_remotely(authenticated_identifier,flow_identifier)
41
- client = authenticated_client(authenticated_identifier,flow_identifier)
42
- validate_and_extract_token_from_response(client.request)
43
- end
44
-
45
- def authenticated_client(authenticated_identifier,flow_identifier)
46
- client = AuthenticatedClient::Client.new
47
- client.url = @configuration['generator-url']
48
- client.token = @configuration['generator-client-auth-token']
49
- client.verb = :post
50
- client.parameters = {'flow_identifier' => flow_identifier}
51
- client.body = { 'authenticated_identifier' => authenticated_identifier }
52
- client
53
- end
54
-
55
- def validate_and_extract_token_from_response(response)
56
- raise "Failure generating token with token generation service. Code #{response.code}" if '200' != response.code
57
- body = JSON.parse(response.body)
58
- raise 'Failure generating token by token service' if 'success' != body['status']
59
- raise 'Token service did not provide token' if body['data'].nil? or body['data']['token'].nil?
60
- body['data']['token']
61
- end
62
-
63
- def generate_meta(authenticated_identifier)
64
- current_time = Time.now
65
- { 'authenticated_identifier' => authenticated_identifier,
66
- 'token_issue_time' => current_time.utc.iso8601(3),
67
- 'token_expiry_time' => (current_time + @configuration['expiry']).utc.iso8601(3),
68
- 'token_identifier' => SecureRandom.hex(32)
69
- }
70
- end
71
-
72
- def encode(meta)
73
- JWT.encode(meta, @private_key, 'ES512')
20
+ def instantiate_provider
21
+ @provider = Object::const_get("SoarAuthenticationToken::#{@configuration['provider']}").new(@configuration)
74
22
  end
75
23
 
76
24
  def validate_configuration
77
- raise "'mode' must be configured" unless @configuration['mode']
78
- raise "'mode' must be configured as either 'local' or 'remote'" unless ['local','remote'].include?(@configuration['mode'])
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'])
91
- end
92
-
93
- def merge_with_default_configuration(configuration)
94
- configuration = {} unless configuration
95
- Hash.deep_merge(DEFAULT_CONFIGURATION,configuration)
96
- end
97
-
98
- def add_token_to_store(meta,flow_identifier)
99
- @store_provider.add(
100
- token_identifier: meta['token_identifier'],
101
- authenticated_identifier: meta['authenticated_identifier'],
102
- token_issue_time: meta['token_issue_time'],
103
- token_expiry_time: meta['token_expiry_time'],
104
- flow_identifier: flow_identifier)
25
+ raise "'provider' must be configured" unless @configuration['provider']
105
26
  end
106
27
  end
107
28
  end
@@ -1,169 +1,28 @@
1
- require 'soar_xt'
2
- require 'jwt'
3
- require 'authenticated_client'
4
-
5
1
  module SoarAuthenticationToken
6
2
  class TokenValidator
7
- DEFAULT_CONFIGURATION = {
8
- 'expiry' => 604800 #a days worth of seconds
9
- } unless defined? DEFAULT_CONFIGURATION; DEFAULT_CONFIGURATION.freeze
10
-
11
3
  def initialize(configuration)
12
- @configuration = merge_with_default_configuration(configuration)
4
+ @configuration = configuration
13
5
  validate_configuration
14
- @public_key = OpenSSL::PKey::EC.new(@configuration['public_key'])
15
- @public_key.private_key = nil
6
+ instantiate_provider
16
7
  end
17
8
 
18
9
  def inject_store_provider(store_provider)
19
- @store_provider = store_provider
10
+ @provider.inject_store_provider(store_provider)
20
11
  end
21
12
 
22
13
  def validate(authentication_token:,flow_identifier: nil)
23
- return validate_locally(authentication_token,flow_identifier) if 'local' == @configuration['mode']
24
- return validate_statically(authentication_token) if 'static' == @configuration['mode']
25
- return validate_remotely(authentication_token,flow_identifier) if 'remote' == @configuration['mode']
26
- raise 'invalid validation mode configured'
14
+ @provider.validate(authentication_token: authentication_token,
15
+ flow_identifier: flow_identifier)
27
16
  end
28
17
 
29
18
  private
30
19
 
31
- def validate_statically(authentication_token)
32
- found_static_token = find_configured_static_token(authentication_token)
33
- return rejection_result(reason: 'Unknown static token') if found_static_token.nil?
34
- meta = compile_meta(token_identifier: 'static_token',
35
- authenticated_identifier: found_static_token['authenticated_identifier'],
36
- token_issue_time: found_static_token['token_issue_time'],
37
- token_expiry_time: found_static_token['token_expiry_time'])
38
- return rejection_result(reason: "Expired token <#{meta['token_expiry_time']}> for <#{meta['authenticated_identifier']}>") if token_expired?(meta)
39
- return success_result(token_meta: meta)
40
- end
41
-
42
- def find_configured_static_token(authentication_token)
43
- @configuration['static_tokens'].each { |static_token|
44
- if authentication_token == static_token['token']
45
- return static_token
46
- end
47
- }
48
- nil
49
- end
50
-
51
- def validate_locally(authentication_token,flow_identifier)
52
- meta = decode_token_meta(authentication_token)
53
- return rejection_result(reason: "Expired token <#{meta['token_expiry_time']}> for <#{meta['authenticated_identifier']}>") if token_expired?(meta)
54
- return rejection_result(reason: "Unknown token for <#{meta['authenticated_identifier']}>") unless token_exist_in_store?(meta,flow_identifier)
55
- success_result(token_meta: meta)
56
- rescue JWT::VerificationError, JWT::DecodeError
57
- rejection_result(reason: 'Token decode/verification failure')
58
- end
59
-
60
- def decode_token_meta(authentication_token)
61
- decoded_token_payload = decode(authentication_token)
62
- compile_meta(token_identifier: decoded_token_payload[0]['token_identifier'],
63
- authenticated_identifier: decoded_token_payload[0]['authenticated_identifier'],
64
- token_issue_time: decoded_token_payload[0]['token_issue_time'],
65
- token_expiry_time: decoded_token_payload[0]['token_expiry_time'])
66
- end
67
-
68
- def compile_meta(token_identifier:,
69
- authenticated_identifier:,
70
- token_issue_time:,
71
- token_expiry_time:)
72
- {
73
- 'token_identifier' => token_identifier,
74
- 'authenticated_identifier' => authenticated_identifier,
75
- 'token_issue_time' => token_issue_time,
76
- 'token_expiry_time' => token_expiry_time,
77
- 'token_age' => token_age(token_issue_time)
78
- }
79
- end
80
-
81
- def token_age(token_issue_time)
82
- Time.now - Time.parse(token_issue_time.to_s)
83
- end
84
-
85
- def validate_remotely(authentication_token,flow_identifier)
86
- response = send_request(authentication_token,flow_identifier)
87
- validate_and_extract_information_from_response(response)
88
- end
89
-
90
- def send_request(authentication_token,flow_identifier)
91
- uri = URI.parse(@configuration['validator-url'])
92
- uri.query = URI.encode_www_form( {'flow_identifier' => flow_identifier} )
93
- http = Net::HTTP.new(uri.host, uri.port)
94
- http.use_ssl = true if uri.is_a?(URI::HTTPS)
95
- request = Net::HTTP::Post.new(uri.request_uri)
96
- request.body = { 'authentication_token' => authentication_token }.to_json
97
- http.request(request)
98
- end
99
-
100
- def validate_and_extract_information_from_response(response)
101
- raise "Failure validating token with token validation service. Code #{response.code} received" if '200' != response.code
102
- body = JSON.parse(response.body)
103
- if ('success' == body['status']) and body['data']
104
- token_validity = body['data']['token_validity']
105
- token_meta = body['data']['token_meta']
106
- message = body['data']['message']
107
- raise 'Token validation service did not provide token_validity' if token_validity.nil?
108
- raise 'Token validation service did not provide token_meta' if token_validity and token_meta.nil?
109
- raise 'Token validation service did not provide message' if message.nil?
110
- return [token_validity, token_meta, message]
111
- end
112
- if 'fail' == body['status']
113
- return rejection_result(reason: 'remote validation failed')
114
- end
115
- raise "Failure validating token with token validation service. Status '#{body['status']}' received"
20
+ def instantiate_provider
21
+ @provider = Object::const_get("SoarAuthenticationToken::#{@configuration['provider']}").new(@configuration)
116
22
  end
117
23
 
118
24
  def validate_configuration
119
- raise "'mode' must be configured" unless @configuration['mode']
120
- raise "'mode' must be configured as either 'local', 'remote' or 'static'" unless ['local','remote', 'static'].include?(@configuration['mode'])
121
- validate_remote_configuration if 'remote' == @configuration['mode']
122
- validate_local_configuration if 'local' == @configuration['mode']
123
- validate_static_configuration if 'static' == @configuration['mode']
124
- end
125
-
126
- def validate_remote_configuration
127
- raise "'validator-url' must be configured in remote mode" unless @configuration['validator-url']
128
- end
129
-
130
- def validate_local_configuration
131
- raise "'public_key' must be configured in local mode" unless @configuration['public_key']
132
- raise "'expiry' must be configured in local mode" unless @configuration['expiry']
133
- raise "'expiry' must be an integer" unless Integer(@configuration['expiry'])
134
- end
135
-
136
- def validate_static_configuration
137
- raise "'static_tokens' must be configured in local mode" unless @configuration['static_tokens']
138
- end
139
-
140
- def merge_with_default_configuration(configuration)
141
- Hash.deep_merge(DEFAULT_CONFIGURATION,configuration)
142
- end
143
-
144
- def decode(authentication_token)
145
- JWT.decode(authentication_token, @public_key, true, { :algorithm => 'ES512' })
146
- end
147
-
148
- def token_expired?(meta)
149
- Time.parse(meta['token_expiry_time'].to_s) < Time.now
150
- end
151
-
152
- def token_exist_in_store?(meta,flow_identifier)
153
- @store_provider.token_exist?(
154
- token_identifier: meta['token_identifier'],
155
- authenticated_identifier: meta['authenticated_identifier'],
156
- token_issue_time: meta['token_issue_time'],
157
- token_expiry_time: meta['token_expiry_time'],
158
- flow_identifier: flow_identifier)
159
- end
160
-
161
- def rejection_result(reason:)
162
- [false, nil, reason]
163
- end
164
-
165
- def success_result(token_meta:)
166
- [true, token_meta, "Valid token for <#{token_meta['authenticated_identifier']}>" ]
25
+ raise "'provider' must be configured" unless @configuration['provider']
167
26
  end
168
27
  end
169
28
  end
@@ -1,3 +1,3 @@
1
1
  module SoarAuthenticationToken
2
- VERSION = '3.0.9'
2
+ VERSION = '4.0.0'
3
3
  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
- 'mode' => 'remote',
12
+ 'provider' => '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
- 'mode' => 'local',
25
+ 'provider' => 'JwtTokenGenerator',
26
26
  'private_key' => private_key,
27
27
  'public_key' => public_key
28
28
  }
@@ -52,8 +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
- 'mode' => 'remote',
56
- 'generator-url' => 'http://authentication-token-generator-service:9393/generate',
55
+ 'provider' => 'RemoteTokenValidator',
57
56
  'validator-url' => 'http://authentication-token-validator-service:9393/validate'
58
57
  }
59
58
  @iut = SoarAuthenticationToken::RackMiddleware.new(@test_app, @iut_configuration)
@@ -9,19 +9,22 @@ describe SoarAuthenticationToken::TokenGenerator do
9
9
 
10
10
  before :each do
11
11
  @generator_configuration_local = {
12
- 'mode' => 'local',
12
+ 'provider' => 'JwtTokenGenerator',
13
13
  'private_key' => @private_key
14
14
  }
15
15
  @validator_configuration_local = {
16
- 'mode' => 'local',
16
+ 'provider' => 'JwtTokenValidator',
17
17
  'public_key' => @public_key
18
18
  }
19
- @configuration_remote = {
20
- 'mode' => 'remote',
19
+ @configuration_remote_generator = {
20
+ 'provider' => 'RemoteTokenGenerator',
21
21
  'generator-url' => 'http://authentication-token-generator-service:9393/generate',
22
- 'validator-url' => 'http://authentication-token-validator-service:9393/validate',
23
22
  'generator-client-auth-token' => 'test_ecosystem_token_for_auth_token_aaapi_authenticator_service'
24
23
  }
24
+ @configuration_remote_validator = {
25
+ 'provider' => 'RemoteTokenValidator',
26
+ 'validator-url' => 'http://authentication-token-validator-service:9393/validate',
27
+ }
25
28
 
26
29
  @test_store = AuthTokenStoreProvider::StubClient.new
27
30
  end
@@ -52,12 +55,12 @@ describe SoarAuthenticationToken::TokenGenerator do
52
55
 
53
56
  context "when generating a new token remotely" do
54
57
  it 'should request the token from the configured remote service' do
55
- @iut = SoarAuthenticationToken::TokenGenerator.new(@configuration_remote)
58
+ @iut = SoarAuthenticationToken::TokenGenerator.new(@configuration_remote_generator)
56
59
  @iut.inject_store_provider(@test_store)
57
60
 
58
61
  token, token_generator_meta = @iut.generate(authenticated_identifier: @test_authenticated_identifier, flow_identifier: 'test-flow-id')
59
62
 
60
- @validator = SoarAuthenticationToken::TokenValidator.new(@configuration_remote)
63
+ @validator = SoarAuthenticationToken::TokenValidator.new(@configuration_remote_validator)
61
64
  @iut.inject_store_provider(@test_store)
62
65
  token_validity, token_validator_meta, messages = @validator.validate(authentication_token: token, flow_identifier: 'test-flow-id')
63
66
 
@@ -10,11 +10,11 @@ describe SoarAuthenticationToken::TokenValidator do
10
10
  @invalid_private_key, @invalid_public_key = keypair_generator.generate
11
11
  @test_identifier = 'a@b.co.za'
12
12
  @local_valid_generator_configuration = {
13
- 'mode' => 'local',
13
+ 'provider' => 'JwtTokenGenerator',
14
14
  'private_key' => @valid_private_key
15
15
  }
16
16
  @local_invalid_generator_configuration = {
17
- 'mode' => 'local',
17
+ 'provider' => 'JwtTokenGenerator',
18
18
  'private_key' => @invalid_private_key
19
19
  }
20
20
 
@@ -25,7 +25,7 @@ describe SoarAuthenticationToken::TokenValidator do
25
25
 
26
26
  current_time = Time.now
27
27
  @static_validator_configuration = {
28
- 'mode' => 'static',
28
+ 'provider' => 'StaticTokenValidator',
29
29
  'static_tokens' => [
30
30
  {
31
31
  'token' => 'some_secret_token_string_1111',
@@ -48,16 +48,16 @@ describe SoarAuthenticationToken::TokenValidator do
48
48
  ]
49
49
  }
50
50
  @local_validator_configuration = {
51
- 'mode' => 'local',
51
+ 'provider' => 'JwtTokenValidator',
52
52
  'public_key' => @valid_public_key
53
53
  }
54
54
  @remote_generator_configuration = {
55
- 'mode' => 'remote',
55
+ 'provider' => 'RemoteTokenGenerator',
56
56
  'generator-url' => 'http://authentication-token-generator-service:9393/generate',
57
57
  'generator-client-auth-token' => 'test_ecosystem_token_for_auth_token_aaapi_authenticator_service'
58
58
  }
59
59
  @remote_validator_configuration = {
60
- 'mode' => 'remote',
60
+ 'provider' => 'RemoteTokenValidator',
61
61
  'validator-url' => 'http://authentication-token-validator-service:9393/validate',
62
62
  'generator-client-auth-token' => 'test_ecosystem_token_for_auth_token_aaapi_authenticator_service'
63
63
  }
@@ -97,7 +97,7 @@ describe SoarAuthenticationToken::TokenValidator do
97
97
  it 'indicate token is valid' do
98
98
  expect(token_validity).to eq true
99
99
  end
100
-
100
+
101
101
  it 'provide the token meta' do
102
102
  expect(token_meta['authenticated_identifier']).to eq @test_identifier
103
103
  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.9
4
+ version: 4.0.0
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-31 00:00:00.000000000 Z
11
+ date: 2017-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: soar_xt
@@ -223,6 +223,11 @@ files:
223
223
  - docker-compose.yml
224
224
  - lib/soar_authentication_token.rb
225
225
  - lib/soar_authentication_token/keypair_generator.rb
226
+ - lib/soar_authentication_token/providers/jwt_token_generator.rb
227
+ - lib/soar_authentication_token/providers/jwt_token_validator.rb
228
+ - lib/soar_authentication_token/providers/remote_token_generator.rb
229
+ - lib/soar_authentication_token/providers/remote_token_validator.rb
230
+ - lib/soar_authentication_token/providers/static_token_validator.rb
226
231
  - lib/soar_authentication_token/rack_middleware.rb
227
232
  - lib/soar_authentication_token/token_generator.rb
228
233
  - lib/soar_authentication_token/token_validator.rb