Configuration

Load the configuration

Canaille can be configured either by a environment variables, environment file, or by a configuration file.

Configuration file

CONFIG

The configuration can be written in toml configuration file which path is passed in the CONFIG environment variable.

config.toml
SECRET_KEY = "very-secret"

[CANAILLE]
NAME = "My organization"

[CANAILLE_SQL]
DATABASE_URI = "postgresql://user:password@localhost/database"
...

You can have a look at the example file for inspiration.

Environment variables

In addition, parameters that have not been set in the configuration file can be read from environment variables. The way environment variables are parsed can be read from the pydantic-settings documentation.

Tip

For environment vars, the separator between sections and variables is a double underscore: __. For instance, the NAME var in the CANAILLE section shown above is CANAILLE__NAME.

Environment file

Any environment variable can also be written in an environment file, which path should be passed in the ENV_FILE environment variable. For instance, set ENV_FILE=.env to load a .env file.

.env
SECRET_KEY="very-secret"
CANAILLE__NAME="My organization"
CANAILLE_SQL__DATABASE_URI="postgresql://user:password@localhost/database"

Configuration methods priority

If a same configuration option is defined by different ways, here is how Canaille will choose which one to use:

  • environment vars have priority over the environment file and the configuration file;

  • environment file will have priority over the configuration file.

Parameters

canaille.app.configuration.RootSettings[source]

The top-level namespace contains the configuration settings unrelated to Canaille.

The configuration parameters from the following libraries can be used:

config.toml
SECRET_KEY = "very-secret"
SERVER_NAME = "auth.mydomain.example"
PREFERRED_URL_SCHEME = false
DEBUG = false

[CANAILLE]
NAME = "My organization"
...
DEBUG: bool = False

The Flask DEBUG configuration setting.

This enables debug options.

Danger

This is useful for development but should be absolutely avoided in production environments.

PREFERRED_URL_SCHEME: str = 'https'

The Flask PREFERRED_URL_SCHEME configuration setting.

This sets the url scheme by which canaille will be served.

SECRET_KEY: str [Required]

The Flask SECRET_KEY configuration setting.

You MUST change this.

SERVER_NAME: str | None = None

The Flask SERVER_NAME configuration setting.

This sets domain name on which canaille will be served.

canaille.core.configuration.CoreSettings[source]

The settings from the CANAILLE namespace.

Those are all the configuration parameters that controls the behavior of Canaille.

ACL: dict[str, ACLSettings] | None = {'DEFAULT': ACLSettings(PERMISSIONS=[<Permission.EDIT_SELF: 'edit_self'>, <Permission.USE_OIDC: 'use_oidc'>], READ=['user_name', 'groups', 'lock_date'], WRITE=['photo', 'given_name', 'family_name', 'display_name', 'password', 'phone_numbers', 'emails', 'profile_url', 'formatted_address', 'street', 'postal_code', 'locality', 'region', 'preferred_language', 'employee_number', 'department', 'title', 'organization'], FILTER=None)}

Mapping of permission groups. See ACLSettings for more details.

The ACL name can be freely chosen. For example:

..code-block:: toml

[CANAILLE.ACL.DEFAULT] PERMISSIONS = [“edit_self”, “use_oidc”] READ = [“user_name”, “groups”] WRITE = [“given_name”, “family_name”]

[CANAILLE.ACL.ADMIN] WRITE = [“user_name”, “groups”]

ADMIN_EMAIL: str | None = None

Administration email contact.

In certain special cases (example : questioning about password corruption), it is necessary to provide an administration contact email.

EMAIL_CONFIRMATION: bool = True

If True, users will need to click on a confirmation link sent by email when they want to add a new email.

By default, this is true if SMTP is configured, else this is false. If explicitly set to true and SMTP is disabled, the email field will be read-only.

EMAIL_OTP: bool = False

If True, then users will need to authenticate themselves via a one-time password sent to their primary email address.

ENABLE_INTRUDER_LOCKOUT: bool = False

If True, then users will have to wait for an increasingly long time between each failed login attempt.

ENABLE_PASSWORD_COMPROMISSION_CHECK: bool = False

If True, Canaille will check if passwords appears in compromission databases such as HIBP when users choose a new one.

ENABLE_PASSWORD_RECOVERY: bool = True

If False, then users cannot ask for a password recovery link by email.

ENABLE_REGISTRATION: bool = False

If True, then users can freely create an account at this instance.

If email verification is available, users must confirm their email before the account is created.

FAVICON: str | None = None

You favicon.

If unset and LOGO is set, then the logo will be used.

HIDE_INVALID_LOGINS: bool = True

If True, when users try to sign in with an invalid login, a message is shown indicating that the password is wrong, but does not give a clue whether the login exists or not.

If False, when a user tries to sign in with an invalid login, a message is shown indicating that the login does not exist.

HTMX: bool = True

Accelerates webpages loading with asynchronous requests.

INVITATION_EXPIRATION: int = 172800

The validity duration of registration invitations, in seconds.

Defaults to 2 days.

JAVASCRIPT: bool = True

Enables Javascript to smooth the user experience.

LANGUAGE: str | None = None

If a language code is set, it will be used for every user.

If unset, the language is guessed according to the users browser.

LOGGING: str | dict | None = None

Configures the logging output using the python logging configuration format:

For example:

..code-block:: toml

[CANAILLE.LOGGING] version = 1 formatters.default.format = “[%(asctime)s] %(levelname)s in %(module)s: %(message)s” root = {level = “INFO”, handlers = [“canaille”]}

[CANAILLE.LOGGING.handlers.canaille] class = “logging.handlers.WatchedFileHandler” filename = “/var/log/canaille.log” formatter = “default”

The logo of your organization, this is useful to make your organization recognizable on login screens.

MAX_PASSWORD_LENGTH: int = 1000

User password maximum length.

There is a technical of 4096 characters with the SQL backend. If the value is 0, None, or greater than 4096, then 4096 will be retained.

MIN_PASSWORD_LENGTH: int = 8

User password minimum length.

If 0 or None, password won’t have a minimum length.

NAME: str = 'Canaille'

Your organization name.

Used for display purpose.

OTP_METHOD: str = None

If OTP_METHOD is defined, then users will need to authenticate themselves using a one-time password (OTP) via an authenticator app. If set to TOTP, the application will use time one-time passwords, If set to HOTP, the application will use HMAC-based one-time passwords.

PASSWORD_COMPROMISSION_CHECK_API_URL: str = 'https://api.pwnedpasswords.com/range/'

Have i been pwned api url for compromission checks.

PASSWORD_LIFETIME: str | None = None

Password validity duration.

If set, user passwords expire after this delay. Users are forced to change their password when the lifetime of the password is over. The duration value is expressed in ISO8601 format. For example, delay of 60 days is written “P60D”.

SENTRY_DSN: str | None = None

A Sentry DSN to collect the exceptions.

This is useful for tracking errors in test and production environments.

SMPP: SMPPSettings | None = None

The settings related to SMPP configuration.

If unset, sms-related features like sms one-time passwords won’t be enabled.

SMS_OTP: bool = False

If True, then users will need to authenticate themselves via a one-time password sent to their primary phone number.

SMTP: SMTPSettings | None = None

The settings related to SMTP and mail configuration.

If unset, mail-related features like password recovery won’t be enabled.

THEME: str = 'default'

The name of a theme in the ‘theme’ directory, or a path to a theme.

Defaults to default. Theming is done with flask-themer.

TIMEZONE: str | None = None

The timezone in which datetimes will be displayed to the users (e.g. CEST).

If unset, the server timezone will be used.

canaille.core.configuration.SMTPSettings[source]

The SMTP configuration. Belong in the CANAILLE.SMTP namespace. If unset, mail related features will be disabled, such as mail verification or password recovery emails.

By default, Canaille will try to send mails from localhost without authentication.

FROM_ADDR: str | None = None

The sender for Canaille mails.

Some mail provider might require a valid sender address.

HOST: str | None = 'localhost'

The SMTP host.

LOGIN: str | None = None

The SMTP login.

PASSWORD: str | None = None

The SMTP password.

PORT: int | None = 25

The SMTP port.

SSL: bool | None = False

Whether to use SSL to connect to the SMTP server.

TLS: bool | None = False

Whether to use TLS to connect to the SMTP server.

canaille.core.configuration.SMPPSettings[source]

The SMPP configuration. Belong in the CANAILLE.SMPP namespace. If not set, sms related features such as sms one-time passwords will be disabled.

HOST: str | None = 'localhost'

The SMPP host.

LOGIN: str | None = None

The SMPP login.

PASSWORD: str | None = None

The SMPP password.

PORT: int | None = 2775

The SMPP port. Use 8775 for SMPP over TLS (recommended).

canaille.core.configuration.ACLSettings[source]

Access Control List settings. Belong in the CANAILLE.ACL namespace.

You can define access controls that define what users can do on canaille. An access control consists in a FILTER to match users, a list of PERMISSIONS matched users will be able to perform, and fields users will be able to READ and WRITE. Users matching several filters will cumulate permissions.

FILTER: dict[str, str] | list[dict[str, str]] | None = None

FILTER can be:

  • None, in which case all the users will match this access control

  • a mapping where keys are user attributes name and the values those user attribute values. All the values must be matched for the user to be part of the access control.

  • a list of those mappings. If a user values match at least one mapping, then the user will be part of the access control

Here are some examples:

FILTER = {user_name = 'admin'}
FILTER = [
    {groups = 'admins},
    {groups = 'moderators'},
]
PERMISSIONS: list[Permission] = [Permission.EDIT_SELF, Permission.USE_OIDC]

A list of Permission users in the access control will be able to manage.

For example:

..code-block:: toml

PERMISSIONS = [“manage_users”, “manage_groups”, “manage_oidc”, “delete_account”, “impersonate_users”]

READ: list[str] = ['user_name', 'groups', 'lock_date']

A list of User attributes that users in the ACL will be able to read.

WRITE: list[str] = ['photo', 'given_name', 'family_name', 'display_name', 'password', 'phone_numbers', 'emails', 'profile_url', 'formatted_address', 'street', 'postal_code', 'locality', 'region', 'preferred_language', 'employee_number', 'department', 'title', 'organization']

A list of User attributes that users in the ACL will be able to edit.

class canaille.core.configuration.Permission(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

The permissions that can be assigned to users.

The permissions are intended to be used in ACLSettings.

DELETE_ACCOUNT = 'delete_account'

Allows users to delete their account.

If used with MANAGE_USERS, users can delete any account.

EDIT_SELF = 'edit_self'

Allows users to edit their own profile.

IMPERSONATE_USERS = 'impersonate_users'

Allows users to take the identity of another user.

MANAGE_GROUPS = 'manage_groups'

Allows group edition and creation.

MANAGE_OIDC = 'manage_oidc'

Allows OpenID Connect client managements.

MANAGE_USERS = 'manage_users'

Allows other users management.

USE_OIDC = 'use_oidc'

Allows OpenID Connect authentication.

canaille.oidc.configuration.OIDCSettings[source]

OpenID Connect settings.

Belong in the CANAILLE_OIDC namespace.

DYNAMIC_CLIENT_REGISTRATION_OPEN: bool = False

Whether a token is needed for the RFC7591 dynamical client registration.

If True, no token is needed to register a client. If False, dynamical client registration needs a token defined in DYNAMIC_CLIENT_REGISTRATION_TOKENS.

DYNAMIC_CLIENT_REGISTRATION_TOKENS: list[str] | None = None

A list of tokens that can be used for dynamic client registration.

JWT: JWTSettings = JWTSettings(PRIVATE_KEY=None, PUBLIC_KEY=None, ISS=None, KTY='RSA', ALG='RS256', EXP=3600, MAPPING=JWTMappingSettings(SUB='{{ user.user_name }}', NAME='{% if user.formatted_name %}{{ user.formatted_name }}{% endif %}', PHONE_NUMBER='{% if user.phone_numbers %}{{ user.phone_numbers[0] }}{% endif %}', EMAIL='{% if user.preferred_email %}{{ user.preferred_email }}{% endif %}', GIVEN_NAME='{% if user.given_name %}{{ user.given_name }}{% endif %}', FAMILY_NAME='{% if user.family_name %}{{ user.family_name }}{% endif %}', PREFERRED_USERNAME='{% if user.display_name %}{{ user.display_name }}{% endif %}', LOCALE='{% if user.preferred_language %}{{ user.preferred_language }}{% endif %}', ADDRESS='{% if user.formatted_address %}{{ user.formatted_address }}{% endif %}', PICTURE="{% if user.photo %}{{ url_for('core.account.photo', user=user, field='photo', _external=True) }}{% endif %}", WEBSITE='{% if user.profile_url %}{{ user.profile_url }}{% endif %}'))

JSON Web Token settings.

REQUIRE_NONCE: bool = True

Force the nonce exchange during the authentication flows.

This adds security but may not be supported by all clients.

canaille.oidc.configuration.JWTSettings[source]

JSON Web Token settings. Belong in the CANAILLE_OIDC.JWT namespace.

You can generate a RSA keypair with:

openssl genrsa -out private.pem 4096
openssl rsa -in private.pem -pubout -outform PEM -out public.pem
ALG: str = 'RS256'

The key algorithm.

EXP: int = 3600

The time the JWT will be valid, in seconds.

ISS: str | None = None

The URI of the identity provider.

KTY: str = 'RSA'

The key type.

MAPPING: JWTMappingSettings | None = JWTMappingSettings(SUB='{{ user.user_name }}', NAME='{% if user.formatted_name %}{{ user.formatted_name }}{% endif %}', PHONE_NUMBER='{% if user.phone_numbers %}{{ user.phone_numbers[0] }}{% endif %}', EMAIL='{% if user.preferred_email %}{{ user.preferred_email }}{% endif %}', GIVEN_NAME='{% if user.given_name %}{{ user.given_name }}{% endif %}', FAMILY_NAME='{% if user.family_name %}{{ user.family_name }}{% endif %}', PREFERRED_USERNAME='{% if user.display_name %}{{ user.display_name }}{% endif %}', LOCALE='{% if user.preferred_language %}{{ user.preferred_language }}{% endif %}', ADDRESS='{% if user.formatted_address %}{{ user.formatted_address }}{% endif %}', PICTURE="{% if user.photo %}{{ url_for('core.account.photo', user=user, field='photo', _external=True) }}{% endif %}", WEBSITE='{% if user.profile_url %}{{ user.profile_url }}{% endif %}')
PRIVATE_KEY: str | None = None

The private key.

If None and debug mode is enabled, then an in-memory key will be used.

PUBLIC_KEY: str | None = None

The public key.

If None and debug mode is enabled, then an in-memory key will be used.

canaille.oidc.configuration.JWTMappingSettings[source]

Mapping between the user model and the JWT fields.

Fields are evaluated with jinja. A user var is available.

ADDRESS: str | None = '{% if user.formatted_address %}{{ user.formatted_address }}{% endif %}'
EMAIL: str | None = '{% if user.preferred_email %}{{ user.preferred_email }}{% endif %}'
FAMILY_NAME: str | None = '{% if user.family_name %}{{ user.family_name }}{% endif %}'
GIVEN_NAME: str | None = '{% if user.given_name %}{{ user.given_name }}{% endif %}'
LOCALE: str | None = '{% if user.preferred_language %}{{ user.preferred_language }}{% endif %}'
NAME: str | None = '{% if user.formatted_name %}{{ user.formatted_name }}{% endif %}'
PHONE_NUMBER: str | None = '{% if user.phone_numbers %}{{ user.phone_numbers[0] }}{% endif %}'
PICTURE: str | None = "{% if user.photo %}{{ url_for('core.account.photo', user=user, field='photo', _external=True) }}{% endif %}"
PREFERRED_USERNAME: str | None = '{% if user.display_name %}{{ user.display_name }}{% endif %}'
SUB: str | None = '{{ user.user_name }}'
WEBSITE: str | None = '{% if user.profile_url %}{{ user.profile_url }}{% endif %}'
canaille.scim.configuration.SCIMSettings[source]

SCIM settings.

ENABLE_SERVER: bool = True
canaille.backends.sql.configuration.SQLSettings[source]

Settings related to the SQL backend.

Belong in the CANAILLE_SQL namespace.

DATABASE_URI: str [Required]

The SQL server URI. For example:

..code-block:: toml

DATABASE_URI = “postgresql://user:password@localhost/database_name”

canaille.backends.ldap.configuration.LDAPSettings[source]

Settings related to the LDAP backend.

Belong in the CANAILLE_LDAP namespace.

BIND_DN: str = 'cn=admin,dc=mydomain,dc=tld'

The LDAP bind DN.

BIND_PW: str = 'admin'

The LDAP bind password.

GROUP_BASE: str [Required]

The LDAP node under which groups will be looked for and saved.

For instance “ou=groups,dc=mydomain,dc=tld”.

GROUP_CLASS: str = 'groupOfNames'

The object class to use for creating new groups.

GROUP_NAME_ATTRIBUTE: str = 'cn'

The attribute to use to identify a group.

GROUP_RDN: str = 'cn'

The attribute to identify an object in the Group DN.

ROOT_DN: str = 'dc=mydomain,dc=tld'

The LDAP root DN.

TIMEOUT: float = 0.0

The LDAP connection timeout.

URI: str = 'ldap://localhost'

The LDAP server URI.

USER_BASE: str [Required]

The LDAP node under which users will be looked for and saved.

For instance ou=users,dc=mydomain,dc=tld.

USER_CLASS: list[str] = ['inetOrgPerson']

The object class to use for creating new users.

USER_FILTER: str = '(|(uid={{ login }})(mail={{ login }}))'

Filter to match users on sign in.

For instance (|(uid={{ login }})(mail={{ login }})). Jinja syntax is supported and a login variable is available, containing the value passed in the login field.

USER_RDN: str = 'uid'

The attribute to identify an object in the User DN.

Example file

Here is a configuration file example:

config.toml
# The Flask secret key for cookies. You MUST change this.
SECRET_KEY = "change me before you go in production"

# The interface on which canaille will be served
# SERVER_NAME = "auth.mydomain.example"
# PREFERRED_URL_SCHEME = "https"

[CANAILLE]

# Your organization name.
# NAME = "Canaille"

# You can display a logo to be recognized on login screens
# LOGO = "/static/img/canaille-head.webp"

# Your favicon. If unset the LOGO will be used.
# FAVICON = "/static/img/canaille-c.webp"

# The name of a theme in the 'theme' directory, or a path path
# to a theme. Defaults to 'default'. Theming is done with
# https://github.com/tktech/flask-themer
# THEME = "default"

# If unset, language is detected
# LANGUAGE = "en"

# The timezone in which datetimes will be displayed to the users.
# If unset, the server timezone will be used.
# TIMEZONE = UTC

# If you have a sentry instance, you can set its dsn here:
# SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"

# Enables javascript to smooth the user experience
# JAVASCRIPT = true

# Accelerates webpages with async requests
# HTMX = true

# If EMAIL_CONFIRMATION is set to true, users will need to click on a
# confirmation link sent by email when they want to add a new email.
# By default, this is true if SMTP is configured, else this is false.
# If explicitly set to true and SMTP is disabled, the email field
# will be read-only.
# EMAIL_CONFIRMATION = True

# If ENABLE_REGISTRATION is true, then users can freely create an account
# at this instance. If email verification is available, users must confirm
# their email before the account is created.
# ENABLE_REGISTRATION = false

# If HIDE_INVALID_LOGINS is set to true (the default), when a user
# tries to sign in with an invalid login, a message is shown indicating
# that the password is wrong, but does not give a clue whether the login
# exists or not.
# If HIDE_INVALID_LOGINS is set to false, when a user tries to sign in with
# an invalid login, a message is shown indicating that the login does not
# exist.
# HIDE_INVALID_LOGINS = true

# If ENABLE_PASSWORD_RECOVERY is false, then users cannot ask for a password
# recovery link by email. This option is true by default.
# ENABLE_PASSWORD_RECOVERY = true

# If ENABLE_INTRUDER_LOCKOUT is true, then users will have to wait for an
# increasingly long time between each failed login attempt. This option is false by default.
# ENABLE_INTRUDER_LOCKOUT = false

# If OTP_METHOD is defined, then users will need to authenticate themselves
# using a one-time password (OTP) via an authenticator app.
# Two options are supported : "TOTP" for time one-time password,
# and "HOTP" for HMAC-based one-time password.
# This option is deactivated by default.
# OTP_METHOD = "TOTP"

# If EMAIL_OTP is true, then users will need to authenticate themselves
# via a one-time password sent to their primary email address.
# This option is false by default.
# EMAIL_OTP = false

# If SMS_OTP is true, then users will need to authenticate themselves
# via a one-time password sent to their primary phone number.
# This option is false by default.
# SMS_OTP = false

# The validity duration of registration invitations, in seconds.
# Defaults to 2 days
# INVITATION_EXPIRATION = 172800

# LOGGING configures the logging output:
# - if unset, everything is logged in the standard output
#   the log level is debug if DEBUG is True, else this is INFO
# - if this is a dictionary, it is passed to the python dictConfig method:
#   https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig
# - if this is a string, it is passed to the python fileConfig method
#   https://docs.python.org/3/library/logging.config.html#logging.config.fileConfig

# Minimum length for user password.
# It is possible not to set a minimum, by entering None or 0.
# MIN_PASSWORD_LENGTH = 8

# Maximum length for user password.
# There is a technical limit with passlib used by sql database of 4096
# characters. If the value entered is 0 or None, or greater than 4096,
# then 4096 will be retained.
# MAX_PASSWORD_LENGTH = 1000

# Administration email contact.
# In certain special cases (example : questioning about password
# corruption), it is necessary to provide an administration contact
# email.
# ADMIN_EMAIL = "admin@mydomain.example"

# If :py:data:`True`, Canaille will check for password compromise on HIBP
# every time a new password is register.
# (https://haveibeenpwned.com/)
# ENABLE_PASSWORD_COMPROMISSION_CHECK = False

# Have i been pwned api url for compromission checks.
# This url should not be modified.
# API_URL_HIBP = "https://api.pwnedpasswords.com/range/"

# Password validity duration.
# If a value is recorded Canaille will check if user's password is expired.
# Then, the user is forced to change his password when the lifetime of the password is over.
# This value is expressed in `ISO8601 format <https://en.wikipedia.org/wiki/ISO_8601#Durations>`_.
# Example for 60 days: "P60D"
# It is possible to disable this option by entering None.
# PASSWORD_LIFETIME = None

# [CANAILLE_SQL]
# The SQL database connection string
# Details on https://docs.sqlalchemy.org/en/20/core/engines.html
# DATABASE_URI = "postgresql://user:password@localhost/database"

# [CANAILLE_LDAP]
# URI = "ldap://ldap"
# ROOT_DN = "dc=mydomain,dc=tld"
# BIND_DN = "cn=admin,dc=mydomain,dc=tld"
# BIND_PW = "admin"
# TIMEOUT =

# Where to search for users?
# USER_BASE = "ou=users,dc=mydomain,dc=tld"

# The object class to use for creating new users
# Note : If you plan on using TOTP/HOTP authentication, use : USER_CLASS = ["inetOrgPerson", "oathHOTPToken"]
# USER_CLASS = "inetOrgPerson"

# The attribute to identify an object in the User dn.
# USER_RDN = "uid"

# Filter to match users on sign in. Jinja syntax is supported
# and a `login` variable is available containing the value
# passed in the login field.
# USER_FILTER = "(|(uid={{ login }})(mail={{ login }}))"

# Where to search for groups?
# GROUP_BASE = "ou=groups,dc=mydomain,dc=tld"

# The object class to use for creating new groups
# GROUP_CLASS = "groupOfNames"

# The attribute to identify an object in the User dn.
# GROUP_RDN = "cn"

# The attribute to use to identify a group
# GROUP_NAME_ATTRIBUTE = "cn"

[CANAILLE.ACL]
# You can define access controls that define what users can do on canaille
# An access control consists in a FILTER to match users, a list of PERMISSIONS
# matched users will be able to perform, and fields users will be able
# to READ and WRITE. Users matching several filters will cumulate permissions.
#
# 'FILTER' parameter can be:
# - absent, in which case all the users will match this access control
# - a mapping where keys are user attributes name and the values those user
#   attribute values. All the values must be matched for the user to be part
#   of the access control.
# - a list of those mappings. If a user values match at least one mapping,
#   then the user will be part of the access control
#
# Here are some examples
#     FILTER = {user_name = 'admin'}
#     FILTER =
#       - {groups = 'admins}
#       - {groups = 'moderators'}
#
# The 'PERMISSIONS' parameter that is an list of items the users in the access
# control will be able to manage. 'PERMISSIONS' is optional. Values can be:
# - "edit_self" to allow users to edit their own profile
# - "use_oidc" to allow OpenID Connect authentication
# - "manage_oidc" to allow OpenID Connect client managements
# - "manage_users" to allow other users management
# - "manage_groups" to allow group edition and creation
# - "delete_account" allows a user to delete his own account. If used with
#                    manage_users, the user can delete any account
# - "impersonate_users" to allow a user to take the identity of another user
#
# The 'READ' and 'WRITE' attributes are the LDAP attributes of the user
# object that users will be able to read and/or write.
[CANAILLE.ACL.DEFAULT]
PERMISSIONS = ["edit_self", "use_oidc"]
READ = [
    "user_name",
    "groups",
    "lock_date",
]
WRITE = [
    "photo",
    "given_name",
    "family_name",
    "display_name",
    "password",
    "phone_numbers",
    "emails",
    "profile_url",
    "formatted_address",
    "street",
    "postal_code",
    "locality",
    "region",
    "preferred_language",
    "employee_number",
    "department",
    "title",
    "organization",
]

[CANAILLE.ACL.ADMIN]
FILTER = {groups = "admins"}
PERMISSIONS = [
    "manage_users",
    "manage_groups",
    "manage_oidc",
    "delete_account",
    "impersonate_users",
]
WRITE = [
    "groups",
    "lock_date",
]

[CANAILLE_OIDC]
# Whether a token is needed for the RFC7591 dynamical client registration.
# If true, no token is needed to register a client.
# If false, dynamical client registration needs a token defined
# in DYNAMIC_CLIENT_REGISTRATION_TOKENS
# DYNAMIC_CLIENT_REGISTRATION_OPEN = false

# A list of tokens that can be used for dynamic client registration
# DYNAMIC_CLIENT_REGISTRATION_TOKENS = [
#     "xxxxxxx-yyyyyyy-zzzzzz",
# ]

# REQUIRE_NONCE force the nonce exchange during the authentication flows.
# This adds security but may not be supported by all clients.
# REQUIRE_NONCE = true

[CANAILLE_OIDC.JWT]
# PRIVATE_KEY and PUBLIC_KEY are the private and
# the public key. You can generate a RSA keypair with:
# openssl genrsa -out private.pem 4096
# openssl rsa -in private.pem -pubout -outform PEM -out public.pem
# If the variables are unset, and debug mode is enabled,
# a in-memory keypair will be used.
# PRIVATE_KEY = "..."
# PUBLIC_KEY = "..."
# The URI of the identity provider
# ISS = "https://auth.mydomain.example"
# The key type parameter
# KTY = "RSA"
# The key algorithm
# ALG = "RS256"
# The time the JWT will be valid, in seconds
# EXP = 3600

[CANAILLE_OIDC.JWT.MAPPING]
# Mapping between JWT fields and LDAP attributes from your
# User objectClass.
# {attribute} will be replaced by the user ldap attribute value.
# Default values fits inetOrgPerson.
# SUB = "{{ user.user_name }}"
# NAME = "{{ user.formatted_name }}"
# PHONE_NUMBER = "{{ user.phone_numbers[0] }}"
# EMAIL = "{{ user.preferred_email }}"
# GIVEN_NAME = "{{ user.given_name }}"
# FAMILY_NAME = "{{ user.family_name }}"
# PREFERRED_USERNAME = "{{ user.display_name }}"
# LOCALE = "{{ user.preferred_language }}"
# ADDRESS = "{{ user.formatted_address }}"
# PICTURE = "{% if user.photo %}{{ url_for('core.account.photo', user=user, field='photo', _external=True) }}{% endif %}"
# WEBSITE = "{{ user.profile_url }}"

# The SMTP server options. If not set, mail related features such as
# user invitations, and password reset emails, will be disabled.
[CANAILLE.SMTP]
# HOST = "localhost"
# PORT = 25
# TLS = false
# SSL = false
# LOGIN = ""
# PASSWORD = ""
# FROM_ADDR = "admin@mydomain.example"

# The SMPP server options. If not set, sms related features such as
# sms one-time passwords will be disabled.
[CANAILLE.SMPP]
# HOST = "localhost"
# PORT = 2775
# LOGIN = ""
# PASSWORD = ""