manageiq-api-common 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|