psycodict.grants

Who gets to read and write the relations psycodict creates.

Creating a search table creates several relations – the table itself, its counts and stats tables – and a reload creates and swaps more. Something has to decide what privileges those come into existence with, and psycodict used to decide it in the code: SELECT to lmfdb and webserver, INSERT to webserver, wherever those roles happened to exist. That is the LMFDB’s deployment, not a fact about a PostgreSQL database, and it meant a new table holding whatever you put in it was readable by two roles you may never have heard of.

A GrantPolicy states those privileges explicitly, by relation kind. The default policy grants nothing, so a relation is reachable only by its owner and by whatever the cluster’s own defaults give away; LMFDBGrantPolicy() reproduces what psycodict used to do, for the deployments that want it:

db = PostgresDatabase(grant_policy=LMFDBGrantPolicy())

A policy is authoritative for the relations psycodict applies it to: it revokes the actions it manages from the roles it names before granting, so the result is the policy and not the policy plus whatever was there before.

class psycodict.grants.GrantPolicy(grants: dict = <factory>, missing_role: str = 'error')[source]

Bases: object

The privileges psycodict grants on the relations it creates.

INPUT:

  • grants – a mapping from relation kind (see RELATION_KINDS) to a mapping from action (see GRANT_ACTIONS) to the roles that get it. Kinds left out get nothing.

  • missing_role – what to do when the policy names a role the cluster does not have: "error" (the default) refuses, so that a policy is never half-applied without saying so; "skip" warns and carries on, which is what a development machine without the deployment’s roles wants.

EXAMPLES:

>>> GrantPolicy({"search": {"SELECT": ("readonly",)}})
GrantPolicy(grants={'search': {'SELECT': ('readonly',)}}, missing_role='error')
grants: dict
missing_role: str = 'error'
property roles

Every role this policy mentions.

These are the roles whose privileges psycodict manages: applying the policy revokes the managed actions from them first, so that what a relation ends up with is the policy rather than the policy plus whatever it inherited. Roles the policy does not mention are not touched.

for_kind(kind)[source]

The {action: roles} this policy gives a relation of kind.

psycodict.grants.LMFDBGrantPolicy(missing_role='skip')[source]

The permissions psycodict granted before they were a policy.

SELECT to lmfdb and webserver on search, counts, stats and metadata relations, and INSERT to webserver on counts and stats, which the website needs in order to record the counts it computes. Backup (_oldN) tables get nothing: they hold the data the live table held before a reload, and a rename carries the live table’s privileges over to them, so the policy revokes what the live table had rather than leaving a copy of production readable by the application roles.

INPUT:

  • missing_role – defaults to "skip", since a development database typically has neither role; pass "error" on a deployment that should have both.