Identity and access control

Enduro optionally supports external OpenID Connect (OIDC) compatible providers for authentication and access control. Users can authenticate against the external provider from the dashboard to receive an access token that will be sent to the API on each request.

Enduro uses Attribute Based Access Control (ABAC) to determine the actions and resources to which an authenticated user has access. It looks for a configurable claim in the access token to know the attributes assigned to the user in their external provider.

This section explains how to configure the OIDC provider in Enduro's API and dashboard.

API configuration

Below is a self-documented API section from an Enduro configuration file in TOML format:

[api]
# TCP address for the server to listen on, in the form "host:port".
listen = "0.0.0.0:9000"
# Enable debug mode.
debug = false
# Allowed CORS origin URL.
corsOrigin = "http://localhost"

[api.auth]
# Enable API authentication. OIDC is the only protocol supported at the
# moment. When enabled the API verifies the access token submitted with
# each request. The API client is responsible for obtaining an access
# token from the provider.
enabled = true

[api.auth.oidc]
# OIDC provider URL. Required when auth. is enabled.
providerURL = "http://keycloak:7470/realms/artefactual"
# OIDC client ID. The client ID must be included in the `aud` claim of
# the access token. Required when auth. is enabled.
clientID = "enduro"
# Do not check if the `email_verified` claim is present and set to `true`.
skipEmailVerifiedCheck = false

[api.auth.oidc.abac]
# Enable Attribute Based Access Control (ABAC). If enabled, the API will
# check a configurable multivalue claim against required attributes based
# on each endpoint configuration.
enabled = true
# Claim path of the Enduro attributes within the access token. If the claim
# path is nested then include all fields separated by `claimPathSeparator`
# (see below). E.g. "attributes.enduro" with `claimPathSeparator = "."`.
# Required when ABAC is enabled.
claimPath = "enduro"
# Separator used to split the claim path fields. The default value of "" will
# try to match the claim path as-is to a top-level field from the access token.
claimPathSeparator = ""
# Add a prefix to filter the values of the configured claim. If the claim
# contains values unrelated to Enduro's ABAC, the values relevant to Enduro
# should be prefixed so they are the only values used for access control.
# For example, a claim with values ["enduro:*", "unrelated"] will be filtered
# to a value of ["*"] when `claimValuePrefix = "enduro:"`. The default "" will
# not filter any value.
claimValuePrefix = ""
# Consider the values obtained from the claim as roles and use the `rolesMapping`
# config below to map them to Enduro attributes.
useRoles = false
# A JSON formatted string specifying a mapping from expected roles to Enduro
# attributes. JSON format:
# {
#   "role1": ["attribute1", "atrribute2"],
#   "role2": ["attribute1", "atrribute2", "attribute3", "atrribute4"]
# }
# Example:
# rolesMapping = '{"admin": ["*"], "operator": ["ingest:sips:list", "ingest:sips:read", "ingest:sips:upload", "ingest:sips:workflows:list"], "readonly": ["ingest:sips:list", "ingest:sips:read", "ingest:sips:workflows:list"]}'
rolesMapping = ""

[api.auth.ticket.redis]
# Redis URI to store a ticket used to set a websocket connection.
address = "redis://redis.enduro-sdps:6379"
# Prefix used as part of the ticket keys in Redis.
prefix = "enduro"

Dashboard configuration

The following environment variables can be used to configure the dashboard:

VITE_OIDC_ENABLED
VITE_OIDC_BASE_URL
VITE_OIDC_AUTHORITY
VITE_OIDC_CLIENT_ID
VITE_OIDC_SCOPES
VITE_OIDC_ABAC_ENABLED
VITE_OIDC_ABAC_CLAIM_PATH
VITE_OIDC_ABAC_CLAIM_PATH_SEPARATOR
VITE_OIDC_ABAC_CLAIM_VALUE_PREFIX
VITE_OIDC_ABAC_USE_ROLES
VITE_OIDC_ABAC_ROLES_MAPPING

They must match the ones configured in the API. VITE_OIDC_AUTHORITY has to be the same OIDC provider URL and VITE_OIDC_CLIENT_ID needs to be the same or a trusted client. This client (or the one used in the API configuration, if they are not the same) must be included in the aud claim from the access token. VITE_OIDC_BASE_URL will be used to generate the signin and signout callback URLs, to set them in the OIDC provider for this client, they will be:

  • Signin: VITE_OIDC_BASE_URL + /user/signin-callback
  • Signout: VITE_OIDC_BASE_URL + /user/signout-callback

The authorization flow will request the openid email profile scopes by default. If needed, VITE_OIDC_SCOPES can be used to replace those scopes.

VITE_OIDC_EXTRA_QUERY_PARAMS can be set to specify further query string parameters to be including in the authorization request. E.g, when using Azure AD a resource parameter is required, or using Auth0 you may need to send an audience client ID. The expected format is key-value pairs separated by = (audience=client-id), if more than one parameter is needed they can be added separated by comma (audience=client-id,key=value).

The ABAC variables will work in the same way as they do in the API, they are explained in detail in the API configuration comments above.

These environment variables can be set at build time, or they can be replaced in the final assets. For example, the following script uses envsubst to do the replacement:

#!/usr/bin/env bash

ENDURO_DASHBOARD_ROOT=/usr/lib/enduro-dashboard
TMP_DIR=/tmp/inject_vite_envs
mkdir $TMP_DIR

# Get a comma delimited list of env var names starting with "VITE"
VITE_ENVS=$(printenv | awk -F= '$1 ~ /^VITE/ {print $1}' | sed 's/^/\$/g' | paste -sd,);
echo "Vite envs: ${VITE_ENVS}"

# Inject environment variables into distribution files
for file in $ENDURO_DASHBOARD_ROOT/assets/*.js;
do
    echo "Inject VITE environment variables into $(basename $file)"
    envsubst $VITE_ENVS < $file > $TMP_DIR/$(basename $file)
    cp $TMP_DIR/$(basename $file) $file
done

rm -rf $TMP_DIR

Required attributes

The following table shows the attributes required for each API endpoint. The attributes allow a wildcard hierarchical declaration. For example, ingest:sips:* will give access to endpoints requiring ingest:sips:list, ingest:sips:read, etc. The * attribute will provide full access to the API.

Method Endpoint Attributes
GET /ingest/sips ingest:sips:list
GET /ingest/sips/{id} ingest:sips:read
POST /ingest/sips/{id}/confirm ingest:sips:review
POST /ingest/sips/{id}/reject ingest:sips:review
GET /ingest/sips/{id}/workflows ingest:sips:workflows:list
POST /ingest/sips/upload ingest:sips:upload
GET /storage/aips storage:aips:list
POST /storage/aips storage:aips:create
GET /storage/aips/{uuid} storage:aips:read
POST /storage/aips/{uuid}/deletion-request storage:aips:deletion:request
POST /storage/aips/{uuid}/deletion-review storage:aips:deletion:review
GET /storage/aips/{uuid}/download storage:aips:download
POST /storage/aips/{uuid}/reject storage:aips:review
GET /storage/aips/{uuid}/store storage:aips:move
POST /storage/aips/{uuid}/store storage:aips:move
POST /storage/aips/{uuid}/submit storage:aips:submit
POST /storage/aips/{uuid}/update storage:aips:submit
POST /storage/aips/{uuid}/workflows storage:aips:workflows:list
GET /storage/locations storage:locations:list
POST /storage/locations storage:locations:create
GET /storage/locations/{uuid} storage:locations:read
GET /storage/locations/{uuid}/aips storage:locations:aips:list