User admin page. Fixes #8

This commit is contained in:
Éloi Rivard 2020-11-01 11:33:56 +01:00
parent 1300265a14
commit 4a20fb3b55
12 changed files with 612 additions and 152 deletions

View file

@ -17,8 +17,14 @@ from flask import (
) )
from flask_babel import gettext as _ from flask_babel import gettext as _
from .forms import LoginForm, ProfileForm, PasswordResetForm, ForgottenPasswordForm from .forms import (
from .flaskutils import current_user, user_needed LoginForm,
AddProfileForm,
EditProfileForm,
PasswordResetForm,
ForgottenPasswordForm,
)
from .flaskutils import current_user, user_needed, admin_needed
from .models import User from .models import User
@ -29,7 +35,9 @@ bp = Blueprint(__name__, "home")
def index(): def index():
if not current_user(): if not current_user():
return redirect(url_for("canaille.account.login")) 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")) @bp.route("/login", methods=("GET", "POST"))
@ -44,7 +52,7 @@ def login():
return render_template("login.html", form=form) return render_template("login.html", form=form)
user = User.get(form.login.data) 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 redirect(url_for("canaille.account.index"))
return render_template("login.html", form=form) return render_template("login.html", form=form)
@ -55,25 +63,87 @@ def logout():
user = current_user() user = current_user()
if user: if user:
flash( 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() user.logout()
return redirect("/") return redirect("/")
@bp.route("/profile/<subject>", methods=("GET", "POST")) @bp.route("/users")
@user_needed() @admin_needed()
def profile(user, subject): def users(user):
subject == user.uid[0] or abort(403) 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"] 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 = { data = {
k.lower(): getattr(user, v)[0] k.lower(): getattr(user, v)[0]
if getattr(user, v) and isinstance(getattr(user, v), list) if getattr(user, v) and isinstance(getattr(user, v), list)
else getattr(user, v) or "" else getattr(user, v) or ""
for k, v in claims.items() 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" form.sub.render_kw["readonly"] = "true"
if request.form: if request.form:
@ -83,7 +153,10 @@ def profile(user, subject):
else: else:
for attribute in form: for attribute in form:
model_attribute_name = claims.get(attribute.name.upper()) 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 continue
user[model_attribute_name] = [attribute.data] user[model_attribute_name] = [attribute.data]
@ -93,7 +166,24 @@ def profile(user, subject):
user.save() 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): def profile_hash(user, password):
@ -214,6 +304,6 @@ def reset(uid, hash):
user.login() user.login()
flash(_("Your password has been updated successfuly"), "success") 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) return render_template("reset-password.html", form=form, uid=uid, hash=hash)

View file

@ -36,6 +36,9 @@ USER_BASE = "ou=users,dc=mydomain,dc=tld"
# USER_FILTER = "(|(uid={login})(mail={login}))" # USER_FILTER = "(|(uid={login})(mail={login}))"
USER_FILTER = "(|(uid={login})(cn={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 # Filter to match admin users. If your server has memberof
# you can filter against group membership # you can filter against group membership
# ADMIN_FILTER = "uid=admin" # ADMIN_FILTER = "uid=admin"

View file

@ -51,6 +51,7 @@ class ProfileForm(FlaskForm):
sub = wtforms.StringField( sub = wtforms.StringField(
_("Username"), _("Username"),
render_kw={"placeholder": _("jdoe")}, render_kw={"placeholder": _("jdoe")},
validators=[wtforms.validators.DataRequired()],
) )
# name = wtforms.StringField(_("Name")) # name = wtforms.StringField(_("Name"))
given_name = wtforms.StringField( given_name = wtforms.StringField(
@ -90,6 +91,12 @@ class ProfileForm(FlaskForm):
# picture = wtforms.StringField(_("Photo")) # picture = wtforms.StringField(_("Photo"))
# website = wtforms.fields.html5.URLField(_("Website")) # 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( password1 = wtforms.PasswordField(
_("Password"), _("Password"),
validators=[wtforms.validators.Optional(), wtforms.validators.Length(min=8)], validators=[wtforms.validators.Optional(), wtforms.validators.Length(min=8)],
@ -98,6 +105,15 @@ class ProfileForm(FlaskForm):
_("Password confirmation"), _("Password confirmation"),
) )
def validate_password2(self, field):
if self.password1.data and self.password1.data != field.data: class AddProfileForm(ProfileForm):
raise wtforms.ValidationError(_("Password and confirmation do not match.")) password1 = wtforms.PasswordField(
_("Password"),
validators=[
wtforms.validators.DataRequired(),
wtforms.validators.Length(min=8),
],
)
password2 = wtforms.PasswordField(
_("Password confirmation"),
)

View file

@ -0,0 +1,10 @@
$('.confirm').click(function(e){
e.preventDefault();
$('.ui.modal')
.modal({
onApprove : function() {
$('.confirm').unbind('click').click();
},
})
.modal('show');
});

View file

@ -32,7 +32,7 @@
</div> </div>
{% endif %} {% endif %}
<a class="item {% if menuitem == "profile" %}active{% 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> <i class="id card icon"></i>
{% trans %}My profile{% endtrans %} {% trans %}My profile{% endtrans %}
</a> </a>
@ -42,6 +42,13 @@
{% trans %}My consents{% endtrans %} {% trans %}My consents{% endtrans %}
</a> </a>
{% if user.admin %} {% 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 %}"> <div class="ui dropdown item {% if menuitem == "admin" %}active{% endif %}">
<i class="settings icon"></i> <i class="settings icon"></i>
Admin Admin

View file

@ -2,18 +2,70 @@
{% import 'fomanticui.j2' as sui %} {% import 'fomanticui.j2' as sui %}
{% import 'flask.j2' as flask %} {% import 'flask.j2' as flask %}
{% block script %}
<script src="/static/js/profile.js"></script>
{% endblock %}
{% block content %} {% 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"> <div class="ui clearing segment">
<h2 class="ui center aligned header"> <h2 class="ui center aligned header">
<div class="content"> <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>
<div class="sub header"> <div class="sub header">
{% if not edited_user %}
{% trans %}Create a new user account{% endtrans %}
{% elif user.uid == edited_user.uid %}
{% trans %}Edit your personal informations{% endtrans %} {% trans %}Edit your personal informations{% endtrans %}
{% else %}
{% trans %}Edit informations about an user{% endtrans %}
{% endif %}
</div> </div>
</h2> </h2>
<form method="POST"
id="{{ form.__class__.__name__|lower }}"
action="{{ request.url }}"
role="form"
enctype="multipart/form-data"
class="ui form"
>
{{ flask.messages() }} {{ flask.messages() }}
{{ sui.render_form(form, _("Edit"), action=request.url) }}
{{ 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> </div>
{% endblock %} {% endblock %}

View 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 %}

View file

@ -8,8 +8,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: contact@yaal.fr\n" "Report-Msgid-Bugs-To: contact@yaal.fr\n"
"POT-Creation-Date: 2020-10-29 15:26+0100\n" "POT-Creation-Date: 2020-11-01 12:32+0100\n"
"PO-Revision-Date: 2020-10-29 15:27+0100\n" "PO-Revision-Date: 2020-11-01 12:38+0100\n"
"Last-Translator: Éloi Rivard <eloi@yaal.fr>\n" "Last-Translator: Éloi Rivard <eloi@yaal.fr>\n"
"Language: fr_FR\n" "Language: fr_FR\n"
"Language-Team: French - France <equipe@yaal.fr>\n" "Language-Team: French - France <equipe@yaal.fr>\n"
@ -20,39 +20,62 @@ msgstr ""
"Generated-By: Babel 2.8.0\n" "Generated-By: Babel 2.8.0\n"
"X-Generator: Gtranslator 3.38.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" msgid "Login failed, please check your information"
msgstr "La connexion a échoué, veuillez vérifier vos informations." 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." msgid "Profile edition failed."
msgstr "L'édition du profil a échoué." msgstr "L'édition du profil a échoué."
#: canaille/account.py:75 #: canaille/account.py:165
msgid "Profile updated successfuly." msgid "Profile updated successfuly."
msgstr "Le profil a été mis à jour avec succès." 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." msgid "Could not send the password reset link."
msgstr "Impossible d'envoyer le lien de réinitialisation." 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." msgid "A password reset link has been sent at your email address."
msgstr "Un lien de réinitialisation vous a été envoyé à votre adresse." 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}" msgid "Password reset on {website_name}"
msgstr "Réinitialisation du mot de passe sur {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" msgid "Could not reset your password"
msgstr "Impossible de réinitialiser votre mot de passe" 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." msgid "The password reset link that brought you here was invalid."
msgstr "Le lien de réinitialisation qui vous a amené ici est invalide." 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" msgid "Your password has been updated successfuly"
msgstr "Votre mot de passe a correctement été mis à jour." 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" msgid "The access has been revoked"
msgstr "L'accès a été révoqué." 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" msgid "Login"
msgstr "Identifiant" 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" msgid "jane@doe.com"
msgstr "martin@dupont.fr" 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" msgid "Password"
msgstr "Mot de passe" 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" msgid "Password confirmation"
msgstr "Confirmation du mot de passe" 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." msgid "Password and confirmation do not match."
msgstr "Le mot de passe et sa confirmation ne correspondent pas." msgstr "Le mot de passe et sa confirmation ne correspondent pas."
#: canaille/forms.py:51 #: canaille/forms.py:52
msgid "Username" msgid "Username"
msgstr "Identifiant" msgstr "Identifiant"
#: canaille/forms.py:56 #: canaille/forms.py:53
msgid "jdoe"
msgstr "mdupont"
#: canaille/forms.py:58
msgid "Given name" msgid "Given name"
msgstr "Prénom" msgstr "Prénom"
#: canaille/forms.py:58 #: canaille/forms.py:60
msgid "John" msgid "John"
msgstr "Martin" msgstr "Martin"
#: canaille/forms.py:64 #: canaille/forms.py:66
msgid "Family Name" msgid "Family Name"
msgstr "Nom de famille" msgstr "Nom de famille"
#: canaille/forms.py:66 #: canaille/forms.py:68
msgid "Doe" msgid "Doe"
msgstr "Dupont" msgstr "Dupont"
#: canaille/forms.py:77 #: canaille/forms.py:79
msgid "Email address" msgid "Email address"
msgstr "Courriel" msgstr "Courriel"
#: canaille/forms.py:87 #: canaille/forms.py:89 canaille/templates/users.html:24
msgid "Phone number" msgid "Phone number"
msgstr "Numéro de téléphone" msgstr "Numéro de téléphone"
#: canaille/forms.py:87 #: canaille/forms.py:89
msgid "555-000-555" msgid "555-000-555"
msgstr "06 01 02 03 04" msgstr "06 01 02 03 04"
@ -137,6 +165,7 @@ msgid "You have been successfully logged out."
msgstr "Vous avez été déconnectés." msgstr "Vous avez été déconnectés."
#: canaille/admin/clients.py:24 canaille/templates/admin/client_list.html:22 #: canaille/admin/clients.py:24 canaille/templates/admin/client_list.html:22
#: canaille/templates/users.html:22
msgid "Name" msgid "Name"
msgstr "Nom" msgstr "Nom"
@ -235,62 +264,66 @@ msgid "Accept"
msgstr "Accepter" msgstr "Accepter"
#: canaille/templates/base.html:8 #: canaille/templates/base.html:8
msgid "OpenID Connect Server" msgid "authorization interface"
msgstr "OpenID Connect Server" 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" msgid "My profile"
msgstr "Mon profil" 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" msgid "My consents"
msgstr "Mes autorisations" msgstr "Mes autorisations"
#: canaille/templates/base.html:51 #: canaille/templates/base.html:48
msgid "Users"
msgstr "Utilisateurs"
#: canaille/templates/base.html:58
msgid "Clients" msgid "Clients"
msgstr "Clients" msgstr "Clients"
#: canaille/templates/base.html:55 #: canaille/templates/base.html:62
msgid "Tokens" msgid "Tokens"
msgstr "Jetons" msgstr "Jetons"
#: canaille/templates/base.html:59 #: canaille/templates/base.html:66
msgid "Codes" msgid "Codes"
msgstr "Codes" msgstr "Codes"
#: canaille/templates/base.html:63 #: canaille/templates/base.html:70
msgid "Consents" msgid "Consents"
msgstr "Autorisations" msgstr "Autorisations"
#: canaille/templates/base.html:70 #: canaille/templates/base.html:77
msgid "Log out" msgid "Log out"
msgstr "Déconnexion" 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." msgid "Consult and revoke the authorization you gave to websites."
msgstr "Consultez et révoquez les autorisation que vous avez données." 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:" msgid "From:"
msgstr "À partir de :" msgstr "À partir de :"
#: canaille/templates/consent_list.html:49 #: canaille/templates/consent_list.html:44
msgid "Revoked:" msgid "Revoked:"
msgstr "Révoqué le :" msgstr "Révoqué le :"
#: canaille/templates/consent_list.html:52 #: canaille/templates/consent_list.html:47
msgid "Has access to:" msgid "Has access to:"
msgstr "A accès à :" msgstr "A accès à :"
#: canaille/templates/consent_list.html:62 #: canaille/templates/consent_list.html:57
msgid "Remove access" msgid "Remove access"
msgstr "Supprimer l'accès" msgstr "Supprimer l'accès"
#: canaille/templates/consent_list.html:72 #: canaille/templates/consent_list.html:67
msgid "Nothing here" msgid "Nothing here"
msgstr "Rien ici" msgstr "Rien ici"
#: canaille/templates/consent_list.html:73 #: canaille/templates/consent_list.html:68
msgid "You did not authorize applications yet." msgid "You did not authorize applications yet."
msgstr "" msgstr ""
"Vous n'avez pas encore autorisé d'application à accéder à votre profil." "Vous n'avez pas encore autorisé d'application à accéder à votre profil."
@ -311,12 +344,12 @@ msgstr "Page non trouvée"
msgid "Technical problem" msgid "Technical problem"
msgstr "Problème technique" msgstr "Problème technique"
#: canaille/templates/forgotten-password.html:7 #: canaille/templates/forgotten-password.html:12
#: canaille/templates/login.html:39 #: canaille/templates/login.html:34
msgid "Forgotten password" msgid "Forgotten password"
msgstr "Mot de passe oublié" msgstr "Mot de passe oublié"
#: canaille/templates/forgotten-password.html:17 #: canaille/templates/forgotten-password.html:19
msgid "" msgid ""
"\n" "\n"
" After this form is sent, if the email address or the login you " " 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" " 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" msgid "Send"
msgstr "Envoyer" msgstr "Envoyer"
#: canaille/templates/forgotten-password.html:35 #: canaille/templates/forgotten-password.html:37
msgid "Login page" msgid "Login page"
msgstr "Page de connexion" msgstr "Page de connexion"
#: canaille/templates/login.html:14 #: canaille/templates/login.html:15
#, python-format #, python-format
msgid "Sign in at %(website)s" msgid "Sign in at %(website)s"
msgstr "Connexion à %(website)s" msgstr "Connexion à %(website)s"
#: canaille/templates/login.html:16 #: canaille/templates/login.html:17
msgid "Log-in and manage your authorizations." msgid "Log-in and manage your authorizations."
msgstr "Connectez-vous et gérez vos autorisations." msgstr "Connectez-vous et gérez vos autorisations."
#: canaille/templates/login.html:38 #: canaille/templates/login.html:33
msgid "Sign in" msgid "Sign in"
msgstr "Se connecter" 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" msgid "Edit your personal informations"
msgstr "Éditez vos informations personnelles" msgstr "Éditez vos informations personnelles"
#: canaille/templates/profile.html:23 #: canaille/templates/profile.html:44
msgid "Edit" msgid "Edit informations about an user"
msgstr "Éditer" msgstr "Éditez les informations d'un utilisateur"
#: canaille/templates/reset-password.html:7 #: canaille/templates/profile.html:66
#: canaille/templates/reset-password.html:17 msgid "Delete the user"
msgstr "Supprimer l'utilisateur"
#: canaille/templates/reset-password.html:12
#: canaille/templates/reset-password.html:19
msgid "Password reset" msgid "Password reset"
msgstr "Réinitialisation du mot de passe" 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/authorization_list.html:18
#: canaille/templates/admin/token_list.html:18 #: canaille/templates/admin/token_list.html:18
msgid "Token" msgid "Token"
@ -388,32 +466,32 @@ msgstr "Utilisateur"
msgid "Created" msgid "Created"
msgstr "Créé" msgstr "Créé"
#: canaille/templates/admin/authorization_view.html:7 #: canaille/templates/admin/authorization_view.html:8
msgid "View a authorization" msgid "View a authorization"
msgstr "Voir une autorisation" msgstr "Voir une autorisation"
#: canaille/templates/admin/client_add.html:7 #: canaille/templates/admin/client_add.html:8
msgid "Add a client" msgid "Add a client"
msgstr "Ajouter un client" msgstr "Ajouter un client"
#: canaille/templates/admin/client_add.html:17 #: canaille/templates/admin/client_add.html:14
#: canaille/templates/admin/client_edit.html:34 #: canaille/templates/admin/client_edit.html:31
msgid "Confirm" msgid "Confirm"
msgstr "Confirmer" msgstr "Confirmer"
#: canaille/templates/admin/client_edit.html:7 #: canaille/templates/admin/client_edit.html:8
msgid "Edit a client" msgid "Edit a client"
msgstr "Éditer un client" msgstr "Éditer un client"
#: canaille/templates/admin/client_edit.html:20 #: canaille/templates/admin/client_edit.html:17
msgid "ID" msgid "ID"
msgstr "ID" msgstr "ID"
#: canaille/templates/admin/client_edit.html:24 #: canaille/templates/admin/client_edit.html:21
msgid "Secret" msgid "Secret"
msgstr "Secret" msgstr "Secret"
#: canaille/templates/admin/client_edit.html:28 #: canaille/templates/admin/client_edit.html:25
msgid "Issued at" msgid "Issued at"
msgstr "Créé le" msgstr "Créé le"
@ -425,7 +503,7 @@ msgstr "Ajouter un client"
msgid "URL" msgid "URL"
msgstr "URL" msgstr "URL"
#: canaille/templates/admin/token_view.html:7 #: canaille/templates/admin/token_view.html:8
msgid "View a token" msgid "View a token"
msgstr "Voir un jeton" msgstr "Voir un jeton"
@ -499,3 +577,9 @@ msgstr ""
#~ msgid "from: %(user)s" #~ msgid "from: %(user)s"
#~ msgstr "pour : %(user)s" #~ msgstr "pour : %(user)s"
#~ msgid "OpenID Connect Server"
#~ msgstr "OpenID Connect Server"
#~ msgid "Edit"
#~ msgstr "Éditer"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,39 +17,62 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.8.0\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" msgid "Login failed, please check your information"
msgstr "" 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." msgid "Profile edition failed."
msgstr "" msgstr ""
#: canaille/account.py:75 #: canaille/account.py:165
msgid "Profile updated successfuly." msgid "Profile updated successfuly."
msgstr "" 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." msgid "Could not send the password reset link."
msgstr "" 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." msgid "A password reset link has been sent at your email address."
msgstr "" msgstr ""
#: canaille/account.py:126 #: canaille/account.py:233
msgid "Password reset on {website_name}" msgid "Password reset on {website_name}"
msgstr "" msgstr ""
#: canaille/account.py:171 #: canaille/account.py:278
msgid "Could not reset your password" msgid "Could not reset your password"
msgstr "" msgstr ""
#: canaille/account.py:190 #: canaille/account.py:297
msgid "The password reset link that brought you here was invalid." msgid "The password reset link that brought you here was invalid."
msgstr "" msgstr ""
#: canaille/account.py:199 #: canaille/account.py:306
msgid "Your password has been updated successfuly" msgid "Your password has been updated successfuly"
msgstr "" msgstr ""
@ -61,55 +84,60 @@ msgstr ""
msgid "The access has been revoked" msgid "The access has been revoked"
msgstr "" msgstr ""
#: canaille/forms.py:8 canaille/forms.py:25 #: canaille/forms.py:9 canaille/forms.py:26
msgid "Login" msgid "Login"
msgstr "" 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" msgid "jane@doe.com"
msgstr "" 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" msgid "Password"
msgstr "" msgstr ""
#: canaille/forms.py:40 canaille/forms.py:97 #: canaille/forms.py:41 canaille/forms.py:105 canaille/forms.py:118
msgid "Password confirmation" msgid "Password confirmation"
msgstr "" msgstr ""
#: canaille/forms.py:43 canaille/forms.py:102 #: canaille/forms.py:44 canaille/forms.py:96
msgid "Password and confirmation do not match." msgid "Password and confirmation do not match."
msgstr "" msgstr ""
#: canaille/forms.py:51 #: canaille/forms.py:52
msgid "Username" msgid "Username"
msgstr "" msgstr ""
#: canaille/forms.py:56 #: canaille/forms.py:53
msgid "Given name" msgid "jdoe"
msgstr "" msgstr ""
#: canaille/forms.py:58 #: canaille/forms.py:58
msgid "Given name"
msgstr ""
#: canaille/forms.py:60
msgid "John" msgid "John"
msgstr "" msgstr ""
#: canaille/forms.py:64 #: canaille/forms.py:66
msgid "Family Name" msgid "Family Name"
msgstr "" msgstr ""
#: canaille/forms.py:66 #: canaille/forms.py:68
msgid "Doe" msgid "Doe"
msgstr "" msgstr ""
#: canaille/forms.py:77 #: canaille/forms.py:79
msgid "Email address" msgid "Email address"
msgstr "" msgstr ""
#: canaille/forms.py:87 #: canaille/forms.py:89 canaille/templates/users.html:24
msgid "Phone number" msgid "Phone number"
msgstr "" msgstr ""
#: canaille/forms.py:87 #: canaille/forms.py:89
msgid "555-000-555" msgid "555-000-555"
msgstr "" msgstr ""
@ -134,6 +162,7 @@ msgid "You have been successfully logged out."
msgstr "" msgstr ""
#: canaille/admin/clients.py:24 canaille/templates/admin/client_list.html:22 #: canaille/admin/clients.py:24 canaille/templates/admin/client_list.html:22
#: canaille/templates/users.html:22
msgid "Name" msgid "Name"
msgstr "" msgstr ""
@ -232,62 +261,66 @@ msgid "Accept"
msgstr "" msgstr ""
#: canaille/templates/base.html:8 #: canaille/templates/base.html:8
msgid "OpenID Connect Server" msgid "authorization interface"
msgstr "" msgstr ""
#: canaille/templates/base.html:37 canaille/templates/profile.html:8 #: canaille/templates/base.html:37 canaille/templates/profile.html:32
msgid "My profile" msgid "My profile"
msgstr "" 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" msgid "My consents"
msgstr "" msgstr ""
#: canaille/templates/base.html:51 #: canaille/templates/base.html:48
msgid "Users"
msgstr ""
#: canaille/templates/base.html:58
msgid "Clients" msgid "Clients"
msgstr "" msgstr ""
#: canaille/templates/base.html:55 #: canaille/templates/base.html:62
msgid "Tokens" msgid "Tokens"
msgstr "" msgstr ""
#: canaille/templates/base.html:59 #: canaille/templates/base.html:66
msgid "Codes" msgid "Codes"
msgstr "" msgstr ""
#: canaille/templates/base.html:63 #: canaille/templates/base.html:70
msgid "Consents" msgid "Consents"
msgstr "" msgstr ""
#: canaille/templates/base.html:70 #: canaille/templates/base.html:77
msgid "Log out" msgid "Log out"
msgstr "" msgstr ""
#: canaille/templates/consent_list.html:21 #: canaille/templates/consent_list.html:22
msgid "Consult and revoke the authorization you gave to websites." msgid "Consult and revoke the authorization you gave to websites."
msgstr "" msgstr ""
#: canaille/templates/consent_list.html:47 #: canaille/templates/consent_list.html:42
msgid "From:" msgid "From:"
msgstr "" msgstr ""
#: canaille/templates/consent_list.html:49 #: canaille/templates/consent_list.html:44
msgid "Revoked:" msgid "Revoked:"
msgstr "" msgstr ""
#: canaille/templates/consent_list.html:52 #: canaille/templates/consent_list.html:47
msgid "Has access to:" msgid "Has access to:"
msgstr "" msgstr ""
#: canaille/templates/consent_list.html:62 #: canaille/templates/consent_list.html:57
msgid "Remove access" msgid "Remove access"
msgstr "" msgstr ""
#: canaille/templates/consent_list.html:72 #: canaille/templates/consent_list.html:67
msgid "Nothing here" msgid "Nothing here"
msgstr "" msgstr ""
#: canaille/templates/consent_list.html:73 #: canaille/templates/consent_list.html:68
msgid "You did not authorize applications yet." msgid "You did not authorize applications yet."
msgstr "" msgstr ""
@ -307,12 +340,12 @@ msgstr ""
msgid "Technical problem" msgid "Technical problem"
msgstr "" msgstr ""
#: canaille/templates/forgotten-password.html:7 #: canaille/templates/forgotten-password.html:12
#: canaille/templates/login.html:39 #: canaille/templates/login.html:34
msgid "Forgotten password" msgid "Forgotten password"
msgstr "" msgstr ""
#: canaille/templates/forgotten-password.html:17 #: canaille/templates/forgotten-password.html:19
msgid "" msgid ""
"\n" "\n"
" After this form is sent, if the email address or the login you " " After this form is sent, if the email address or the login you "
@ -323,40 +356,83 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: canaille/templates/forgotten-password.html:34 #: canaille/templates/forgotten-password.html:36
#: canaille/templates/profile.html:62
msgid "Send" msgid "Send"
msgstr "" msgstr ""
#: canaille/templates/forgotten-password.html:35 #: canaille/templates/forgotten-password.html:37
msgid "Login page" msgid "Login page"
msgstr "" msgstr ""
#: canaille/templates/login.html:14 #: canaille/templates/login.html:15
#, python-format #, python-format
msgid "Sign in at %(website)s" msgid "Sign in at %(website)s"
msgstr "" msgstr ""
#: canaille/templates/login.html:16 #: canaille/templates/login.html:17
msgid "Log-in and manage your authorizations." msgid "Log-in and manage your authorizations."
msgstr "" msgstr ""
#: canaille/templates/login.html:38 #: canaille/templates/login.html:33
msgid "Sign in" msgid "Sign in"
msgstr "" 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" msgid "Edit your personal informations"
msgstr "" msgstr ""
#: canaille/templates/profile.html:23 #: canaille/templates/profile.html:44
msgid "Edit" msgid "Edit informations about an user"
msgstr "" msgstr ""
#: canaille/templates/reset-password.html:7 #: canaille/templates/profile.html:66
#: canaille/templates/reset-password.html:17 msgid "Delete the user"
msgstr ""
#: canaille/templates/reset-password.html:12
#: canaille/templates/reset-password.html:19
msgid "Password reset" msgid "Password reset"
msgstr "" 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/authorization_list.html:18
#: canaille/templates/admin/token_list.html:18 #: canaille/templates/admin/token_list.html:18
msgid "Token" msgid "Token"
@ -378,32 +454,32 @@ msgstr ""
msgid "Created" msgid "Created"
msgstr "" msgstr ""
#: canaille/templates/admin/authorization_view.html:7 #: canaille/templates/admin/authorization_view.html:8
msgid "View a authorization" msgid "View a authorization"
msgstr "" msgstr ""
#: canaille/templates/admin/client_add.html:7 #: canaille/templates/admin/client_add.html:8
msgid "Add a client" msgid "Add a client"
msgstr "" msgstr ""
#: canaille/templates/admin/client_add.html:17 #: canaille/templates/admin/client_add.html:14
#: canaille/templates/admin/client_edit.html:34 #: canaille/templates/admin/client_edit.html:31
msgid "Confirm" msgid "Confirm"
msgstr "" msgstr ""
#: canaille/templates/admin/client_edit.html:7 #: canaille/templates/admin/client_edit.html:8
msgid "Edit a client" msgid "Edit a client"
msgstr "" msgstr ""
#: canaille/templates/admin/client_edit.html:20 #: canaille/templates/admin/client_edit.html:17
msgid "ID" msgid "ID"
msgstr "" msgstr ""
#: canaille/templates/admin/client_edit.html:24 #: canaille/templates/admin/client_edit.html:21
msgid "Secret" msgid "Secret"
msgstr "" msgstr ""
#: canaille/templates/admin/client_edit.html:28 #: canaille/templates/admin/client_edit.html:25
msgid "Issued at" msgid "Issued at"
msgstr "" msgstr ""
@ -415,7 +491,7 @@ msgstr ""
msgid "URL" msgid "URL"
msgstr "" msgstr ""
#: canaille/templates/admin/token_view.html:7 #: canaille/templates/admin/token_view.html:8
msgid "View a token" msgid "View a token"
msgstr "" msgstr ""

View file

@ -129,7 +129,8 @@ def app(slapd_server, keypair_path):
"BIND_PW": slapd_server.root_pw, "BIND_PW": slapd_server.root_pw,
"USER_BASE": "ou=users", "USER_BASE": "ou=users",
"USER_FILTER": "(|(uid={login})(cn={login}))", "USER_FILTER": "(|(uid={login})(cn={login}))",
"ADMIN_FILTER": "uid=admin", "USER_CLASS": "inetOrgPerson",
"ADMIN_FILTER": "(|(uid=admin)(sn=admin))",
}, },
"JWT": { "JWT": {
"PUBLIC_KEY": public_key_path, "PUBLIC_KEY": public_key_path,

View file

@ -1,3 +1,6 @@
from canaille.models import User
def test_profile(testclient, slapd_connection, logged_user): def test_profile(testclient, slapd_connection, logged_user):
res = testclient.get("/profile/user", status=200) 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["email"] = "email@mydomain.tld"
res.form["phone_number"] = "555-666-777" 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) 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.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 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.form["email"] = "yolo"
res = res.form.submit(status=200) res = res.form.submit(name="action", value="edit", status=200)
logged_user.reload(slapd_connection) 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["password1"] = "new_password"
res.form["password2"] = "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(): with testclient.app.app_context():
assert logged_user.check_password("new_password") 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["password1"] = "correct horse battery staple"
res.form["password2"] = "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(): with testclient.app.app_context():
assert logged_user.check_password("correct horse battery staple") 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["password1"] = "new_password"
res.form["password2"] = "other_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(): with testclient.app.app_context():
assert logged_user.check_password("correct horse battery staple") 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["password1"] = "new_password"
res.form["password2"] = "" res.form["password2"] = ""
res = res.form.submit(status=200) res = res.form.submit(name="action", value="edit", status=200)
with testclient.app.app_context(): with testclient.app.app_context():
assert logged_user.check_password("correct horse battery staple") 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