forked from Github-Mirrors/canaille
Merge branch 'main' of gitlab.com:yaal/canaille into 179-check-passwords-on-compromised-password-databases
This commit is contained in:
commit
e6a9f2dcc6
22 changed files with 2341 additions and 2977 deletions
54
.github/workflows/tests.yaml
vendored
54
.github/workflows/tests.yaml
vendored
|
@ -11,7 +11,7 @@ on:
|
|||
- '*.*.*'
|
||||
jobs:
|
||||
tests:
|
||||
name: ${{ matrix.python }}
|
||||
name: py${{ matrix.python }} unit tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
@ -23,12 +23,12 @@ jobs:
|
|||
- '3.10'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
- uses: actions/setup-python@v5
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
cache: 'poetry'
|
||||
enable-cache: true
|
||||
- name: Install Python ${{ matrix.python }}
|
||||
run: uv python install ${{ matrix.python }}
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
|
@ -36,25 +36,25 @@ jobs:
|
|||
- name: App armor configuration for slapd
|
||||
if: ${{ !env.ACT }}
|
||||
run: sudo aa-complain /usr/sbin/slapd
|
||||
- name: Install dependencies and run tests
|
||||
- name: Run tests
|
||||
run: |
|
||||
# python tzinfo fails on 'act' without this
|
||||
ulimit -n 1024
|
||||
export TZ=UTC
|
||||
poetry --version
|
||||
poetry install --extras all
|
||||
poetry run pytest --showlocals
|
||||
uv sync --all-extras
|
||||
uv run pytest --showlocals
|
||||
|
||||
minversions:
|
||||
name: minimum dependency versions
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
- uses: actions/setup-python@v5
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v3
|
||||
with:
|
||||
python-version: '3.10'
|
||||
cache: 'poetry'
|
||||
enable-cache: true
|
||||
- name: Install Python 3.10
|
||||
run: uv python install 3.10
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
|
@ -62,37 +62,33 @@ jobs:
|
|||
- name: App armor configuration for slapd
|
||||
if: ${{ !env.ACT }}
|
||||
run: sudo aa-complain /usr/sbin/slapd
|
||||
- run: sed -i -E 's/"(\^|>=)([0-9\.]+)([^,]*)"/"==\2"/' pyproject.toml
|
||||
- run: sed -i -E 's/python = "==/python = "^/' pyproject.toml
|
||||
- name: Install dependencies and run tests
|
||||
- name: Run tests
|
||||
run: |
|
||||
# python tzinfo fails on 'act' without this
|
||||
ulimit -n 1024
|
||||
export TZ=UTC
|
||||
poetry --version
|
||||
poetry lock
|
||||
poetry install --extras all
|
||||
poetry run pytest --showlocals
|
||||
uv sync --all-extras --resolution=lowest-direct
|
||||
uv run pytest --showlocals
|
||||
|
||||
style:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pre-commit/action@v3.0.1
|
||||
|
||||
doc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
- uses: actions/setup-python@v5
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v3
|
||||
with:
|
||||
python-version: '3.13'
|
||||
cache: 'poetry'
|
||||
enable-cache: true
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt --yes --quiet install libsasl2-dev python3-dev libldap2-dev libssl-dev slapd ldap-utils
|
||||
- run: |
|
||||
export TZ=UTC
|
||||
poetry install --with doc
|
||||
poetry run sphinx-build doc build/sphinx/html
|
||||
uv sync --group doc
|
||||
uv run sphinx-build doc build/sphinx/html
|
||||
|
|
128
.gitlab-ci.yml
128
.gitlab-ci.yml
|
@ -1,80 +1,82 @@
|
|||
---
|
||||
image: python
|
||||
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
- release
|
||||
|
||||
before_script:
|
||||
- apt update
|
||||
- env DEBIAN_FRONTEND=noninteractive apt install --yes --quiet python3-dev libldap2-dev libsasl2-dev libssl-dev slapd ldap-utils python3-poetry
|
||||
- poetry config virtualenvs.in-project true
|
||||
variables:
|
||||
UV_VERSION: 0.4
|
||||
UV_CACHE_DIR: .uv-cache
|
||||
BASE_LAYER: bookworm-slim
|
||||
|
||||
cache:
|
||||
- key:
|
||||
files:
|
||||
- uv.lock
|
||||
paths:
|
||||
- .venv
|
||||
- $UV_CACHE_DIR
|
||||
|
||||
before_script:
|
||||
- apt update
|
||||
- env DEBIAN_FRONTEND=noninteractive apt install --yes --quiet gcc python3-dev libldap2-dev libsasl2-dev libssl-dev slapd ldap-utils git curl
|
||||
# Rust is needed to install the zxcvbn dependency
|
||||
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
- export PATH="$HOME/.cargo/bin:$PATH"
|
||||
# Needed until zxcvbn supports Python 3.13
|
||||
# https://github.com/fief-dev/zxcvbn-rs-py/issues/2
|
||||
- export PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1
|
||||
|
||||
style:
|
||||
image: python:3.13
|
||||
variables:
|
||||
PYTHON_VERSION: "3.13"
|
||||
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
|
||||
stage: test
|
||||
script:
|
||||
# pre-commit version is fixed until docformatted hook is compatible
|
||||
# https://github.com/PyCQA/docformatter/issues/293
|
||||
- pip install "pre-commit<4.0.0"
|
||||
- pre-commit run --all-files --show-diff-on-failure
|
||||
|
||||
python310:
|
||||
image: python:3.10
|
||||
stage: test
|
||||
script:
|
||||
- poetry install --extras all
|
||||
- poetry run pytest
|
||||
|
||||
python311:
|
||||
image: python:3.11
|
||||
stage: test
|
||||
script:
|
||||
- poetry install --extras all
|
||||
- poetry run pytest
|
||||
|
||||
python312:
|
||||
image: python:3.12
|
||||
stage: test
|
||||
script:
|
||||
- poetry install --extras all
|
||||
- poetry run pytest
|
||||
|
||||
python313:
|
||||
image: python:3.13
|
||||
stage: test
|
||||
script:
|
||||
- poetry install --extras all
|
||||
- poetry run pytest
|
||||
|
||||
minversions:
|
||||
image: python:3.13
|
||||
stage: test
|
||||
script:
|
||||
- sed -i -E 's/"(\^|>=)([0-9\.]+)([^,]*)"/"==\2"/' pyproject.toml
|
||||
- sed -i -E 's/python = "==/python = "^/' pyproject.toml
|
||||
- poetry lock
|
||||
- poetry install --extras all
|
||||
- poetry run pytest
|
||||
|
||||
doc:
|
||||
image: python:3.13
|
||||
stage: test
|
||||
script:
|
||||
- poetry install --only doc
|
||||
- poetry run sphinx-build doc build/sphinx/html
|
||||
- uv sync --all-extras
|
||||
- uv run pre-commit run --all-files --show-diff-on-failure
|
||||
- uv cache prune --ci
|
||||
|
||||
coverage:
|
||||
image: python:3.13
|
||||
variables:
|
||||
PYTHON_VERSION: "3.12"
|
||||
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
|
||||
stage: test
|
||||
allow_failure: true
|
||||
script:
|
||||
- pip install coveralls pyyaml tomli
|
||||
- poetry install --extras all
|
||||
- poetry run pytest --cov --cov-fail-under=100 --cov-report term:skip-covered -n auto
|
||||
- coveralls
|
||||
- uv sync --all-extras
|
||||
- uv pip install coveralls pyyaml tomli
|
||||
- uv run pytest --cov --cov-fail-under=100 --cov-report term:skip-covered -n auto
|
||||
- uv run coveralls
|
||||
- uv cache prune --ci
|
||||
|
||||
tests:
|
||||
needs: ["coverage", "style"]
|
||||
parallel:
|
||||
matrix:
|
||||
- PYTHON_VERSION: ['3.10', '3.11', '3.12']
|
||||
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
|
||||
stage: test
|
||||
script:
|
||||
- uv sync --all-extras
|
||||
- uv run pytest
|
||||
- uv cache prune --ci
|
||||
|
||||
minversions:
|
||||
needs: ["tests"]
|
||||
variables:
|
||||
PYTHON_VERSION: "3.10"
|
||||
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
|
||||
stage: test
|
||||
script:
|
||||
- uv sync --all-extras --resolution=lowest-direct
|
||||
- uv run pytest
|
||||
- uv cache prune --ci
|
||||
|
||||
doc:
|
||||
variables:
|
||||
PYTHON_VERSION: "3.13"
|
||||
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
|
||||
stage: test
|
||||
script:
|
||||
- uv sync --group doc
|
||||
- uv run sphinx-build doc build/sphinx/html
|
||||
- uv cache prune --ci
|
||||
|
|
|
@ -9,11 +9,11 @@ build:
|
|||
- libldap2-dev
|
||||
- libssl-dev
|
||||
tools:
|
||||
python: "3.11"
|
||||
python: "3.12"
|
||||
jobs:
|
||||
post_create_environment:
|
||||
- pip install poetry
|
||||
- poetry export --with doc --output requirements.txt
|
||||
- pip install uv
|
||||
- uv export --group doc --no-hashes --output-file requirements.txt
|
||||
post_install:
|
||||
- pip install .
|
||||
- pip install --requirement requirements.txt
|
||||
|
|
18
CHANGES.rst
18
CHANGES.rst
|
@ -1,22 +1,22 @@
|
|||
[0.0.56] - Unreleased
|
||||
---------------------
|
||||
|
||||
Fixed
|
||||
^^^^^
|
||||
- With LDAP backend, updating another user groups could result in a permission lost for the editor. :issue:`202`
|
||||
|
||||
Added
|
||||
^^^^^
|
||||
- 1 new parameter : MAX_PASSWORD_LENGTH :issue:`174`
|
||||
- 1 new validator : maximum password length (default 1000) :issue:`174`
|
||||
- password strength progress bar :issue:`174`
|
||||
- implementation of zxcvbn-rs-py which score the password strength :issue:`174`
|
||||
- New security events logs :issue:`177`
|
||||
- :attr:`~canaille.core.configuration.CoreSettings.MAX_PASSWORD_LENGHT` and
|
||||
:attr:`~canaille.core.configuration.CoreSettings.MIN_PASSWORD_LENGHT` configuration options :issue:`174`
|
||||
- Password strength visual indicator :issue:`174`
|
||||
- Security events logs :issue:`177`
|
||||
- Support for Python 3.13 :pr:`186`
|
||||
|
||||
Changed
|
||||
^^^^^^^
|
||||
- Maximum Python requirement is < 3.13 (because of the password_strength_calculator : zxcvbn-rs-py)
|
||||
- MIN_PASSWORD_LENGTH become a parameter :issue:`174`
|
||||
- all password tests and validator are supported by password1 field :issue:`174`
|
||||
- password2 (or Password confirmation) field only support "EQUAL TO PASSWORD" test :issue:`174`
|
||||
- Update to HTMX 2.0.3 :pr:`184`
|
||||
- Migrate from poetry to uv :pr:`187`
|
||||
|
||||
Removed
|
||||
^^^^^^^
|
||||
|
|
|
@ -106,23 +106,23 @@ users and groups with the ``populate`` command:
|
|||
.. code-block:: console
|
||||
|
||||
# If using docker:
|
||||
docker compose exec canaille env CONFIG=conf-docker/canaille-ldap.toml poetry run canaille populate --nb 100 users # or docker-compose
|
||||
docker compose exec canaille env CONFIG=conf-docker/canaille-ldap.toml uv run canaille populate --nb 100 users # or docker-compose
|
||||
|
||||
# If running in local environment
|
||||
env CONFIG=conf/canaille-ldap.toml poetry run canaille populate --nb 100 users
|
||||
env CONFIG=conf/canaille-ldap.toml uv run canaille populate --nb 100 users
|
||||
|
||||
Adapt to use either the `ldap` or the `sql` configuration file. Note that this will not work with the memory backend.
|
||||
|
||||
Unit tests
|
||||
----------
|
||||
|
||||
To run the tests, you just can run `poetry run pytest` and/or `tox` to test all the supported python environments.
|
||||
To run the tests, you just can run `uv run pytest` and/or `uv run tox` to test all the supported python environments.
|
||||
Everything must be green before patches get merged.
|
||||
|
||||
To test a specific backend you can pass ``--backend memory``, ``--backend sql`` or ``--backend ldap`` to pytest and tox.
|
||||
|
||||
The test coverage is 100%, patches won't be accepted if not entirely covered. You can check the
|
||||
test coverage with ``poetry run pytest --cov --cov-report=html`` or ``tox -e coverage -- --cov-report=html``.
|
||||
test coverage with ``uv run pytest --cov --cov-report=html`` or ``tox -e coverage -- --cov-report=html``.
|
||||
You can check the HTML coverage report in the newly created `htmlcov` directory.
|
||||
|
||||
Code style
|
||||
|
@ -170,12 +170,13 @@ The generated documentation is located at ``build/sphinx/html``.
|
|||
Publish a new release
|
||||
---------------------
|
||||
|
||||
1. Check that dependencies are up to date with ``poetry show --outdated --with dev,doc,demo`` and update dependencies accordingly in separated commits.
|
||||
2. Check that tests are still green for every supported python version, and that coverage is still at 100%, by running ``tox``
|
||||
1. Check that dependencies are up to date with ``uv sync --upgrade`` and update dependencies accordingly in separated commits.
|
||||
2. Check that tests are still green for every supported python version, and that coverage is still at 100%, by running ``uv run tox``
|
||||
3. Check that the demo environments are still working
|
||||
4. Check that the :ref:`development/changelog:Release notes` section is correctly filled up
|
||||
5. Increase the version number in ``pyproject.toml``
|
||||
6. Commit with ``git commit``
|
||||
7. Publish with ``poetry publish --build``
|
||||
7. Build with ``uv build``
|
||||
7. Publish with ``uv publish``
|
||||
8. Tag you commit with ``git tag XX.YY.ZZ``
|
||||
9. Push the release commit and the new tag on the repository with ``git push --tags``
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
include canaille/*.sample.*
|
||||
graft canaille/backends/ldap/schemas
|
||||
graft canaille/templates
|
||||
graft canaille/themes
|
||||
graft canaille/translations
|
||||
graft canaille/static
|
|
@ -57,7 +57,7 @@ def setup_flask(app):
|
|||
|
||||
@app.context_processor
|
||||
def global_processor():
|
||||
from canaille.app.flask import current_user
|
||||
from canaille.app.session import current_user
|
||||
|
||||
return {
|
||||
"debug": app.debug or app.config.get("TESTING", False),
|
||||
|
|
|
@ -5,61 +5,14 @@ from urllib.parse import urlunsplit
|
|||
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask import g
|
||||
from flask import request
|
||||
from flask import session
|
||||
from werkzeug.routing import BaseConverter
|
||||
|
||||
from canaille.app import models
|
||||
from canaille.app.i18n import gettext as _
|
||||
from canaille.app.session import current_user
|
||||
from canaille.app.themes import render_template
|
||||
|
||||
|
||||
def current_user():
|
||||
if "user" in g:
|
||||
return g.user
|
||||
|
||||
for user_id in session.get("user_id", [])[::-1]:
|
||||
user = current_app.backend.instance.get(models.User, user_id)
|
||||
if user and (
|
||||
not current_app.backend.has_account_lockability() or not user.locked
|
||||
):
|
||||
g.user = user
|
||||
return g.user
|
||||
|
||||
session["user_id"].remove(user_id)
|
||||
|
||||
if "user_id" in session and not session["user_id"]:
|
||||
del session["user_id"]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def login_user(user):
|
||||
"""Open a session for the user."""
|
||||
g.user = user
|
||||
try:
|
||||
previous = (
|
||||
session["user_id"]
|
||||
if isinstance(session["user_id"], list)
|
||||
else [session["user_id"]]
|
||||
)
|
||||
session["user_id"] = previous + [user.id]
|
||||
except KeyError:
|
||||
session["user_id"] = [user.id]
|
||||
|
||||
|
||||
def logout_user():
|
||||
"""Close the user session."""
|
||||
try:
|
||||
session["user_id"].pop()
|
||||
del g.user
|
||||
if not session["user_id"]:
|
||||
del session["user_id"]
|
||||
except (IndexError, KeyError):
|
||||
pass
|
||||
|
||||
|
||||
def user_needed():
|
||||
def wrapper(view_function):
|
||||
@wraps(view_function)
|
||||
|
|
|
@ -46,7 +46,7 @@ def setup_i18n(app):
|
|||
|
||||
|
||||
def locale_selector():
|
||||
from .flask import current_user
|
||||
from .session import current_user
|
||||
|
||||
user = current_user()
|
||||
available_language_codes = getattr(g, "available_language_codes", [])
|
||||
|
|
50
canaille/app/session.py
Normal file
50
canaille/app/session.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
from flask import current_app
|
||||
from flask import g
|
||||
from flask import session
|
||||
|
||||
from canaille.app import models
|
||||
|
||||
|
||||
def current_user():
|
||||
if "user" in g:
|
||||
return g.user
|
||||
|
||||
for user_id in session.get("user_id", [])[::-1]:
|
||||
user = current_app.backend.instance.get(models.User, user_id)
|
||||
if user and (
|
||||
not current_app.backend.has_account_lockability() or not user.locked
|
||||
):
|
||||
g.user = user
|
||||
return g.user
|
||||
|
||||
session["user_id"].remove(user_id)
|
||||
|
||||
if "user_id" in session and not session["user_id"]:
|
||||
del session["user_id"]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def login_user(user):
|
||||
"""Open a session for the user."""
|
||||
g.user = user
|
||||
try:
|
||||
previous = (
|
||||
session["user_id"]
|
||||
if isinstance(session["user_id"], list)
|
||||
else [session["user_id"]]
|
||||
)
|
||||
session["user_id"] = previous + [user.id]
|
||||
except KeyError:
|
||||
session["user_id"] = [user.id]
|
||||
|
||||
|
||||
def logout_user():
|
||||
"""Close the user session."""
|
||||
try:
|
||||
session["user_id"].pop()
|
||||
del g.user
|
||||
if not session["user_id"]:
|
||||
del session["user_id"]
|
||||
except (IndexError, KeyError):
|
||||
pass
|
|
@ -123,6 +123,7 @@ class BackendModel:
|
|||
return any(self.match_filter(subfilter) for subfilter in filter)
|
||||
|
||||
# If attribute are models, resolve the instance
|
||||
filter = filter.copy()
|
||||
for attribute, value in filter.items():
|
||||
model, _ = self.get_model_annotations(attribute)
|
||||
|
||||
|
|
|
@ -302,16 +302,12 @@ class CoreSettings(BaseModel):
|
|||
MIN_PASSWORD_LENGTH: int = 8
|
||||
"""Minimum length for user password.
|
||||
|
||||
Defaults to 8.
|
||||
|
||||
It is possible not to set a minimum, by entering None or 0.
|
||||
"""
|
||||
|
||||
MAX_PASSWORD_LENGTH: int = 1000
|
||||
"""Maximum length for user password.
|
||||
|
||||
Defaults to 1000.
|
||||
|
||||
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.
|
||||
|
|
|
@ -24,9 +24,6 @@ from canaille.app import build_hash
|
|||
from canaille.app import default_fields
|
||||
from canaille.app import models
|
||||
from canaille.app import obj_to_b64
|
||||
from canaille.app.flask import current_user
|
||||
from canaille.app.flask import login_user
|
||||
from canaille.app.flask import logout_user
|
||||
from canaille.app.flask import permissions_needed
|
||||
from canaille.app.flask import render_htmx_template
|
||||
from canaille.app.flask import request_is_htmx
|
||||
|
@ -42,6 +39,9 @@ from canaille.app.forms import set_readonly
|
|||
from canaille.app.forms import set_writable
|
||||
from canaille.app.i18n import gettext as _
|
||||
from canaille.app.i18n import reload_translations
|
||||
from canaille.app.session import current_user
|
||||
from canaille.app.session import login_user
|
||||
from canaille.app.session import logout_user
|
||||
from canaille.app.themes import render_template
|
||||
from canaille.backends import Backend
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@ from flask import session
|
|||
from flask import url_for
|
||||
|
||||
from canaille.app import build_hash
|
||||
from canaille.app.flask import current_user
|
||||
from canaille.app.flask import login_user
|
||||
from canaille.app.flask import logout_user
|
||||
from canaille.app.flask import smtp_needed
|
||||
from canaille.app.i18n import gettext as _
|
||||
from canaille.app.session import current_user
|
||||
from canaille.app.session import login_user
|
||||
from canaille.app.session import logout_user
|
||||
from canaille.app.themes import render_template
|
||||
from canaille.backends import Backend
|
||||
|
||||
|
|
|
@ -18,10 +18,10 @@ from werkzeug.datastructures import CombinedMultiDict
|
|||
|
||||
from canaille import csrf
|
||||
from canaille.app import models
|
||||
from canaille.app.flask import current_user
|
||||
from canaille.app.flask import logout_user
|
||||
from canaille.app.flask import set_parameter_in_url_query
|
||||
from canaille.app.i18n import gettext as _
|
||||
from canaille.app.session import current_user
|
||||
from canaille.app.session import logout_user
|
||||
from canaille.app.themes import render_template
|
||||
from canaille.backends import Backend
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"Report-Msgid-Bugs-To: contact@yaal.coop\n"
|
||||
"POT-Creation-Date: 2024-10-28 11:57+0100\n"
|
||||
"PO-Revision-Date: 2024-09-13 08:47+0000\n"
|
||||
"Last-Translator: Éloi Rivard <eloi.rivard@nubla.fr>\n"
|
||||
"PO-Revision-Date: 2024-11-01 09:00+0000\n"
|
||||
"Last-Translator: sebastien yaal <sebastien@yaal.coop>\n"
|
||||
"Language-Team: French <https://hosted.weblate.org/projects/canaille/canaille/"
|
||||
"fr/>\n"
|
||||
"Language: fr\n"
|
||||
|
@ -19,7 +19,7 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||
"X-Generator: Weblate 5.8-dev\n"
|
||||
"X-Generator: Weblate 5.8.2-dev\n"
|
||||
"Generated-By: Babel 2.12.1\n"
|
||||
|
||||
#: canaille/app/flask.py:100
|
||||
|
@ -40,11 +40,11 @@ msgstr "N’est pas un numéro de téléphone valide"
|
|||
|
||||
#: canaille/app/forms.py:55
|
||||
msgid "Field must be at least {minimum_password_length} characters long."
|
||||
msgstr ""
|
||||
msgstr "Ce champ doit contenir au minimum {minimum_password_length} caractères."
|
||||
|
||||
#: canaille/app/forms.py:67
|
||||
msgid "Field cannot be longer than {maximum_password_length} characters."
|
||||
msgstr ""
|
||||
msgstr "Ce champ doit contenir au maximum {maximum_password_length} caractères."
|
||||
|
||||
#: canaille/app/forms.py:240
|
||||
msgid "The page number is not valid"
|
||||
|
@ -1967,10 +1967,8 @@ msgid "Add another field"
|
|||
msgstr "Ajouter un autre champ"
|
||||
|
||||
#: canaille/templates/macro/form.html:119
|
||||
#, fuzzy
|
||||
#| msgid "Password reset"
|
||||
msgid "Password strength"
|
||||
msgstr "Réinitialisation du mot de passe"
|
||||
msgstr "Robustesse du mot de passe"
|
||||
|
||||
#: canaille/templates/macro/table.html:8
|
||||
msgid "Search…"
|
||||
|
|
|
@ -7,11 +7,21 @@ RUN \
|
|||
gcc \
|
||||
libsasl2-dev \
|
||||
libldap2-dev \
|
||||
libssl-dev
|
||||
libssl-dev \
|
||||
curl
|
||||
|
||||
COPY poetry.lock pyproject.toml demo/demoapp.py /opt/canaille/
|
||||
RUN pip install poetry
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
|
||||
# Needed until zxcvbn supports Python 3.13
|
||||
# https://github.com/fief-dev/zxcvbn-rs-py/issues/2
|
||||
ENV PYO3_USE_ABI3_FORWARD_COMPATIBILITY="1"
|
||||
|
||||
COPY uv.lock pyproject.toml hatch_build.py LICENSE.rst README.md demo/demoapp.py /opt/canaille/
|
||||
COPY canaille /opt/canaille/canaille
|
||||
RUN pip install uv
|
||||
WORKDIR /opt/canaille
|
||||
RUN poetry install --with demo --without dev --extras all
|
||||
RUN uv sync --group demo --all-extras
|
||||
|
||||
ENTRYPOINT ["poetry", "run", "flask", "run", "--host=0.0.0.0", "--extra-files", "/opt/canaille/conf/canaille-memory.toml", "--extra-files", "/opt/canaille/conf/canaille-ldap.toml", "--extra-files", "/opt/canaille/conf/canaille-sql.toml"]
|
||||
ENTRYPOINT ["uv", "run", "flask", "run", "--host=0.0.0.0", "--extra-files", "/opt/canaille/conf/canaille-memory.toml", "--extra-files", "/opt/canaille/conf/canaille-ldap.toml", "--extra-files", "/opt/canaille/conf/canaille-sql.toml"]
|
||||
|
|
18
demo/run.sh
18
demo/run.sh
|
@ -13,24 +13,28 @@ if ! type python > /dev/null 2>&1 && ! type python3 > /dev/null 2>&1; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if ! type poetry > /dev/null 2>&1; then
|
||||
echo "Cannot start the canaille demo server. Please install poetry on your system"
|
||||
if ! type uv > /dev/null 2>&1; then
|
||||
echo "Cannot start the canaille demo server. Please install uv on your system"
|
||||
echo "or run the demo with docker-compose."
|
||||
echo "https://python-poetry.org/docs/#installation"
|
||||
echo "https://docs.astral.sh/uv/getting-started/installation/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pushd "$DIR" > /dev/null 2>&1 || exit
|
||||
|
||||
poetry install --with demo --all-extras
|
||||
# Needed until zxcvbn supports Python 3.13
|
||||
# https://github.com/fief-dev/zxcvbn-rs-py/issues/2
|
||||
export PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1
|
||||
|
||||
uv sync --group demo --all-extras
|
||||
|
||||
if [ "$BACKEND" = "memory" ]; then
|
||||
|
||||
poetry run honcho --env ../.env --procfile Procfile-memory start
|
||||
uv run honcho --env ../.env --procfile Procfile-memory start
|
||||
|
||||
elif [ "$BACKEND" = "sql" ]; then
|
||||
|
||||
poetry run honcho --env ../.env --procfile Procfile-sql start
|
||||
uv run honcho --env ../.env --procfile Procfile-sql start
|
||||
|
||||
elif [ "$BACKEND" = "ldap" ]; then
|
||||
|
||||
|
@ -40,7 +44,7 @@ elif [ "$BACKEND" = "ldap" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
poetry run honcho --env ../.env --procfile Procfile-ldap start
|
||||
uv run honcho --env ../.env --procfile Procfile-ldap start
|
||||
|
||||
else
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import os
|
||||
|
||||
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
||||
|
||||
def create_mo_files(setup_kwargs):
|
||||
|
||||
def create_mo_files():
|
||||
from babel.messages.frontend import compile_catalog
|
||||
|
||||
cmd = compile_catalog()
|
||||
|
@ -10,7 +12,11 @@ def create_mo_files(setup_kwargs):
|
|||
cmd.statistics = True
|
||||
cmd.finalize_options()
|
||||
cmd.run()
|
||||
return setup_kwargs
|
||||
|
||||
|
||||
class CustomBuildHook(BuildHookInterface):
|
||||
def initialize(self, version, build_data):
|
||||
create_mo_files()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
2615
poetry.lock
generated
2615
poetry.lock
generated
File diff suppressed because it is too large
Load diff
308
pyproject.toml
308
pyproject.toml
|
@ -1,13 +1,13 @@
|
|||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0", "babel", "setuptools; python_version>='3.12'"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
requires = ["hatchling", "babel", "setuptools >= 50.0.0; python_version>='3.12'"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool]
|
||||
[tool.poetry]
|
||||
[project]
|
||||
name = "Canaille"
|
||||
version = "0.0.55"
|
||||
description = "Lightweight identity and authorization management software"
|
||||
license = "MIT"
|
||||
license = {file = "LICENSE.rst"}
|
||||
readme = "README.md"
|
||||
keywords = ["oidc", "oauth", "oauth2", "openid", "identity"]
|
||||
classifiers = [
|
||||
"Intended Audience :: Developers",
|
||||
|
@ -25,137 +25,102 @@ classifiers = [
|
|||
"Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP",
|
||||
|
||||
]
|
||||
authors = ["Yaal Coop <contact@yaal.coop>"]
|
||||
maintainers = [
|
||||
"Éloi Rivard <eloi@yaal.coop>",
|
||||
authors = [{name="Yaal Coop", email="contact@yaal.coop"}]
|
||||
maintainers = [{name="Éloi Rivard", email="eloi@yaal.coop"}]
|
||||
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"flask >= 3.0.0",
|
||||
"flask-wtf >= 1.2.1",
|
||||
"pydantic-settings >= 2.0.3",
|
||||
"wtforms >= 3.1.1",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
front = [
|
||||
"email_validator >= 2.0.0",
|
||||
"flask-babel >= 4.0.0",
|
||||
"flask-themer >= 2.0.0",
|
||||
"pycountry >= 23.12.7",
|
||||
"pytz >= 2022.7",
|
||||
"toml >= 0.10.0",
|
||||
# zxcvbn does not support Python 3.13 yet
|
||||
# This leads to Canaille installation to fail in some situations.
|
||||
# https://github.com/fief-dev/zxcvbn-rs-py/issues/2
|
||||
"zxcvbn-rs-py >= 0.1.1; python_version<'3.13'",
|
||||
]
|
||||
|
||||
oidc = [
|
||||
"authlib >= 1.3.0",
|
||||
]
|
||||
|
||||
ldap = [
|
||||
"python-ldap >= 3.4.0",
|
||||
]
|
||||
|
||||
sentry = [
|
||||
"sentry-sdk >= 2.0.0",
|
||||
]
|
||||
|
||||
sql = [
|
||||
"passlib >= 1.7.4",
|
||||
"sqlalchemy >= 2.0.23",
|
||||
"sqlalchemy-json >= 0.7.0",
|
||||
"sqlalchemy-utils >= 0.41.1",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://canaille.yaal.coop"
|
||||
documentation = "https://canaille.readthedocs.io/en/latest/"
|
||||
repository = "https://gitlab.com/yaal/canaille"
|
||||
readme = "README.md"
|
||||
include = ["canaille/translations/*/LC_MESSAGES/*.mo"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "<3.13,>=3.10"
|
||||
flask = "^3.0.0"
|
||||
flask-wtf = "^1.2.1"
|
||||
pydantic-settings = "^2.0.3"
|
||||
wtforms = "^3.1.1"
|
||||
|
||||
# extra : front
|
||||
email_validator = {version = "^2.0.0", optional=true}
|
||||
flask-babel = {version = "^4.0.0", optional=true}
|
||||
flask-themer = {version = "^2.0.0", optional=true}
|
||||
pycountry = {version = ">=22.1.10", optional=true}
|
||||
pytz = {version = ">=2022.7", optional=true}
|
||||
toml = {version = "^0.10.0", optional=true, python = "<3.11"}
|
||||
zxcvbn-rs-py = {version = "^0.1.1", optional=true}
|
||||
|
||||
# extra : oidc
|
||||
authlib = {version = "^1.2.1", optional=true}
|
||||
|
||||
# extra : ldap
|
||||
python-ldap = {version = "^3.4.0", optional=true}
|
||||
|
||||
# extra : sentry
|
||||
sentry-sdk = {version = "^2.0.0", optional=true, extras=["flask"]}
|
||||
|
||||
# extra : sql
|
||||
passlib = {version = "^1.7.4", optional=true}
|
||||
sqlalchemy = {version = "^2.0.23", optional=true}
|
||||
sqlalchemy-json = {version = "^0.7.0", optional=true}
|
||||
sqlalchemy-utils = {version = "^0.41.1", optional=true}
|
||||
|
||||
[tool.poetry.group.doc]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.doc.dependencies]
|
||||
autodoc-pydantic = "^2.0.1"
|
||||
shibuya = "^2024.3.1"
|
||||
sphinx = ">=7.0.0"
|
||||
sphinx-design = "^0.6.0"
|
||||
sphinx-issues = "^5.0.0"
|
||||
sphinx-click = "^6.0.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
coverage = {version = "*", extras=["toml"]}
|
||||
faker = "*"
|
||||
flask-webtest = "*"
|
||||
# pre-commit version is fixed until docformatted hook is compatible
|
||||
# https://github.com/PyCQA/docformatter/issues/293
|
||||
pre-commit = "^3.0.0"
|
||||
pyquery = "*"
|
||||
pytest = "^8.0.0"
|
||||
pytest-coverage = "*"
|
||||
pytest-httpserver = "*"
|
||||
pytest-lazy-fixtures = "^1.0.7"
|
||||
pytest-smtpd = "^0.1.0"
|
||||
pytest-xdist = "^3.3.1"
|
||||
slapd = "*"
|
||||
time-machine = "^2.14.1"
|
||||
toml = "^0.10.0"
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"babel >= 2.14.0",
|
||||
"coverage[toml] >= 6.0.0",
|
||||
"faker >= 30.0.0",
|
||||
"flask-webtest >= 0.1.6",
|
||||
# pre-commit version is fixed until docformatted hook is compatible
|
||||
# https://github.com/PyCQA/docformatter/issues/293
|
||||
"pre-commit < 4.0.0",
|
||||
"pre-commit-uv>=4.1.4",
|
||||
"pyquery >= 2.0.0",
|
||||
"pytest >= 8.0.0",
|
||||
"pytest-cov >= 6.0.0",
|
||||
"pytest-httpserver >= 1.1.0",
|
||||
"pytest-lazy-fixtures >= 1.0.7",
|
||||
"pytest-smtpd >= 0.1.0",
|
||||
"pytest-xdist >= 3.3.1",
|
||||
"slapd >= 0.1.5",
|
||||
"time-machine >= 2.14.1",
|
||||
"toml >= 0.10.0",
|
||||
"tox-uv >= 1.16.0",
|
||||
|
||||
# Babel 2.14 does not directly depend on setuptools
|
||||
# https://github.com/python-babel/babel/blob/40e60a1f6cf178d9f57fcc14f157ea1b2ab77361/CHANGES.rst?plain=1#L22-L24
|
||||
# and neither python 3.12 due to PEP 632
|
||||
# https://peps.python.org/pep-0632/
|
||||
setuptools = {version = "*", python = ">=3.12"}
|
||||
|
||||
[tool.poetry.group.demo]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.demo.dependencies]
|
||||
faker = "*"
|
||||
honcho = "*"
|
||||
slapd = "*"
|
||||
requests = "*"
|
||||
watchdog = "^4.0.0"
|
||||
|
||||
[tool.poetry.extras]
|
||||
front = [
|
||||
"click",
|
||||
"email_validator",
|
||||
"flask-babel",
|
||||
"flask-themer",
|
||||
"pycountry",
|
||||
"pytz",
|
||||
"toml",
|
||||
"zxcvbn-rs-py",
|
||||
]
|
||||
ldap = [
|
||||
"python-ldap",
|
||||
]
|
||||
oidc = [
|
||||
"authlib",
|
||||
]
|
||||
sentry = [
|
||||
"sentry-sdk",
|
||||
]
|
||||
sql = [
|
||||
"passlib",
|
||||
"sqlalchemy",
|
||||
"sqlalchemy-json",
|
||||
"sqlalchemy-utils",
|
||||
]
|
||||
all = [
|
||||
"click",
|
||||
"email_validator",
|
||||
"flask-babel",
|
||||
"flask-themer",
|
||||
"passlib",
|
||||
"pycountry",
|
||||
"pytz",
|
||||
"toml",
|
||||
"python-ldap",
|
||||
"authlib",
|
||||
"sentry-sdk",
|
||||
"sqlalchemy",
|
||||
"sqlalchemy-json",
|
||||
"sqlalchemy-utils",
|
||||
"zxcvbn-rs-py",
|
||||
"setuptools >= 50.0.0; python_version>='3.12'"
|
||||
]
|
||||
|
||||
[tool.poetry.scripts]
|
||||
doc = [
|
||||
"autodoc-pydantic >= 2.0.1",
|
||||
"shibuya >= 2024.3.1",
|
||||
"sphinx >= 7.0.0",
|
||||
"sphinx-design >= 0.6.0",
|
||||
"sphinx-issues >= 5.0.0",
|
||||
"sphinx-click >= 6.0.0",
|
||||
]
|
||||
|
||||
demo = [
|
||||
"faker",
|
||||
"honcho",
|
||||
"slapd",
|
||||
"requests",
|
||||
"watchdog >= 4.0.0",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
canaille = "canaille.commands:cli"
|
||||
|
||||
[options.packages.find]
|
||||
|
@ -166,9 +131,25 @@ exclude = [
|
|||
"doc.*",
|
||||
]
|
||||
|
||||
[tool.poetry.build]
|
||||
generate-setup-file = false
|
||||
script = "build.py"
|
||||
[tool.hatch.build]
|
||||
include = [
|
||||
"canaille/",
|
||||
"doc/",
|
||||
"tests/",
|
||||
"CHANGES.rst",
|
||||
"CONTRIBUTING.rst",
|
||||
"README.md",
|
||||
]
|
||||
exclude = [
|
||||
"docs/_build/",
|
||||
]
|
||||
artifacts = ["canaille/translations/*/LC_MESSAGES/*.mo"]
|
||||
|
||||
[tool.hatch.build.hooks.custom]
|
||||
dependencies = [
|
||||
"Babel>=2.6.0",
|
||||
"setuptools >= 50.0.0; python_version>='3.12'",
|
||||
]
|
||||
|
||||
[tool.coverage.run]
|
||||
source = [
|
||||
|
@ -206,41 +187,52 @@ force-single-line = true
|
|||
docstring-code-format = true
|
||||
|
||||
[tool.tox]
|
||||
legacy_tox_ini = """
|
||||
[tox]
|
||||
isolated_build = true
|
||||
skipsdist = true
|
||||
envlist =
|
||||
style
|
||||
py310
|
||||
py311
|
||||
py312
|
||||
py313
|
||||
doc
|
||||
coverage
|
||||
requires = ["tox>=4.19"]
|
||||
env_list = [
|
||||
"style",
|
||||
"py310",
|
||||
"py311",
|
||||
"py312",
|
||||
"py313",
|
||||
"minversions",
|
||||
"doc",
|
||||
"coverage",
|
||||
]
|
||||
|
||||
[testenv]
|
||||
allowlist_externals = poetry
|
||||
commands =
|
||||
poetry install --extras all
|
||||
poetry run pytest --showlocals --full-trace {posargs}
|
||||
[tool.tox.env_run_base]
|
||||
# Needed until zxcvbn supports Python 3.13
|
||||
# https://github.com/fief-dev/zxcvbn-rs-py/issues/2
|
||||
set_env = {PYO3_USE_ABI3_FORWARD_COMPATIBILITY = "1"}
|
||||
runner = "uv-venv-lock-runner"
|
||||
dependency_groups = ["dev"]
|
||||
uv_sync_flags = ["--all-extras"]
|
||||
commands = [
|
||||
["pytest", "--showlocals", "--full-trace", "{posargs}"],
|
||||
]
|
||||
|
||||
[testenv:style]
|
||||
commands =
|
||||
pip install pre-commit<4
|
||||
pre-commit run --all-files
|
||||
[tool.tox.env.style]
|
||||
skip_install = true
|
||||
runner = "uv-venv-runner"
|
||||
commands = [
|
||||
["pre-commit", "run", "--all-files", "--show-diff-on-failure"],
|
||||
]
|
||||
|
||||
[testenv:doc]
|
||||
commands =
|
||||
poetry install --with doc --without dev --extras oidc
|
||||
poetry run sphinx-build doc build/sphinx/html
|
||||
[tool.tox.env.minversions]
|
||||
uv_resolution = "lowest-direct"
|
||||
basepython = ["python3.10"]
|
||||
|
||||
[testenv:coverage]
|
||||
commands =
|
||||
poetry install --extras all
|
||||
poetry run pytest --cov --cov-fail-under=100 --cov-report term:skip-covered {posargs:-n auto}
|
||||
poetry run coverage html
|
||||
"""
|
||||
[tool.tox.env.doc]
|
||||
dependency_groups = ["doc"]
|
||||
commands = [
|
||||
["sphinx-build", "--builder", "html", "doc", "build/sphinx/html"],
|
||||
["sphinx-build", "--builder", "man", "doc", "build/sphinx/html"],
|
||||
]
|
||||
|
||||
[tool.tox.env.coverage]
|
||||
commands = [
|
||||
["pytest", "--cov", "--cov-fail-under=100", "--cov-report", "term:skip-covered", "{posargs}"],
|
||||
["coverage", "html"],
|
||||
]
|
||||
|
||||
[[tool.babel.mappings]]
|
||||
method = "python"
|
||||
|
|
Loading…
Reference in a new issue