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.
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.
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;
if no configuration method is used, Canaille will look for a
canaille.toml
configuration file in the current working directory.
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 = "https" DEBUG = false [CANAILLE] NAME = "My organization" ...
- CACHE_TYPE: str = 'SimpleCache'¶
The cache type.
The default
SimpleCache
is a lightweight in-memory cache. See the Flask-Caching documentation for further details.
- 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 | None = None¶
The Flask
SECRET_KEY
configuration setting.You MUST set a value before deploying in production.
- 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:
[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 andSMTP
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.
- 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.
- INVITATION_EXPIRATION: int = 172800¶
The validity duration of registration invitations, in seconds.
Defaults to 2 days.
- 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:
If
None
, everything is logged in the standard error output. The log level isDEBUG
if theDEBUG
setting isTrue
, else this isINFO
.If this is a
dict
, it is passed tologging.config.dictConfig()
:If this is a
str
, it is expected to be a file path that will be passed tologging.config.fileConfig()
.
For example:
[CANAILLE.LOGGING] version = 1 formatters.default.format = "[%(asctime)s] - $(ip)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"
- LOGIN_ATTRIBUTES: list[str] | dict[str, str] = ['user_name', 'emails']¶
The attributes users can use to identify themselves, generally a combination of
user_name
,emails
andphone_numbers
.When this is a
list
, it expects the attribute names to match.When this is a
dict
, keys are expected to be the attribute names to match, and values are a Jinja string with alogin
variable available. This can be used to tune the user input, and for example remove a domain name.
LOGIN_ATTRIBUTES = ["user_name", "emails"] LOGIN_ATTRIBUTES = {user_name = "{{ login | replace('@example.org', '') }}", emails = "{{ login }}"}
- LOGO: str | None = None¶
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.
Note
There is a technical limit 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.
- OTP_METHOD: OTPMethod | None = 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 toHOTP
, 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: Annotated[Path, PathType(path_type=dir)] | None = None¶
A path to a theme.
See the theming documentation for more details.
- 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.
- 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.
- 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 ofPERMISSIONS
matched users will be able to perform, and fields users will be able toREAD
andWRITE
. 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 controla 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:
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. IfFalse
, dynamical client registration needs a token defined inDYNAMIC_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.
- 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
- 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 %}')¶
- canaille.oidc.configuration.JWTMappingSettings[source]¶
Mapping between the user model and the JWT fields.
Fields are evaluated with jinja. A
user
var is available.
- canaille.backends.sql.configuration.SQLSettings[source]¶
Settings related to the SQL backend.
Belong in the
CANAILLE_SQL
namespace.- AUTO_MIGRATE: bool = True¶
Whether to automatically apply database migrations.
If
True
, database migrations will be automatically applied when Canaille web application is launched. IfFalse
, migrations must be applied manually withcanaille db upgrade
.Note
When running the CLI, migrations will never be applied.
- canaille.backends.ldap.configuration.LDAPSettings[source]¶
Settings related to the LDAP backend.
Belong in the
CANAILLE_LDAP
namespace.- GROUP_BASE: str [Required]¶
The LDAP node under which groups will be looked for and saved.
For instance “ou=groups,dc=example,dc=org”.
Example file¶
Here is a configuration file example that can be generated with the canaille config dump command:
# The Flask SECRET_KEY configuration setting.
#
# You MUST set a value before deploying in production.
# SECRET_KEY =
# The Flask SERVER_NAME configuration setting.
#
# This sets domain name on which canaille will be served.
# SERVER_NAME =
# The Flask PREFERRED_URL_SCHEME configuration setting.
#
# This sets the url scheme by which canaille will be served.
# PREFERRED_URL_SCHEME = "https"
# The Flask DEBUG configuration setting.
#
# This enables debug options.
#
# This is useful for development but should be absolutely
# avoided in production environments.
# DEBUG = false
# The cache type.
#
# The default SimpleCache is a lightweight in-memory cache. See the Flask-Caching
# documentation for further details.
# CACHE_TYPE = "SimpleCache"
[CANAILLE]
# Your organization name.
#
# Used for display purpose.
# NAME = "Canaille"
# The logo of your organization, this is useful to make your organization
# recognizable on login screens.
# LOGO =
# You favicon.
#
# If unset and LOGO is set, then the logo will be used.
# FAVICON =
# A path to a theme.
#
# See the theming documentation for more details.
# THEME =
# If a language code is set, it will be used for every user.
#
# If unset, the language is guessed according to the users browser.
# LANGUAGE =
# The timezone in which datetimes will be displayed to the users (e.g. CEST).
#
# If unset, the server timezone will be used.
# TIMEZONE =
# A Sentry (https://sentry.io) DSN to collect the exceptions.
#
# This is useful for tracking errors in test and production environments.
# SENTRY_DSN =
# Enables Javascript to smooth the user experience.
# JAVASCRIPT = true
# Accelerates webpages loading with asynchronous requests.
# HTMX = 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_CONFIRMATION = true
# 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.
# ENABLE_REGISTRATION = false
# 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.
# HIDE_INVALID_LOGINS = true
# If False, then users cannot ask for a password recovery link by email.
# ENABLE_PASSWORD_RECOVERY = true
# If True, then users will have to wait for an increasingly long time between each
# failed login attempt.
# 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. 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.
# OTP_METHOD =
# If True, then users will need to authenticate themselves via a one-time password
# sent to their primary email address.
# EMAIL_OTP = false
# If True, then users will need to authenticate themselves via a one-time password
# sent to their primary phone number.
# SMS_OTP = false
# The validity duration of registration invitations, in seconds.
#
# Defaults to 2 days.
# INVITATION_EXPIRATION = 172800
# User password minimum length.
#
# If 0 or None, password won't have a minimum length.
# MIN_PASSWORD_LENGTH = 8
# User password maximum length.
#
# There is a technical limit of 4096 characters with the SQL backend.
# If the value is 0, 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 =
# If True, Canaille will check if passwords appears in compromission databases
# such as HIBP (https://haveibeenpwned.com) when users choose a new one.
# ENABLE_PASSWORD_COMPROMISSION_CHECK = false
# Have i been pwned api url for compromission checks.
# PASSWORD_COMPROMISSION_CHECK_API_URL = "https://api.pwnedpasswords.com/range/"
# 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 (https://en.wikipedia.org/wiki/ISO_8601#Durations).
# For example, delay of 60 days is written "P60D".
# PASSWORD_LIFETIME =
# Configures the logging output using the python logging configuration format:
#
# - If None, everything is logged in the standard error output.
# The log level is DEBUG if the DEBUG
# setting is True, else this is INFO.
# - If this is a dict, it is passed to logging.config.dictConfig:
# - If this is a str, it is expected to be a file path that will be passed
# to logging.config.fileConfig.
#
# For example:
#
# [CANAILLE.LOGGING]
# version = 1
# formatters.default.format = "[%(asctime)s] - $(ip)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"
# LOGGING =
# The settings related to SMTP and mail configuration.
#
# If unset, mail-related features like password recovery won't be enabled.
[CANAILLE.SMTP]
# The SMTP host.
# HOST = "localhost"
# The SMTP port.
# PORT = 25
# Whether to use TLS to connect to the SMTP server.
# TLS = false
# Whether to use SSL to connect to the SMTP server.
# SSL = false
# The SMTP login.
# LOGIN =
# The SMTP password.
# PASSWORD =
# The sender for Canaille mails.
#
# Some mail provider might require a valid sender address.
# FROM_ADDR =
# The settings related to SMPP configuration.
#
# If unset, sms-related features like sms one-time passwords won't be enabled.
[CANAILLE.SMPP]
# The SMPP host.
# HOST = "localhost"
# The SMPP port. Use 8775 for SMPP over TLS (recommended).
# PORT = 2775
# The SMPP login.
# LOGIN =
# The SMPP password.
# PASSWORD =
[CANAILLE_SQL]
# The SQL server URI. For example:
#
# DATABASE_URI = "postgresql://user:password@localhost/database_name"
# DATABASE_URI =
DATABASE_URI = "sqlite:///canaille.sqlite"
# Password hashing scheme.
#
# Defines password hashing scheme in SQL database. examples : "mssql2000",
# "ldap_salted_sha1", "pbkdf2_sha512"
# PASSWORD_SCHEMES = "pbkdf2_sha512"
# Whether to automatically apply database migrations.
#
# If True, database migrations will be automatically applied when Canaille web
# application is launched. If False, migrations must be applied manually with
# canaille db upgrade.
#
# When running the CLI, migrations will never be applied.
# AUTO_MIGRATE = true
[CANAILLE_LDAP]
# The LDAP server URI.
# URI = "ldap://localhost"
# The LDAP root DN.
# ROOT_DN = "dc=example,dc=org"
# The LDAP bind DN.
# BIND_DN = "cn=admin,dc=example,dc=org"
# The LDAP bind password.
# BIND_PW = "admin"
# The LDAP connection timeout.
# TIMEOUT = 0.0
# The LDAP node under which users will be looked for and saved.
#
# For instance `ou=users,dc=example,dc=org`.
# USER_BASE =
USER_BASE = "ou=users,dc=example,dc=org"
# The attribute to identify an object in the User DN.
# USER_RDN = "uid"
# The LDAP node under which groups will be looked for and saved.
#
# For instance `"ou=groups,dc=example,dc=org"`.
# GROUP_BASE =
GROUP_BASE = "ou=groups,dc=example,dc=org"
# The object class to use for creating new groups.
# GROUP_CLASS = "groupOfNames"
# The attribute to identify an object in the Group DN.
# GROUP_RDN = "cn"
# The attribute to use to identify a group.
# GROUP_NAME_ATTRIBUTE = "cn"
[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 =
# Force the nonce exchange during the authentication flows.
#
# This adds security but may not be supported by all clients.
# REQUIRE_NONCE = true
# JSON Web Token settings.
[CANAILLE_OIDC.JWT]
# The private key.
#
# If None and debug mode is enabled, then an in-memory key will be used.
# PRIVATE_KEY =
# The public key.
#
# If None and debug mode is enabled, then an in-memory key will be used.
# PUBLIC_KEY =
# The URI of the identity provider.
# ISS =
# The key type.
# KTY = "RSA"
# The key algorithm.
# ALG = "RS256"
# The time the JWT will be valid, in seconds.
# EXP = 3600
[CANAILLE_OIDC.JWT.MAPPING]
# 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 %}"
[CANAILLE_SCIM]
# Whether the SCIM server API is enabled.
#
# When enabled, services plugged to Canaille can update users and groups using the
# API.
# ENABLE_SERVER = true