2023-12-18 17:06:03 +00:00
|
|
|
from enum import Enum
|
|
|
|
|
|
|
|
from pydantic import BaseModel
|
|
|
|
from pydantic import ValidationInfo
|
|
|
|
from pydantic import field_validator
|
|
|
|
|
|
|
|
|
|
|
|
class SMTPSettings(BaseModel):
|
|
|
|
"""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.
|
|
|
|
"""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
HOST: str | None = "localhost"
|
2023-12-18 17:06:03 +00:00
|
|
|
"""The SMTP host."""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
PORT: int | None = 25
|
2023-12-18 17:06:03 +00:00
|
|
|
"""The SMTP port."""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
TLS: bool | None = False
|
2024-09-11 07:33:42 +00:00
|
|
|
"""Whether to use TLS to connect to the SMTP server."""
|
2023-12-18 17:06:03 +00:00
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
SSL: bool | None = False
|
2024-09-11 07:33:42 +00:00
|
|
|
"""Whether to use SSL to connect to the SMTP server."""
|
2023-12-18 17:06:03 +00:00
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
LOGIN: str | None = None
|
2023-12-18 17:06:03 +00:00
|
|
|
"""The SMTP login."""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
PASSWORD: str | None = None
|
2023-12-18 17:06:03 +00:00
|
|
|
"""The SMTP password."""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
FROM_ADDR: str | None = None
|
2023-12-18 17:06:03 +00:00
|
|
|
"""The sender for Canaille mails.
|
|
|
|
|
|
|
|
Some mail provider might require a valid sender address.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
2024-11-29 09:32:17 +00:00
|
|
|
class SMPPSettings(BaseModel):
|
|
|
|
"""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."""
|
|
|
|
|
|
|
|
PORT: int | None = 2775
|
|
|
|
"""The SMPP port. Use 8775 for SMPP over TLS (recommended)."""
|
|
|
|
|
|
|
|
LOGIN: str | None = None
|
|
|
|
"""The SMPP login."""
|
|
|
|
|
|
|
|
PASSWORD: str | None = None
|
|
|
|
"""The SMPP password."""
|
|
|
|
|
|
|
|
|
2023-12-18 17:06:03 +00:00
|
|
|
class Permission(str, Enum):
|
2024-05-14 07:05:46 +00:00
|
|
|
"""The permissions that can be assigned to users.
|
|
|
|
|
|
|
|
The permissions are intended to be used in :attr:`ACLSettings <canaille.core.configuration.ACLSettings.PERMISSIONS>`.
|
|
|
|
"""
|
2023-12-18 17:06:03 +00:00
|
|
|
|
|
|
|
EDIT_SELF = "edit_self"
|
|
|
|
"""Allows users to edit their own profile."""
|
|
|
|
|
|
|
|
USE_OIDC = "use_oidc"
|
|
|
|
"""Allows OpenID Connect authentication."""
|
|
|
|
|
|
|
|
MANAGE_OIDC = "manage_oidc"
|
|
|
|
"""Allows OpenID Connect client managements."""
|
|
|
|
|
|
|
|
MANAGE_USERS = "manage_users"
|
|
|
|
"""Allows other users management."""
|
|
|
|
|
|
|
|
MANAGE_GROUPS = "manage_groups"
|
|
|
|
"""Allows group edition and creation."""
|
|
|
|
|
|
|
|
DELETE_ACCOUNT = "delete_account"
|
|
|
|
"""Allows users to delete their account.
|
|
|
|
|
2024-05-14 07:32:32 +00:00
|
|
|
If used with :attr:`~canaille.core.configuration.Permission.MANAGE_USERS`, users can delete any account.
|
2023-12-18 17:06:03 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
IMPERSONATE_USERS = "impersonate_users"
|
|
|
|
"""Allows users to take the identity of another user."""
|
|
|
|
|
|
|
|
|
|
|
|
class ACLSettings(BaseModel):
|
|
|
|
"""Access Control List settings. Belong in the ``CANAILLE.ACL`` namespace.
|
|
|
|
|
2024-12-08 19:04:37 +00:00
|
|
|
You can define access controls that define what users can do on canaille.
|
2023-12-18 17:06:03 +00:00
|
|
|
An access control consists in a :attr:`FILTER` to match users, a list of :attr:`PERMISSIONS`
|
|
|
|
matched users will be able to perform, and fields users will be able
|
|
|
|
to :attr:`READ` and :attr:`WRITE`. Users matching several filters will cumulate permissions.
|
|
|
|
"""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
PERMISSIONS: list[Permission] = [Permission.EDIT_SELF, Permission.USE_OIDC]
|
2023-12-18 17:06:03 +00:00
|
|
|
"""A list of :class:`Permission` users in the access control will be able
|
2024-12-08 11:22:19 +00:00
|
|
|
to manage.
|
|
|
|
|
|
|
|
For example:
|
2024-12-08 10:33:26 +00:00
|
|
|
|
|
|
|
..code-block:: toml
|
2023-12-18 17:06:03 +00:00
|
|
|
|
2024-12-08 11:22:19 +00:00
|
|
|
PERMISSIONS = ["manage_users", "manage_groups", "manage_oidc", "delete_account", "impersonate_users"]
|
2024-12-08 10:33:26 +00:00
|
|
|
"""
|
2023-12-18 17:06:03 +00:00
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
READ: list[str] = [
|
2023-12-18 17:06:03 +00:00
|
|
|
"user_name",
|
|
|
|
"groups",
|
|
|
|
"lock_date",
|
|
|
|
]
|
|
|
|
"""A list of :class:`~canaille.core.models.User` attributes that users in
|
|
|
|
the ACL will be able to read."""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
WRITE: list[str] = [
|
2023-12-18 17:06:03 +00:00
|
|
|
"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 :class:`~canaille.core.models.User` attributes that users in
|
|
|
|
the ACL will be able to edit."""
|
|
|
|
|
|
|
|
@field_validator("READ")
|
|
|
|
def validate_read_values(
|
|
|
|
cls,
|
2024-10-28 08:13:00 +00:00
|
|
|
read: list[str],
|
2023-12-18 17:06:03 +00:00
|
|
|
info: ValidationInfo,
|
2024-10-28 08:13:00 +00:00
|
|
|
) -> list[str]:
|
2023-12-18 17:06:03 +00:00
|
|
|
from canaille.core.models import User
|
|
|
|
|
2024-04-06 21:22:38 +00:00
|
|
|
assert all(field in User.attributes for field in read)
|
2023-12-18 17:06:03 +00:00
|
|
|
return read
|
|
|
|
|
|
|
|
@field_validator("WRITE")
|
|
|
|
def validate_write_values(
|
|
|
|
cls,
|
2024-10-28 08:13:00 +00:00
|
|
|
write: list[str],
|
2023-12-18 17:06:03 +00:00
|
|
|
info: ValidationInfo,
|
2024-10-28 08:13:00 +00:00
|
|
|
) -> list[str]:
|
2023-12-18 17:06:03 +00:00
|
|
|
from canaille.core.models import User
|
|
|
|
|
2024-04-06 21:22:38 +00:00
|
|
|
assert all(field in User.attributes for field in write)
|
2023-12-18 17:06:03 +00:00
|
|
|
return write
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
FILTER: dict[str, str] | list[dict[str, str]] | None = None
|
2023-12-18 17:06:03 +00:00
|
|
|
""":attr:`FILTER` can be:
|
|
|
|
|
|
|
|
- :py:data:`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'},
|
|
|
|
]
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
class CoreSettings(BaseModel):
|
|
|
|
"""The settings from the ``CANAILLE`` namespace.
|
|
|
|
|
|
|
|
Those are all the configuration parameters that controls the
|
|
|
|
behavior of Canaille.
|
|
|
|
"""
|
|
|
|
|
|
|
|
NAME: str = "Canaille"
|
|
|
|
"""Your organization name.
|
|
|
|
|
|
|
|
Used for display purpose.
|
|
|
|
"""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
LOGO: str | None = None
|
2023-12-18 17:06:03 +00:00
|
|
|
"""The logo of your organization, this is useful to make your organization
|
|
|
|
recognizable on login screens."""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
FAVICON: str | None = None
|
2023-12-18 17:06:03 +00:00
|
|
|
"""You favicon.
|
|
|
|
|
|
|
|
If unset and :attr:`LOGO` is set, then the logo will be used.
|
|
|
|
"""
|
|
|
|
|
|
|
|
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 <https://github.com/tktech/flask-themer>`_.
|
|
|
|
"""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
LANGUAGE: str | None = None
|
2023-12-18 17:06:03 +00:00
|
|
|
"""If a language code is set, it will be used for every user.
|
|
|
|
|
|
|
|
If unset, the language is guessed according to the users browser.
|
|
|
|
"""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
TIMEZONE: str | None = None
|
2023-12-18 17:06:03 +00:00
|
|
|
"""The timezone in which datetimes will be displayed to the users (e.g.
|
|
|
|
``CEST``).
|
|
|
|
|
|
|
|
If unset, the server timezone will be used.
|
|
|
|
"""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
SENTRY_DSN: str | None = None
|
2023-12-18 17:06:03 +00:00
|
|
|
"""A `Sentry <https://sentry.io>`_ DSN to collect the exceptions.
|
|
|
|
|
|
|
|
This is useful for tracking errors in test and production environments.
|
|
|
|
"""
|
|
|
|
|
|
|
|
JAVASCRIPT: bool = True
|
|
|
|
"""Enables Javascript to smooth the user experience."""
|
|
|
|
|
|
|
|
HTMX: bool = True
|
2024-09-11 07:33:42 +00:00
|
|
|
"""Accelerates webpages loading with asynchronous requests."""
|
2023-12-18 17:06:03 +00:00
|
|
|
|
|
|
|
EMAIL_CONFIRMATION: bool = True
|
|
|
|
"""If :py:data:`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
|
2024-09-11 07:33:42 +00:00
|
|
|
if ``SMTP`` is configured, else this is false. If explicitly set to true
|
2023-12-18 17:06:03 +00:00
|
|
|
and ``SMTP`` is disabled, the email field will be read-only.
|
|
|
|
"""
|
|
|
|
|
|
|
|
ENABLE_REGISTRATION: bool = False
|
|
|
|
"""If :py:data:`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 :py:data:`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
|
2024-09-11 07:33:42 +00:00
|
|
|
clue whether the login exists or not.
|
2023-12-18 17:06:03 +00:00
|
|
|
|
|
|
|
If :py:data:`False`,
|
|
|
|
when a user tries to sign in with an invalid login, a message is shown
|
|
|
|
indicating that the login does not exist.
|
|
|
|
"""
|
|
|
|
|
|
|
|
ENABLE_PASSWORD_RECOVERY: bool = True
|
|
|
|
"""If :py:data:`False`, then users cannot ask for a password recovery link
|
|
|
|
by email."""
|
|
|
|
|
2024-12-05 10:42:51 +00:00
|
|
|
ENABLE_INTRUDER_LOCKOUT: bool = False
|
|
|
|
"""If :py:data:`True`, then users will have to wait for an increasingly
|
|
|
|
long time between each failed login attempt."""
|
|
|
|
|
2024-11-14 08:49:19 +00:00
|
|
|
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.
|
2024-12-12 21:02:46 +00:00
|
|
|
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."""
|
2024-10-25 07:51:01 +00:00
|
|
|
|
2024-11-18 13:16:38 +00:00
|
|
|
EMAIL_OTP: bool = False
|
|
|
|
"""If :py:data:`True`, then users will need to authenticate themselves
|
|
|
|
via a one-time password sent to their primary email address."""
|
|
|
|
|
2024-11-29 09:32:17 +00:00
|
|
|
SMS_OTP: bool = False
|
|
|
|
"""If :py:data:`True`, then users will need to authenticate themselves
|
|
|
|
via a one-time password sent to their primary phone number."""
|
|
|
|
|
2023-12-18 17:06:03 +00:00
|
|
|
INVITATION_EXPIRATION: int = 172800
|
|
|
|
"""The validity duration of registration invitations, in seconds.
|
|
|
|
|
|
|
|
Defaults to 2 days.
|
|
|
|
"""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
LOGGING: str | dict | None = None
|
2023-12-18 17:06:03 +00:00
|
|
|
"""Configures the logging output using the python logging configuration format:
|
|
|
|
|
2024-12-08 10:27:38 +00:00
|
|
|
- If :data:`None`, everything is logged in the standard error output.
|
|
|
|
The log level is :data:`~logging.DEBUG` if the :attr:`~canaille.app.configuration.RootSettings.DEBUG`
|
|
|
|
setting is :py:data:`True`, else this is :py:data:`~logging.INFO`.
|
|
|
|
- If this is a :class:`dict`, it is passed to :func:`logging.config.dictConfig`:
|
|
|
|
- If this is a :class:`str`, it is expected to be a file path that will be passed
|
|
|
|
to :func:`logging.config.fileConfig`.
|
2023-12-18 17:06:03 +00:00
|
|
|
|
2024-12-08 10:33:26 +00:00
|
|
|
For example:
|
|
|
|
|
|
|
|
..code-block:: toml
|
2023-12-18 17:06:03 +00:00
|
|
|
|
2024-03-30 10:40:05 +00:00
|
|
|
[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"
|
2023-12-18 17:06:03 +00:00
|
|
|
"""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
SMTP: SMTPSettings | None = None
|
2023-12-18 17:06:03 +00:00
|
|
|
"""The settings related to SMTP and mail configuration.
|
|
|
|
|
|
|
|
If unset, mail-related features like password recovery won't be
|
|
|
|
enabled.
|
|
|
|
"""
|
|
|
|
|
2024-11-29 09:32:17 +00:00
|
|
|
SMPP: SMPPSettings | None = None
|
|
|
|
"""The settings related to SMPP configuration.
|
|
|
|
|
|
|
|
If unset, sms-related features like sms one-time passwords won't be
|
|
|
|
enabled.
|
|
|
|
"""
|
|
|
|
|
2024-10-28 08:13:00 +00:00
|
|
|
ACL: dict[str, ACLSettings] | None = {"DEFAULT": ACLSettings()}
|
2023-12-18 17:06:03 +00:00
|
|
|
"""Mapping of permission groups. See :class:`ACLSettings` for more details.
|
|
|
|
|
2024-12-08 10:33:26 +00:00
|
|
|
The ACL name can be freely chosen. For example:
|
|
|
|
|
|
|
|
..code-block:: toml
|
2023-12-18 17:06:03 +00:00
|
|
|
|
|
|
|
[CANAILLE.ACL.DEFAULT]
|
|
|
|
PERMISSIONS = ["edit_self", "use_oidc"]
|
|
|
|
READ = ["user_name", "groups"]
|
|
|
|
WRITE = ["given_name", "family_name"]
|
|
|
|
|
|
|
|
[CANAILLE.ACL.ADMIN]
|
|
|
|
WRITE = ["user_name", "groups"]
|
|
|
|
"""
|
2024-10-28 21:17:47 +00:00
|
|
|
|
|
|
|
MIN_PASSWORD_LENGTH: int = 8
|
2024-12-08 10:58:10 +00:00
|
|
|
"""User password minimum length.
|
2024-10-28 21:17:47 +00:00
|
|
|
|
2024-12-08 10:58:10 +00:00
|
|
|
If 0 or :data:`None`, password won't have a minimum length.
|
2024-10-28 21:17:47 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
MAX_PASSWORD_LENGTH: int = 1000
|
2024-12-08 10:58:10 +00:00
|
|
|
"""User password maximum length.
|
2024-10-28 21:17:47 +00:00
|
|
|
|
2024-12-08 10:58:10 +00:00
|
|
|
There is a technical of 4096 characters with the SQL backend.
|
|
|
|
If the value is 0, :data:`None`, or greater than 4096,
|
2024-10-28 21:17:47 +00:00
|
|
|
then 4096 will be retained.
|
|
|
|
"""
|
2024-11-12 15:50:00 +00:00
|
|
|
|
2024-11-15 15:28:21 +00:00
|
|
|
ADMIN_EMAIL: str | None = None
|
2024-11-12 15:50:00 +00:00
|
|
|
"""Administration email contact.
|
|
|
|
|
|
|
|
In certain special cases (example : questioning about password
|
|
|
|
corruption), it is necessary to provide an administration contact
|
|
|
|
email.
|
|
|
|
"""
|
2024-11-13 15:12:50 +00:00
|
|
|
|
2024-11-19 12:56:07 +00:00
|
|
|
ENABLE_PASSWORD_COMPROMISSION_CHECK: bool = False
|
2024-11-21 09:43:31 +00:00
|
|
|
"""If :py:data:`True`, Canaille will check if passwords appears in
|
|
|
|
compromission databases such as `HIBP <https://haveibeenpwned.com>`_
|
|
|
|
when users choose a new one."""
|
2024-11-15 15:28:21 +00:00
|
|
|
|
2024-11-21 09:43:31 +00:00
|
|
|
PASSWORD_COMPROMISSION_CHECK_API_URL: str = "https://api.pwnedpasswords.com/range/"
|
|
|
|
"""Have i been pwned api url for compromission checks."""
|
2024-12-17 13:45:10 +00:00
|
|
|
|
|
|
|
PASSWORD_LIFETIME: str | None = None
|
|
|
|
"""Password validity duration.
|
|
|
|
|
2024-12-22 15:12:50 +00:00
|
|
|
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>`_.
|
2024-12-22 15:36:37 +00:00
|
|
|
For example, delay of 60 days is written "P60D".
|
2024-12-17 13:45:10 +00:00
|
|
|
"""
|