Droids

Users accessing an API are called droids. They are distinct from persona accounts which belong to specific human beings and may belong to an entity (like an event, assembly, etc.).

Droids must transmit their specific secret with every request in the http header. The key of that header is defined globally in cdedb.models.droid.APIToken.request_header_key.

The API facility is based on three different kinds of objects:

class

A class, that inherits from either StaticAPIToken or DynamicAPIToken. Accordingly there are two “kinds” of classes: “static” and “dynamic”.

Every such class creates its own API with a unique scope of available endpoints and backend methods. The name attribute of the class determines the primary role of resulting “droids” and the “droid name” used to identify instances of the class.

Also called “class of droid” or “class of token”.

token

A set of credentials for a “class” of token.

“static droids” are singletons, i.e. there is exactly one of each class (there is no need to instantiate the class). For every static droid, a “secret” may be set in the API_TOKENS section of the SecretsConfig using the “class name” as a key. This “secret” is the “token” for that “static droid”.

“dynamic droids” have multiple instances, each with their own secet. Here a “token” is a row in a database table. This data is represented by an instance of the “droid class” (which is a subclass of DynamicAPIToken, which also subclasses CdEDataclass).

“token” may also refer to the specially formatted string submitted in the request header of a request accessing an API. More accurately this should be called “token string”.

Might also be called “API token”, “droid token” or “<kind> token”, e.g. “orga token” for orga droids, “resolve token” for the resolve droid, etc.

droid

A User object for a request performed via the API.

When a valid API token is provided in the request header, the session backend returns an instance of the User class for that request. This user object and/or the program submitting the request is called a droid. Unless the droid class defines such an association, this user object is not assicated with a persona.

The “kind” defines how the user object is created, often gaining additional attributes depending on the specific “kind”, like an event id for orga droids.

“droid” is sometimes used to refer to the all users for a class of droid (e.g. “the orga droid” for “all users instances for the ‘orga’ class of droid”.) or the entire API of that class (e.g. “an orga droid endpoint”).

Other nomenclature:

secret

A string of cryptographically random alphanumeric characters of sufficient length, that serves as authentication for an API.

Newly created secrets are 64 hexadecimal characters, i.e. 256 Bits of entropy. Similar to passwords, secrets are stored only in the form of a salted hash.

A newly created secret will be displayed to the user exactly once, after which it is impossible to discern the secret from the stored hash.

token string

A specially formatted string that is sent in the request header, when using an API. This string includes the “droid name” and the “secret”.

class name

A class attribute of a “class”. Used in the “droid name” to indentify the class of droid.

Not to be confused with the actual programmatic name of the class (<class>.name rather than <class>.__name__).

Examples: “resolve”, “quick_partial_export”, “orga”.

droid name

A string, identifying exactly one “token” (see above), i.e. a static droid (which are singletons) or one instance of a dynamic droid.

To determine the class of the droid from the droid name use resolve_droid_name().

Examples: “static/resolve”, “orga/<token_id>”.

static droid

A simple API with only one secret set via the SecretsConfig.

dynamic droid

A complex API for which users can create instances (“tokens”), each with an individual secret.

class cdedb.models.droid.APIToken

Base class for all API Tokens.

This baseclass conveniently bundles some constants and static methods useful in the context of droid APIs.

name: ClassVar[str]

A Unique string identifying the class of droid a token belongs to.

Must match \w+, i.e. be alphanumeric, but may contain _.

Needs to be overridden by subclasses.

classmethod get_droid_name_pattern() Pattern[str]

Return a regex pattern matching all droid names for this class.

This is a classmethod with a constant return per class, which should be cached.

The pattern for a static droid may only match exactly one string.

The pattern for a dynamic droid must have exactly one capture group, which captures the token id in that droids namespace. (i.e. the pattern for orga droids should match for example orga/123 and capture 123).

get_droid_name() str

Return the droid name prefixed with the namespace for this class of droid.

For static droids this is a classmethod, for dynamic droids an instance method.

Static droids live in the static namespace, with their name being their class name.

Dynamic droids each have their own namespaces, which is their class name, while the name is simply the id of the database entry. Dynamic droids also provide a _get_droid_name() class method which is used in get_droid_name() and takes the id as an argument, so the droid name can be determined without needing to create an instance.

Example droid names: static/resolve, orga/<orga_token_id>.

get_token_string(secret: str) str

Return a correctly formatted token string from droid name given secret.

For static droids this is a classmethod, for dynamic droids an instance method.

get_user() User

Return a User object for API requests.

request_header_key = 'X-CdEDB-API-token'

The key of the token string in the request header.

static create_secret() str

Provide a central definition for how to create a secret token.

token_string_pattern = re.compile('CdEDB-(?P<droid_name>\\w+/\\w+)/(?P<secret>[0-9a-zA-Z\\-]+)/')

Basic pattern for all api token. The droid name will be used to decide how to validate the given secret.

class cdedb.models.droid.StaticAPIToken

Bases: APIToken

Base class for static droids. These tokens need not be instantiated.

Static droids have no attributes other than their name, although subclasses might want to override the get_user classmethod.

A static droid API can only be accessed if a secret is set in the API_TOKENS section of the SecretsConfig.

name: ClassVar[str]

Name of the static droid.

class cdedb.models.droid.DynamicAPIToken(id: ProtoID, title: str, notes: Optional[str], etime: datetime)

Bases: CdEDataclass, APIToken

Base class for dynamic droids.

Since this inherits from CdEDataclass, storage and validation are implemented mostly automatically.

Subclasses need to define the class name and the database table the tokens are stored in, as well as fields for any additional database columns.

Fields of dynamic tokens which should not be changeable after creation can be specified as fixed_fields in the form of a tuple of string.

name: ClassVar[str]

Name of the dynamic droid. Also serves as namespace for droid names.

title: str

Configurable title.

notes: Optional[str]

Configurable notes field.

etime: datetime

Expiration time. Set once during creation.

ctime: datetime

Creation time. Automatically set by event backend on creation.

rtime: Optional[datetime] = None

Revocation time. Automatically set by event backend on revocation.

atime: Optional[datetime] = None

Last access time. Automatically updated by session backend on every request.

fixed_fields: ClassVar[tuple[str, ...]] = ('etime',)

Subclasses may define unchangeable fields.

Currently two static APIs exist:

class cdedb.models.droid.ResolveToken

Bases: StaticAPIToken

name: ClassVar[str] = 'resolve'
class cdedb.models.droid.QuickPartialExportToken

Bases: StaticAPIToken

name: ClassVar[str] = 'quick_partial_export'

Currently one dynamic API exists:

class cdedb.models.droid.OrgaToken(id: ProtoID, title: str, notes: Optional[str], etime: datetime, event_id: ID)
OrgaToken(

id: vtypes.ID, title: str, notes: Optional[str], etime: datetime.datetime, ctime: datetime.datetime, rtime: Optional[datetime.datetime], atime: Optional[datetime.datetime], event_id: vtypes.ID)

name: ClassVar[str] = 'orga'
event_id: ID

ID of the event this token is linked to. May not change.

fixed_fields: ClassVar[tuple[str, ...]] = ('etime', 'event_id')

Fields which may not change.

database_table: ClassVar[str] = 'event.orga_apitokens'

Table where data for this class of token is stored.

This class implements the Orga API.