soar_authentication_token 0.0.3 → 0.0.4

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: 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