soar_authentication_token 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 30c0c39a09540212db058ce833ebcd693e2defd2
4
- data.tar.gz: cc20f98a7bdaa653cd2fe437946d6646b268bd78
3
+ metadata.gz: 0f96b19d4ab690a6bfdd43d38c9c16795dbef257
4
+ data.tar.gz: a91497c50df87f8adaaa76c6929e33a8f3afd9fe
5
5
  SHA512:
6
- metadata.gz: b76a8b2663458cd8b417c391e73b2467c31d8cbca6b2d8b5ab343f65641e698e9e2e25b0e65cdccef98f2270eddcfb712bc5ad101c0b69bead28f49f72c24eed
7
- data.tar.gz: 1b9804a8de2dc0dc9ea7a84e6f9c3641d84a0a3b62b8ae94e7087813da8b49f04e9a5da910fb2860f093ce86048f2e5c4edefdb39a6afe70b6370de7d433bbee
6
+ metadata.gz: 0230c1bbd09ba5887a6be2908824dc5d441f28da8e9128c4da1240f967a5d2588df2f42d3b94cb984abb9375a507ab5f312f36f5e6b624d9938c595084cd1db3
7
+ data.tar.gz: e8dc6ceb4f1d612f40bc62b64c912a7b5085e77fe49edbeea3a40b29c0cbd209bb3b82da67d7805d50ef9d97bbaab8c7ff61bcfb04a53cbe866b731edafa677c
data/README.md CHANGED
@@ -23,10 +23,9 @@ Or install it yourself as:
23
23
 
24
24
  ## Testing
25
25
 
26
- Run the rspec test tests:
27
-
28
- $ docker-compose run --rm soar-authentication-token bundle exec rspec -cfd spec
26
+ Run the rspec test tests using docker compose:
29
27
 
28
+ $ docker-compose run soar-authentication-token /bin/bash -c 'sleep 10; bundle exec rspec -cfd spec'
30
29
 
31
30
  ## Usage
32
31
 
@@ -4,4 +4,27 @@ services:
4
4
  build: .
5
5
  image: soar-authentication-token
6
6
  volumes:
7
- - .:/usr/local/src/
7
+ - .:/usr/local/src/
8
+ links:
9
+ - authentication-token-generator-service
10
+ - authentication-token-validator-service
11
+ authentication-token-generator-service:
12
+ build: ../authentication-token-generator-service
13
+ image: authentication-token-generator-service
14
+ expose:
15
+ - "9393"
16
+ volumes:
17
+ - ../authentication-token-generator-service:/usr/local/src/
18
+ environment:
19
+ - RACK_ENV=development
20
+ - ENVIRONMENT_FILE=environment_local.yml
21
+ authentication-token-validator-service:
22
+ build: ../authentication-token-validator-service
23
+ image: authentication-token-validator-service
24
+ expose:
25
+ - "9393"
26
+ volumes:
27
+ - ../authentication-token-validator-service:/usr/local/src/
28
+ environment:
29
+ - RACK_ENV=development
30
+ - ENVIRONMENT_FILE=environment_local.yml
@@ -4,4 +4,5 @@ end
4
4
  require 'soar_authentication_token/keypair_generator'
5
5
  require 'soar_authentication_token/token_generator'
6
6
  require 'soar_authentication_token/token_validator'
7
+ require 'soar_authentication_token/rack_middleware'
7
8
  require 'soar_authentication_token/version'
@@ -0,0 +1,97 @@
1
+ require 'browser'
2
+ require 'cgi'
3
+ require 'net/http'
4
+ require 'nokogiri'
5
+ require 'rack/request'
6
+ require 'uri'
7
+
8
+ module SoarAuthenticationToken
9
+
10
+ class RackMiddleware
11
+
12
+ def initialize(app, config)
13
+ @app = app
14
+ @config = config
15
+ end
16
+
17
+ def call(env)
18
+ request = Rack::Request.new env
19
+ if @config[:browsers_only] and !is_browser?(request)
20
+ @app.call env
21
+ else
22
+ session, params = request.session, request.params
23
+ if session['user']
24
+ @app.call env
25
+ elsif params['logoutRequest']
26
+ callback(:on_logout, env, st_from_logoutrequest(params['logoutRequest']))
27
+ [200, {}, ['']]
28
+ elsif user = signon(request)
29
+ session['user'] = user
30
+ callback(:on_login, env, request['ticket'])
31
+ @app.call env
32
+ else
33
+ service = get_service request
34
+ [302, {'Location' => signon_url(service)}, ['']]
35
+ end
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def callback(on_what, env, service_ticket)
42
+ @config[on_what].call(env, service_ticket) if(@config[on_what])
43
+ end
44
+
45
+ def st_from_logoutrequest(logout_request)
46
+ document = Nokogiri::XML::Document.parse(logout_request)
47
+ document.xpath('/samlp:LogoutRequest/samlp:SessionIndex').text
48
+ end
49
+
50
+
51
+ def is_browser?(request)
52
+ ua = request.user_agent
53
+ Browser.new(ua: ua).id != :other
54
+ end
55
+
56
+ def signon(request)
57
+ ticket = request.params['ticket']
58
+ service = get_service(request)
59
+ if ticket and service
60
+ redeem_service_ticket(ticket, service)
61
+ end
62
+ end
63
+
64
+ def get_service(request)
65
+ url = request.url
66
+ url.gsub!(/\&ticket=ST-\w+/, '')
67
+ url.gsub!(/\?ticket=ST-\w+/, '?')
68
+ url.chomp('?')
69
+ end
70
+
71
+ def redeem_service_ticket(ticket, service)
72
+ uri = URI.parse validation_url(ticket, service)
73
+ req = Net::HTTP::Get.new(uri.to_s)
74
+ verify_mode = @config[:ignore_certificate] ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
75
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => (uri.scheme == 'https'), :verify_mode => verify_mode) do |http|
76
+ response = http.request(req)
77
+ if response.code.to_i == 200
78
+ response_xml = Nokogiri::XML::Document.parse(response.body)
79
+ user_element = response_xml.xpath('/cas:serviceResponse/cas:authenticationSuccess/cas:user')
80
+ unless user_element.empty?
81
+ user_element.inner_text
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def signon_url(service = '')
88
+ "#{@config[:prefix]}/login?service=#{CGI.escape service}"
89
+ end
90
+
91
+ def validation_url(ticket, service)
92
+ "#{@config[:prefix]}/serviceValidate?ticket=#{ticket}&service=#{CGI.escape service}"
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -1,12 +1,14 @@
1
1
  require 'soar_xt'
2
2
  require 'jwt'
3
3
  require 'securerandom'
4
+ require 'time'
5
+ require 'net/http'
6
+ require 'uri'
7
+ require 'json'
4
8
 
5
9
  module SoarAuthenticationToken
6
10
  class TokenGenerator
7
11
  DEFAULT_CONFIGURATION = {
8
- 'mode' => 'remote',
9
- 'url' => ''
10
12
  } unless defined? DEFAULT_CONFIGURATION; DEFAULT_CONFIGURATION.freeze
11
13
 
12
14
  def initialize(configuration)
@@ -15,12 +17,38 @@ module SoarAuthenticationToken
15
17
  @private_key = OpenSSL::PKey::EC.new(@configuration['private_key'])
16
18
  end
17
19
 
18
- def generate(authenticated_identifier:)
19
- encode(payload(authenticated_identifier))
20
+ def generate(authenticated_identifier:, flow_identifier: nil)
21
+ return generate_locally(authenticated_identifier) if 'local' == @configuration['mode']
22
+ return generate_remotely(authenticated_identifier,flow_identifier)
20
23
  end
21
24
 
22
25
  private
23
26
 
27
+ def generate_locally(authenticated_identifier)
28
+ encode(payload(authenticated_identifier))
29
+ end
30
+
31
+ def generate_remotely(authenticated_identifier,flow_identifier)
32
+ uri = URI.parse(@configuration['generator-url'])
33
+
34
+ request = Net::HTTP::Post.new uri
35
+ request.set_form_data({'flow_identifier' => flow_identifier})
36
+ request.body = { 'authenticated_identifier' => authenticated_identifier }.to_json
37
+ response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') { |http|
38
+ http.request request
39
+ }
40
+
41
+ validate_and_extract_token_from_response(response)
42
+ end
43
+
44
+ def validate_and_extract_token_from_response(response)
45
+ raise "Failure generating token with token generation service. Code #{response.code}" if '200' != response.code
46
+ body = JSON.parse(response.body)
47
+ raise 'Failure generating token by token service' if 'success' != body['status']
48
+ raise 'Token service did not provide token' if body['data'].nil? or body['data']['token'].nil?
49
+ body['data']['token']
50
+ end
51
+
24
52
  def payload(authenticated_identifier)
25
53
  { 'authenticated_identifier' => authenticated_identifier,
26
54
  'issue_time' => Time.now.utc.iso8601(3),
@@ -35,7 +63,11 @@ module SoarAuthenticationToken
35
63
  def validate_configuration
36
64
  raise "'mode' must be configured" unless @configuration['mode']
37
65
  raise "'mode' must be configured as either 'local' or 'remote'" unless ['local','remote'].include?(@configuration['mode'])
38
-
66
+ if 'remote' == @configuration['mode']
67
+ raise "'generator-url' must be configured in remote mode" if @configuration['generator-url'].nil?
68
+ else
69
+ raise "'private_key' must be configured in local mode" unless @configuration['private_key']
70
+ end
39
71
  end
40
72
 
41
73
  def merge_with_default_configuration(configuration)
@@ -4,10 +4,7 @@ require 'jwt'
4
4
  module SoarAuthenticationToken
5
5
  class TokenValidator
6
6
  DEFAULT_CONFIGURATION = {
7
- 'mode' => 'local',
8
- 'expiry' => 604800, #a day in seconds
9
- 'public_key' => '',
10
- 'url' => ''
7
+ 'expiry' => 604800 #a days worth of seconds
11
8
  } unless defined? DEFAULT_CONFIGURATION; DEFAULT_CONFIGURATION.freeze
12
9
 
13
10
  def initialize(configuration)
@@ -17,9 +14,9 @@ module SoarAuthenticationToken
17
14
  @public_key.private_key = nil
18
15
  end
19
16
 
20
- def validate(authentication_token)
17
+ def validate(authentication_token:,flow_identifier: nil)
21
18
  return validate_locally(authentication_token) if 'local' == @configuration['mode']
22
- return validate_remotely(authentication_token)
19
+ return validate_remotely(authentication_token,flow_identifier)
23
20
  end
24
21
 
25
22
  private
@@ -32,15 +29,42 @@ module SoarAuthenticationToken
32
29
  [false, nil]
33
30
  end
34
31
 
35
- def validate_remotely(authentication_token)
36
- [true, 'uuid']
32
+ def validate_remotely(authentication_token,flow_identifier)
33
+ uri = URI.parse(@configuration['validator-url'])
34
+
35
+ request = Net::HTTP::Post.new uri
36
+ request.set_form_data({'flow_identifier' => flow_identifier})
37
+ request.body = { 'authentication_token' => authentication_token }.to_json
38
+ response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') { |http|
39
+ http.request request
40
+ }
41
+
42
+ validate_and_extract_information_from_response(response)
43
+ end
44
+
45
+ def validate_and_extract_information_from_response(response)
46
+ raise "Failure validating token with token validation service. Code #{response.code} received" if '200' != response.code
47
+ body = JSON.parse(response.body)
48
+ if 'success' == body['status']
49
+ raise 'Token validation service did not provide authenticated_identifier' if body['data'].nil? or body['data']['authenticated_identifier'].nil?
50
+ return [true, body['data']['authenticated_identifier']]
51
+ end
52
+ if 'fail' == body['status']
53
+ return [false, nil]
54
+ end
55
+ raise "Failure validating token with token validation service. Status '#{body['status']}' received"
37
56
  end
38
57
 
39
58
  def validate_configuration
40
59
  raise "'mode' must be configured" unless @configuration['mode']
41
60
  raise "'mode' must be configured as either 'local' or 'remote'" unless ['local','remote'].include?(@configuration['mode'])
42
- raise "'expiry' must be configured" unless @configuration['expiry']
43
- raise "'expiry' must be an integer" unless Integer(@configuration['expiry'])
61
+ if 'remote' == @configuration['mode']
62
+ raise "'validator-url' must be configured in remote mode" unless @configuration['validator-url']
63
+ else
64
+ raise "'public_key' must be configured in local mode" unless @configuration['public_key']
65
+ raise "'expiry' must be configured in local mode" unless @configuration['expiry']
66
+ raise "'expiry' must be an integer" unless Integer(@configuration['expiry'])
67
+ end
44
68
  end
45
69
 
46
70
  def merge_with_default_configuration(configuration)
@@ -1,3 +1,3 @@
1
1
  module SoarAuthenticationToken
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4'
3
3
  end
@@ -4,49 +4,59 @@ describe SoarAuthenticationToken::TokenGenerator do
4
4
  before :all do
5
5
  keypair_generator = SoarAuthenticationToken::KeypairGenerator.new
6
6
  @private_key, @public_key = keypair_generator.generate
7
+ @test_authenticated_identifier = 'a@b.co.za'
7
8
  end
8
9
 
9
10
  before :each do
10
- generator_configuration = {
11
+ @generator_configuration_local = {
11
12
  'mode' => 'local',
12
13
  'private_key' => @private_key
13
14
  }
14
- validator_configuration = {
15
+ @validator_configuration_local = {
15
16
  'mode' => 'local',
16
17
  'public_key' => @public_key
17
18
  }
18
- @iut = SoarAuthenticationToken::TokenGenerator.new(generator_configuration)
19
- @validator = SoarAuthenticationToken::TokenValidator.new(validator_configuration)
19
+ @configuration_remote = {
20
+ 'mode' => 'remote',
21
+ 'generator-url' => 'http://authentication-token-generator-service:9393/generate',
22
+ 'validator-url' => 'http://authentication-token-validator-service:9393/validate'
23
+ }
20
24
  end
21
25
 
22
26
  after :each do
23
27
  end
24
28
 
29
+ after :all do
30
+ end
31
+
25
32
  it 'has a version number' do
26
33
  expect(SoarAuthenticationToken::VERSION).not_to be nil
27
34
  end
28
35
 
29
36
  context "when generating a new token locally" do
30
37
  it 'should provide token using configured private key' do
38
+ @iut = SoarAuthenticationToken::TokenGenerator.new(@generator_configuration_local)
39
+ @validator = SoarAuthenticationToken::TokenValidator.new(@validator_configuration_local)
31
40
 
32
- #binding.pry
33
- token = @iut.generate(authenticated_identifier: 'a@b.co.za')
34
- print @validator.validate(token)
35
- print @validator.validate("asdfasdf")
36
- end
37
- end
41
+ token = @iut.generate(authenticated_identifier: @test_authenticated_identifier, flow_identifier: 'test-flow-id')
42
+ token_validity, token_identifier = @validator.validate(authentication_token: token, flow_identifier: 'test-flow-id')
38
43
 
39
- context "when generating a new token locally" do
40
- it 'should provide token using the configured private key' do
41
- #TODO
42
- #expect(true).to eq false
44
+ expect(token_validity).to eq(true)
45
+ expect(token_identifier).to eq(@test_authenticated_identifier)
43
46
  end
44
47
  end
45
48
 
46
49
  context "when generating a new token remotely" do
47
50
  it 'should provide token using the configured remote service' do
48
- #TODO
49
- #expect(true).to eq false
51
+ @iut = SoarAuthenticationToken::TokenGenerator.new(@configuration_remote)
52
+
53
+ token = @iut.generate(authenticated_identifier: @test_authenticated_identifier, flow_identifier: 'test-flow-id')
54
+
55
+ @validator = SoarAuthenticationToken::TokenValidator.new(@configuration_remote)
56
+ token_validity, token_identifier = @validator.validate(authentication_token: token, flow_identifier: 'test-flow-id')
57
+
58
+ expect(token_validity).to eq(true)
59
+ expect(token_identifier).to eq(@test_authenticated_identifier)
50
60
  end
51
61
  end
52
62
  end
@@ -7,24 +7,34 @@ describe SoarAuthenticationToken::TokenValidator do
7
7
  @valid_private_key, @valid_public_key = keypair_generator.generate
8
8
  @invalid_private_key, @invalid_public_key = keypair_generator.generate
9
9
  @test_identifier = 'a@b.co.za'
10
- @valid_generator_configuration = {
10
+ @local_valid_generator_configuration = {
11
11
  'mode' => 'local',
12
12
  'private_key' => @valid_private_key
13
13
  }
14
- @invalid_generator_configuration = {
14
+ @local_invalid_generator_configuration = {
15
15
  'mode' => 'local',
16
16
  'private_key' => @invalid_private_key
17
17
  }
18
- @validator_configuration = {
18
+ @local_validator_configuration = {
19
19
  'mode' => 'local',
20
20
  'public_key' => @valid_public_key
21
21
  }
22
- @valid_generator = SoarAuthenticationToken::TokenGenerator.new(@valid_generator_configuration)
23
- @invalid_generator = SoarAuthenticationToken::TokenGenerator.new(@invalid_generator_configuration)
22
+ @remote_generator_configuration = {
23
+ 'mode' => 'remote',
24
+ 'generator-url' => 'http://authentication-token-generator-service:9393/generate',
25
+ }
26
+ @remote_validator_configuration = {
27
+ 'mode' => 'remote',
28
+ 'validator-url' => 'http://authentication-token-validator-service:9393/validate'
29
+ }
30
+ @local_valid_generator = SoarAuthenticationToken::TokenGenerator.new(@local_valid_generator_configuration)
31
+ @local_invalid_generator = SoarAuthenticationToken::TokenGenerator.new(@local_invalid_generator_configuration)
32
+ @remote_generator = SoarAuthenticationToken::TokenGenerator.new(@remote_generator_configuration)
24
33
  end
25
34
 
26
35
  before :each do
27
- @iut = SoarAuthenticationToken::TokenValidator.new(@validator_configuration)
36
+ @iut_local = SoarAuthenticationToken::TokenValidator.new(@local_validator_configuration)
37
+ @iut_remote = SoarAuthenticationToken::TokenValidator.new(@remote_validator_configuration)
28
38
  end
29
39
 
30
40
  after :each do
@@ -36,26 +46,26 @@ describe SoarAuthenticationToken::TokenValidator do
36
46
 
37
47
  context "when validating a token locally using the configured public key" do
38
48
  it 'should indicate valid if the token is valid' do
39
- token = @valid_generator.generate(authenticated_identifier: @test_identifier)
40
- token_validity, token_identifier = @iut.validate(token)
49
+ token = @local_valid_generator.generate(authenticated_identifier: @test_identifier)
50
+ token_validity, token_identifier = @iut_local.validate(authentication_token: token)
41
51
  expect(token_validity).to eq true
42
52
  end
43
53
 
44
54
  it 'should indicate invalid if the token is invalid' do
45
- token = @invalid_generator.generate(authenticated_identifier: @test_identifier)
46
- token_validity, token_identifier = @iut.validate(token)
55
+ token = @local_invalid_generator.generate(authenticated_identifier: @test_identifier)
56
+ token_validity, token_identifier = @iut_local.validate(authentication_token: token)
47
57
  expect(token_validity).to eq false
48
58
  end
49
59
 
50
60
  it 'should provide the authenticated_identifier if the token is valid' do
51
- token = @valid_generator.generate(authenticated_identifier: @test_identifier)
52
- token_validity, token_identifier = @iut.validate(token)
61
+ token = @local_valid_generator.generate(authenticated_identifier: @test_identifier)
62
+ token_validity, token_identifier = @iut_local.validate(authentication_token: token)
53
63
  expect(token_identifier).to eq @test_identifier
54
64
  end
55
65
 
56
66
  it 'should not provide the authenticated_identifier if the token is invalid' do
57
- token = @invalid_generator.generate(authenticated_identifier: @test_identifier)
58
- token_validity, token_identifier = @iut.validate(token)
67
+ token = @local_invalid_generator.generate(authenticated_identifier: @test_identifier)
68
+ token_validity, token_identifier = @iut_local.validate(authentication_token: token)
59
69
  expect(token_identifier).to eq nil
60
70
  end
61
71
 
@@ -71,6 +81,38 @@ describe SoarAuthenticationToken::TokenValidator do
71
81
  end
72
82
 
73
83
  context "when validating a token remotely using the configured url" do
74
- #TODO
84
+ it 'should indicate valid if the token is valid' do
85
+ token = @remote_generator.generate(authenticated_identifier: @test_identifier)
86
+ token_validity, token_identifier = @iut_remote.validate(authentication_token: token)
87
+ expect(token_validity).to eq true
88
+ end
89
+
90
+ it 'should indicate invalid if the token is invalid' do
91
+ token = @local_invalid_generator.generate(authenticated_identifier: @test_identifier)
92
+ token_validity, token_identifier = @iut_remote.validate(authentication_token: token)
93
+ expect(token_validity).to eq false
94
+ end
95
+
96
+ it 'should provide the authenticated_identifier if the token is valid' do
97
+ token = @remote_generator.generate(authenticated_identifier: @test_identifier)
98
+ token_validity, token_identifier = @iut_remote.validate(authentication_token: token)
99
+ expect(token_identifier).to eq @test_identifier
100
+ end
101
+
102
+ it 'should not provide the authenticated_identifier if the token is invalid' do
103
+ token = @local_invalid_generator.generate(authenticated_identifier: @test_identifier)
104
+ token_validity, token_identifier = @iut_remote.validate(authentication_token: token)
105
+ expect(token_identifier).to eq nil
106
+ end
107
+
108
+ it 'should indicate as invalid tokens that are older than the configured expiry time' do
109
+ #TODO
110
+ #expect(true).to eq false
111
+ end
112
+
113
+ it 'should indicate as valid tokens that are not older than the configured expiry time' do
114
+ #TODO
115
+ #expect(true).to eq false
116
+ end
75
117
  end
76
118
  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: 0.0.3
4
+ version: 0.0.4
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: 2016-11-29 00:00:00.000000000 Z
11
+ date: 2016-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: soar_xt
@@ -125,6 +125,7 @@ files:
125
125
  - docker-compose.yml
126
126
  - lib/soar_authentication_token.rb
127
127
  - lib/soar_authentication_token/keypair_generator.rb
128
+ - lib/soar_authentication_token/rack_middleware.rb
128
129
  - lib/soar_authentication_token/token_generator.rb
129
130
  - lib/soar_authentication_token/token_validator.rb
130
131
  - lib/soar_authentication_token/version.rb