TokenService

class gafaelfawr.services.token.TokenService(*, config, token_cache, token_db_store, token_redis_store, token_change_store, admin_store, session, logger)

Bases: object

Manage tokens.

Parameters:

Methods Summary

audit(*[, fix])

Check Gafaelfawr data stores for consistency.

create_oidc_token(auth_data, *, ip_address)

Add a new OpenID Connect access token.

create_session_token(user_info, *, scopes, ...)

Create a new session token.

create_token_from_admin_request(request, ...)

Create a new service or user token from an admin request.

create_user_token(auth_data, username, *, ...)

Add a new user token.

delete_all_tokens()

Delete all stored tokens.

delete_token(key, auth_data, username, *, ...)

Delete a token.

expire_tokens()

Bookkeeping for expired tokens.

gather_state_metrics(events)

Gather metrics from the stored state and record them.

get_change_history(auth_data, *[, cursor, ...])

Retrieve the change history of a token.

get_data(token)

Retrieve the data for a token from Redis.

get_internal_token(token_data, service, ...)

Get or create a new internal token.

get_notebook_token(token_data, ip_address, *)

Get or create a new notebook token.

get_token_info(key, auth_data, username)

Get information about a token.

get_token_info_unchecked(key[, username])

Get information about a token without checking authorization.

list_tokens(auth_data[, username])

List tokens.

modify_token(key, auth_data[, username, ...])

Modify a token.

truncate_history()

Drop history entries older than the cutoff date.

Methods Documentation

async audit(*, fix=False)

Check Gafaelfawr data stores for consistency.

If any errors are found and Slack is configured, report them to Slack in addition to returning them.

Parameters:

fix (bool, default: False) – Whether to fix problems that the audit code knows how to fix (which is not all alerts).

Returns:

A list of human-readable alert messages formatted in Markdown.

Return type:

list of str

async create_oidc_token(auth_data, *, ip_address)

Add a new OpenID Connect access token.

Parameters:
  • auth_data (TokenData) – The token data for the parent token used for authentication.

  • ip_address (str) – The IP address from which the request came.

Returns:

A new child token of type oidc.

Return type:

Token

Raises:

InvalidGrantError – Raised if the underlying authentication token has expired.

async create_session_token(user_info, *, scopes, ip_address)

Create a new session token.

Parameters:
  • user_info (TokenUserInfo) – The user information to associate with the token.

  • scopes (set[str]) – The scopes of the token. admin:token will be added to these scopes if the user is an admin.

  • ip_address (str) – The IP address from which the request came.

Returns:

The newly-created token.

Return type:

Token

Raises:

PermissionDeniedError – Raised if the provided username is invalid.

async create_token_from_admin_request(request, auth_data, *, ip_address)

Create a new service or user token from an admin request.

Parameters:
  • request (AdminTokenRequest) – The incoming request.

  • auth_data (TokenData) – The data for the authenticated user making the request.

  • ip_address (str | None) – The IP address from which the request came, or None for internal requests by Gafaelfawr.

Returns:

The newly-created token.

Return type:

Token

Raises:
async create_user_token(auth_data, username, *, token_name, scopes, expires=None, ip_address)

Add a new user token.

Parameters:
  • auth_data (TokenData) – The token data for the authentication token of the user creating a user token.

  • username (str) – The username for which to create a token.

  • token_name (str) – The name of the token.

  • scopes (set[str]) – The scopes of the token.

  • expires (datetime | None, default: None) – When the token should expire. If not given, defaults to the expiration of the authentication token taken from data.

  • ip_address (str) – The IP address from which the request came.

Returns:

The newly-created token.

Return type:

Token

Raises:
  • DuplicateTokenNameError – Raised if a token with this name for this user already exists.

  • InvalidExpiresError – Raised if the provided expiration time was invalid.

  • PermissionDeniedError – Raised if the given username didn’t match the user information in the authentication token, or if the specified username is invalid.

Notes

This can only be used by the user themselves, not by a token administrator for a different user, because this API does not provide a way to set the additional user information for the token and instead always takes it from the authentication token.

async delete_all_tokens()

Delete all stored tokens.

This only purges them from Redis, not from the database. It is normally called in combination with truncating all database tables (which is much faster than deleting entries line by line).

Return type:

None

async delete_token(key, auth_data, username, *, ip_address)

Delete a token.

Parameters:
  • key (str) – The key of the token to delete.

  • auth_data (TokenData) – The token data for the authentication token of the user deleting the token.

  • username (str) – Constrain deletions to tokens owned by the given user.

  • ip_address (str) – The IP address from which the request came.

Returns:

True if the token has been deleted, False if it was not found.

Return type:

bool

async expire_tokens()

Bookkeeping for expired tokens.

Token expiration is primarily controlled by the Redis expiration, after which the token disappears from Redis and effectively expires from an authentication standpoint. However, we want to do some additional bookkeeping of expired tokens: remove them from the database and add an expiration entry to the token history table.

This method is meant to be run periodically, outside of any given user request.

Return type:

None

async gather_state_metrics(events)

Gather metrics from the stored state and record them.

Parameters:

events (StateEvents) – Publishers for state events.

Return type:

None

async get_change_history(auth_data, *, cursor=None, limit=None, since=None, until=None, username=None, actor=None, key=None, token=None, token_type=None, ip_or_cidr=None)

Retrieve the change history of a token.

Parameters:
  • auth_data (TokenData) – Authentication information for the user making the request.

  • cursor (TokenChangeHistoryCursor | None, default: None) – A pagination cursor specifying where to start in the results.

  • limit (int | None, default: None) – Limit the number of returned results.

  • since (datetime | None, default: None) – Limit the results to events at or after this time.

  • until (datetime | None, default: None) – Limit the results to events before or at this time.

  • username (str | None, default: None) – Limit the results to tokens owned by this user.

  • actor (str | None, default: None) – Limit the results to actions performed by this user.

  • key (str | None, default: None) – Limit the results to this token and any subtokens of this token. Note that this will currently pick up direct subtokens but not subtokens of subtokens.

  • token (str | None, default: None) – Limit the results to only this token.

  • token_type (TokenType | None, default: None) – Limit the results to tokens of this type.

  • ip_or_cidr (str | None, default: None) – Limit the results to changes made from this IPv4 or IPv6 address or CIDR block.

Returns:

A list of changes matching the search criteria.

Return type:

safir.database.CountedPaginatedList of TokenChangeHistoryEntry

Raises:

InvalidIPAddressError – Raised if the provided argument was syntactically invalid for both an IP address and a CIDR block.

async get_data(token)

Retrieve the data for a token from Redis.

Doubles as a way to check the validity of the token.

Parameters:

token (Token) – The token.

Returns:

The data underlying the token, or None if the token is not found or is invalid.

Return type:

TokenData or None

async get_internal_token(token_data, service, scopes, *, ip_address, minimum_lifetime=None)

Get or create a new internal token.

Parameters:
  • token_data (TokenData) – The authentication data on which to base the new token.

  • service (str) – The internal service to which the token is delegated.

  • scopes (set[str]) – The scopes the new token should have.

  • ip_address (str) – The IP address from which the request came.

  • minimum_lifetime (timedelta | None, default: None) – If set, the minimum required lifetime of the token.

Returns:

The newly-created token.

Return type:

Token

Raises:

PermissionDeniedError – Raised if the username is invalid.

async get_notebook_token(token_data, ip_address, *, minimum_lifetime=None)

Get or create a new notebook token.

Parameters:
  • token_data (TokenData) – The authentication data on which to base the new token.

  • ip_address (str) – The IP address from which the request came.

  • minimum_lifetime (timedelta | None, default: None) – If set, the minimum required lifetime of the token.

Returns:

The newly-created token.

Return type:

Token

Raises:

PermissionDeniedError – Raised if the username is invalid.

async get_token_info(key, auth_data, username)

Get information about a token.

Parameters:
  • key (str) – The key of the token.

  • auth_data (TokenData) – The authentication data of the person requesting the token information, used for authorization checks.

  • username (str) – Constrain the result to tokens from that user and return None if the token exists but is for a different user.

Returns:

Token information from the database, or None if the token was not found or username was given and the token was for another user.

Return type:

TokenInfo or None

Raises:

PermissionDeniedError – Raised if the authenticated user doesn’t have permission to manipulate tokens for that user.

async get_token_info_unchecked(key, username=None)

Get information about a token without checking authorization.

Parameters:
  • key (str) – The key of the token.

  • username (str | None, default: None) – If set, constrain the result to tokens from that user and return None if the token exists but is for a different user.

Returns:

Token information from the database, or None if the token was not found or username was given and the token was for another user.

Return type:

TokenInfo or None

async list_tokens(auth_data, username=None)

List tokens.

Parameters:
  • auth_data (TokenData) – The token data for the authentication token of the user making this modification.

  • username (str | None, default: None) – Limit results to the given username.

Returns:

Information for all matching tokens.

Return type:

list of TokenInfo

Raises:

PermissionDeniedError – Raised if the user whose tokens are being listed does not match the authentication information.

async modify_token(key, auth_data, username=None, *, ip_address, token_name=None, scopes=None, expires=None, no_expire=False)

Modify a token.

Token modification is only allowed for token administrators. Users who want to modify their own tokens should instead create a new token and delete the old one. Arguably, it shouldn’t be allowed for token administrators either, but it allows them to fix bugs and the code is tested and working.

Parameters:
  • key (str) – The key of the token to modify.

  • auth_data (TokenData) – The token data for the authentication token of the user making this modification.

  • username (str | None, default: None) – If given, constrain modifications to tokens owned by the given user.

  • ip_address (str) – The IP address from which the request came.

  • token_name (str | None, default: None) – The new name for the token.

  • scopes (set[str] | None, default: None) – The new scopes for the token.

  • expires (datetime | None, default: None) – The new expiration time for the token.

  • no_expire (bool, default: False) – If set, the token should not expire. This is a separate parameter because passing None to expires is ambiguous.

Returns:

Information for the updated token or None if the token was not found.

Return type:

TokenInfo or None

Raises:
async truncate_history()

Drop history entries older than the cutoff date.

This method is meant to be run periodically, outside of any given user request.

Return type:

None