soar-authorization-access_manager 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b67e5c55ac918c54ee269e0fad8f91d732fa78e4
4
+ data.tar.gz: d6819a19cff228a33b4771f5e83307622a74dc98
5
+ SHA512:
6
+ metadata.gz: 8bb4ecd171455518a6025e5bf452038c0c3c5038e1c6ac6eedaca92d5c29981f655af968b0a541a91b1524e50632be0781e4b9afcfd337a8279ea175c621a827
7
+ data.tar.gz: ffb1cccb2c81bc668caf3569d4d81840e7ecb427e5ca59a61372b353c3e0f496c548f554184336843c8110e907b50ac26c9c1c865e6ee4c921955c79dafff437
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "soar-authorization-access_manager"
7
+ spec.version = "0.0.1"
8
+ spec.authors = ["Ernst Van Graan", "Charles Mulder"]
9
+ spec.email = ["ernst.van.graan@hetzner.co.za", "charles.mulder@hetzner.co.za"]
10
+
11
+ spec.summary = %q{Access Manager that uses policy services to determine authorization}
12
+ spec.description = %q{Access Manager that uses policy services to determine authorization}
13
+ spec.homepage = "https://github.com/hetznerZA/soar_policy_access_manager"
14
+ spec.license = "MIT"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ # if spec.respond_to?(:metadata)
19
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
20
+ # else
21
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
22
+ # end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_dependency 'soar_sr', "~> 1.1.24"
30
+ spec.add_dependency "jsender", "~> 0.2.0"
31
+
32
+ end
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .byebug_history
11
+ *.gem
12
+ *.swp
13
+ *.swo
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+ --require spec_helper
@@ -0,0 +1 @@
1
+ soar-authorization-access_manager
@@ -0,0 +1 @@
1
+ ruby-2.3.0
@@ -0,0 +1,6 @@
1
+ FROM ruby:2.3.1
2
+ WORKDIR /usr/local/src
3
+ RUN gem install jsender rack
4
+ ADD config.ru /usr/local/src/
5
+ CMD rackup -E production ./config.ru -p 8080 --host 0.0.0.0
6
+
@@ -0,0 +1,14 @@
1
+ FROM ruby:2.3.1
2
+ ARG USER_ID
3
+ ARG USER_NAME
4
+ RUN useradd -u $USER_ID $USER_NAME -m
5
+ RUN gem install bundler
6
+ RUN chown -R $USER_NAME:$USER_NAME /usr/local
7
+ USER $USER_NAME
8
+ WORKDIR /usr/local/src
9
+ ADD Gemfile /usr/local/src/
10
+ ADD soar-policy-access_manager.gemspec /usr/local/src/
11
+ RUN bundle install --without development --with test
12
+ VOLUME /usr/local/src
13
+ CMD bundle exec cucumber && bundle exec rspec
14
+
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in soar_policy_access_manager.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rspec', '~> 3.5'
8
+ gem 'rspec-expectations', '~> 3.5'
9
+ gem 'cucumber', '~> 2.4'
10
+ end
11
+
12
+
13
+
@@ -0,0 +1,183 @@
1
+ # Soar::Authorization::AccessManager
2
+
3
+ This Access Manager adheres to SoarAm::AmApi. It is initialized with a soar_sr service registy client (https://rubygems.org/gems/soar_sr)
4
+
5
+ This access manager denies access for unauthenticated requests, that is, request that do not have request.session['user'] set. If set, this access manager then queries the service registry for meta regarding the service identifier in question.
6
+
7
+ If the service meta indicates no policy, the request is allowed. It the service meta indicates a policy, the policy service is asked, given the authenticated subject identifier, service identifier, resource identifier and request parameters, whether the request should be allowed. This access manager then allows / denies accordingly.
8
+
9
+ In the case of the service not found in the service registry, or a failure of any kind, the request is denied. In the interest of security, exceptions while authorizing will be swallowed by this access manager. Only the last exception message will be reported to STDERR.
10
+
11
+ ## Quickstart
12
+ When using soar_sc the [soar_authorization](https://github.com/hetznerZA/soar_authorization) gem automagically passes values set in [your router](https://github.com/hetznerZA/soar_sc_routing) as post parameters in authorization requests to policies.
13
+
14
+ SoarSc Routing params | SoarAuthorization params | AccessManager Params | Policy Params
15
+ ----------------------|---------------------------|---------------------------|-----------------------
16
+ service_name | | service_identifier | requestor_identifier
17
+ path | | resource_identifier | resource_identifier
18
+ | authentication.identifier | authentication_identifier | subject_identifier
19
+
20
+ ### Stub provider
21
+ > Use this provider during tests and development when you don't want any http policy authorization requests.
22
+
23
+ Create meta variable mapping services to policy identifiers (names)
24
+ ```ruby
25
+ > meta = {
26
+ 'service_identifier1' => {
27
+ 'policy' => 'policy1'
28
+ }
29
+ }
30
+ ```
31
+ Create policies variable mapping policy identifiers to resources. Access to a resource is allowed if your authentication identifier (uuid) is present in the array.
32
+ ```ruby
33
+ > policies = {
34
+ 'policy1' => {
35
+ '/resource_identifier1' => ['authentication_identifier1'],
36
+ '/resource_identifier2' => []
37
+ }
38
+ }
39
+ ```
40
+ Instantiate the stub provider and pass it to the access manager model.
41
+ ```ruby
42
+ > provider = Soar::Authorization::AccessManager::Provider::Stub.new(meta: meta, policies: policies})
43
+ > access_manager = Soar::Authorization::AccessManager.new(provider)
44
+ > puts access_manager.authorized?({
45
+ service_identifier: 'service_identifier1',
46
+ resource_identifier: '/resource_identifier1',
47
+ request: {
48
+ params: {},
49
+ authentication_identifier: 'authentication_identifier1'
50
+ }
51
+ })
52
+ ```
53
+
54
+ ### Policy provider
55
+ > Use this provider during tests and development to send authorization requests directly to a policy.
56
+
57
+ Create meta variable mapping services to policy identifiers (names)
58
+ ```ruby
59
+ > meta = {
60
+ 'service_identifier1' => 'policy1',
61
+ 'service_identifier2' => 'policy2'
62
+ }
63
+ ```
64
+ Create policies variable mapping policy identifiers to policy endpoints.
65
+ ```ruby
66
+ > policies = {
67
+ 'policy1' => 'http://localhost:8080/allow',
68
+ 'policy2' => 'http://localhost:8080/deny'
69
+ }
70
+ ```
71
+
72
+ Instantiate the stub provider and pass it to the access manager model.
73
+ ```ruby
74
+ > provider = Soar::Authorization::AccessManager::Provider::Policy.new(meta: meta, policies: policies})
75
+ > access_manager = Soar::Authorization::AccessManager.new(provider)
76
+ > puts access_manager.authorized?({
77
+ service_identifier: 'service_identifier1',
78
+ resource_identifier: '',
79
+ request: {
80
+ params: {},
81
+ authentication_identifier: ''
82
+ }
83
+ })
84
+ ```
85
+
86
+ ### Service registry provider
87
+ > Use this provider during production. It reaches out to the service registry and policies. It's used by soar sc.
88
+
89
+ ```ruby
90
+ > service_registry = SoarSc::service_registry
91
+ > provider = Soar::Authorization::AccessManager::Provider::ServiceRegistry.new(service_registry)
92
+ > access_manager = Soar::Authorization::AccessManager.new(provider)
93
+ > puts access_manager.authorized?({
94
+ service_identifier: 'service_identifier1',
95
+ resource_identifier: '/path',
96
+ request: {
97
+ params: request.params,
98
+ authentication_identifier: SoarAuthentication::Authentication.new(request).identifier
99
+ }
100
+ })
101
+ ```
102
+
103
+ This access manager can be used with the SoarAuthorization::Authorize middleware:
104
+
105
+ ```ruby
106
+ > SoarAuthorization::Authorize::register_access_manager('/path', 'path-service', access_manager)
107
+ > use SoarAuthorization::Authorize
108
+ ```
109
+
110
+ ## Tests
111
+
112
+ ### Local
113
+
114
+ #### Unit Tests
115
+
116
+ ```bash
117
+ $ bundle exec rspec
118
+ ```
119
+
120
+ #### Provider Integration Tests
121
+
122
+ ##### Stub provider
123
+ ```bash
124
+ $ TEST_ORCHESTRATION_PROVIDER=Stub bundle exec cucumber
125
+ ```
126
+
127
+ ##### Service Registry provider
128
+
129
+ Serve two policies, *allow all* and *deny all*, locally via file *config.ru* using [Rack webserver](https://rack.github.io/).
130
+ ```bash
131
+ $ docker-compose up
132
+ ```
133
+
134
+ Test providers for the stub and service registry via the model api interface
135
+ ```bash
136
+ $ TEST_ORCHESTRATION_PROVIDER=ServiceRegistry bundle exec cucumber
137
+ ```
138
+
139
+ ##### Policy provider
140
+
141
+ Serve two policies, *allow all* and *deny all*, locally via file *config.ru* using [Rack webserver](https://rack.github.io/).
142
+ ```bash
143
+ $ docker-compose up
144
+ ```
145
+
146
+ Test providers for the stub and service registry via the model api interface
147
+ ```bash
148
+ $ TEST_ORCHESTRATION_PROVIDER=Policy bundle exec cucumber
149
+ ```
150
+
151
+ ### CI
152
+ ```
153
+ $ USER_NAME=$USER USER_ID=$(id -u) POLICY_HOST=policy:8080 TEST_ORCHESTRATION_PROVIDER=ServiceRegistry docker-compose --file docker-compose.test.yml up --abort-on-container-exit --remove-orphans
154
+ EXIT_CODE=$(docker ps -a -f "name=soar-policy-access_manager-tests" -q | xargs docker inspect -f "{{ .State.ExitCode }}")
155
+ exit $EXIT_CODE
156
+ ```
157
+
158
+ #### Enviroment Variables explained
159
+ Sets USER_NAME and USER_ID to your currently logged in user. This is important when you're mounting volumes into a container. Root in the container = root on your local.
160
+ ```bash
161
+ USER_NAME=$USER
162
+ USER_ID=$(id -u)
163
+ ```
164
+
165
+ Select whether you're testing the *ServiceRegistry* , *Policy* or *Stub* provider. Defaults to *Stub*
166
+ ```bash
167
+ TEST_ORCHESTRATION_PROVIDER=Stub
168
+ ```
169
+
170
+ ## Contributing
171
+
172
+ Please send feedback and comments to the author at:
173
+
174
+ Ernst van Graan <ernst.van.graan@hetzner.co.za>
175
+
176
+ This gem is sponsored by Hetzner (Pty) Ltd - http://hetzner.co.za
177
+
178
+ ## License
179
+
180
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
181
+
182
+ ## Resources
183
+ * [soar_am](https://github.com/hetznerZA/soar_am)
@@ -0,0 +1,37 @@
1
+ require 'rack'
2
+ require 'rack/server'
3
+ require 'json'
4
+
5
+ ##
6
+ # Simulates two policies available on the network. One allows all requests, the other, denies all requests.
7
+ # Used for testing the access manager model with both the stub and service registry providers
8
+ ##
9
+ class StubPolicies
10
+
11
+ def self.call(env)
12
+ request = Rack::Request.new env
13
+ case request.path
14
+ when '/allow'
15
+ [200, {"Context-Type" => "application/json"}, [{ 'status' => 'success', 'data' => { 'allowed' => true, 'detail' => '', 'idm' => nil, 'rule_set' => 'allow all', 'notifications' => ['Policy approved authorization request'] }}.to_json]]
16
+ when '/deny'
17
+ [200, {"Context-Type" => "application/json"}, [{ 'status' => 'success', 'data' => { 'allowed' => false, 'detail' => '', 'idm' => nil, 'rule_set' => 'allow all', 'notifications' => ['Policy rejected authorization request'] }}.to_json]]
18
+ else
19
+ [200, {"Context-Type" => "application/json"}, [{ 'status' => 'success', 'data' => { 'notifications' => ['No policy associated with service'] }}.to_json]]
20
+ end
21
+
22
+ end
23
+ end
24
+
25
+ class PrintPolicies
26
+ def self.call(env)
27
+ request = Rack::Request.new env
28
+ [200, {"Context-Type" => "application/json"}, [{
29
+ "is a post request?" => request.post?,
30
+ "request path" => request.path,
31
+ "request params" => request.params
32
+ }.to_json]]
33
+ end
34
+ end
35
+
36
+ Rack::Server.start :app => StubPolicies
37
+ #Rack::Server.start :app => PrintPolicies
@@ -0,0 +1,30 @@
1
+ version: "2"
2
+ services:
3
+ policy:
4
+ build:
5
+ context: .
6
+ dockerfile: Dockerfile.policies
7
+ image: soar-policy-access_manager-policy
8
+ container_name: soar-policy-access_manager-policy
9
+ expose:
10
+ - "8080"
11
+
12
+ tests:
13
+ build:
14
+ context: .
15
+ dockerfile: Dockerfile.tests
16
+ args:
17
+ - USER_NAME
18
+ - USER_ID
19
+ image: soar-policy-access_manager-tests
20
+ container_name: soar-policy-access_manager-tests
21
+ volumes:
22
+ - .:/usr/local/src
23
+ links:
24
+ - policy
25
+ environment:
26
+ - TEST_ORCHESTRATION_PROVIDER=Stub
27
+ - POLICY_HOST=http://policy:8080
28
+
29
+
30
+
@@ -0,0 +1,14 @@
1
+ version: "2"
2
+ services:
3
+ stub_policy:
4
+ build:
5
+ context: .
6
+ dockerfile: Dockerfile.policies
7
+ ports:
8
+ - "8080:8080"
9
+ expose:
10
+ - "8080"
11
+ network_mode: "host"
12
+
13
+
14
+
@@ -0,0 +1,11 @@
1
+ require 'soar/authorization/access_manager/model'
2
+ module Soar
3
+ module Authorization
4
+ module AccessManager
5
+
6
+ def self.new(provider)
7
+ Soar::Authorization::AccessManager::Model.new(provider)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+
2
+ module Soar
3
+ module Authorization
4
+ module AccessManager
5
+ module Error
6
+ class PolicyAccessManagerException < StandardError; end;
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ require 'soar/authorization/access_manager/provider/stub'
2
+ require 'soar/authorization/access_manager/provider/policy'
3
+ require 'soar/authorization/access_manager/provider/service_registry'
4
+
5
+ module Soar
6
+ module Authorization
7
+ module AccessManager
8
+ class Model
9
+
10
+ def initialize(provider)
11
+ @provider = provider
12
+ end
13
+
14
+ ##
15
+ # @param service_identifier [String]
16
+ # @param resource_identifier [String]
17
+ # @param request [Hash]
18
+ ##
19
+ def authorized?(service_identifier: nil, resource_identifier: nil, request: nil)
20
+ @provider.authorized?(service_identifier, resource_identifier, request)
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,94 @@
1
+ require 'jsender'
2
+ require 'json'
3
+ require 'soar/authorization/access_manager/error'
4
+
5
+ module Soar
6
+ module Authorization
7
+ module AccessManager
8
+ module Provider
9
+
10
+ class Policy
11
+
12
+ include Jsender
13
+
14
+ def initialize(meta: {}, policies: {})
15
+ @meta = meta
16
+ @policies = policies
17
+ end
18
+
19
+ def authorized?(service_identifier, resource_identifier, request)
20
+ notifications = []
21
+ decision = false
22
+
23
+ begin
24
+ if ENV['RACK_ENV'] == 'development'
25
+ notifications << 'Authorized in development environment'
26
+ decision = true
27
+ end
28
+
29
+ policy = get_policy(service_identifier)
30
+
31
+ if policy.nil?
32
+ decision = true
33
+ notifications << 'No policy associated with service'
34
+ else
35
+ decision, detail = ask_policy(policy, request[:authentication_identifier], service_identifier, resource_identifier, request)
36
+ notifications.concat(detail) if not detail.empty?
37
+ notifications << 'Policy rejected authorization request' if not decision
38
+ notifications << 'Policy approved authorization request' if decision
39
+ end
40
+ rescue SoarSr::ValidationError => ex
41
+ notifications << "AccessManager error authorizing #{service_identifier} for #{resource_identifier}: #{ex.message}"
42
+ decision = false
43
+ rescue Exception => ex
44
+ notifications << "AccessManager error authorizing #{service_identifier} for #{resource_identifier}: #{ex.message}"
45
+ decision = false
46
+ end
47
+
48
+ success(notifications, { 'approved' => decision } )
49
+ end
50
+
51
+ private
52
+
53
+ def get_policy(service_identifier)
54
+ @meta[service_identifier]
55
+ end
56
+
57
+ def ask_policy(policy, subject_identifier, service_identifier, resource_identifier, request)
58
+ notifications = []
59
+ uri = find_uri(policy)
60
+ if uri.nil?
61
+ notifications << "Could not retrieve policy for service"
62
+ return false, notifications
63
+ end
64
+ url = URI.parse(uri)
65
+ params = {
66
+ 'resource_identifier' => resource_identifier,
67
+ 'subject_identifier' => subject_identifier,
68
+ 'service_identifier' => service_identifier,
69
+ 'request' => {
70
+ 'params' => request[:params]
71
+ },
72
+ 'flow_identifier' => request[:params]['flow_identifier']
73
+ }
74
+ res = Net::HTTP.post_form(url, params)
75
+ result = JSON.parse(res.body)
76
+ if result['status'] == 'error'
77
+ notifications << 'Policy query result was not success'
78
+ return false, notifications
79
+ end
80
+ return result['data']['allowed'], notifications
81
+ rescue => ex
82
+ notifications << "Exception while asking policy #{ex.message}"
83
+ return false, notifications
84
+ end
85
+
86
+ def find_uri(policy)
87
+ @policies[policy]
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+
@@ -0,0 +1,99 @@
1
+ require 'jsender'
2
+ require "soar_sr"
3
+ require 'soar/authorization/access_manager/error'
4
+
5
+ module Soar
6
+ module Authorization
7
+ module AccessManager
8
+ module Provider
9
+
10
+ class ServiceRegistry
11
+
12
+ include Jsender
13
+ attr_reader :service_registry
14
+
15
+ def initialize(service_registry)
16
+ @service_registry = service_registry
17
+ end
18
+
19
+ def authorized?(service_identifier, resource_identifier, request)
20
+ notifications = []
21
+ decision = false
22
+
23
+ begin
24
+ if ENV['RACK_ENV'] == 'development'
25
+ notifications << 'Authorized in development environment'
26
+ decision = true
27
+ end
28
+
29
+ meta = @service_registry.services.meta_for_service(service_identifier)
30
+ policy = meta['policy'] if meta and meta.is_a?(Hash) and meta['policy']
31
+
32
+ if policy.nil?
33
+ decision = true
34
+ notifications << 'No policy associated with service'
35
+ else
36
+ decision, detail = ask_policy(policy, request[:authentication_identifier], service_identifier, resource_identifier, request)
37
+ notifications.concat(detail) if not detail.empty?
38
+ notifications << 'Policy rejected authorization request' if not decision
39
+ notifications << 'Policy approved authorization request' if decision
40
+ end
41
+ rescue SoarSr::ValidationError => ex
42
+ notifications << "AccessManager error authorizing #{service_identifier} for #{resource_identifier}: #{ex.message}"
43
+ decision = false
44
+ rescue Exception => ex
45
+ notifications << "AccessManager error authorizing #{service_identifier} for #{resource_identifier}: #{ex.message}"
46
+ decision = false
47
+ end
48
+
49
+ success(notifications, { 'approved' => decision } )
50
+ end
51
+
52
+ private
53
+
54
+ def ask_policy(policy, subject_identifier, service_identifier, resource_identifier, request)
55
+ notifications = []
56
+ uri = find_first_uri(policy)
57
+ if uri.nil?
58
+ notifications << "Could not retrieve policy for service"
59
+ return false, notifications
60
+ end
61
+ url = URI.parse(uri)
62
+ params = {
63
+ 'resource_identifier' => resource_identifier,
64
+ 'subject_identifier' => subject_identifier,
65
+ 'service_identifier' => service_identifier,
66
+ 'request' => {
67
+ 'params' => request[:params],
68
+ },
69
+ 'flow_identifier' => request[:params]['flow_identifier']
70
+ }
71
+ res = Net::HTTP.post_form(url, params)
72
+
73
+ result = JSON.parse(res.body)
74
+ if result['status'] == 'error'
75
+ notifications << 'Policy query result was not success'
76
+ return false, notifications
77
+ end
78
+ return result['data']['allowed'], notifications
79
+ rescue => ex
80
+ notifications << "Exception while asking policy #{ex.message}"
81
+ return false, notifications
82
+ end
83
+
84
+ def find_first_uri(policy)
85
+ result = @service_registry.services.service_by_name(policy)
86
+ return nil if not result['status'] == 'success'
87
+ return nil if result['data']['services'].nil? or result['data']['services'].first.nil?
88
+ service = result['data']['services'].first
89
+ return nil if service[1].nil? or service[1]['uris'].nil?
90
+ access = service[1]['uris'].first
91
+ return nil if access.nil? or access[1].nil? or access[1]['access_point'].nil?
92
+ access[1]['access_point']
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+
@@ -0,0 +1,95 @@
1
+ require 'jsender'
2
+
3
+ module Soar
4
+ module Authorization
5
+ module AccessManager
6
+ module Provider
7
+ class Stub
8
+
9
+ include Jsender
10
+
11
+ ##
12
+ # @param [Hash] meta mapping service identifiers to policy identifiers
13
+ # @param [Hash] policies, policy identifiers map to resource identifiers, that map to an array of authentication_identifiers that are allowed access
14
+ ##
15
+ def initialize(meta: {}, policies: {})
16
+ @meta = meta
17
+ @policies = policies
18
+ end
19
+
20
+ ##
21
+ # @param [String] service_identifier
22
+ # @param [String] resource_identifier
23
+ # @param [Hash] request
24
+ # @return [Hash] a jsend hash
25
+ ##
26
+ def authorized?(service_identifier, resource_identifier, request)
27
+ notifications = []
28
+ decision = false
29
+
30
+ begin
31
+ if ENV['RACK_ENV'] == 'development'
32
+ notifications << 'Authorized in development environment'
33
+ decision = true
34
+ end
35
+
36
+ meta = get_meta(service_identifier)
37
+ policy = meta['policy'] if meta and meta.is_a?(Hash) and meta['policy']
38
+
39
+ if policy.nil?
40
+ decision = true
41
+ notifications << 'No policy associated with service'
42
+ else
43
+ decision, detail = ask_policy(policy, request[:authentication_identifier], service_identifier, resource_identifier, request)
44
+ notifications.concat(detail) if not detail.empty?
45
+ notifications << 'Policy rejected authorization request' if not decision
46
+ notifications << 'Policy approved authorization request' if decision
47
+ end
48
+ rescue SoarSr::ValidationError => ex
49
+ notifications << "AccessManager error authorizing #{service_identifier} for #{resource_identifier}: #{ex.message}"
50
+ decision = false
51
+ rescue Exception => ex
52
+ notifications << "AccessManager error authorizing #{service_identifier} for #{resource_identifier}: #{ex.message}"
53
+ decision = false
54
+ end
55
+ success(notifications, { 'approved' => decision } )
56
+ end
57
+
58
+ private
59
+
60
+ ##
61
+ # @param [String] service identifier
62
+ # @return [Hash, nil] policy hash or nil
63
+ ##
64
+ def get_meta(service_identifier)
65
+ @meta[service_identifier]
66
+ end
67
+
68
+ ##
69
+ # @param [String] policy
70
+ # @param [String] authentication_identifier
71
+ # @param [String] service_identifier
72
+ # @param [String] resource_identifier
73
+ # @param [Hash] request
74
+ # @return [Bool] result
75
+ # @return [Array] notifications
76
+ ##
77
+ def ask_policy(policy, authentication_identifier, service_identifier, resource_identifier, params)
78
+ notifications = []
79
+ result = @policies[policy][resource_identifier].include?(authentication_identifier)
80
+
81
+ if not result
82
+ notifications << 'Policy query result was not success'
83
+ return false, notifications
84
+ end
85
+ return result, notifications
86
+ rescue => ex
87
+ notifications << "Exception while asking policy #{ex.message}"
88
+ return false, notifications
89
+ end
90
+
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,67 @@
1
+ require 'soar/authorization/access_manager'
2
+ require 'jsender'
3
+ require 'uri'
4
+ require 'ostruct'
5
+
6
+ module Soar
7
+ module Authorization
8
+ module AccessManager
9
+ module Test
10
+ module OrchestrationProvider
11
+ class Policy
12
+
13
+ ##
14
+ # for a specific path and service
15
+ # I want to tell the access manager to query a specific url and paramaters for a policy
16
+ # service_identifier = service_name in router
17
+ # resource_identifier = path in router
18
+ ##
19
+ def initialize
20
+ policy_host = ENV['POLICY_HOST'] || 'localhost:8080'
21
+ @meta = {
22
+ 'service_identifier1' => 'allow_policy',
23
+ 'service_identifier2' => 'deny_policy'
24
+ }
25
+ @policies = {
26
+ 'allow_policy' => "http://#{policy_host}/allow",
27
+ 'deny_policy' => "http://#{policy_host}/deny"
28
+ }
29
+ @request = {
30
+ authentication_identifier: 'authentication_identifier1',
31
+ params: {},
32
+ }
33
+ @resource_identifier = 'resource_identifier1'
34
+ end
35
+
36
+ def grant_access
37
+ @service_identifier = 'service_identifier1'
38
+ end
39
+
40
+ def deny_access
41
+ @service_identifier = 'service_identifier2'
42
+ end
43
+
44
+ def no_policy
45
+ @service_identifier = 'service_identifier3'
46
+ end
47
+
48
+ def notification
49
+ @response['data']['notifications']
50
+ end
51
+
52
+ def authorized?
53
+ provider = Soar::Authorization::AccessManager::Provider::Policy.new(meta: @meta, policies: @policies )
54
+ model = Soar::Authorization::AccessManager.new(provider)
55
+ @response = model.authorized?(service_identifier: @service_identifier, resource_identifier: @resource_identifier, request: @request)
56
+ end
57
+
58
+ def authorized
59
+ @response['data']['approved']
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,102 @@
1
+ require 'soar/authorization/access_manager/model'
2
+ require 'jsender'
3
+ require 'uri'
4
+ require 'ostruct'
5
+
6
+ module Soar
7
+ module Authorization
8
+ module AccessManager
9
+ module Test
10
+ module OrchestrationProvider
11
+ class ServiceRegistry
12
+
13
+ class Services
14
+ include Jsender
15
+
16
+ def initialize()
17
+ @policy_host = ENV['POLICY_HOST'] || 'localhost:8080'
18
+ @meta = {
19
+ 'service_identifier1' => {
20
+ 'policy' => 'allow'
21
+ },
22
+ 'service_identifier2' => {
23
+ 'policy' => 'deny'
24
+ }
25
+ }
26
+ end
27
+
28
+ def service_by_name(policy)
29
+ case policy
30
+ when 'allow' # allow policy
31
+ success_data({
32
+ "services" => [[{}, { "uris" => [[{}, { "access_point" => "http://#{@policy_host}/allow" }]]}]]
33
+ })
34
+ when 'deny' # deny policy
35
+ success_data({
36
+ "services" => [[{}, { "uris" => [[{}, { "access_point" => "http://#{@policy_host}/deny" }]]}]]
37
+ })
38
+ else # no policy
39
+ success_data({
40
+ "services" => [[{}, { "uris" => [[{}, { "access_point" => "http://#{@policy_host}/" }]]}]]
41
+ })
42
+ end
43
+ end
44
+
45
+ def meta_for_service(service_identifier)
46
+ @meta[service_identifier]
47
+ end
48
+
49
+ end
50
+
51
+ class Stub
52
+ include Jsender
53
+
54
+ attr_accessor :services
55
+
56
+ def initialize(services)
57
+ @services = services
58
+ end
59
+
60
+ end
61
+
62
+ def initialize
63
+ @resource_identifier = 'resource_identifier1'
64
+ @request = {
65
+ params: {},
66
+ authentication_identifier: 'authentication_identifier1'
67
+ }
68
+ end
69
+
70
+ def grant_access
71
+ @service_identifier = 'service_identifier1'
72
+ end
73
+
74
+ def deny_access
75
+ @service_identifier = 'service_identifier2'
76
+ end
77
+
78
+ def no_policy
79
+ @service_identifier = 'service_identifier3'
80
+ end
81
+
82
+ def notification
83
+ @response['data']['notifications']
84
+ end
85
+
86
+ def authorized?
87
+ service_registry = Stub.new(Services.new)
88
+ model_provider = Soar::Authorization::AccessManager::Provider::ServiceRegistry.new(service_registry)
89
+ model = Soar::Authorization::AccessManager.new(model_provider)
90
+ @response = model.authorized?(service_identifier: @service_identifier, resource_identifier: @resource_identifier, request: @request)
91
+ end
92
+
93
+ def authorized
94
+ @response['data']['approved']
95
+ end
96
+
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,63 @@
1
+ require 'securerandom'
2
+ require 'soar/authorization/access_manager'
3
+
4
+ module Soar
5
+ module Authorization
6
+ module AccessManager
7
+ module Test
8
+ module OrchestrationProvider
9
+ class Stub
10
+
11
+ def initialize
12
+ @meta = {
13
+ 'service_identifier1' => {
14
+ 'policy' => 'policy1'
15
+ }
16
+ }
17
+ @policies = {
18
+ 'policy1' => {
19
+ 'resource_identifier1' => ['authentication_identifier1'],
20
+ 'resource_identifier2' => []
21
+ }
22
+ }
23
+ @request = {
24
+ params: {},
25
+ authentication_identifier: 'authentication_identifier1'
26
+ }
27
+ end
28
+
29
+ def grant_access
30
+ @service_identifier = 'service_identifier1'
31
+ @resource_identifier = 'resource_identifier1'
32
+ end
33
+
34
+ def deny_access
35
+ @service_identifier = 'service_identifier1'
36
+ @resource_identifier = 'resource_identifier2'
37
+ end
38
+
39
+ def no_policy
40
+ @service_identifier = 'service_identifier2'
41
+ @resource_identifier = nil
42
+ end
43
+
44
+ def notification
45
+ @response['data']['notifications']
46
+ end
47
+
48
+ def authorized?
49
+ model_provider = Soar::Authorization::AccessManager::Provider::Stub.new(meta: @meta, policies: @policies)
50
+ model = Soar::Authorization::AccessManager.new(model_provider)
51
+ @response = model.authorized?(service_identifier: @service_identifier, resource_identifier: @resource_identifier, request: @request)
52
+ end
53
+
54
+ def authorized
55
+ @response['data']['approved']
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,40 @@
1
+ module Soar
2
+ module Authorization
3
+ module AccessManager
4
+ module Test
5
+ class Orchestrator
6
+
7
+ def initialize(orchestration_provider)
8
+ @orchestration_provider = orchestration_provider
9
+ end
10
+
11
+ def grant_access
12
+ @orchestration_provider.grant_access
13
+ end
14
+
15
+ def deny_access
16
+ @orchestration_provider.deny_access
17
+ end
18
+
19
+ def no_policy
20
+ @orchestration_provider.no_policy
21
+ end
22
+
23
+ def notification
24
+ @orchestration_provider.notification
25
+ end
26
+
27
+ def authorized?
28
+ @orchestration_provider.authorized?
29
+ end
30
+
31
+ def authorized
32
+ @orchestration_provider.authorized
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: soar-authorization-access_manager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ernst Van Graan
8
+ - Charles Mulder
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2016-12-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: soar_sr
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 1.1.24
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 1.1.24
28
+ - !ruby/object:Gem::Dependency
29
+ name: jsender
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 0.2.0
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 0.2.0
42
+ description: Access Manager that uses policy services to determine authorization
43
+ email:
44
+ - ernst.van.graan@hetzner.co.za
45
+ - charles.mulder@hetzner.co.za
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".gemspec"
51
+ - ".gitignore"
52
+ - ".rspec"
53
+ - ".ruby-gemset"
54
+ - ".ruby-version"
55
+ - Dockerfile.policies
56
+ - Dockerfile.tests
57
+ - Gemfile
58
+ - README.md
59
+ - config.ru
60
+ - docker-compose.test.yml
61
+ - docker-compose.yml
62
+ - lib/soar/authorization/access_manager.rb
63
+ - lib/soar/authorization/access_manager/error.rb
64
+ - lib/soar/authorization/access_manager/model.rb
65
+ - lib/soar/authorization/access_manager/provider/policy.rb
66
+ - lib/soar/authorization/access_manager/provider/service_registry.rb
67
+ - lib/soar/authorization/access_manager/provider/stub.rb
68
+ - lib/soar/authorization/access_manager/test/orchestration_provider/policy.rb
69
+ - lib/soar/authorization/access_manager/test/orchestration_provider/service_registry.rb
70
+ - lib/soar/authorization/access_manager/test/orchestration_provider/stub.rb
71
+ - lib/soar/authorization/access_manager/test/orchestrator.rb
72
+ homepage: https://github.com/hetznerZA/soar_policy_access_manager
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.5.1
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Access Manager that uses policy services to determine authorization
96
+ test_files: []