cerbos 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +9 -0
  3. data/CHANGELOG.md +9 -0
  4. data/LICENSE.txt +190 -0
  5. data/README.md +67 -0
  6. data/cerbos.gemspec +36 -0
  7. data/lib/cerbos/client.rb +188 -0
  8. data/lib/cerbos/error.rb +112 -0
  9. data/lib/cerbos/input/attributes.rb +29 -0
  10. data/lib/cerbos/input/aux_data.rb +26 -0
  11. data/lib/cerbos/input/jwt.rb +38 -0
  12. data/lib/cerbos/input/principal.rb +63 -0
  13. data/lib/cerbos/input/resource.rb +63 -0
  14. data/lib/cerbos/input/resource_check.rb +35 -0
  15. data/lib/cerbos/input/resource_query.rb +55 -0
  16. data/lib/cerbos/input.rb +36 -0
  17. data/lib/cerbos/mutual_tls.rb +33 -0
  18. data/lib/cerbos/output/check_resources.rb +226 -0
  19. data/lib/cerbos/output/plan_resources.rb +149 -0
  20. data/lib/cerbos/output/server_info.rb +38 -0
  21. data/lib/cerbos/output.rb +37 -0
  22. data/lib/cerbos/protobuf/cerbos/audit/v1/audit_pb.rb +48 -0
  23. data/lib/cerbos/protobuf/cerbos/effect/v1/effect_pb.rb +23 -0
  24. data/lib/cerbos/protobuf/cerbos/engine/v1/engine_pb.rb +166 -0
  25. data/lib/cerbos/protobuf/cerbos/policy/v1/policy_pb.rb +247 -0
  26. data/lib/cerbos/protobuf/cerbos/request/v1/request_pb.rb +178 -0
  27. data/lib/cerbos/protobuf/cerbos/response/v1/response_pb.rb +230 -0
  28. data/lib/cerbos/protobuf/cerbos/schema/v1/schema_pb.rb +37 -0
  29. data/lib/cerbos/protobuf/cerbos/svc/v1/svc_pb.rb +21 -0
  30. data/lib/cerbos/protobuf/cerbos/svc/v1/svc_services_pb.rb +73 -0
  31. data/lib/cerbos/protobuf/cerbos/telemetry/v1/telemetry_pb.rb +99 -0
  32. data/lib/cerbos/protobuf/google/api/annotations_pb.rb +17 -0
  33. data/lib/cerbos/protobuf/google/api/expr/v1alpha1/checked_pb.rb +117 -0
  34. data/lib/cerbos/protobuf/google/api/expr/v1alpha1/syntax_pb.rb +113 -0
  35. data/lib/cerbos/protobuf/google/api/field_behavior_pb.rb +27 -0
  36. data/lib/cerbos/protobuf/google/api/http_pb.rb +39 -0
  37. data/lib/cerbos/protobuf/protoc-gen-openapiv2/options/annotations_pb.rb +21 -0
  38. data/lib/cerbos/protobuf/protoc-gen-openapiv2/options/openapiv2_pb.rb +200 -0
  39. data/lib/cerbos/protobuf/validate/validate_pb.rb +293 -0
  40. data/lib/cerbos/protobuf.rb +9 -0
  41. data/lib/cerbos/tls.rb +24 -0
  42. data/lib/cerbos/version.rb +6 -0
  43. data/lib/cerbos.rb +22 -0
  44. data/yard_extensions.rb +33 -0
  45. metadata +107 -0
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cerbos
4
+ module Input
5
+ # A JSON Web Token to use as an auxiliary data source, which will be verified against the Cerbos policy decision point (PDP) server's configured JSON Web Key Sets (JWKS) unless verification is disabled on the server.
6
+ #
7
+ # @see https://docs.cerbos.dev/cerbos/latest/configuration/auxdata.html#_jwt Configuring the PDP
8
+ class JWT
9
+ # The encoded JWT.
10
+ #
11
+ # @return [String]
12
+ attr_reader :token
13
+
14
+ # The ID of the JWKS to be used by the PDP server to verify the JWT.
15
+ #
16
+ # @return [String]
17
+ # @return [nil] if not provided (in which case the PDP server must have only one JWKS configured or verification disabled).
18
+ attr_reader :key_set_id
19
+
20
+ # Specify a JWT to use as an auxiliary data source.
21
+ #
22
+ # @param token [String] the encoded JWT.
23
+ # @param key_set_id [String, nil] the ID of the JWKS to be used by the PDP server to verify the JWT. May be set to `nil` if the PDP server only has one JWKS configured or verification disabled.
24
+ def initialize(token:, key_set_id: nil)
25
+ @token = token
26
+ @key_set_id = key_set_id
27
+ end
28
+
29
+ # @private
30
+ def to_protobuf
31
+ Protobuf::Cerbos::Request::V1::AuxData::JWT.new(
32
+ token: token,
33
+ key_set_id: key_set_id
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cerbos
4
+ module Input
5
+ # A principal (often a user, but potentially another actor like a service account) to authorize.
6
+ class Principal
7
+ # A unique identifier for the principal.
8
+ #
9
+ # @return [String]
10
+ attr_reader :id
11
+
12
+ # The roles held by the principal.
13
+ #
14
+ # @return [Array<String>]
15
+ attr_reader :roles
16
+
17
+ # Application-specific attributes describing the principal.
18
+ #
19
+ # @return [Attributes]
20
+ attr_reader :attributes
21
+
22
+ # The policy version to use when authorizing the principal.
23
+ #
24
+ # @return [String]
25
+ # @return [nil] if not provided (in which case the Cerbos policy decision point server's configured default version will be used).
26
+ attr_reader :policy_version
27
+
28
+ # The policy scope to use when authorizing the principal.
29
+ #
30
+ # @return [String]
31
+ # @return [nil] if not provided.
32
+ #
33
+ # @see https://docs.cerbos.dev/cerbos/latest/policies/scoped_policies.html Scoped policies
34
+ attr_reader :scope
35
+
36
+ # Specify a principal to authorize.
37
+ #
38
+ # @param id [String] a unique identifier for the principal.
39
+ # @param roles [Array<String>] the roles held by the principal.
40
+ # @param attributes [Attributes, Hash] application-specific attributes describing the principal.
41
+ # @param policy_version [String, nil] the policy version to use when authorizing the principal (`nil` to use the Cerbos policy decision point server's configured default version).
42
+ # @param scope [String, nil] the policy scope to use when authorizing the principal.
43
+ def initialize(id:, roles:, attributes: {}, policy_version: nil, scope: nil)
44
+ @id = id
45
+ @roles = roles
46
+ @attributes = Input.coerce_required(attributes, Attributes)
47
+ @policy_version = policy_version
48
+ @scope = scope
49
+ end
50
+
51
+ # @private
52
+ def to_protobuf
53
+ Protobuf::Cerbos::Engine::V1::Principal.new(
54
+ id: id,
55
+ roles: roles,
56
+ attr: attributes.to_protobuf,
57
+ policy_version: policy_version,
58
+ scope: scope
59
+ )
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cerbos
4
+ module Input
5
+ # A resource on which to check a principal's permissions.
6
+ class Resource
7
+ # The type of resource.
8
+ #
9
+ # @return [String]
10
+ attr_reader :kind
11
+
12
+ # A unique identifier for the resource.
13
+ #
14
+ # @return [String]
15
+ attr_reader :id
16
+
17
+ # Application-specific attributes describing the resource.
18
+ #
19
+ # @return [Attributes]
20
+ attr_reader :attributes
21
+
22
+ # The policy version to use when checking the principal's permissions on the resource.
23
+ #
24
+ # @return [String]
25
+ # @return [nil] if not provided (in which case the Cerbos policy decision point server's configured default version will be used).
26
+ attr_reader :policy_version
27
+
28
+ # The policy scope to use when checking the principal's permissions on the resource.
29
+ #
30
+ # @return [String]
31
+ # @return [nil] if not provided.
32
+ #
33
+ # @see https://docs.cerbos.dev/cerbos/latest/policies/scoped_policies.html Scoped policies
34
+ attr_reader :scope
35
+
36
+ # Specify a resource on which to check a principal's permissions.
37
+ #
38
+ # @param kind [String] the type of resource.
39
+ # @param id [String] a unique identifier for the resource.
40
+ # @param attributes [Attributes, Hash] application-specific attributes describing the resource.
41
+ # @param policy_version [String, nil] the policy version to use when checking the principal's permissions on the resource (`nil` to use the Cerbos policy decision point server's configured default version).
42
+ # @param scope [String, nil] the policy scope to use when checking the principal's permissions on the resource.
43
+ def initialize(kind:, id:, attributes: {}, policy_version: nil, scope: nil)
44
+ @kind = kind
45
+ @id = id
46
+ @attributes = Input.coerce_required(attributes, Attributes)
47
+ @policy_version = policy_version
48
+ @scope = scope
49
+ end
50
+
51
+ # @private
52
+ def to_protobuf
53
+ Protobuf::Cerbos::Engine::V1::Resource.new(
54
+ kind: kind,
55
+ id: id,
56
+ attr: attributes.to_protobuf,
57
+ policy_version: policy_version,
58
+ scope: scope
59
+ )
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cerbos
4
+ module Input
5
+ # A resource and actions to check a principal's permissions.
6
+ class ResourceCheck
7
+ # The resource to check.
8
+ #
9
+ # @return [Resource]
10
+ attr_reader :resource
11
+
12
+ # The actions to check.
13
+ #
14
+ # @return [Array<String>]
15
+ attr_reader :actions
16
+
17
+ # Specify a resource and actions to check a principal's permissions.
18
+ #
19
+ # @param resource [Resource, Hash] the resource to check.
20
+ # @param actions [Array<String>] the actions to check.
21
+ def initialize(resource:, actions:)
22
+ @resource = Input.coerce_required(resource, Resource)
23
+ @actions = actions
24
+ end
25
+
26
+ # @private
27
+ def to_protobuf
28
+ Protobuf::Cerbos::Request::V1::CheckResourcesRequest::ResourceEntry.new(
29
+ resource: resource.to_protobuf,
30
+ actions: actions
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cerbos
4
+ module Input
5
+ # Partial details of resources to be queried.
6
+ class ResourceQuery
7
+ # The type of resources to be queried.
8
+ #
9
+ # @return [String]
10
+ attr_reader :kind
11
+
12
+ # Any application-specific attributes describing the resources to be queried that are known in advance.
13
+ #
14
+ # @return [Attributes]
15
+ attr_reader :attributes
16
+
17
+ # The policy version to use when planning the query.
18
+ #
19
+ # @return [String]
20
+ # @return [nil] if not provided (in which case the Cerbos policy decision point server's configured default version will be used).
21
+ attr_reader :policy_version
22
+
23
+ # The policy scope to use when planning the query.
24
+ #
25
+ # @return [String]
26
+ # @return [nil] if not provided.
27
+ #
28
+ # @see https://docs.cerbos.dev/cerbos/latest/policies/scoped_policies.html Scoped policies
29
+ attr_reader :scope
30
+
31
+ # Specify partial details of resources to be queried.
32
+ #
33
+ # @param kind [String] the type of resources to be queried.
34
+ # @param attributes [Attributes, Hash] any application-specific attributes describing the resources to be queried that are known in advance.
35
+ # @param policy_version [String, nil] the policy version to use when planning the query (`nil` to use the Cerbos policy decision point server's configured default version).
36
+ # @param scope [String, nil] the policy scope to use when planning the query.
37
+ def initialize(kind:, attributes: {}, policy_version: nil, scope: nil)
38
+ @kind = kind
39
+ @attributes = Input.coerce_required(attributes, Attributes)
40
+ @policy_version = policy_version
41
+ @scope = scope
42
+ end
43
+
44
+ # @private
45
+ def to_protobuf
46
+ Protobuf::Cerbos::Engine::V1::PlanResourcesRequest::Resource.new(
47
+ kind: kind,
48
+ attr: attributes.to_protobuf,
49
+ policy_version: policy_version,
50
+ scope: scope
51
+ )
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cerbos
4
+ # Namespace for objects passed to {Client} methods.
5
+ module Input
6
+ # @private
7
+ def self.coerce_required(value, to_class)
8
+ raise ArgumentError, "Value is required" if value.nil?
9
+ return value if value.is_a?(to_class)
10
+
11
+ to_class.new(**value)
12
+ rescue ArgumentError, TypeError => error
13
+ raise Error::InvalidArgument.new(details: "Failed to create #{to_class.name} from #{value.inspect}: #{error}")
14
+ end
15
+
16
+ # @private
17
+ def self.coerce_optional(value, to_class)
18
+ return nil if value.nil?
19
+
20
+ coerce_required(value, to_class)
21
+ end
22
+
23
+ # @private
24
+ def self.coerce_array(values, to_class)
25
+ values.map { |value| coerce_required(value, to_class) }
26
+ end
27
+ end
28
+ end
29
+
30
+ require_relative "input/attributes"
31
+ require_relative "input/aux_data"
32
+ require_relative "input/jwt"
33
+ require_relative "input/principal"
34
+ require_relative "input/resource"
35
+ require_relative "input/resource_check"
36
+ require_relative "input/resource_query"
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cerbos
4
+ # Settings for encrypting the gRPC connection and authenticating the client with mutual TLS.
5
+ class MutualTLS < TLS
6
+ # The PEM-encoded client certificate.
7
+ #
8
+ # @return [String]
9
+ attr_reader :client_certificate_pem
10
+
11
+ # The PEM-encoded client private key.
12
+ #
13
+ # @return [String]
14
+ attr_reader :client_key_pem
15
+
16
+ # Create settings for encrypting the gRPC connection and authenticating the client with mutual TLS.
17
+ #
18
+ # @param client_certificate_pem [String] the PEM-encoded client certificate.
19
+ # @param client_key_pem [String] the PEM-encoded client private key.
20
+ # @param tls_settings [Hash] arguments to pass to {TLS#initialize}.
21
+ def initialize(client_certificate_pem:, client_key_pem:, **tls_settings)
22
+ super(**tls_settings)
23
+
24
+ @client_certificate_pem = client_certificate_pem
25
+ @client_key_pem = client_key_pem
26
+ end
27
+
28
+ # @private
29
+ def to_channel_credentials
30
+ GRPC::Core::ChannelCredentials.new(root_certificates_pem, client_key_pem, client_certificate_pem)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cerbos
4
+ module Output
5
+ # The outcome of checking a principal's permissions on a set of resources.
6
+ #
7
+ # @see Client#check_resources
8
+ CheckResources = Output.new_class(:request_id, :results) do
9
+ # @!attribute [r] request_id
10
+ # The identifier for tracing the request.
11
+ #
12
+ # @return [String]
13
+
14
+ # @!attribute [r] results
15
+ # The outcomes of the permission checks for each resource.
16
+ #
17
+ # @return [Array<Result>]
18
+
19
+ def self.from_protobuf(check_resources)
20
+ new(
21
+ request_id: check_resources.request_id,
22
+ results: (check_resources.results || []).map { |entry| CheckResources::Result.from_protobuf(entry) }
23
+ )
24
+ end
25
+
26
+ # Check the policy decision was that an action should be allowed for a resource.
27
+ #
28
+ # @param resource [Input::Resource, Hash] the resource search criteria (see {#find_result}).
29
+ # @param action [String] the action to check.
30
+ #
31
+ # @return [Boolean]
32
+ # @return [nil] if the resource or action is not present in the results.
33
+ def allow?(resource:, action:)
34
+ find_result(resource)&.allow?(action)
35
+ end
36
+
37
+ # Find an item from {#results} by resource.
38
+ #
39
+ # @param resource [Input::Resource, Hash] the resource search criteria. `kind` and `id` are required; `policy_version` and `scope` may also be provided if needed to distinguish between multiple results for the same `kind` and `id`.
40
+ #
41
+ # @return [Result]
42
+ # @return [nil] if not found.
43
+ def find_result(resource)
44
+ search = Input.coerce_required(resource, Input::Resource)
45
+ results.find { |result| matching_resource?(search, result.resource) }
46
+ end
47
+
48
+ private
49
+
50
+ def matching_resource?(search, candidate)
51
+ search.kind == candidate.kind &&
52
+ search.id == candidate.id &&
53
+ (search.policy_version.nil? || search.policy_version == candidate.policy_version) &&
54
+ (search.scope.nil? || search.scope == candidate.scope)
55
+ end
56
+ end
57
+
58
+ # The outcome of checking a principal's permissions on single resource.
59
+ CheckResources::Result = Output.new_class(:resource, :actions, :validation_errors, :metadata) do
60
+ # @!attribute [r] resource
61
+ # The resource that was checked.
62
+ #
63
+ # @return [Resource]
64
+
65
+ # @!attribute [r] actions
66
+ # The policy decisions for each action.
67
+ #
68
+ # @return [Hash{String => :EFFECT_ALLOW, :EFFECT_DENY}]
69
+
70
+ # @!attribute [r] validation_errors
71
+ # Any schema validation errors for the principal or resource attributes.
72
+ #
73
+ # @return [Array<ValidationError>]
74
+
75
+ # @!attribute [r] metadata
76
+ # Additional information about how the policy decisions were reached.
77
+ #
78
+ # @return [Metadata]
79
+ # @return [nil] if `include_metadata` was `false`.
80
+
81
+ def self.from_protobuf(entry)
82
+ new(
83
+ resource: CheckResources::Result::Resource.from_protobuf(entry.resource),
84
+ actions: entry.actions.to_h,
85
+ validation_errors: (entry.validation_errors || []).map { |validation_error| CheckResources::Result::ValidationError.from_protobuf(validation_error) },
86
+ metadata: CheckResources::Result::Metadata.from_protobuf(entry.meta)
87
+ )
88
+ end
89
+
90
+ # Check if the policy decision was that a given action should be allowed for the resource.
91
+ #
92
+ # @return [Boolean]
93
+ # @return [nil] if the action is not present in the results.
94
+ def allow?(action)
95
+ actions[action]&.eql?(:EFFECT_ALLOW)
96
+ end
97
+
98
+ # List the actions that should be allowed for the resource.
99
+ #
100
+ # @return [Array<String>]
101
+ def allowed_actions
102
+ actions.filter_map { |action, effect| action if effect == :EFFECT_ALLOW }
103
+ end
104
+ end
105
+
106
+ # A resource that was checked.
107
+ CheckResources::Result::Resource = Output.new_class(:kind, :id, :policy_version, :scope) do
108
+ # @!attribute [r] kind
109
+ # The type of resource.
110
+ #
111
+ # @return [String]
112
+
113
+ # @!attribute [r] id
114
+ # The unique identifier of the resource.
115
+ #
116
+ # @return [String]
117
+
118
+ # @!attribute [r] policy_version
119
+ # The policy version against which the check was performed.
120
+ #
121
+ # @return [String]
122
+
123
+ # @!attribute [r] scope
124
+ # The policy scope against which the check was performed.
125
+ #
126
+ # @return [String]
127
+ #
128
+ # @see https://docs.cerbos.dev/cerbos/latest/policies/scoped_policies.html Scoped policies
129
+
130
+ def self.from_protobuf(resource)
131
+ new(
132
+ kind: resource.kind,
133
+ id: resource.id,
134
+ policy_version: resource.policy_version,
135
+ scope: resource.scope
136
+ )
137
+ end
138
+ end
139
+
140
+ # An error that occurred while validating the principal or resource attributes against a schema.
141
+ CheckResources::Result::ValidationError = Output.new_class(:path, :message, :source) do
142
+ # @!attribute [r] path
143
+ # The path to the attribute that failed validation.
144
+ #
145
+ # @return [String]
146
+
147
+ # @!attribute [r] message
148
+ # The error message.
149
+ #
150
+ # @return [String]
151
+
152
+ # @!attribute [r] source
153
+ # The source of the invalid attributes.
154
+ #
155
+ # @return [:SOURCE_PRINCIPAL, :SOURCE_RESOURCE]
156
+
157
+ def self.from_protobuf(validation_error)
158
+ new(
159
+ path: validation_error.path,
160
+ message: validation_error.message,
161
+ source: validation_error.source
162
+ )
163
+ end
164
+
165
+ # Check if the principal's attributes failed schema validation.
166
+ #
167
+ # @return [Boolean]
168
+ def from_principal?
169
+ source == :SOURCE_PRINCIPAL
170
+ end
171
+
172
+ # Check if the resource's attributes failed schema validation.
173
+ #
174
+ # @return [Boolean]
175
+ def from_resource?
176
+ source == :SOURCE_RESOURCE
177
+ end
178
+ end
179
+
180
+ # Additional information about how policy decisions were reached.
181
+ CheckResources::Result::Metadata = Output.new_class(:actions, :effective_derived_roles) do
182
+ # @!attribute [r] actions
183
+ # Additional information about how the policy decision was reached for each action.
184
+ #
185
+ # @return [Hash{String => Effect}]
186
+
187
+ # @!attribute [r] effective_derived_roles
188
+ # The derived roles that were applied to the principal for the resource.
189
+ #
190
+ # @return [Array<String>]
191
+ #
192
+ # @see https://docs.cerbos.dev/cerbos/latest/policies/derived_roles.html Derived roles
193
+
194
+ def self.from_protobuf(meta)
195
+ return nil if meta.nil?
196
+
197
+ new(
198
+ actions: meta.actions.map { |action, effect| [action, CheckResources::Result::Metadata::Effect.from_protobuf(effect)] }.to_h,
199
+ effective_derived_roles: meta.effective_derived_roles || []
200
+ )
201
+ end
202
+ end
203
+
204
+ # Additional information about how a policy decision was reached.
205
+ CheckResources::Result::Metadata::Effect = Output.new_class(:matched_policy, :matched_scope) do
206
+ # @!attribute [r] matched_policy
207
+ # The policy that was used to make the decision.
208
+ #
209
+ # @return [String]
210
+
211
+ # @!attribute [r] matched_scope
212
+ # The policy scope that was used to make the decision.
213
+ #
214
+ # @return [String]
215
+ #
216
+ # @see https://docs.cerbos.dev/cerbos/latest/policies/scoped_policies.html Scoped policies
217
+
218
+ def self.from_protobuf(effect_meta)
219
+ new(
220
+ matched_policy: effect_meta.matched_policy,
221
+ matched_scope: effect_meta.matched_scope
222
+ )
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cerbos
4
+ module Output
5
+ # A query plan that can be used to obtain a list of resources on which a principal is allowed to perform a particular action.
6
+ #
7
+ # @see Client#plan_resources
8
+ PlanResources = Output.new_class(:request_id, :kind, :condition, :metadata) do
9
+ # @!attribute [r] request_id
10
+ # The identifier for tracing the request.
11
+ #
12
+ # @return [String]
13
+
14
+ # @!attribute [r] kind
15
+ # The type of plan.
16
+ #
17
+ # @return [:KIND_ALWAYS_ALLOWED, :KIND_ALWAYS_DENIED, :KIND_CONDITIONAL]
18
+
19
+ # @!attribute [r] condition
20
+ # The root node of the query condition abstract syntax tree.
21
+ #
22
+ # @return [Expression, Expression::Variable]
23
+ # @return [nil] if the specified action is not conditional (is always allowed or denied) for the principal on resources matching the input.
24
+ #
25
+ # @see #always_allowed?
26
+ # @see #always_denied?
27
+ # @see #conditional?
28
+
29
+ # @!attribute [r] metadata
30
+ # Additional information about the query plan.
31
+ #
32
+ # @return [Metadata]
33
+ # @return [nil] if `include_metadata` was `false`.
34
+
35
+ def self.from_protobuf(plan_resources)
36
+ new(
37
+ request_id: plan_resources.request_id,
38
+ kind: plan_resources.filter.kind,
39
+ condition: PlanResources::Expression::Operand.from_protobuf(plan_resources.filter.condition),
40
+ metadata: PlanResources::Metadata.from_protobuf(plan_resources.meta)
41
+ )
42
+ end
43
+
44
+ # Check if the specified action is always allowed for the principal on resources matching the input.
45
+ #
46
+ # @return [Boolean]
47
+ def always_allowed?
48
+ kind == :KIND_ALWAYS_ALLOWED
49
+ end
50
+
51
+ # Check if the specified action is always denied for the principal on resources matching the input.
52
+ #
53
+ # @return [Boolean]
54
+ def always_denied?
55
+ kind == :KIND_ALWAYS_DENIED
56
+ end
57
+
58
+ # Check if the specified action is conditionally allowed for the principal on resources matching the input.
59
+ #
60
+ # @return [Boolean]
61
+ def conditional?
62
+ kind == :KIND_CONDITIONAL
63
+ end
64
+ end
65
+
66
+ # An abstract syntax tree node representing an expression to evaluate.
67
+ PlanResources::Expression = Output.new_class(:operator, :operands) do
68
+ # @!attribute [r] operator
69
+ # The operator to invoke.
70
+ #
71
+ # @return [String]
72
+
73
+ # @!attribute [r] operands
74
+ # The operands on which to invoke the operator.
75
+ #
76
+ # @return [Array<Expression, Value, Variable>]
77
+
78
+ def self.from_protobuf(expression)
79
+ new(
80
+ operator: expression.operator,
81
+ operands: (expression.operands || []).map { |operand| PlanResources::Expression::Operand.from_protobuf(operand) }
82
+ )
83
+ end
84
+ end
85
+
86
+ # @private
87
+ module PlanResources::Expression::Operand
88
+ def self.from_protobuf(operand)
89
+ if operand.nil?
90
+ nil
91
+ elsif operand.has_expression?
92
+ PlanResources::Expression.from_protobuf(operand.expression)
93
+ elsif operand.has_value?
94
+ PlanResources::Expression::Value.from_protobuf(operand.value)
95
+ else
96
+ PlanResources::Expression::Variable.from_protobuf(operand.variable)
97
+ end
98
+ end
99
+ end
100
+
101
+ # An abstract syntax tree node representing a constant value.
102
+ PlanResources::Expression::Value = Output.new_class(:value) do
103
+ # @!attribute [r] value
104
+ # The constant value.
105
+ #
106
+ # @return [String, Numeric, Boolean, Array, Hash, nil]
107
+
108
+ def self.from_protobuf(value)
109
+ new(value: value.to_ruby(true))
110
+ end
111
+ end
112
+
113
+ # An abstract syntax tree node representing a variable whose value was unknown when producing the query plan.
114
+ PlanResources::Expression::Variable = Output.new_class(:name) do
115
+ # @!attribute [r] name
116
+ # The name of the variable.
117
+ #
118
+ # @return [String]
119
+
120
+ def self.from_protobuf(variable)
121
+ new(name: variable)
122
+ end
123
+ end
124
+
125
+ # Additional information about the query plan.
126
+ PlanResources::Metadata = Output.new_class(:condition_string, :matched_scope) do
127
+ # @!attribute [r] condition_string
128
+ # The query condition abstract syntax tree rendered as a human-readable string, to help with debugging.
129
+ #
130
+ # @return [String]
131
+
132
+ # @!attribute [r] matched_scope
133
+ # The policy scope that was used to plan the query.
134
+ #
135
+ # @return [String]
136
+ #
137
+ # @see https://docs.cerbos.dev/cerbos/latest/policies/scoped_policies.html Scoped policies
138
+
139
+ def self.from_protobuf(meta)
140
+ return nil if meta.nil?
141
+
142
+ new(
143
+ condition_string: meta.filter_debug,
144
+ matched_scope: meta.matched_scope
145
+ )
146
+ end
147
+ end
148
+ end
149
+ end