diff --git a/CHANGES.rst b/CHANGES.rst index 4ae66f4c..5117da27 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,6 +20,8 @@ Added - Implement OIDC client_credentials flow. :issue:`207` - Button in the client admin page to create client tokens. - Basic SCIM implementation. :issue:`116` :pr:`197` +- Password expiry policy :issue:`176` +- :attr:`~canaille.core.configuration.CoreSettings.PASSWORD_LIFETIME` Changed ^^^^^^^ diff --git a/canaille/app/flask.py b/canaille/app/flask.py index 73597653..8281bd64 100644 --- a/canaille/app/flask.py +++ b/canaille/app/flask.py @@ -5,8 +5,11 @@ from urllib.parse import urlunsplit from flask import abort from flask import current_app +from flask import flash from flask import make_response +from flask import redirect from flask import request +from flask import url_for from werkzeug.exceptions import HTTPException from werkzeug.routing import BaseConverter @@ -15,21 +18,7 @@ from canaille.app.session import current_user from canaille.app.themes import render_template -def user_needed(): - def wrapper(view_function): - @wraps(view_function) - def decorator(*args, **kwargs): - user = current_user() - if not user: - abort(403) - return view_function(*args, user=user, **kwargs) - - return decorator - - return wrapper - - -def permissions_needed(*args): +def user_needed(*args): permissions = set(args) def wrapper(view_function): @@ -38,6 +27,19 @@ def permissions_needed(*args): user = current_user() if not user or not user.can(*permissions): abort(403) + + if user.has_expired_password(): + flash( + _("Your password has expired, please choose a new password."), + "info", + ) + return redirect( + url_for( + "core.account.reset", + user=user, + ) + ) + return view_function(*args, user=user, **kwargs) return decorator diff --git a/canaille/backends/ldap/models.py b/canaille/backends/ldap/models.py index 8a04b689..dc78bf82 100644 --- a/canaille/backends/ldap/models.py +++ b/canaille/backends/ldap/models.py @@ -41,6 +41,7 @@ class User(canaille.core.models.User, LDAPObject): "one_time_password": "oathTokenPIN", "one_time_password_emission_date": "oathSecretTime", "password_failure_timestamps": "pwdFailureTime", + "password_last_update": "pwdChangedTime", } def match_filter(self, filter): diff --git a/canaille/backends/memory/backend.py b/canaille/backends/memory/backend.py index 853218cb..46c2fc5a 100644 --- a/canaille/backends/memory/backend.py +++ b/canaille/backends/memory/backend.py @@ -84,6 +84,10 @@ class MemoryBackend(Backend): def set_user_password(self, user, password): user.password = password + user.password_last_update = datetime.datetime.now( + datetime.timezone.utc + ).replace(microsecond=0) + self.save(user) def query(self, model, **kwargs): diff --git a/canaille/backends/sql/backend.py b/canaille/backends/sql/backend.py index 726ecff7..9bf23d62 100644 --- a/canaille/backends/sql/backend.py +++ b/canaille/backends/sql/backend.py @@ -77,6 +77,9 @@ class SQLBackend(Backend): def set_user_password(self, user, password): user.password = password + user.password_last_update = datetime.datetime.now( + datetime.timezone.utc + ).replace(microsecond=0) self.save(user) def query(self, model, **kwargs): diff --git a/canaille/backends/sql/models.py b/canaille/backends/sql/models.py index caa1822a..a59a44d9 100644 --- a/canaille/backends/sql/models.py +++ b/canaille/backends/sql/models.py @@ -76,6 +76,9 @@ class User(canaille.core.models.User, Base, SqlAlchemyModel): password: Mapped[str] = mapped_column( PasswordType(schemes=["pbkdf2_sha512"]), nullable=True ) + password_last_update: Mapped[datetime.datetime] = mapped_column( + TZDateTime(timezone=True), nullable=True + ) _password_failure_timestamps: Mapped[list[str]] = mapped_column( MutableJson, nullable=True ) diff --git a/canaille/config.sample.toml b/canaille/config.sample.toml index 95f8cca2..2898a30b 100644 --- a/canaille/config.sample.toml +++ b/canaille/config.sample.toml @@ -120,6 +120,14 @@ SECRET_KEY = "change me before you go in production" # 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 `_. +# 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 diff --git a/canaille/core/configuration.py b/canaille/core/configuration.py index 8ee3960c..cc9472c3 100644 --- a/canaille/core/configuration.py +++ b/canaille/core/configuration.py @@ -374,3 +374,13 @@ class CoreSettings(BaseModel): 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 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 `_. + Example for 60 days: "P60D" + It is possible to disable this option by entering None. + """ diff --git a/canaille/core/endpoints/account.py b/canaille/core/endpoints/account.py index df114187..9576475f 100644 --- a/canaille/core/endpoints/account.py +++ b/canaille/core/endpoints/account.py @@ -24,7 +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 permissions_needed from canaille.app.flask import render_htmx_template from canaille.app.flask import request_is_htmx from canaille.app.flask import smtp_needed @@ -53,6 +52,7 @@ from ..mails import send_registration_mail from .forms import EmailConfirmationForm from .forms import InvitationForm from .forms import JoinForm +from .forms import PasswordResetForm from .forms import build_profile_form bp = Blueprint("account", __name__) @@ -140,7 +140,7 @@ def about(): @bp.route("/users", methods=["GET", "POST"]) -@permissions_needed("manage_users") +@user_needed("manage_users") def users(user): table_form = TableForm( models.User, @@ -195,7 +195,7 @@ class RegistrationPayload(VerificationPayload): @bp.route("/invite", methods=["GET", "POST"]) @smtp_needed() -@permissions_needed("manage_users") +@user_needed("manage_users") def user_invitation(user): form = InvitationForm(request.form or None) email_sent = None @@ -406,7 +406,7 @@ def email_confirmation(data, hash): @bp.route("/profile", methods=("GET", "POST")) -@permissions_needed("manage_users") +@user_needed("manage_users") def profile_creation(user): form = build_profile_form(user.writable_fields, user.readable_fields) form.process(CombinedMultiDict((request.files, request.form)) or None) @@ -847,7 +847,7 @@ def profile_delete(user, edited_user): @bp.route("/impersonate/") -@permissions_needed("impersonate_users") +@user_needed("impersonate_users") def impersonate(user, puppet): if puppet.locked: abort(403, _("Locked users cannot be impersonated.")) @@ -881,3 +881,23 @@ def photo(user, field): return send_file( stream, mimetype="image/jpeg", last_modified=user.last_modified, etag=etag ) + + +@bp.route("/reset/", methods=["GET", "POST"]) +def reset(user): + form = PasswordResetForm(request.form) + if user != current_user() or not user.has_expired_password(): + abort(403) + + if request.form and form.validate(): + Backend.instance.set_user_password(user, form.password.data) + login_user(user) + flash(_("Your password has been updated successfully"), "success") + return redirect( + session.pop( + "redirect-after-login", + url_for("core.account.profile_edition", edited_user=user), + ) + ) + + return render_template("core/reset-password.html", form=form, user=user, hash=None) diff --git a/canaille/core/endpoints/admin.py b/canaille/core/endpoints/admin.py index 8a23ea82..a802e248 100644 --- a/canaille/core/endpoints/admin.py +++ b/canaille/core/endpoints/admin.py @@ -7,7 +7,7 @@ from wtforms import StringField from wtforms.validators import DataRequired from canaille.app import obj_to_b64 -from canaille.app.flask import permissions_needed +from canaille.app.flask import user_needed from canaille.app.forms import Form from canaille.app.forms import email_validator from canaille.app.i18n import gettext as _ @@ -34,7 +34,7 @@ class MailTestForm(Form): @bp.route("/mail", methods=["GET", "POST"]) -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def mail_index(user): form = MailTestForm(request.form or None) if request.form and form.validate(): @@ -47,7 +47,7 @@ def mail_index(user): @bp.route("/mail/test.html") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def test_html(user): base_url = url_for("core.account.index", _external=True) return render_template( @@ -62,7 +62,7 @@ def test_html(user): @bp.route("/mail/test.txt") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def test_txt(user): base_url = url_for("core.account.index", _external=True) return render_template( @@ -73,7 +73,7 @@ def test_txt(user): @bp.route("/mail/password-init.html") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def password_init_html(user): base_url = url_for("core.account.index", _external=True) reset_url = url_for( @@ -99,7 +99,7 @@ def password_init_html(user): @bp.route("/mail/password-init.txt") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def password_init_txt(user): base_url = url_for("core.account.index", _external=True) reset_url = url_for( @@ -118,7 +118,7 @@ def password_init_txt(user): @bp.route("/mail/reset.html") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def password_reset_html(user): base_url = url_for("core.account.index", _external=True) reset_url = url_for( @@ -144,7 +144,7 @@ def password_reset_html(user): @bp.route("/mail/reset.txt") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def password_reset_txt(user): base_url = url_for("core.account.index", _external=True) reset_url = url_for( @@ -163,7 +163,7 @@ def password_reset_txt(user): @bp.route("/mail///invitation.html") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def invitation_html(user, identifier, email): base_url = url_for("core.account.index", _external=True) registration_url = url_for( @@ -186,7 +186,7 @@ def invitation_html(user, identifier, email): @bp.route("/mail///invitation.txt") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def invitation_txt(user, identifier, email): base_url = url_for("core.account.index", _external=True) registration_url = url_for( @@ -205,7 +205,7 @@ def invitation_txt(user, identifier, email): @bp.route("/mail///email-confirmation.html") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def email_confirmation_html(user, identifier, email): base_url = url_for("core.account.index", _external=True) email_confirmation_url = url_for( @@ -228,7 +228,7 @@ def email_confirmation_html(user, identifier, email): @bp.route("/mail///email-confirmation.txt") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def email_confirmation_txt(user, identifier, email): base_url = url_for("core.account.index", _external=True) email_confirmation_url = url_for( @@ -247,7 +247,7 @@ def email_confirmation_txt(user, identifier, email): @bp.route("/mail//registration.html") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def registration_html(user, email): base_url = url_for("core.account.index", _external=True) registration_url = url_for( @@ -270,7 +270,7 @@ def registration_html(user, email): @bp.route("/mail//registration.txt") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def registration_txt(user, email): base_url = url_for("core.account.index", _external=True) registration_url = url_for( @@ -289,7 +289,7 @@ def registration_txt(user, email): @bp.route("/mail/compromised_password_check_failure.html") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def compromised_password_check_failure_html(user): base_url = url_for("core.account.index", _external=True) user_name = "" @@ -313,7 +313,7 @@ def compromised_password_check_failure_html(user): @bp.route("/mail/compromised_password_check_failure.txt") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def compromised_password_check_failure_txt(user): base_url = url_for("core.account.index", _external=True) user_name = "" @@ -333,7 +333,7 @@ def compromised_password_check_failure_txt(user): @bp.route("/mail/email_otp.html") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def email_otp_html(user): base_url = url_for("core.account.index", _external=True) otp = "000000" @@ -351,7 +351,7 @@ def email_otp_html(user): @bp.route("/mail/email_otp.txt") -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def email_otp_txt(user): base_url = url_for("core.account.index", _external=True) otp = "000000" diff --git a/canaille/core/endpoints/groups.py b/canaille/core/endpoints/groups.py index 19efaf63..939c09db 100644 --- a/canaille/core/endpoints/groups.py +++ b/canaille/core/endpoints/groups.py @@ -6,8 +6,8 @@ from flask import request from flask import url_for from canaille.app import models -from canaille.app.flask import permissions_needed from canaille.app.flask import render_htmx_template +from canaille.app.flask import user_needed from canaille.app.forms import TableForm from canaille.app.i18n import gettext as _ from canaille.app.themes import render_template @@ -21,7 +21,7 @@ bp = Blueprint("groups", __name__, url_prefix="/groups") @bp.route("/", methods=["GET", "POST"]) -@permissions_needed("manage_groups") +@user_needed("manage_groups") def groups(user): table_form = TableForm(models.Group, formdata=request.form) if request.form and request.form.get("page") and not table_form.validate(): @@ -33,7 +33,7 @@ def groups(user): @bp.route("/add", methods=("GET", "POST")) -@permissions_needed("manage_groups") +@user_needed("manage_groups") def create_group(user): form = CreateGroupForm(request.form or None) @@ -61,7 +61,7 @@ def create_group(user): @bp.route("/", methods=("GET", "POST")) -@permissions_needed("manage_groups") +@user_needed("manage_groups") def group(user, group): if ( request.method == "GET" diff --git a/canaille/core/models.py b/canaille/core/models.py index b1d23cda..98ba9232 100644 --- a/canaille/core/models.py +++ b/canaille/core/models.py @@ -4,6 +4,7 @@ from typing import Annotated from typing import ClassVar from flask import current_app +from pydantic import TypeAdapter from canaille.backends.models import Model from canaille.core.configuration import Permission @@ -99,6 +100,11 @@ class User(Model): "never"). """ + password_last_update: datetime.datetime | None = None + """Specifies the last time the entry's password was changed. + By default, the date of creation of the password is retained. + """ + preferred_language: str | None = None """Indicates the user's preferred written or spoken languages and is generally used for selecting a localized user interface. @@ -486,6 +492,23 @@ class User(Model): ).total_seconds() return max(calculated_delay - time_since_last_failed_bind, 0) + def has_expired_password(self): + last_update = self.password_last_update or datetime.datetime.now( + datetime.timezone.utc + ) + if current_app.config["CANAILLE"]["PASSWORD_LIFETIME"] is None: + password_expiration = None + else: + password_expiration = TypeAdapter(datetime.timedelta).validate_python( + current_app.config["CANAILLE"]["PASSWORD_LIFETIME"] + ) + + return ( + password_expiration is not None + and last_update + password_expiration + < datetime.datetime.now(datetime.timezone.utc) + ) + class Group(Model): """User model, based on the `SCIM Group schema diff --git a/canaille/oidc/endpoints/authorizations.py b/canaille/oidc/endpoints/authorizations.py index c4cbce20..08a08667 100644 --- a/canaille/oidc/endpoints/authorizations.py +++ b/canaille/oidc/endpoints/authorizations.py @@ -3,8 +3,8 @@ from flask import abort from flask import request from canaille.app import models -from canaille.app.flask import permissions_needed from canaille.app.flask import render_htmx_template +from canaille.app.flask import user_needed from canaille.app.forms import TableForm from canaille.app.themes import render_template @@ -12,7 +12,7 @@ bp = Blueprint("authorizations", __name__, url_prefix="/admin/authorization") @bp.route("/", methods=["GET", "POST"]) -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def index(user): table_form = TableForm(models.AuthorizationCode, formdata=request.form) if request.form and request.form.get("page") and not table_form.validate(): @@ -26,7 +26,7 @@ def index(user): @bp.route("/", methods=["GET", "POST"]) -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def view(user, authorization): return render_template( "oidc/authorization_view.html", diff --git a/canaille/oidc/endpoints/clients.py b/canaille/oidc/endpoints/clients.py index 13ae71ce..8b0a9326 100644 --- a/canaille/oidc/endpoints/clients.py +++ b/canaille/oidc/endpoints/clients.py @@ -10,8 +10,8 @@ from flask import url_for from werkzeug.security import gen_salt from canaille.app import models -from canaille.app.flask import permissions_needed from canaille.app.flask import render_htmx_template +from canaille.app.flask import user_needed from canaille.app.forms import TableForm from canaille.app.i18n import gettext as _ from canaille.app.themes import render_template @@ -23,7 +23,7 @@ bp = Blueprint("clients", __name__, url_prefix="/admin/client") @bp.route("/", methods=["GET", "POST"]) -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def index(user): table_form = TableForm(models.Client, formdata=request.form) if request.form and request.form.get("page") and not table_form.validate(): @@ -35,7 +35,7 @@ def index(user): @bp.route("/add", methods=["GET", "POST"]) -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def add(user): form = ClientAddForm(request.form or None) @@ -87,7 +87,7 @@ def add(user): @bp.route("/edit/", methods=["GET", "POST"]) -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def edit(user, client): if request.form.get("action") == "confirm-delete": return render_template("oidc/modals/delete-client.html", client=client) diff --git a/canaille/oidc/endpoints/tokens.py b/canaille/oidc/endpoints/tokens.py index a91e5481..afec9e29 100644 --- a/canaille/oidc/endpoints/tokens.py +++ b/canaille/oidc/endpoints/tokens.py @@ -7,8 +7,8 @@ from flask import flash from flask import request from canaille.app import models -from canaille.app.flask import permissions_needed from canaille.app.flask import render_htmx_template +from canaille.app.flask import user_needed from canaille.app.forms import TableForm from canaille.app.i18n import gettext as _ from canaille.app.themes import render_template @@ -20,7 +20,7 @@ bp = Blueprint("tokens", __name__, url_prefix="/admin/token") @bp.route("/", methods=["GET", "POST"]) -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def index(user): table_form = TableForm(models.Token, formdata=request.form) if request.form and request.form.get("page") and not table_form.validate(): @@ -32,7 +32,7 @@ def index(user): @bp.route("/", methods=["GET", "POST"]) -@permissions_needed("manage_oidc") +@user_needed("manage_oidc") def view(user, token): form = TokenRevokationForm(request.form or None) diff --git a/canaille/templates/core/reset-password.html b/canaille/templates/core/reset-password.html index 4a0c721e..f015fa7c 100644 --- a/canaille/templates/core/reset-password.html +++ b/canaille/templates/core/reset-password.html @@ -28,5 +28,6 @@ Displays a password reset form.
{{ fui.render_form(form, _("Password reset")) }}
+ {% endblock %} diff --git a/canaille/translations/messages.pot b/canaille/translations/messages.pot index fe67da18..355f36cd 100644 --- a/canaille/translations/messages.pot +++ b/canaille/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-12-12 12:02+0100\n" +"POT-Creation-Date: 2024-12-20 09:35+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,7 +17,11 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.16.0\n" -#: canaille/app/flask.py:55 +#: canaille/app/flask.py:33 +msgid "Your password has expired, please choose a new password." +msgstr "" + +#: canaille/app/flask.py:57 msgid "No SMTP server has been configured" msgstr "" @@ -45,11 +49,11 @@ msgstr "" msgid "This password appears on public compromission databases and is not secure." msgstr "" -#: canaille/app/forms.py:285 +#: canaille/app/forms.py:291 msgid "The page number is not valid" msgstr "" -#: canaille/app/forms.py:313 +#: canaille/app/forms.py:319 msgid "Not a valid datetime value." msgstr "" @@ -72,36 +76,36 @@ msgid "" "please contact the administrators." msgstr "" -#: canaille/backends/ldap/backend.py:99 +#: canaille/backends/ldap/backend.py:100 msgid "Could not connect to the LDAP server '{uri}'" msgstr "" -#: canaille/backends/ldap/backend.py:106 +#: canaille/backends/ldap/backend.py:107 msgid "LDAP authentication failed with user '{user}'" msgstr "" -#: canaille/backends/ldap/backend.py:175 +#: canaille/backends/ldap/backend.py:176 msgid "John Doe" msgstr "" -#: canaille/backends/ldap/backend.py:178 canaille/core/endpoints/forms.py:166 -#: canaille/core/endpoints/forms.py:427 +#: canaille/backends/ldap/backend.py:179 canaille/core/endpoints/forms.py:105 +#: canaille/core/endpoints/forms.py:372 msgid "jdoe" msgstr "" -#: canaille/backends/ldap/backend.py:181 +#: canaille/backends/ldap/backend.py:182 msgid "john.doe@example.com" msgstr "" -#: canaille/backends/ldap/backend.py:183 +#: canaille/backends/ldap/backend.py:184 msgid " or " msgstr "" -#: canaille/backends/ldap/backend.py:238 +#: canaille/backends/ldap/backend.py:244 msgid "Your account has been locked." msgstr "" -#: canaille/backends/ldap/backend.py:243 +#: canaille/backends/ldap/backend.py:249 msgid "You should change your password." msgstr "" @@ -139,6 +143,36 @@ msgstr "" msgid "One-time password authentication on {website_name}" msgstr "" +#: canaille/core/validators.py:16 +msgid "The user name '{user_name}' already exists" +msgstr "" + +#: canaille/core/validators.py:25 +msgid "The email '{email}' is already used" +msgstr "" + +#: canaille/core/validators.py:32 +msgid "The group '{group}' already exists" +msgstr "" + +#: canaille/core/validators.py:41 +msgid "The login '{login}' does not exist" +msgstr "" + +#: canaille/core/validators.py:48 +msgid "The user you are trying to remove does not exist." +msgstr "" + +#: canaille/core/validators.py:53 +msgid "The user '{user}' has already been removed from the group '{group}'" +msgstr "" + +#: canaille/core/validators.py:71 +msgid "" +"The group '{group}' cannot be removed, because it must have at least one " +"user left." +msgstr "" + #: canaille/core/endpoints/account.py:92 canaille/core/endpoints/account.py:118 msgid "You will receive soon an email to continue the registration process." msgstr "" @@ -167,141 +201,145 @@ msgstr "" msgid "You are already logged in, you cannot create an account." msgstr "" -#: canaille/core/endpoints/account.py:301 canaille/core/endpoints/forms.py:317 -#: canaille/core/endpoints/forms.py:445 canaille/core/templates/groups.html:5 -#: canaille/core/templates/groups.html:23 -#: canaille/core/templates/partial/group-members.html:15 -#: canaille/core/templates/partial/users.html:18 -#: canaille/templates/base.html:58 +#: canaille/core/endpoints/account.py:301 canaille/core/endpoints/forms.py:256 +#: canaille/core/endpoints/forms.py:390 canaille/templates/base.html:80 +#: canaille/templates/core/groups.html:10 +#: canaille/templates/core/groups.html:28 +#: canaille/templates/core/partial/group-members.html:15 +#: canaille/templates/core/partial/users.html:18 msgid "Groups" msgstr "" -#: canaille/core/endpoints/account.py:339 -#: canaille/core/endpoints/account.py:432 +#: canaille/core/endpoints/account.py:337 +#: canaille/core/endpoints/account.py:426 msgid "User account creation failed." msgstr "" -#: canaille/core/endpoints/account.py:350 +#: canaille/core/endpoints/account.py:346 msgid "Your account has been created successfully." msgstr "" -#: canaille/core/endpoints/account.py:365 -#: canaille/core/endpoints/account.py:387 +#: canaille/core/endpoints/account.py:361 +#: canaille/core/endpoints/account.py:383 msgid "The email confirmation link that brought you here is invalid." msgstr "" -#: canaille/core/endpoints/account.py:372 +#: canaille/core/endpoints/account.py:368 msgid "The email confirmation link that brought you here has expired." msgstr "" -#: canaille/core/endpoints/account.py:379 +#: canaille/core/endpoints/account.py:375 msgid "The invitation link that brought you here was invalid." msgstr "" -#: canaille/core/endpoints/account.py:394 +#: canaille/core/endpoints/account.py:390 msgid "This address email have already been confirmed." msgstr "" -#: canaille/core/endpoints/account.py:401 +#: canaille/core/endpoints/account.py:397 msgid "This address email is already associated with another account." msgstr "" -#: canaille/core/endpoints/account.py:408 +#: canaille/core/endpoints/account.py:404 msgid "Your email address have been confirmed." msgstr "" -#: canaille/core/endpoints/account.py:442 +#: canaille/core/endpoints/account.py:434 msgid "User account creation succeed." msgstr "" -#: canaille/core/endpoints/account.py:618 -#: canaille/core/endpoints/account.py:804 +#: canaille/core/endpoints/account.py:610 +#: canaille/core/endpoints/account.py:798 msgid "Profile edition failed." msgstr "" -#: canaille/core/endpoints/account.py:628 -#: canaille/core/endpoints/account.py:822 +#: canaille/core/endpoints/account.py:620 +#: canaille/core/endpoints/account.py:816 msgid "Profile updated successfully." msgstr "" -#: canaille/core/endpoints/account.py:636 +#: canaille/core/endpoints/account.py:628 msgid "Email addition failed." msgstr "" -#: canaille/core/endpoints/account.py:641 +#: canaille/core/endpoints/account.py:633 msgid "" "An email has been sent to the email address. Please check your inbox and " "click on the verification link it contains" msgstr "" -#: canaille/core/endpoints/account.py:648 +#: canaille/core/endpoints/account.py:640 msgid "Could not send the verification email" msgstr "" -#: canaille/core/endpoints/account.py:658 +#: canaille/core/endpoints/account.py:650 msgid "Email deletion failed." msgstr "" -#: canaille/core/endpoints/account.py:661 +#: canaille/core/endpoints/account.py:653 msgid "The email have been successfully deleted." msgstr "" -#: canaille/core/endpoints/account.py:698 +#: canaille/core/endpoints/account.py:692 msgid "" "A password initialization link has been sent at the user email address. " "It should be received within a few minutes." msgstr "" -#: canaille/core/endpoints/account.py:705 canaille/core/endpoints/auth.py:183 +#: canaille/core/endpoints/account.py:699 canaille/core/endpoints/auth.py:183 msgid "Could not send the password initialization email" msgstr "" -#: canaille/core/endpoints/account.py:716 +#: canaille/core/endpoints/account.py:710 msgid "" "A password reset link has been sent at the user email address. It should " "be received within a few minutes." msgstr "" -#: canaille/core/endpoints/account.py:723 +#: canaille/core/endpoints/account.py:717 msgid "Could not send the password reset email" msgstr "" -#: canaille/core/endpoints/account.py:739 +#: canaille/core/endpoints/account.py:733 msgid "The account has been locked" msgstr "" -#: canaille/core/endpoints/account.py:750 +#: canaille/core/endpoints/account.py:744 msgid "The account has been unlocked" msgstr "" -#: canaille/core/endpoints/account.py:763 +#: canaille/core/endpoints/account.py:757 msgid "One-time password authentication has been reset" msgstr "" -#: canaille/core/endpoints/account.py:842 +#: canaille/core/endpoints/account.py:836 #, python-format msgid "The user %(user)s has been successfully deleted" msgstr "" -#: canaille/core/endpoints/account.py:859 +#: canaille/core/endpoints/account.py:853 msgid "Locked users cannot be impersonated." msgstr "" -#: canaille/core/endpoints/account.py:863 canaille/core/endpoints/auth.py:134 +#: canaille/core/endpoints/account.py:857 canaille/core/endpoints/auth.py:134 #: canaille/core/endpoints/auth.py:367 #, python-format msgid "Connection successful. Welcome %(user)s" msgstr "" +#: canaille/core/endpoints/account.py:895 canaille/core/endpoints/auth.py:269 +msgid "Your password has been updated successfully" +msgstr "" + #: canaille/core/endpoints/admin.py:23 -#: canaille/core/templates/partial/users.html:15 +#: canaille/templates/core/partial/users.html:15 msgid "Email" msgstr "" -#: canaille/core/endpoints/admin.py:29 canaille/core/endpoints/forms.py:99 -#: canaille/core/endpoints/forms.py:122 canaille/core/endpoints/forms.py:211 -#: canaille/core/endpoints/forms.py:413 canaille/core/endpoints/forms.py:439 -#: canaille/core/endpoints/forms.py:463 canaille/core/endpoints/forms.py:479 +#: canaille/core/endpoints/admin.py:29 canaille/core/endpoints/forms.py:38 +#: canaille/core/endpoints/forms.py:61 canaille/core/endpoints/forms.py:150 +#: canaille/core/endpoints/forms.py:356 canaille/core/endpoints/forms.py:384 +#: canaille/core/endpoints/forms.py:408 canaille/core/endpoints/forms.py:424 msgid "jane.doe@example.com" msgstr "" @@ -368,1224 +406,242 @@ msgstr "" msgid "The password reset link that brought you here was invalid." msgstr "" -#: canaille/core/endpoints/auth.py:269 -msgid "Your password has been updated successfully" -msgstr "" - -#: canaille/core/endpoints/forms.py:33 -msgid "The user name '{user_name}' already exists" -msgstr "" - -#: canaille/core/endpoints/forms.py:42 -msgid "The email '{email}' is already used" -msgstr "" - -#: canaille/core/endpoints/forms.py:49 -msgid "The group '{group}' already exists" -msgstr "" - -#: canaille/core/endpoints/forms.py:58 -msgid "The login '{login}' does not exist" -msgstr "" - -#: canaille/core/endpoints/forms.py:65 -msgid "The user you are trying to remove does not exist." -msgstr "" - -#: canaille/core/endpoints/forms.py:70 -msgid "The user '{user}' has already been removed from the group '{group}'" -msgstr "" - -#: canaille/core/endpoints/forms.py:88 -msgid "" -"The group '{group}' cannot be removed, because it must have at least one " -"user left." -msgstr "" - -#: canaille/core/endpoints/forms.py:96 canaille/core/endpoints/forms.py:119 -#: canaille/core/templates/partial/group-members.html:9 -#: canaille/core/templates/partial/users.html:9 +#: canaille/core/endpoints/forms.py:35 canaille/core/endpoints/forms.py:58 +#: canaille/templates/core/partial/group-members.html:9 +#: canaille/templates/core/partial/users.html:9 msgid "Login" msgstr "" -#: canaille/core/endpoints/forms.py:109 canaille/core/endpoints/forms.py:131 -#: canaille/core/endpoints/forms.py:265 -#: canaille/core/templates/profile_settings.html:63 +#: canaille/core/endpoints/forms.py:48 canaille/core/endpoints/forms.py:70 +#: canaille/core/endpoints/forms.py:204 +#: canaille/templates/core/profile_settings.html:74 msgid "Password" msgstr "" -#: canaille/core/endpoints/forms.py:138 canaille/core/endpoints/forms.py:277 +#: canaille/core/endpoints/forms.py:77 canaille/core/endpoints/forms.py:216 msgid "Password confirmation" msgstr "" -#: canaille/core/endpoints/forms.py:141 canaille/core/endpoints/forms.py:280 +#: canaille/core/endpoints/forms.py:80 canaille/core/endpoints/forms.py:219 msgid "Password and confirmation do not match." msgstr "" -#: canaille/core/endpoints/forms.py:160 +#: canaille/core/endpoints/forms.py:99 msgid "Automatic" msgstr "" -#: canaille/core/endpoints/forms.py:165 +#: canaille/core/endpoints/forms.py:104 msgid "Username" msgstr "" -#: canaille/core/endpoints/forms.py:169 canaille/core/endpoints/forms.py:369 -#: canaille/core/endpoints/forms.py:383 -#: canaille/core/templates/partial/group-members.html:12 -#: canaille/core/templates/partial/groups.html:6 -#: canaille/core/templates/partial/users.html:12 -#: canaille/oidc/endpoints/forms.py:29 -#: canaille/oidc/templates/partial/client_list.html:6 +#: canaille/core/endpoints/forms.py:108 canaille/core/endpoints/forms.py:310 +#: canaille/core/endpoints/forms.py:326 canaille/oidc/endpoints/forms.py:29 +#: canaille/templates/core/partial/group-members.html:12 +#: canaille/templates/core/partial/groups.html:6 +#: canaille/templates/core/partial/users.html:12 +#: canaille/templates/oidc/partial/client_list.html:6 msgid "Name" msgstr "" -#: canaille/core/endpoints/forms.py:171 +#: canaille/core/endpoints/forms.py:110 msgid "Title" msgstr "" -#: canaille/core/endpoints/forms.py:171 +#: canaille/core/endpoints/forms.py:110 msgid "Vice president" msgstr "" -#: canaille/core/endpoints/forms.py:174 +#: canaille/core/endpoints/forms.py:113 msgid "Given name" msgstr "" -#: canaille/core/endpoints/forms.py:176 +#: canaille/core/endpoints/forms.py:115 msgid "John" msgstr "" -#: canaille/core/endpoints/forms.py:182 +#: canaille/core/endpoints/forms.py:121 msgid "Family Name" msgstr "" -#: canaille/core/endpoints/forms.py:185 +#: canaille/core/endpoints/forms.py:124 msgid "Doe" msgstr "" -#: canaille/core/endpoints/forms.py:191 +#: canaille/core/endpoints/forms.py:130 msgid "Display Name" msgstr "" -#: canaille/core/endpoints/forms.py:194 +#: canaille/core/endpoints/forms.py:133 msgid "Johnny" msgstr "" -#: canaille/core/endpoints/forms.py:201 canaille/core/endpoints/forms.py:469 -#: canaille/core/templates/profile_edit.html:176 +#: canaille/core/endpoints/forms.py:140 canaille/core/endpoints/forms.py:414 +#: canaille/templates/core/profile_edit.html:188 msgid "Email addresses" msgstr "" -#: canaille/core/endpoints/forms.py:207 canaille/core/endpoints/forms.py:459 +#: canaille/core/endpoints/forms.py:146 canaille/core/endpoints/forms.py:404 msgid "" "This email will be used as a recovery address to reset the password if " "needed" msgstr "" -#: canaille/core/endpoints/forms.py:221 +#: canaille/core/endpoints/forms.py:160 msgid "Phone numbers" msgstr "" -#: canaille/core/endpoints/forms.py:222 +#: canaille/core/endpoints/forms.py:161 msgid "555-000-555" msgstr "" -#: canaille/core/endpoints/forms.py:229 +#: canaille/core/endpoints/forms.py:168 msgid "Address" msgstr "" -#: canaille/core/endpoints/forms.py:231 +#: canaille/core/endpoints/forms.py:170 msgid "132, Foobar Street, Gotham City 12401, XX" msgstr "" -#: canaille/core/endpoints/forms.py:235 +#: canaille/core/endpoints/forms.py:174 msgid "Street" msgstr "" -#: canaille/core/endpoints/forms.py:237 +#: canaille/core/endpoints/forms.py:176 msgid "132, Foobar Street" msgstr "" -#: canaille/core/endpoints/forms.py:241 +#: canaille/core/endpoints/forms.py:180 msgid "Postal Code" msgstr "" -#: canaille/core/endpoints/forms.py:247 +#: canaille/core/endpoints/forms.py:186 msgid "Locality" msgstr "" -#: canaille/core/endpoints/forms.py:249 +#: canaille/core/endpoints/forms.py:188 msgid "Gotham City" msgstr "" -#: canaille/core/endpoints/forms.py:253 +#: canaille/core/endpoints/forms.py:192 msgid "Region" msgstr "" -#: canaille/core/endpoints/forms.py:255 +#: canaille/core/endpoints/forms.py:194 msgid "North Pole" msgstr "" -#: canaille/core/endpoints/forms.py:259 +#: canaille/core/endpoints/forms.py:198 msgid "Photo" msgstr "" -#: canaille/core/endpoints/forms.py:263 -#: canaille/core/templates/profile_add.html:56 -#: canaille/core/templates/profile_edit.html:64 +#: canaille/core/endpoints/forms.py:202 +#: canaille/templates/core/profile_add.html:64 +#: canaille/templates/core/profile_edit.html:76 msgid "Delete the photo" msgstr "" -#: canaille/core/endpoints/forms.py:288 +#: canaille/core/endpoints/forms.py:227 msgid "User number" msgstr "" -#: canaille/core/endpoints/forms.py:290 canaille/core/endpoints/forms.py:296 +#: canaille/core/endpoints/forms.py:229 canaille/core/endpoints/forms.py:235 msgid "1234" msgstr "" -#: canaille/core/endpoints/forms.py:294 +#: canaille/core/endpoints/forms.py:233 msgid "Department" msgstr "" -#: canaille/core/endpoints/forms.py:300 +#: canaille/core/endpoints/forms.py:239 msgid "Organization" msgstr "" -#: canaille/core/endpoints/forms.py:302 +#: canaille/core/endpoints/forms.py:241 msgid "Cogip LTD." msgstr "" -#: canaille/core/endpoints/forms.py:306 +#: canaille/core/endpoints/forms.py:245 msgid "Website" msgstr "" -#: canaille/core/endpoints/forms.py:308 +#: canaille/core/endpoints/forms.py:247 msgid "https://mywebsite.tld" msgstr "" -#: canaille/core/endpoints/forms.py:313 +#: canaille/core/endpoints/forms.py:252 msgid "Preferred language" msgstr "" -#: canaille/core/endpoints/forms.py:323 +#: canaille/core/endpoints/forms.py:262 msgid "users, admins …" msgstr "" -#: canaille/core/endpoints/forms.py:348 +#: canaille/core/endpoints/forms.py:287 msgid "Account expiration" msgstr "" -#: canaille/core/endpoints/forms.py:372 +#: canaille/core/endpoints/forms.py:313 msgid "group" msgstr "" -#: canaille/core/endpoints/forms.py:376 canaille/core/endpoints/forms.py:393 -#: canaille/core/templates/partial/groups.html:7 +#: canaille/core/endpoints/forms.py:317 canaille/core/endpoints/forms.py:336 +#: canaille/templates/core/partial/groups.html:7 msgid "Description" msgstr "" -#: canaille/core/endpoints/forms.py:407 canaille/core/endpoints/forms.py:432 +#: canaille/core/endpoints/forms.py:350 canaille/core/endpoints/forms.py:377 msgid "Email address" msgstr "" -#: canaille/core/endpoints/forms.py:426 +#: canaille/core/endpoints/forms.py:371 msgid "User name" msgstr "" -#: canaille/core/endpoints/forms.py:430 +#: canaille/core/endpoints/forms.py:375 msgid "Username editable by the invitee" msgstr "" -#: canaille/core/endpoints/forms.py:472 +#: canaille/core/endpoints/forms.py:417 msgid "New email address" msgstr "" -#: canaille/core/endpoints/forms.py:488 -#: canaille/core/templates/mails/email_otp.txt:5 +#: canaille/core/endpoints/forms.py:433 +#: canaille/templates/core/mails/email_otp.txt:5 msgid "One-time password" msgstr "" -#: canaille/core/endpoints/forms.py:494 +#: canaille/core/endpoints/forms.py:439 msgid "123456" msgstr "" -#: canaille/core/endpoints/groups.py:40 +#: canaille/core/endpoints/groups.py:42 msgid "Group creation failed." msgstr "" -#: canaille/core/endpoints/groups.py:48 +#: canaille/core/endpoints/groups.py:50 #, python-format msgid "The group %(group)s has been successfully created" msgstr "" -#: canaille/core/endpoints/groups.py:108 +#: canaille/core/endpoints/groups.py:110 #, python-format msgid "The group %(group)s has been successfully edited." msgstr "" -#: canaille/core/endpoints/groups.py:116 +#: canaille/core/endpoints/groups.py:118 msgid "Group edition failed." msgstr "" -#: canaille/core/endpoints/groups.py:145 +#: canaille/core/endpoints/groups.py:147 #, python-format msgid "%(user_name)s has been removed from the group %(group_name)s" msgstr "" -#: canaille/core/endpoints/groups.py:162 +#: canaille/core/endpoints/groups.py:164 #, python-format msgid "The group %(group)s has been successfully deleted" msgstr "" -#: canaille/core/templates/about.html:5 canaille/core/templates/about.html:16 -#: canaille/templates/base.html:99 -msgid "About Canaille" -msgstr "" - -#: canaille/core/templates/about.html:18 -msgid "Free and open-source identity provider." -msgstr "" - -#: canaille/core/templates/about.html:21 -#, python-format -msgid "Version %(version)s" -msgstr "" - -#: canaille/core/templates/about.html:22 -msgid "Homepage" -msgstr "" - -#: canaille/core/templates/about.html:23 -msgid "Documentation" -msgstr "" - -#: canaille/core/templates/about.html:24 -msgid "Source code" -msgstr "" - -#: canaille/core/templates/firstlogin.html:12 -msgid "First login" -msgstr "" - -#: canaille/core/templates/firstlogin.html:19 -msgid "" -"It seems this is the first time you are logging here. In order to " -"finalize your account configuration, you need to set a password to your " -"account. We will send you an email containing a link that will allow you " -"to set a password. Please click on the \"Send the initialization email\" " -"button below to send the email." -msgstr "" - -#: canaille/core/templates/firstlogin.html:34 -#: canaille/core/templates/forgotten-password.html:43 -#: canaille/core/templates/join.html:39 -#: canaille/core/templates/profile_add.html:191 -msgid "Login page" -msgstr "" - -#: canaille/core/templates/firstlogin.html:35 -msgid "Send the initialization email" -msgstr "" - -#: canaille/core/templates/forgotten-password.html:5 -#: canaille/core/templates/forgotten-password.html:16 -#: canaille/core/templates/join.html:41 canaille/core/templates/login.html:43 -#: canaille/core/templates/password.html:41 -msgid "Forgotten password" -msgstr "" - -#: canaille/core/templates/forgotten-password.html:23 -msgid "" -"After this form is sent, if the email address or the login you provided " -"exists, you will receive an email containing a link that will allow you " -"to reset your password." -msgstr "" - -#: canaille/core/templates/forgotten-password.html:41 -#: canaille/core/templates/login.html:40 -msgid "Create an account" -msgstr "" - -#: canaille/core/templates/forgotten-password.html:46 -msgid "Send again" -msgstr "" - -#: canaille/core/templates/forgotten-password.html:48 -#: canaille/core/templates/mails/admin.html:54 -msgid "Send" -msgstr "" - -#: canaille/core/templates/group.html:7 canaille/core/templates/group.html:29 -msgid "Group creation" -msgstr "" - -#: canaille/core/templates/group.html:9 canaille/core/templates/group.html:31 -msgid "Group edition" -msgstr "" - -#: canaille/core/templates/group.html:16 canaille/core/templates/groups.html:11 -#: canaille/core/templates/invite.html:15 -#: canaille/core/templates/profile_add.html:16 -#: canaille/core/templates/users.html:11 -msgid "View" -msgstr "" - -#: canaille/core/templates/group.html:20 canaille/core/templates/groups.html:15 -#: canaille/core/templates/invite.html:19 -#: canaille/core/templates/profile_add.html:20 -#: canaille/core/templates/users.html:15 -msgid "Add" -msgstr "" - -#: canaille/core/templates/group.html:37 -msgid "Create a new group" -msgstr "" - -#: canaille/core/templates/group.html:39 -msgid "Edit information about a group" -msgstr "" - -#: canaille/core/templates/group.html:50 -msgid "" -"Because group cannot be empty, you will be added to the group. You can " -"remove you later by editing your profile when you will have added other " -"members to the group." -msgstr "" - -#: canaille/core/templates/group.html:58 -msgid "Delete group" -msgstr "" - -#: canaille/core/templates/group.html:63 -msgid "Create group" -msgstr "" - -#: canaille/core/templates/group.html:65 -#: canaille/core/templates/profile_edit.html:164 -#: canaille/core/templates/profile_settings.html:172 -#: canaille/oidc/templates/client_edit.html:81 -msgid "Edit" -msgstr "" - -#: canaille/core/templates/group.html:77 -msgid "Group members" -msgstr "" - -#: canaille/core/templates/invite.html:5 canaille/core/templates/invite.html:97 -msgid "Invite a user" -msgstr "" - -#: canaille/core/templates/invite.html:23 -#: canaille/core/templates/profile_add.html:25 -#: canaille/core/templates/users.html:20 -msgid "Invite" -msgstr "" - -#: canaille/core/templates/invite.html:35 -msgid "Invitation link" -msgstr "" - -#: canaille/core/templates/invite.html:37 -msgid "Invitation sent" -msgstr "" - -#: canaille/core/templates/invite.html:39 -msgid "Invitation not sent" -msgstr "" - -#: canaille/core/templates/invite.html:47 -msgid "" -"Here is the invitation link you can provide to the user you want to " -"invite:" -msgstr "" - -#: canaille/core/templates/invite.html:54 -#, python-format -msgid "This invitation link has been sent to %(email)s" -msgstr "" - -#: canaille/core/templates/invite.html:55 -msgid "" -"If you need to provide this link by other ways than email, you can copy " -"it there:" -msgstr "" - -#: canaille/core/templates/invite.html:62 -#, python-format -msgid "" -"This invitation link could not be sent to %(email)s due to technical " -"issues." -msgstr "" - -#: canaille/core/templates/invite.html:63 -msgid "" -"However you can copy the link there to provide it by other ways than " -"email:" -msgstr "" - -#: canaille/core/templates/invite.html:74 -#: canaille/oidc/templates/token_view.html:130 -#: canaille/oidc/templates/token_view.html:141 -msgid "Copy" -msgstr "" - -#: canaille/core/templates/invite.html:81 -#: canaille/core/templates/invite.html:128 -msgid "Create a user" -msgstr "" - -#: canaille/core/templates/invite.html:85 -msgid "Invite another user" -msgstr "" - -#: canaille/core/templates/invite.html:102 -msgid "" -"After this form is sent, the recipient your indicated will receive an " -"email containing an account creation link." -msgstr "" - -#: canaille/core/templates/invite.html:131 -msgid "Generate a link" -msgstr "" - -#: canaille/core/templates/invite.html:134 -msgid "Send the invitation" -msgstr "" - -#: canaille/core/templates/join.html:6 canaille/core/templates/join.html:17 -#: canaille/core/templates/profile_add.html:6 -#: canaille/core/templates/profile_add.html:34 -#: canaille/core/templates/profile_edit.html:7 -msgid "User creation" -msgstr "" - -#: canaille/core/templates/join.html:19 -#: canaille/core/templates/profile_add.html:37 -msgid "Create a new user account" -msgstr "" - -#: canaille/core/templates/join.html:24 -msgid "" -"Before you can create an account, please enter a valid email address. " -"Then you will receive an email containing a link that will allow you to " -"finish your registration." -msgstr "" - -#: canaille/core/templates/join.html:44 -#: canaille/core/templates/profile_add.html:194 -msgid "Submit" -msgstr "" - -#: canaille/core/templates/login.html:21 -#, python-format -msgid "Sign in at %(website)s" -msgstr "" - -#: canaille/core/templates/login.html:23 -msgid "Manage your information and your authorizations" -msgstr "" - -#: canaille/core/templates/login.html:45 -#: canaille/core/templates/mails/registration.txt:5 -#: canaille/core/templates/setup-2fa.html:53 -msgid "Continue" -msgstr "" - -#: canaille/core/templates/password.html:21 -#: canaille/core/templates/setup-2fa.html:21 -#: canaille/core/templates/verify-2fa.html:21 -#, python-format -msgid "Sign in as %(username)s" -msgstr "" - -#: canaille/core/templates/password.html:23 -msgid "Please enter your password for this account." -msgstr "" - -#: canaille/core/templates/password.html:39 -#: canaille/core/templates/verify-2fa.html:39 -#, python-format -msgid "I am not %(username)s" -msgstr "" - -#: canaille/core/templates/password.html:43 canaille/templates/base.html:74 -msgid "Sign in" -msgstr "" - -#: canaille/core/templates/profile_add.html:42 -#: canaille/core/templates/profile_edit.html:22 -#: canaille/core/templates/profile_settings.html:15 -msgid "Personal information" -msgstr "" - -#: canaille/core/templates/profile_add.html:53 -#: canaille/core/templates/profile_add.html:64 -#: canaille/core/templates/profile_edit.html:61 -#: canaille/core/templates/profile_edit.html:72 -msgid "Click to upload a photo" -msgstr "" - -#: canaille/core/templates/profile_add.html:152 -#: canaille/core/templates/profile_edit.html:26 -#: canaille/core/templates/profile_settings.html:19 -#: canaille/core/templates/profile_settings.html:40 -msgid "Account settings" -msgstr "" - -#: canaille/core/templates/profile_add.html:175 -msgid "User password is not mandatory" -msgstr "" - -#: canaille/core/templates/profile_add.html:179 -msgid "" -"The user will not be able to authenticate unless the password is set, but" -" they will be able to ask for a password initialization mail." -msgstr "" - -#: canaille/core/templates/profile_add.html:181 -msgid "The user will not be able to authenticate unless the password is set." -msgstr "" - -#: canaille/core/templates/profile_edit.html:9 -#: canaille/core/templates/profile_edit.html:35 -#: canaille/core/templates/profile_settings.html:6 -msgid "My profile" -msgstr "" - -#: canaille/core/templates/profile_edit.html:11 -#: canaille/core/templates/profile_edit.html:37 -#: canaille/core/templates/profile_settings.html:8 -msgid "User profile edition" -msgstr "" - -#: canaille/core/templates/profile_edit.html:43 -msgid "Edit your personal information" -msgstr "" - -#: canaille/core/templates/profile_edit.html:45 -msgid "Edit information about a user" -msgstr "" - -#: canaille/core/templates/profile_edit.html:174 -msgid "My email addresses" -msgstr "" - -#: canaille/core/templates/profile_edit.html:195 -msgid "Remove this email address" -msgstr "" - -#: canaille/core/templates/profile_edit.html:216 -msgid "Send a verification email to validate this address." -msgstr "" - -#: canaille/core/templates/profile_edit.html:221 -#: canaille/core/templates/verify-2fa.html:40 -msgid "Verify" -msgstr "" - -#: canaille/core/templates/partial/profile_field.html:31 -#: canaille/core/templates/profile_settings.html:30 -msgid "This user cannot edit this field" -msgstr "" - -#: canaille/core/templates/partial/profile_field.html:40 -#: canaille/core/templates/profile_settings.html:32 -msgid "This user cannot see this field" -msgstr "" - -#: canaille/core/templates/profile_settings.html:43 -#, python-format -msgid "Created on %(creation_datetime)s" -msgstr "" - -#: canaille/core/templates/profile_settings.html:67 -msgid "" -"You can change your password here by typing it once it each fields below." -" The more a password is long, the more it is strong. A good password is a" -" long password that you will remember, not a complicated password that " -"your use for all your accounts." -msgstr "" - -#: canaille/core/templates/profile_settings.html:91 -#: canaille/core/templates/profile_settings.html:113 -msgid "Send mail" -msgstr "" - -#: canaille/core/templates/profile_settings.html:95 -msgid "This user does not have a password yet" -msgstr "" - -#: canaille/core/templates/profile_settings.html:98 -msgid "You can solve this by:" -msgstr "" - -#: canaille/core/templates/profile_settings.html:100 -msgid "setting a password using this form;" -msgstr "" - -#: canaille/core/templates/profile_settings.html:101 -msgid "sending the user a password initialization mail, by clicking this button;" -msgstr "" - -#: canaille/core/templates/profile_settings.html:102 -msgid "" -"or simply waiting for the user to sign-in a first time, and then receive " -"a password initialization mail." -msgstr "" - -#: canaille/core/templates/profile_settings.html:105 -msgid "The user will not be able to authenticate unless the password is set" -msgstr "" - -#: canaille/core/templates/mails/admin.html:109 -#: canaille/core/templates/mails/reset.html:19 -#: canaille/core/templates/profile_settings.html:116 -#: canaille/core/templates/reset-password.html:12 -#: canaille/core/templates/reset-password.html:18 -msgid "Password reset" -msgstr "" - -#: canaille/core/templates/profile_settings.html:118 -msgid "" -"If the user has forgotten his password, you can send him a password reset" -" email by clicking this button." -msgstr "" - -#: canaille/core/templates/profile_settings.html:127 -msgid "Unlock" -msgstr "" - -#: canaille/core/templates/profile_settings.html:130 -msgid "This user account is locked" -msgstr "" - -#: canaille/core/templates/profile_settings.html:133 -msgid "The user won't be able to connect until their account is unlocked." -msgstr "" - -#: canaille/core/templates/modals/reset-otp.html:27 -#: canaille/core/templates/profile_settings.html:145 -msgid "Reset one-time password authentication" -msgstr "" - -#: canaille/core/templates/profile_settings.html:151 -msgid "Lock the account" -msgstr "" - -#: canaille/core/templates/profile_settings.html:158 -msgid "Delete the user" -msgstr "" - -#: canaille/core/templates/profile_settings.html:160 -msgid "Delete my account" -msgstr "" - -#: canaille/core/templates/profile_settings.html:167 -msgid "Impersonate" -msgstr "" - -#: canaille/core/templates/setup-2fa.html:23 -msgid "Set up multi-factor authentication." -msgstr "" - -#: canaille/core/templates/users.html:5 canaille/core/templates/users.html:29 -#: canaille/templates/base.html:51 -msgid "Users" -msgstr "" - -#: canaille/core/templates/verify-2fa.html:23 -msgid "One-time password authentication." -msgstr "" - -#: canaille/core/templates/mails/admin.html:5 -#: canaille/core/templates/mails/admin.html:11 -#: canaille/oidc/templates/authorization_list.html:11 -#: canaille/oidc/templates/authorization_view.html:10 -#: canaille/oidc/templates/client_add.html:11 -#: canaille/oidc/templates/client_edit.html:11 -#: canaille/oidc/templates/client_list.html:11 -#: canaille/oidc/templates/token_list.html:11 -#: canaille/oidc/templates/token_view.html:15 -msgid "Emails" -msgstr "" - -#: canaille/core/templates/mails/admin.html:16 -#: canaille/oidc/templates/authorization_list.html:15 -#: canaille/oidc/templates/authorization_view.html:14 -#: canaille/oidc/templates/client_add.html:15 -#: canaille/oidc/templates/client_edit.html:15 -#: canaille/oidc/templates/client_list.html:5 -#: canaille/oidc/templates/client_list.html:15 -#: canaille/oidc/templates/client_list.html:35 -#: canaille/oidc/templates/token_list.html:15 -#: canaille/oidc/templates/token_view.html:19 -msgid "Clients" -msgstr "" - -#: canaille/core/templates/mails/admin.html:20 -#: canaille/oidc/templates/authorization_list.html:19 -#: canaille/oidc/templates/authorization_view.html:18 -#: canaille/oidc/templates/client_add.html:5 -#: canaille/oidc/templates/client_add.html:19 -#: canaille/oidc/templates/client_add.html:34 -#: canaille/oidc/templates/client_edit.html:19 -#: canaille/oidc/templates/client_list.html:19 -#: canaille/oidc/templates/token_list.html:19 -#: canaille/oidc/templates/token_view.html:23 -msgid "Add a client" -msgstr "" - -#: canaille/core/templates/mails/admin.html:24 -#: canaille/oidc/templates/authorization_list.html:23 -#: canaille/oidc/templates/authorization_view.html:22 -#: canaille/oidc/templates/client_add.html:23 -#: canaille/oidc/templates/client_edit.html:23 -#: canaille/oidc/templates/client_list.html:23 -#: canaille/oidc/templates/token_list.html:5 -#: canaille/oidc/templates/token_list.html:23 -#: canaille/oidc/templates/token_list.html:35 -#: canaille/oidc/templates/token_view.html:27 -msgid "Tokens" -msgstr "" - -#: canaille/core/templates/mails/admin.html:28 -#: canaille/oidc/templates/authorization_list.html:5 -#: canaille/oidc/templates/authorization_list.html:27 -#: canaille/oidc/templates/authorization_list.html:35 -#: canaille/oidc/templates/authorization_view.html:26 -#: canaille/oidc/templates/client_add.html:27 -#: canaille/oidc/templates/client_edit.html:27 -#: canaille/oidc/templates/client_list.html:27 -#: canaille/oidc/templates/token_list.html:27 -#: canaille/oidc/templates/token_view.html:31 -msgid "Codes" -msgstr "" - -#: canaille/core/templates/mails/admin.html:37 -msgid "Mail sending test" -msgstr "" - -#: canaille/core/templates/mails/admin.html:44 -msgid "" -"This form will send a dummy email to the address you want. This should be" -" used for testing mail configuration." -msgstr "" - -#: canaille/core/templates/mails/admin.html:62 -msgid "No mail server has been configured. The mail sending test is disabled." -msgstr "" - -#: canaille/core/templates/mails/admin.html:73 -msgid "Email preview" -msgstr "" - -#: canaille/core/templates/mails/admin.html:85 -#: canaille/core/templates/mails/test.html:19 -#: canaille/core/templates/mails/test.txt:1 -msgid "Connectivity test" -msgstr "" - -#: canaille/core/templates/mails/admin.html:97 -#: canaille/core/templates/mails/firstlogin.html:19 -#: canaille/core/templates/mails/reset.txt:1 -msgid "Password initialization" -msgstr "" - -#: canaille/core/templates/mails/admin.html:121 -#: canaille/core/templates/mails/email-confirmation.txt:1 -msgid "Email verification" -msgstr "" - -#: canaille/core/templates/mails/admin.html:133 -msgid "Invitation" -msgstr "" - -#: canaille/core/templates/mails/admin.html:145 -#: canaille/core/templates/mails/registration.html:19 -#: canaille/core/templates/mails/registration.txt:1 -msgid "Registration" -msgstr "" - -#: canaille/core/templates/mails/admin.html:158 -#: canaille/core/templates/mails/compromised_password_check_failure.html:19 -#: canaille/core/templates/mails/compromised_password_check_failure.txt:1 -msgid "Compromised password check failure" -msgstr "" - -#: canaille/core/templates/mails/admin.html:171 -msgid "Email one-time password" -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.html:28 -#, python-format -msgid "" -"Our services were unable to verify if the %(user_name)s's password is " -"compromised." -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.html:31 -#: canaille/core/templates/mails/compromised_password_check_failure.txt:5 -#, python-format -msgid "" -"You have to check manually if the new password of the user %(user_name)s " -"is compromised." -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.html:32 -#: canaille/core/templates/mails/compromised_password_check_failure.txt:6 -msgid "Follow this steps :" -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.html:33 -#: canaille/core/templates/mails/compromised_password_check_failure.txt:7 -msgid "1. click on the link above \"Check if password is compromised\"." -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.html:40 -#: canaille/core/templates/mails/compromised_password_check_failure.html:75 -#: canaille/core/templates/mails/compromised_password_check_failure.txt:13 -msgid "Check if password is compromised" -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.html:47 -#: canaille/core/templates/mails/compromised_password_check_failure.txt:8 -#, python-format -msgid "" -"2. in the page that will open, search the following hashed password in " -"the page : %(hashed_password)s" -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.html:48 -#: canaille/core/templates/mails/compromised_password_check_failure.txt:9 -msgid "3. if the password is in the list :" -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.html:49 -msgid "3.1. open this link and reset user's password." -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.html:57 -#, python-format -msgid "Reset %(user_name)s's password" -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.html:64 -#, python-format -msgid "3.2. send an email to the user to explain the situation : %(user_email)s" -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.txt:3 -#, python-format -msgid "" -"Our services were unable to verify if the " -"%(site_url)s/profile/%(user_name)s/settings's password is compromised." -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.txt:10 -#, python-format -msgid "" -"3.1. open this link %(site_url)s/profile/%(user_name)s/settings and reset" -" user's password." -msgstr "" - -#: canaille/core/templates/mails/compromised_password_check_failure.txt:11 -#, python-format -msgid "3.2. send an email to the user to explain the situation : %(user_email)s." -msgstr "" - -#: canaille/core/templates/mails/email-confirmation.html:19 -msgid "Email address confirmation" -msgstr "" - -#: canaille/core/templates/mails/email-confirmation.html:27 -#, python-format -msgid "" -"You added an email address to your account on %(site_name)s. Please click" -" on the \"Validate email address\" button below to confirm your email " -"address." -msgstr "" - -#: canaille/core/templates/mails/email-confirmation.html:39 -msgid "Validate email address" -msgstr "" - -#: canaille/core/templates/mails/email-confirmation.txt:3 -#, python-format -msgid "" -"You added an email address to your account on %(site_name)s. Please click" -" on the link below to confirm your email address." -msgstr "" - -#: canaille/core/templates/mails/email-confirmation.txt:8 -msgid "Verification link" -msgstr "" - -#: canaille/core/templates/mails/email_otp.html:19 -#: canaille/core/templates/mails/email_otp.txt:1 -msgid "One-time password authentication" -msgstr "" - -#: canaille/core/templates/mails/email_otp.html:27 -#, python-format -msgid "" -"Someone, probably you, asked for a one-time password at %(site_name)s. If" -" you did not ask for this email, please ignore it. If you did, please " -"enter the one-time password below at %(site_name)s to complete your " -"authentication." -msgstr "" - -#: canaille/core/templates/mails/email_otp.html:30 -msgid "Your one-time password" -msgstr "" - -#: canaille/core/templates/mails/email_otp.txt:3 -#, python-format -msgid "" -"Please enter the one-time password below at %(site_name)s to complete " -"your authentication." -msgstr "" - -#: canaille/core/templates/mails/firstlogin.html:27 -#, python-format -msgid "" -"In order to finalize your account configuration at %(site_name)s, we need" -" to setup your password. Please click on the \"Initialize password\" " -"button below and follow the instructions." -msgstr "" - -#: canaille/core/templates/mails/firstlogin.html:38 -#: canaille/core/templates/mails/firstlogin.txt:1 -#: canaille/core/templates/mails/firstlogin.txt:5 -#: canaille/core/templates/mails/invitation.txt:5 -#: canaille/core/templates/mails/reset.txt:5 -msgid "Initialize password" -msgstr "" - -#: canaille/core/templates/mails/firstlogin.txt:3 -#: canaille/core/templates/mails/reset.txt:3 -#, python-format -msgid "" -"In order to finalize your account configuration at %(site_name)s, we need" -" to setup your password. Please click on the link below and follow the " -"instructions." -msgstr "" - -#: canaille/core/templates/mails/invitation.html:19 -#: canaille/core/templates/mails/invitation.txt:1 -msgid "Account creation" -msgstr "" - -#: canaille/core/templates/mails/invitation.html:27 -#, python-format -msgid "" -"You have been invited to create an account at %(site_name)s. To proceed " -"you can click on the \"Create my account\" button below and follow the " -"instructions." -msgstr "" - -#: canaille/core/templates/mails/invitation.html:38 -msgid "Create my account" -msgstr "" - -#: canaille/core/templates/mails/invitation.txt:3 -#, python-format -msgid "" -"You have been invited to create an account at %(site_name)s. To proceed " -"you can click on the link below and follow the instructions." -msgstr "" - -#: canaille/core/templates/mails/registration.html:27 -#, python-format -msgid "" -"You've started the registration process at %(site_name)s. To proceed you " -"can click on the \"Continue account creation\" button below and follow " -"the instructions." -msgstr "" - -#: canaille/core/templates/mails/registration.html:38 -msgid "Continue account creation" -msgstr "" - -#: canaille/core/templates/mails/registration.txt:3 -#, python-format -msgid "" -"You've started the registration process at %(site_name)s. To proceed you " -"can click on the link below and follow the instructions." -msgstr "" - -#: canaille/core/templates/mails/reset.html:27 -#, python-format -msgid "" -"Someone, probably you, asked for a password reinitialization link at " -"%(site_name)s. If you did not ask for this email, please ignore it. If " -"you need to reset your password, please click on the \"Reset password\" " -"button below and follow the instructions." -msgstr "" - -#: canaille/core/templates/mails/reset.html:38 -msgid "Reset password" -msgstr "" - -#: canaille/core/templates/mails/test.html:27 -#: canaille/core/templates/mails/test.txt:3 -#, python-format -msgid "" -"This email is a test to check that you receive communications from " -"%(site_name)s." -msgstr "" - -#: canaille/core/templates/modals/delete-account.html:9 -msgid "Account deletion" -msgstr "" - -#: canaille/core/templates/modals/delete-account.html:14 -#, python-format -msgid "" -"Are you sure you want to delete the account of %(user_name)s? This action" -" is unrevokable and all the data about this user will be removed." -msgstr "" - -#: canaille/core/templates/modals/delete-account.html:18 -msgid "" -"Are you sure you want to delete your account? This action is unrevokable " -"and all your data will be removed forever." -msgstr "" - -#: canaille/core/templates/modals/delete-account.html:26 -#: canaille/core/templates/modals/delete-group.html:22 -#: canaille/core/templates/modals/lock-account.html:26 -#: canaille/core/templates/modals/remove-group-member.html:22 -#: canaille/core/templates/modals/reset-otp.html:26 -#: canaille/oidc/templates/modals/delete-client.html:20 -#: canaille/oidc/templates/modals/revoke-token.html:20 -msgid "Cancel" -msgstr "" - -#: canaille/core/templates/modals/delete-account.html:27 -#: canaille/core/templates/modals/delete-group.html:25 -#: canaille/oidc/templates/modals/delete-client.html:21 -msgid "Delete" -msgstr "" - -#: canaille/core/templates/modals/delete-group.html:9 -msgid "Group deletion" -msgstr "" - -#: canaille/core/templates/modals/delete-group.html:13 -#, python-format -msgid "" -"Are you sure you want to delete the group \"%(group_name)s\"? This action" -" is unrevokable and all the data about this group will be removed." -msgstr "" - -#: canaille/core/templates/modals/lock-account.html:9 -msgid "Account locking" -msgstr "" - -#: canaille/core/templates/modals/lock-account.html:14 -#, python-format -msgid "" -"Are you sure you want to lock the account of %(user_name)s ? The user " -"won't be able to login until their account is unlocked." -msgstr "" - -#: canaille/core/templates/modals/lock-account.html:18 -msgid "" -"Are you sure you want to lock your account? You won't be able to login " -"until your account is unlocked." -msgstr "" - -#: canaille/core/templates/modals/lock-account.html:27 -msgid "Lock" -msgstr "" - -#: canaille/core/templates/modals/remove-group-member.html:10 -msgid "Group member deletion" -msgstr "" - -#: canaille/core/templates/modals/remove-group-member.html:14 -#, python-format -msgid "" -"Are you sure you want to remove %(user_name)s from the group " -"\"%(group_name)s\"?" -msgstr "" - -#: canaille/core/templates/modals/remove-group-member.html:25 -#: canaille/core/templates/partial/group-members.html:64 -#: canaille/core/templates/partial/group-members.html:69 -msgid "Remove" -msgstr "" - -#: canaille/core/templates/modals/reset-otp.html:9 -msgid "One-time password authentication reset" -msgstr "" - -#: canaille/core/templates/modals/reset-otp.html:14 -#, python-format -msgid "" -"Are you sure you want to reset one-time password (OTP) authentication for" -" %(user_name)s ? The user will have to perform OTP setup again at next " -"login." -msgstr "" - -#: canaille/core/templates/modals/reset-otp.html:18 -msgid "" -"Are you sure you want to reset one-time password (OTP) authentication? " -"You will have to perform OTP setup again at next login." -msgstr "" - -#: canaille/core/templates/partial/group-members.html:16 -msgid "Actions" -msgstr "" - -#: canaille/core/templates/partial/group-members.html:27 -#: canaille/core/templates/partial/users.html:29 -msgid "This account is locked" -msgstr "" - -#: canaille/core/templates/partial/group-members.html:68 -msgid "Groups must have at least one member" -msgstr "" - -#: canaille/core/templates/partial/group-members.html:83 -#: canaille/core/templates/partial/groups.html:31 -#: canaille/core/templates/partial/users.html:73 -#: canaille/oidc/templates/partial/authorization_list.html:31 -#: canaille/oidc/templates/partial/client_list.html:35 -#: canaille/oidc/templates/partial/token_list.html:45 -msgid "No item matches your request" -msgstr "" - -#: canaille/core/templates/partial/group-members.html:85 -#: canaille/core/templates/partial/groups.html:33 -#: canaille/core/templates/partial/users.html:75 -#: canaille/oidc/templates/partial/authorization_list.html:33 -#: canaille/oidc/templates/partial/client_list.html:37 -#: canaille/oidc/templates/partial/token_list.html:47 -msgid "Maybe try with different criteria?" -msgstr "" - -#: canaille/core/templates/partial/group-members.html:88 -#: canaille/core/templates/partial/groups.html:36 -#: canaille/core/templates/partial/users.html:78 -#: canaille/oidc/templates/partial/authorization_list.html:36 -#: canaille/oidc/templates/partial/client_list.html:40 -#: canaille/oidc/templates/partial/token_list.html:50 -#: canaille/oidc/templates/preconsent_list.html:91 -msgid "There is nothing here" -msgstr "" - -#: canaille/core/templates/partial/groups.html:8 -msgid "Number of members" -msgstr "" - -#: canaille/core/templates/sms/sms_otp.txt:1 -#, python-format -msgid "Here is the one-time password you requested for %(website_name)s: %(otp)s" -msgstr "" - #: canaille/oidc/utils.py:6 msgid "Info about yourself, such as your name." msgstr "" @@ -1626,29 +682,29 @@ msgstr "" msgid "The client has been deleted." msgstr "" -#: canaille/oidc/endpoints/consents.py:75 -#: canaille/oidc/endpoints/consents.py:114 +#: canaille/oidc/endpoints/consents.py:76 +#: canaille/oidc/endpoints/consents.py:115 msgid "Could not revoke this access" msgstr "" -#: canaille/oidc/endpoints/consents.py:78 +#: canaille/oidc/endpoints/consents.py:79 msgid "The access is already revoked" msgstr "" -#: canaille/oidc/endpoints/consents.py:86 -#: canaille/oidc/endpoints/consents.py:129 +#: canaille/oidc/endpoints/consents.py:87 +#: canaille/oidc/endpoints/consents.py:130 msgid "The access has been revoked" msgstr "" -#: canaille/oidc/endpoints/consents.py:95 +#: canaille/oidc/endpoints/consents.py:96 msgid "Could not restore this access" msgstr "" -#: canaille/oidc/endpoints/consents.py:98 +#: canaille/oidc/endpoints/consents.py:99 msgid "The access is not revoked" msgstr "" -#: canaille/oidc/endpoints/consents.py:105 +#: canaille/oidc/endpoints/consents.py:106 msgid "The access has been restored" msgstr "" @@ -1673,7 +729,7 @@ msgid "Grant types" msgstr "" #: canaille/oidc/endpoints/forms.py:90 -#: canaille/oidc/templates/token_view.html:67 +#: canaille/templates/oidc/token_view.html:74 msgid "Scope" msgstr "" @@ -1721,11 +777,11 @@ msgstr "" msgid "Pre-consent" msgstr "" -#: canaille/oidc/endpoints/oauth.py:377 +#: canaille/oidc/endpoints/oauth.py:379 msgid "You have been disconnected" msgstr "" -#: canaille/oidc/endpoints/oauth.py:394 +#: canaille/oidc/endpoints/oauth.py:396 msgid "You have not been disconnected" msgstr "" @@ -1733,339 +789,1287 @@ msgstr "" msgid "The token has successfully been revoked." msgstr "" -#: canaille/oidc/templates/authorization_view.html:4 -#: canaille/oidc/templates/authorization_view.html:33 -msgid "View an authorization" -msgstr "" - -#: canaille/oidc/templates/authorize.html:11 -#, python-format -msgid "The application %(name)s is requesting access to:" -msgstr "" - -#: canaille/oidc/templates/authorize.html:35 -#, python-format -msgid "You are logged in as %(name)s" -msgstr "" - -#: canaille/oidc/templates/authorize.html:42 -msgid "Deny" -msgstr "" - -#: canaille/oidc/templates/authorize.html:45 -msgid "Switch user" -msgstr "" - -#: canaille/oidc/templates/authorize.html:48 -msgid "Accept" -msgstr "" - -#: canaille/oidc/templates/client_add.html:37 -msgid "Create" -msgstr "" - -#: canaille/oidc/templates/client_edit.html:5 -#: canaille/oidc/templates/client_edit.html:34 -msgid "Edit a client" -msgstr "" - -#: canaille/oidc/templates/client_edit.html:40 -msgid "ID" -msgstr "" - -#: canaille/oidc/templates/client_edit.html:43 -#: canaille/oidc/templates/client_edit.html:52 -#: canaille/oidc/templates/client_edit.html:61 -#: canaille/templates/macro/form.html:67 -msgid "This field is not editable" -msgstr "" - -#: canaille/oidc/templates/client_edit.html:49 -msgid "Secret" -msgstr "" - -#: canaille/oidc/templates/client_edit.html:58 -msgid "Issued at" -msgstr "" - -#: canaille/oidc/templates/client_edit.html:75 -msgid "New token" -msgstr "" - -#: canaille/oidc/templates/client_edit.html:78 -msgid "Delete the client" -msgstr "" - -#: canaille/oidc/templates/consent_list.html:4 -#: canaille/oidc/templates/consent_list.html:10 -#: canaille/oidc/templates/consent_list.html:24 -#: canaille/oidc/templates/preconsent_list.html:4 -#: canaille/oidc/templates/preconsent_list.html:10 -msgid "My consents" -msgstr "" - -#: canaille/oidc/templates/consent_list.html:15 -#: canaille/oidc/templates/preconsent_list.html:15 -#: canaille/oidc/templates/preconsent_list.html:24 -msgid "Pre-authorized applications" -msgstr "" - -#: canaille/oidc/templates/consent_list.html:27 -msgid "Consult and revoke the authorization you gave to websites." -msgstr "" - -#: canaille/oidc/templates/consent_list.html:45 -msgid "From:" -msgstr "" - -#: canaille/oidc/templates/consent_list.html:48 -msgid "Revoked:" -msgstr "" - -#: canaille/oidc/templates/consent_list.html:53 -msgid "Had access to:" -msgstr "" - -#: canaille/oidc/templates/consent_list.html:55 -#: canaille/oidc/templates/preconsent_list.html:46 -msgid "Has access to:" -msgstr "" - -#: canaille/oidc/templates/consent_list.html:78 -#: canaille/oidc/templates/preconsent_list.html:68 -msgid "Policy" -msgstr "" - -#: canaille/oidc/templates/consent_list.html:86 -#: canaille/oidc/templates/preconsent_list.html:76 -msgid "Terms of service" -msgstr "" - -#: canaille/oidc/templates/consent_list.html:93 -msgid "Restore access" -msgstr "" - -#: canaille/oidc/templates/consent_list.html:98 -#: canaille/oidc/templates/preconsent_list.html:82 -msgid "Revoke access" -msgstr "" - -#: canaille/oidc/templates/consent_list.html:108 -msgid "You did not authorize applications yet." -msgstr "" - -#: canaille/oidc/templates/logout.html:9 canaille/templates/base.html:69 -msgid "Log out" -msgstr "" - -#: canaille/oidc/templates/logout.html:10 -msgid "Do you want to log out?" -msgstr "" - -#: canaille/oidc/templates/logout.html:14 -#, python-format -msgid "You are currently logged in as %(username)s." -msgstr "" - -#: canaille/oidc/templates/logout.html:16 -#, python-format -msgid "The application %(client_name)s wants to disconnect your account." -msgstr "" - -#: canaille/oidc/templates/logout.html:27 -msgid "Stay logged" -msgstr "" - -#: canaille/oidc/templates/logout.html:30 -msgid "Logout" -msgstr "" - -#: canaille/oidc/templates/preconsent_list.html:27 -msgid "Those applications automatically have authorizations to access you data." -msgstr "" - -#: canaille/oidc/templates/token_view.html:5 -#: canaille/oidc/templates/token_view.html:38 -msgid "Token details" -msgstr "" - -#: canaille/oidc/templates/partial/authorization_list.html:6 -#: canaille/oidc/templates/partial/token_list.html:6 -#: canaille/oidc/templates/token_view.html:45 -msgid "Client" -msgstr "" - -#: canaille/oidc/templates/partial/authorization_list.html:7 -#: canaille/oidc/templates/partial/token_list.html:7 -#: canaille/oidc/templates/token_view.html:53 -msgid "Subject" -msgstr "" - -#: canaille/oidc/templates/token_view.html:77 -msgid "Audience" -msgstr "" - -#: canaille/oidc/templates/token_view.html:91 -msgid "Issue date" -msgstr "" - -#: canaille/oidc/templates/token_view.html:95 -msgid "Expiration date" -msgstr "" - -#: canaille/oidc/templates/token_view.html:100 -msgid "This token has expired." -msgstr "" - -#: canaille/oidc/templates/token_view.html:106 -msgid "Revokation date" -msgstr "" - -#: canaille/oidc/templates/token_view.html:112 -msgid "Revoke token" -msgstr "" - -#: canaille/oidc/templates/token_view.html:115 -msgid "This token has not been revoked" -msgstr "" - -#: canaille/oidc/templates/token_view.html:121 -msgid "Token type" -msgstr "" - -#: canaille/oidc/templates/token_view.html:125 -msgid "Access token" -msgstr "" - -#: canaille/oidc/templates/token_view.html:136 -msgid "Refresh token" -msgstr "" - -#: canaille/oidc/templates/token_view.html:138 -msgid "No refresh token" -msgstr "" - -#: canaille/oidc/templates/modals/delete-client.html:9 -msgid "Client deletion" -msgstr "" - -#: canaille/oidc/templates/modals/delete-client.html:13 -#, python-format -msgid "" -"Are you sure you want to delete the client %(client_name)s? This action " -"is unrevokable and all the data about this client will be removed." -msgstr "" - -#: canaille/oidc/templates/modals/revoke-token.html:9 -msgid "Token revokation" -msgstr "" - -#: canaille/oidc/templates/modals/revoke-token.html:13 -msgid "Are you sure you want to revoke this token? This action is unrevokable." -msgstr "" - -#: canaille/oidc/templates/modals/revoke-token.html:21 -msgid "Revoke" -msgstr "" - -#: canaille/oidc/templates/partial/authorization_list.html:5 -msgid "Code" -msgstr "" - -#: canaille/oidc/templates/partial/authorization_list.html:8 -#: canaille/oidc/templates/partial/client_list.html:8 -#: canaille/oidc/templates/partial/token_list.html:8 -msgid "Created" -msgstr "" - -#: canaille/oidc/templates/partial/client_list.html:7 -msgid "URL" -msgstr "" - -#: canaille/oidc/templates/partial/token_list.html:5 -msgid "Token" -msgstr "" - -#: canaille/templates/base.html:6 +#: canaille/templates/base.html:28 msgid "Authorization interface" msgstr "" -#: canaille/templates/base.html:37 +#: canaille/templates/base.html:59 msgid "Profile" msgstr "" -#: canaille/templates/base.html:44 +#: canaille/templates/base.html:66 msgid "Consents" msgstr "" -#: canaille/templates/base.html:64 +#: canaille/templates/base.html:73 canaille/templates/core/users.html:12 +#: canaille/templates/core/users.html:36 +msgid "Users" +msgstr "" + +#: canaille/templates/base.html:86 msgid "Admin" msgstr "" -#: canaille/templates/error.html:19 +#: canaille/templates/base.html:91 canaille/templates/oidc/logout.html:18 +msgid "Log out" +msgstr "" + +#: canaille/templates/base.html:96 canaille/templates/core/password.html:43 +msgid "Sign in" +msgstr "" + +#: canaille/templates/base.html:121 canaille/templates/core/about.html:13 +#: canaille/templates/core/about.html:24 +msgid "About Canaille" +msgstr "" + +#: canaille/templates/error.html:29 msgid "Bad request" msgstr "" -#: canaille/templates/error.html:21 +#: canaille/templates/error.html:31 msgid "Unauthorized" msgstr "" -#: canaille/templates/error.html:23 +#: canaille/templates/error.html:33 msgid "Page not found" msgstr "" -#: canaille/templates/error.html:25 +#: canaille/templates/error.html:35 msgid "Technical problem" msgstr "" -#: canaille/templates/error.html:31 +#: canaille/templates/error.html:41 msgid "The request you made is invalid" msgstr "" -#: canaille/templates/error.html:33 +#: canaille/templates/error.html:43 msgid "You do not have the authorizations to access this page" msgstr "" -#: canaille/templates/error.html:35 +#: canaille/templates/error.html:45 msgid "The page you are looking for does not exist" msgstr "" -#: canaille/templates/error.html:37 +#: canaille/templates/error.html:47 msgid "Please contact your administrator" msgstr "" +#: canaille/templates/core/about.html:26 +msgid "Free and open-source identity provider." +msgstr "" + +#: canaille/templates/core/about.html:29 +#, python-format +msgid "Version %(version)s" +msgstr "" + +#: canaille/templates/core/about.html:30 +msgid "Homepage" +msgstr "" + +#: canaille/templates/core/about.html:31 +msgid "Documentation" +msgstr "" + +#: canaille/templates/core/about.html:32 +msgid "Source code" +msgstr "" + +#: canaille/templates/core/firstlogin.html:16 +msgid "First login" +msgstr "" + +#: canaille/templates/core/firstlogin.html:23 +msgid "" +"It seems this is the first time you are logging here. In order to " +"finalize your account configuration, you need to set a password to your " +"account. We will send you an email containing a link that will allow you " +"to set a password. Please click on the \"Send the initialization email\" " +"button below to send the email." +msgstr "" + +#: canaille/templates/core/firstlogin.html:38 +#: canaille/templates/core/forgotten-password.html:47 +#: canaille/templates/core/join.html:47 +#: canaille/templates/core/profile_add.html:199 +msgid "Login page" +msgstr "" + +#: canaille/templates/core/firstlogin.html:39 +msgid "Send the initialization email" +msgstr "" + +#: canaille/templates/core/forgotten-password.html:9 +#: canaille/templates/core/forgotten-password.html:20 +#: canaille/templates/core/join.html:49 canaille/templates/core/login.html:50 +#: canaille/templates/core/password.html:41 +msgid "Forgotten password" +msgstr "" + +#: canaille/templates/core/forgotten-password.html:27 +msgid "" +"After this form is sent, if the email address or the login you provided " +"exists, you will receive an email containing a link that will allow you " +"to reset your password." +msgstr "" + +#: canaille/templates/core/forgotten-password.html:45 +#: canaille/templates/core/login.html:47 +msgid "Create an account" +msgstr "" + +#: canaille/templates/core/forgotten-password.html:50 +msgid "Send again" +msgstr "" + +#: canaille/templates/core/forgotten-password.html:52 +#: canaille/templates/core/mails/admin.html:54 +msgid "Send" +msgstr "" + +#: canaille/templates/core/group.html:16 canaille/templates/core/group.html:38 +msgid "Group creation" +msgstr "" + +#: canaille/templates/core/group.html:18 canaille/templates/core/group.html:40 +msgid "Group edition" +msgstr "" + +#: canaille/templates/core/group.html:25 canaille/templates/core/groups.html:16 +#: canaille/templates/core/invite.html:22 +#: canaille/templates/core/profile_add.html:24 +#: canaille/templates/core/users.html:18 +msgid "View" +msgstr "" + +#: canaille/templates/core/group.html:29 canaille/templates/core/groups.html:20 +#: canaille/templates/core/invite.html:26 +#: canaille/templates/core/profile_add.html:28 +#: canaille/templates/core/users.html:22 +msgid "Add" +msgstr "" + +#: canaille/templates/core/group.html:46 +msgid "Create a new group" +msgstr "" + +#: canaille/templates/core/group.html:48 +msgid "Edit information about a group" +msgstr "" + +#: canaille/templates/core/group.html:59 +msgid "" +"Because group cannot be empty, you will be added to the group. You can " +"remove you later by editing your profile when you will have added other " +"members to the group." +msgstr "" + +#: canaille/templates/core/group.html:67 +msgid "Delete group" +msgstr "" + +#: canaille/templates/core/group.html:72 +msgid "Create group" +msgstr "" + +#: canaille/templates/core/group.html:74 +#: canaille/templates/core/profile_edit.html:176 +#: canaille/templates/core/profile_settings.html:183 +#: canaille/templates/oidc/client_edit.html:90 +msgid "Edit" +msgstr "" + +#: canaille/templates/core/group.html:86 +msgid "Group members" +msgstr "" + +#: canaille/templates/core/invite.html:12 +#: canaille/templates/core/invite.html:104 +msgid "Invite a user" +msgstr "" + +#: canaille/templates/core/invite.html:30 +#: canaille/templates/core/profile_add.html:33 +#: canaille/templates/core/users.html:27 +msgid "Invite" +msgstr "" + +#: canaille/templates/core/invite.html:42 +msgid "Invitation link" +msgstr "" + +#: canaille/templates/core/invite.html:44 +msgid "Invitation sent" +msgstr "" + +#: canaille/templates/core/invite.html:46 +msgid "Invitation not sent" +msgstr "" + +#: canaille/templates/core/invite.html:54 +msgid "" +"Here is the invitation link you can provide to the user you want to " +"invite:" +msgstr "" + +#: canaille/templates/core/invite.html:61 +#, python-format +msgid "This invitation link has been sent to %(email)s" +msgstr "" + +#: canaille/templates/core/invite.html:62 +msgid "" +"If you need to provide this link by other ways than email, you can copy " +"it there:" +msgstr "" + +#: canaille/templates/core/invite.html:69 +#, python-format +msgid "" +"This invitation link could not be sent to %(email)s due to technical " +"issues." +msgstr "" + +#: canaille/templates/core/invite.html:70 +msgid "" +"However you can copy the link there to provide it by other ways than " +"email:" +msgstr "" + +#: canaille/templates/core/invite.html:81 +#: canaille/templates/oidc/token_view.html:137 +#: canaille/templates/oidc/token_view.html:148 +msgid "Copy" +msgstr "" + +#: canaille/templates/core/invite.html:88 +#: canaille/templates/core/invite.html:135 +msgid "Create a user" +msgstr "" + +#: canaille/templates/core/invite.html:92 +msgid "Invite another user" +msgstr "" + +#: canaille/templates/core/invite.html:109 +msgid "" +"After this form is sent, the recipient your indicated will receive an " +"email containing an account creation link." +msgstr "" + +#: canaille/templates/core/invite.html:138 +msgid "Generate a link" +msgstr "" + +#: canaille/templates/core/invite.html:141 +msgid "Send the invitation" +msgstr "" + +#: canaille/templates/core/join.html:14 canaille/templates/core/join.html:25 +#: canaille/templates/core/profile_add.html:14 +#: canaille/templates/core/profile_add.html:42 +#: canaille/templates/core/profile_edit.html:19 +msgid "User creation" +msgstr "" + +#: canaille/templates/core/join.html:27 +#: canaille/templates/core/profile_add.html:45 +msgid "Create a new user account" +msgstr "" + +#: canaille/templates/core/join.html:32 +msgid "" +"Before you can create an account, please enter a valid email address. " +"Then you will receive an email containing a link that will allow you to " +"finish your registration." +msgstr "" + +#: canaille/templates/core/join.html:52 +#: canaille/templates/core/profile_add.html:202 +msgid "Submit" +msgstr "" + +#: canaille/templates/core/login.html:28 +#, python-format +msgid "Sign in at %(website)s" +msgstr "" + +#: canaille/templates/core/login.html:30 +msgid "Manage your information and your authorizations" +msgstr "" + +#: canaille/templates/core/login.html:52 +#: canaille/templates/core/mails/registration.txt:5 +#: canaille/templates/core/setup-2fa.html:64 +msgid "Continue" +msgstr "" + +#: canaille/templates/core/password.html:21 +#: canaille/templates/core/setup-2fa.html:32 +#: canaille/templates/core/verify-2fa.html:32 +#, python-format +msgid "Sign in as %(username)s" +msgstr "" + +#: canaille/templates/core/password.html:23 +msgid "Please enter your password for this account." +msgstr "" + +#: canaille/templates/core/password.html:39 +#: canaille/templates/core/verify-2fa.html:50 +#, python-format +msgid "I am not %(username)s" +msgstr "" + +#: canaille/templates/core/profile_add.html:50 +#: canaille/templates/core/profile_edit.html:34 +#: canaille/templates/core/profile_settings.html:26 +msgid "Personal information" +msgstr "" + +#: canaille/templates/core/profile_add.html:61 +#: canaille/templates/core/profile_add.html:72 +#: canaille/templates/core/profile_edit.html:73 +#: canaille/templates/core/profile_edit.html:84 +msgid "Click to upload a photo" +msgstr "" + +#: canaille/templates/core/profile_add.html:160 +#: canaille/templates/core/profile_edit.html:38 +#: canaille/templates/core/profile_settings.html:30 +#: canaille/templates/core/profile_settings.html:51 +msgid "Account settings" +msgstr "" + +#: canaille/templates/core/profile_add.html:183 +msgid "User password is not mandatory" +msgstr "" + +#: canaille/templates/core/profile_add.html:187 +msgid "" +"The user will not be able to authenticate unless the password is set, but" +" they will be able to ask for a password initialization mail." +msgstr "" + +#: canaille/templates/core/profile_add.html:189 +msgid "The user will not be able to authenticate unless the password is set." +msgstr "" + +#: canaille/templates/core/profile_edit.html:21 +#: canaille/templates/core/profile_edit.html:47 +#: canaille/templates/core/profile_settings.html:17 +msgid "My profile" +msgstr "" + +#: canaille/templates/core/profile_edit.html:23 +#: canaille/templates/core/profile_edit.html:49 +#: canaille/templates/core/profile_settings.html:19 +msgid "User profile edition" +msgstr "" + +#: canaille/templates/core/profile_edit.html:55 +msgid "Edit your personal information" +msgstr "" + +#: canaille/templates/core/profile_edit.html:57 +msgid "Edit information about a user" +msgstr "" + +#: canaille/templates/core/profile_edit.html:186 +msgid "My email addresses" +msgstr "" + +#: canaille/templates/core/profile_edit.html:207 +msgid "Remove this email address" +msgstr "" + +#: canaille/templates/core/profile_edit.html:228 +msgid "Send a verification email to validate this address." +msgstr "" + +#: canaille/templates/core/profile_edit.html:233 +#: canaille/templates/core/verify-2fa.html:51 +msgid "Verify" +msgstr "" + +#: canaille/templates/core/partial/profile_field.html:31 +#: canaille/templates/core/profile_settings.html:41 +msgid "This user cannot edit this field" +msgstr "" + +#: canaille/templates/core/partial/profile_field.html:40 +#: canaille/templates/core/profile_settings.html:43 +msgid "This user cannot see this field" +msgstr "" + +#: canaille/templates/core/profile_settings.html:54 +#, python-format +msgid "Created on %(creation_datetime)s" +msgstr "" + +#: canaille/templates/core/profile_settings.html:78 +msgid "" +"You can change your password here by typing it once it each fields below." +" The more a password is long, the more it is strong. A good password is a" +" long password that you will remember, not a complicated password that " +"your use for all your accounts." +msgstr "" + +#: canaille/templates/core/profile_settings.html:102 +#: canaille/templates/core/profile_settings.html:124 +msgid "Send mail" +msgstr "" + +#: canaille/templates/core/profile_settings.html:106 +msgid "This user does not have a password yet" +msgstr "" + +#: canaille/templates/core/profile_settings.html:109 +msgid "You can solve this by:" +msgstr "" + +#: canaille/templates/core/profile_settings.html:111 +msgid "setting a password using this form;" +msgstr "" + +#: canaille/templates/core/profile_settings.html:112 +msgid "sending the user a password initialization mail, by clicking this button;" +msgstr "" + +#: canaille/templates/core/profile_settings.html:113 +msgid "" +"or simply waiting for the user to sign-in a first time, and then receive " +"a password initialization mail." +msgstr "" + +#: canaille/templates/core/profile_settings.html:116 +msgid "The user will not be able to authenticate unless the password is set" +msgstr "" + +#: canaille/templates/core/mails/admin.html:109 +#: canaille/templates/core/mails/reset.html:19 +#: canaille/templates/core/profile_settings.html:127 +#: canaille/templates/core/reset-password.html:23 +#: canaille/templates/core/reset-password.html:29 +msgid "Password reset" +msgstr "" + +#: canaille/templates/core/profile_settings.html:129 +msgid "" +"If the user has forgotten his password, you can send him a password reset" +" email by clicking this button." +msgstr "" + +#: canaille/templates/core/profile_settings.html:138 +msgid "Unlock" +msgstr "" + +#: canaille/templates/core/profile_settings.html:141 +msgid "This user account is locked" +msgstr "" + +#: canaille/templates/core/profile_settings.html:144 +msgid "The user won't be able to connect until their account is unlocked." +msgstr "" + +#: canaille/templates/core/modals/reset-otp.html:27 +#: canaille/templates/core/profile_settings.html:156 +msgid "Reset one-time password authentication" +msgstr "" + +#: canaille/templates/core/profile_settings.html:162 +msgid "Lock the account" +msgstr "" + +#: canaille/templates/core/profile_settings.html:169 +msgid "Delete the user" +msgstr "" + +#: canaille/templates/core/profile_settings.html:171 +msgid "Delete my account" +msgstr "" + +#: canaille/templates/core/profile_settings.html:178 +msgid "Impersonate" +msgstr "" + +#: canaille/templates/core/setup-2fa.html:34 +msgid "Set up multi-factor authentication." +msgstr "" + +#: canaille/templates/core/verify-2fa.html:34 +msgid "One-time password authentication." +msgstr "" + +#: canaille/templates/core/mails/admin.html:5 +#: canaille/templates/core/mails/admin.html:11 +#: canaille/templates/oidc/authorization_list.html:18 +#: canaille/templates/oidc/authorization_view.html:17 +#: canaille/templates/oidc/client_add.html:18 +#: canaille/templates/oidc/client_edit.html:20 +#: canaille/templates/oidc/client_list.html:18 +#: canaille/templates/oidc/token_list.html:18 +#: canaille/templates/oidc/token_view.html:22 +msgid "Emails" +msgstr "" + +#: canaille/templates/core/mails/admin.html:16 +#: canaille/templates/oidc/authorization_list.html:22 +#: canaille/templates/oidc/authorization_view.html:21 +#: canaille/templates/oidc/client_add.html:22 +#: canaille/templates/oidc/client_edit.html:24 +#: canaille/templates/oidc/client_list.html:12 +#: canaille/templates/oidc/client_list.html:22 +#: canaille/templates/oidc/client_list.html:42 +#: canaille/templates/oidc/token_list.html:22 +#: canaille/templates/oidc/token_view.html:26 +msgid "Clients" +msgstr "" + +#: canaille/templates/core/mails/admin.html:20 +#: canaille/templates/oidc/authorization_list.html:26 +#: canaille/templates/oidc/authorization_view.html:25 +#: canaille/templates/oidc/client_add.html:12 +#: canaille/templates/oidc/client_add.html:26 +#: canaille/templates/oidc/client_add.html:41 +#: canaille/templates/oidc/client_edit.html:28 +#: canaille/templates/oidc/client_list.html:26 +#: canaille/templates/oidc/token_list.html:26 +#: canaille/templates/oidc/token_view.html:30 +msgid "Add a client" +msgstr "" + +#: canaille/templates/core/mails/admin.html:24 +#: canaille/templates/oidc/authorization_list.html:30 +#: canaille/templates/oidc/authorization_view.html:29 +#: canaille/templates/oidc/client_add.html:30 +#: canaille/templates/oidc/client_edit.html:32 +#: canaille/templates/oidc/client_list.html:30 +#: canaille/templates/oidc/token_list.html:12 +#: canaille/templates/oidc/token_list.html:30 +#: canaille/templates/oidc/token_list.html:42 +#: canaille/templates/oidc/token_view.html:34 +msgid "Tokens" +msgstr "" + +#: canaille/templates/core/mails/admin.html:28 +#: canaille/templates/oidc/authorization_list.html:12 +#: canaille/templates/oidc/authorization_list.html:34 +#: canaille/templates/oidc/authorization_list.html:42 +#: canaille/templates/oidc/authorization_view.html:33 +#: canaille/templates/oidc/client_add.html:34 +#: canaille/templates/oidc/client_edit.html:36 +#: canaille/templates/oidc/client_list.html:34 +#: canaille/templates/oidc/token_list.html:34 +#: canaille/templates/oidc/token_view.html:38 +msgid "Codes" +msgstr "" + +#: canaille/templates/core/mails/admin.html:37 +msgid "Mail sending test" +msgstr "" + +#: canaille/templates/core/mails/admin.html:44 +msgid "" +"This form will send a dummy email to the address you want. This should be" +" used for testing mail configuration." +msgstr "" + +#: canaille/templates/core/mails/admin.html:62 +msgid "No mail server has been configured. The mail sending test is disabled." +msgstr "" + +#: canaille/templates/core/mails/admin.html:73 +msgid "Email preview" +msgstr "" + +#: canaille/templates/core/mails/admin.html:85 +#: canaille/templates/core/mails/test.html:19 +#: canaille/templates/core/mails/test.txt:1 +msgid "Connectivity test" +msgstr "" + +#: canaille/templates/core/mails/admin.html:97 +#: canaille/templates/core/mails/firstlogin.html:19 +#: canaille/templates/core/mails/reset.txt:1 +msgid "Password initialization" +msgstr "" + +#: canaille/templates/core/mails/admin.html:121 +#: canaille/templates/core/mails/email-confirmation.txt:1 +msgid "Email verification" +msgstr "" + +#: canaille/templates/core/mails/admin.html:133 +msgid "Invitation" +msgstr "" + +#: canaille/templates/core/mails/admin.html:145 +#: canaille/templates/core/mails/registration.html:19 +#: canaille/templates/core/mails/registration.txt:1 +msgid "Registration" +msgstr "" + +#: canaille/templates/core/mails/admin.html:158 +#: canaille/templates/core/mails/compromised_password_check_failure.html:19 +#: canaille/templates/core/mails/compromised_password_check_failure.txt:1 +msgid "Compromised password check failure" +msgstr "" + +#: canaille/templates/core/mails/admin.html:171 +msgid "Email one-time password" +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.html:28 +#, python-format +msgid "" +"Our services were unable to verify if the %(user_name)s's password is " +"compromised." +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.html:31 +#: canaille/templates/core/mails/compromised_password_check_failure.txt:5 +#, python-format +msgid "" +"You have to check manually if the new password of the user %(user_name)s " +"is compromised." +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.html:32 +#: canaille/templates/core/mails/compromised_password_check_failure.txt:6 +msgid "Follow this steps :" +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.html:33 +#: canaille/templates/core/mails/compromised_password_check_failure.txt:7 +msgid "1. click on the link above \"Check if password is compromised\"." +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.html:40 +#: canaille/templates/core/mails/compromised_password_check_failure.html:75 +#: canaille/templates/core/mails/compromised_password_check_failure.txt:13 +msgid "Check if password is compromised" +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.html:47 +#: canaille/templates/core/mails/compromised_password_check_failure.txt:8 +#, python-format +msgid "" +"2. in the page that will open, search the following hashed password in " +"the page : %(hashed_password)s" +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.html:48 +#: canaille/templates/core/mails/compromised_password_check_failure.txt:9 +msgid "3. if the password is in the list :" +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.html:49 +msgid "3.1. open this link and reset user's password." +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.html:57 +#, python-format +msgid "Reset %(user_name)s's password" +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.html:64 +#, python-format +msgid "3.2. send an email to the user to explain the situation : %(user_email)s" +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.txt:3 +#, python-format +msgid "" +"Our services were unable to verify if the " +"%(site_url)s/profile/%(user_name)s/settings's password is compromised." +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.txt:10 +#, python-format +msgid "" +"3.1. open this link %(site_url)s/profile/%(user_name)s/settings and reset" +" user's password." +msgstr "" + +#: canaille/templates/core/mails/compromised_password_check_failure.txt:11 +#, python-format +msgid "3.2. send an email to the user to explain the situation : %(user_email)s." +msgstr "" + +#: canaille/templates/core/mails/email-confirmation.html:19 +msgid "Email address confirmation" +msgstr "" + +#: canaille/templates/core/mails/email-confirmation.html:27 +#, python-format +msgid "" +"You added an email address to your account on %(site_name)s. Please click" +" on the \"Validate email address\" button below to confirm your email " +"address." +msgstr "" + +#: canaille/templates/core/mails/email-confirmation.html:39 +msgid "Validate email address" +msgstr "" + +#: canaille/templates/core/mails/email-confirmation.txt:3 +#, python-format +msgid "" +"You added an email address to your account on %(site_name)s. Please click" +" on the link below to confirm your email address." +msgstr "" + +#: canaille/templates/core/mails/email-confirmation.txt:8 +msgid "Verification link" +msgstr "" + +#: canaille/templates/core/mails/email_otp.html:19 +#: canaille/templates/core/mails/email_otp.txt:1 +msgid "One-time password authentication" +msgstr "" + +#: canaille/templates/core/mails/email_otp.html:27 +#, python-format +msgid "" +"Someone, probably you, asked for a one-time password at %(site_name)s. If" +" you did not ask for this email, please ignore it. If you did, please " +"enter the one-time password below at %(site_name)s to complete your " +"authentication." +msgstr "" + +#: canaille/templates/core/mails/email_otp.html:30 +msgid "Your one-time password" +msgstr "" + +#: canaille/templates/core/mails/email_otp.txt:3 +#, python-format +msgid "" +"Please enter the one-time password below at %(site_name)s to complete " +"your authentication." +msgstr "" + +#: canaille/templates/core/mails/firstlogin.html:27 +#, python-format +msgid "" +"In order to finalize your account configuration at %(site_name)s, we need" +" to setup your password. Please click on the \"Initialize password\" " +"button below and follow the instructions." +msgstr "" + +#: canaille/templates/core/mails/firstlogin.html:38 +#: canaille/templates/core/mails/firstlogin.txt:1 +#: canaille/templates/core/mails/firstlogin.txt:5 +#: canaille/templates/core/mails/invitation.txt:5 +#: canaille/templates/core/mails/reset.txt:5 +msgid "Initialize password" +msgstr "" + +#: canaille/templates/core/mails/firstlogin.txt:3 +#: canaille/templates/core/mails/reset.txt:3 +#, python-format +msgid "" +"In order to finalize your account configuration at %(site_name)s, we need" +" to setup your password. Please click on the link below and follow the " +"instructions." +msgstr "" + +#: canaille/templates/core/mails/invitation.html:19 +#: canaille/templates/core/mails/invitation.txt:1 +msgid "Account creation" +msgstr "" + +#: canaille/templates/core/mails/invitation.html:27 +#, python-format +msgid "" +"You have been invited to create an account at %(site_name)s. To proceed " +"you can click on the \"Create my account\" button below and follow the " +"instructions." +msgstr "" + +#: canaille/templates/core/mails/invitation.html:38 +msgid "Create my account" +msgstr "" + +#: canaille/templates/core/mails/invitation.txt:3 +#, python-format +msgid "" +"You have been invited to create an account at %(site_name)s. To proceed " +"you can click on the link below and follow the instructions." +msgstr "" + +#: canaille/templates/core/mails/registration.html:27 +#, python-format +msgid "" +"You've started the registration process at %(site_name)s. To proceed you " +"can click on the \"Continue account creation\" button below and follow " +"the instructions." +msgstr "" + +#: canaille/templates/core/mails/registration.html:38 +msgid "Continue account creation" +msgstr "" + +#: canaille/templates/core/mails/registration.txt:3 +#, python-format +msgid "" +"You've started the registration process at %(site_name)s. To proceed you " +"can click on the link below and follow the instructions." +msgstr "" + +#: canaille/templates/core/mails/reset.html:27 +#, python-format +msgid "" +"Someone, probably you, asked for a password reinitialization link at " +"%(site_name)s. If you did not ask for this email, please ignore it. If " +"you need to reset your password, please click on the \"Reset password\" " +"button below and follow the instructions." +msgstr "" + +#: canaille/templates/core/mails/reset.html:38 +msgid "Reset password" +msgstr "" + +#: canaille/templates/core/mails/test.html:27 +#: canaille/templates/core/mails/test.txt:3 +#, python-format +msgid "" +"This email is a test to check that you receive communications from " +"%(site_name)s." +msgstr "" + +#: canaille/templates/core/modals/delete-account.html:9 +msgid "Account deletion" +msgstr "" + +#: canaille/templates/core/modals/delete-account.html:14 +#, python-format +msgid "" +"Are you sure you want to delete the account of %(user_name)s? This action" +" is unrevokable and all the data about this user will be removed." +msgstr "" + +#: canaille/templates/core/modals/delete-account.html:18 +msgid "" +"Are you sure you want to delete your account? This action is unrevokable " +"and all your data will be removed forever." +msgstr "" + +#: canaille/templates/core/modals/delete-account.html:26 +#: canaille/templates/core/modals/delete-group.html:22 +#: canaille/templates/core/modals/lock-account.html:26 +#: canaille/templates/core/modals/remove-group-member.html:22 +#: canaille/templates/core/modals/reset-otp.html:26 +#: canaille/templates/oidc/modals/delete-client.html:20 +#: canaille/templates/oidc/modals/revoke-token.html:20 +msgid "Cancel" +msgstr "" + +#: canaille/templates/core/modals/delete-account.html:27 +#: canaille/templates/core/modals/delete-group.html:25 +#: canaille/templates/oidc/modals/delete-client.html:21 +msgid "Delete" +msgstr "" + +#: canaille/templates/core/modals/delete-group.html:9 +msgid "Group deletion" +msgstr "" + +#: canaille/templates/core/modals/delete-group.html:13 +#, python-format +msgid "" +"Are you sure you want to delete the group \"%(group_name)s\"? This action" +" is unrevokable and all the data about this group will be removed." +msgstr "" + +#: canaille/templates/core/modals/lock-account.html:9 +msgid "Account locking" +msgstr "" + +#: canaille/templates/core/modals/lock-account.html:14 +#, python-format +msgid "" +"Are you sure you want to lock the account of %(user_name)s ? The user " +"won't be able to login until their account is unlocked." +msgstr "" + +#: canaille/templates/core/modals/lock-account.html:18 +msgid "" +"Are you sure you want to lock your account? You won't be able to login " +"until your account is unlocked." +msgstr "" + +#: canaille/templates/core/modals/lock-account.html:27 +msgid "Lock" +msgstr "" + +#: canaille/templates/core/modals/remove-group-member.html:10 +msgid "Group member deletion" +msgstr "" + +#: canaille/templates/core/modals/remove-group-member.html:14 +#, python-format +msgid "" +"Are you sure you want to remove %(user_name)s from the group " +"\"%(group_name)s\"?" +msgstr "" + +#: canaille/templates/core/modals/remove-group-member.html:25 +#: canaille/templates/core/partial/group-members.html:64 +#: canaille/templates/core/partial/group-members.html:69 +msgid "Remove" +msgstr "" + +#: canaille/templates/core/modals/reset-otp.html:9 +msgid "One-time password authentication reset" +msgstr "" + +#: canaille/templates/core/modals/reset-otp.html:14 +#, python-format +msgid "" +"Are you sure you want to reset one-time password (OTP) authentication for" +" %(user_name)s ? The user will have to perform OTP setup again at next " +"login." +msgstr "" + +#: canaille/templates/core/modals/reset-otp.html:18 +msgid "" +"Are you sure you want to reset one-time password (OTP) authentication? " +"You will have to perform OTP setup again at next login." +msgstr "" + +#: canaille/templates/core/partial/group-members.html:16 +msgid "Actions" +msgstr "" + +#: canaille/templates/core/partial/group-members.html:27 +#: canaille/templates/core/partial/users.html:29 +msgid "This account is locked" +msgstr "" + +#: canaille/templates/core/partial/group-members.html:68 +msgid "Groups must have at least one member" +msgstr "" + +#: canaille/templates/core/partial/group-members.html:83 +#: canaille/templates/core/partial/groups.html:31 +#: canaille/templates/core/partial/users.html:73 +#: canaille/templates/oidc/partial/authorization_list.html:31 +#: canaille/templates/oidc/partial/client_list.html:35 +#: canaille/templates/oidc/partial/token_list.html:45 +msgid "No item matches your request" +msgstr "" + +#: canaille/templates/core/partial/group-members.html:85 +#: canaille/templates/core/partial/groups.html:33 +#: canaille/templates/core/partial/users.html:75 +#: canaille/templates/oidc/partial/authorization_list.html:33 +#: canaille/templates/oidc/partial/client_list.html:37 +#: canaille/templates/oidc/partial/token_list.html:47 +msgid "Maybe try with different criteria?" +msgstr "" + +#: canaille/templates/core/partial/group-members.html:88 +#: canaille/templates/core/partial/groups.html:36 +#: canaille/templates/core/partial/users.html:78 +#: canaille/templates/oidc/partial/authorization_list.html:36 +#: canaille/templates/oidc/partial/client_list.html:40 +#: canaille/templates/oidc/partial/token_list.html:50 +#: canaille/templates/oidc/preconsent_list.html:106 +msgid "There is nothing here" +msgstr "" + +#: canaille/templates/core/partial/groups.html:8 +msgid "Number of members" +msgstr "" + +#: canaille/templates/core/sms/sms_otp.txt:1 +#, python-format +msgid "Here is the one-time password you requested for %(website_name)s: %(otp)s" +msgstr "" + #: canaille/templates/macro/form.html:71 +#: canaille/templates/oidc/client_edit.html:52 +#: canaille/templates/oidc/client_edit.html:61 +#: canaille/templates/oidc/client_edit.html:70 +msgid "This field is not editable" +msgstr "" + +#: canaille/templates/macro/form.html:75 msgid "This field is required" msgstr "" -#: canaille/templates/macro/form.html:84 +#: canaille/templates/macro/form.html:88 msgid "Remove this field" msgstr "" -#: canaille/templates/macro/form.html:96 +#: canaille/templates/macro/form.html:100 msgid "Add another field" msgstr "" -#: canaille/templates/macro/form.html:119 +#: canaille/templates/macro/form.html:123 msgid "Password strength" msgstr "" -#: canaille/templates/macro/table.html:8 +#: canaille/templates/macro/table.html:11 msgid "Search…" msgstr "" -#: canaille/templates/macro/table.html:20 +#: canaille/templates/macro/table.html:23 msgid "Search" msgstr "" -#: canaille/templates/macro/table.html:59 +#: canaille/templates/macro/table.html:62 msgid "Page" msgstr "" -#: canaille/templates/macro/table.html:106 +#: canaille/templates/macro/table.html:109 #, python-format msgid "%(nb_items_formatted)s item" msgid_plural "%(nb_items_formatted)s items" msgstr[0] "" msgstr[1] "" +#: canaille/templates/oidc/authorization_view.html:11 +#: canaille/templates/oidc/authorization_view.html:40 +msgid "View an authorization" +msgstr "" + +#: canaille/templates/oidc/authorize.html:21 +#, python-format +msgid "The application %(name)s is requesting access to:" +msgstr "" + +#: canaille/templates/oidc/authorize.html:45 +#, python-format +msgid "You are logged in as %(name)s" +msgstr "" + +#: canaille/templates/oidc/authorize.html:52 +msgid "Deny" +msgstr "" + +#: canaille/templates/oidc/authorize.html:55 +msgid "Switch user" +msgstr "" + +#: canaille/templates/oidc/authorize.html:58 +msgid "Accept" +msgstr "" + +#: canaille/templates/oidc/client_add.html:44 +msgid "Create" +msgstr "" + +#: canaille/templates/oidc/client_edit.html:14 +#: canaille/templates/oidc/client_edit.html:43 +msgid "Edit a client" +msgstr "" + +#: canaille/templates/oidc/client_edit.html:49 +msgid "ID" +msgstr "" + +#: canaille/templates/oidc/client_edit.html:58 +msgid "Secret" +msgstr "" + +#: canaille/templates/oidc/client_edit.html:67 +msgid "Issued at" +msgstr "" + +#: canaille/templates/oidc/client_edit.html:84 +msgid "New token" +msgstr "" + +#: canaille/templates/oidc/client_edit.html:87 +msgid "Delete the client" +msgstr "" + +#: canaille/templates/oidc/consent_list.html:4 +#: canaille/templates/oidc/consent_list.html:10 +#: canaille/templates/oidc/consent_list.html:24 +#: canaille/templates/oidc/preconsent_list.html:19 +#: canaille/templates/oidc/preconsent_list.html:25 +msgid "My consents" +msgstr "" + +#: canaille/templates/oidc/consent_list.html:15 +#: canaille/templates/oidc/preconsent_list.html:30 +#: canaille/templates/oidc/preconsent_list.html:39 +msgid "Pre-authorized applications" +msgstr "" + +#: canaille/templates/oidc/consent_list.html:27 +msgid "Consult and revoke the authorization you gave to websites." +msgstr "" + +#: canaille/templates/oidc/consent_list.html:45 +msgid "From:" +msgstr "" + +#: canaille/templates/oidc/consent_list.html:48 +msgid "Revoked:" +msgstr "" + +#: canaille/templates/oidc/consent_list.html:53 +msgid "Had access to:" +msgstr "" + +#: canaille/templates/oidc/consent_list.html:55 +#: canaille/templates/oidc/preconsent_list.html:61 +msgid "Has access to:" +msgstr "" + +#: canaille/templates/oidc/consent_list.html:78 +#: canaille/templates/oidc/preconsent_list.html:83 +msgid "Policy" +msgstr "" + +#: canaille/templates/oidc/consent_list.html:86 +#: canaille/templates/oidc/preconsent_list.html:91 +msgid "Terms of service" +msgstr "" + +#: canaille/templates/oidc/consent_list.html:93 +msgid "Restore access" +msgstr "" + +#: canaille/templates/oidc/consent_list.html:98 +#: canaille/templates/oidc/preconsent_list.html:97 +msgid "Revoke access" +msgstr "" + +#: canaille/templates/oidc/consent_list.html:108 +msgid "You did not authorize applications yet." +msgstr "" + +#: canaille/templates/oidc/logout.html:19 +msgid "Do you want to log out?" +msgstr "" + +#: canaille/templates/oidc/logout.html:23 +#, python-format +msgid "You are currently logged in as %(username)s." +msgstr "" + +#: canaille/templates/oidc/logout.html:25 +#, python-format +msgid "The application %(client_name)s wants to disconnect your account." +msgstr "" + +#: canaille/templates/oidc/logout.html:36 +msgid "Stay logged" +msgstr "" + +#: canaille/templates/oidc/logout.html:39 +msgid "Logout" +msgstr "" + +#: canaille/templates/oidc/preconsent_list.html:42 +msgid "Those applications automatically have authorizations to access you data." +msgstr "" + +#: canaille/templates/oidc/token_view.html:12 +#: canaille/templates/oidc/token_view.html:45 +msgid "Token details" +msgstr "" + +#: canaille/templates/oidc/partial/authorization_list.html:6 +#: canaille/templates/oidc/partial/token_list.html:6 +#: canaille/templates/oidc/token_view.html:52 +msgid "Client" +msgstr "" + +#: canaille/templates/oidc/partial/authorization_list.html:7 +#: canaille/templates/oidc/partial/token_list.html:7 +#: canaille/templates/oidc/token_view.html:60 +msgid "Subject" +msgstr "" + +#: canaille/templates/oidc/token_view.html:84 +msgid "Audience" +msgstr "" + +#: canaille/templates/oidc/token_view.html:98 +msgid "Issue date" +msgstr "" + +#: canaille/templates/oidc/token_view.html:102 +msgid "Expiration date" +msgstr "" + +#: canaille/templates/oidc/token_view.html:107 +msgid "This token has expired." +msgstr "" + +#: canaille/templates/oidc/token_view.html:113 +msgid "Revokation date" +msgstr "" + +#: canaille/templates/oidc/token_view.html:119 +msgid "Revoke token" +msgstr "" + +#: canaille/templates/oidc/token_view.html:122 +msgid "This token has not been revoked" +msgstr "" + +#: canaille/templates/oidc/token_view.html:128 +msgid "Token type" +msgstr "" + +#: canaille/templates/oidc/token_view.html:132 +msgid "Access token" +msgstr "" + +#: canaille/templates/oidc/token_view.html:143 +msgid "Refresh token" +msgstr "" + +#: canaille/templates/oidc/token_view.html:145 +msgid "No refresh token" +msgstr "" + +#: canaille/templates/oidc/modals/delete-client.html:9 +msgid "Client deletion" +msgstr "" + +#: canaille/templates/oidc/modals/delete-client.html:13 +#, python-format +msgid "" +"Are you sure you want to delete the client %(client_name)s? This action " +"is unrevokable and all the data about this client will be removed." +msgstr "" + +#: canaille/templates/oidc/modals/revoke-token.html:9 +msgid "Token revokation" +msgstr "" + +#: canaille/templates/oidc/modals/revoke-token.html:13 +msgid "Are you sure you want to revoke this token? This action is unrevokable." +msgstr "" + +#: canaille/templates/oidc/modals/revoke-token.html:21 +msgid "Revoke" +msgstr "" + +#: canaille/templates/oidc/partial/authorization_list.html:5 +msgid "Code" +msgstr "" + +#: canaille/templates/oidc/partial/authorization_list.html:8 +#: canaille/templates/oidc/partial/client_list.html:8 +#: canaille/templates/oidc/partial/token_list.html:8 +msgid "Created" +msgstr "" + +#: canaille/templates/oidc/partial/client_list.html:7 +msgid "URL" +msgstr "" + +#: canaille/templates/oidc/partial/token_list.html:5 +msgid "Token" +msgstr "" + diff --git a/tests/core/test_account.py b/tests/core/test_account.py index 61d57cd4..97b3b0df 100644 --- a/tests/core/test_account.py +++ b/tests/core/test_account.py @@ -1,5 +1,8 @@ import datetime +from unittest import mock +import pytest +import time_machine from flask import g from canaille.app import models @@ -196,3 +199,126 @@ def test_account_locked_during_session(testclient, logged_user, backend): logged_user.lock_date = datetime.datetime.now(datetime.timezone.utc) backend.save(logged_user) testclient.get("/profile/user/settings", status=403) + + +def test_expired_password_redirection_and_register_new_password_for_memory_and_sql( + testclient, + logged_user, + user, + backend, + admin, +): + """time_machine does not work with ldap.""" + if "ldap" in backend.__class__.__module__: + pytest.skip() + + testclient.app.config["WTF_CSRF_ENABLED"] = False + backend.reload(logged_user) + res = testclient.get("/profile/user/settings", status=200) + res.form["password1"] = "123456789" + res.form["password2"] = "123456789" + + with time_machine.travel("2020-01-01 01:00:00+00:00", tick=False) as traveller: + res = res.form.submit(name="action", value="edit-settings") + + testclient.app.config["CANAILLE"]["PASSWORD_LIFETIME"] = "P5D" + + traveller.shift(datetime.timedelta(days=5, minutes=1)) + + backend.reload(g.user) + + res = testclient.get("/profile/user/settings") + + testclient.get("/reset/admin", status=403) + + assert ( + "info", + "Your password has expired, please choose a new password.", + ) in res.flashes + assert res.location == "/reset/user" + + backend.reload(logged_user) + res = testclient.get("/reset/user") + + res.form["password"] = "foobarbaz" + res.form["confirmation"] = "foobarbaz" + res = res.form.submit() + assert ("success", "Your password has been updated successfully") in res.flashes + + +@mock.patch("canaille.core.models.User.has_expired_password") +def test_expired_password_redirection_and_register_new_password_for_ldap_sql_and_memory( + has_expired, + testclient, + logged_user, + user, + backend, + admin, +): + """time_machine does not work with ldap.""" + has_expired.return_value = False + assert user.password_last_update is None + res = testclient.get("/profile/user/settings", status=200) + res.form["password1"] = "123456789" + res.form["password2"] = "123456789" + res = res.form.submit(name="action", value="edit-settings") + backend.reload(logged_user) + assert user.password_last_update is not None + + has_expired.return_value = True + res = testclient.get("/profile/user/settings") + testclient.get("/reset/admin", status=403) + assert ( + "info", + "Your password has expired, please choose a new password.", + ) in res.flashes + assert res.location == "/reset/user" + backend.reload(logged_user) + backend.reload(g.user) + backend.reload(user) + + res = testclient.get("/reset/user") + + res.form["password"] = "foobarbaz" + res.form["confirmation"] = "foobarbaz" + res = res.form.submit() + assert ("success", "Your password has been updated successfully") in res.flashes + + +def test_not_expired_password_or_wrong_user_redirection( + testclient, logged_user, user, backend, admin +): + assert user.password_last_update is None + res = testclient.get("/profile/user/settings", status=200) + res.form["password1"] = "123456789" + res.form["password2"] = "123456789" + res = res.form.submit(name="action", value="edit-settings") + backend.reload(logged_user) + assert user.password_last_update is not None + + def test_two_redirections(password_lifetime): + testclient.app.config["CANAILLE"]["PASSWORD_LIFETIME"] = password_lifetime + testclient.get("/reset/user", status=403) + testclient.get("/reset/admin", status=403) + + test_two_redirections(None) + + testclient.app.config["CANAILLE"]["PASSWORD_LIFETIME"] = "PT0S" + res = testclient.get("/profile/user/settings") + assert ( + "info", + "Your password has expired, please choose a new password.", + ) in res.flashes + assert res.location == "/reset/user" + + testclient.app.config["CANAILLE"]["PASSWORD_LIFETIME"] = "P1D" + res = testclient.get("/profile/user/settings") + res.form["password1"] = "123456789" + res.form["password2"] = "123456789" + res = res.form.submit(name="action", value="edit-settings") + + test_two_redirections("P1D") + + +def test_expired_password_needed_without_current_user(testclient, user): + testclient.get("/reset/user", status=403)