manageiq-api-common 1.1.0 → 2.0.0
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 +4 -4
- data/lib/manageiq/api/common.rb +0 -1
- data/lib/manageiq/api/common/application_controller_mixins/exception_handling.rb +41 -0
- data/lib/manageiq/api/common/application_controller_mixins/request_body_validation.rb +1 -14
- data/lib/manageiq/api/common/application_controller_mixins/request_path.rb +0 -5
- data/lib/manageiq/api/common/open_api/generator.rb +67 -7
- data/lib/manageiq/api/common/rbac/access.rb +66 -0
- data/lib/manageiq/api/common/rbac/acl.rb +74 -0
- data/lib/manageiq/api/common/rbac/policies.rb +33 -0
- data/lib/manageiq/api/common/rbac/query_shared_resource.rb +45 -0
- data/lib/manageiq/api/common/rbac/roles.rb +77 -0
- data/lib/manageiq/api/common/rbac/seed.rb +140 -0
- data/lib/manageiq/api/common/rbac/service.rb +67 -0
- data/lib/manageiq/api/common/rbac/share_resource.rb +60 -0
- data/lib/manageiq/api/common/rbac/unshare_resource.rb +32 -0
- data/lib/manageiq/api/common/rbac/utilities.rb +30 -0
- data/lib/manageiq/api/common/request.rb +3 -0
- data/lib/manageiq/api/common/version.rb +1 -1
- data/spec/support/rbac_shared_contexts.rb +44 -0
- data/spec/support/service_spec_helper.rb +26 -0
- metadata +31 -5
- data/lib/manageiq/api/common/api_error.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf558ff2ddd7a2cbed5ffca066b2f769523d4fdd3b835d9497c280d97838f81b
|
4
|
+
data.tar.gz: 160883b30284ec6b53f0c14c43b66413ac9b9ab75a87766ebc91bdfb15b4c658
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 108cba357408d2e2889321672976053c8a2d45a0b2f189ad5c6534b6d1d9d0f30e9637681c6e45751b157acc38061f93cf805c08c5ecba2b367284e639061f44
|
7
|
+
data.tar.gz: 30f9ab415d4b6d949b1ae62d40ee22a53f0f0ec5c5a938da3cec4a98b926b472e95e33531cf7cb7f3f914d9ba3ca204916175ee1ca7a1dc4c8ac18f0dfa826b5
|
data/lib/manageiq/api/common.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
module ManageIQ
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module ApplicationControllerMixins
|
5
|
+
module ExceptionHandling
|
6
|
+
DEFAULT_ERROR_CODE = 400
|
7
|
+
|
8
|
+
def self.included(other)
|
9
|
+
other.rescue_from(StandardError, RuntimeError) do |exception|
|
10
|
+
errors = ManageIQ::API::Common::ErrorDocument.new.tap do |error_document|
|
11
|
+
exception_list_from(exception).each do |exc|
|
12
|
+
code = exc.respond_to?(:code) ? exc.code : error_code_from_class(exc)
|
13
|
+
error_document.add(code, "#{exc.class}: #{exc.message}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
render :json => errors.to_h, :status => error_code_from_class(exception)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def exception_list_from(exception)
|
22
|
+
[].tap do |arr|
|
23
|
+
until exception.nil?
|
24
|
+
arr << exception
|
25
|
+
exception = exception.cause
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def error_code_from_class(exception)
|
31
|
+
if ActionDispatch::ExceptionWrapper.rescue_responses.key?(exception.class.to_s)
|
32
|
+
ActionDispatch::ExceptionWrapper.rescue_responses[exception.class.to_s]
|
33
|
+
else
|
34
|
+
DEFAULT_ERROR_CODE
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -12,16 +12,6 @@ module ManageIQ
|
|
12
12
|
other.include(OpenapiEnabled)
|
13
13
|
|
14
14
|
other.before_action(:validate_request)
|
15
|
-
|
16
|
-
other.rescue_from(ActionController::UnpermittedParameters) do |exception|
|
17
|
-
error_document = ManageIQ::API::Common::ErrorDocument.new.add(400, exception.message)
|
18
|
-
render :json => error_document.to_h, :status => error_document.status
|
19
|
-
end
|
20
|
-
|
21
|
-
other.rescue_from(ManageIQ::API::Common::ApplicationControllerMixins::RequestBodyValidation::BodyParseError) do |_exception|
|
22
|
-
error_document = ManageIQ::API::Common::ErrorDocument.new.add(400, "Failed to parse request body, expected JSON")
|
23
|
-
render :json => error_document.to_h, :status => error_document.status
|
24
|
-
end
|
25
15
|
end
|
26
16
|
|
27
17
|
private
|
@@ -32,7 +22,7 @@ module ManageIQ
|
|
32
22
|
parsed_body = raw_body.blank? ? {} : JSON.parse(raw_body)
|
33
23
|
ActionController::Parameters.new(parsed_body).permit!
|
34
24
|
rescue JSON::ParserError
|
35
|
-
raise ManageIQ::API::Common::ApplicationControllerMixins::RequestBodyValidation::BodyParseError
|
25
|
+
raise ManageIQ::API::Common::ApplicationControllerMixins::RequestBodyValidation::BodyParseError, "Failed to parse request body, expected JSON"
|
36
26
|
end
|
37
27
|
end
|
38
28
|
|
@@ -50,9 +40,6 @@ module ManageIQ
|
|
50
40
|
api_version,
|
51
41
|
body_params.as_json
|
52
42
|
)
|
53
|
-
rescue OpenAPIParser::OpenAPIError => exception
|
54
|
-
error_document = ManageIQ::API::Common::ErrorDocument.new.add(400, exception.message)
|
55
|
-
render :json => error_document.to_h, :status => :bad_request
|
56
43
|
end
|
57
44
|
end
|
58
45
|
end
|
@@ -10,11 +10,6 @@ module ManageIQ
|
|
10
10
|
other.extend(self::ClassMethods)
|
11
11
|
|
12
12
|
other.before_action(:validate_primary_collection_id)
|
13
|
-
|
14
|
-
other.rescue_from(ManageIQ::API::Common::ApplicationControllerMixins::RequestPath::RequestPathError) do |exception|
|
15
|
-
error_document = ManageIQ::API::Common::ErrorDocument.new.add(400, exception.message)
|
16
|
-
render :json => error_document.to_h, :status => error_document.status
|
17
|
-
end
|
18
13
|
end
|
19
14
|
|
20
15
|
def request_path
|
@@ -105,6 +105,34 @@ module ManageIQ
|
|
105
105
|
"##{SCHEMAS_PATH}/#{klass_name}"
|
106
106
|
end
|
107
107
|
|
108
|
+
def build_schema_error_not_found
|
109
|
+
klass_name = "ErrorNotFound"
|
110
|
+
|
111
|
+
schemas[klass_name] = {
|
112
|
+
"type" => "object",
|
113
|
+
"properties" => {
|
114
|
+
"errors" => {
|
115
|
+
"type" => "array",
|
116
|
+
"items" => {
|
117
|
+
"type" => "object",
|
118
|
+
"properties" => {
|
119
|
+
"status" => {
|
120
|
+
"type" => "integer",
|
121
|
+
"example" => 404
|
122
|
+
},
|
123
|
+
"detail" => {
|
124
|
+
"type" => "string",
|
125
|
+
"example" => "Record not found"
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
"##{SCHEMAS_PATH}/#{klass_name}"
|
134
|
+
end
|
135
|
+
|
108
136
|
def parameters
|
109
137
|
@parameters ||= {
|
110
138
|
"QueryFilter" => {
|
@@ -158,10 +186,10 @@ module ManageIQ
|
|
158
186
|
end
|
159
187
|
|
160
188
|
def openapi_list_description(klass_name, primary_collection)
|
161
|
-
|
189
|
+
sub_collection = (primary_collection != klass_name)
|
162
190
|
{
|
163
|
-
"summary" => "List #{klass_name.pluralize}#{" for #{primary_collection}" if
|
164
|
-
"operationId" => "list#{primary_collection}#{klass_name.pluralize}",
|
191
|
+
"summary" => "List #{klass_name.pluralize}#{" for #{primary_collection}" if sub_collection}",
|
192
|
+
"operationId" => "list#{primary_collection if sub_collection}#{klass_name.pluralize}",
|
165
193
|
"description" => "Returns an array of #{klass_name} objects",
|
166
194
|
"parameters" => [
|
167
195
|
{ "$ref" => "##{PARAMETERS_PATH}/QueryLimit" },
|
@@ -179,7 +207,18 @@ module ManageIQ
|
|
179
207
|
}
|
180
208
|
}
|
181
209
|
}.tap do |h|
|
182
|
-
h["parameters"] << { "$ref" => build_parameter("ID") } if
|
210
|
+
h["parameters"] << { "$ref" => build_parameter("ID") } if sub_collection
|
211
|
+
|
212
|
+
next unless sub_collection
|
213
|
+
|
214
|
+
h["responses"]["404"] = {
|
215
|
+
"description" => "Not found",
|
216
|
+
"content" => {
|
217
|
+
"application/json" => {
|
218
|
+
"schema" => { "$ref" => build_schema_error_not_found }
|
219
|
+
}
|
220
|
+
}
|
221
|
+
}
|
183
222
|
end
|
184
223
|
end
|
185
224
|
|
@@ -215,7 +254,14 @@ module ManageIQ
|
|
215
254
|
}
|
216
255
|
}
|
217
256
|
},
|
218
|
-
"404" => {
|
257
|
+
"404" => {
|
258
|
+
"description" => "Not found",
|
259
|
+
"content" => {
|
260
|
+
"application/json" => {
|
261
|
+
"schema" => { "$ref" => build_schema_error_not_found }
|
262
|
+
}
|
263
|
+
}
|
264
|
+
}
|
219
265
|
}
|
220
266
|
}
|
221
267
|
end
|
@@ -228,7 +274,14 @@ module ManageIQ
|
|
228
274
|
"parameters" => [{ "$ref" => build_parameter("ID") }],
|
229
275
|
"responses" => {
|
230
276
|
"204" => { "description" => "#{klass_name} deleted" },
|
231
|
-
"404" => {
|
277
|
+
"404" => {
|
278
|
+
"description" => "Not found",
|
279
|
+
"content" => {
|
280
|
+
"application/json" => {
|
281
|
+
"schema" => { "$ref" => build_schema_error_not_found }
|
282
|
+
}
|
283
|
+
}
|
284
|
+
}
|
232
285
|
}
|
233
286
|
}
|
234
287
|
end
|
@@ -281,7 +334,14 @@ module ManageIQ
|
|
281
334
|
"responses" => {
|
282
335
|
"204" => { "description" => "Updated, no content" },
|
283
336
|
"400" => { "description" => "Bad request" },
|
284
|
-
"404" => {
|
337
|
+
"404" => {
|
338
|
+
"description" => "Not found",
|
339
|
+
"content" => {
|
340
|
+
"application/json" => {
|
341
|
+
"schema" => { "$ref" => build_schema_error_not_found }
|
342
|
+
}
|
343
|
+
}
|
344
|
+
}
|
285
345
|
}
|
286
346
|
}
|
287
347
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module ManageIQ
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module RBAC
|
5
|
+
class Access
|
6
|
+
attr_reader :acl
|
7
|
+
DEFAULT_LIMIT = 500
|
8
|
+
def initialize(resource, verb)
|
9
|
+
@resource = resource
|
10
|
+
@verb = verb
|
11
|
+
@regexp = Regexp.new(":(#{Regexp.escape(@resource)}|\\*):(#{Regexp.escape(@verb)}|\\*)")
|
12
|
+
@app_name = ENV["APP_NAME"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def process
|
16
|
+
Service.call(RBACApiClient::AccessApi) do |api|
|
17
|
+
@acl ||= Service.paginate(api, :get_principal_access, {:limit => DEFAULT_LIMIT}, @app_name).select do |item|
|
18
|
+
@regexp.match?(item.permission)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def accessible?
|
25
|
+
@acl.any?
|
26
|
+
end
|
27
|
+
|
28
|
+
def id_list
|
29
|
+
ids.include?('*') ? [] : ids
|
30
|
+
end
|
31
|
+
|
32
|
+
def owner_scoped?
|
33
|
+
ids.include?('*') ? false : owner_scope_filter?
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.enabled?
|
37
|
+
ENV['BYPASS_RBAC'].blank?
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def ids
|
43
|
+
@ids ||= @acl.each_with_object([]) do |item, ids|
|
44
|
+
item.resource_definitions.each do |rd|
|
45
|
+
next unless rd.attribute_filter.key == 'id'
|
46
|
+
next unless rd.attribute_filter.operation == 'equal'
|
47
|
+
|
48
|
+
ids << rd.attribute_filter.value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def owner_scope_filter?
|
54
|
+
@acl.any? do |item|
|
55
|
+
item.resource_definitions.any? do |rd|
|
56
|
+
rd.attribute_filter.key == 'owner' &&
|
57
|
+
rd.attribute_filter.operation == 'equal' &&
|
58
|
+
rd.attribute_filter.value == '{{username}}'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ManageIQ
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module RBAC
|
5
|
+
class ACL
|
6
|
+
def create(resource_id, permissions)
|
7
|
+
permissions.collect do |permission|
|
8
|
+
create_acl(permission, resource_id)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove(acls, resource_id, permissions)
|
13
|
+
permissions.each_with_object(acls) do |permission, as|
|
14
|
+
delete_matching(as, resource_id, permission)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(acls, resource_id, permissions)
|
19
|
+
new_acls = permissions.each_with_object([]) do |permission, as|
|
20
|
+
next if find_matching(acls, resource_id, permission)
|
21
|
+
|
22
|
+
as << create_acl(permission, resource_id)
|
23
|
+
end
|
24
|
+
new_acls + acls
|
25
|
+
end
|
26
|
+
|
27
|
+
def resource_defintions_empty?(acls, permission)
|
28
|
+
acls.each do |acl|
|
29
|
+
if acl.permission == permission
|
30
|
+
return acl.resource_definitions.empty?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def create_acl(permission, resource_id = nil)
|
39
|
+
resource_def = resource_definition(resource_id) if resource_id
|
40
|
+
RBACApiClient::Access.new.tap do |access|
|
41
|
+
access.permission = permission
|
42
|
+
access.resource_definitions = resource_def ? [resource_def] : []
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def resource_definition(resource_id)
|
47
|
+
rdf = RBACApiClient::ResourceDefinitionFilter.new.tap do |obj|
|
48
|
+
obj.key = 'id'
|
49
|
+
obj.operation = 'equal'
|
50
|
+
obj.value = resource_id.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
RBACApiClient::ResourceDefinition.new.tap do |rd|
|
54
|
+
rd.attribute_filter = rdf
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def matches?(access, resource_id, permission)
|
59
|
+
access.permission == permission &&
|
60
|
+
access.resource_definitions.any? { |rdf| rdf.attribute_filter.key == 'id' && rdf.attribute_filter.operation == 'equal' && rdf.attribute_filter.value == resource_id.to_s }
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_matching(acls, resource_id, permission)
|
64
|
+
acls.detect { |access| matches?(access, resource_id, permission) }
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete_matching(acls, resource_id, permission)
|
68
|
+
acls.delete_if { |access| matches?(access, resource_id, permission) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ManageIQ
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module RBAC
|
5
|
+
class Policies
|
6
|
+
def initialize(prefix)
|
7
|
+
@prefix = prefix
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_policy(policy_name, description, group_name, role_uuid)
|
11
|
+
Service.call(RBACApiClient::PolicyApi) do |api_instance|
|
12
|
+
policy_in = RBACApiClient::PolicyIn.new
|
13
|
+
policy_in.name = policy_name
|
14
|
+
policy_in.description = description
|
15
|
+
policy_in.group = group_name
|
16
|
+
policy_in.roles = [role_uuid]
|
17
|
+
api_instance.create_policies(policy_in)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# delete all policies that contains the role.
|
22
|
+
def delete_policy(role)
|
23
|
+
Service.call(RBACApiClient::PolicyApi) do |api_instance|
|
24
|
+
Service.paginate(api_instance, :list_policies, :name => @prefix).each do |policy|
|
25
|
+
api_instance.delete_policy(policy.uuid) if policy.roles.map(&:uuid).include?(role.uuid)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module ManageIQ
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module RBAC
|
5
|
+
class QuerySharedResource
|
6
|
+
require 'rbac-api-client'
|
7
|
+
|
8
|
+
include Utilities
|
9
|
+
attr_accessor :share_info
|
10
|
+
|
11
|
+
def initialize(options)
|
12
|
+
@app_name = options[:app_name]
|
13
|
+
@resource_id = options[:resource_id]
|
14
|
+
@resource_name = options[:resource_name]
|
15
|
+
@share_info = []
|
16
|
+
@roles = RBAC::Roles.new("#{@app_name}-#{@resource_name}-#{@resource_id}")
|
17
|
+
end
|
18
|
+
|
19
|
+
def process
|
20
|
+
build_share_info
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def build_share_info
|
27
|
+
@roles.with_each_role do |role|
|
28
|
+
_id, group_uuid = parse_ids_from_name(role.name)
|
29
|
+
group = get_group(group_uuid)
|
30
|
+
@share_info << { 'group_name' => group.name,
|
31
|
+
'group_uuid' => group.uuid,
|
32
|
+
'permissions' => role.access.collect(&:permission)}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_group(uuid)
|
37
|
+
Service.call(RBACApiClient::GroupApi) do |api_instance|
|
38
|
+
api_instance.get_group(uuid)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module ManageIQ
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module RBAC
|
5
|
+
class Roles
|
6
|
+
attr_reader :roles
|
7
|
+
|
8
|
+
def initialize(prefix = nil, scope = 'principal')
|
9
|
+
@roles = {}
|
10
|
+
load(prefix, scope)
|
11
|
+
end
|
12
|
+
|
13
|
+
def find(name)
|
14
|
+
uuid = @roles[name]
|
15
|
+
get(uuid) if uuid
|
16
|
+
end
|
17
|
+
|
18
|
+
def with_each_role
|
19
|
+
@roles.each_value do |uuid|
|
20
|
+
yield get(uuid)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def add(name, acls)
|
25
|
+
Service.call(RBACApiClient::RoleApi) do |api_instance|
|
26
|
+
role_in = RBACApiClient::RoleIn.new
|
27
|
+
role_in.name = name
|
28
|
+
role_in.access = acls
|
29
|
+
api_instance.create_roles(role_in).tap do |role|
|
30
|
+
@roles[name] = role.uuid
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def update(role)
|
36
|
+
Service.call(RBACApiClient::RoleApi) do |api_instance|
|
37
|
+
api_instance.update_role(role.uuid, role)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(role)
|
42
|
+
@roles.delete(role.name)
|
43
|
+
Service.call(RBACApiClient::RoleApi) do |api_instance|
|
44
|
+
api_instance.delete_role(role.uuid)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.assigned_role?(role_name)
|
49
|
+
opts = { :name => role_name,
|
50
|
+
:scope => 'principal' }
|
51
|
+
|
52
|
+
Service.call(RBACApiClient::RoleApi) do |api_instance|
|
53
|
+
Service.paginate(api_instance, :list_roles, opts).count.positive?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def load(prefix, scope)
|
60
|
+
opts = { :scope => scope, :name => prefix, :limit => 500 }
|
61
|
+
Service.call(RBACApiClient::RoleApi) do |api_instance|
|
62
|
+
Service.paginate(api_instance, :list_roles, opts).each do |role|
|
63
|
+
@roles[role.name] = role.uuid
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def get(uuid)
|
69
|
+
Service.call(RBACApiClient::RoleApi) do |api_instance|
|
70
|
+
api_instance.get_role(uuid)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module ManageIQ
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module RBAC
|
5
|
+
require 'rbac-api-client'
|
6
|
+
|
7
|
+
class Seed
|
8
|
+
def initialize(seed_file, user_file)
|
9
|
+
@acl_data = YAML.load_file(seed_file)
|
10
|
+
@request = create_request(user_file)
|
11
|
+
end
|
12
|
+
|
13
|
+
def process
|
14
|
+
ManageIQ::API::Common::Request.with_request(@request) do
|
15
|
+
create_groups
|
16
|
+
create_roles
|
17
|
+
create_policies
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def create_groups
|
24
|
+
current = current_groups
|
25
|
+
names = current.collect(&:name)
|
26
|
+
group = RBACApiClient::Group.new
|
27
|
+
begin
|
28
|
+
Service.call(RBACApiClient::GroupApi) do |api_instance|
|
29
|
+
@acl_data['groups'].each do |grp|
|
30
|
+
next if names.include?(grp['name'])
|
31
|
+
|
32
|
+
Rails.logger.info("Creating #{grp['name']}")
|
33
|
+
group.name = grp['name']
|
34
|
+
group.description = grp['description']
|
35
|
+
api_instance.create_group(group)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
rescue RBACApiClient::ApiError => e
|
39
|
+
Rails.logger.error("Exception when calling GroupApi->create_group: #{e}")
|
40
|
+
raise
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def current_groups
|
45
|
+
Service.call(RBACApiClient::GroupApi) do |api|
|
46
|
+
Service.paginate(api, :list_groups, {}).to_a
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_roles
|
51
|
+
current = current_roles
|
52
|
+
names = current.collect(&:name)
|
53
|
+
role_in = RBACApiClient::RoleIn.new
|
54
|
+
begin
|
55
|
+
Service.call(RBACApiClient::RoleApi) do |api_instance|
|
56
|
+
@acl_data['roles'].each do |role|
|
57
|
+
next if names.include?(role['name'])
|
58
|
+
|
59
|
+
role_in.name = role['name']
|
60
|
+
role_in.access = []
|
61
|
+
role['access'].each do |obj|
|
62
|
+
access = RBACApiClient::Access.new
|
63
|
+
access.permission = obj['permission']
|
64
|
+
access.resource_definitions = create_rds(obj)
|
65
|
+
role_in.access << access
|
66
|
+
end
|
67
|
+
api_instance.create_roles(role_in)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
rescue RBACApiClient::ApiError => e
|
71
|
+
Rails.logger.error("Exception when calling RoleApi->create_roles: #{e}")
|
72
|
+
raise
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_rds(obj)
|
77
|
+
obj.fetch('resource_definitions', []).collect do |item|
|
78
|
+
RBACApiClient::ResourceDefinition.new.tap do |rd|
|
79
|
+
rd.attribute_filter = RBACApiClient::ResourceDefinitionFilter.new.tap do |rdf|
|
80
|
+
rdf.key = item['attribute_filter']['key']
|
81
|
+
rdf.value = item['attribute_filter']['value']
|
82
|
+
rdf.operation = item['attribute_filter']['operation']
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def current_roles
|
89
|
+
Service.call(RBACApiClient::RoleApi) do |api|
|
90
|
+
Service.paginate(api, :list_roles, {}).to_a
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def create_policies
|
95
|
+
names = current_policies.collect(&:name)
|
96
|
+
groups = current_groups
|
97
|
+
roles = current_roles
|
98
|
+
policy_in = RBACApiClient::PolicyIn.new
|
99
|
+
begin
|
100
|
+
Service.call(RBACApiClient::PolicyApi) do |api_instance|
|
101
|
+
@acl_data['policies'].each do |policy|
|
102
|
+
next if names.include?(policy['name'])
|
103
|
+
|
104
|
+
policy_in.name = policy['name']
|
105
|
+
policy_in.description = policy['description']
|
106
|
+
policy_in.group = find_uuid('Group', groups, policy['group']['name'])
|
107
|
+
policy_in.roles = [find_uuid('Role', roles, policy['role']['name'])]
|
108
|
+
api_instance.create_policies(policy_in)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
rescue RBACApiClient::ApiError => e
|
112
|
+
Rails.logger.error("Exception when calling PolicyApi->create_policies: #{e}")
|
113
|
+
raise
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def current_policies
|
118
|
+
Service.call(RBACApiClient::PolicyApi) do |api|
|
119
|
+
Service.paginate(api, :list_policies, {}).to_a
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def find_uuid(type, data, name)
|
124
|
+
result = data.detect { |item| item.name == name }
|
125
|
+
raise "#{type} #{name} not found in RBAC service" unless result
|
126
|
+
|
127
|
+
result.uuid
|
128
|
+
end
|
129
|
+
|
130
|
+
def create_request(user_file)
|
131
|
+
raise "File #{user_file} not found" unless File.exist?(user_file)
|
132
|
+
|
133
|
+
user = YAML.load_file(user_file)
|
134
|
+
{:headers => {'x-rh-identity' => Base64.strict_encode64(user.to_json)}, :original_url => '/'}
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module ManageIQ
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module RBAC
|
5
|
+
require 'rbac-api-client'
|
6
|
+
|
7
|
+
class Service
|
8
|
+
def self.call(klass)
|
9
|
+
setup
|
10
|
+
yield init(klass)
|
11
|
+
rescue RBACApiClient::ApiError => err
|
12
|
+
Rails.logger.error("RBACApiClient::ApiError #{err.message} ")
|
13
|
+
raise
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.paginate(obj, method, pagination_options, *method_args)
|
17
|
+
Enumerator.new do |enum|
|
18
|
+
opts = { :limit => 10, :offset => 0 }.merge(pagination_options)
|
19
|
+
count = nil
|
20
|
+
fetched = 0
|
21
|
+
begin
|
22
|
+
loop do
|
23
|
+
args = [method_args, opts].flatten.compact
|
24
|
+
result = obj.send(method, *args)
|
25
|
+
count ||= result.meta.count
|
26
|
+
opts[:offset] = opts[:offset] + result.data.count
|
27
|
+
result.data.each do |element|
|
28
|
+
enum.yield element
|
29
|
+
end
|
30
|
+
fetched += result.data.count
|
31
|
+
break if count == fetched || result.data.empty?
|
32
|
+
end
|
33
|
+
rescue StandardError => e
|
34
|
+
Rails.logger.error("Exception when calling pagination on #{method} #{e}")
|
35
|
+
raise
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private_class_method def self.setup
|
41
|
+
RBACApiClient.configure do |config|
|
42
|
+
config.host = ENV['RBAC_URL'] || 'localhost'
|
43
|
+
config.scheme = URI.parse(ENV['RBAC_URL']).try(:scheme) || 'http'
|
44
|
+
dev_credentials(config)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private_class_method def self.init(klass)
|
49
|
+
headers = ManageIQ::API::Common::Request.current_forwardable
|
50
|
+
Rails.logger.info("Sending Headers to RBAC #{headers}")
|
51
|
+
klass.new.tap do |api|
|
52
|
+
api.api_client.default_headers = api.api_client.default_headers.merge(headers)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private_class_method def self.dev_credentials(config)
|
57
|
+
# Set up user/pass for basic auth if we're in dev and they exist.
|
58
|
+
if Rails.env.development?
|
59
|
+
config.username = ENV.fetch('DEV_USERNAME')
|
60
|
+
config.password = ENV.fetch('DEV_PASSWORD')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ManageIQ
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module RBAC
|
5
|
+
class ShareResource
|
6
|
+
require 'rbac-api-client'
|
7
|
+
include Utilities
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@app_name = options[:app_name]
|
11
|
+
@resource_name = options[:resource_name]
|
12
|
+
@permissions = options[:permissions]
|
13
|
+
@resource_ids = options[:resource_ids]
|
14
|
+
@group_uuids = SortedSet.new(options[:group_uuids])
|
15
|
+
@acls = RBAC::ACL.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def process
|
19
|
+
validate_groups
|
20
|
+
@roles = RBAC::Roles.new("#{@app_name}-#{@resource_name}-")
|
21
|
+
@group_uuids.each { |uuid| manage_roles_for_group(uuid) }
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def manage_roles_for_group(group_uuid)
|
28
|
+
@resource_ids.each do |resource_id|
|
29
|
+
name = unique_name(resource_id, group_uuid)
|
30
|
+
role = @roles.find(name)
|
31
|
+
role ? update_existing_role(role, resource_id) : add_new_role(name, group_uuid, resource_id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def update_existing_role(role, resource_id)
|
36
|
+
role.access = @acls.add(role.access, resource_id, @permissions)
|
37
|
+
@roles.update(role) if role.access.present?
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_new_role(name, group_uuid, resource_id)
|
41
|
+
acls = @acls.create(resource_id, @permissions)
|
42
|
+
role = @roles.add(name, acls)
|
43
|
+
add_policy(name, group_uuid, role.uuid)
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_policy(name, group_uuid, role_uuid)
|
47
|
+
Service.call(RBACApiClient::PolicyApi) do |api_instance|
|
48
|
+
policy_in = RBACApiClient::PolicyIn.new
|
49
|
+
policy_in.name = name
|
50
|
+
policy_in.description = 'Shared Policy'
|
51
|
+
policy_in.group = group_uuid
|
52
|
+
policy_in.roles = [role_uuid]
|
53
|
+
api_instance.create_policies(policy_in)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ManageIQ
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module RBAC
|
5
|
+
require 'rbac-api-client'
|
6
|
+
|
7
|
+
class UnshareResource < ShareResource
|
8
|
+
attr_accessor :count
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
@count = 0
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def manage_roles_for_group(group_uuid)
|
18
|
+
@resource_ids.each do |resource_id|
|
19
|
+
name = unique_name(resource_id, group_uuid)
|
20
|
+
role = @roles.find(name)
|
21
|
+
next unless role
|
22
|
+
|
23
|
+
role.access = @acls.remove(role.access, resource_id, @permissions)
|
24
|
+
role.access.present? ? @roles.update(role) : @roles.delete(role)
|
25
|
+
@count += 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ManageIQ
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module RBAC
|
5
|
+
module Utilities
|
6
|
+
def validate_groups
|
7
|
+
Service.call(RBACApiClient::GroupApi) do |api|
|
8
|
+
uuids = SortedSet.new
|
9
|
+
Service.paginate(api, :list_groups, {}).each { |group| uuids << group.uuid }
|
10
|
+
missing = @group_uuids - uuids
|
11
|
+
raise ManageIQ::API::Common::InvalidParameter, "The following group uuids are missing #{missing.to_a.join(",")}" unless missing.empty?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def unique_name(resource_id, group_id)
|
16
|
+
"#{@app_name}-#{@resource_name}-#{resource_id}-group-#{group_id}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_ids_from_name(name)
|
20
|
+
@regexp ||= Regexp.new("#{@app_name}-#{@resource_name}-(?<resource_id>.*)-group-(?<group_uuid>.*)")
|
21
|
+
result = @regexp.match(name)
|
22
|
+
if result
|
23
|
+
[result[:resource_id], result[:group_uuid]]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
RSpec.shared_context "rbac_objects" do
|
2
|
+
let(:app_name) { 'catalog' }
|
3
|
+
let(:resource) { "portfolios" }
|
4
|
+
let(:permissions) { ["#{app_name}:#{resource}:read"] }
|
5
|
+
let(:resource_id1) { "1" }
|
6
|
+
let(:resource_id2) { "2" }
|
7
|
+
let(:resource_id3) { "3" }
|
8
|
+
let(:group1) { instance_double(RBACApiClient::GroupOut, :name => 'group1', :uuid => "123") }
|
9
|
+
let(:group2) { instance_double(RBACApiClient::GroupOut, :name => 'group2', :uuid => "12345") }
|
10
|
+
let(:group3) { instance_double(RBACApiClient::GroupOut, :name => 'group3', :uuid => "45665") }
|
11
|
+
let(:role1) { instance_double(RBACApiClient::RoleOut, :name => "#{app_name}-#{resource}-#{resource_id1}-group-#{group1.uuid}", :uuid => "67899") }
|
12
|
+
let(:role2) { instance_double(RBACApiClient::RoleOut, :name => "#{app_name}-#{resource}-#{resource_id2}-group-#{group1.uuid}", :uuid => "55555") }
|
13
|
+
let(:role1_detail) { instance_double(RBACApiClient::RoleWithAccess, :name => role1.name, :uuid => role1.uuid, :access => [access1]) }
|
14
|
+
let(:role2_detail) { instance_double(RBACApiClient::RoleWithAccess, :name => role2.name, :uuid => role2.uuid, :access => []) }
|
15
|
+
let(:role1_detail_updated) { instance_double(RBACApiClient::RoleWithAccess, :name => role1.name, :uuid => role1.uuid, :access => []) }
|
16
|
+
let(:groups) { [group1, group2, group3] }
|
17
|
+
let(:roles) { [role1] }
|
18
|
+
let(:policies) { [instance_double(RBACApiClient::PolicyIn, :group => group1, :roles => roles)] }
|
19
|
+
let(:filter1) { instance_double(RBACApiClient::ResourceDefinitionFilter, :key => 'id', :operation => 'equal', :value => resource_id1) }
|
20
|
+
let(:resource_def1) { instance_double(RBACApiClient::ResourceDefinition, :attribute_filter => filter1) }
|
21
|
+
let(:filter2) { instance_double(RBACApiClient::ResourceDefinitionFilter, :key => 'id', :operation => 'equal', :value => resource_id2) }
|
22
|
+
let(:resource_def2) { instance_double(RBACApiClient::ResourceDefinition, :attribute_filter => filter2) }
|
23
|
+
let(:filter3) { instance_double(RBACApiClient::ResourceDefinitionFilter, :key => 'id', :operation => 'equal', :value => resource_id3) }
|
24
|
+
let(:resource_def3) { instance_double(RBACApiClient::ResourceDefinition, :attribute_filter => filter3) }
|
25
|
+
let(:filter4) { instance_double(RBACApiClient::ResourceDefinitionFilter, :key => 'id', :operation => 'equal', :value => '*') }
|
26
|
+
let(:resource_def4) { instance_double(RBACApiClient::ResourceDefinition, :attribute_filter => filter4) }
|
27
|
+
let(:access1) { instance_double(RBACApiClient::Access, :permission => "#{app_name}:#{resource}:read", :resource_definitions => [resource_def1]) }
|
28
|
+
let(:access2) { instance_double(RBACApiClient::Access, :permission => "#{app_name}:#{resource}:write", :resource_definitions => [resource_def2]) }
|
29
|
+
let(:access3) { instance_double(RBACApiClient::Access, :permission => "#{app_name}:#{resource}:order", :resource_definitions => []) }
|
30
|
+
let(:admin_access) { instance_double(RBACApiClient::Access, :permission => "#{app_name}:#{resource}:read", :resource_definitions => [resource_def4]) }
|
31
|
+
let(:group_uuids) { [group1.uuid, group2.uuid, group3.uuid] }
|
32
|
+
let(:api_instance) { double }
|
33
|
+
let(:rs_class) { class_double("ManageIQ::API::Common::RBAC::Service").as_stubbed_const(:transfer_nested_constants => true) }
|
34
|
+
let(:current_user) { '{{username}}' }
|
35
|
+
let(:id_value) { '*' }
|
36
|
+
let(:owner_filter) { instance_double(RBACApiClient::ResourceDefinitionFilter, :key => 'owner', :operation => 'equal', :value => current_user) }
|
37
|
+
let(:owner_resource_def) { instance_double(RBACApiClient::ResourceDefinition, :attribute_filter => owner_filter) }
|
38
|
+
let(:id_filter) { instance_double(RBACApiClient::ResourceDefinitionFilter, :key => 'id', :operation => 'equal', :value => id_value) }
|
39
|
+
let(:id_resource_def) { instance_double(RBACApiClient::ResourceDefinition, :attribute_filter => id_filter) }
|
40
|
+
let(:owner_resource) { 'orders' }
|
41
|
+
let(:owner_permission) { "#{app_name}:#{owner_resource}:read" }
|
42
|
+
let(:owner_access) { instance_double(RBACApiClient::Access, :permission => owner_permission, :resource_definitions => [owner_resource_def]) }
|
43
|
+
let(:all_access) { instance_double(RBACApiClient::Access, :permission => owner_permission, :resource_definitions => [id_resource_def]) }
|
44
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ServiceSpecHelper
|
2
|
+
RSpec.configure do |config|
|
3
|
+
config.around(:example, :type => :service) do |example|
|
4
|
+
default_tenant = Tenant.first_or_create!(:external_tenant => default_account_number)
|
5
|
+
|
6
|
+
ActsAsTenant.with_tenant(default_tenant) do
|
7
|
+
example.call
|
8
|
+
end
|
9
|
+
|
10
|
+
Tenant.delete_all
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_headers
|
15
|
+
{ 'x-rh-identity' => encoded_user_hash,
|
16
|
+
'x-rh-insights-request-id' => 'gobbledygook' }
|
17
|
+
end
|
18
|
+
|
19
|
+
def original_url
|
20
|
+
"http://example.com"
|
21
|
+
end
|
22
|
+
|
23
|
+
def default_request
|
24
|
+
{ :headers => default_headers, :original_url => original_url }
|
25
|
+
end
|
26
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: manageiq-api-common
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ManageIQ Authors
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-10-
|
11
|
+
date: 2019-10-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: acts_as_tenant
|
@@ -134,14 +134,14 @@ dependencies:
|
|
134
134
|
requirements:
|
135
135
|
- - "~>"
|
136
136
|
- !ruby/object:Gem::Version
|
137
|
-
version: 0.
|
137
|
+
version: 0.6.1
|
138
138
|
type: :runtime
|
139
139
|
prerelease: false
|
140
140
|
version_requirements: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: 0.
|
144
|
+
version: 0.6.1
|
145
145
|
- !ruby/object:Gem::Dependency
|
146
146
|
name: graphql
|
147
147
|
requirement: !ruby/object:Gem::Requirement
|
@@ -288,6 +288,20 @@ dependencies:
|
|
288
288
|
- - ">="
|
289
289
|
- !ruby/object:Gem::Version
|
290
290
|
version: '0'
|
291
|
+
- !ruby/object:Gem::Dependency
|
292
|
+
name: webmock
|
293
|
+
requirement: !ruby/object:Gem::Requirement
|
294
|
+
requirements:
|
295
|
+
- - ">="
|
296
|
+
- !ruby/object:Gem::Version
|
297
|
+
version: '0'
|
298
|
+
type: :development
|
299
|
+
prerelease: false
|
300
|
+
version_requirements: !ruby/object:Gem::Requirement
|
301
|
+
requirements:
|
302
|
+
- - ">="
|
303
|
+
- !ruby/object:Gem::Version
|
304
|
+
version: '0'
|
291
305
|
description: Header, Encryption, RBAC, Serialization, Pagination and other common
|
292
306
|
behavior for microservices
|
293
307
|
email:
|
@@ -307,9 +321,9 @@ files:
|
|
307
321
|
- lib/generators/shared_utilities/templates/migration_existing.rb
|
308
322
|
- lib/manageiq-api-common.rb
|
309
323
|
- lib/manageiq/api/common.rb
|
310
|
-
- lib/manageiq/api/common/api_error.rb
|
311
324
|
- lib/manageiq/api/common/application_controller_mixins/api_doc.rb
|
312
325
|
- lib/manageiq/api/common/application_controller_mixins/common.rb
|
326
|
+
- lib/manageiq/api/common/application_controller_mixins/exception_handling.rb
|
313
327
|
- lib/manageiq/api/common/application_controller_mixins/openapi_enabled.rb
|
314
328
|
- lib/manageiq/api/common/application_controller_mixins/parameters.rb
|
315
329
|
- lib/manageiq/api/common/application_controller_mixins/request_body_validation.rb
|
@@ -341,13 +355,25 @@ files:
|
|
341
355
|
- lib/manageiq/api/common/open_api/serializer.rb
|
342
356
|
- lib/manageiq/api/common/option_redirect_enhancements.rb
|
343
357
|
- lib/manageiq/api/common/paginated_response.rb
|
358
|
+
- lib/manageiq/api/common/rbac/access.rb
|
359
|
+
- lib/manageiq/api/common/rbac/acl.rb
|
360
|
+
- lib/manageiq/api/common/rbac/policies.rb
|
361
|
+
- lib/manageiq/api/common/rbac/query_shared_resource.rb
|
362
|
+
- lib/manageiq/api/common/rbac/roles.rb
|
363
|
+
- lib/manageiq/api/common/rbac/seed.rb
|
364
|
+
- lib/manageiq/api/common/rbac/service.rb
|
365
|
+
- lib/manageiq/api/common/rbac/share_resource.rb
|
366
|
+
- lib/manageiq/api/common/rbac/unshare_resource.rb
|
367
|
+
- lib/manageiq/api/common/rbac/utilities.rb
|
344
368
|
- lib/manageiq/api/common/request.rb
|
345
369
|
- lib/manageiq/api/common/routing.rb
|
346
370
|
- lib/manageiq/api/common/user.rb
|
347
371
|
- lib/manageiq/api/common/version.rb
|
348
372
|
- lib/tasks/manageiq/api/common_tasks.rake
|
349
373
|
- spec/support/default_as_json.rb
|
374
|
+
- spec/support/rbac_shared_contexts.rb
|
350
375
|
- spec/support/requests_spec_helper.rb
|
376
|
+
- spec/support/service_spec_helper.rb
|
351
377
|
- spec/support/user_header_spec_helper.rb
|
352
378
|
homepage: https://github.com/ManageIQ/manageiq-api-common.git
|
353
379
|
licenses:
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module ManageIQ
|
2
|
-
module API
|
3
|
-
module Common
|
4
|
-
class ApiError < StandardError
|
5
|
-
attr_reader :errors
|
6
|
-
|
7
|
-
def initialize(status, detail)
|
8
|
-
@errors = ErrorDocument.new.add(status, detail)
|
9
|
-
end
|
10
|
-
|
11
|
-
def status
|
12
|
-
@errors.status
|
13
|
-
end
|
14
|
-
|
15
|
-
def add(status, detail)
|
16
|
-
@errors.add(status, detail)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|