aserto 0.20.5 → 0.30.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/README.md +11 -81
- data/VERSION +1 -1
- data/lib/aserto/auth_client.rb +35 -8
- data/lib/aserto/config.rb +1 -0
- data/lib/aserto/directory/interceptors/headers.rb +23 -3
- data/lib/aserto/directory/v3/client.rb +114 -0
- data/lib/aserto/directory/v3/config.rb +60 -0
- data/lib/aserto/directory/v3/exporter.rb +35 -0
- data/lib/aserto/directory/v3/importer.rb +40 -0
- data/lib/aserto/directory/v3/model.rb +36 -0
- data/lib/aserto/directory/v3/reader.rb +266 -0
- data/lib/aserto/directory/v3/writer.rb +125 -0
- data/lib/aserto/errors.rb +1 -0
- data/lib/aserto/identity_mapper/manual.rb +16 -0
- data/lib/aserto/identity_mapper.rb +6 -3
- data/lib/aserto.rb +10 -9
- metadata +14 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4471efcec975432356d584d3d480e0ab503e139787c8f96ab88032ab15356175
|
4
|
+
data.tar.gz: aa46126a15fb9f583fd8dc9a1b064889f95ccfa3814bc4c6c7588c0ed7f78175
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61af3804724a6138e21a5233c25712213860b4e2939331f1087e728c36a6cf5fefb31c6e23b77fc219df75d84abf2ecb1675892e60663e787cd6932884be3dec
|
7
|
+
data.tar.gz: f73188bf7eb0a4b58944022c715fdcb65718e3d8debe3215a6a82437781f544ff78cc1012a169ffd8deb2a762650da9d0970030a2e8eeaab461abe1dc4233d3b
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ You can initialize a directory client as follows:
|
|
32
32
|
```ruby
|
33
33
|
require 'aserto/directory/client'
|
34
34
|
|
35
|
-
directory_client =
|
35
|
+
directory_client =Aserto::Directory::V3::Client.new(
|
36
36
|
url: "directory.eng.aserto.com:8443",
|
37
37
|
tenant_id: "aserto-tenant-id",
|
38
38
|
api_key: "basic directory api key",
|
@@ -44,85 +44,7 @@ directory_client = Aserto::Directory::Client.new(
|
|
44
44
|
- `tenant_id`: Aserto tenant ID (_required_ if using hosted directory)
|
45
45
|
- `cert_path`: Path to the grpc service certificate when connecting to local topaz instance.
|
46
46
|
|
47
|
-
|
48
|
-
Get an object instance with the type `type-name` and the key `object-key`. For example:
|
49
|
-
|
50
|
-
```ruby
|
51
|
-
user = directory_client.object(type: 'user', key: 'euang@acmecorp.com')
|
52
|
-
```
|
53
|
-
|
54
|
-
Get an array of relations of a certain type for an object instance. For example:
|
55
|
-
|
56
|
-
```ruby
|
57
|
-
identity = 'euang@acmecorp.com';
|
58
|
-
relations = directory_client.relation(
|
59
|
-
{
|
60
|
-
subject: {
|
61
|
-
type: 'user',
|
62
|
-
},
|
63
|
-
object: {
|
64
|
-
type: 'identity',
|
65
|
-
key: identity
|
66
|
-
},
|
67
|
-
relation: {
|
68
|
-
name: 'identifier',
|
69
|
-
objectType: 'identity'
|
70
|
-
}
|
71
|
-
}
|
72
|
-
)
|
73
|
-
```
|
74
|
-
|
75
|
-
### Setting objects and relations
|
76
|
-
|
77
|
-
Create a new object
|
78
|
-
```ruby
|
79
|
-
user = directory_client.set_object(object: { type: "user", key: "test-object", display_name: "test object" })
|
80
|
-
identity = directory_client.set_object(object: { type: "identity", key: "test-identity" })
|
81
|
-
```
|
82
|
-
|
83
|
-
Update an existing object
|
84
|
-
```ruby
|
85
|
-
user = directory_client.set_object(object: { type: "user", key: "test-object", display_name: "test object" })
|
86
|
-
user.display_name = 'test object edit'
|
87
|
-
updated_user = directory_client.set_object(object: user)
|
88
|
-
```
|
89
|
-
|
90
|
-
Create a new relation
|
91
|
-
```ruby
|
92
|
-
directory_client.set_relation(
|
93
|
-
subject: { type: "user", "test-object" },
|
94
|
-
relation: "identifier",
|
95
|
-
object: { type: "identity", key: "test-identity" }
|
96
|
-
)
|
97
|
-
```
|
98
|
-
|
99
|
-
Delete a relation
|
100
|
-
```ruby
|
101
|
-
pp client.delete_relation(
|
102
|
-
subject: { type: "user", key: "test-object" },
|
103
|
-
relation: { name: "identifier", object_type: "identity" },
|
104
|
-
object: { type: "identity", key: "test-identity" }
|
105
|
-
)
|
106
|
-
```
|
107
|
-
|
108
|
-
### Checking permissions and relations
|
109
|
-
Check permission
|
110
|
-
```ruby
|
111
|
-
directory_client.check_permission(
|
112
|
-
subject: { type: "user", key: "011a88bc-7df9-4d92-ba1f-2ff319e101e1" },
|
113
|
-
permission: { name: "read" },
|
114
|
-
object: { type: "group", key: "executive" }
|
115
|
-
)
|
116
|
-
```
|
117
|
-
|
118
|
-
Check relation
|
119
|
-
```ruby
|
120
|
-
directory_client.check_relation(
|
121
|
-
subject: { type: "user", key: "dfdadc39-7335-404d-af66-c77cf13a15f8" },
|
122
|
-
relation: { name: "identifier", object_type: "identity" },
|
123
|
-
object: { type: "identity", key: "euang@acmecorp.com" }
|
124
|
-
)
|
125
|
-
```
|
47
|
+
See https://rubydoc.info/gems/aserto/docs/Aserto/Directory/V3/Client for full documentation
|
126
48
|
|
127
49
|
## Authorizer
|
128
50
|
`Aserto::Authorization` is a middleware that allows Ruby applications to use Aserto as the Authorization provider.
|
@@ -150,7 +72,7 @@ The middleware accepts the following optional parameters:
|
|
150
72
|
| cert_path | `""` | Path to the grpc service certificate when connecting to local topaz instance. |
|
151
73
|
| decision | `"allowed"` | The decision that will be used by the middleware when creating an authorizer request. |
|
152
74
|
| logger | `STDOUT` | The logger to be used by the middleware. |
|
153
|
-
| identity_mapping | `{ type: :none }` | The strategy for retrieving the identity, possible values: `:jwt, :sub, :none` |
|
75
|
+
| identity_mapping | `{ type: :none }` | The strategy for retrieving the identity, possible values: `:jwt, :sub, :manual, :none` |
|
154
76
|
| disabled_for | `[{}]` | Which path and actions to skip the authorization for. |
|
155
77
|
| on_unauthorized | `-> { return [403, {}, ["Forbidden"]] }`| A lambda that is executed when the authorization fails. |
|
156
78
|
|
@@ -172,6 +94,14 @@ config.identity_mapping = {
|
|
172
94
|
}
|
173
95
|
```
|
174
96
|
|
97
|
+
```ruby
|
98
|
+
# configure the middleware to use a manual identity.
|
99
|
+
config.identity_mapping = {
|
100
|
+
type: :manual,
|
101
|
+
value: "my-identity",
|
102
|
+
}
|
103
|
+
```
|
104
|
+
|
175
105
|
The whole identity resolution can be overwritten by providing a custom function.
|
176
106
|
```ruby
|
177
107
|
# config/initializers/aserto.rb
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.30.0
|
data/lib/aserto/auth_client.rb
CHANGED
@@ -13,6 +13,7 @@ module Aserto
|
|
13
13
|
INTERNAL_MAPPING = {
|
14
14
|
unknown: Aserto::Authorizer::V2::Api::IdentityType::IDENTITY_TYPE_UNKNOWN,
|
15
15
|
none: Aserto::Authorizer::V2::Api::IdentityType::IDENTITY_TYPE_NONE,
|
16
|
+
manual: Aserto::Authorizer::V2::Api::IdentityType::IDENTITY_TYPE_MANUAL,
|
16
17
|
sub: Aserto::Authorizer::V2::Api::IdentityType::IDENTITY_TYPE_SUB,
|
17
18
|
jwt: Aserto::Authorizer::V2::Api::IdentityType::IDENTITY_TYPE_JWT
|
18
19
|
}.freeze
|
@@ -22,26 +23,52 @@ module Aserto
|
|
22
23
|
def initialize(request)
|
23
24
|
@request = request
|
24
25
|
@config = Aserto.config
|
25
|
-
@client = Aserto::Authorizer::V2::Authorizer::Stub.new(
|
26
|
+
@client = @config.client || Aserto::Authorizer::V2::Authorizer::Stub.new(
|
26
27
|
config.service_url,
|
27
28
|
load_creds
|
28
29
|
)
|
29
30
|
end
|
30
31
|
|
31
32
|
def is
|
32
|
-
exec_is(config.decision)
|
33
|
+
exec_is(request_is(config.decision))
|
34
|
+
end
|
35
|
+
|
36
|
+
def check(object_id:, object_type:, relation:)
|
37
|
+
resource_context_fields = {
|
38
|
+
object_id: object_id,
|
39
|
+
object_type: object_type,
|
40
|
+
relation: relation
|
41
|
+
}.merge!(resource_context.to_h)
|
42
|
+
|
43
|
+
check_resource_context = Google::Protobuf::Struct
|
44
|
+
.from_hash(resource_context_fields.transform_keys!(&:to_s))
|
45
|
+
|
46
|
+
request = Aserto::Authorizer::V2::IsRequest.new(
|
47
|
+
{
|
48
|
+
policy_context: Aserto::Authorizer::V2::Api::PolicyContext.new(
|
49
|
+
{
|
50
|
+
path: config.policy_root ? "#{config.policy_root}.check" : "rebac.check",
|
51
|
+
decisions: [config.decision]
|
52
|
+
}
|
53
|
+
),
|
54
|
+
policy_instance: policy_instance,
|
55
|
+
identity_context: identity_context,
|
56
|
+
resource_context: check_resource_context
|
57
|
+
}
|
58
|
+
)
|
59
|
+
exec_is(request)
|
33
60
|
end
|
34
61
|
|
35
62
|
def allowed?
|
36
|
-
exec_is("allowed")
|
63
|
+
exec_is(request_is("allowed"))
|
37
64
|
end
|
38
65
|
|
39
66
|
def visible?
|
40
|
-
exec_is("visible")
|
67
|
+
exec_is(request_is("visible"))
|
41
68
|
end
|
42
69
|
|
43
70
|
def enabled?
|
44
|
-
exec_is("enabled")
|
71
|
+
exec_is(request_is("enabled"))
|
45
72
|
end
|
46
73
|
|
47
74
|
private
|
@@ -55,15 +82,15 @@ module Aserto
|
|
55
82
|
end
|
56
83
|
end
|
57
84
|
|
58
|
-
def exec_is(
|
85
|
+
def exec_is(request)
|
59
86
|
begin
|
60
|
-
response = client.is(
|
87
|
+
response = client.is(request, headers)
|
61
88
|
rescue GRPC::BadStatus => e
|
62
89
|
Aserto.logger.error(e.inspect)
|
63
90
|
return false
|
64
91
|
end
|
65
92
|
|
66
|
-
decision = response.decisions.find { |el| el.decision ==
|
93
|
+
decision = response.decisions.find { |el| el.decision == request.policy_context.decisions[0] }
|
67
94
|
return false unless decision
|
68
95
|
|
69
96
|
decision.is
|
data/lib/aserto/config.rb
CHANGED
@@ -10,11 +10,31 @@ module Aserto
|
|
10
10
|
super()
|
11
11
|
end
|
12
12
|
|
13
|
-
def request_response(
|
13
|
+
def request_response(request: nil, call: nil, method: nil, metadata: nil)
|
14
|
+
update_metadata(metadata)
|
15
|
+
yield(request, call, method, metadata)
|
16
|
+
end
|
17
|
+
|
18
|
+
def bidi_streamer(requests: nil, call: nil, method: nil, metadata: nil)
|
19
|
+
update_metadata(metadata)
|
20
|
+
yield(requests, call, method, metadata)
|
21
|
+
end
|
22
|
+
|
23
|
+
def client_streamer(requests: nil, call: nil, method: nil, metadata: nil)
|
24
|
+
update_metadata(metadata)
|
25
|
+
yield(requests, call, method, metadata)
|
26
|
+
end
|
27
|
+
|
28
|
+
def server_streamer(request: nil, call: nil, method: nil, metadata: nil)
|
29
|
+
update_metadata(metadata)
|
30
|
+
yield(request, call, method, metadata)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def update_metadata(metadata)
|
14
36
|
metadata["aserto-tenant-id"] = @tenant_id
|
15
37
|
metadata["authorization"] = @api_key
|
16
|
-
|
17
|
-
yield(method, request, call, metadata)
|
18
38
|
end
|
19
39
|
end
|
20
40
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "aserto/directory"
|
4
|
+
require_relative "../interceptors/headers"
|
5
|
+
require_relative "config"
|
6
|
+
require_relative "reader"
|
7
|
+
require_relative "writer"
|
8
|
+
require_relative "model"
|
9
|
+
require_relative "importer"
|
10
|
+
require_relative "exporter"
|
11
|
+
|
12
|
+
module Aserto
|
13
|
+
module Directory
|
14
|
+
module V3
|
15
|
+
class Client
|
16
|
+
extend Forwardable
|
17
|
+
include ::Aserto::Directory::V3::Reader
|
18
|
+
# @!parse include ::Aserto::Directory::V3::Reader
|
19
|
+
|
20
|
+
include ::Aserto::Directory::V3::Writer
|
21
|
+
# @!parse include ::Aserto::Directory::V3::Writer
|
22
|
+
|
23
|
+
include ::Aserto::Directory::V3::Model
|
24
|
+
# @!parse include ::Aserto::Directory::V3::Model
|
25
|
+
|
26
|
+
include ::Aserto::Directory::V3::Importer
|
27
|
+
# @!parse include ::Aserto::Directory::V3::Importer
|
28
|
+
|
29
|
+
include ::Aserto::Directory::V3::Exporter
|
30
|
+
# @!parse include ::Aserto::Directory::V3::Exporter
|
31
|
+
|
32
|
+
# Creates a new Directory V3 Client
|
33
|
+
#
|
34
|
+
# @param config [Aserto::Directory::V3::Config] the service configuration
|
35
|
+
# Base configuration
|
36
|
+
# If non-nil, this configuration is used for any client that doesn't have its own configuration.
|
37
|
+
# If nil, only clients that have their own configuration will be created.
|
38
|
+
#
|
39
|
+
# @example Create a new Directory V3 Client with all the services
|
40
|
+
# client = Aserto::Directory::V3::Client.new(
|
41
|
+
# {
|
42
|
+
# url: "directory.eng.aserto.com:8443",
|
43
|
+
# tenant_id: "tenant-id",
|
44
|
+
# api_key: "basic api-key",
|
45
|
+
# }
|
46
|
+
# )
|
47
|
+
#
|
48
|
+
# @example Create a new Directory V3 Client with reader only
|
49
|
+
# client = Aserto::Directory::V3::Client.new(
|
50
|
+
# {
|
51
|
+
# reader: {
|
52
|
+
# url: "directory.eng.aserto.com:8443",
|
53
|
+
# tenant_id: "tenant-id",
|
54
|
+
# api_key: "basic api-key",
|
55
|
+
# }
|
56
|
+
# }
|
57
|
+
# )
|
58
|
+
#
|
59
|
+
# @return [Aserto::Directory::V3::Client] the new Directory V3 Client
|
60
|
+
def initialize(config)
|
61
|
+
base_config = ::Aserto::Directory::V3::Config.new(config)
|
62
|
+
|
63
|
+
@reader = create_client(:reader, base_config.reader)
|
64
|
+
@writer = create_client(:writer, base_config.writer)
|
65
|
+
@importer = create_client(:importer, base_config.importer)
|
66
|
+
@exporter = create_client(:exporter, base_config.exporter)
|
67
|
+
@model = create_client(:model, base_config.model)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
attr_reader :reader, :writer, :model, :importer, :exporter
|
73
|
+
|
74
|
+
class NullClient
|
75
|
+
def initialize(name)
|
76
|
+
@name = name
|
77
|
+
end
|
78
|
+
|
79
|
+
def method_missing(method, *_args)
|
80
|
+
puts "Cannot call '#{method}': '#{@name.to_s.capitalize}' client is not initialized."
|
81
|
+
end
|
82
|
+
|
83
|
+
def respond_to_missing?(_name, _include_private)
|
84
|
+
true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
SERVICE_MAP = {
|
89
|
+
reader: ::Aserto::Directory::Reader::V3::Reader::Stub,
|
90
|
+
writer: ::Aserto::Directory::Writer::V3::Writer::Stub,
|
91
|
+
importer: ::Aserto::Directory::Importer::V3::Importer::Stub,
|
92
|
+
exporter: ::Aserto::Directory::Exporter::V3::Exporter::Stub,
|
93
|
+
model: ::Aserto::Directory::Model::V3::Model::Stub
|
94
|
+
}.freeze
|
95
|
+
|
96
|
+
def create_client(type, config)
|
97
|
+
return NullClient.new(type) unless config
|
98
|
+
|
99
|
+
SERVICE_MAP[type].new(
|
100
|
+
config.url,
|
101
|
+
config.credentials,
|
102
|
+
interceptors: config.interceptors
|
103
|
+
)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
remove_const(:Reader)
|
108
|
+
remove_const(:Writer)
|
109
|
+
remove_const(:Model)
|
110
|
+
remove_const(:Importer)
|
111
|
+
remove_const(:Exporter)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../interceptors/headers"
|
4
|
+
|
5
|
+
module Aserto
|
6
|
+
module Directory
|
7
|
+
module V3
|
8
|
+
class Config
|
9
|
+
attr_reader :reader, :writer, :importer, :exporter, :model
|
10
|
+
|
11
|
+
def initialize(config)
|
12
|
+
@base = {
|
13
|
+
url: config[:url] || "directory.prod.aserto.com:8443",
|
14
|
+
api_key: config[:api_key],
|
15
|
+
tenant_id: config[:tenant_id],
|
16
|
+
cert_path: config[:cert_path]
|
17
|
+
}
|
18
|
+
|
19
|
+
@reader = build(**(config[:reader] || {}))
|
20
|
+
@writer = build(**(config[:writer] || {}))
|
21
|
+
@importer = build(**(config[:importer] || {}))
|
22
|
+
@exporter = build(**(config[:exporter] || {}))
|
23
|
+
@model = build(**(config[:model] || {}))
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
class BaseConfig
|
29
|
+
attr_reader :url, :credentials, :interceptors
|
30
|
+
|
31
|
+
def initialize(url, credentials, interceptors)
|
32
|
+
@url = url
|
33
|
+
@credentials = credentials
|
34
|
+
@interceptors = interceptors
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def build(
|
39
|
+
url: @base[:url],
|
40
|
+
api_key: @base[:api_key],
|
41
|
+
tenant_id: @base[:tenant_id],
|
42
|
+
cert_path: @base[:cert_path]
|
43
|
+
)
|
44
|
+
|
45
|
+
interceptors = []
|
46
|
+
interceptors = [Interceptors::Headers.new(api_key, tenant_id)] if !api_key.nil? && !tenant_id.nil?
|
47
|
+
BaseConfig.new(url, load_creds(cert_path), interceptors)
|
48
|
+
end
|
49
|
+
|
50
|
+
def load_creds(cert_path)
|
51
|
+
if cert_path && File.file?(cert_path)
|
52
|
+
GRPC::Core::ChannelCredentials.new(File.read(cert_path))
|
53
|
+
else
|
54
|
+
GRPC::Core::ChannelCredentials.new
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aserto
|
4
|
+
module Directory
|
5
|
+
module V3
|
6
|
+
module Exporter
|
7
|
+
DATA_TYPE = {
|
8
|
+
unknown: ::Aserto::Directory::Exporter::V3::Option::OPTION_UNKNOWN,
|
9
|
+
objects: ::Aserto::Directory::Exporter::V3::Option::OPTION_DATA_OBJECTS,
|
10
|
+
relations: ::Aserto::Directory::Exporter::V3::Option::OPTION_DATA_RELATIONS,
|
11
|
+
all: ::Aserto::Directory::Exporter::V3::Option::OPTION_DATA
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
#
|
15
|
+
# Exports directory data
|
16
|
+
#
|
17
|
+
# @param [String] data_type one of [:unknown, :objects, :relations, :all]
|
18
|
+
#
|
19
|
+
def export(data_type: :unknown)
|
20
|
+
data = []
|
21
|
+
operation = exporter.export(
|
22
|
+
Aserto::Directory::Exporter::V3::ExportRequest.new(options: DATA_TYPE[data_type]),
|
23
|
+
return_op: true
|
24
|
+
)
|
25
|
+
|
26
|
+
response = operation.execute
|
27
|
+
response.each { |r| data.push(r) }
|
28
|
+
operation.wait
|
29
|
+
|
30
|
+
data
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aserto
|
4
|
+
module Directory
|
5
|
+
module V3
|
6
|
+
module Importer
|
7
|
+
#
|
8
|
+
# Imports objects and relations
|
9
|
+
#
|
10
|
+
# @param Array[Hash] data to be imported
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# directory.import(
|
14
|
+
# [
|
15
|
+
# { object: { id: "import-user", type: "user" } },
|
16
|
+
# { object: { id: "import-group", type: "group" } },
|
17
|
+
# {
|
18
|
+
# relation: {
|
19
|
+
# object_id: "import-user",
|
20
|
+
# object_type: "user",
|
21
|
+
# relation: "member",
|
22
|
+
# subject_id: "import-group",
|
23
|
+
# subject_type: "group"
|
24
|
+
# }
|
25
|
+
# }
|
26
|
+
# ]
|
27
|
+
# )
|
28
|
+
def import(data)
|
29
|
+
data.map! do |value|
|
30
|
+
Aserto::Directory::Importer::V3::ImportRequest.new(value)
|
31
|
+
end
|
32
|
+
operation = importer.import(data, return_op: true)
|
33
|
+
response = operation.execute
|
34
|
+
response.each { |r| } # ensures that the server sends trailing data
|
35
|
+
operation.wait
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aserto
|
4
|
+
module Directory
|
5
|
+
module V3
|
6
|
+
module Model
|
7
|
+
# rubocop:disable Naming/AccessorMethodName
|
8
|
+
|
9
|
+
# Get the content of a manifest
|
10
|
+
# @return [Hash] { body: String, updated_at: Timestap, etag: String }
|
11
|
+
def get_manifest
|
12
|
+
response = {}
|
13
|
+
manifest_enum = @model.get_manifest(Aserto::Directory::Model::V3::GetManifestRequest.new)
|
14
|
+
manifest_enum.each do |resp|
|
15
|
+
response[:body] = resp.body.data if resp.respond_to?(:body) && !resp.body.nil?
|
16
|
+
if resp.respond_to?(:metadata)
|
17
|
+
response[:updated_at] ||= resp.metadata&.updated_at&.to_time
|
18
|
+
response[:etag] ||= resp.metadata&.etag
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
response
|
23
|
+
end
|
24
|
+
|
25
|
+
# Set the content of a manifest
|
26
|
+
# @param body [String]
|
27
|
+
# @return [Aserto::Directory::Model::V3::SetManifestResponse]
|
28
|
+
def set_manifest(body)
|
29
|
+
@model.set_manifest([Aserto::Directory::Model::V3::SetManifestRequest.new(body: { data: body })])
|
30
|
+
end
|
31
|
+
|
32
|
+
# rubocop:enable Naming/AccessorMethodName
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aserto
|
4
|
+
module Directory
|
5
|
+
module V3
|
6
|
+
module Reader
|
7
|
+
#
|
8
|
+
# find an object by id and type
|
9
|
+
#
|
10
|
+
# @param object_id [String]
|
11
|
+
# @param object_type [String]
|
12
|
+
#
|
13
|
+
# @return [Aserto::Directory::Reader::V3::GetObjectResponse]
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# client.get_object(
|
17
|
+
# object_type: "user",
|
18
|
+
# object_id: "rick@the-citadel.com"
|
19
|
+
# )
|
20
|
+
def get_object(object_id:, object_type:)
|
21
|
+
reader.get_object(
|
22
|
+
Aserto::Directory::Reader::V3::GetObjectRequest.new(
|
23
|
+
object_id: object_id,
|
24
|
+
object_type: object_type
|
25
|
+
)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# list objects by type
|
31
|
+
#
|
32
|
+
# @param object_type [String]
|
33
|
+
# @param page [Hash]
|
34
|
+
# @option page [Integer] :size
|
35
|
+
# @option page [String] :token
|
36
|
+
#
|
37
|
+
# @return [Aserto::Directory::V3::GetObjectsResponse]
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# client.get_objects(
|
41
|
+
# object_type: "user",
|
42
|
+
# page: { size: 2 }
|
43
|
+
# )
|
44
|
+
def get_objects(object_type:, page: { size: 100 })
|
45
|
+
reader.get_objects(
|
46
|
+
Aserto::Directory::Reader::V3::GetObjectsRequest.new(
|
47
|
+
object_type: object_type,
|
48
|
+
page: Aserto::Directory::Common::V3::PaginationRequest.new(page)
|
49
|
+
)
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# find a relation between two objects
|
55
|
+
#
|
56
|
+
# @param [String] object_type
|
57
|
+
# @param [String] object_id
|
58
|
+
# @param [String] relation
|
59
|
+
# @param [String] subject_type
|
60
|
+
# @param [String] subject_id
|
61
|
+
# @param [String] subject_relation
|
62
|
+
# @param [Boolean] with_objects
|
63
|
+
#
|
64
|
+
# @return [Aserto::Directory::Reader::V3::GetRelationResponse]
|
65
|
+
#
|
66
|
+
# @example
|
67
|
+
# client.get_relation(
|
68
|
+
# object_type: "user",
|
69
|
+
# object_id: "rick@the-citadel.com",
|
70
|
+
# relation: "member",
|
71
|
+
# object_type: "group",
|
72
|
+
# object_id: "evil_genius"
|
73
|
+
# )
|
74
|
+
def get_relation(object_type:, object_id:, relation:, subject_type:, subject_id:, subject_relation: "",
|
75
|
+
with_objects: false)
|
76
|
+
reader.get_relation(
|
77
|
+
Aserto::Directory::Reader::V3::GetRelationRequest.new(
|
78
|
+
object_type: object_type,
|
79
|
+
object_id: object_id,
|
80
|
+
relation: relation,
|
81
|
+
subject_type: subject_type,
|
82
|
+
subject_id: subject_id,
|
83
|
+
subject_relation: subject_relation,
|
84
|
+
with_objects: with_objects
|
85
|
+
)
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# List relations between two objects
|
91
|
+
#
|
92
|
+
# @param [String] object_type
|
93
|
+
# @param [String] object_id
|
94
|
+
# @param [String] relation
|
95
|
+
# @param [String] subject_type
|
96
|
+
# @param [String] subject_id
|
97
|
+
# @param [String] subject_relation
|
98
|
+
# @param [Boolean] with_objects
|
99
|
+
# @param page [Hash]
|
100
|
+
# @option page [Integer] :size
|
101
|
+
# @option page [String] :token
|
102
|
+
#
|
103
|
+
# @return [Aserto::Directory::Reader::V3::GetRelationsResponse]
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# client.get_relations(
|
107
|
+
# object_type: "user",
|
108
|
+
# object_id: "rick@the-citadel.com",
|
109
|
+
# relation: "member",
|
110
|
+
# page: { size: 10 }
|
111
|
+
# )
|
112
|
+
def get_relations(object_type: "", object_id: "", relation: "", subject_type: "", subject_id: "",
|
113
|
+
subject_relation: "", with_objects: false, page: { size: 100 })
|
114
|
+
reader.get_relations(
|
115
|
+
Aserto::Directory::Reader::V3::GetRelationsRequest.new(
|
116
|
+
object_type: object_type,
|
117
|
+
object_id: object_id,
|
118
|
+
relation: relation,
|
119
|
+
subject_type: subject_type,
|
120
|
+
subject_id: subject_id,
|
121
|
+
subject_relation: subject_relation,
|
122
|
+
with_objects: with_objects,
|
123
|
+
page: Aserto::Directory::Common::V3::PaginationRequest.new(page)
|
124
|
+
)
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Check relation between two objects
|
130
|
+
#
|
131
|
+
# @param [String] object_type
|
132
|
+
# @param [String] object_id
|
133
|
+
# @param [String] relation
|
134
|
+
# @param [String] subject_type
|
135
|
+
# @param [String] subject_id
|
136
|
+
# @param [Boolean] trace
|
137
|
+
#
|
138
|
+
# @return [Aserto::Directory::Reader::V3::CheckRelationResponse]
|
139
|
+
#
|
140
|
+
# @example
|
141
|
+
# client.check_relation(
|
142
|
+
# object_type: "user",
|
143
|
+
# object_id: "rick@the-citadel.com",
|
144
|
+
# relation: "member",
|
145
|
+
# object_type: "group",
|
146
|
+
# object_id: "evil_genius"
|
147
|
+
# )
|
148
|
+
def check_relation(object_type:, object_id:, relation:, subject_type:, subject_id:, trace: false)
|
149
|
+
reader.check_relation(
|
150
|
+
Aserto::Directory::Reader::V3::CheckRelationRequest.new(
|
151
|
+
object_type: object_type,
|
152
|
+
object_id: object_id,
|
153
|
+
relation: relation,
|
154
|
+
subject_type: subject_type,
|
155
|
+
subject_id: subject_id,
|
156
|
+
trace: trace
|
157
|
+
)
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
#
|
162
|
+
# Check relation between two objects
|
163
|
+
#
|
164
|
+
# @param [String] object_type
|
165
|
+
# @param [String] object_id
|
166
|
+
# @param [String] relation
|
167
|
+
# @param [String] subject_type
|
168
|
+
# @param [String] subject_id
|
169
|
+
# @param [Boolean] trace
|
170
|
+
#
|
171
|
+
# @return [Aserto::Directory::Reader::V3::CheckResponse]
|
172
|
+
#
|
173
|
+
# @example
|
174
|
+
# client.check(
|
175
|
+
# object_type: "user",
|
176
|
+
# object_id: "rick@the-citadel.com",
|
177
|
+
# relation: "member",
|
178
|
+
# object_type: "group",
|
179
|
+
# object_id: "evil_genius"
|
180
|
+
# )
|
181
|
+
def check(object_type:, object_id:, relation:, subject_type:, subject_id:, trace: false)
|
182
|
+
reader.check(
|
183
|
+
Aserto::Directory::Reader::V3::CheckRequest.new(
|
184
|
+
object_type: object_type,
|
185
|
+
object_id: object_id,
|
186
|
+
relation: relation,
|
187
|
+
subject_type: subject_type,
|
188
|
+
subject_id: subject_id,
|
189
|
+
trace: trace
|
190
|
+
)
|
191
|
+
)
|
192
|
+
end
|
193
|
+
|
194
|
+
#
|
195
|
+
# Check permission between two objects
|
196
|
+
#
|
197
|
+
# @param [String] object_type
|
198
|
+
# @param [String] object_id
|
199
|
+
# @param [String] permission
|
200
|
+
# @param [String] subject_type
|
201
|
+
# @param [String] subject_id
|
202
|
+
# @param [Boolean] trace
|
203
|
+
#
|
204
|
+
# @return [Aserto::Directory::Reader::V3::CheckPermissionResponse]
|
205
|
+
#
|
206
|
+
# @example
|
207
|
+
# client.check_permission(
|
208
|
+
# object_type: "user",
|
209
|
+
# object_id: "rick@the-citadel.com",
|
210
|
+
# permission: "read",
|
211
|
+
# object_type: "group",
|
212
|
+
# object_id: "evil_genius"
|
213
|
+
# )
|
214
|
+
def check_permission(object_type:, object_id:, permission:, subject_type:, subject_id:, trace: false)
|
215
|
+
reader.check_permission(
|
216
|
+
Aserto::Directory::Reader::V3::CheckPermissionRequest.new(
|
217
|
+
object_type: object_type,
|
218
|
+
object_id: object_id,
|
219
|
+
permission: permission,
|
220
|
+
subject_type: subject_type,
|
221
|
+
subject_id: subject_id,
|
222
|
+
trace: trace
|
223
|
+
)
|
224
|
+
)
|
225
|
+
end
|
226
|
+
|
227
|
+
#
|
228
|
+
# Returns object graph from anchor to subject or object.
|
229
|
+
#
|
230
|
+
# @param [String] anchor_type <description>
|
231
|
+
# @param [String] anchor_id <description>
|
232
|
+
# @param [String] object_type <description>
|
233
|
+
# @param [String] object_id <description>
|
234
|
+
# @param [String] relation <description>
|
235
|
+
# @param [String] subject_type <description>
|
236
|
+
# @param [String] <description>
|
237
|
+
#
|
238
|
+
# @return [Aserto::Directory::Reader::V3::GetGraphResponse]
|
239
|
+
#
|
240
|
+
# @example
|
241
|
+
# directory.get_graph(
|
242
|
+
# anchor_type: "user",
|
243
|
+
# anchor_id: "rick@the-citadel.com",
|
244
|
+
# subject_id: "rick@the-citadel.com",
|
245
|
+
# subject_type: "user",
|
246
|
+
# relation: "member"
|
247
|
+
# )
|
248
|
+
def get_graph(anchor_type:, anchor_id:, object_type: "", object_id: "", relation: "", subject_type: "",
|
249
|
+
subject_id: "", subject_relation: "")
|
250
|
+
reader.get_graph(
|
251
|
+
Aserto::Directory::Reader::V3::GetGraphRequest.new(
|
252
|
+
anchor_type: anchor_type,
|
253
|
+
anchor_id: anchor_id,
|
254
|
+
object_type: object_type,
|
255
|
+
object_id: object_id,
|
256
|
+
relation: relation,
|
257
|
+
subject_type: subject_type,
|
258
|
+
subject_id: subject_id,
|
259
|
+
subject_relation: subject_relation
|
260
|
+
)
|
261
|
+
)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aserto
|
4
|
+
module Directory
|
5
|
+
module V3
|
6
|
+
module Writer
|
7
|
+
require "google/protobuf/well_known_types"
|
8
|
+
|
9
|
+
#
|
10
|
+
# Create a new object
|
11
|
+
#
|
12
|
+
# @param [String] object_id
|
13
|
+
# @param [String] object_type
|
14
|
+
# @param [String] display_name
|
15
|
+
# @param [Hash] properties
|
16
|
+
# @param [String] etag
|
17
|
+
#
|
18
|
+
# @return [Aserto::Directory::Writer::V3::SetObjectResponse]
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# client.set_object(object_id: "1234", object_type: "user", properties: { email: "test" })
|
22
|
+
def set_object(object_id:, object_type:, display_name: "", properties: {}, etag: nil)
|
23
|
+
writer.set_object(
|
24
|
+
Aserto::Directory::Writer::V3::SetObjectRequest.new(
|
25
|
+
object: {
|
26
|
+
id: object_id,
|
27
|
+
type: object_type,
|
28
|
+
display_name: display_name,
|
29
|
+
properties: Google::Protobuf::Struct.from_hash(properties.transform_keys!(&:to_s)),
|
30
|
+
etag: etag
|
31
|
+
}
|
32
|
+
)
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Delete an object
|
38
|
+
#
|
39
|
+
# @param [String] object_id
|
40
|
+
# @param [String] object_type
|
41
|
+
# @param [Boolean] with_relations
|
42
|
+
#
|
43
|
+
# @return [ Aserto::Directory::Writer::V3::DeleteObjectResponse]
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# client.delete_object(object_id: "1234", object_type: "user")
|
47
|
+
def delete_object(object_id:, object_type:, with_relations: false)
|
48
|
+
writer.delete_object(
|
49
|
+
Aserto::Directory::Writer::V3::DeleteObjectRequest.new(
|
50
|
+
object_id: object_id,
|
51
|
+
object_type: object_type,
|
52
|
+
with_relations: with_relations
|
53
|
+
)
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Creates a relation between two objects
|
59
|
+
#
|
60
|
+
# @param [String] object_type
|
61
|
+
# @param [String] object_id
|
62
|
+
# @param [String] relation
|
63
|
+
# @param [String] subject_type
|
64
|
+
# @param [String] subject_id
|
65
|
+
#
|
66
|
+
# @return [Aserto::Directory::Writer::V3::SetRelationResponse]
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
# client.set_relation(
|
70
|
+
# object_type: "user",
|
71
|
+
# object_id: "rick@the-citadel.com",
|
72
|
+
# relation: "member",
|
73
|
+
# object_type: "group",
|
74
|
+
# object_id: "evil_genius"
|
75
|
+
# )
|
76
|
+
def set_relation(object_type:, object_id:, relation:, subject_type:, subject_id:)
|
77
|
+
writer.set_relation(
|
78
|
+
Aserto::Directory::Writer::V3::SetRelationRequest.new(
|
79
|
+
relation: {
|
80
|
+
object_type: object_type,
|
81
|
+
object_id: object_id,
|
82
|
+
relation: relation,
|
83
|
+
subject_type: subject_type,
|
84
|
+
subject_id: subject_id
|
85
|
+
}
|
86
|
+
)
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Delete a relation between two objects
|
92
|
+
#
|
93
|
+
# @param [String] object_type
|
94
|
+
# @param [String] object_id
|
95
|
+
# @param [String] relation
|
96
|
+
# @param [String] subject_type
|
97
|
+
# @param [String] subject_id
|
98
|
+
# @param [String] subject_relation
|
99
|
+
#
|
100
|
+
# @return [Aserto::Directory::Writer::V3::DeleteRelationRequest]
|
101
|
+
#
|
102
|
+
# @example
|
103
|
+
# client.get_relation(
|
104
|
+
# object_type: "user",
|
105
|
+
# object_id: "rick@the-citadel.com",
|
106
|
+
# relation: "member",
|
107
|
+
# object_type: "group",
|
108
|
+
# object_id: "evil_genius"
|
109
|
+
# )
|
110
|
+
def delete_relation(object_type:, object_id:, relation:, subject_type:, subject_id:, subject_relation: "")
|
111
|
+
writer.delete_relation(
|
112
|
+
Aserto::Directory::Writer::V3::DeleteRelationRequest.new(
|
113
|
+
object_type: object_type,
|
114
|
+
object_id: object_id,
|
115
|
+
relation: relation,
|
116
|
+
subject_type: subject_type,
|
117
|
+
subject_id: subject_id,
|
118
|
+
subject_relation: subject_relation
|
119
|
+
)
|
120
|
+
)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/aserto/errors.rb
CHANGED
@@ -4,20 +4,23 @@ require_relative "identity_mapper/base"
|
|
4
4
|
require_relative "identity_mapper/none"
|
5
5
|
require_relative "identity_mapper/sub"
|
6
6
|
require_relative "identity_mapper/jwt"
|
7
|
+
require_relative "identity_mapper/manual"
|
7
8
|
|
8
9
|
module Aserto
|
9
10
|
module IdentityMapper
|
10
11
|
STRATEGY = {
|
11
12
|
none: Aserto::IdentityMapper::None,
|
13
|
+
manual: Aserto::IdentityMapper::Manual,
|
12
14
|
sub: Aserto::IdentityMapper::Sub,
|
13
15
|
jwt: Aserto::IdentityMapper::Jwt
|
14
16
|
}.freeze
|
15
17
|
|
16
18
|
class << self
|
17
19
|
def execute(request)
|
18
|
-
STRATEGY
|
19
|
-
Aserto.config.identity_mapping[:type].to_sym || :none
|
20
|
-
|
20
|
+
STRATEGY.fetch(
|
21
|
+
Aserto.config.identity_mapping[:type].to_sym || :none,
|
22
|
+
Aserto::IdentityMapper::None
|
23
|
+
).execute(request)
|
21
24
|
end
|
22
25
|
end
|
23
26
|
end
|
data/lib/aserto.rb
CHANGED
@@ -12,6 +12,7 @@ require_relative "aserto/resource_mapper"
|
|
12
12
|
require_relative "aserto/auth_client"
|
13
13
|
require_relative "aserto/errors"
|
14
14
|
require_relative "aserto/directory/client"
|
15
|
+
require_relative "aserto/directory/v3/client"
|
15
16
|
|
16
17
|
module Aserto
|
17
18
|
class << self
|
@@ -52,17 +53,17 @@ module Aserto
|
|
52
53
|
def with_resource_mapper
|
53
54
|
Aserto::ResourceMapper.class_eval do |klass|
|
54
55
|
klass.define_singleton_method(:execute) do |request|
|
55
|
-
|
56
|
-
result = yield(request)
|
57
|
-
unless result.is_a?(Hash)
|
58
|
-
raise Aserto::InvalidResourceMapping, "block must return a hash, got: #{result.class}"
|
59
|
-
end
|
56
|
+
return unless block_given?
|
60
57
|
|
61
|
-
|
62
|
-
|
63
|
-
result.
|
64
|
-
Google::Protobuf::Struct.from_hash(result)
|
58
|
+
result = yield(request)
|
59
|
+
unless result.is_a?(Hash)
|
60
|
+
raise Aserto::InvalidResourceMapping, "block must return a hash, got: #{result.class}"
|
65
61
|
end
|
62
|
+
|
63
|
+
require "google/protobuf/well_known_types"
|
64
|
+
|
65
|
+
result.transform_keys!(&:to_s)
|
66
|
+
Google::Protobuf::Struct.from_hash(result)
|
66
67
|
end
|
67
68
|
end
|
68
69
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aserto
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.30.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aserto
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aserto-authorizer
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.20.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.20.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: aserto-directory
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.30.1
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 0.30.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: jwt
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,10 +83,18 @@ files:
|
|
83
83
|
- lib/aserto/directory/client.rb
|
84
84
|
- lib/aserto/directory/interceptors/headers.rb
|
85
85
|
- lib/aserto/directory/requests.rb
|
86
|
+
- lib/aserto/directory/v3/client.rb
|
87
|
+
- lib/aserto/directory/v3/config.rb
|
88
|
+
- lib/aserto/directory/v3/exporter.rb
|
89
|
+
- lib/aserto/directory/v3/importer.rb
|
90
|
+
- lib/aserto/directory/v3/model.rb
|
91
|
+
- lib/aserto/directory/v3/reader.rb
|
92
|
+
- lib/aserto/directory/v3/writer.rb
|
86
93
|
- lib/aserto/errors.rb
|
87
94
|
- lib/aserto/identity_mapper.rb
|
88
95
|
- lib/aserto/identity_mapper/base.rb
|
89
96
|
- lib/aserto/identity_mapper/jwt.rb
|
97
|
+
- lib/aserto/identity_mapper/manual.rb
|
90
98
|
- lib/aserto/identity_mapper/none.rb
|
91
99
|
- lib/aserto/identity_mapper/sub.rb
|
92
100
|
- lib/aserto/policy_path_mapper.rb
|