API reference¶
Reference of the app's Python code, generated from its docstrings. For the HTTP API, see API and the Swagger UI of a deployment.
App¶
app ¶
App factory, and entry point for development.
The image serves the app with Gunicorn (cloud_registry.app:create_app(), with
FOCA's settings, foca.gunicorn_config); python -m cloud_registry.app runs it
with Flask's development server.
create_app ¶
Create the app from the config, with the registry's checks.
Refuses service clients (FOCA's require_no_service_clients()), as the
app doesn't support them, requires a size limit of request bodies and
that the service info serves the registration rules of the app, adds
missing keys of services, resumes interrupted renames of services (cf.
resume_renames(); also before requests of the registry's operations,
cf. resume_renames_periodically()) and adds the types of registered
services that aren't stored (e.g., on upgrades). Warns of stored
services with invalid URLs or URL schemes that are not allowed, and of
rules that let owners change roles of groups although the rule for
these is changed (cf. warn_owner_rules()).
FOCA validates the config, e.g., that token issuers are trusted (needed
for access control) with an audience, the group settings and the
service info (service_info), refuses to start without admins
(security.access_control.require_admin), warns of placeholders in the
service info, and limits the size of request bodies
(server.max_content_length). Also requires the public URL of the app
(server.public_url), for the Location header of new services.
Gunicorn calls this in each worker, after forking (MongoDB clients must not be shared across forks), so the checks run in each worker; if they fail, the worker fails to boot and Gunicorn exits.
Returns:
| Type | Description |
|---|---|
App
|
Connexion app. |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
Request bodies are not limited in size
( |
ValidationError
|
The config is invalid, e.g., no token issuers are trusted, or the service info is invalid (naming the invalid field). |
ValueError
|
The config is invalid, e.g., groups are mapped without
a group claim, there are no admins (unless admins are optional,
|
main ¶
Run the app with Flask's development server, on the configured port.
For development; not for production.
Configuration¶
custom_config ¶
Cloud Registry custom config models.
IdConfig ¶
Bases: FOCABaseConfig
Model for defining unique identifier for services on cloud registry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
charset
|
A string of allowed characters. |
required | |
length
|
Length of returned string. |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
charset |
str
|
A string of allowed characters. |
length |
PositiveInt
|
Length of returned string. |
Raises:
| Type | Description |
|---|---|
ValidationError
|
The class was instantianted with an illegal
data type, or |
Example
IdConfig( ... charset='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', ... length=6 ... ) IdConfig(charset='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', length=6)
ServicesConfig ¶
Bases: FOCABaseConfig
Model for defining the service database store for cloud registry.
Defines the configurations for service identifiers stored on cloud registry, the schemes allowed in service URLs and the environments allowed for services.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
Unique identifier for a service in cloud registry. |
required | |
url_schemes
|
Schemes allowed in service URLs ( |
required | |
environments
|
Values allowed for the environment of services
( |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
id |
IdConfig
|
Unique identifier for a service in cloud registry. |
url_schemes |
list[Literal['http', 'https']]
|
Schemes allowed in service URLs ( |
environments |
list[Annotated[str, Field(max_length=256)]]
|
Values allowed for the environment of services
( |
Raises:
| Type | Description |
|---|---|
ValidationError
|
The class was instantianted with an illegal
data type, or |
Example
ServicesConfig( ... id=IdConfig( ... charset='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', ... length=6 ... ) ... ) ServicesConfig(id=IdConfig(charset='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', length=6), url_schemes=['https'], environments=['prod', 'test', 'dev', 'staging'])
EndpointsConfig ¶
Bases: FOCABaseConfig
Model for defining endpoint configuration parameters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
services
|
Cloud registry service store parameters. |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
services |
ServicesConfig
|
Cloud registry service store parameters. |
Raises:
| Type | Description |
|---|---|
ValidationError
|
The class was instantianted with an illegal data type. |
Example
EndpointsConfig( ... services=ServicesConfig( ... id=IdConfig( ... charset='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', ... length=6 ... ) ... ) ... ) EndpointsConfig(services=ServicesConfig(id=IdConfig(charset='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', length=6), url_schemes=['https'], environments=['prod', 'test', 'dev', 'staging']))
AccessControlConfig ¶
Bases: FOCABaseConfig
Model for access control settings of the app.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
service_types
|
Who may add service types: |
required | |
max_unused_service_types
|
How many service types that no service
has a user other than an admin may have added, in every mode
( |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
service_types |
Literal['open', 'controlled', 'moderated']
|
Who may add service types: |
max_unused_service_types |
NonNegativeInt | None
|
How many service types that no service
has a user other than an admin may have added, in every mode
( |
Raises:
| Type | Description |
|---|---|
ValidationError
|
The class was instantianted with an illegal
data type, |
Example
AccessControlConfig(service_types='controlled') AccessControlConfig(service_types='controlled', max_unused_service_types=10)
CustomConfig ¶
Bases: FOCABaseConfig
Model for defining the custom configurations for cloud registry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
endpoints
|
Endpoint service configurations for cloud registry. |
required | |
access_control
|
Access control settings of the app. |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
endpoints |
EndpointsConfig
|
Endpoint service configurations for cloud registry. |
access_control |
AccessControlConfig
|
Access control settings of the app. |
Raises:
| Type | Description |
|---|---|
ValidationError
|
The class was instantianted with an illegal data type. |
Example
CustomConfig( ... endpoints=EndpointsConfig( ... services=ServicesConfig( ... id=IdConfig( ... charset='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', ... length=6 ... ) ... ) ... ) ... ) CustomConfig(endpoints=EndpointsConfig(services=ServicesConfig(id=IdConfig(charset='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', length=6), url_schemes=['https'], environments=['prod', 'test', 'dev', 'staging'])), access_control=AccessControlConfig(service_types='open', max_unused_service_types=10))
utils ¶
Typed access to the configuration and collections of the app.
get_collection ¶
Get a collection of the service store of the current app.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Name of the collection, as configured. |
required |
Returns:
| Type | Description |
|---|---|
Collection[dict[str, Any]]
|
MongoDB collection. |
is_date_time ¶
Check whether a string is an RFC 3339 date-time (non-strings pass).
Used for the timestamps of registered services (cf.
cloud_registry.ga4gh.registry.service.check_date_times()).
now ¶
Get the current time, in the precision of the database.
Returns:
| Type | Description |
|---|---|
datetime
|
Current time (UTC), in milliseconds, like MongoDB dates. |
format_time ¶
Format a time as an RFC 3339 date-time in UTC.
Used for the times of verifications and the timestamps of services.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time
|
datetime
|
Time in UTC; naive, as read from the database, or aware. |
required |
Returns:
| Type | Description |
|---|---|
str
|
RFC 3339 date-time in UTC, in milliseconds, e.g., |
str
|
|
Controllers¶
server ¶
Controllers for service endpoints.
getServices ¶
List all services.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of services, without the pending services that the user may |
list[dict[str, Any]]
|
not see (cf. |
getServiceById ¶
Retrieve service by its identifier.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Identifier of service to be retrieved. |
required |
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Service object. |
Raises:
| Type | Description |
|---|---|
NotFound
|
The service doesn't exist, or is
pending and the user may not see it (cf. |
getServiceTypes ¶
List types of services.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of the stored service types (cf. |
list[dict[str, Any]]
|
|
list[dict[str, Any]]
|
artifact and version; without the pending types that the user may |
list[dict[str, Any]]
|
not see (cf. |
postService ¶
Add service with an auto-generated identifier.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
RegisterResponse
|
Identifier of registered service, with status |
RegisterResponse
|
|
deleteService ¶
Delete service.
The roles on the service are revoked first, so that none are left on a
deleted service if deleting it fails, and nobody can act on it in
between; and again afterwards, as roles granted concurrently (after
their request checked that the service exists) would be left otherwise.
Not while the service is renamed (cf. changing()).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Identifier of service to be deleted. |
required |
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
Identifier of deleted service. |
putService ¶
Add/replace service with a user-supplied ID.
Only admins may add services: for others, the service must exist (cf.
RegisterService._replace()), also if it is deleted concurrently. Not
while the service is renamed (cf. changing()).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Identifier of service to be registered/updated. |
required |
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
RegisterResponse
|
Identifier of registered/updated service; for registered services, |
RegisterResponse
|
with status |
service ¶
Controller for registering services.
HiddenDuplicateService ¶
DuplicateService ¶
Bases: CodedConflict
A service with the same URL and API type is registered.
The description names the registered service; for users who may not
read it, responses use hidden() instead.
Attributes:
| Name | Type | Description |
|---|---|---|
HIDDEN_DESCRIPTION |
Description without the registered service. |
|
existing_id |
Identifier of the registered service. |
Create the exception for a service with the same URL and API type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
existing_id
|
str
|
Identifier of the registered service. |
required |
hidden ¶
Get the conflict without the registered service.
Returns:
| Type | Description |
|---|---|
HiddenDuplicateService
|
Conflict whose description doesn't name the registered service, |
HiddenDuplicateService
|
with the same code. |
InvalidService ¶
Bases: BadRequest
The URL or API type of a service, or a link, is missing or invalid.
RegisterService ¶
RegisterService(data: dict[str, Any], id: str | None = None, allow_create: bool = True, keep_timestamps: bool = False)
Class for registering services with the registry.
Initialize service data.
The registry sets the timestamps of services (cf. DATE_TIME_FIELDS):
createdAt when a service is registered, kept on updates, and
updatedAt when it is registered or updated. Timestamps in data
are ignored, unless keep_timestamps is set.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, Any]
|
Service metadata consistent with the
|
required |
id
|
str | None
|
Service identifier. Auto-generated if not provided. |
None
|
allow_create
|
bool
|
With an identifier, whether the service may be
registered if it doesn't exist (e.g., for admins only, cf.
|
True
|
keep_timestamps
|
bool
|
Whether timestamps in |
False
|
Attributes:
| Name | Type | Description |
|---|---|---|
data |
Service metadata; a copy of |
|
replace |
Whether an existing service with the provided identifier
should be replaced. Set to |
|
was_created |
Whether a new service was registered. |
|
verification_removed |
Whether the verification of a replaced
service was removed (cf. |
|
pending |
Whether the service is stored as pending (cf.
|
|
allow_create |
Whether a service with the identifier may be registered. |
|
id_charset |
A string of allowed characters for generating service identifiers. |
|
id_length |
Length of generated service identifiers. |
|
url_schemes |
Schemes allowed in service URLs. |
|
environments |
Values allowed for |
|
db_coll |
Database collection for storing service objects. |
validate ¶
Check the service, before it is registered.
Also called by register_metadata(); callers may call it first, so
that they only act (e.g., store its type) for valid services.
Returns:
| Type | Description |
|---|---|
str
|
Key of the service (cf. |
Raises:
| Type | Description |
|---|---|
InvalidService
|
Cf. |
DuplicateService
|
Another service with the same URL and API type is registered. |
register_metadata ¶
Register service.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
retries
|
int
|
Without a service identifier (POST), how many times
the generation of a random identifier and insertion into the
database are retried on |
9
|
Raises:
| Type | Description |
|---|---|
InvalidService
|
The URL or API type of the service, the URL of a
link, a timestamp (cf. |
DuplicateService
|
Another service with the same URL and API type is registered. |
InternalServerError
|
The service could not be registered within the retries. |
NotFound
|
With an identifier, the
service doesn't exist (also if it was deleted concurrently),
and |
normalize_url ¶
Normalize a service URL for comparisons.
See foca.utils.urls.normalize_url(): common spellings are normalized,
and queries and fragments removed; http and https URLs are different
services.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
Service URL; leading and trailing spaces are ignored. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Normalized URL. |
Raises:
| Type | Description |
|---|---|
InvalidService
|
The URL is invalid, or not an HTTP(S) URL. |
check_url_scheme ¶
Check that a service URL is valid and uses one of the allowed schemes.
See foca.utils.urls.check_url(), which checks the whole URL, like
normalize_url(); for URLs that normalize_url() accepts, only the
scheme can fail the check.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
Service URL; leading and trailing spaces are ignored. |
required |
schemes
|
Collection[str]
|
Allowed schemes, lowercase (cf.
|
required |
Raises:
| Type | Description |
|---|---|
InvalidService
|
The URL is invalid, or its scheme is not one of
|
check_strings ¶
Check that the strings of a request body can be stored.
Unpaired surrogates (e.g., from JSON escapes like \ud800) can't be
encoded (UTF-8), so they fail when stored or logged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
object
|
Request body, or part of it. |
required |
field
|
str
|
Path of |
''
|
Raises:
| Type | Description |
|---|---|
InvalidService
|
A string, or a key, has unpaired surrogates. |
check_date_times ¶
Check that the timestamps of a service are RFC 3339 date-times.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, Any]
|
Service metadata. |
required |
Raises:
| Type | Description |
|---|---|
InvalidService
|
A timestamp (cf. |
check_environment ¶
Check that the environment of a service is allowed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, Any]
|
Service metadata. |
required |
environments
|
list[str]
|
Allowed values (cf.
|
required |
Raises:
| Type | Description |
|---|---|
InvalidService
|
The service has an environment ( |
check_links ¶
Check the URLs of the links of a service (cf. LINK_FIELDS).
Links are not compared, so not normalized, but checked as written (cf.
foca.utils.urls.check_url()).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, Any]
|
Service metadata. |
required |
Raises:
| Type | Description |
|---|---|
InvalidService
|
A URL is invalid, or uses a scheme the field doesn't allow; the message names the field. |
service_key ¶
Get the key identifying a service by its URL and API type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, Any]
|
Service metadata, with |
required |
Returns:
| Type | Description |
|---|---|
str
|
Key from the API type ( |
str
|
case-insensitive and without surrounding whitespace) and the |
str
|
normalized URL, as a JSON array, but not the version, so that |
str
|
upgrading a service to a new API version updates its entry. |
Raises:
| Type | Description |
|---|---|
InvalidService
|
The URL or type is missing or invalid. |
service_exists ¶
Check whether a service is registered.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Service identifier. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
Whether a service with identifier |
add_service_keys ¶
Add keys to services registered without one.
For example, to services registered before keys were introduced.
Concurrent runs of this function are safe (e.g., in each worker of the app on startup): keys added by another run are kept. Services registered without keys while it runs (e.g., by an app of an older version) are not safe: they may be missed or reported as duplicates.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
Services have invalid URLs or types, or the same URL and API type; the message names all of them. |
warn_invalid_urls ¶
Warn of stored services with keys whose URLs are invalid.
For example, URLs accepted before stricter checks were introduced (cf.
"Stored services" in docs/upgrading.md). Such services are still
served, but can only be updated with a valid URL, and their keys may
differ from those of the same URL spelled validly, so registrations of
the service with such a spelling are not detected as duplicates.
Services without keys are checked when keys are added (cf.
add_service_keys()).
warn_environments ¶
Warn of stored services whose environments are not allowed.
For example, of services registered before the environments were checked
(custom.endpoints.services.environments). Such services are still
served, but can only be updated with an allowed environment (or none).
warn_url_schemes ¶
Warn of stored services whose URLs use schemes that are not allowed.
For example, http URLs registered before https became the only
scheme allowed by default (custom.endpoints.services.url_schemes).
Such services are still served, but can only be updated with an allowed
scheme. Services with URLs that aren't strings or valid URLs (cf.
normalize_url()) are skipped.
service_info ¶
Service info fields of the registry, computed from the config.
FOCA serves the service info (GET /service-info) from the config
(service_info.service), with the fields that runtime_fields() returns
(service_info.runtime_fields in config.yaml; called on each request):
the rules for registrations (registration_rules), an extension of the GA4GH
Service schema (RegistryServiceInfo in api/additions.openapi.yaml).
They are built from the config in effect, so that clients (e.g., UIs) can
read them instead of configuring them: the environments
(custom.endpoints.services.environments) and service URL schemes
(url_schemes) allowed, who may add service types
(custom.access_control.service_types), and how many unused types users
other than admins may have added (max_unused_service_types).
registration_rules ¶
Get the rules for registrations of the config in effect.
Must be called in an app context.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Environments allowed for services ( |
dict[str, Any]
|
value is), schemes allowed in service URLs ( |
dict[str, Any]
|
add service types ( |
dict[str, Any]
|
|
dict[str, Any]
|
than admins may have added ( |
dict[str, Any]
|
there is no limit; |
runtime_fields ¶
Get the service info fields computed at runtime.
Called by FOCA in an app context, on startup and for each request for
the service info (service_info.runtime_fields).
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
The registration rules (cf. |
check_service_info_fields ¶
Check that the service info has the registration rules of the app.
Must be called in an app context.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
No service info is configured, its fields aren't
computed by |
verification ¶
Controllers for verifications of services.
Admins verify services they checked (e.g., that they accept tokens of the
deployment's identity provider).
The time of the verification is stored in an internal field of the service
(VERIFIED), not served with the service, so that the GA4GH endpoints are
unchanged. Replacing a service with another URL (as written) or API type
removes it (cf. RegisterService._replace()), deleting a service deletes it
with the service. Verifications are served with the URL and API type of the
service, read from the same document, so that clients can match them against
the services they list.
Only admins may verify services and remove verifications: no policy in
config.yaml allows these operations to other users. Verifications can be
read by anyone, except those of pending services that the user may not see
(cf. cloud_registry.ga4gh.registry.service_types.visible_services()).
VerificationConflict ¶
Bases: CodedConflict
The service has another URL or API type than the one checked.
Create the exception, with a description of what to do.
PendingVerification ¶
Bases: CodedConflict
The service is pending, so it can't be verified.
Create the exception, with a description of what to do.
getServiceVerification ¶
Get the verification of a service.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Verification of the service. |
Raises:
| Type | Description |
|---|---|
NotFound
|
The service doesn't exist, is not verified, or is pending and the user may not see it. |
putServiceVerification ¶
putServiceVerification(serviceId: str, verification: dict[str, Any] | None = None, **kwargs: Any) -> dict[str, Any] | Response
Verify a service, or renew its verification.
If the request names the URL and API type the admin checked, the service is only verified if it has them (checked in the same write), so that a service changed after the admin checked it isn't verified.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
verification
|
dict[str, Any] | None
|
URL and API type the admin checked; optional. |
None
|
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
dict[str, Any] | Response
|
Verification of the service, at the current time (or the later time |
dict[str, Any] | Response
|
of a previous verification); or a problem details response: |
dict[str, Any] | Response
|
if the body is neither JSON nor empty, or the URL in it is invalid; |
dict[str, Any] | Response
|
|
dict[str, Any] | Response
|
checked, or is pending (cf. |
dict[str, Any] | Response
|
pending state (their type is approved) before they are verified. |
Raises:
| Type | Description |
|---|---|
NotFound
|
The service doesn't exist. |
deleteServiceVerification ¶
Remove the verification of a service.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
Identifier of the service. |
Raises:
| Type | Description |
|---|---|
NotFound
|
The service doesn't exist or is not verified. |
getVerifications ¶
List verified services.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
Identifier and verification of each verified service, sorted by |
list[dict[str, Any]]
|
identifier; without the pending services that the user may not see. |
Access control¶
permissions ¶
Access control: policies, service ownership and permission controllers.
The controllers manage permissions on services. Roles
(security.access_control.resource_roles) are granted to users (by their
identifier) or to groups (values of the group claim,
security.access_control.claim_groups); groups count like users, e.g., a group
may be the only owner of a service. Roles are granted, revoked and listed with
FOCA's resource ownership helpers, which keep the last owner of a service
(409 Conflict) and log changes of roles (who changed which role of whom on
which service), for auditing. The app adds the display names of users, the
check that services exist, and which groups users may grant roles to.
Policies are configured in config.yaml (security.access_control) and
enforced by FOCA on all operations that require authentication, using the
model in cloud_registry/access_control/model.conf. Subjects with the role
admin (mapped from group claims via security.access_control.group_mapping,
which deployments set) may do everything.
validate_bearer_token ¶
Validate a bearer token, keeping why it was rejected.
The function that Connexion validates tokens with (x-bearerInfoFunc
in config.yaml): FOCA's validate_token(), but its exception is also
kept for the request, so that check_read_token() can tell why a token
of a public read was rejected without validating it again.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token
|
str
|
Bearer token. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Token info (cf. FOCA's |
Raises:
| Type | Description |
|---|---|
Unauthorized
|
The token was rejected. |
check_read_token ¶
Reject invalid tokens sent with reads that don't require login.
Public reads accept requests without a token (anonymous) and with one
(for the visibility rules of the user; cf. security in the API
specification). Connexion treats requests whose token it can't validate
as anonymous then; so that clients notice (e.g., an expired token) and
log in again, rather than see less without a hint, requests with an
invalid token are rejected as on other operations (401, with an
invalid_token challenge), with the exception kept when Connexion
validated it (cf. validate_bearer_token(); validated again only if
Connexion didn't, e.g., for an empty token). If it can't be validated
for other reasons (e.g., the identity provider is unavailable), the
request stays anonymous, so that public reads keep working, and this is
logged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[..., T]
|
Controller of a read. |
required |
Returns:
| Type | Description |
|---|---|
Callable[..., T]
|
The controller, checking the token first. |
services_path ¶
Get the path of the services, under which roles are granted.
Returns:
| Type | Description |
|---|---|
str
|
Request path of the service collection, including the base path of |
str
|
the API handling the current request. |
service_path ¶
Get resource path of a service, i.e., the path roles are granted on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Request path of the service, including the base path of the API |
str
|
handling the current request. |
may_read_service ¶
Check whether the current user may get a service (GET).
The same decision as for a request to GET /services/{serviceId} with
the token of the current request, and the opt-in roles it asks for
(e.g., admin mode; security.access_control.opt_in_header): the route
of that request decides whether FOCA enforces policies on it (only if
reads require login, cf. "Requiring login for reads" in
docs/access-control.md), and if so, its policies are evaluated by
FOCA's is_permitted(), for the path and method of that request.
The check relies on internals of FOCA (_enforces_access_control()) and
Connexion (connexion_context), as FOCA has no public helper for the
decision on another request; the tests pin it to that of GET.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
Whether the current user may get the service; |
bool
|
isn't routed to |
bool
|
for identifiers with slashes, which are routed to other operations or |
bool
|
none). |
may_add_service_types ¶
Check whether the current user may add service types.
Must be called in a request context.
Returns:
| Type | Description |
|---|---|
bool
|
Whether the user is an admin, or the deployment lets any |
bool
|
authenticated user add types ( |
bool
|
is |
bool
|
|
service_types_pending ¶
Check whether service types that the current user adds are pending.
Must be called in a request context.
Returns:
| Type | Description |
|---|---|
bool
|
Whether the deployment moderates service types |
bool
|
( |
bool
|
is not an admin, so that their new types are pending until an admin |
bool
|
approves them. |
current_user ¶
Get the subject of the current request, for log messages.
Returns:
| Type | Description |
|---|---|
str
|
User identifier, with non-printable characters escaped. |
remember_current_user ¶
Store the current subject's display name for services with a role.
Stored for the services the subject of the current request has a role on. Names are shown to owners and admins listing the roles on a service, so they are only stored per service, once the user has used the registry with a role on it (and are removed with the role). Nothing is stored if the identity provider reports no name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
service_ids
|
list[str]
|
Identifiers of services the user has a role on. |
required |
add_owner ¶
Make the subject of the current request owner of a new service.
See FOCA's add_owner(), which logs the new role.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
Raises:
| Type | Description |
|---|---|
Unauthorized
|
The current request has no subject. |
ValueError
|
The role can't be stored (e.g., for service identifiers with commas). |
remove_owner ¶
Revoke the owner role of the current subject on a service.
Also deletes the name stored for the subject of the current request on the service (e.g., if the service they registered was deleted meanwhile). Other roles on the service (e.g., of the owner of a service registered again with the identifier) are kept.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
Raises:
| Type | Description |
|---|---|
Unauthorized
|
The current request has no subject. |
remove_permissions ¶
Revoke all roles granted on a service.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
getServicePermissions ¶
List roles granted on a service.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
Roles granted on the service: user identifier, display name (if |
list[dict[str, Any]]
|
known) and role of each role granted to a user, followed by group |
list[dict[str, Any]]
|
and role of each role granted to a group. |
check_namespaces ¶
Check that no trusted issuer has a namespace with the segment groups.
Must be called in an app context. The identifiers of its users would
start with groups/, the path of the roles of groups, so their roles
couldn't be changed via the permissions API (cf. _check_user_id()).
Raises:
| Type | Description |
|---|---|
ValueError
|
The namespace of a trusted issuer
( |
warn_owner_rules ¶
Warn if owners can change roles of groups although that rule is changed.
Deployments that change or remove the rule of owners' roles of groups
(OWNER_GROUP_RULE) usually restrict them, but other rules may still
allow them: owners-change-permissions (/services/:id/permissions/*)
also covers the roles of groups (unlike in 0.5.0). So, unless the rule
is kept as in the packaged base config (BASE_CONFIG, which deployments
change with overlays), warns of configured rules (named or a list) for
owners or any user (*) that allow PUT or DELETE on the roles of
both users (EXAMPLE_USERS) and groups (paths of the base rule with
example values), i.e., that cover groups as a side effect; rules for
groups only (e.g., the changed rule itself) are the deployment's choice.
Rules stored via FOCA's permissions API are not checked.
Must be called in an app context.
putServicePermission ¶
Grant a user a role on a service, replacing any other role.
See _put_role().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
userId
|
str
|
User identifier ( |
required |
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
User identifier and granted role. |
Raises:
| Type | Description |
|---|---|
NotFound
|
The user identifier is |
deleteServicePermission ¶
Revoke all roles of a user on a service (cf. _delete_roles()).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
userId
|
str
|
User identifier. |
required |
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
User identifier. |
Raises:
| Type | Description |
|---|---|
NotFound
|
The user identifier is |
putServiceGroupPermission ¶
Grant a group a role on a service, replacing any other role.
See _put_role(). Owners may only grant roles to groups they are members
of (i.e., whose group claim contains it), as they could
otherwise hand a service to any group; admins may grant roles to any group.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
group
|
str
|
Group, i.e., a value of the group claim of its members. |
required |
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Group and granted role. |
Raises:
| Type | Description |
|---|---|
Forbidden
|
The subject of the current request is neither a member of the group nor an admin. |
deleteServiceGroupPermission ¶
Revoke all roles of a group on a service (cf. _delete_roles()).
Any owner may revoke roles of groups, also of groups they are not a member of.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
serviceId
|
str
|
Service identifier. |
required |
group
|
str
|
Group. |
required |
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
The group. |
getMe ¶
Get the current user, and store or refresh their display name.
The name is stored for the services the user has a role on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Further arguments that Connexion passes ( |
{}
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
User identifier ( |
dict[str, Any]
|
request ( |
dict[str, Any]
|
the request asks for the opt-in role |
dict[str, Any]
|
switch to admin mode ( |
dict[str, Any]
|
|
dict[str, Any]
|
without opt-in), whether they may add service types |
dict[str, Any]
|
( |
dict[str, Any]
|
whether these are pending ( |
dict[str, Any]
|
|
dict[str, Any]
|
FOCA's |
dict[str, Any]
|
groups the user is a member of (with |
dict[str, Any]
|
identifier (roles of the user first). Names are only stored for |
dict[str, Any]
|
services with roles granted to the user, as only these are listed |
dict[str, Any]
|
with names (cf. |
Raises:
| Type | Description |
|---|---|
Unauthorized
|
The request has no subject (the endpoint requires authentication, so it always has one). |
Migrations¶
namespace_subjects ¶
Namespace the stored subjects of a token issuer, for several issuers.
With several trusted token issuers, FOCA namespaces subjects by issuer
(<namespace>|<sub>; by default, the namespace is the issuer), so that equal
subjects of different issuers are different users. Subjects stored while the
issuer's subjects were bare (e.g., while it was the only trusted issuer) then
no longer match its users. This migration rewrites them to the namespaced
form:
- users in stored policy rules (
user:<sub>, e.g., owners and maintainers of services, or roles granted via FOCA's permissions API); configured rules (security.access_control.policies) are left as they are, as the app replaces them with those of the config on startup, so change the config instead; - the users who added service types (
created_byofservice_types), for the limit of unused types and the visibility of pending types; - the users of the stored display names (
user_idofuser_names).
Subjects that are already namespaced (with |) are kept, so the migration
can be run again, e.g., after an interrupted run. It then marks the stored
rules as changed, so that the cached enforcers of running app instances load
them again (FOCA's Adapter.mark_changed()).
Run it once the config trusts the new issuer (so that it names the
namespace), with the app's config (FOCA_CONFIG_OVERLAY), e.g., in a
container of the app: python -m cloud_registry.namespace_subjects
<namespace> (add --dry-run to only count the subjects). Until then, the
issuer's stored users lose their roles. Prefer namespace: "" for the
issuer trusted so far, which keeps its subjects bare (see "Several token
issuers" in docs/upgrading.md).
check_namespace ¶
Check that a namespace is one of the trusted issuers.
Must be called in an app context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
namespace
|
str
|
Namespace of the issuer's subjects. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
The namespace is empty or invalid (cf. FOCA's
|
namespace_subjects ¶
Namespace the stored subjects of an issuer.
Must be called in an app context. All bare subjects are taken as the issuer's: run it for the issuer whose subjects were stored bare, i.e., the one trusted so far.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
namespace
|
str
|
Namespace of the issuer's subjects, as in the config
(by default, the issuer, e.g., |
required |
dry_run
|
bool
|
Only count the subjects to rewrite, without changes. |
False
|
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
Numbers of stored policy rules ( |
dict[str, int]
|
( |
dict[str, int]
|
(rewritten, unless |
dict[str, int]
|
users ( |
Raises:
| Type | Description |
|---|---|
ValueError
|
No trusted issuer has the namespace (cf.
|
main ¶
Namespace the stored subjects of an issuer, with the app's config.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
argv
|
Sequence[str] | None
|
Command line arguments, without the program name: the
namespace, and optionally |
None
|
Returns:
| Type | Description |
|---|---|
int
|
The exit status: 0 on success, 1 if the namespace is not that of a |
int
|
trusted issuer. |
Errors¶
exceptions ¶
App exceptions.
CodedConflict ¶
Bases: Conflict
409 Conflict with a stable code, for clients to tell conflicts apart.
The code is served as the extension member code of the problem details
built with problem(), next to the unchanged detail, and documented in
the API specification (schema ConflictError). Conflicts that reach
FOCA's error handler instead (i.e., raised, not returned) get the code of
their entry in exceptions (default: CONFLICT_CODE), so add an entry
for those, as for FOCA's LastOwner.
Attributes:
| Name | Type | Description |
|---|---|---|
problem_code |
str
|
Code of the conflict (kebab case); not |
ServiceBusy ¶
Bases: CodedConflict
The service is being renamed, or changed while it is to be renamed.
Raised by changes of services that waited in vain for a rename to finish,
and returned by renames that waited in vain for changes or another rename
(cf. cloud_registry.ga4gh.registry.locks); in exceptions, so that the
raised one has its code, too.
Create the exception, with a description of what to do.
problem ¶
Build a problem details response with the description of an exception.
See FOCA's problem_response(); conflicts (409) have their code
(code): the one of the class for coded conflicts (cf.
CodedConflict), otherwise the one of their entry in exceptions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exc
|
HTTPException
|
werkzeug exception with a request-specific description. |
required |
extensions
|
Any
|
Extension members of the problem details; for
conflicts, not |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
Problem details response. |