Authentication and User Management#

class APITokenScopeEnum[source]#

Bases: str, Enum

Named scopes for API tokens

A token’s scope is what it is allowed to do, relative to its owner’s role - the effective permission can only ever be a restriction of the role, never an extension. A scope is stored and transmitted as a plain string, validated by validate_api_token_scope(); this enum lists the named values that validator accepts. Currently there is exactly one, unlimited, meaning the token carries the owner’s full role. Future scopes may be parameterized (for example, limiting writes to particular projects) and so will be handled by the validator’s grammar rather than listed here. The “everything” scope is always this explicit named value - a null or empty scope must never be interpreted as unlimited access.

unlimited = 'unlimited'#
__init__(*args, **kwds)#
validate_api_token_scope(scope)[source]#

Validates an API token scope, returning its canonical string form

This is the single place scope values are validated, shared by the client models and the server. Today the grammar is trivial - the only valid scope is “unlimited” - and future parameterized scopes extend the grammar here, without changing any field or column type.

Raises ValueError for a scope this version does not recognize.

Parameters:

scope (str | APITokenScopeEnum)

Return type:

str

looks_like_api_token(raw)[source]#

Returns whether a string is shaped like an API token

This is a cheap syntactic check (prefix, length, character set), shared by the client (to fail fast before a network round trip) and the server (to reject obviously-bad input before hashing). It says nothing about whether the token actually exists or is valid.

Parameters:

raw (str)

Return type:

bool

class AuthTypeEnum[source]#

Bases: str, Enum

password = 'password'#
__init__(*args, **kwds)#
is_valid_password(password)[source]#

Checks that a password is acceptable as a new password

This is the password policy applied when a password is being set (adding a user or changing a password). It is deliberately not applied when verifying a password at login time – see the verification-time checks in the user socket – since tightening this policy would otherwise lock out existing users with older, weaker passwords.

Raises an InvalidPasswordError if the password is not acceptable.

Parameters:

password (str)

Return type:

None

is_valid_username(username)[source]#
Parameters:

username (str)

Return type:

None

is_valid_groupname(groupname)[source]#
Parameters:

groupname (str)

Return type:

None

class GroupInfo[source]#

Bases: BaseModel

Information about a group

Fields#

Field

Type

Required

Default

description

str

No

''

groupname

str

Yes

id

int | None

No

None

id: int | None#

ID of the group

groupname: str#

The name of the group

description: str#

Text description of the group

class UserInfo[source]#

Bases: BaseModel

Information about a user

Fields#

Field

Type

Required

Default

Constraints

auth_type

AuthTypeEnum

No

<AuthTypeEnum.password: 'password'>

email

str

No

''

max_length=128

enabled

bool

Yes

fullname

str

No

''

max_length=128

groups

list[str]

No

[]

id

int | None

No

None

organization

str

No

''

max_length=128

role

str

Yes

username

str

Yes

id: int | None#

The id of the user

auth_type: AuthTypeEnum#

Type of authentication the user uses

username: str#

The username of this user

role: str#

The role this user belongs to

groups: list[str]#

Groups this user belongs to

enabled: bool#

Whether this user is enabled or not

fullname: Annotated[str, StringConstraints(strip_whitespace=None, to_upper=None, to_lower=None, strict=None, min_length=None, max_length=128, pattern=None, ascii_only=None)]#

The full name or description of the user

Constraints:
  • max_length = 128

organization: Annotated[str, StringConstraints(strip_whitespace=None, to_upper=None, to_lower=None, strict=None, min_length=None, max_length=128, pattern=None, ascii_only=None)]#

The organization the user belongs to

Constraints:
  • max_length = 128

email: Annotated[str, StringConstraints(strip_whitespace=None, to_upper=None, to_lower=None, strict=None, min_length=None, max_length=128, pattern=None, ascii_only=None)]#

The email address for the user

Constraints:
  • max_length = 128

class APIToken[source]#

Bases: BaseModel

Metadata about a long-lived API token

This never contains the token itself. The plaintext token is shown exactly once, when the token is created (see NewAPIToken); afterwards only this metadata is available.

Fields#

Field

Type

Required

Default

created_at

datetime

Yes

expires_at

datetime | None

No

None

id

int

Yes

last_used_at

datetime | None

No

None

name

str

Yes

scope

str

No

'unlimited'

token_prefix

str

Yes

user_id

int

Yes

id: int#

The id of the token (used to revoke it)

user_id: int#

The id of the user the token authenticates as

token_prefix: str#

The first few characters of the token, for identifying it in a listing

name: str#

The name given to the token when it was created (unique among the user’s tokens)

scope: str#

What the token is allowed to do (see APITokenScopeEnum). Deliberately a plain string here, like UserInfo.role, so an older client can still parse listings from a newer server that has scopes this client does not know about

created_at: datetime#

When the token was created

expires_at: datetime | None#

When the token expires, or null if it never expires

last_used_at: datetime | None#

Approximate time the token was last presented on a request, or null if never used

class NewAPIToken[source]#

Bases: BaseModel

A newly-created API token, including the plaintext token

The plaintext token is only available here, in the response to creating the token. It is not stored and cannot be retrieved later.

Fields#

Field

Type

Required

Default

info

APIToken

Yes

token

str

Yes

token: str#

The plaintext token. Paste this into a client’s Authorization header. Store it securely - it cannot be retrieved again

info: APIToken#

Metadata about the token

class APITokenCreateBody[source]#

Bases: BaseModel

Options for creating a new API token

Fields#

Field

Type

Required

Default

Constraints

expires_at

AwareDatetime | None

No

None

name

str

Yes

min_length=1, max_length=128

scope

str

No

'unlimited'

name: str#

A name to identify the token. Must be unique among the user’s tokens.

Constraints:
  • min_length = 1

  • max_length = 128

scope: str#

What the token should be allowed to do. Currently only “unlimited” (the owner’s full role) exists. A plain (but validated) string, so the public schema never changes when new scopes are added; a scope this server does not recognize is rejected

expires_at: AwareDatetime | None#

When the token should expire. Must be timezone-aware. Null requests a non-expiring token, subject to the server’s api_token_default_lifetime and api_token_max_lifetime policy

class APITokenModifyBody[source]#

Bases: BaseModel

Changes to an existing API token

Only the name may be changed. Everything else about a token (its secret, owner, scope, and expiration) is fixed at creation; to change those, create a new token and delete the old one.

Fields#

Field

Type

Required

Default

Constraints

name

str

Yes

min_length=1, max_length=128

name: str#

The new name for the token. Must be unique among the user’s tokens.

Constraints:
  • min_length = 1

  • max_length = 128