forked from Github-Mirrors/canaille
User admin page. Fixes #8
This commit is contained in:
parent
1300265a14
commit
4a20fb3b55
12 changed files with 612 additions and 152 deletions
|
@ -17,8 +17,14 @@ from flask import (
|
|||
)
|
||||
from flask_babel import gettext as _
|
||||
|
||||
from .forms import LoginForm, ProfileForm, PasswordResetForm, ForgottenPasswordForm
|
||||
from .flaskutils import current_user, user_needed
|
||||
from .forms import (
|
||||
LoginForm,
|
||||
AddProfileForm,
|
||||
EditProfileForm,
|
||||
PasswordResetForm,
|
||||
ForgottenPasswordForm,
|
||||
)
|
||||
from .flaskutils import current_user, user_needed, admin_needed
|
||||
from .models import User
|
||||
|
||||
|
||||
|
@ -29,7 +35,9 @@ bp = Blueprint(__name__, "home")
|
|||
def index():
|
||||
if not current_user():
|
||||
return redirect(url_for("canaille.account.login"))
|
||||
return redirect(url_for("canaille.account.profile", subject=current_user().uid[0]))
|
||||
return redirect(
|
||||
url_for("canaille.account.profile_edition", username=current_user().uid[0])
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/login", methods=("GET", "POST"))
|
||||
|
@ -44,7 +52,7 @@ def login():
|
|||
return render_template("login.html", form=form)
|
||||
|
||||
user = User.get(form.login.data)
|
||||
flash(_(f"Connection successful. Welcome {user.name}"), "success")
|
||||
flash(_("Connection successful. Welcome %(user)s", user=user.name), "success")
|
||||
return redirect(url_for("canaille.account.index"))
|
||||
|
||||
return render_template("login.html", form=form)
|
||||
|
@ -55,25 +63,87 @@ def logout():
|
|||
user = current_user()
|
||||
if user:
|
||||
flash(
|
||||
_(f"You have been disconnected. See you next time {user.name}"), "success"
|
||||
_("You have been disconnected. See you next time %(user)s", user=user.name),
|
||||
"success",
|
||||
)
|
||||
user.logout()
|
||||
return redirect("/")
|
||||
|
||||
|
||||
@bp.route("/profile/<subject>", methods=("GET", "POST"))
|
||||
@user_needed()
|
||||
def profile(user, subject):
|
||||
subject == user.uid[0] or abort(403)
|
||||
@bp.route("/users")
|
||||
@admin_needed()
|
||||
def users(user):
|
||||
users = User.filter(objectClass=current_app.config["LDAP"]["USER_CLASS"])
|
||||
return render_template("users.html", users=users, menuitem="users")
|
||||
|
||||
|
||||
@bp.route("/profile", methods=("GET", "POST"))
|
||||
@admin_needed()
|
||||
def profile_creation(user):
|
||||
claims = current_app.config["JWT"]["MAPPING"]
|
||||
form = AddProfileForm(request.form or None)
|
||||
try:
|
||||
del form.sub.render_kw["readonly"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if request.form:
|
||||
if not form.validate():
|
||||
flash(_("User creation failed."), "error")
|
||||
|
||||
else:
|
||||
user = User(objectClass=current_app.config["LDAP"]["USER_CLASS"])
|
||||
for attribute in form:
|
||||
model_attribute_name = claims.get(attribute.name.upper())
|
||||
if (
|
||||
not model_attribute_name
|
||||
or model_attribute_name not in user.must + user.may
|
||||
):
|
||||
continue
|
||||
|
||||
user[model_attribute_name] = [attribute.data]
|
||||
|
||||
user.cn = [f"{user.givenName[0]} {user.sn[0]}"]
|
||||
user.save()
|
||||
|
||||
flash(_("User creation succeed."), "success")
|
||||
|
||||
return redirect(
|
||||
url_for("canaille.account.profile_edition", username=user.uid[0])
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"profile.html", form=form, menuitem="users", edited_user=None
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/profile/<username>", methods=("GET", "POST"))
|
||||
@user_needed()
|
||||
def profile_edition(user, username):
|
||||
user.admin or username == user.uid[0] or abort(403)
|
||||
|
||||
if request.method == "GET" or request.form.get("action") == "edit":
|
||||
return profile_edit(user, username)
|
||||
|
||||
if request.form.get("action") == "delete":
|
||||
return profile_delete(user, username)
|
||||
|
||||
abort(400)
|
||||
|
||||
|
||||
def profile_edit(user, username):
|
||||
menuitem = "profile" if username == user.uid[0] else "users"
|
||||
claims = current_app.config["JWT"]["MAPPING"]
|
||||
if username != user.uid[0]:
|
||||
user = User.get(username) or abort(404)
|
||||
|
||||
data = {
|
||||
k.lower(): getattr(user, v)[0]
|
||||
if getattr(user, v) and isinstance(getattr(user, v), list)
|
||||
else getattr(user, v) or ""
|
||||
for k, v in claims.items()
|
||||
}
|
||||
form = ProfileForm(request.form or None, data=data)
|
||||
form = EditProfileForm(request.form or None, data=data)
|
||||
form.sub.render_kw["readonly"] = "true"
|
||||
|
||||
if request.form:
|
||||
|
@ -83,7 +153,10 @@ def profile(user, subject):
|
|||
else:
|
||||
for attribute in form:
|
||||
model_attribute_name = claims.get(attribute.name.upper())
|
||||
if not model_attribute_name or not hasattr(user, model_attribute_name):
|
||||
if (
|
||||
not model_attribute_name
|
||||
or model_attribute_name not in user.must + user.may
|
||||
):
|
||||
continue
|
||||
|
||||
user[model_attribute_name] = [attribute.data]
|
||||
|
@ -93,7 +166,24 @@ def profile(user, subject):
|
|||
|
||||
user.save()
|
||||
|
||||
return render_template("profile.html", form=form, menuitem="profile")
|
||||
return render_template(
|
||||
"profile.html", form=form, menuitem=menuitem, edited_user=user
|
||||
)
|
||||
|
||||
|
||||
def profile_delete(user, username):
|
||||
self_deletion = username == user.uid[0]
|
||||
if self_deletion:
|
||||
user.logout()
|
||||
else:
|
||||
user = User.get(username) or abort(404)
|
||||
|
||||
flash(_("The user %(user)s has been sucessfuly deleted", user=user.name), "success")
|
||||
user.delete()
|
||||
|
||||
if self_deletion:
|
||||
return redirect(url_for("canaille.account.index"))
|
||||
return redirect(url_for("canaille.account.users"))
|
||||
|
||||
|
||||
def profile_hash(user, password):
|
||||
|
@ -214,6 +304,6 @@ def reset(uid, hash):
|
|||
user.login()
|
||||
|
||||
flash(_("Your password has been updated successfuly"), "success")
|
||||
return redirect(url_for("canaille.account.profile", subject=uid))
|
||||
return redirect(url_for("canaille.account.profile_edition", username=uid))
|
||||
|
||||
return render_template("reset-password.html", form=form, uid=uid, hash=hash)
|
||||
|
|
|
@ -36,6 +36,9 @@ USER_BASE = "ou=users,dc=mydomain,dc=tld"
|
|||
# USER_FILTER = "(|(uid={login})(mail={login}))"
|
||||
USER_FILTER = "(|(uid={login})(cn={login}))"
|
||||
|
||||
# A class to use for creating new users
|
||||
USER_CLASS = "inetOrgPerson"
|
||||
|
||||
# Filter to match admin users. If your server has memberof
|
||||
# you can filter against group membership
|
||||
# ADMIN_FILTER = "uid=admin"
|
||||
|
|
|
@ -51,6 +51,7 @@ class ProfileForm(FlaskForm):
|
|||
sub = wtforms.StringField(
|
||||
_("Username"),
|
||||
render_kw={"placeholder": _("jdoe")},
|
||||
validators=[wtforms.validators.DataRequired()],
|
||||
)
|
||||
# name = wtforms.StringField(_("Name"))
|
||||
given_name = wtforms.StringField(
|
||||
|
@ -90,6 +91,12 @@ class ProfileForm(FlaskForm):
|
|||
# picture = wtforms.StringField(_("Photo"))
|
||||
# website = wtforms.fields.html5.URLField(_("Website"))
|
||||
|
||||
def validate_password2(self, field):
|
||||
if self.password1.data and self.password1.data != field.data:
|
||||
raise wtforms.ValidationError(_("Password and confirmation do not match."))
|
||||
|
||||
|
||||
class EditProfileForm(ProfileForm):
|
||||
password1 = wtforms.PasswordField(
|
||||
_("Password"),
|
||||
validators=[wtforms.validators.Optional(), wtforms.validators.Length(min=8)],
|
||||
|
@ -98,6 +105,15 @@ class ProfileForm(FlaskForm):
|
|||
_("Password confirmation"),
|
||||
)
|
||||
|
||||
def validate_password2(self, field):
|
||||
if self.password1.data and self.password1.data != field.data:
|
||||
raise wtforms.ValidationError(_("Password and confirmation do not match."))
|
||||
|
||||
class AddProfileForm(ProfileForm):
|
||||
password1 = wtforms.PasswordField(
|
||||
_("Password"),
|
||||
validators=[
|
||||
wtforms.validators.DataRequired(),
|
||||
wtforms.validators.Length(min=8),
|
||||
],
|
||||
)
|
||||
password2 = wtforms.PasswordField(
|
||||
_("Password confirmation"),
|
||||
)
|
||||
|
|
10
canaille/static/js/profile.js
Normal file
10
canaille/static/js/profile.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
$('.confirm').click(function(e){
|
||||
e.preventDefault();
|
||||
$('.ui.modal')
|
||||
.modal({
|
||||
onApprove : function() {
|
||||
$('.confirm').unbind('click').click();
|
||||
},
|
||||
})
|
||||
.modal('show');
|
||||
});
|
|
@ -32,7 +32,7 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
<a class="item {% if menuitem == "profile" %}active{% endif %}"
|
||||
href="{{ url_for('canaille.account.profile', subject=user.uid[0]) }}">
|
||||
href="{{ url_for('canaille.account.profile_edition', username=user.uid[0]) }}">
|
||||
<i class="id card icon"></i>
|
||||
{% trans %}My profile{% endtrans %}
|
||||
</a>
|
||||
|
@ -42,6 +42,13 @@
|
|||
{% trans %}My consents{% endtrans %}
|
||||
</a>
|
||||
{% if user.admin %}
|
||||
<a class="item {% if menuitem == "users" %}active{% endif %}"
|
||||
href="{{ url_for('canaille.account.users') }}">
|
||||
<i class="users icon"></i>
|
||||
{% trans %}Users{% endtrans %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if user.admin %}
|
||||
<div class="ui dropdown item {% if menuitem == "admin" %}active{% endif %}">
|
||||
<i class="settings icon"></i>
|
||||
Admin
|
||||
|
|
|
@ -2,18 +2,70 @@
|
|||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block script %}
|
||||
<script src="/static/js/profile.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if user.admin and edited_user %}
|
||||
<div class="ui basic modal">
|
||||
<div class="ui icon header">
|
||||
<i class="user minus icon"></i>
|
||||
{% trans %}User deletion{% endtrans %}
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>{{ _("Are you sure you want to delete this user? This action is unrevokable and all the data about this user will be removed.") }}</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui inverted cancel button">{% trans %}Cancel{% endtrans %}</div>
|
||||
<div class="ui inverted red approve button">{% trans %}Delete{% endtrans %}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="ui clearing segment">
|
||||
<h2 class="ui center aligned header">
|
||||
<div class="content">
|
||||
{{ _("My profile") }}
|
||||
{% if not edited_user %}
|
||||
{% trans %}User creation{% endtrans %}
|
||||
{% elif user.uid == edited_user.uid %}
|
||||
{% trans %}My profile{% endtrans %}
|
||||
{% else %}
|
||||
{% trans %}User profile edition{% endtrans %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
<div class="sub header">
|
||||
{% trans %}Edit your personal informations{% endtrans %}
|
||||
{% if not edited_user %}
|
||||
{% trans %}Create a new user account{% endtrans %}
|
||||
{% elif user.uid == edited_user.uid %}
|
||||
{% trans %}Edit your personal informations{% endtrans %}
|
||||
{% else %}
|
||||
{% trans %}Edit informations about an user{% endtrans %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
{{ flask.messages() }}
|
||||
{{ sui.render_form(form, _("Edit"), action=request.url) }}
|
||||
<form method="POST"
|
||||
id="{{ form.__class__.__name__|lower }}"
|
||||
action="{{ request.url }}"
|
||||
role="form"
|
||||
enctype="multipart/form-data"
|
||||
class="ui form"
|
||||
>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
{{ sui.render_fields(form) }}
|
||||
|
||||
<button type="submit" class="ui right floated primary button" name="action" value="edit" id="edit">
|
||||
{{ _("Send") }}
|
||||
</button>
|
||||
{% if user.admin and edited_user %}
|
||||
<button type="submit" class="ui right floated negative button confirm" name="action" value="delete" id="delete">
|
||||
{{ _("Delete the user") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
35
canaille/templates/users.html
Normal file
35
canaille/templates/users.html
Normal file
|
@ -0,0 +1,35 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block style %}
|
||||
<link href="/static/datatables/jquery.dataTables.min.css" rel="stylesheet">
|
||||
<link href="/static/datatables/dataTables.semanticui.min.css" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script src="/static/datatables/jquery.dataTables.min.js"></script>
|
||||
<script src="/static/datatables/dataTables.semanticui.min.js"></script>
|
||||
<script src="/static/js/users.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui segment">
|
||||
<a class="ui primary button" href="{{ url_for('canaille.account.profile_creation') }}">{% trans %}Add a user{% endtrans %}</a>
|
||||
</div>
|
||||
|
||||
<table class="ui table">
|
||||
<thead>
|
||||
<th>{% trans %}Name{% endtrans %}</th>
|
||||
<th>{% trans %}Email{% endtrans %}</th>
|
||||
<th>{% trans %}Phone number{% endtrans %}</th>
|
||||
</thead>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('canaille.account.profile_edition', username=user.uid[0]) }}">{{ user.name }}</a></td>
|
||||
<td><a href="mailto:{{ user.mail[0] }}">{{ user.mail[0] }}</a></td>
|
||||
<td>{{ user.telephoneNumber[0] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
Binary file not shown.
|
@ -8,8 +8,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: contact@yaal.fr\n"
|
||||
"POT-Creation-Date: 2020-10-29 15:26+0100\n"
|
||||
"PO-Revision-Date: 2020-10-29 15:27+0100\n"
|
||||
"POT-Creation-Date: 2020-11-01 12:32+0100\n"
|
||||
"PO-Revision-Date: 2020-11-01 12:38+0100\n"
|
||||
"Last-Translator: Éloi Rivard <eloi@yaal.fr>\n"
|
||||
"Language: fr_FR\n"
|
||||
"Language-Team: French - France <equipe@yaal.fr>\n"
|
||||
|
@ -20,39 +20,62 @@ msgstr ""
|
|||
"Generated-By: Babel 2.8.0\n"
|
||||
"X-Generator: Gtranslator 3.38.0\n"
|
||||
|
||||
#: canaille/account.py:35 canaille/oauth.py:58
|
||||
#: canaille/account.py:51 canaille/oauth.py:58
|
||||
msgid "Login failed, please check your information"
|
||||
msgstr "La connexion a échoué, veuillez vérifier vos informations."
|
||||
|
||||
#: canaille/account.py:64
|
||||
#: canaille/account.py:55
|
||||
#, python-format
|
||||
msgid "Connection successful. Welcome %(user)s"
|
||||
msgstr "Connexion réussie. Bienvenue %(user)s"
|
||||
|
||||
#: canaille/account.py:66
|
||||
#, python-format
|
||||
msgid "You have been disconnected. See you next time %(user)s"
|
||||
msgstr "Vous avez été déconnectés. À bientôt %(user)s"
|
||||
|
||||
#: canaille/account.py:92
|
||||
msgid "User creation failed."
|
||||
msgstr "La création de l'utilisateur a échoué."
|
||||
|
||||
#: canaille/account.py:109
|
||||
msgid "User creation succeed."
|
||||
msgstr "La création de l'utilisateur a réussi."
|
||||
|
||||
#: canaille/account.py:151
|
||||
msgid "Profile edition failed."
|
||||
msgstr "L'édition du profil a échoué."
|
||||
|
||||
#: canaille/account.py:75
|
||||
#: canaille/account.py:165
|
||||
msgid "Profile updated successfuly."
|
||||
msgstr "Le profil a été mis à jour avec succès."
|
||||
|
||||
#: canaille/account.py:97
|
||||
#: canaille/account.py:181
|
||||
#, python-format
|
||||
msgid "The user %(user)s has been sucessfuly deleted"
|
||||
msgstr "L'utilisateur %(user)s a bien été supprimé"
|
||||
|
||||
#: canaille/account.py:204
|
||||
msgid "Could not send the password reset link."
|
||||
msgstr "Impossible d'envoyer le lien de réinitialisation."
|
||||
|
||||
#: canaille/account.py:104 canaille/account.py:177
|
||||
#: canaille/account.py:211 canaille/account.py:284
|
||||
msgid "A password reset link has been sent at your email address."
|
||||
msgstr "Un lien de réinitialisation vous a été envoyé à votre adresse."
|
||||
|
||||
#: canaille/account.py:126
|
||||
#: canaille/account.py:233
|
||||
msgid "Password reset on {website_name}"
|
||||
msgstr "Réinitialisation du mot de passe sur {website_name}"
|
||||
|
||||
#: canaille/account.py:171
|
||||
#: canaille/account.py:278
|
||||
msgid "Could not reset your password"
|
||||
msgstr "Impossible de réinitialiser votre mot de passe"
|
||||
|
||||
#: canaille/account.py:190
|
||||
#: canaille/account.py:297
|
||||
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/account.py:199
|
||||
#: canaille/account.py:306
|
||||
msgid "Your password has been updated successfuly"
|
||||
msgstr "Votre mot de passe a correctement été mis à jour."
|
||||
|
||||
|
@ -64,55 +87,60 @@ msgstr "Impossible de supprimer cet accès."
|
|||
msgid "The access has been revoked"
|
||||
msgstr "L'accès a été révoqué."
|
||||
|
||||
#: canaille/forms.py:8 canaille/forms.py:25
|
||||
#: canaille/forms.py:9 canaille/forms.py:26
|
||||
msgid "Login"
|
||||
msgstr "Identifiant"
|
||||
|
||||
#: canaille/forms.py:11 canaille/forms.py:28 canaille/forms.py:80
|
||||
#: canaille/forms.py:12 canaille/forms.py:29 canaille/forms.py:82
|
||||
msgid "jane@doe.com"
|
||||
msgstr "martin@dupont.fr"
|
||||
|
||||
#: canaille/forms.py:18 canaille/forms.py:37 canaille/forms.py:93
|
||||
#: canaille/forms.py:19 canaille/forms.py:38 canaille/forms.py:101
|
||||
#: canaille/forms.py:111
|
||||
msgid "Password"
|
||||
msgstr "Mot de passe"
|
||||
|
||||
#: canaille/forms.py:40 canaille/forms.py:97
|
||||
#: canaille/forms.py:41 canaille/forms.py:105 canaille/forms.py:118
|
||||
msgid "Password confirmation"
|
||||
msgstr "Confirmation du mot de passe"
|
||||
|
||||
#: canaille/forms.py:43 canaille/forms.py:102
|
||||
#: canaille/forms.py:44 canaille/forms.py:96
|
||||
msgid "Password and confirmation do not match."
|
||||
msgstr "Le mot de passe et sa confirmation ne correspondent pas."
|
||||
|
||||
#: canaille/forms.py:51
|
||||
#: canaille/forms.py:52
|
||||
msgid "Username"
|
||||
msgstr "Identifiant"
|
||||
|
||||
#: canaille/forms.py:56
|
||||
#: canaille/forms.py:53
|
||||
msgid "jdoe"
|
||||
msgstr "mdupont"
|
||||
|
||||
#: canaille/forms.py:58
|
||||
msgid "Given name"
|
||||
msgstr "Prénom"
|
||||
|
||||
#: canaille/forms.py:58
|
||||
#: canaille/forms.py:60
|
||||
msgid "John"
|
||||
msgstr "Martin"
|
||||
|
||||
#: canaille/forms.py:64
|
||||
#: canaille/forms.py:66
|
||||
msgid "Family Name"
|
||||
msgstr "Nom de famille"
|
||||
|
||||
#: canaille/forms.py:66
|
||||
#: canaille/forms.py:68
|
||||
msgid "Doe"
|
||||
msgstr "Dupont"
|
||||
|
||||
#: canaille/forms.py:77
|
||||
#: canaille/forms.py:79
|
||||
msgid "Email address"
|
||||
msgstr "Courriel"
|
||||
|
||||
#: canaille/forms.py:87
|
||||
#: canaille/forms.py:89 canaille/templates/users.html:24
|
||||
msgid "Phone number"
|
||||
msgstr "Numéro de téléphone"
|
||||
|
||||
#: canaille/forms.py:87
|
||||
#: canaille/forms.py:89
|
||||
msgid "555-000-555"
|
||||
msgstr "06 01 02 03 04"
|
||||
|
||||
|
@ -137,6 +165,7 @@ msgid "You have been successfully logged out."
|
|||
msgstr "Vous avez été déconnectés."
|
||||
|
||||
#: canaille/admin/clients.py:24 canaille/templates/admin/client_list.html:22
|
||||
#: canaille/templates/users.html:22
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
|
@ -235,62 +264,66 @@ msgid "Accept"
|
|||
msgstr "Accepter"
|
||||
|
||||
#: canaille/templates/base.html:8
|
||||
msgid "OpenID Connect Server"
|
||||
msgstr "OpenID Connect Server"
|
||||
msgid "authorization interface"
|
||||
msgstr " - Interface de gestion des autorisations"
|
||||
|
||||
#: canaille/templates/base.html:37 canaille/templates/profile.html:8
|
||||
#: canaille/templates/base.html:37 canaille/templates/profile.html:32
|
||||
msgid "My profile"
|
||||
msgstr "Mon profil"
|
||||
|
||||
#: canaille/templates/base.html:42 canaille/templates/consent_list.html:18
|
||||
#: canaille/templates/base.html:42 canaille/templates/consent_list.html:19
|
||||
msgid "My consents"
|
||||
msgstr "Mes autorisations"
|
||||
|
||||
#: canaille/templates/base.html:51
|
||||
#: canaille/templates/base.html:48
|
||||
msgid "Users"
|
||||
msgstr "Utilisateurs"
|
||||
|
||||
#: canaille/templates/base.html:58
|
||||
msgid "Clients"
|
||||
msgstr "Clients"
|
||||
|
||||
#: canaille/templates/base.html:55
|
||||
#: canaille/templates/base.html:62
|
||||
msgid "Tokens"
|
||||
msgstr "Jetons"
|
||||
|
||||
#: canaille/templates/base.html:59
|
||||
#: canaille/templates/base.html:66
|
||||
msgid "Codes"
|
||||
msgstr "Codes"
|
||||
|
||||
#: canaille/templates/base.html:63
|
||||
#: canaille/templates/base.html:70
|
||||
msgid "Consents"
|
||||
msgstr "Autorisations"
|
||||
|
||||
#: canaille/templates/base.html:70
|
||||
#: canaille/templates/base.html:77
|
||||
msgid "Log out"
|
||||
msgstr "Déconnexion"
|
||||
|
||||
#: canaille/templates/consent_list.html:21
|
||||
#: canaille/templates/consent_list.html:22
|
||||
msgid "Consult and revoke the authorization you gave to websites."
|
||||
msgstr "Consultez et révoquez les autorisation que vous avez données."
|
||||
|
||||
#: canaille/templates/consent_list.html:47
|
||||
#: canaille/templates/consent_list.html:42
|
||||
msgid "From:"
|
||||
msgstr "À partir de :"
|
||||
|
||||
#: canaille/templates/consent_list.html:49
|
||||
#: canaille/templates/consent_list.html:44
|
||||
msgid "Revoked:"
|
||||
msgstr "Révoqué le :"
|
||||
|
||||
#: canaille/templates/consent_list.html:52
|
||||
#: canaille/templates/consent_list.html:47
|
||||
msgid "Has access to:"
|
||||
msgstr "A accès à :"
|
||||
|
||||
#: canaille/templates/consent_list.html:62
|
||||
#: canaille/templates/consent_list.html:57
|
||||
msgid "Remove access"
|
||||
msgstr "Supprimer l'accès"
|
||||
|
||||
#: canaille/templates/consent_list.html:72
|
||||
#: canaille/templates/consent_list.html:67
|
||||
msgid "Nothing here"
|
||||
msgstr "Rien ici"
|
||||
|
||||
#: canaille/templates/consent_list.html:73
|
||||
#: canaille/templates/consent_list.html:68
|
||||
msgid "You did not authorize applications yet."
|
||||
msgstr ""
|
||||
"Vous n'avez pas encore autorisé d'application à accéder à votre profil."
|
||||
|
@ -311,12 +344,12 @@ msgstr "Page non trouvée"
|
|||
msgid "Technical problem"
|
||||
msgstr "Problème technique"
|
||||
|
||||
#: canaille/templates/forgotten-password.html:7
|
||||
#: canaille/templates/login.html:39
|
||||
#: canaille/templates/forgotten-password.html:12
|
||||
#: canaille/templates/login.html:34
|
||||
msgid "Forgotten password"
|
||||
msgstr "Mot de passe oublié"
|
||||
|
||||
#: canaille/templates/forgotten-password.html:17
|
||||
#: canaille/templates/forgotten-password.html:19
|
||||
msgid ""
|
||||
"\n"
|
||||
" After this form is sent, if the email address or the login you "
|
||||
|
@ -333,40 +366,85 @@ msgstr ""
|
|||
" vous permettra de ré-initialiser votre mot de passe.\n"
|
||||
" "
|
||||
|
||||
#: canaille/templates/forgotten-password.html:34
|
||||
#: canaille/templates/forgotten-password.html:36
|
||||
#: canaille/templates/profile.html:62
|
||||
msgid "Send"
|
||||
msgstr "Envoyer"
|
||||
|
||||
#: canaille/templates/forgotten-password.html:35
|
||||
#: canaille/templates/forgotten-password.html:37
|
||||
msgid "Login page"
|
||||
msgstr "Page de connexion"
|
||||
|
||||
#: canaille/templates/login.html:14
|
||||
#: canaille/templates/login.html:15
|
||||
#, python-format
|
||||
msgid "Sign in at %(website)s"
|
||||
msgstr "Connexion à %(website)s"
|
||||
|
||||
#: canaille/templates/login.html:16
|
||||
#: canaille/templates/login.html:17
|
||||
msgid "Log-in and manage your authorizations."
|
||||
msgstr "Connectez-vous et gérez vos autorisations."
|
||||
|
||||
#: canaille/templates/login.html:38
|
||||
#: canaille/templates/login.html:33
|
||||
msgid "Sign in"
|
||||
msgstr "Se connecter"
|
||||
|
||||
#: canaille/templates/profile.html:11
|
||||
#: canaille/templates/profile.html:14
|
||||
msgid "User deletion"
|
||||
msgstr "Suppression d'un utilisateur"
|
||||
|
||||
#: canaille/templates/profile.html:17
|
||||
msgid ""
|
||||
"Are you sure you want to delete this user? This action is unrevokable and "
|
||||
"all the data about this user will be removed."
|
||||
msgstr ""
|
||||
"Êtes-vous sûrs de vouloir supprimer cet utilisateur ? Cette action est "
|
||||
"irrévocable, et toutes les données de cet utilisateur seront supprimées."
|
||||
|
||||
#: canaille/templates/profile.html:20
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: canaille/templates/profile.html:21
|
||||
msgid "Delete"
|
||||
msgstr "Supprimer"
|
||||
|
||||
#: canaille/templates/profile.html:30
|
||||
msgid "User creation"
|
||||
msgstr "Nouvel utilisateur"
|
||||
|
||||
#: canaille/templates/profile.html:34
|
||||
msgid "User profile edition"
|
||||
msgstr "Édition d'un profil utilisateur"
|
||||
|
||||
#: canaille/templates/profile.html:40
|
||||
msgid "Create a new user account"
|
||||
msgstr "Création d'un nouveau compte utilisateur"
|
||||
|
||||
#: canaille/templates/profile.html:42
|
||||
msgid "Edit your personal informations"
|
||||
msgstr "Éditez vos informations personnelles"
|
||||
|
||||
#: canaille/templates/profile.html:23
|
||||
msgid "Edit"
|
||||
msgstr "Éditer"
|
||||
#: canaille/templates/profile.html:44
|
||||
msgid "Edit informations about an user"
|
||||
msgstr "Éditez les informations d'un utilisateur"
|
||||
|
||||
#: canaille/templates/reset-password.html:7
|
||||
#: canaille/templates/reset-password.html:17
|
||||
#: canaille/templates/profile.html:66
|
||||
msgid "Delete the user"
|
||||
msgstr "Supprimer l'utilisateur"
|
||||
|
||||
#: canaille/templates/reset-password.html:12
|
||||
#: canaille/templates/reset-password.html:19
|
||||
msgid "Password reset"
|
||||
msgstr "Réinitialisation du mot de passe"
|
||||
|
||||
#: canaille/templates/users.html:17
|
||||
msgid "Add a user"
|
||||
msgstr "Nouvel utilisateur"
|
||||
|
||||
#: canaille/templates/users.html:23
|
||||
msgid "Email"
|
||||
msgstr "Courriel"
|
||||
|
||||
#: canaille/templates/admin/authorization_list.html:18
|
||||
#: canaille/templates/admin/token_list.html:18
|
||||
msgid "Token"
|
||||
|
@ -388,32 +466,32 @@ msgstr "Utilisateur"
|
|||
msgid "Created"
|
||||
msgstr "Créé"
|
||||
|
||||
#: canaille/templates/admin/authorization_view.html:7
|
||||
#: canaille/templates/admin/authorization_view.html:8
|
||||
msgid "View a authorization"
|
||||
msgstr "Voir une autorisation"
|
||||
|
||||
#: canaille/templates/admin/client_add.html:7
|
||||
#: canaille/templates/admin/client_add.html:8
|
||||
msgid "Add a client"
|
||||
msgstr "Ajouter un client"
|
||||
|
||||
#: canaille/templates/admin/client_add.html:17
|
||||
#: canaille/templates/admin/client_edit.html:34
|
||||
#: canaille/templates/admin/client_add.html:14
|
||||
#: canaille/templates/admin/client_edit.html:31
|
||||
msgid "Confirm"
|
||||
msgstr "Confirmer"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:7
|
||||
#: canaille/templates/admin/client_edit.html:8
|
||||
msgid "Edit a client"
|
||||
msgstr "Éditer un client"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:20
|
||||
#: canaille/templates/admin/client_edit.html:17
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:24
|
||||
#: canaille/templates/admin/client_edit.html:21
|
||||
msgid "Secret"
|
||||
msgstr "Secret"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:28
|
||||
#: canaille/templates/admin/client_edit.html:25
|
||||
msgid "Issued at"
|
||||
msgstr "Créé le"
|
||||
|
||||
|
@ -425,7 +503,7 @@ msgstr "Ajouter un client"
|
|||
msgid "URL"
|
||||
msgstr "URL"
|
||||
|
||||
#: canaille/templates/admin/token_view.html:7
|
||||
#: canaille/templates/admin/token_view.html:8
|
||||
msgid "View a token"
|
||||
msgstr "Voir un jeton"
|
||||
|
||||
|
@ -499,3 +577,9 @@ msgstr ""
|
|||
|
||||
#~ msgid "from: %(user)s"
|
||||
#~ msgstr "pour : %(user)s"
|
||||
|
||||
#~ msgid "OpenID Connect Server"
|
||||
#~ msgstr "OpenID Connect Server"
|
||||
|
||||
#~ msgid "Edit"
|
||||
#~ msgstr "Éditer"
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2020-10-29 15:26+0100\n"
|
||||
"POT-Creation-Date: 2020-11-01 12:32+0100\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"
|
||||
|
@ -17,39 +17,62 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.8.0\n"
|
||||
|
||||
#: canaille/account.py:35 canaille/oauth.py:58
|
||||
#: canaille/account.py:51 canaille/oauth.py:58
|
||||
msgid "Login failed, please check your information"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:64
|
||||
#: canaille/account.py:55
|
||||
#, python-format
|
||||
msgid "Connection successful. Welcome %(user)s"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:66
|
||||
#, python-format
|
||||
msgid "You have been disconnected. See you next time %(user)s"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:92
|
||||
msgid "User creation failed."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:109
|
||||
msgid "User creation succeed."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:151
|
||||
msgid "Profile edition failed."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:75
|
||||
#: canaille/account.py:165
|
||||
msgid "Profile updated successfuly."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:97
|
||||
#: canaille/account.py:181
|
||||
#, python-format
|
||||
msgid "The user %(user)s has been sucessfuly deleted"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:204
|
||||
msgid "Could not send the password reset link."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:104 canaille/account.py:177
|
||||
#: canaille/account.py:211 canaille/account.py:284
|
||||
msgid "A password reset link has been sent at your email address."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:126
|
||||
#: canaille/account.py:233
|
||||
msgid "Password reset on {website_name}"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:171
|
||||
#: canaille/account.py:278
|
||||
msgid "Could not reset your password"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:190
|
||||
#: canaille/account.py:297
|
||||
msgid "The password reset link that brought you here was invalid."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/account.py:199
|
||||
#: canaille/account.py:306
|
||||
msgid "Your password has been updated successfuly"
|
||||
msgstr ""
|
||||
|
||||
|
@ -61,55 +84,60 @@ msgstr ""
|
|||
msgid "The access has been revoked"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:8 canaille/forms.py:25
|
||||
#: canaille/forms.py:9 canaille/forms.py:26
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:11 canaille/forms.py:28 canaille/forms.py:80
|
||||
#: canaille/forms.py:12 canaille/forms.py:29 canaille/forms.py:82
|
||||
msgid "jane@doe.com"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:18 canaille/forms.py:37 canaille/forms.py:93
|
||||
#: canaille/forms.py:19 canaille/forms.py:38 canaille/forms.py:101
|
||||
#: canaille/forms.py:111
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:40 canaille/forms.py:97
|
||||
#: canaille/forms.py:41 canaille/forms.py:105 canaille/forms.py:118
|
||||
msgid "Password confirmation"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:43 canaille/forms.py:102
|
||||
#: canaille/forms.py:44 canaille/forms.py:96
|
||||
msgid "Password and confirmation do not match."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:51
|
||||
#: canaille/forms.py:52
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:56
|
||||
msgid "Given name"
|
||||
#: canaille/forms.py:53
|
||||
msgid "jdoe"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:58
|
||||
msgid "Given name"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:60
|
||||
msgid "John"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:64
|
||||
#: canaille/forms.py:66
|
||||
msgid "Family Name"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:66
|
||||
#: canaille/forms.py:68
|
||||
msgid "Doe"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:77
|
||||
#: canaille/forms.py:79
|
||||
msgid "Email address"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:87
|
||||
#: canaille/forms.py:89 canaille/templates/users.html:24
|
||||
msgid "Phone number"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:87
|
||||
#: canaille/forms.py:89
|
||||
msgid "555-000-555"
|
||||
msgstr ""
|
||||
|
||||
|
@ -134,6 +162,7 @@ msgid "You have been successfully logged out."
|
|||
msgstr ""
|
||||
|
||||
#: canaille/admin/clients.py:24 canaille/templates/admin/client_list.html:22
|
||||
#: canaille/templates/users.html:22
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
|
@ -232,62 +261,66 @@ msgid "Accept"
|
|||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:8
|
||||
msgid "OpenID Connect Server"
|
||||
msgid "authorization interface"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:37 canaille/templates/profile.html:8
|
||||
#: canaille/templates/base.html:37 canaille/templates/profile.html:32
|
||||
msgid "My profile"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:42 canaille/templates/consent_list.html:18
|
||||
#: canaille/templates/base.html:42 canaille/templates/consent_list.html:19
|
||||
msgid "My consents"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:51
|
||||
#: canaille/templates/base.html:48
|
||||
msgid "Users"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:58
|
||||
msgid "Clients"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:55
|
||||
#: canaille/templates/base.html:62
|
||||
msgid "Tokens"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:59
|
||||
#: canaille/templates/base.html:66
|
||||
msgid "Codes"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:63
|
||||
#: canaille/templates/base.html:70
|
||||
msgid "Consents"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:70
|
||||
#: canaille/templates/base.html:77
|
||||
msgid "Log out"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:21
|
||||
#: canaille/templates/consent_list.html:22
|
||||
msgid "Consult and revoke the authorization you gave to websites."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:47
|
||||
#: canaille/templates/consent_list.html:42
|
||||
msgid "From:"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:49
|
||||
#: canaille/templates/consent_list.html:44
|
||||
msgid "Revoked:"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:52
|
||||
#: canaille/templates/consent_list.html:47
|
||||
msgid "Has access to:"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:62
|
||||
#: canaille/templates/consent_list.html:57
|
||||
msgid "Remove access"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:72
|
||||
#: canaille/templates/consent_list.html:67
|
||||
msgid "Nothing here"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:73
|
||||
#: canaille/templates/consent_list.html:68
|
||||
msgid "You did not authorize applications yet."
|
||||
msgstr ""
|
||||
|
||||
|
@ -307,12 +340,12 @@ msgstr ""
|
|||
msgid "Technical problem"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/forgotten-password.html:7
|
||||
#: canaille/templates/login.html:39
|
||||
#: canaille/templates/forgotten-password.html:12
|
||||
#: canaille/templates/login.html:34
|
||||
msgid "Forgotten password"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/forgotten-password.html:17
|
||||
#: canaille/templates/forgotten-password.html:19
|
||||
msgid ""
|
||||
"\n"
|
||||
" After this form is sent, if the email address or the login you "
|
||||
|
@ -323,40 +356,83 @@ msgid ""
|
|||
" "
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/forgotten-password.html:34
|
||||
#: canaille/templates/forgotten-password.html:36
|
||||
#: canaille/templates/profile.html:62
|
||||
msgid "Send"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/forgotten-password.html:35
|
||||
#: canaille/templates/forgotten-password.html:37
|
||||
msgid "Login page"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/login.html:14
|
||||
#: canaille/templates/login.html:15
|
||||
#, python-format
|
||||
msgid "Sign in at %(website)s"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/login.html:16
|
||||
#: canaille/templates/login.html:17
|
||||
msgid "Log-in and manage your authorizations."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/login.html:38
|
||||
#: canaille/templates/login.html:33
|
||||
msgid "Sign in"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:11
|
||||
#: canaille/templates/profile.html:14
|
||||
msgid "User deletion"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:17
|
||||
msgid ""
|
||||
"Are you sure you want to delete this user? This action is unrevokable and"
|
||||
" all the data about this user will be removed."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:20
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:21
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:30
|
||||
msgid "User creation"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:34
|
||||
msgid "User profile edition"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:40
|
||||
msgid "Create a new user account"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:42
|
||||
msgid "Edit your personal informations"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:23
|
||||
msgid "Edit"
|
||||
#: canaille/templates/profile.html:44
|
||||
msgid "Edit informations about an user"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/reset-password.html:7
|
||||
#: canaille/templates/reset-password.html:17
|
||||
#: canaille/templates/profile.html:66
|
||||
msgid "Delete the user"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/reset-password.html:12
|
||||
#: canaille/templates/reset-password.html:19
|
||||
msgid "Password reset"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/users.html:17
|
||||
msgid "Add a user"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/users.html:23
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/authorization_list.html:18
|
||||
#: canaille/templates/admin/token_list.html:18
|
||||
msgid "Token"
|
||||
|
@ -378,32 +454,32 @@ msgstr ""
|
|||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/authorization_view.html:7
|
||||
#: canaille/templates/admin/authorization_view.html:8
|
||||
msgid "View a authorization"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_add.html:7
|
||||
#: canaille/templates/admin/client_add.html:8
|
||||
msgid "Add a client"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_add.html:17
|
||||
#: canaille/templates/admin/client_edit.html:34
|
||||
#: canaille/templates/admin/client_add.html:14
|
||||
#: canaille/templates/admin/client_edit.html:31
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:7
|
||||
#: canaille/templates/admin/client_edit.html:8
|
||||
msgid "Edit a client"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:20
|
||||
#: canaille/templates/admin/client_edit.html:17
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:24
|
||||
#: canaille/templates/admin/client_edit.html:21
|
||||
msgid "Secret"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:28
|
||||
#: canaille/templates/admin/client_edit.html:25
|
||||
msgid "Issued at"
|
||||
msgstr ""
|
||||
|
||||
|
@ -415,7 +491,7 @@ msgstr ""
|
|||
msgid "URL"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/token_view.html:7
|
||||
#: canaille/templates/admin/token_view.html:8
|
||||
msgid "View a token"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -129,7 +129,8 @@ def app(slapd_server, keypair_path):
|
|||
"BIND_PW": slapd_server.root_pw,
|
||||
"USER_BASE": "ou=users",
|
||||
"USER_FILTER": "(|(uid={login})(cn={login}))",
|
||||
"ADMIN_FILTER": "uid=admin",
|
||||
"USER_CLASS": "inetOrgPerson",
|
||||
"ADMIN_FILTER": "(|(uid=admin)(sn=admin))",
|
||||
},
|
||||
"JWT": {
|
||||
"PUBLIC_KEY": public_key_path,
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
from canaille.models import User
|
||||
|
||||
|
||||
def test_profile(testclient, slapd_connection, logged_user):
|
||||
res = testclient.get("/profile/user", status=200)
|
||||
|
||||
|
@ -7,7 +10,7 @@ def test_profile(testclient, slapd_connection, logged_user):
|
|||
res.form["email"] = "email@mydomain.tld"
|
||||
res.form["phone_number"] = "555-666-777"
|
||||
|
||||
res = res.form.submit(status=200)
|
||||
res = res.form.submit(name="action", value="edit", status=200)
|
||||
|
||||
logged_user.reload(slapd_connection)
|
||||
|
||||
|
@ -26,7 +29,7 @@ def test_bad_email(testclient, slapd_connection, logged_user):
|
|||
|
||||
res.form["email"] = "john@doe.com"
|
||||
|
||||
res = res.form.submit(status=200)
|
||||
res = res.form.submit(name="action", value="edit", status=200)
|
||||
|
||||
assert ["john@doe.com"] == logged_user.mail
|
||||
|
||||
|
@ -34,7 +37,7 @@ def test_bad_email(testclient, slapd_connection, logged_user):
|
|||
|
||||
res.form["email"] = "yolo"
|
||||
|
||||
res = res.form.submit(status=200)
|
||||
res = res.form.submit(name="action", value="edit", status=200)
|
||||
|
||||
logged_user.reload(slapd_connection)
|
||||
|
||||
|
@ -47,7 +50,7 @@ def test_password_change(testclient, slapd_connection, logged_user):
|
|||
res.form["password1"] = "new_password"
|
||||
res.form["password2"] = "new_password"
|
||||
|
||||
res = res.form.submit(status=200)
|
||||
res = res.form.submit(name="action", value="edit", status=200)
|
||||
|
||||
with testclient.app.app_context():
|
||||
assert logged_user.check_password("new_password")
|
||||
|
@ -57,7 +60,7 @@ def test_password_change(testclient, slapd_connection, logged_user):
|
|||
res.form["password1"] = "correct horse battery staple"
|
||||
res.form["password2"] = "correct horse battery staple"
|
||||
|
||||
res = res.form.submit(status=200)
|
||||
res = res.form.submit(name="action", value="edit", status=200)
|
||||
|
||||
with testclient.app.app_context():
|
||||
assert logged_user.check_password("correct horse battery staple")
|
||||
|
@ -69,7 +72,7 @@ def test_password_change_fail(testclient, slapd_connection, logged_user):
|
|||
res.form["password1"] = "new_password"
|
||||
res.form["password2"] = "other_password"
|
||||
|
||||
res = res.form.submit(status=200)
|
||||
res = res.form.submit(name="action", value="edit", status=200)
|
||||
|
||||
with testclient.app.app_context():
|
||||
assert logged_user.check_password("correct horse battery staple")
|
||||
|
@ -79,7 +82,90 @@ def test_password_change_fail(testclient, slapd_connection, logged_user):
|
|||
res.form["password1"] = "new_password"
|
||||
res.form["password2"] = ""
|
||||
|
||||
res = res.form.submit(status=200)
|
||||
res = res.form.submit(name="action", value="edit", status=200)
|
||||
|
||||
with testclient.app.app_context():
|
||||
assert logged_user.check_password("correct horse battery staple")
|
||||
|
||||
|
||||
def test_simple_user_cannot_edit_other(testclient, logged_user):
|
||||
testclient.get("/profile/user", status=200)
|
||||
testclient.get("/profile/admin", status=403)
|
||||
testclient.post("/profile/admin", {"action": "edit"}, status=403)
|
||||
testclient.post("/profile/admin", {"action": "delete"}, status=403)
|
||||
testclient.get("/users", status=403)
|
||||
|
||||
|
||||
def test_admin_bad_request(testclient, logged_admin):
|
||||
testclient.post("/profile/admin", {"action": "foobar"}, status=400)
|
||||
testclient.get("/profile/foobar", status=404)
|
||||
|
||||
|
||||
def test_user_creation_edition_and_deletion(testclient, slapd_connection, logged_admin):
|
||||
# The user does not exist.
|
||||
res = testclient.get("/users", status=200)
|
||||
with testclient.app.app_context():
|
||||
assert User.get("george", conn=slapd_connection) is None
|
||||
assert "george" not in res.text
|
||||
|
||||
# Fill the profile for a new user.
|
||||
res = testclient.get("/profile", status=200)
|
||||
res.form["sub"] = "george"
|
||||
res.form["given_name"] = "George"
|
||||
res.form["family_name"] = "Abitbol"
|
||||
res.form["email"] = "george@abitbol.com"
|
||||
res.form["phone_number"] = "555-666-888"
|
||||
|
||||
# Passwords have been forgotten
|
||||
res = res.form.submit(name="action", value="edit", status=200)
|
||||
with testclient.app.app_context():
|
||||
assert User.get("george", conn=slapd_connection) is None
|
||||
res.form["password1"] = "totoyolo"
|
||||
res.form["password2"] = "totoyolo"
|
||||
|
||||
# User have been created
|
||||
res = res.form.submit(name="action", value="edit", status=302).follow(status=200)
|
||||
with testclient.app.app_context():
|
||||
assert "George" == User.get("george", conn=slapd_connection).givenName[0]
|
||||
assert "george" in testclient.get("/users", status=200).text
|
||||
res.form["given_name"] = "Georgio"
|
||||
|
||||
# User have been edited
|
||||
res = res.form.submit(name="action", value="edit", status=200)
|
||||
with testclient.app.app_context():
|
||||
assert "Georgio" == User.get("george", conn=slapd_connection).givenName[0]
|
||||
assert "george" in testclient.get("/users", status=200).text
|
||||
|
||||
# User have been deleted.
|
||||
res = res.form.submit(name="action", value="delete", status=302).follow(status=200)
|
||||
with testclient.app.app_context():
|
||||
assert User.get("george", conn=slapd_connection) is None
|
||||
assert "george" not in res.text
|
||||
|
||||
|
||||
def test_admin_self_deletion(testclient, slapd_connection):
|
||||
User.ocs_by_name(slapd_connection)
|
||||
admin = User(
|
||||
objectClass=["inetOrgPerson"],
|
||||
cn="Temp admin",
|
||||
sn="admin",
|
||||
uid="temp",
|
||||
mail="temp@temp.com",
|
||||
userPassword="{SSHA}Vmgh2jkD0idX3eZHf8RzGos31oerjGiU",
|
||||
)
|
||||
admin.save(slapd_connection)
|
||||
with testclient.session_transaction() as sess:
|
||||
sess["user_dn"] = admin.dn
|
||||
|
||||
res = testclient.get("/profile/temp")
|
||||
res = (
|
||||
res.form.submit(name="action", value="delete", status=302)
|
||||
.follow(status=302)
|
||||
.follow(status=200)
|
||||
)
|
||||
|
||||
with testclient.app.app_context():
|
||||
assert User.get("temp", conn=slapd_connection) is None
|
||||
|
||||
with testclient.session_transaction() as sess:
|
||||
"user_dn" not in sess
|
||||
|
|
Loading…
Reference in a new issue