forked from Github-Mirrors/canaille
Merge branch canaille:main into main
This commit is contained in:
commit
db72597cc0
41 changed files with 863 additions and 637 deletions
1
.github/workflows/tests.yaml
vendored
1
.github/workflows/tests.yaml
vendored
|
@ -21,7 +21,6 @@ jobs:
|
|||
- '3.10'
|
||||
- '3.9'
|
||||
- '3.8'
|
||||
- '3.7'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v2
|
||||
|
|
|
@ -20,11 +20,6 @@ style:
|
|||
stage: test
|
||||
script: tox -e style
|
||||
|
||||
python37:
|
||||
image: python:3.7
|
||||
stage: test
|
||||
script: tox -e py37
|
||||
|
||||
python38:
|
||||
image: python:3.8
|
||||
stage: test
|
||||
|
|
|
@ -11,6 +11,7 @@ repos:
|
|||
rev: 'v0.0.269'
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v3.9.0
|
||||
hooks:
|
||||
|
@ -20,7 +21,7 @@ repos:
|
|||
rev: v3.4.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: ["--py37-plus"]
|
||||
args: ["--py38-plus"]
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.3.0
|
||||
hooks:
|
||||
|
|
|
@ -3,11 +3,20 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_,
|
||||
and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.
|
||||
|
||||
Removed
|
||||
*******
|
||||
|
||||
- Stop support for python 3.7 :pr:`131`
|
||||
|
||||
[0.0.26] - 2023-06-03
|
||||
=====================
|
||||
|
||||
Added
|
||||
*****
|
||||
|
||||
- Implemented account expiration based on OpenLDAP ppolicy overlay. Needs OpenLDAP 2.5+
|
||||
:issue:`13` :pr:`118`
|
||||
- Timezone configuration entry. :issue:`137` :pr:`130`
|
||||
|
||||
Fixed
|
||||
*****
|
||||
|
|
|
@ -17,6 +17,7 @@ csrf = CSRFProtect()
|
|||
|
||||
def setup_config(app, config=None, validate=True):
|
||||
import canaille.app.configuration
|
||||
from canaille.oidc.installation import install
|
||||
|
||||
app.config.from_mapping(
|
||||
{
|
||||
|
@ -35,6 +36,9 @@ def setup_config(app, config=None, validate=True):
|
|||
"Either create conf/config.toml or set the 'CONFIG' variable environment."
|
||||
)
|
||||
|
||||
if app.debug: # pragma: no cover
|
||||
install(app.config)
|
||||
|
||||
if validate:
|
||||
canaille.app.configuration.validate(app.config)
|
||||
|
||||
|
@ -50,6 +54,9 @@ def setup_backend(app, backend):
|
|||
g.backend = backend
|
||||
app.backend = backend
|
||||
|
||||
if app.debug: # pragma: no cover
|
||||
backend.install(app.config)
|
||||
|
||||
|
||||
def setup_sentry(app): # pragma: no cover
|
||||
if not app.config.get("SENTRY_DSN"):
|
||||
|
@ -182,7 +189,6 @@ def create_app(config=None, validate=True, backend=None):
|
|||
try:
|
||||
from .oidc.oauth import setup_oauth
|
||||
from .app.i18n import setup_i18n
|
||||
from .app.installation import install
|
||||
|
||||
setup_logging(app)
|
||||
setup_backend(app, backend)
|
||||
|
@ -193,9 +199,6 @@ def create_app(config=None, validate=True, backend=None):
|
|||
setup_themer(app)
|
||||
setup_flask(app)
|
||||
|
||||
if app.debug: # pragma: no cover
|
||||
install(app.config)
|
||||
|
||||
except Exception as exc: # pragma: no cover
|
||||
if sentry_sdk:
|
||||
sentry_sdk.capture_exception(exc)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import datetime
|
||||
import math
|
||||
|
||||
import pytz
|
||||
import wtforms
|
||||
from canaille.app.i18n import DEFAULT_LANGUAGE_CODE
|
||||
from canaille.app.i18n import locale_selector
|
||||
from canaille.app.i18n import timezone_selector
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask import make_response
|
||||
|
@ -81,3 +84,31 @@ class TableForm(I18NFormMixin, FlaskForm):
|
|||
def validate_page(self, field):
|
||||
if field.data < 1 or field.data > self.page_max:
|
||||
raise wtforms.validators.ValidationError(_("The page number is not valid"))
|
||||
|
||||
|
||||
class DateTimeUTCField(wtforms.DateTimeLocalField):
|
||||
def _value(self):
|
||||
if not self.data:
|
||||
return ""
|
||||
|
||||
user_timezone = timezone_selector()
|
||||
locale_dt = self.data.astimezone(user_timezone)
|
||||
return locale_dt.strftime(self.format[0])
|
||||
|
||||
def process_formdata(self, valuelist):
|
||||
if not valuelist:
|
||||
return
|
||||
|
||||
date_str = " ".join(valuelist)
|
||||
user_timezone = timezone_selector()
|
||||
for format in self.strptime_format:
|
||||
try:
|
||||
unaware_dt = datetime.datetime.strptime(date_str, format)
|
||||
locale_dt = user_timezone.localize(unaware_dt)
|
||||
utc_dt = locale_dt.astimezone(pytz.utc)
|
||||
self.data = utc_dt
|
||||
return
|
||||
except ValueError:
|
||||
self.data = None
|
||||
|
||||
raise ValueError(self.gettext("Not a valid datetime value."))
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import gettext
|
||||
|
||||
import pycountry
|
||||
import pytz
|
||||
from babel.dates import LOCALTZ
|
||||
from flask import current_app
|
||||
from flask import g
|
||||
from flask import request
|
||||
|
@ -13,7 +15,9 @@ babel = Babel()
|
|||
|
||||
|
||||
def setup_i18n(app):
|
||||
babel.init_app(app, locale_selector=locale_selector)
|
||||
babel.init_app(
|
||||
app, locale_selector=locale_selector, timezone_selector=timezone_selector
|
||||
)
|
||||
|
||||
@app.before_request
|
||||
def before_request():
|
||||
|
@ -40,6 +44,13 @@ def locale_selector():
|
|||
return request.accept_languages.best_match(available_language_codes)
|
||||
|
||||
|
||||
def timezone_selector():
|
||||
try:
|
||||
return pytz.timezone(current_app.config.get("TIMEZONE"))
|
||||
except pytz.exceptions.UnknownTimeZoneError:
|
||||
return LOCALTZ
|
||||
|
||||
|
||||
def native_language_name_from_code(code):
|
||||
language = pycountry.languages.get(alpha_2=code[:2])
|
||||
if code == DEFAULT_LANGUAGE_CODE:
|
||||
|
|
|
@ -127,6 +127,12 @@ class LDAPBackend(Backend):
|
|||
)
|
||||
|
||||
def teardown(self):
|
||||
try: # pragma: no cover
|
||||
if request.endpoint == "static":
|
||||
return
|
||||
except RuntimeError: # pragma: no cover
|
||||
pass
|
||||
|
||||
if self.connection: # pragma: no branch
|
||||
self.connection.unbind_s()
|
||||
self.connection = None
|
||||
|
|
|
@ -25,6 +25,10 @@ SECRET_KEY = "change me before you go in production"
|
|||
# If unset, language is detected
|
||||
# LANGUAGE = "en"
|
||||
|
||||
# The timezone in which datetimes will be displayed to the users.
|
||||
# If unset, the server timezone will be used.
|
||||
# TIMEZONE = UTC
|
||||
|
||||
# If you have a sentry instance, you can set its dsn here:
|
||||
# SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
|
||||
|
||||
|
|
|
@ -425,7 +425,7 @@ def registration(data, hash):
|
|||
else:
|
||||
user = profile_create(current_app, form)
|
||||
user.login()
|
||||
flash(_("Your account has been created successfuly."), "success")
|
||||
flash(_("Your account has been created successfully."), "success")
|
||||
return redirect(
|
||||
url_for("account.profile_edition", username=user.user_name[0])
|
||||
)
|
||||
|
@ -574,7 +574,7 @@ def profile_edition(user, username):
|
|||
user.preferred_language = None
|
||||
|
||||
user.save()
|
||||
flash(_("Profile updated successfuly."), "success")
|
||||
flash(_("Profile updated successfully."), "success")
|
||||
return redirect(url_for("account.profile_edition", username=username))
|
||||
|
||||
return render_template(
|
||||
|
@ -696,7 +696,7 @@ def profile_settings_edit(editor, edited_user):
|
|||
edited_user.set_password(form["password1"].data)
|
||||
|
||||
edited_user.save()
|
||||
flash(_("Profile updated successfuly."), "success")
|
||||
flash(_("Profile updated successfully."), "success")
|
||||
return redirect(
|
||||
url_for("account.profile_settings", username=edited_user.user_name[0])
|
||||
)
|
||||
|
@ -816,7 +816,7 @@ def reset(user_name, hash):
|
|||
user.set_password(form.password.data)
|
||||
user.login()
|
||||
|
||||
flash(_("Your password has been updated successfuly"), "success")
|
||||
flash(_("Your password has been updated successfully"), "success")
|
||||
return redirect(url_for("account.profile_edition", username=user_name))
|
||||
|
||||
return render_template(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import wtforms.form
|
||||
from canaille.app import models
|
||||
from canaille.app.forms import DateTimeUTCField
|
||||
from canaille.app.forms import HTMXBaseForm
|
||||
from canaille.app.forms import HTMXForm
|
||||
from canaille.app.forms import is_uri
|
||||
|
@ -286,7 +287,7 @@ def profile_form(write_field_names, readonly_field_names, user=None):
|
|||
del fields["groups"]
|
||||
|
||||
if current_app.backend.get().has_account_lockability(): # pragma: no branch
|
||||
fields["lock_date"] = wtforms.DateTimeLocalField(
|
||||
fields["lock_date"] = DateTimeUTCField(
|
||||
_("Account expiration"),
|
||||
validators=[wtforms.validators.Optional()],
|
||||
format=[
|
||||
|
|
|
@ -7,8 +7,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2023-05-26 15:31+0200\n"
|
||||
"PO-Revision-Date: 2023-05-14 23:49+0000\n"
|
||||
"POT-Creation-Date: 2023-05-30 09:43+0200\n"
|
||||
"PO-Revision-Date: 2023-05-29 15:50+0000\n"
|
||||
"Last-Translator: Ettore Atalan <atalanttore@googlemail.com>\n"
|
||||
"Language-Team: German <https://hosted.weblate.org/projects/canaille/canaille/"
|
||||
"de/>\n"
|
||||
|
@ -22,15 +22,15 @@ msgstr ""
|
|||
|
||||
#: canaille/app/flask.py:65
|
||||
msgid "No SMTP server has been configured"
|
||||
msgstr ""
|
||||
msgstr "Es wurde kein SMTP-Server konfiguriert"
|
||||
|
||||
#: canaille/app/forms.py:20
|
||||
msgid "This is not a valid URL"
|
||||
msgstr ""
|
||||
msgstr "Dies ist keine gültige URL"
|
||||
|
||||
#: canaille/app/forms.py:83
|
||||
msgid "The page number is not valid"
|
||||
msgstr ""
|
||||
msgstr "Die Seitenzahl ist nicht gültig"
|
||||
|
||||
#: canaille/backends/ldap/backend.py:98
|
||||
msgid "Could not connect to the LDAP server '{uri}'"
|
||||
|
@ -42,30 +42,28 @@ msgstr ""
|
|||
|
||||
#: canaille/backends/ldap/backend.py:214
|
||||
msgid "John Doe"
|
||||
msgstr ""
|
||||
msgstr "Max Mustermann"
|
||||
|
||||
#: canaille/backends/ldap/backend.py:217 canaille/core/forms.py:124
|
||||
#: canaille/core/forms.py:340
|
||||
#: canaille/core/forms.py:341
|
||||
msgid "jdoe"
|
||||
msgstr ""
|
||||
msgstr "mmustermann"
|
||||
|
||||
#: canaille/backends/ldap/backend.py:220
|
||||
msgid "john@doe.com"
|
||||
msgstr ""
|
||||
msgstr "max@mustermann.de"
|
||||
|
||||
#: canaille/backends/ldap/backend.py:222
|
||||
msgid " or "
|
||||
msgstr ""
|
||||
msgstr " oder "
|
||||
|
||||
#: canaille/backends/ldap/models.py:121
|
||||
#, fuzzy
|
||||
#| msgid "Your account has already been created."
|
||||
msgid "Your account has been locked."
|
||||
msgstr "Ihr Konto wurde bereits erstellt."
|
||||
msgstr "Ihr Konto wurde gesperrt."
|
||||
|
||||
#: canaille/backends/ldap/models.py:126
|
||||
msgid "You should change your password."
|
||||
msgstr ""
|
||||
msgstr "Sie sollten Ihr Passwort ändern."
|
||||
|
||||
#: canaille/core/account.py:97 canaille/core/account.py:120
|
||||
#: canaille/core/account.py:128 canaille/oidc/endpoints.py:81
|
||||
|
@ -73,7 +71,7 @@ msgstr ""
|
|||
msgid "Login failed, please check your information"
|
||||
msgstr "Anmeldung fehlgeschlagen, bitte überprüfen Sie Ihre Angaben"
|
||||
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:667
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:660
|
||||
#, python-format
|
||||
msgid "Connection successful. Welcome %(user)s"
|
||||
msgstr "Verbindung erfolgreich. Willkommen %(user)s"
|
||||
|
@ -112,19 +110,21 @@ msgid "You are already logged in, you cannot create an account."
|
|||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:319 canaille/core/forms.py:263
|
||||
#: canaille/core/forms.py:358 canaille/templates/groups.html:5
|
||||
#: canaille/core/forms.py:359 canaille/templates/groups.html:5
|
||||
#: canaille/templates/groups.html:25 canaille/templates/partial/users.html:18
|
||||
#: canaille/themes/default/base.html:61
|
||||
msgid "Groups"
|
||||
msgstr ""
|
||||
msgstr "Gruppen"
|
||||
|
||||
#: canaille/core/account.py:341 canaille/core/account.py:372
|
||||
msgid "User account creation failed."
|
||||
msgstr ""
|
||||
msgstr "Erstellung des Benutzerkontos fehlgeschlagen."
|
||||
|
||||
#: canaille/core/account.py:346
|
||||
msgid "Your account has been created successfuly."
|
||||
msgstr ""
|
||||
#, fuzzy
|
||||
#| msgid "Your account has been created successfuly."
|
||||
msgid "Your account has been created successfully."
|
||||
msgstr "Ihr Konto wurde erfolgreich erstellt."
|
||||
|
||||
#: canaille/core/account.py:412
|
||||
msgid "User account creation succeed."
|
||||
|
@ -134,9 +134,11 @@ msgstr ""
|
|||
msgid "Profile edition failed."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:624
|
||||
msgid "Profile updated successfuly."
|
||||
msgstr ""
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:617
|
||||
#, fuzzy
|
||||
#| msgid "Your password has been updated successfuly"
|
||||
msgid "Profile updated successfully."
|
||||
msgstr "Ihr Passwort wurde erfolgreich aktualisiert"
|
||||
|
||||
#: canaille/core/account.py:531
|
||||
msgid ""
|
||||
|
@ -159,25 +161,23 @@ msgid "Could not send the password reset email"
|
|||
msgstr "Die E-Mail zum Zurücksetzen des Passworts konnte nicht gesendet werden"
|
||||
|
||||
#: canaille/core/account.py:559
|
||||
#, fuzzy
|
||||
msgid "The account has been locked"
|
||||
msgstr "Der Kunde wurde bearbeitet."
|
||||
msgstr "Das Konto wurde gesperrt"
|
||||
|
||||
#: canaille/core/account.py:570
|
||||
#, fuzzy
|
||||
msgid "The account has been unlocked"
|
||||
msgstr "Der Kunde wurde bearbeitet."
|
||||
msgstr "Das Konto wurde entsperrt"
|
||||
|
||||
#: canaille/core/account.py:644
|
||||
#: canaille/core/account.py:637
|
||||
#, python-format
|
||||
msgid "The user %(user)s has been sucessfuly deleted"
|
||||
msgstr ""
|
||||
msgstr "Der Benutzer %(user)s wurde erfolgreich gelöscht"
|
||||
|
||||
#: canaille/core/account.py:684
|
||||
#: canaille/core/account.py:677
|
||||
msgid "Could not send the password reset link."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:688
|
||||
#: canaille/core/account.py:681
|
||||
msgid ""
|
||||
"A password reset link has been sent at your email address. You should "
|
||||
"receive it within a few minutes."
|
||||
|
@ -185,7 +185,7 @@ msgstr ""
|
|||
"Ein Link zum Zurücksetzen des Passworts wurde an Ihre E-Mail-Adresse "
|
||||
"gesendet. Sie sollten ihn innerhalb weniger Minuten erhalten."
|
||||
|
||||
#: canaille/core/account.py:699
|
||||
#: canaille/core/account.py:692
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The user '%(user)s' does not have permissions to update their password. We "
|
||||
|
@ -194,19 +194,21 @@ msgstr ""
|
|||
"Der Benutzer „%(user)s“ hat nicht die Berechtigung, sein Passwort zu "
|
||||
"aktualisieren. Wir können keine E-Mail zum Zurücksetzen des Passworts senden."
|
||||
|
||||
#: canaille/core/account.py:714
|
||||
#: canaille/core/account.py:707
|
||||
msgid "We encountered an issue while we sent the password recovery email."
|
||||
msgstr ""
|
||||
"Beim Versand der E-Mail zur Wiederherstellung des Passworts ist ein Problem "
|
||||
"aufgetreten."
|
||||
|
||||
#: canaille/core/account.py:735
|
||||
#: canaille/core/account.py:728
|
||||
msgid "The password reset link that brought you here was invalid."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:744
|
||||
msgid "Your password has been updated successfuly"
|
||||
msgstr ""
|
||||
#: canaille/core/account.py:737
|
||||
#, fuzzy
|
||||
#| msgid "Your password has been updated successfuly"
|
||||
msgid "Your password has been updated successfully"
|
||||
msgstr "Ihr Passwort wurde erfolgreich aktualisiert"
|
||||
|
||||
#: canaille/core/admin.py:23 canaille/templates/partial/users.html:15
|
||||
msgid "Email"
|
||||
|
@ -214,7 +216,7 @@ msgstr "E-Mail"
|
|||
|
||||
#: canaille/core/admin.py:29 canaille/core/forms.py:56
|
||||
#: canaille/core/forms.py:80 canaille/core/forms.py:168
|
||||
#: canaille/core/forms.py:352
|
||||
#: canaille/core/forms.py:353
|
||||
msgid "jane@doe.com"
|
||||
msgstr ""
|
||||
|
||||
|
@ -228,7 +230,7 @@ msgstr "Die Testeinladungs-E-Mail wurde nicht korrekt gesendet"
|
|||
|
||||
#: canaille/core/admin.py:89 canaille/core/mails.py:92
|
||||
msgid "Password initialization on {website_name}"
|
||||
msgstr ""
|
||||
msgstr "Passwortinitialisierung auf {website_name}"
|
||||
|
||||
#: canaille/core/admin.py:131 canaille/core/mails.py:52
|
||||
msgid "Password reset on {website_name}"
|
||||
|
@ -259,16 +261,16 @@ msgstr ""
|
|||
#: canaille/core/forms.py:53 canaille/core/forms.py:77
|
||||
#: canaille/templates/partial/users.html:9
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
msgstr "Anmeldung"
|
||||
|
||||
#: canaille/core/forms.py:66 canaille/core/forms.py:89
|
||||
#: canaille/core/forms.py:213
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
msgstr "Passwort"
|
||||
|
||||
#: canaille/core/forms.py:96 canaille/core/forms.py:223
|
||||
msgid "Password confirmation"
|
||||
msgstr ""
|
||||
msgstr "Passwortbestätigung"
|
||||
|
||||
#: canaille/core/forms.py:99 canaille/core/forms.py:226
|
||||
msgid "Password and confirmation do not match."
|
||||
|
@ -278,12 +280,12 @@ msgstr "Passwort und Bestätigung stimmen nicht überein."
|
|||
msgid "Automatic"
|
||||
msgstr "Automatisch"
|
||||
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:339
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:340
|
||||
msgid "Username"
|
||||
msgstr "Anmeldename"
|
||||
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:311
|
||||
#: canaille/core/forms.py:325 canaille/oidc/forms.py:22
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:312
|
||||
#: canaille/core/forms.py:326 canaille/oidc/forms.py:22
|
||||
#: canaille/templates/partial/groups.html:6
|
||||
#: canaille/templates/partial/oidc/admin/client_list.html:6
|
||||
#: canaille/templates/partial/users.html:12
|
||||
|
@ -292,11 +294,11 @@ msgstr "Name"
|
|||
|
||||
#: canaille/core/forms.py:129
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
msgstr "Titel"
|
||||
|
||||
#: canaille/core/forms.py:129
|
||||
msgid "Vice president"
|
||||
msgstr ""
|
||||
msgstr "Vizepräsident"
|
||||
|
||||
#: canaille/core/forms.py:132
|
||||
msgid "Given name"
|
||||
|
@ -322,7 +324,7 @@ msgstr "Anzeigename"
|
|||
msgid "Johnny"
|
||||
msgstr "Maria"
|
||||
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:345
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:346
|
||||
msgid "Email address"
|
||||
msgstr "E-Mail-Adresse"
|
||||
|
||||
|
@ -352,16 +354,15 @@ msgstr "Beispielstraße 132, 11111 Musterstadt"
|
|||
|
||||
#: canaille/core/forms.py:183
|
||||
msgid "Street"
|
||||
msgstr ""
|
||||
msgstr "Straße"
|
||||
|
||||
#: canaille/core/forms.py:185
|
||||
#, fuzzy
|
||||
msgid "132, Foobar Street"
|
||||
msgstr "Beispielstraße 132, 11111 Musterstadt"
|
||||
msgstr "Beispielstraße 132"
|
||||
|
||||
#: canaille/core/forms.py:189
|
||||
msgid "Postal Code"
|
||||
msgstr ""
|
||||
msgstr "Postleitzahl"
|
||||
|
||||
#: canaille/core/forms.py:195
|
||||
msgid "Locality"
|
||||
|
@ -373,11 +374,11 @@ msgstr ""
|
|||
|
||||
#: canaille/core/forms.py:201
|
||||
msgid "Region"
|
||||
msgstr ""
|
||||
msgstr "Region"
|
||||
|
||||
#: canaille/core/forms.py:203
|
||||
msgid "North Pole"
|
||||
msgstr ""
|
||||
msgstr "Nordpol"
|
||||
|
||||
#: canaille/core/forms.py:207
|
||||
msgid "Photo"
|
||||
|
@ -389,23 +390,20 @@ msgid "Delete the photo"
|
|||
msgstr "Foto löschen"
|
||||
|
||||
#: canaille/core/forms.py:234
|
||||
#, fuzzy
|
||||
#| msgid "Username"
|
||||
msgid "User number"
|
||||
msgstr "Anmeldename"
|
||||
msgstr "Benutzernummer"
|
||||
|
||||
#: canaille/core/forms.py:236 canaille/core/forms.py:242
|
||||
msgid "1234"
|
||||
msgstr "1234"
|
||||
|
||||
#: canaille/core/forms.py:240
|
||||
#, fuzzy
|
||||
msgid "Department"
|
||||
msgstr "Telefonnummer"
|
||||
msgstr "Abteilung"
|
||||
|
||||
#: canaille/core/forms.py:246
|
||||
msgid "Organization"
|
||||
msgstr ""
|
||||
msgstr "Organisation"
|
||||
|
||||
#: canaille/core/forms.py:248
|
||||
msgid "Cogip LTD."
|
||||
|
@ -428,20 +426,20 @@ msgstr "Bevorzugte Sprache"
|
|||
msgid "users, admins …"
|
||||
msgstr "Benutzer/innen, Administrator/innen …"
|
||||
|
||||
#: canaille/core/forms.py:289
|
||||
#: canaille/core/forms.py:290
|
||||
msgid "Account expiration"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/forms.py:314
|
||||
#: canaille/core/forms.py:315
|
||||
msgid "group"
|
||||
msgstr "Gruppe"
|
||||
|
||||
#: canaille/core/forms.py:318 canaille/core/forms.py:332
|
||||
#: canaille/core/forms.py:319 canaille/core/forms.py:333
|
||||
#: canaille/templates/partial/groups.html:7
|
||||
msgid "Description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
#: canaille/core/forms.py:343
|
||||
#: canaille/core/forms.py:344
|
||||
msgid "Username editable by the invitee"
|
||||
msgstr "Vom Eingeladenen bearbeitbarer Benutzername"
|
||||
|
||||
|
@ -452,7 +450,7 @@ msgstr "Gruppenerstellung fehlgeschlagen."
|
|||
#: canaille/core/groups.py:45
|
||||
#, python-format
|
||||
msgid "The group %(group)s has been sucessfully created"
|
||||
msgstr ""
|
||||
msgstr "Die Gruppe %(group)s wurde erfolgreich erstellt"
|
||||
|
||||
#: canaille/core/groups.py:97
|
||||
#, python-format
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2023-05-26 15:31+0200\n"
|
||||
"PO-Revision-Date: 2023-03-31 13:40+0000\n"
|
||||
"Report-Msgid-Bugs-To: contact@yaal.coop\n"
|
||||
"POT-Creation-Date: 2023-05-30 09:43+0200\n"
|
||||
"PO-Revision-Date: 2023-05-31 19:28+0000\n"
|
||||
"Last-Translator: gallegonovato <fran-carro@hotmail.es>\n"
|
||||
"Language-Team: Spanish <https://hosted.weblate.org/projects/canaille/"
|
||||
"canaille/es/>\n"
|
||||
|
@ -18,7 +18,7 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.17-dev\n"
|
||||
"X-Generator: Weblate 4.18-dev\n"
|
||||
"Generated-By: Babel 2.12.1\n"
|
||||
|
||||
#: canaille/app/flask.py:65
|
||||
|
@ -46,7 +46,7 @@ msgid "John Doe"
|
|||
msgstr "John Doe"
|
||||
|
||||
#: canaille/backends/ldap/backend.py:217 canaille/core/forms.py:124
|
||||
#: canaille/core/forms.py:340
|
||||
#: canaille/core/forms.py:341
|
||||
msgid "jdoe"
|
||||
msgstr "jdoe"
|
||||
|
||||
|
@ -59,14 +59,12 @@ msgid " or "
|
|||
msgstr " o "
|
||||
|
||||
#: canaille/backends/ldap/models.py:121
|
||||
#, fuzzy
|
||||
#| msgid "Your account has already been created."
|
||||
msgid "Your account has been locked."
|
||||
msgstr "Tu cuenta ya existe."
|
||||
msgstr "Tu cuenta ha sido bloqueada."
|
||||
|
||||
#: canaille/backends/ldap/models.py:126
|
||||
msgid "You should change your password."
|
||||
msgstr ""
|
||||
msgstr "Deberías cambiar tu contraseña."
|
||||
|
||||
#: canaille/core/account.py:97 canaille/core/account.py:120
|
||||
#: canaille/core/account.py:128 canaille/oidc/endpoints.py:81
|
||||
|
@ -74,7 +72,7 @@ msgstr ""
|
|||
msgid "Login failed, please check your information"
|
||||
msgstr "Falló el inicio de sesión, comprueba tus credenciales"
|
||||
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:667
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:660
|
||||
#, python-format
|
||||
msgid "Connection successful. Welcome %(user)s"
|
||||
msgstr "Acceso correcto. Bienvenido/a %(user)s"
|
||||
|
@ -113,7 +111,7 @@ msgid "You are already logged in, you cannot create an account."
|
|||
msgstr "Ya has iniciado sesión, no puedes crear una cuenta."
|
||||
|
||||
#: canaille/core/account.py:319 canaille/core/forms.py:263
|
||||
#: canaille/core/forms.py:358 canaille/templates/groups.html:5
|
||||
#: canaille/core/forms.py:359 canaille/templates/groups.html:5
|
||||
#: canaille/templates/groups.html:25 canaille/templates/partial/users.html:18
|
||||
#: canaille/themes/default/base.html:61
|
||||
msgid "Groups"
|
||||
|
@ -124,7 +122,7 @@ msgid "User account creation failed."
|
|||
msgstr "Falló la creación de la cuenta de usuario."
|
||||
|
||||
#: canaille/core/account.py:346
|
||||
msgid "Your account has been created successfuly."
|
||||
msgid "Your account has been created successfully."
|
||||
msgstr "Tu cuenta se ha creado correctamente."
|
||||
|
||||
#: canaille/core/account.py:412
|
||||
|
@ -135,8 +133,8 @@ msgstr "Cuenta creada correctamente."
|
|||
msgid "Profile edition failed."
|
||||
msgstr "Falló la actualización del perfil."
|
||||
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:624
|
||||
msgid "Profile updated successfuly."
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:617
|
||||
msgid "Profile updated successfully."
|
||||
msgstr "Perfil actualizado correctamente."
|
||||
|
||||
#: canaille/core/account.py:531
|
||||
|
@ -160,27 +158,23 @@ msgid "Could not send the password reset email"
|
|||
msgstr "No fue posible enviar el email para restablecer de contraseña"
|
||||
|
||||
#: canaille/core/account.py:559
|
||||
#, fuzzy
|
||||
#| msgid "The access has been revoked"
|
||||
msgid "The account has been locked"
|
||||
msgstr "El acceso ha sido revocado"
|
||||
msgstr "La cuenta ha sido bloqueada"
|
||||
|
||||
#: canaille/core/account.py:570
|
||||
#, fuzzy
|
||||
#| msgid "The access has been revoked"
|
||||
msgid "The account has been unlocked"
|
||||
msgstr "El acceso ha sido revocado"
|
||||
msgstr "La cuenta ha sido desbloqueada"
|
||||
|
||||
#: canaille/core/account.py:644
|
||||
#: canaille/core/account.py:637
|
||||
#, python-format
|
||||
msgid "The user %(user)s has been sucessfuly deleted"
|
||||
msgstr "El usuario %(user)s ha sido eliminado correctamente"
|
||||
|
||||
#: canaille/core/account.py:684
|
||||
#: canaille/core/account.py:677
|
||||
msgid "Could not send the password reset link."
|
||||
msgstr "No fue posible enviar el email para restablecer de contraseña."
|
||||
|
||||
#: canaille/core/account.py:688
|
||||
#: canaille/core/account.py:681
|
||||
msgid ""
|
||||
"A password reset link has been sent at your email address. You should "
|
||||
"receive it within a few minutes."
|
||||
|
@ -188,7 +182,7 @@ msgstr ""
|
|||
"Se ha enviado un enlace para restablecer la contraseña a tu correo "
|
||||
"electrónico. Deberías recibirlo en algunos minutos."
|
||||
|
||||
#: canaille/core/account.py:699
|
||||
#: canaille/core/account.py:692
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The user '%(user)s' does not have permissions to update their password. We "
|
||||
|
@ -197,18 +191,18 @@ msgstr ""
|
|||
"El usuario '%(user)s' no tiene permiso para actualizar su contraseña. No es "
|
||||
"posible enviarle un email para que restablezca su contraseña."
|
||||
|
||||
#: canaille/core/account.py:714
|
||||
#: canaille/core/account.py:707
|
||||
msgid "We encountered an issue while we sent the password recovery email."
|
||||
msgstr "Se produjo un problema enviando el email para recuperar la contraseña."
|
||||
|
||||
#: canaille/core/account.py:735
|
||||
#: canaille/core/account.py:728
|
||||
msgid "The password reset link that brought you here was invalid."
|
||||
msgstr ""
|
||||
"El enlace para restablecer la contraseña con el que has accedido no es "
|
||||
"válido."
|
||||
|
||||
#: canaille/core/account.py:744
|
||||
msgid "Your password has been updated successfuly"
|
||||
#: canaille/core/account.py:737
|
||||
msgid "Your password has been updated successfully"
|
||||
msgstr "Tu contraseña se ha actualizado correctamente"
|
||||
|
||||
#: canaille/core/admin.py:23 canaille/templates/partial/users.html:15
|
||||
|
@ -217,7 +211,7 @@ msgstr "Correo electrónico"
|
|||
|
||||
#: canaille/core/admin.py:29 canaille/core/forms.py:56
|
||||
#: canaille/core/forms.py:80 canaille/core/forms.py:168
|
||||
#: canaille/core/forms.py:352
|
||||
#: canaille/core/forms.py:353
|
||||
msgid "jane@doe.com"
|
||||
msgstr "jane@doe.com"
|
||||
|
||||
|
@ -279,12 +273,12 @@ msgstr "Las dos contraseñas no coinciden."
|
|||
msgid "Automatic"
|
||||
msgstr "Automático"
|
||||
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:339
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:340
|
||||
msgid "Username"
|
||||
msgstr "Nombre de usuario"
|
||||
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:311
|
||||
#: canaille/core/forms.py:325 canaille/oidc/forms.py:22
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:312
|
||||
#: canaille/core/forms.py:326 canaille/oidc/forms.py:22
|
||||
#: canaille/templates/partial/groups.html:6
|
||||
#: canaille/templates/partial/oidc/admin/client_list.html:6
|
||||
#: canaille/templates/partial/users.html:12
|
||||
|
@ -323,7 +317,7 @@ msgstr "Nombre a mostrar"
|
|||
msgid "Johnny"
|
||||
msgstr "Johnny"
|
||||
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:345
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:346
|
||||
msgid "Email address"
|
||||
msgstr "Correo electrónico"
|
||||
|
||||
|
@ -396,10 +390,8 @@ msgid "1234"
|
|||
msgstr "1234"
|
||||
|
||||
#: canaille/core/forms.py:240
|
||||
#, fuzzy
|
||||
#| msgid "Department number"
|
||||
msgid "Department"
|
||||
msgstr "Número del departamento"
|
||||
msgstr "Departamento"
|
||||
|
||||
#: canaille/core/forms.py:246
|
||||
msgid "Organization"
|
||||
|
@ -425,22 +417,20 @@ msgstr "Idioma preferido"
|
|||
msgid "users, admins …"
|
||||
msgstr "usuarios, administradores…"
|
||||
|
||||
#: canaille/core/forms.py:289
|
||||
#, fuzzy
|
||||
#| msgid "Account creation"
|
||||
#: canaille/core/forms.py:290
|
||||
msgid "Account expiration"
|
||||
msgstr "Crear cuenta"
|
||||
msgstr "Caducidad de la cuenta"
|
||||
|
||||
#: canaille/core/forms.py:314
|
||||
#: canaille/core/forms.py:315
|
||||
msgid "group"
|
||||
msgstr "grupo"
|
||||
|
||||
#: canaille/core/forms.py:318 canaille/core/forms.py:332
|
||||
#: canaille/core/forms.py:319 canaille/core/forms.py:333
|
||||
#: canaille/templates/partial/groups.html:7
|
||||
msgid "Description"
|
||||
msgstr "Descripción"
|
||||
|
||||
#: canaille/core/forms.py:343
|
||||
#: canaille/core/forms.py:344
|
||||
msgid "Username editable by the invitee"
|
||||
msgstr "Nombre de usuario editable por el invitado"
|
||||
|
||||
|
@ -1073,36 +1063,28 @@ msgid "Submit"
|
|||
msgstr "Enviar"
|
||||
|
||||
#: canaille/templates/profile_settings.html:70
|
||||
#, fuzzy
|
||||
#| msgid "Account deletion"
|
||||
msgid "Account locking"
|
||||
msgstr "Eliminar cuenta"
|
||||
msgstr "Bloqueo de la cuenta"
|
||||
|
||||
#: canaille/templates/profile_settings.html:75
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Are you sure you want to revoke this token? This action is unrevokable."
|
||||
msgid ""
|
||||
"Are you sure you want to lock this account? The user won't be able to login "
|
||||
"until their account is unlocked."
|
||||
msgstr ""
|
||||
"¿Está seguro de que desea revocar este token? Esta acción es irrevocable."
|
||||
"¿Está seguro de que deseas bloquear esta cuenta? El usuario no podrá iniciar "
|
||||
"sesión hasta que la cuenta esté desbloqueada."
|
||||
|
||||
#: canaille/templates/profile_settings.html:77
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Are you sure you want to delete your account? This action is unrevokable "
|
||||
#| "and all your data will be removed forever."
|
||||
msgid ""
|
||||
"Are you sure you want to lock your account? You won't be abel to login until "
|
||||
"your account is unlocked."
|
||||
msgstr ""
|
||||
"¿Seguro que quieres eliminar tu cuenta? Esta acción no se puede deshacer y "
|
||||
"todos tus datos se eliminarán para siempre."
|
||||
"¿Estás seguro de que desea bloquear tu cuenta? No podrás iniciar sesión "
|
||||
"hasta que tu cuenta esté desbloqueada."
|
||||
|
||||
#: canaille/templates/profile_settings.html:83
|
||||
msgid "Lock"
|
||||
msgstr ""
|
||||
msgstr "Bloquear"
|
||||
|
||||
#: canaille/templates/profile_settings.html:124
|
||||
msgid "Send email"
|
||||
|
@ -1153,27 +1135,19 @@ msgstr ""
|
|||
|
||||
#: canaille/templates/profile_settings.html:160
|
||||
msgid "Unlock"
|
||||
msgstr ""
|
||||
msgstr "Desbloquear"
|
||||
|
||||
#: canaille/templates/profile_settings.html:163
|
||||
#, fuzzy
|
||||
#| msgid "This user cannot see this field"
|
||||
msgid "This user account is locked"
|
||||
msgstr "Este usuario no puede ver este campo"
|
||||
msgstr "Esta cuenta de usuario está bloqueada"
|
||||
|
||||
#: canaille/templates/profile_settings.html:166
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "The user will not be able to authenticate unless the password is set."
|
||||
msgid "The user won't be able to connect until their account is unlocked."
|
||||
msgstr ""
|
||||
"El usuario no podrá iniciar sesión a menos que se establezca una contraeña."
|
||||
msgstr "El usuario no podrá conectarse hasta que la cuenta esté desbloqueada."
|
||||
|
||||
#: canaille/templates/profile_settings.html:178
|
||||
#, fuzzy
|
||||
#| msgid "Delete my account"
|
||||
msgid "Lock the account"
|
||||
msgstr "Eliminar mi cuenta"
|
||||
msgstr "Bloquear la cuenta"
|
||||
|
||||
#: canaille/templates/profile_settings.html:185
|
||||
msgid "Delete the user"
|
||||
|
@ -1420,10 +1394,8 @@ msgid "View an authorization"
|
|||
msgstr "Ver una autorización"
|
||||
|
||||
#: canaille/templates/oidc/admin/client_add.html:39
|
||||
#, fuzzy
|
||||
#| msgid "Created"
|
||||
msgid "Create"
|
||||
msgstr "Creado"
|
||||
msgstr "Crear"
|
||||
|
||||
#: canaille/templates/oidc/admin/client_edit.html:5
|
||||
#: canaille/templates/oidc/admin/client_edit.html:54
|
||||
|
@ -1665,7 +1637,7 @@ msgstr "¿Quizás probando con otros criterios?"
|
|||
|
||||
#: canaille/templates/partial/users.html:29
|
||||
msgid "This account is locked"
|
||||
msgstr ""
|
||||
msgstr "Esta cuenta está bloqueada"
|
||||
|
||||
#: canaille/templates/partial/oidc/admin/authorization_list.html:5
|
||||
msgid "Code"
|
||||
|
|
|
@ -9,8 +9,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: contact@yaal.coop\n"
|
||||
"POT-Creation-Date: 2023-05-26 15:31+0200\n"
|
||||
"PO-Revision-Date: 2023-05-27 14:50+0000\n"
|
||||
"POT-Creation-Date: 2023-05-30 09:43+0200\n"
|
||||
"PO-Revision-Date: 2023-05-31 19:28+0000\n"
|
||||
"Last-Translator: Éloi Rivard <eloi.rivard@nubla.fr>\n"
|
||||
"Language-Team: French <https://hosted.weblate.org/projects/canaille/canaille/"
|
||||
"fr/>\n"
|
||||
|
@ -48,7 +48,7 @@ msgid "John Doe"
|
|||
msgstr "Camille Dupont"
|
||||
|
||||
#: canaille/backends/ldap/backend.py:217 canaille/core/forms.py:124
|
||||
#: canaille/core/forms.py:340
|
||||
#: canaille/core/forms.py:341
|
||||
msgid "jdoe"
|
||||
msgstr "cdupont"
|
||||
|
||||
|
@ -74,7 +74,7 @@ msgstr "Vous devriez changer votre mot de passe."
|
|||
msgid "Login failed, please check your information"
|
||||
msgstr "La connexion a échoué, veuillez vérifier vos informations"
|
||||
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:667
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:660
|
||||
#, python-format
|
||||
msgid "Connection successful. Welcome %(user)s"
|
||||
msgstr "Connexion réussie. Bienvenue %(user)s"
|
||||
|
@ -113,7 +113,7 @@ msgid "You are already logged in, you cannot create an account."
|
|||
msgstr "Vous êtes déjà connectés, vous ne pouvez pas créer de compte."
|
||||
|
||||
#: canaille/core/account.py:319 canaille/core/forms.py:263
|
||||
#: canaille/core/forms.py:358 canaille/templates/groups.html:5
|
||||
#: canaille/core/forms.py:359 canaille/templates/groups.html:5
|
||||
#: canaille/templates/groups.html:25 canaille/templates/partial/users.html:18
|
||||
#: canaille/themes/default/base.html:61
|
||||
msgid "Groups"
|
||||
|
@ -124,7 +124,7 @@ msgid "User account creation failed."
|
|||
msgstr "La création du compte utilisateur a échoué."
|
||||
|
||||
#: canaille/core/account.py:346
|
||||
msgid "Your account has been created successfuly."
|
||||
msgid "Your account has been created successfully."
|
||||
msgstr "Votre compte utilisateur a été créé avec succès."
|
||||
|
||||
#: canaille/core/account.py:412
|
||||
|
@ -135,8 +135,8 @@ msgstr "La création du compte utilisateur a réussi."
|
|||
msgid "Profile edition failed."
|
||||
msgstr "L'édition du profil a échoué."
|
||||
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:624
|
||||
msgid "Profile updated successfuly."
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:617
|
||||
msgid "Profile updated successfully."
|
||||
msgstr "Le profil a été mis à jour avec succès."
|
||||
|
||||
#: canaille/core/account.py:531
|
||||
|
@ -167,16 +167,16 @@ msgstr "Le compte a été verrouillé"
|
|||
msgid "The account has been unlocked"
|
||||
msgstr "Le compte a été déverrouillé"
|
||||
|
||||
#: canaille/core/account.py:644
|
||||
#: canaille/core/account.py:637
|
||||
#, python-format
|
||||
msgid "The user %(user)s has been sucessfuly deleted"
|
||||
msgstr "L'utilisateur %(user)s a bien été supprimé"
|
||||
|
||||
#: canaille/core/account.py:684
|
||||
#: canaille/core/account.py:677
|
||||
msgid "Could not send the password reset link."
|
||||
msgstr "Impossible d'envoyer le lien de réinitialisation."
|
||||
|
||||
#: canaille/core/account.py:688
|
||||
#: canaille/core/account.py:681
|
||||
msgid ""
|
||||
"A password reset link has been sent at your email address. You should "
|
||||
"receive it within a few minutes."
|
||||
|
@ -184,7 +184,7 @@ msgstr ""
|
|||
"Un lien de ré-initialisation de votre mot de passe vous a été envoyé par "
|
||||
"courriel. Vous devriez le recevoir d'ici quelques minutes."
|
||||
|
||||
#: canaille/core/account.py:699
|
||||
#: canaille/core/account.py:692
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The user '%(user)s' does not have permissions to update their password. We "
|
||||
|
@ -193,18 +193,18 @@ msgstr ""
|
|||
"L'utilisateur '%(user)s' n'a pas la permission d'éditer son mot de passe. "
|
||||
"Nous ne pouvons pas lui envoyer un courriel de réinitialisation."
|
||||
|
||||
#: canaille/core/account.py:714
|
||||
#: canaille/core/account.py:707
|
||||
msgid "We encountered an issue while we sent the password recovery email."
|
||||
msgstr ""
|
||||
"Nous avons rencontré un problème lors de l'envoi du courriel de "
|
||||
"réinitialisation de mot de passe."
|
||||
|
||||
#: canaille/core/account.py:735
|
||||
#: canaille/core/account.py:728
|
||||
msgid "The password reset link that brought you here was invalid."
|
||||
msgstr "Le lien de réinitialisation qui vous a amené ici est invalide."
|
||||
|
||||
#: canaille/core/account.py:744
|
||||
msgid "Your password has been updated successfuly"
|
||||
#: canaille/core/account.py:737
|
||||
msgid "Your password has been updated successfully"
|
||||
msgstr "Votre mot de passe a correctement été mis à jour"
|
||||
|
||||
#: canaille/core/admin.py:23 canaille/templates/partial/users.html:15
|
||||
|
@ -213,7 +213,7 @@ msgstr "Courriel"
|
|||
|
||||
#: canaille/core/admin.py:29 canaille/core/forms.py:56
|
||||
#: canaille/core/forms.py:80 canaille/core/forms.py:168
|
||||
#: canaille/core/forms.py:352
|
||||
#: canaille/core/forms.py:353
|
||||
msgid "jane@doe.com"
|
||||
msgstr "camille@dupont.fr"
|
||||
|
||||
|
@ -275,12 +275,12 @@ msgstr "Le mot de passe et sa confirmation ne correspondent pas."
|
|||
msgid "Automatic"
|
||||
msgstr "Automatique"
|
||||
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:339
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:340
|
||||
msgid "Username"
|
||||
msgstr "Identifiant"
|
||||
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:311
|
||||
#: canaille/core/forms.py:325 canaille/oidc/forms.py:22
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:312
|
||||
#: canaille/core/forms.py:326 canaille/oidc/forms.py:22
|
||||
#: canaille/templates/partial/groups.html:6
|
||||
#: canaille/templates/partial/oidc/admin/client_list.html:6
|
||||
#: canaille/templates/partial/users.html:12
|
||||
|
@ -319,7 +319,7 @@ msgstr "Nom d'affichage"
|
|||
msgid "Johnny"
|
||||
msgstr "Martin.D"
|
||||
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:345
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:346
|
||||
msgid "Email address"
|
||||
msgstr "Courriel"
|
||||
|
||||
|
@ -419,20 +419,20 @@ msgstr "Langue préférée"
|
|||
msgid "users, admins …"
|
||||
msgstr "utilisateurs, administrateurs …"
|
||||
|
||||
#: canaille/core/forms.py:289
|
||||
#: canaille/core/forms.py:290
|
||||
msgid "Account expiration"
|
||||
msgstr "Expiration du compte"
|
||||
|
||||
#: canaille/core/forms.py:314
|
||||
#: canaille/core/forms.py:315
|
||||
msgid "group"
|
||||
msgstr "groupe"
|
||||
|
||||
#: canaille/core/forms.py:318 canaille/core/forms.py:332
|
||||
#: canaille/core/forms.py:319 canaille/core/forms.py:333
|
||||
#: canaille/templates/partial/groups.html:7
|
||||
msgid "Description"
|
||||
msgstr "Description"
|
||||
|
||||
#: canaille/core/forms.py:343
|
||||
#: canaille/core/forms.py:344
|
||||
msgid "Username editable by the invitee"
|
||||
msgstr "L'identifiant sera éditable par la personne invitée"
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2023-05-26 15:31+0200\n"
|
||||
"POT-Creation-Date: 2023-05-30 09:43+0200\n"
|
||||
"PO-Revision-Date: 2023-02-19 22:35+0000\n"
|
||||
"Last-Translator: Jesús P Rey <i18n@chuso.net>\n"
|
||||
"Language-Team: Galician <https://hosted.weblate.org/projects/canaille/"
|
||||
|
@ -45,7 +45,7 @@ msgid "John Doe"
|
|||
msgstr "John Doe"
|
||||
|
||||
#: canaille/backends/ldap/backend.py:217 canaille/core/forms.py:124
|
||||
#: canaille/core/forms.py:340
|
||||
#: canaille/core/forms.py:341
|
||||
msgid "jdoe"
|
||||
msgstr "jdoe"
|
||||
|
||||
|
@ -73,7 +73,7 @@ msgstr ""
|
|||
msgid "Login failed, please check your information"
|
||||
msgstr "Fallou o inicio de sesión, comproba as túas credenciais"
|
||||
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:667
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:660
|
||||
#, python-format
|
||||
msgid "Connection successful. Welcome %(user)s"
|
||||
msgstr "Acceso correcto. Benvido/a %(user)s"
|
||||
|
@ -112,7 +112,7 @@ msgid "You are already logged in, you cannot create an account."
|
|||
msgstr "Xa iniciaches sesión, non podes crear unha conta."
|
||||
|
||||
#: canaille/core/account.py:319 canaille/core/forms.py:263
|
||||
#: canaille/core/forms.py:358 canaille/templates/groups.html:5
|
||||
#: canaille/core/forms.py:359 canaille/templates/groups.html:5
|
||||
#: canaille/templates/groups.html:25 canaille/templates/partial/users.html:18
|
||||
#: canaille/themes/default/base.html:61
|
||||
msgid "Groups"
|
||||
|
@ -125,7 +125,7 @@ msgstr "Fallou a creación da conta de usuario."
|
|||
#: canaille/core/account.py:346
|
||||
#, fuzzy
|
||||
#| msgid "You account has been created successfuly."
|
||||
msgid "Your account has been created successfuly."
|
||||
msgid "Your account has been created successfully."
|
||||
msgstr "A túa conta creouse correctamente."
|
||||
|
||||
#: canaille/core/account.py:412
|
||||
|
@ -136,8 +136,10 @@ msgstr "Conta creada correctamente."
|
|||
msgid "Profile edition failed."
|
||||
msgstr "Fallou a actualización do perfil."
|
||||
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:624
|
||||
msgid "Profile updated successfuly."
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:617
|
||||
#, fuzzy
|
||||
#| msgid "Profile updated successfuly."
|
||||
msgid "Profile updated successfully."
|
||||
msgstr "Perfil actualizado correctamente."
|
||||
|
||||
#: canaille/core/account.py:531
|
||||
|
@ -172,16 +174,16 @@ msgstr "O acceso foi revogado"
|
|||
msgid "The account has been unlocked"
|
||||
msgstr "O acceso foi revogado"
|
||||
|
||||
#: canaille/core/account.py:644
|
||||
#: canaille/core/account.py:637
|
||||
#, python-format
|
||||
msgid "The user %(user)s has been sucessfuly deleted"
|
||||
msgstr "O usuario %(user)s eliminouse correctamente"
|
||||
|
||||
#: canaille/core/account.py:684
|
||||
#: canaille/core/account.py:677
|
||||
msgid "Could not send the password reset link."
|
||||
msgstr "Non foi posible enviar o email para restablecer o contrasinal."
|
||||
|
||||
#: canaille/core/account.py:688
|
||||
#: canaille/core/account.py:681
|
||||
msgid ""
|
||||
"A password reset link has been sent at your email address. You should "
|
||||
"receive it within a few minutes."
|
||||
|
@ -189,7 +191,7 @@ msgstr ""
|
|||
"Enviouse unha ligazón para restablecer o contrasinal ó teu correo "
|
||||
"electrónico. Deberías recibilo nun prazo de 10 minutos."
|
||||
|
||||
#: canaille/core/account.py:699
|
||||
#: canaille/core/account.py:692
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The user '%(user)s' does not have permissions to update their password. We "
|
||||
|
@ -198,17 +200,19 @@ msgstr ""
|
|||
"O usuario '%(user)s' non ten permiso para actualizar o seu contrasinal. Non "
|
||||
"é posible enviarlle un email para que restableza o seu contrasinal."
|
||||
|
||||
#: canaille/core/account.py:714
|
||||
#: canaille/core/account.py:707
|
||||
msgid "We encountered an issue while we sent the password recovery email."
|
||||
msgstr "Produciuse un problema enviando o email para recuperar o contrasinal."
|
||||
|
||||
#: canaille/core/account.py:735
|
||||
#: canaille/core/account.py:728
|
||||
msgid "The password reset link that brought you here was invalid."
|
||||
msgstr ""
|
||||
"A ligazón para restablecer o contrasinal co que accediches non é válido."
|
||||
|
||||
#: canaille/core/account.py:744
|
||||
msgid "Your password has been updated successfuly"
|
||||
#: canaille/core/account.py:737
|
||||
#, fuzzy
|
||||
#| msgid "Your password has been updated successfuly"
|
||||
msgid "Your password has been updated successfully"
|
||||
msgstr "O teu contrasinal actualizouse correctamente"
|
||||
|
||||
#: canaille/core/admin.py:23 canaille/templates/partial/users.html:15
|
||||
|
@ -217,7 +221,7 @@ msgstr "Correo electrónico"
|
|||
|
||||
#: canaille/core/admin.py:29 canaille/core/forms.py:56
|
||||
#: canaille/core/forms.py:80 canaille/core/forms.py:168
|
||||
#: canaille/core/forms.py:352
|
||||
#: canaille/core/forms.py:353
|
||||
msgid "jane@doe.com"
|
||||
msgstr "jane@doe.com"
|
||||
|
||||
|
@ -281,12 +285,12 @@ msgstr "Os dous contrasinais non coinciden."
|
|||
msgid "Automatic"
|
||||
msgstr "Automático"
|
||||
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:339
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:340
|
||||
msgid "Username"
|
||||
msgstr "Nome de usuario"
|
||||
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:311
|
||||
#: canaille/core/forms.py:325 canaille/oidc/forms.py:22
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:312
|
||||
#: canaille/core/forms.py:326 canaille/oidc/forms.py:22
|
||||
#: canaille/templates/partial/groups.html:6
|
||||
#: canaille/templates/partial/oidc/admin/client_list.html:6
|
||||
#: canaille/templates/partial/users.html:12
|
||||
|
@ -325,7 +329,7 @@ msgstr "Nome a amosar"
|
|||
msgid "Johnny"
|
||||
msgstr "Johnny"
|
||||
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:345
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:346
|
||||
msgid "Email address"
|
||||
msgstr "Correo electrónico"
|
||||
|
||||
|
@ -433,22 +437,22 @@ msgstr "Idioma preferido"
|
|||
msgid "users, admins …"
|
||||
msgstr "usuarios, administradores…"
|
||||
|
||||
#: canaille/core/forms.py:289
|
||||
#: canaille/core/forms.py:290
|
||||
#, fuzzy
|
||||
#| msgid "Account creation"
|
||||
msgid "Account expiration"
|
||||
msgstr "Creación de conta"
|
||||
|
||||
#: canaille/core/forms.py:314
|
||||
#: canaille/core/forms.py:315
|
||||
msgid "group"
|
||||
msgstr "grupo"
|
||||
|
||||
#: canaille/core/forms.py:318 canaille/core/forms.py:332
|
||||
#: canaille/core/forms.py:319 canaille/core/forms.py:333
|
||||
#: canaille/templates/partial/groups.html:7
|
||||
msgid "Description"
|
||||
msgstr "Descripción"
|
||||
|
||||
#: canaille/core/forms.py:343
|
||||
#: canaille/core/forms.py:344
|
||||
msgid "Username editable by the invitee"
|
||||
msgstr "Nome de usuario editable polo invitado"
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2023-05-26 15:31+0200\n"
|
||||
"POT-Creation-Date: 2023-05-30 09:43+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -42,7 +42,7 @@ msgid "John Doe"
|
|||
msgstr ""
|
||||
|
||||
#: canaille/backends/ldap/backend.py:217 canaille/core/forms.py:124
|
||||
#: canaille/core/forms.py:340
|
||||
#: canaille/core/forms.py:341
|
||||
msgid "jdoe"
|
||||
msgstr ""
|
||||
|
||||
|
@ -68,7 +68,7 @@ msgstr ""
|
|||
msgid "Login failed, please check your information"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:667
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:660
|
||||
#, python-format
|
||||
msgid "Connection successful. Welcome %(user)s"
|
||||
msgstr ""
|
||||
|
@ -105,7 +105,7 @@ msgid "You are already logged in, you cannot create an account."
|
|||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:319 canaille/core/forms.py:263
|
||||
#: canaille/core/forms.py:358 canaille/templates/groups.html:5
|
||||
#: canaille/core/forms.py:359 canaille/templates/groups.html:5
|
||||
#: canaille/templates/groups.html:25 canaille/templates/partial/users.html:18
|
||||
#: canaille/themes/default/base.html:61
|
||||
msgid "Groups"
|
||||
|
@ -116,7 +116,7 @@ msgid "User account creation failed."
|
|||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:346
|
||||
msgid "Your account has been created successfuly."
|
||||
msgid "Your account has been created successfully."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:412
|
||||
|
@ -127,8 +127,8 @@ msgstr ""
|
|||
msgid "Profile edition failed."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:624
|
||||
msgid "Profile updated successfuly."
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:617
|
||||
msgid "Profile updated successfully."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:531
|
||||
|
@ -155,38 +155,38 @@ msgstr ""
|
|||
msgid "The account has been unlocked"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:644
|
||||
#: canaille/core/account.py:637
|
||||
#, python-format
|
||||
msgid "The user %(user)s has been sucessfuly deleted"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:684
|
||||
#: canaille/core/account.py:677
|
||||
msgid "Could not send the password reset link."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:688
|
||||
#: canaille/core/account.py:681
|
||||
msgid ""
|
||||
"A password reset link has been sent at your email address. You should "
|
||||
"receive it within a few minutes."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:699
|
||||
#: canaille/core/account.py:692
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The user '%(user)s' does not have permissions to update their password. "
|
||||
"We cannot send a password reset email."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:714
|
||||
#: canaille/core/account.py:707
|
||||
msgid "We encountered an issue while we sent the password recovery email."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:735
|
||||
#: canaille/core/account.py:728
|
||||
msgid "The password reset link that brought you here was invalid."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/account.py:744
|
||||
msgid "Your password has been updated successfuly"
|
||||
#: canaille/core/account.py:737
|
||||
msgid "Your password has been updated successfully"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/admin.py:23 canaille/templates/partial/users.html:15
|
||||
|
@ -195,7 +195,7 @@ msgstr ""
|
|||
|
||||
#: canaille/core/admin.py:29 canaille/core/forms.py:56
|
||||
#: canaille/core/forms.py:80 canaille/core/forms.py:168
|
||||
#: canaille/core/forms.py:352
|
||||
#: canaille/core/forms.py:353
|
||||
msgid "jane@doe.com"
|
||||
msgstr ""
|
||||
|
||||
|
@ -257,12 +257,12 @@ msgstr ""
|
|||
msgid "Automatic"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:339
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:340
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:311
|
||||
#: canaille/core/forms.py:325 canaille/oidc/forms.py:22
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:312
|
||||
#: canaille/core/forms.py:326 canaille/oidc/forms.py:22
|
||||
#: canaille/templates/partial/groups.html:6
|
||||
#: canaille/templates/partial/oidc/admin/client_list.html:6
|
||||
#: canaille/templates/partial/users.html:12
|
||||
|
@ -301,7 +301,7 @@ msgstr ""
|
|||
msgid "Johnny"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:345
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:346
|
||||
msgid "Email address"
|
||||
msgstr ""
|
||||
|
||||
|
@ -400,20 +400,20 @@ msgstr ""
|
|||
msgid "users, admins …"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/forms.py:289
|
||||
#: canaille/core/forms.py:290
|
||||
msgid "Account expiration"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/forms.py:314
|
||||
#: canaille/core/forms.py:315
|
||||
msgid "group"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/forms.py:318 canaille/core/forms.py:332
|
||||
#: canaille/core/forms.py:319 canaille/core/forms.py:333
|
||||
#: canaille/templates/partial/groups.html:7
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/core/forms.py:343
|
||||
#: canaille/core/forms.py:344
|
||||
msgid "Username editable by the invitee"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2023-05-26 15:31+0200\n"
|
||||
"POT-Creation-Date: 2023-05-30 09:43+0200\n"
|
||||
"PO-Revision-Date: 2023-03-24 22:36+0000\n"
|
||||
"Last-Translator: Sofi <sofi+git@mailbox.org>\n"
|
||||
"Language-Team: Norwegian Bokmål <https://hosted.weblate.org/projects/"
|
||||
|
@ -46,7 +46,7 @@ msgid "John Doe"
|
|||
msgstr "Ola Nordmann"
|
||||
|
||||
#: canaille/backends/ldap/backend.py:217 canaille/core/forms.py:124
|
||||
#: canaille/core/forms.py:340
|
||||
#: canaille/core/forms.py:341
|
||||
msgid "jdoe"
|
||||
msgstr "onord"
|
||||
|
||||
|
@ -75,7 +75,7 @@ msgstr ""
|
|||
msgid "Login failed, please check your information"
|
||||
msgstr "Kunne ikke logge inn. Sjekk at alt er riktig."
|
||||
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:667
|
||||
#: canaille/core/account.py:136 canaille/core/account.py:660
|
||||
#, fuzzy, python-format
|
||||
msgid "Connection successful. Welcome %(user)s"
|
||||
msgstr "Velkommen %(user)s"
|
||||
|
@ -114,7 +114,7 @@ msgid "You are already logged in, you cannot create an account."
|
|||
msgstr "Du er allerede innlogget. Du kan ikke opprette konto."
|
||||
|
||||
#: canaille/core/account.py:319 canaille/core/forms.py:263
|
||||
#: canaille/core/forms.py:358 canaille/templates/groups.html:5
|
||||
#: canaille/core/forms.py:359 canaille/templates/groups.html:5
|
||||
#: canaille/templates/groups.html:25 canaille/templates/partial/users.html:18
|
||||
#: canaille/themes/default/base.html:61
|
||||
msgid "Groups"
|
||||
|
@ -127,7 +127,7 @@ msgstr "Kunne ikke opprette brukerkonto."
|
|||
|
||||
#: canaille/core/account.py:346
|
||||
#, fuzzy
|
||||
msgid "Your account has been created successfuly."
|
||||
msgid "Your account has been created successfully."
|
||||
msgstr "Kontoen din ble opprettet."
|
||||
|
||||
#: canaille/core/account.py:412
|
||||
|
@ -140,8 +140,10 @@ msgstr "Brukerkonto opprettet."
|
|||
msgid "Profile edition failed."
|
||||
msgstr "Kunne ikke redigere profil."
|
||||
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:624
|
||||
msgid "Profile updated successfuly."
|
||||
#: canaille/core/account.py:495 canaille/core/account.py:617
|
||||
#, fuzzy
|
||||
#| msgid "Profile updated successfuly."
|
||||
msgid "Profile updated successfully."
|
||||
msgstr "Profil redigert."
|
||||
|
||||
#: canaille/core/account.py:531
|
||||
|
@ -176,23 +178,23 @@ msgstr "Tilgangen har blitt tilbakekalt"
|
|||
msgid "The account has been unlocked"
|
||||
msgstr "Tilgangen har blitt tilbakekalt"
|
||||
|
||||
#: canaille/core/account.py:644
|
||||
#: canaille/core/account.py:637
|
||||
#, python-format
|
||||
msgid "The user %(user)s has been sucessfuly deleted"
|
||||
msgstr "Brukeren %(user)s ble slettet"
|
||||
|
||||
#: canaille/core/account.py:684
|
||||
#: canaille/core/account.py:677
|
||||
msgid "Could not send the password reset link."
|
||||
msgstr "Kunne ikke sende lenke for passordgjenoppretting."
|
||||
|
||||
#: canaille/core/account.py:688
|
||||
#: canaille/core/account.py:681
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"A password reset link has been sent at your email address. You should "
|
||||
"receive it within a few minutes."
|
||||
msgstr "Lenke for tilbakestilling av passord sendt til din e-postadresse."
|
||||
|
||||
#: canaille/core/account.py:699
|
||||
#: canaille/core/account.py:692
|
||||
#, fuzzy, python-format
|
||||
msgid ""
|
||||
"The user '%(user)s' does not have permissions to update their password. We "
|
||||
|
@ -201,18 +203,18 @@ msgstr ""
|
|||
"Brukeren «%(user)s» har ikke tilgang til å oppdatere passordet sitt. Kan "
|
||||
"ikke sende e-post om tilbakestilling av passord."
|
||||
|
||||
#: canaille/core/account.py:714
|
||||
#: canaille/core/account.py:707
|
||||
#, fuzzy
|
||||
msgid "We encountered an issue while we sent the password recovery email."
|
||||
msgstr "Kunne ikke sende e-post for gjenoppretting av passord."
|
||||
|
||||
#: canaille/core/account.py:735
|
||||
#: canaille/core/account.py:728
|
||||
msgid "The password reset link that brought you here was invalid."
|
||||
msgstr "Passordtilbakestillingslenken som tok deg hit er ikke gyldig."
|
||||
|
||||
#: canaille/core/account.py:744
|
||||
#: canaille/core/account.py:737
|
||||
#, fuzzy
|
||||
msgid "Your password has been updated successfuly"
|
||||
msgid "Your password has been updated successfully"
|
||||
msgstr "Passordet ditt er oppdatert."
|
||||
|
||||
#: canaille/core/admin.py:23 canaille/templates/partial/users.html:15
|
||||
|
@ -222,7 +224,7 @@ msgstr "E-postadresse"
|
|||
|
||||
#: canaille/core/admin.py:29 canaille/core/forms.py:56
|
||||
#: canaille/core/forms.py:80 canaille/core/forms.py:168
|
||||
#: canaille/core/forms.py:352
|
||||
#: canaille/core/forms.py:353
|
||||
msgid "jane@doe.com"
|
||||
msgstr "ola@nordmann.no"
|
||||
|
||||
|
@ -290,12 +292,12 @@ msgstr "Passordet og bekreftelsen samsvarer ikke."
|
|||
msgid "Automatic"
|
||||
msgstr "Automatisk"
|
||||
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:339
|
||||
#: canaille/core/forms.py:123 canaille/core/forms.py:340
|
||||
msgid "Username"
|
||||
msgstr "Brukernavn"
|
||||
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:311
|
||||
#: canaille/core/forms.py:325 canaille/oidc/forms.py:22
|
||||
#: canaille/core/forms.py:127 canaille/core/forms.py:312
|
||||
#: canaille/core/forms.py:326 canaille/oidc/forms.py:22
|
||||
#: canaille/templates/partial/groups.html:6
|
||||
#: canaille/templates/partial/oidc/admin/client_list.html:6
|
||||
#: canaille/templates/partial/users.html:12
|
||||
|
@ -334,7 +336,7 @@ msgstr "Visningsnavn"
|
|||
msgid "Johnny"
|
||||
msgstr "Ola"
|
||||
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:345
|
||||
#: canaille/core/forms.py:158 canaille/core/forms.py:346
|
||||
msgid "Email address"
|
||||
msgstr "E-postadresse"
|
||||
|
||||
|
@ -439,22 +441,22 @@ msgstr "Foretrukket språk"
|
|||
msgid "users, admins …"
|
||||
msgstr "brukere, administratorer …"
|
||||
|
||||
#: canaille/core/forms.py:289
|
||||
#: canaille/core/forms.py:290
|
||||
#, fuzzy
|
||||
#| msgid "Account creation"
|
||||
msgid "Account expiration"
|
||||
msgstr "Kontoopprettelse"
|
||||
|
||||
#: canaille/core/forms.py:314
|
||||
#: canaille/core/forms.py:315
|
||||
msgid "group"
|
||||
msgstr "gruppe"
|
||||
|
||||
#: canaille/core/forms.py:318 canaille/core/forms.py:332
|
||||
#: canaille/core/forms.py:319 canaille/core/forms.py:333
|
||||
#: canaille/templates/partial/groups.html:7
|
||||
msgid "Description"
|
||||
msgstr "Beskrivelse"
|
||||
|
||||
#: canaille/core/forms.py:343
|
||||
#: canaille/core/forms.py:344
|
||||
msgid "Username editable by the invitee"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@ FAVICON = "/static/img/canaille-c.png"
|
|||
# If unset, language is detected
|
||||
# LANGUAGE = "en"
|
||||
|
||||
# The timezone in which datetimes will be displayed to the users.
|
||||
# If unset, the server timezone will be used.
|
||||
# TIMEZONE = UTC
|
||||
|
||||
# If you have a sentry instance, you can set its dsn here:
|
||||
# SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@ FAVICON = "/static/img/canaille-c.png"
|
|||
# If unset, language is detected
|
||||
# LANGUAGE = "en"
|
||||
|
||||
# The timezone in which datetimes will be displayed to the users.
|
||||
# If unset, the server timezone will be used.
|
||||
# TIMEZONE = UTC
|
||||
|
||||
# If you have a sentry instance, you can set its dsn here:
|
||||
# SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
|
||||
|
||||
|
|
36
doc/backends.rst
Normal file
36
doc/backends.rst
Normal file
|
@ -0,0 +1,36 @@
|
|||
Backends
|
||||
#############
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
LDAP
|
||||
====
|
||||
|
||||
Canaille can integrate with several OpenLDAP overlays:
|
||||
|
||||
memberof / refint
|
||||
-----------------
|
||||
|
||||
*memberof* and *refint* overlays are needed for the Canaille group membership to work correctly.
|
||||
|
||||
Here is a configuration example compatible with canaille:
|
||||
|
||||
.. literalinclude :: ../demo/ldif/memberof-config.ldif
|
||||
:language: ldif
|
||||
|
||||
.. literalinclude :: ../demo/ldif/refint-config.ldif
|
||||
:language: ldif
|
||||
|
||||
ppolicy
|
||||
-------
|
||||
|
||||
If *ppolicy* is configured and the ``pwdEndTime`` attribute is available (since OpenLDAP 2.6), then account locking support will be enabled in canaille. To allow users to manage account expiration, they need to have a *write* permission on the ``lock_date`` attribute.
|
||||
|
||||
Here is a configuration example compatible with canaille:
|
||||
|
||||
.. literalinclude :: ../demo/ldif/ppolicy-config.ldif
|
||||
:language: ldif
|
||||
|
||||
.. literalinclude :: ../demo/ldif/ppolicy.ldif
|
||||
:language: ldif
|
|
@ -2,15 +2,12 @@
|
|||
import datetime
|
||||
import os
|
||||
import sys
|
||||
from importlib import metadata
|
||||
from unittest import mock
|
||||
|
||||
sys.path.insert(0, os.path.abspath(".."))
|
||||
sys.path.insert(0, os.path.abspath("../canaille"))
|
||||
|
||||
if sys.version_info[:2] >= (3, 8):
|
||||
from importlib import metadata
|
||||
else:
|
||||
import importlib_metadata as metadata
|
||||
|
||||
# Readthedocs does not support C modules, so
|
||||
# we have to mock them.
|
||||
|
|
|
@ -34,6 +34,9 @@ Canaille is based on Flask, so any `flask configuration <https://flask.palletspr
|
|||
:LANGUAGE:
|
||||
*Optional.* The locale code of the language to use. If not set, the language of the browser will be used.
|
||||
|
||||
:TIMEZONE:
|
||||
*Optional.* The timezone in which datetimes will be displayed to the users. If unset, the server timezone will be used.
|
||||
|
||||
:SENTRY_DSN:
|
||||
*Optional.* A DSN to a sentry instance.
|
||||
This needs the ``sentry_sdk`` python package to be installed.
|
||||
|
|
|
@ -34,6 +34,7 @@ Table of contents
|
|||
:maxdepth: 2
|
||||
|
||||
install
|
||||
backends
|
||||
configuration
|
||||
contributing
|
||||
specifications
|
||||
|
|
466
poetry.lock
generated
466
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -5,7 +5,7 @@ build-backend = "poetry.masonry.api"
|
|||
[tool]
|
||||
[tool.poetry]
|
||||
name = "Canaille"
|
||||
version = "0.0.25"
|
||||
version = "0.0.26"
|
||||
description = "Minimalistic identity provider"
|
||||
license = "MIT"
|
||||
keywords = ["oidc", "oauth", "oauth2", "openid", "identity"]
|
||||
|
@ -36,7 +36,7 @@ readme = "README.md"
|
|||
include = ["canaille/translations/*/LC_MESSAGES/*.mo"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.7, <4"
|
||||
python = ">=3.8, <4"
|
||||
authlib = ">1,<2"
|
||||
click = "<9"
|
||||
email_validator = "<3"
|
||||
|
@ -46,6 +46,7 @@ flask-themer = "<2"
|
|||
flask-wtf = "<2"
|
||||
pycountry = "^22.3.5"
|
||||
python-ldap = "<4"
|
||||
pytz = "^2023.3"
|
||||
toml = "<1"
|
||||
wtforms = "<4"
|
||||
|
||||
|
@ -58,6 +59,7 @@ optional = true
|
|||
"sphinx" = "*"
|
||||
"sphinx-rtd-theme" = "*"
|
||||
"sphinx-issues" = "*"
|
||||
pygments-ldif = "^1.0.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
coverage = {version = "*", extras=["toml"]}
|
||||
|
@ -74,6 +76,7 @@ pytest-httpserver = "*"
|
|||
slapd = "*"
|
||||
smtpdfix = "*"
|
||||
pytest-flask = "^1.2.0"
|
||||
pytest-lazy-fixture = "^0.6.3"
|
||||
|
||||
[tool.poetry.group.demo]
|
||||
optional = true
|
||||
|
@ -120,7 +123,6 @@ isolated_build = true
|
|||
skipsdist = true
|
||||
envlist =
|
||||
style
|
||||
py37
|
||||
py38
|
||||
py39
|
||||
py310
|
||||
|
|
|
@ -8,7 +8,7 @@ def test_check_command(testclient):
|
|||
|
||||
|
||||
def test_check_command_fail(testclient):
|
||||
testclient.app.config["SMTP"]["HOST"] = "ldap://invalid-ldap.com"
|
||||
testclient.app.config["SMTP"]["HOST"] = "invalid-domain.com"
|
||||
runner = testclient.app.test_cli_runner()
|
||||
res = runner.invoke(cli, ["check"])
|
||||
assert res.exit_code == 1, res.stdout
|
||||
|
|
|
@ -7,6 +7,11 @@ from canaille.app.flask import set_parameter_in_url_query
|
|||
from flask_webtest import TestApp
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def configuration(ldap_configuration):
|
||||
yield ldap_configuration
|
||||
|
||||
|
||||
def test_set_parameter_in_url_query():
|
||||
assert (
|
||||
set_parameter_in_url_query("https://auth.mydomain.tld", foo="bar")
|
||||
|
|
186
tests/app/test_forms.py
Normal file
186
tests/app/test_forms.py
Normal file
|
@ -0,0 +1,186 @@
|
|||
import datetime
|
||||
|
||||
import wtforms
|
||||
from babel.dates import LOCALTZ
|
||||
from canaille.app.forms import DateTimeUTCField
|
||||
from flask import current_app
|
||||
from werkzeug.datastructures import ImmutableMultiDict
|
||||
|
||||
|
||||
def test_datetime_utc_field_no_timezone_is_local_timezone(testclient):
|
||||
del current_app.config["TIMEZONE"]
|
||||
|
||||
offset = LOCALTZ.utcoffset(datetime.datetime.utcnow())
|
||||
|
||||
class TestForm(wtforms.Form):
|
||||
dt = DateTimeUTCField()
|
||||
|
||||
form = TestForm()
|
||||
form.validate()
|
||||
assert form.dt.data is None
|
||||
|
||||
utc_date = datetime.datetime(2023, 6, 1, 12, tzinfo=datetime.timezone.utc)
|
||||
locale_date = datetime.datetime(2023, 6, 1, 12) + offset
|
||||
rendered_locale_date = locale_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
rendered_locale_date_form = locale_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
request_form = ImmutableMultiDict({"dt": rendered_locale_date_form})
|
||||
form = TestForm(request_form)
|
||||
assert form.validate()
|
||||
assert form.dt.data == utc_date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_locale_date_form}">'
|
||||
)
|
||||
|
||||
form = TestForm(data={"dt": utc_date})
|
||||
assert form.validate()
|
||||
assert form.dt.data == utc_date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_locale_date}">'
|
||||
)
|
||||
|
||||
class Foobar:
|
||||
dt = utc_date
|
||||
|
||||
form = TestForm(obj=Foobar())
|
||||
assert form.validate()
|
||||
assert form.dt.data == utc_date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_locale_date}">'
|
||||
)
|
||||
|
||||
|
||||
def test_datetime_utc_field_utc(testclient):
|
||||
current_app.config["TIMEZONE"] = "UTC"
|
||||
|
||||
class TestForm(wtforms.Form):
|
||||
dt = DateTimeUTCField()
|
||||
|
||||
form = TestForm()
|
||||
form.validate()
|
||||
assert form.dt.data is None
|
||||
|
||||
date = datetime.datetime(2023, 6, 1, 12, tzinfo=datetime.timezone.utc)
|
||||
rendered_date = date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
rendered_date_form = date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
request_form = ImmutableMultiDict({"dt": rendered_date_form})
|
||||
form = TestForm(request_form)
|
||||
assert form.validate()
|
||||
assert form.dt.data == date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_date_form}">'
|
||||
)
|
||||
|
||||
form = TestForm(data={"dt": date})
|
||||
assert form.validate()
|
||||
assert form.dt.data == date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_date}">'
|
||||
)
|
||||
|
||||
class Foobar:
|
||||
dt = date
|
||||
|
||||
form = TestForm(obj=Foobar())
|
||||
assert form.validate()
|
||||
assert form.dt.data == date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_date}">'
|
||||
)
|
||||
|
||||
|
||||
def test_datetime_utc_field_japan_timezone(testclient):
|
||||
current_app.config["TIMEZONE"] = "Japan"
|
||||
|
||||
class TestForm(wtforms.Form):
|
||||
dt = DateTimeUTCField()
|
||||
|
||||
form = TestForm()
|
||||
form.validate()
|
||||
assert form.dt.data is None
|
||||
|
||||
utc_date = datetime.datetime(2023, 6, 1, 12, tzinfo=datetime.timezone.utc)
|
||||
locale_date = datetime.datetime(2023, 6, 1, 21)
|
||||
rendered_locale_date = locale_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
rendered_locale_date_form = locale_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
request_form = ImmutableMultiDict({"dt": rendered_locale_date_form})
|
||||
form = TestForm(request_form)
|
||||
assert form.validate()
|
||||
assert form.dt.data == utc_date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_locale_date_form}">'
|
||||
)
|
||||
|
||||
form = TestForm(data={"dt": utc_date})
|
||||
assert form.validate()
|
||||
assert form.dt.data == utc_date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_locale_date}">'
|
||||
)
|
||||
|
||||
class Foobar:
|
||||
dt = utc_date
|
||||
|
||||
form = TestForm(obj=Foobar())
|
||||
assert form.validate()
|
||||
assert form.dt.data == utc_date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_locale_date}">'
|
||||
)
|
||||
|
||||
|
||||
def test_datetime_utc_field_invalid_timezone(testclient):
|
||||
current_app.config["TIMEZONE"] = "invalid"
|
||||
|
||||
offset = LOCALTZ.utcoffset(datetime.datetime.utcnow())
|
||||
|
||||
class TestForm(wtforms.Form):
|
||||
dt = DateTimeUTCField()
|
||||
|
||||
form = TestForm()
|
||||
form.validate()
|
||||
assert form.dt.data is None
|
||||
|
||||
utc_date = datetime.datetime(2023, 6, 1, 12, tzinfo=datetime.timezone.utc)
|
||||
locale_date = datetime.datetime(2023, 6, 1, 12) + offset
|
||||
rendered_locale_date = locale_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
rendered_locale_date_form = locale_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
request_form = ImmutableMultiDict({"dt": rendered_locale_date_form})
|
||||
form = TestForm(request_form)
|
||||
assert form.validate()
|
||||
assert form.dt.data == utc_date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_locale_date_form}">'
|
||||
)
|
||||
|
||||
form = TestForm(data={"dt": utc_date})
|
||||
assert form.validate()
|
||||
assert form.dt.data == utc_date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_locale_date}">'
|
||||
)
|
||||
|
||||
class Foobar:
|
||||
dt = utc_date
|
||||
|
||||
form = TestForm(obj=Foobar())
|
||||
assert form.validate()
|
||||
assert form.dt.data == utc_date
|
||||
assert (
|
||||
form.dt()
|
||||
== f'<input id="dt" name="dt" type="datetime-local" value="{rendered_locale_date}">'
|
||||
)
|
|
@ -24,7 +24,7 @@ def test_preferred_language(testclient, logged_user):
|
|||
|
||||
res.form["preferred_language"] = "en"
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert res.flashes == [("success", "Profile updated successfuly.")]
|
||||
assert res.flashes == [("success", "Profile updated successfully.")]
|
||||
res = res.follow()
|
||||
logged_user.reload()
|
||||
assert logged_user.preferred_language == "en"
|
||||
|
@ -35,7 +35,7 @@ def test_preferred_language(testclient, logged_user):
|
|||
|
||||
res.form["preferred_language"] = "auto"
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert res.flashes == [("success", "Profile updated successfuly.")]
|
||||
assert res.flashes == [("success", "Profile updated successfully.")]
|
||||
res = res.follow()
|
||||
logged_user.reload()
|
||||
assert logged_user.preferred_language is None
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import os
|
||||
|
||||
import slapd
|
||||
|
||||
|
||||
class CustomSlapdObject(slapd.Slapd):
|
||||
def __init__(self):
|
||||
schemas = [
|
||||
schema
|
||||
for schema in [
|
||||
"core.ldif",
|
||||
"cosine.ldif",
|
||||
"nis.ldif",
|
||||
"inetorgperson.ldif",
|
||||
"ppolicy.ldif",
|
||||
]
|
||||
if os.path.exists(os.path.join(self.SCHEMADIR, schema))
|
||||
]
|
||||
|
||||
super().__init__(
|
||||
suffix="dc=mydomain,dc=tld",
|
||||
schemas=schemas,
|
||||
)
|
||||
|
||||
def init_tree(self):
|
||||
suffix_dc = self.suffix.split(",")[0][3:]
|
||||
self.ldapadd(
|
||||
"\n".join(
|
||||
[
|
||||
"dn: " + self.suffix,
|
||||
"objectClass: dcObject",
|
||||
"objectClass: organization",
|
||||
"dc: " + suffix_dc,
|
||||
"o: " + suffix_dc,
|
||||
"",
|
||||
"dn: " + self.root_dn,
|
||||
"objectClass: applicationProcess",
|
||||
"cn: " + self.root_cn,
|
||||
]
|
||||
)
|
||||
+ "\n"
|
||||
)
|
50
tests/backends/ldap/fixtures.py
Normal file
50
tests/backends/ldap/fixtures.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
import pytest
|
||||
from canaille.backends.ldap.backend import LDAPBackend
|
||||
from tests.backends.ldap import CustomSlapdObject
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def slapd_server():
|
||||
slapd = CustomSlapdObject()
|
||||
|
||||
try:
|
||||
slapd.start()
|
||||
slapd.init_tree()
|
||||
for ldif in (
|
||||
"demo/ldif/memberof-config.ldif",
|
||||
"demo/ldif/ppolicy-config.ldif",
|
||||
"demo/ldif/ppolicy.ldif",
|
||||
"canaille/backends/ldap/schemas/oauth2-openldap.ldif",
|
||||
"demo/ldif/bootstrap-users-tree.ldif",
|
||||
"demo/ldif/bootstrap-oidc-tree.ldif",
|
||||
):
|
||||
with open(ldif) as fd:
|
||||
slapd.ldapadd(fd.read())
|
||||
yield slapd
|
||||
finally:
|
||||
slapd.stop()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ldap_configuration(configuration, slapd_server):
|
||||
configuration["BACKENDS"] = {
|
||||
"LDAP": {
|
||||
"ROOT_DN": slapd_server.suffix,
|
||||
"URI": slapd_server.ldap_uri,
|
||||
"BIND_DN": slapd_server.root_dn,
|
||||
"BIND_PW": slapd_server.root_pw,
|
||||
"USER_BASE": "ou=users",
|
||||
"USER_FILTER": "(uid={login})",
|
||||
"GROUP_BASE": "ou=groups",
|
||||
"TIMEOUT": 0.1,
|
||||
},
|
||||
}
|
||||
yield configuration
|
||||
del configuration["BACKENDS"]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ldap_backend(slapd_server, ldap_configuration):
|
||||
backend = LDAPBackend(ldap_configuration)
|
||||
with backend.session():
|
||||
yield backend
|
|
@ -1,7 +1,13 @@
|
|||
import pytest
|
||||
from canaille import create_app
|
||||
from flask_webtest import TestApp
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def configuration(slapd_server, ldap_configuration):
|
||||
yield ldap_configuration
|
||||
|
||||
|
||||
def test_ldap_connection_remote_ldap_unreachable(configuration):
|
||||
app = create_app(configuration)
|
||||
testclient = TestApp(app)
|
||||
|
|
|
@ -6,7 +6,13 @@ from canaille.backends.ldap.backend import LDAPBackend
|
|||
from canaille.backends.ldap.ldapobject import LDAPObject
|
||||
from canaille.commands import cli
|
||||
from flask_webtest import TestApp
|
||||
from tests.conftest import CustomSlapdObject
|
||||
|
||||
from . import CustomSlapdObject
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def configuration(ldap_configuration):
|
||||
yield ldap_configuration
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
|
@ -1,93 +1,23 @@
|
|||
import os
|
||||
|
||||
import pytest
|
||||
import slapd
|
||||
from canaille import create_app
|
||||
from canaille.app import models
|
||||
from canaille.backends.ldap.backend import LDAPBackend
|
||||
from flask_webtest import TestApp
|
||||
from pytest_lazyfixture import lazy_fixture
|
||||
from werkzeug.security import gen_salt
|
||||
|
||||
|
||||
class CustomSlapdObject(slapd.Slapd):
|
||||
def __init__(self):
|
||||
schemas = [
|
||||
schema
|
||||
for schema in [
|
||||
"core.ldif",
|
||||
"cosine.ldif",
|
||||
"nis.ldif",
|
||||
"inetorgperson.ldif",
|
||||
"ppolicy.ldif",
|
||||
]
|
||||
if os.path.exists(os.path.join(self.SCHEMADIR, schema))
|
||||
]
|
||||
|
||||
super().__init__(
|
||||
suffix="dc=mydomain,dc=tld",
|
||||
schemas=schemas,
|
||||
)
|
||||
|
||||
def init_tree(self):
|
||||
suffix_dc = self.suffix.split(",")[0][3:]
|
||||
self.ldapadd(
|
||||
"\n".join(
|
||||
[
|
||||
"dn: " + self.suffix,
|
||||
"objectClass: dcObject",
|
||||
"objectClass: organization",
|
||||
"dc: " + suffix_dc,
|
||||
"o: " + suffix_dc,
|
||||
"",
|
||||
"dn: " + self.root_dn,
|
||||
"objectClass: applicationProcess",
|
||||
"cn: " + self.root_cn,
|
||||
]
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def slapd_server():
|
||||
slapd = CustomSlapdObject()
|
||||
|
||||
try:
|
||||
slapd.start()
|
||||
slapd.init_tree()
|
||||
for ldif in (
|
||||
"demo/ldif/memberof-config.ldif",
|
||||
"demo/ldif/ppolicy-config.ldif",
|
||||
"demo/ldif/ppolicy.ldif",
|
||||
"canaille/backends/ldap/schemas/oauth2-openldap.ldif",
|
||||
"demo/ldif/bootstrap-users-tree.ldif",
|
||||
"demo/ldif/bootstrap-oidc-tree.ldif",
|
||||
):
|
||||
with open(ldif) as fd:
|
||||
slapd.ldapadd(fd.read())
|
||||
yield slapd
|
||||
finally:
|
||||
slapd.stop()
|
||||
pytest_plugins = [
|
||||
"tests.backends.ldap.fixtures",
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def configuration(slapd_server, smtpd):
|
||||
def configuration(smtpd):
|
||||
smtpd.config.use_starttls = True
|
||||
conf = {
|
||||
"SECRET_KEY": gen_salt(24),
|
||||
"LOGO": "/static/img/canaille-head.png",
|
||||
"BACKENDS": {
|
||||
"LDAP": {
|
||||
"ROOT_DN": slapd_server.suffix,
|
||||
"URI": slapd_server.ldap_uri,
|
||||
"BIND_DN": slapd_server.root_dn,
|
||||
"BIND_PW": slapd_server.root_pw,
|
||||
"USER_BASE": "ou=users",
|
||||
"USER_FILTER": "(uid={login})",
|
||||
"GROUP_BASE": "ou=groups",
|
||||
"TIMEOUT": 0.1,
|
||||
},
|
||||
},
|
||||
"TIMEZONE": "UTC",
|
||||
"ACL": {
|
||||
"DEFAULT": {
|
||||
"READ": ["user_name", "groups"],
|
||||
|
@ -146,11 +76,9 @@ def configuration(slapd_server, smtpd):
|
|||
return conf
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def backend(slapd_server, configuration):
|
||||
backend = LDAPBackend(configuration)
|
||||
with backend.session():
|
||||
yield backend
|
||||
@pytest.fixture(params=[lazy_fixture("ldap_backend")])
|
||||
def backend(request):
|
||||
yield request.param
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
|
@ -36,7 +36,7 @@ def test_invitation(testclient, logged_admin, foo_group, smtpd):
|
|||
|
||||
res = res.form.submit(status=302)
|
||||
|
||||
assert ("success", "Your account has been created successfuly.") in res.flashes
|
||||
assert ("success", "Your account has been created successfully.") in res.flashes
|
||||
res = res.follow(status=200)
|
||||
|
||||
user = models.User.get_from_login("someone")
|
||||
|
@ -86,7 +86,7 @@ def test_invitation_editable_user_name(testclient, logged_admin, foo_group, smtp
|
|||
|
||||
res = res.form.submit(status=302)
|
||||
|
||||
assert ("success", "Your account has been created successfuly.") in res.flashes
|
||||
assert ("success", "Your account has been created successfully.") in res.flashes
|
||||
res = res.follow(status=200)
|
||||
|
||||
user = models.User.get_from_login("djorje")
|
||||
|
|
|
@ -10,7 +10,7 @@ def test_password_reset(testclient, user):
|
|||
res.form["password"] = "foobarbaz"
|
||||
res.form["confirmation"] = "foobarbaz"
|
||||
res = res.form.submit()
|
||||
assert ("success", "Your password has been updated successfuly") in res.flashes
|
||||
assert ("success", "Your password has been updated successfully") in res.flashes
|
||||
|
||||
user.reload()
|
||||
assert user.check_password("foobarbaz")[0]
|
||||
|
|
|
@ -161,7 +161,7 @@ def test_edition_remove_fields(
|
|||
res.form["phone_number"] = ""
|
||||
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert res.flashes == [("success", "Profile updated successfuly.")], res.text
|
||||
assert res.flashes == [("success", "Profile updated successfully.")], res.text
|
||||
res = res.follow()
|
||||
|
||||
logged_user.reload()
|
||||
|
|
|
@ -57,7 +57,7 @@ def test_photo_on_profile_edition(
|
|||
res.form["photo"] = Upload("logo.jpg", jpeg_photo)
|
||||
res.form["photo_delete"] = False
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert ("success", "Profile updated successfuly.") in res.flashes
|
||||
assert ("success", "Profile updated successfully.") in res.flashes
|
||||
res = res.follow()
|
||||
|
||||
logged_user.reload()
|
||||
|
@ -68,7 +68,7 @@ def test_photo_on_profile_edition(
|
|||
res = testclient.get("/profile/user", status=200)
|
||||
res.form["photo_delete"] = False
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert ("success", "Profile updated successfuly.") in res.flashes
|
||||
assert ("success", "Profile updated successfully.") in res.flashes
|
||||
res = res.follow()
|
||||
|
||||
logged_user.reload()
|
||||
|
@ -79,7 +79,7 @@ def test_photo_on_profile_edition(
|
|||
res = testclient.get("/profile/user", status=200)
|
||||
res.form["photo_delete"] = True
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert ("success", "Profile updated successfuly.") in res.flashes
|
||||
assert ("success", "Profile updated successfully.") in res.flashes
|
||||
res = res.follow()
|
||||
|
||||
logged_user.reload()
|
||||
|
@ -91,7 +91,7 @@ def test_photo_on_profile_edition(
|
|||
res.form["photo"] = Upload("logo.jpg", jpeg_photo)
|
||||
res.form["photo_delete"] = True
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert ("success", "Profile updated successfuly.") in res.flashes
|
||||
assert ("success", "Profile updated successfully.") in res.flashes
|
||||
res = res.follow()
|
||||
|
||||
logged_user.reload()
|
||||
|
|
|
@ -24,7 +24,7 @@ def test_edition(
|
|||
|
||||
res.form["user_name"] = "toto"
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert res.flashes == [("success", "Profile updated successfuly.")]
|
||||
assert res.flashes == [("success", "Profile updated successfully.")]
|
||||
res = res.follow()
|
||||
|
||||
logged_user.reload()
|
||||
|
@ -68,7 +68,7 @@ def test_edition_without_groups(
|
|||
testclient.app.config["ACL"]["DEFAULT"]["READ"] = []
|
||||
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert res.flashes == [("success", "Profile updated successfuly.")]
|
||||
assert res.flashes == [("success", "Profile updated successfully.")]
|
||||
res = res.follow()
|
||||
|
||||
logged_user.reload()
|
||||
|
@ -97,7 +97,7 @@ def test_password_change(testclient, logged_user):
|
|||
res.form["password2"] = "correct horse battery staple"
|
||||
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert ("success", "Profile updated successfuly.") in res.flashes
|
||||
assert ("success", "Profile updated successfully.") in res.flashes
|
||||
res = res.follow()
|
||||
|
||||
logged_user.reload()
|
||||
|
@ -349,7 +349,7 @@ def test_past_lock_date(
|
|||
) - datetime.timedelta(days=30)
|
||||
res.form["lock_date"] = expiration_datetime.strftime("%Y-%m-%d %H:%M")
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert res.flashes == [("success", "Profile updated successfuly.")]
|
||||
assert res.flashes == [("success", "Profile updated successfully.")]
|
||||
|
||||
res = res.follow()
|
||||
user = models.User.get(id=user.id)
|
||||
|
@ -372,7 +372,7 @@ def test_future_lock_date(
|
|||
) + datetime.timedelta(days=30)
|
||||
res.form["lock_date"] = expiration_datetime.strftime("%Y-%m-%d %H:%M")
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert res.flashes == [("success", "Profile updated successfuly.")]
|
||||
assert res.flashes == [("success", "Profile updated successfully.")]
|
||||
|
||||
res = res.follow()
|
||||
user = models.User.get(id=user.id)
|
||||
|
@ -396,7 +396,7 @@ def test_empty_lock_date(
|
|||
res = testclient.get("/profile/user/settings", status=200)
|
||||
res.form["lock_date"] = ""
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert res.flashes == [("success", "Profile updated successfuly.")]
|
||||
assert res.flashes == [("success", "Profile updated successfully.")]
|
||||
|
||||
res = res.follow()
|
||||
user.reload()
|
||||
|
@ -418,7 +418,7 @@ def test_account_limit_values(
|
|||
)
|
||||
res.form["lock_date"] = expiration_datetime.strftime("%Y-%m-%d %H:%M:%S")
|
||||
res = res.form.submit(name="action", value="edit")
|
||||
assert res.flashes == [("success", "Profile updated successfuly.")]
|
||||
assert res.flashes == [("success", "Profile updated successfully.")]
|
||||
|
||||
res = res.follow()
|
||||
user = models.User.get(id=user.id)
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import os
|
||||
|
||||
import pytest
|
||||
from canaille import create_app
|
||||
from canaille.commands import cli
|
||||
from flask_webtest import TestApp
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def configuration(ldap_configuration):
|
||||
yield ldap_configuration
|
||||
|
||||
|
||||
def test_install_keypair(configuration, tmpdir):
|
||||
keys_dir = os.path.join(tmpdir, "keys")
|
||||
os.makedirs(keys_dir)
|
||||
|
|
|
@ -14,7 +14,9 @@ from werkzeug.security import gen_salt
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def app(app):
|
||||
# For some reason all the params from the overriden fixture must be present here
|
||||
# https://github.com/pytest-dev/pytest/issues/11075
|
||||
def app(app, configuration, backend):
|
||||
os.environ["AUTHLIB_INSECURE_TRANSPORT"] = "true"
|
||||
yield app
|
||||
|
||||
|
|
Loading…
Reference in a new issue