Open Policy Agent#

No extra dependency: requests use the standard library.

from needtoknow.authorizers.opa import OPAAuthorizer

authorizer = OPAAuthorizer("http://localhost:8181", filter_path="needtoknow/filter")

Your Rego policy decides; needtoknow asks. A complete, tested policy is in examples/opa/needtoknow.rego:

opa run --server examples/opa

Decisions#

check queries decisions_path (default needtoknow/decisions) with this input:

{
  "subject": {"id": "bob", "groups": ["engineering"], "attributes": {}},
  "action": "read",
  "resources": [{"id": "handbook-1", "metadata": {"allowed_groups": ["*"]}}]
}

and expects a list of booleans, one per resource, in order. In the example policy:

decisions := [allowed(r) | some r in input.resources]

Filters for pre-filtering#

If you set filter_path, filter_for queries it with {"subject": ..., "action": ...} and expects a filter in JSON form, or null when the policy cannot express the subject’s access as a filter:

filter := {"or": [
	{"in": {"field": "allowed_users", "values": [input.subject.id]}},
	{"in": {"field": "allowed_groups", "values": array.concat(input.subject.groups, ["*"])}},
]}

The JSON form is {"in": {"field": ..., "values": [...]}}, {"and": [...]} or {"or": [...]}, the same as filter_to_dict. The filter must select exactly what decisions allows, since the vector store applies the filter and decisions re-checks the results. If the two disagree, the re-check removes the extra hits and counts them as violations.

Failing closed#

Every failure raises AuthorizationError: an unreachable server, a timeout (timeout=, 2 seconds by default), a non-200 response, invalid JSON, an undefined decision, a list of the wrong length or type, or a malformed filter. An undefined decision is treated as a deny, never an allow.

headers= adds headers to every request, for example {"Authorization": "Bearer ..."} when OPA requires a token. Connections are reused and transient failures retried once (retries=); a circuit_breaker= pauses requests to a failing server, and ssl_context= sets your own CA or mutual TLS. See resilience.