soar_pl 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -2
- data/lib/soar_pl/authorization_policy.rb +86 -65
- data/lib/soar_pl/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2b08e955891c93d88edb864c6699ac241c236d6
|
4
|
+
data.tar.gz: 0d2615ff9cf68374b1f865aa0246264d33859c9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b94c726c2dd3507818944dd314973446edd3a04d9af421a167c5b8d6f1c63ea34eb16ae25ef809a4dd6efcfce4719f1a9d4b4153e8962f30374c445daebabec
|
7
|
+
data.tar.gz: 487f45e98d1da42adf7aa04d1c7d82c5e1f9cc251eb229dd18b639f60eda1e2749ee9e6f8e62d23e2160096865565225effd4931e87410818369b44b6441baa9
|
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
SoarPl is a generic authorization policy implementation. It was designed for use in the SOAR architecture. Extend the AuthorizationPolicy class with your own, and provide the needed IDM interaction and rule set for interrogating subject_identifier, requestor_identifier, resource_identifier and request in order to make an authorization decision. The policy communicates the result using jsend.
|
4
4
|
|
5
|
+
The policy can be given a subject identifier, representing the subject to be authorized. Optionally, the resource identifier of the resource the subject wants to access can be supplied, as well as details of the request itself. Additionally, the identifier of the requestor asking the authorization question of the policy can also be supplied. This information in conjunction with the subject's roles and attributes as discovered from the (optional) IDM provided, form the full information set that the rule set can use to make the authorization decision.
|
6
|
+
|
5
7
|
## Installation
|
6
8
|
|
7
9
|
Add this line to your application's Gemfile:
|
@@ -20,7 +22,8 @@ Or install it yourself as:
|
|
20
22
|
spec.add_development_dependency 'soar_pl'
|
21
23
|
bundle exec irb
|
22
24
|
|
23
|
-
|
25
|
+
In the examples that follow, @iut refers to 'implementation under test' a.k.a 'item under test'
|
26
|
+
Extend the {SoarPl::AuthorizationPolicy AuthorizationPolicy} class to create a policy. Examples can be found here: https://github.hetzner.co.za/hetznerZA/soar_policies/tree/master/production
|
24
27
|
|
25
28
|
The implementation reaches out to the IDM provided and retrieves entity roles and attributes.
|
26
29
|
The IDM provided must adhere to the following API:
|
@@ -53,7 +56,7 @@ Optionally, require roles to be present for an entity that you identify with a s
|
|
53
56
|
|
54
57
|
If requiring roles, you must provide an IDM to retrieve the entity's roles, and the attributes for each role, from:
|
55
58
|
|
56
|
-
@iut.
|
59
|
+
@iut.use_idm(@idm_instance)
|
57
60
|
|
58
61
|
Check authorization for a subject identifier, (optionally) providing it with all your rule set (MyRules) needs to make the authorization decision:
|
59
62
|
|
@@ -93,3 +96,8 @@ Bug reports and feature requests are welcome by email to ernst dot van dot graan
|
|
93
96
|
|
94
97
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
95
98
|
|
99
|
+
## TODO
|
100
|
+
|
101
|
+
- The IDM API will be published in a future gem, along with a SOAR reference identity registry implementation
|
102
|
+
- Examples are currently in a private repo, this will be made public as the use matures
|
103
|
+
- Helper methods for interrogating status may be added (e.g. valid?, configuration_valid?, allowed?, denied?, etc.)
|
@@ -17,89 +17,36 @@ module SoarPl
|
|
17
17
|
attr_accessor :request_debug_allow
|
18
18
|
|
19
19
|
def initialize(policy_identifier, policy_configuration)
|
20
|
-
data = { 'dependencies' =>
|
21
|
-
{ 'configuration' => 'invalid',
|
22
|
-
'policy_identifier' => 'invalid',
|
23
|
-
'rule_set' => 'invalid' } }
|
24
20
|
@roles = []
|
25
21
|
@policy_identifier = policy_identifier
|
26
22
|
@configuration = policy_configuration
|
27
|
-
|
28
|
-
valid_policy_identifier = valid_non_empty_string?(policy_identifier)
|
29
|
-
valid_configuration = @configuration.is_a?(Hash)
|
30
|
-
#byebug
|
31
|
-
valid_rule_set = (self.class.name != 'SoarPl::AuthorizationPolicy')
|
32
|
-
|
33
|
-
if (policy_identifier.nil?)
|
34
|
-
@status = fail('no identifier provided')
|
35
|
-
elsif (not valid_policy_identifier)
|
36
|
-
@status = fail('invalid identifier provided')
|
37
|
-
elsif policy_configuration.nil?
|
38
|
-
@status = fail('no configuration provided')
|
39
|
-
elsif not valid_configuration
|
40
|
-
@status = fail('invalid configuration provided', data)
|
41
|
-
elsif not valid_rule_set
|
42
|
-
# Must extend this class and provide a rule set in apply_rule_set(...)
|
43
|
-
@status = fail('invalid rule set provided')
|
44
|
-
else
|
45
|
-
@status = success_data(data)
|
46
|
-
end
|
47
|
-
data['dependencies']['configuration'] = (valid_configuration ? 'valid' : 'invalid')
|
48
|
-
data['dependencies']['rule_set'] = (valid_rule_set ? 'valid' : 'invalid')
|
49
|
-
data['dependencies']['policy_identifier'] = (valid_policy_identifier ? 'valid' : 'invalid')
|
23
|
+
validate_bootstrap(policy_identifier, policy_configuration)
|
50
24
|
setup
|
51
25
|
end
|
52
26
|
|
53
27
|
def requires_roles(roles)
|
54
28
|
roles = [roles] if not roles.is_a?(Array)
|
55
|
-
|
56
29
|
@roles = roles
|
57
30
|
end
|
58
31
|
|
59
|
-
def
|
32
|
+
def use_idm(idm)
|
60
33
|
@idm = idm
|
61
34
|
end
|
62
35
|
|
63
36
|
def authorize(subject_identifier, requestor_identifier, resource_identifier, request)
|
64
|
-
|
65
|
-
if
|
66
|
-
|
67
|
-
begin
|
68
|
-
requested = JSON.parse(request) if not request.is_a?(Hash)
|
69
|
-
rescue => ex
|
70
|
-
return fail("Invalid request", build_result(false, "Invalid request", @idm))
|
71
|
-
end
|
72
|
-
end
|
73
|
-
return fail_invalid("resource identifier") if resource_identifier and (not valid_non_empty_string?(resource_identifier))
|
74
|
-
return fail("Invalid request", build_result(false, "Invalid request", @idm)) if requested and (not requested.is_a?(Hash))
|
75
|
-
return fail_invalid("requestor identifier") if requestor_identifier and (not valid_non_empty_string?(requestor_identifier))
|
76
|
-
return fail_invalid("subject identifier") if subject_identifier and (not valid_non_empty_string?(subject_identifier))
|
37
|
+
result = translate_request(request)
|
38
|
+
return result if result['status'] == 'fail'
|
39
|
+
requested = result['data']['requested']
|
77
40
|
|
78
|
-
|
79
|
-
|
80
|
-
begin
|
81
|
-
subject_roles = discover_subject_roles(subject_identifier) if @idm
|
82
|
-
rescue => ex
|
83
|
-
error = 'Entity error (IDM)'
|
84
|
-
return fail(error, build_result(false, error, @idm))
|
85
|
-
end
|
86
|
-
|
87
|
-
return success_data(build_result(false, "Role missing", @idm)) if not roles_present?(subject_roles, @roles)
|
88
|
-
|
89
|
-
begin
|
90
|
-
attributes = discover_subject_role_attributes(subject_identifier, subject_roles) if @idm
|
91
|
-
rescue => ex
|
92
|
-
error = 'Entity error (IDM)'
|
93
|
-
return fail(error, build_result(false, error, @idm))
|
94
|
-
end
|
41
|
+
validation_error = validate_authorization(subject_identifier, requestor_identifier, resource_identifier, requested)
|
42
|
+
return validation_error if validation_error
|
95
43
|
|
96
|
-
result
|
44
|
+
result = discover_entity(subject_identifier)
|
45
|
+
error = result['data']['notifications'].first
|
46
|
+
return fail(error, build_result(false, error, @idm)) if result['status'] == 'fail'
|
97
47
|
|
98
|
-
|
99
|
-
|
100
|
-
else
|
101
|
-
success_data(build_result(false, message, @idm))
|
102
|
-
end
|
48
|
+
result, message = apply_rule_set(subject_identifier, requestor_identifier, resource_identifier, requested, result['data']['subject_roles'], result['data']['attributes'])
|
49
|
+
build_response(result, message)
|
103
50
|
end
|
104
51
|
|
105
52
|
protected
|
@@ -111,6 +58,16 @@ module SoarPl
|
|
111
58
|
# override me
|
112
59
|
end
|
113
60
|
|
61
|
+
def discover_entity(subject_identifier)
|
62
|
+
subject_roles = discover_subject_roles(subject_identifier) if @idm
|
63
|
+
return fail("Role missing") if not roles_present?(subject_roles, @roles)
|
64
|
+
attributes = discover_subject_role_attributes(subject_identifier, subject_roles) if @idm
|
65
|
+
success_data( { 'subject_roles' => subject_roles, 'attributes' => attributes } )
|
66
|
+
|
67
|
+
rescue => ex
|
68
|
+
return fail('Entity error (IDM)')
|
69
|
+
end
|
70
|
+
|
114
71
|
def discover_subject_roles(subject_identifier)
|
115
72
|
subject_roles = @idm.get_roles(subject_identifier)
|
116
73
|
end
|
@@ -125,6 +82,70 @@ module SoarPl
|
|
125
82
|
|
126
83
|
private
|
127
84
|
|
85
|
+
def validate_bootstrap(policy_identifier, policy_configuration)
|
86
|
+
valid_policy_identifier = valid_non_empty_string?(policy_identifier)
|
87
|
+
valid_configuration = @configuration.is_a?(Hash)
|
88
|
+
valid_rule_set = (self.class.name != 'SoarPl::AuthorizationPolicy')
|
89
|
+
set_bootstrap_status(policy_identifier, policy_configuration, valid_configuration, valid_rule_set, valid_policy_identifier)
|
90
|
+
|
91
|
+
return valid_configuration, valid_rule_set, valid_policy_identifier
|
92
|
+
end
|
93
|
+
|
94
|
+
def data_invalidated
|
95
|
+
{ 'dependencies' =>
|
96
|
+
{ 'configuration' => 'invalid',
|
97
|
+
'policy_identifier' => 'invalid',
|
98
|
+
'rule_set' => 'invalid' } }
|
99
|
+
end
|
100
|
+
|
101
|
+
def set_bootstrap_status(policy_identifier, policy_configuration, valid_configuration, valid_rule_set, valid_policy_identifier)
|
102
|
+
data = data_invalidated
|
103
|
+
|
104
|
+
if (policy_identifier.nil?)
|
105
|
+
@status = fail('no identifier provided')
|
106
|
+
elsif (not valid_policy_identifier)
|
107
|
+
@status = fail('invalid identifier provided')
|
108
|
+
elsif policy_configuration.nil?
|
109
|
+
@status = fail('no configuration provided')
|
110
|
+
elsif not valid_configuration
|
111
|
+
@status = fail('invalid configuration provided', data)
|
112
|
+
elsif not valid_rule_set
|
113
|
+
# Must extend this class and provide a rule set in apply_rule_set(...)
|
114
|
+
@status = fail('invalid rule set provided')
|
115
|
+
else
|
116
|
+
@status = success_data(data)
|
117
|
+
end
|
118
|
+
|
119
|
+
data['dependencies']['configuration'] = (valid_configuration ? 'valid' : 'invalid')
|
120
|
+
data['dependencies']['rule_set'] = (valid_rule_set ? 'valid' : 'invalid')
|
121
|
+
data['dependencies']['policy_identifier'] = (valid_policy_identifier ? 'valid' : 'invalid')
|
122
|
+
end
|
123
|
+
|
124
|
+
def translate_request(request)
|
125
|
+
requested = {}
|
126
|
+
if request
|
127
|
+
requested = request
|
128
|
+
begin
|
129
|
+
requested = JSON.parse(request) if not request.is_a?(Hash)
|
130
|
+
rescue => ex
|
131
|
+
return fail("Invalid request", build_result(false, "Invalid request", @idm))
|
132
|
+
end
|
133
|
+
end
|
134
|
+
success_data({'requested' => requested})
|
135
|
+
end
|
136
|
+
|
137
|
+
def validate_authorization(subject_identifier, requestor_identifier, resource_identifier, requested)
|
138
|
+
return fail_invalid("resource identifier") if resource_identifier and (not valid_non_empty_string?(resource_identifier))
|
139
|
+
return fail("Invalid request", build_result(false, "Invalid request", @idm)) if requested and (not requested.is_a?(Hash))
|
140
|
+
return fail_invalid("requestor identifier") if requestor_identifier and (not valid_non_empty_string?(requestor_identifier))
|
141
|
+
return fail_invalid("subject identifier") if subject_identifier and (not valid_non_empty_string?(subject_identifier))
|
142
|
+
end
|
143
|
+
|
144
|
+
def build_response(result, message)
|
145
|
+
return success_data(build_result(true, message, @idm)) if result
|
146
|
+
success_data(build_result(false, message, @idm))
|
147
|
+
end
|
148
|
+
|
128
149
|
def fail_invalid(description)
|
129
150
|
fail("Invalid #{description}", build_result(false, "Invalid #{description}", @idm))
|
130
151
|
end
|
data/lib/soar_pl/version.rb
CHANGED