Ory Keto#
Ory Keto stores relationships in the style of Google’s Zanzibar:
Document:plan#view@ann, or Document:pay#view@(Group:hr#members) for everyone in a group.
KetoAuthorizer asks Keto whether the subject may view each chunk’s document. It needs no extra
dependency.
from wardowl import PermissionedRetriever
from wardowl.authorizers.keto import KetoAuthorizer
authorizer = KetoAuthorizer("http://keto:4466") # Keto's read API
retriever = PermissionedRetriever(backend, authorizer)
How chunks map to Keto#
wardowl |
Keto |
|---|---|
The chunk’s document |
Object |
|
Relation |
The subject |
|
The subject id, and each group with group_subjects=True, travel as separate JSON fields
(subject_id, or subject_set with its own namespace, object and relation), so a : or # in a
name cannot change what it refers to.
Checks go to POST /relation-tuples/batch/check, batch_size tuples at a time (10 by default).
Each chunk is one tuple, plus one for each of the subject’s groups with group_subjects=True.
Batch size#
Keto refuses a batch check of more than limit.max_batch_check_size tuples with HTTP 400, and that
limit is 10 unless you configure it. batch_size defaults to the same 10, so the defaults work
together. To check more tuples per request, raise both, the server’s limit first:
# keto.yml
limit:
max_batch_check_size: 100
authorizer = KetoAuthorizer("http://keto:4466", batch_size=100)
A batch_size above the server’s limit makes every check of more tuples than the limit fail, and
the retriever then returns nothing.
Groups from the token#
When group memberships live in your identity provider rather than in Keto, group_subjects=True
also checks the subject set Group:<group>#members for each of the subject’s groups, and allows a
chunk if any check does. The groups are then trusted as given, so they must come from the verified
token, as Security model explains.
Tenants#
Keto’s relationships have no tenant: Group:hr#members is one group, whichever tenant’s directory
it came from, and Document:handbook is one object. With every tenant in one Keto:
Qualify every name that can repeat across tenants, in the relationships and in what you send: document ids (
doc_idacme/handbook) and, withgroup_subjects=True, group names (acme/hr). Otherwise the members of one tenant’shrread what another tenant shared with itshr.Add the tenant check:
AllOf(TenantAuthorizer(), KetoAuthorizer(...))denies another tenant’s chunks before Keto is asked (Combining). Keto alone honours every relationship it holds, a direct grant to a user on another tenant’s document included, whether it was written on purpose, by mistake or by a sync gone wrong.
from wardowl import AllOf, Subject, TenantAuthorizer
authorizer = AllOf(TenantAuthorizer(), KetoAuthorizer("http://keto:4466", group_subjects=True))
subject = Subject(user.id, [f"{user.tenant}/{g}" for g in user.groups], {"tenant": user.tenant})
Pre-filtering#
Keto has no reverse lookup (“which documents may ann view?”), so filter_for returns None and the
retriever post-filters: it searches, then checks each candidate, widening the search until it has
k permitted chunks or reaches max_candidates. For users who may read little of a large corpus,
combine Keto with a filter the store can apply, such as AllOf(TenantAuthorizer(), KetoAuthorizer(...)),
so the search starts inside the subject’s tenant.
Failures#
Every failure raises AuthorizationError, and the retriever returns nothing: an unreachable server,
a malformed answer, a wrong number of results, or a result that carries an error (an unknown
namespace, for example). timeout= (2 seconds by default) bounds the whole call except the DNS
lookup of the host (OPA explains), and an answer larger than
max_response_bytes= (10 MiB by default) is refused. An Authorization header over
http:// to another machine needs allow_insecure=True, as for OPA.
Connections are reused, transient failures are retried once, and a CircuitBreaker can pause
requests to a failing server.
examples/keto/keto.yml and examples/keto/namespaces.ts are the configuration CI uses: an
in-memory store whose Document namespace grants view to users and to the members of groups.