cerbos 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +9 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE.txt +190 -0
- data/README.md +67 -0
- data/cerbos.gemspec +36 -0
- data/lib/cerbos/client.rb +188 -0
- data/lib/cerbos/error.rb +112 -0
- data/lib/cerbos/input/attributes.rb +29 -0
- data/lib/cerbos/input/aux_data.rb +26 -0
- data/lib/cerbos/input/jwt.rb +38 -0
- data/lib/cerbos/input/principal.rb +63 -0
- data/lib/cerbos/input/resource.rb +63 -0
- data/lib/cerbos/input/resource_check.rb +35 -0
- data/lib/cerbos/input/resource_query.rb +55 -0
- data/lib/cerbos/input.rb +36 -0
- data/lib/cerbos/mutual_tls.rb +33 -0
- data/lib/cerbos/output/check_resources.rb +226 -0
- data/lib/cerbos/output/plan_resources.rb +149 -0
- data/lib/cerbos/output/server_info.rb +38 -0
- data/lib/cerbos/output.rb +37 -0
- data/lib/cerbos/protobuf/cerbos/audit/v1/audit_pb.rb +48 -0
- data/lib/cerbos/protobuf/cerbos/effect/v1/effect_pb.rb +23 -0
- data/lib/cerbos/protobuf/cerbos/engine/v1/engine_pb.rb +166 -0
- data/lib/cerbos/protobuf/cerbos/policy/v1/policy_pb.rb +247 -0
- data/lib/cerbos/protobuf/cerbos/request/v1/request_pb.rb +178 -0
- data/lib/cerbos/protobuf/cerbos/response/v1/response_pb.rb +230 -0
- data/lib/cerbos/protobuf/cerbos/schema/v1/schema_pb.rb +37 -0
- data/lib/cerbos/protobuf/cerbos/svc/v1/svc_pb.rb +21 -0
- data/lib/cerbos/protobuf/cerbos/svc/v1/svc_services_pb.rb +73 -0
- data/lib/cerbos/protobuf/cerbos/telemetry/v1/telemetry_pb.rb +99 -0
- data/lib/cerbos/protobuf/google/api/annotations_pb.rb +17 -0
- data/lib/cerbos/protobuf/google/api/expr/v1alpha1/checked_pb.rb +117 -0
- data/lib/cerbos/protobuf/google/api/expr/v1alpha1/syntax_pb.rb +113 -0
- data/lib/cerbos/protobuf/google/api/field_behavior_pb.rb +27 -0
- data/lib/cerbos/protobuf/google/api/http_pb.rb +39 -0
- data/lib/cerbos/protobuf/protoc-gen-openapiv2/options/annotations_pb.rb +21 -0
- data/lib/cerbos/protobuf/protoc-gen-openapiv2/options/openapiv2_pb.rb +200 -0
- data/lib/cerbos/protobuf/validate/validate_pb.rb +293 -0
- data/lib/cerbos/protobuf.rb +9 -0
- data/lib/cerbos/tls.rb +24 -0
- data/lib/cerbos/version.rb +6 -0
- data/lib/cerbos.rb +22 -0
- data/yard_extensions.rb +33 -0
- 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
|
data/lib/cerbos/input.rb
ADDED
@@ -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
|