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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7247661363785d8bcce7034a816c1d6c0d59ea054190066b599e590b140cf994
4
- data.tar.gz: f7cc418cdcf5ee823527a13ad959f4c38a53c470396a13c195eb59a298994dbe
3
+ metadata.gz: 4471efcec975432356d584d3d480e0ab503e139787c8f96ab88032ab15356175
4
+ data.tar.gz: aa46126a15fb9f583fd8dc9a1b064889f95ccfa3814bc4c6c7588c0ed7f78175
5
5
  SHA512:
6
- metadata.gz: d6f02241c7687ad870792af0fccde1ea326c2461a3b8535da94f2b309a4d65e2d478cf36fd7754b7b963388ae87e8bd1f38440fb33577a7ec03708fca8181424
7
- data.tar.gz: 80284a19aad72b4064a5626bbbc23aecf5c2ae0ac91066478a09f1643a97543ab5cf6c2d98523c319decd8ad097385aeb54de5705d9061813566f36e2cbd4e75
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 = Aserto::Directory::Client.new(
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
- ### Getting objects and relations
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.20.5
1
+ 0.30.0
@@ -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(decision)
85
+ def exec_is(request)
59
86
  begin
60
- response = client.is(request_is(decision), headers)
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 == 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
@@ -26,6 +26,7 @@ module Aserto
26
26
  DEFAULT_ATTRS = {
27
27
  authorizer_api_key: "",
28
28
  tenant_id: "",
29
+ client: nil,
29
30
  service_url: "localhost:8282",
30
31
  decision: "allowed",
31
32
  disabled_for: [{}],
@@ -10,11 +10,31 @@ module Aserto
10
10
  super()
11
11
  end
12
12
 
13
- def request_response(method:, request:, call:, metadata:)
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
@@ -3,6 +3,7 @@
3
3
  module Aserto
4
4
  class Error < StandardError; end
5
5
  class InvalidResourceMapping < Error; end
6
+ class InvalidIdentityType < Error; end
6
7
 
7
8
  class AccessDenied < Error
8
9
  attr_reader :action, :conditions
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aserto
4
+ module IdentityMapper
5
+ module Manual
6
+ class << self
7
+ def execute(_request)
8
+ {
9
+ type: :manual,
10
+ identity: ::Aserto.config.identity_mapping[:value].to_s
11
+ }
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -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
- ].execute(request)
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
- if block_given?
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
- require "google/protobuf/well_known_types"
62
-
63
- result.transform_keys!(&:to_s)
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.20.5
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-06-23 00:00:00.000000000 Z
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.0.5
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.0.5
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.0.3
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.0.3
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