Class: Cerbos::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/cerbos/client.rb

Overview

A client for interacting with the Cerbos policy decision point (PDP) server over gRPC.

An instance of the client may be shared between threads.

Due to a limitation in the underlying grpc gem, creating a client instance before a process fork is only (experimentally) supported on Linux and requires you to

  • have at least v1.57.0 of the grpc gem installed,
  • set the GRPC_ENABLE_FORK_SUPPORT environment variable to 1,
  • call GRPC.prefork before forking,
  • call GRPC.postfork_parent in the parent process after forking, and
  • call GRPC.postfork_child in the child processes after forking.

Otherwise, if your application runs on a forking webserver (for example, Puma in clustered mode), then you'll need to ensure that you only create client instances in the child (worker) processes.

Instance Method Summary collapse

Constructor Details

#initialize(target, tls:, grpc_channel_args: {}, grpc_metadata: {}, on_validation_error: :return, playground_instance: nil, timeout: nil) ⇒ Client

Create a client for interacting with the Cerbos PDP server over gRPC.

Examples:

Connect via TCP with no encryption

client = Cerbos::Client.new("localhost:3593", tls: false)

Connect via a Unix socket with no encryption

client = Cerbos::Client.new("unix:/var/run/cerbos.grpc.sock", tls: false)

Connect to the hosted demo PDP to experiment in the playground

client = Cerbos::Client.new("demo-pdp.cerbos.cloud", tls: Cerbos::TLS.new, playground_instance: "gE623b0180QlsG5a4QIN6UOZ6f3iSFW2")

Raise an error when input fails schema validation

client = Cerbos::Client.new("localhost:3593", tls: false, on_validation_error: :raise)

Invoke a callback when input fails schema validation

client = Cerbos::Client.new("localhost:3593", tls: false, on_validation_error: ->(validation_errors) { do_something_with validation_errors })

Parameters:

  • target (String)

    Cerbos PDP server address ("host", "host:port", or "unix:/path/to/socket").

  • tls (TLS, MutualTLS, false)

    gRPC connection encryption settings (false for plaintext).

  • grpc_channel_args (Hash{String, Symbol => String, Integer}) (defaults to: {})

    low-level settings for the gRPC channel (see available keys in the gRPC documentation).

  • grpc_metadata (Hash{String, Symbol => String, Array<String>}) (defaults to: {})

    gRPC metadata (a.k.a. HTTP headers) to add to every request to the PDP.

  • on_validation_error (:return, :raise, #call) (defaults to: :return)

    action to take when input fails schema validation (:return to return the validation errors in the response, :raise to raise Error::ValidationFailed, or a callback to invoke).

  • playground_instance (String, nil) (defaults to: nil)

    identifier of the playground instance to use when prototyping against the hosted demo PDP.

  • timeout (Numeric, nil) (defaults to: nil)

    timeout for gRPC calls, in seconds (nil to never time out).



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/cerbos/client.rb', line 41

def initialize(target, tls:, grpc_channel_args: {}, grpc_metadata: {}, on_validation_error: :return, playground_instance: nil, timeout: nil)
  @grpc_metadata = .transform_keys(&:to_sym)
  @on_validation_error = on_validation_error

  handle_errors do
    credentials = tls ? tls.to_channel_credentials : :this_channel_is_insecure

    unless playground_instance.nil?
      credentials = credentials.compose(GRPC::Core::CallCredentials.new(->(*) { {"playground-instance" => playground_instance} }))
    end

    channel_args = grpc_channel_args.merge({
      "grpc.primary_user_agent" => [grpc_channel_args["grpc.primary_user_agent"], "cerbos-sdk-ruby/#{VERSION}"].compact.join(" ")
    })

    @cerbos_service = Protobuf::Cerbos::Svc::V1::CerbosService::Stub.new(
      target,
      credentials,
      channel_args: channel_args,
      timeout: timeout
    )
  end
end

Instance Method Details

#allow?(principal:, resource:, action:, aux_data: nil, request_id: SecureRandom.uuid, grpc_metadata: {}) ⇒ Boolean

Check if a principal is allowed to perform an action on a resource.

Examples:

client.allow?(
  principal: {id: "[email protected]", roles: ["USER"]},
  resource: {kind: "document", id: "1"},
  action: "view"
) # => true

Parameters:

  • principal (Input::Principal, Hash)

    the principal to check.

  • resource (Input::Resource, Hash)

    the resource to check.

  • action (String)

    the action to check.

  • aux_data (Input::AuxData, Hash, nil) (defaults to: nil)

    auxiliary data.

  • request_id (String) (defaults to: SecureRandom.uuid)

    identifier for tracing the request.

  • grpc_metadata (Hash{String, Symbol => String, Array<String>}) (defaults to: {})

    gRPC metadata (a.k.a. HTTP headers) to add to the request.

Returns:

  • (Boolean)


82
83
84
85
86
87
88
89
90
91
# File 'lib/cerbos/client.rb', line 82

def allow?(principal:, resource:, action:, aux_data: nil, request_id: SecureRandom.uuid, grpc_metadata: {})
  check_resource(
    principal: principal,
    resource: resource,
    actions: [action],
    aux_data: aux_data,
    request_id: request_id,
    grpc_metadata: 
  ).allow?(action)
end

#check_resource(principal:, resource:, actions:, aux_data: nil, include_metadata: false, request_id: SecureRandom.uuid, grpc_metadata: {}) ⇒ Output::CheckResources::Result

Check a principal's permissions on a resource.

Examples:

decision = client.check_resource(
  principal: {id: "[email protected]", roles: ["USER"]},
  resource: {kind: "document", id: "1"},
  actions: ["view", "edit"]
)

decision.allow?("view") # => true

Parameters:

  • principal (Input::Principal, Hash)

    the principal to check.

  • resource (Input::Resource, Hash)

    the resource to check.

  • actions (Array<String>)

    the actions to check.

  • aux_data (Input::AuxData, Hash, nil) (defaults to: nil)

    auxiliary data.

  • include_metadata (Boolean) (defaults to: false)

    true to include additional metadata (Output::CheckResources::Result::Metadata) in the results.

  • request_id (String) (defaults to: SecureRandom.uuid)

    identifier for tracing the request.

  • grpc_metadata (Hash{String, Symbol => String, Array<String>}) (defaults to: {})

    gRPC metadata (a.k.a. HTTP headers) to add to the request.

Returns:



113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/cerbos/client.rb', line 113

def check_resource(principal:, resource:, actions:, aux_data: nil, include_metadata: false, request_id: SecureRandom.uuid, grpc_metadata: {})
  handle_errors do
    check_resources(
      principal: principal,
      resources: [Input::ResourceCheck.new(resource: resource, actions: actions)],
      aux_data: aux_data,
      include_metadata: ,
      request_id: request_id,
      grpc_metadata: 
    ).find_result(resource)
  end
end

#check_resources(principal:, resources:, aux_data: nil, include_metadata: false, request_id: SecureRandom.uuid, grpc_metadata: {}) ⇒ Output::CheckResources

Check a principal's permissions on a set of resources.

Examples:

decision = client.check_resources(
  principal: {id: "[email protected]", roles: ["USER"]},
  resources: [
    {
      resource: {kind: "document", id: "1"},
      actions: ["view", "edit"]
    },
    {
      resource: {kind: "image", id: "1"},
      actions: ["delete"]
    }
  ]
)

decision.allow?(resource: {kind: "document", id: "1"}, action: "view") # => true

Parameters:

  • principal (Input::Principal, Hash)

    the principal to check.

  • resources (Array<Input::ResourceCheck, Hash>)

    the resources and actions to check.

  • aux_data (Input::AuxData, Hash, nil) (defaults to: nil)

    auxiliary data.

  • include_metadata (Boolean) (defaults to: false)

    true to include additional metadata (Output::CheckResources::Result::Metadata) in the results.

  • request_id (String) (defaults to: SecureRandom.uuid)

    identifier for tracing the request.

  • grpc_metadata (Hash{String, Symbol => String, Array<String>}) (defaults to: {})

    gRPC metadata (a.k.a. HTTP headers) to add to the request.

Returns:



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/cerbos/client.rb', line 153

def check_resources(principal:, resources:, aux_data: nil, include_metadata: false, request_id: SecureRandom.uuid, grpc_metadata: {})
  handle_errors do
    request = Protobuf::Cerbos::Request::V1::CheckResourcesRequest.new(
      principal: Input.coerce_required(principal, Input::Principal).to_protobuf,
      resources: Input.coerce_array(resources, Input::ResourceCheck).map(&:to_protobuf),
      aux_data: Input.coerce_optional(aux_data, Input::AuxData)&.to_protobuf,
      include_meta: ,
      request_id: request_id
    )

    response = perform_request(@cerbos_service, :check_resources, request, )

    Output::CheckResources.from_protobuf(response).tap do |output|
      handle_validation_errors output
    end
  end
end

#plan_resources(principal:, resource:, action:, aux_data: nil, include_metadata: false, request_id: SecureRandom.uuid, grpc_metadata: {}) ⇒ Output::PlanResources

Produce a query plan that can be used to obtain a list of resources on which a principal is allowed to perform a particular action.

Examples:

plan = client.plan_resources(
  principal: {id: "[email protected]", roles: ["USER"]},
  resource: {kind: "document"},
  action: "view"
)

plan.conditional? # => true
plan.condition # => #<Cerbos::Output::PlanResources::Expression ...>

Parameters:

  • principal (Input::Principal, Hash)

    the principal for whom to plan.

  • resource (Input::ResourceQuery, Hash)

    partial details of the resources for which to plan.

  • action (String)

    the action for which to plan.

  • aux_data (Input::AuxData, Hash, nil) (defaults to: nil)

    auxiliary data.

  • include_metadata (Boolean) (defaults to: false)

    true to include additional metadata (Output::CheckResources::Result::Metadata) in the results.

  • request_id (String) (defaults to: SecureRandom.uuid)

    identifier for tracing the request.

  • grpc_metadata (Hash{String, Symbol => String, Array<String>}) (defaults to: {})

    gRPC metadata (a.k.a. HTTP headers) to add to the request.

Returns:



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/cerbos/client.rb', line 192

def plan_resources(principal:, resource:, action:, aux_data: nil, include_metadata: false, request_id: SecureRandom.uuid, grpc_metadata: {})
  handle_errors do
    request = Protobuf::Cerbos::Request::V1::PlanResourcesRequest.new(
      principal: Input.coerce_required(principal, Input::Principal).to_protobuf,
      resource: Input.coerce_required(resource, Input::ResourceQuery).to_protobuf,
      action: action,
      aux_data: Input.coerce_optional(aux_data, Input::AuxData)&.to_protobuf,
      include_meta: ,
      request_id: request_id
    )

    response = perform_request(@cerbos_service, :plan_resources, request, )

    Output::PlanResources.from_protobuf(response).tap do |output|
      handle_validation_errors output
    end
  end
end

#server_info(grpc_metadata: {}) ⇒ Output::ServerInfo

Retrieve information about the Cerbos PDP server.

Parameters:

  • grpc_metadata (Hash{String, Symbol => String, Array<String>}) (defaults to: {})

    gRPC metadata (a.k.a. HTTP headers) to add to the request.

Returns:



216
217
218
219
220
221
222
223
224
# File 'lib/cerbos/client.rb', line 216

def server_info(grpc_metadata: {})
  handle_errors do
    request = Protobuf::Cerbos::Request::V1::ServerInfoRequest.new

    response = perform_request(@cerbos_service, :server_info, request, )

    Output::ServerInfo.from_protobuf(response)
  end
end