Users can delete their own accounts. #35

This commit is contained in:
Éloi Rivard 2021-01-01 16:42:13 +01:00
parent 801031f3b1
commit cbe06cc128
9 changed files with 166 additions and 90 deletions

View file

@ -20,6 +20,7 @@ Added
- Improved consents page. :issue:`27`
- Admin user page. :issue:`8`
- Project logo. :pr:`29`
- User account self-deletion can be enabled in the configuration with `SELF_DELETION`. :issue:`35`
- Admins can impersonate users. :issue:`39`
- Forgotten page UX improvement. :pr:`43`
- Admins can remove clients. :pr:`45`

View file

@ -190,7 +190,11 @@ def profile_creation(user):
)
return render_template(
"profile.html", form=form, menuitem="users", edited_user=None
"profile.html",
form=form,
menuitem="users",
edited_user=None,
self_deletion=False,
)
@ -256,7 +260,11 @@ def profile_edit(user, username):
user.save()
return render_template(
"profile.html", form=form, menuitem=menuitem, edited_user=user
"profile.html",
form=form,
menuitem=menuitem,
edited_user=user,
self_deletion=current_app.config.get("SELF_DELETION", True),
)

View file

@ -7,14 +7,20 @@
{% endblock %}
{% block content %}
{% if user.moderator and edited_user %}
{% if self_deletion or (user.moderator and edited_user) %}
<div class="ui basic modal">
<div class="ui icon header">
<i class="user minus icon"></i>
{% trans %}User deletion{% endtrans %}
{% trans %}Account 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>
<p>
{% if user.uid != edited_user.uid %}
{{ _("Are you sure you want to delete this user? This action is unrevokable and all the data about this user will be removed.") }}
{% else %}
{{ _("Are you sure you want to delete your account? This action is unrevokable and all your data will be removed forever.") }}
{% endif %}
</p>
</div>
<div class="actions">
<div class="ui inverted cancel button">{% trans %}Cancel{% endtrans %}</div>
@ -96,7 +102,7 @@
{{ _("Impersonate") }}
</a>
{% endif %}
{% if user.moderator and edited_user %}
{% if user.moderator and edited_user or self_deletion %}
<button type="submit" class="ui right floated basic negative button confirm" name="action" value="delete" id="delete">
{% if user.uid != edited_user.uid %}
{{ _("Delete the user") }}

View file

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: contact@yaal.fr\n"
"POT-Creation-Date: 2021-01-01 15:28+0100\n"
"PO-Revision-Date: 2021-01-01 15:29+0100\n"
"POT-Creation-Date: 2021-01-01 16:40+0100\n"
"PO-Revision-Date: 2021-01-01 16:41+0100\n"
"Last-Translator: Éloi Rivard <eloi@yaal.fr>\n"
"Language: fr_FR\n"
"Language-Team: French - France <equipe@yaal.fr>\n"
@ -58,7 +58,7 @@ msgstr "Impossible d'envoyer le courriel d'initialisation de mot de passe."
msgid "User creation failed."
msgstr "La création de l'utilisateur a échoué."
#: canaille/account.py:181 canaille/account.py:254
#: canaille/account.py:181 canaille/account.py:258
msgid "Profile updated successfuly."
msgstr "Le profil a été mis à jour avec succès."
@ -66,20 +66,20 @@ msgstr "Le profil a été mis à jour avec succès."
msgid "User creation succeed."
msgstr "La création de l'utilisateur a réussi."
#: canaille/account.py:238
#: canaille/account.py:242
msgid "Profile edition failed."
msgstr "L'édition du profil a échoué."
#: canaille/account.py:270
#: canaille/account.py:278
#, python-format
msgid "The user %(user)s has been sucessfuly deleted"
msgstr "L'utilisateur %(user)s a bien été supprimé"
#: canaille/account.py:293
#: canaille/account.py:301
msgid "Could not send the password reset link."
msgstr "Impossible d'envoyer le lien de réinitialisation."
#: canaille/account.py:300 canaille/account.py:345
#: canaille/account.py:308 canaille/account.py:353
msgid ""
"A password reset link has been sent at your email address. You should "
"receive it within 10 minutes."
@ -87,19 +87,19 @@ msgstr ""
"Un lien de ré-initialisation de votre mot de passe vous a été envoyé par "
"mail. Vous devriez le recevoir d'ici une dizaine de minutes."
#: canaille/account.py:318 canaille/admin/mail.py:29
#: canaille/account.py:326 canaille/admin/mail.py:29
msgid "Password reset on {website_name}"
msgstr "Réinitialisation du mot de passe sur {website_name}"
#: canaille/account.py:351
#: canaille/account.py:359
msgid "Could not reset your password"
msgstr "Impossible de réinitialiser votre mot de passe"
#: canaille/account.py:365
#: canaille/account.py:373
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:374
#: canaille/account.py:382
msgid "Your password has been updated successfuly"
msgstr "Votre mot de passe a correctement été mis à jour."
@ -332,7 +332,7 @@ msgstr "Accepter"
msgid "authorization interface"
msgstr " - Interface de gestion des autorisations"
#: canaille/templates/base.html:37 canaille/templates/profile.html:32
#: canaille/templates/base.html:37 canaille/templates/profile.html:38
msgid "My profile"
msgstr "Mon profil"
@ -471,7 +471,7 @@ msgid "Send again"
msgstr "Envoyer à nouveau"
#: canaille/templates/forgotten-password.html:40
#: canaille/templates/profile.html:92
#: canaille/templates/profile.html:98
msgid "Send"
msgstr "Envoyer"
@ -489,10 +489,10 @@ msgid "Sign in"
msgstr "Se connecter"
#: canaille/templates/profile.html:14
msgid "User deletion"
msgstr "Suppression d'un utilisateur"
msgid "Account deletion"
msgstr "Suppression d'un compte"
#: canaille/templates/profile.html:17
#: canaille/templates/profile.html:19
msgid ""
"Are you sure you want to delete this user? This action is unrevokable and "
"all the data about this user will be removed."
@ -500,53 +500,61 @@ 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:21
msgid ""
"Are you sure you want to delete your account? This action is unrevokable and "
"all your data will be removed forever."
msgstr ""
"Êtes-vous sûrs de vouloir supprimer votre compte ? Cette action est "
"irrévocable et toutes vos données seront supprimées pour toujours."
#: canaille/templates/admin/client_edit.html:19
#: canaille/templates/profile.html:20
#: canaille/templates/profile.html:26
msgid "Cancel"
msgstr "Annuler"
#: canaille/templates/admin/client_edit.html:20
#: canaille/templates/profile.html:21
#: canaille/templates/profile.html:27
msgid "Delete"
msgstr "Supprimer"
#: canaille/templates/profile.html:30
#: canaille/templates/profile.html:36
msgid "User creation"
msgstr "Nouvel utilisateur"
#: canaille/templates/profile.html:34
#: canaille/templates/profile.html:40
msgid "User profile edition"
msgstr "Édition d'un profil utilisateur"
#: canaille/templates/profile.html:40
#: canaille/templates/profile.html:46
msgid "Create a new user account"
msgstr "Création d'un nouveau compte utilisateur"
#: canaille/templates/profile.html:42
#: canaille/templates/profile.html:48
msgid "Edit your personal informations"
msgstr "Éditez vos informations personnelles"
#: canaille/templates/profile.html:44
#: canaille/templates/profile.html:50
msgid "Edit informations about an user"
msgstr "Éditez les informations d'un utilisateur"
#: canaille/templates/profile.html:61
#: canaille/templates/profile.html:67
msgid "Personal information"
msgstr "Informations personnelles"
#: canaille/templates/profile.html:80
#: canaille/templates/profile.html:86
msgid "Account information"
msgstr "Informations sur le compte"
#: canaille/templates/profile.html:96
#: canaille/templates/profile.html:102
msgid "Impersonate"
msgstr "Prendre l'identité"
#: canaille/templates/profile.html:102
#: canaille/templates/profile.html:108
msgid "Delete the user"
msgstr "Supprimer l'utilisateur"
#: canaille/templates/profile.html:104
#: canaille/templates/profile.html:110
msgid "Delete my account"
msgstr "Supprimer mon compte"
@ -763,3 +771,6 @@ msgstr ""
#~ msgid "A password reset link has been sent at your email address."
#~ msgstr "Un lien de réinitialisation vous a été envoyé à votre adresse."
#~ msgid "User deletion"
#~ msgstr "Suppression d'un utilisateur"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2021-01-01 15:28+0100\n"
"POT-Creation-Date: 2021-01-01 16:40+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"
@ -53,7 +53,7 @@ msgstr ""
msgid "User creation failed."
msgstr ""
#: canaille/account.py:181 canaille/account.py:254
#: canaille/account.py:181 canaille/account.py:258
msgid "Profile updated successfuly."
msgstr ""
@ -61,38 +61,38 @@ msgstr ""
msgid "User creation succeed."
msgstr ""
#: canaille/account.py:238
#: canaille/account.py:242
msgid "Profile edition failed."
msgstr ""
#: canaille/account.py:270
#: canaille/account.py:278
#, python-format
msgid "The user %(user)s has been sucessfuly deleted"
msgstr ""
#: canaille/account.py:293
#: canaille/account.py:301
msgid "Could not send the password reset link."
msgstr ""
#: canaille/account.py:300 canaille/account.py:345
#: canaille/account.py:308 canaille/account.py:353
msgid ""
"A password reset link has been sent at your email address. You should "
"receive it within 10 minutes."
msgstr ""
#: canaille/account.py:318 canaille/admin/mail.py:29
#: canaille/account.py:326 canaille/admin/mail.py:29
msgid "Password reset on {website_name}"
msgstr ""
#: canaille/account.py:351
#: canaille/account.py:359
msgid "Could not reset your password"
msgstr ""
#: canaille/account.py:365
#: canaille/account.py:373
msgid "The password reset link that brought you here was invalid."
msgstr ""
#: canaille/account.py:374
#: canaille/account.py:382
msgid "Your password has been updated successfuly"
msgstr ""
@ -325,7 +325,7 @@ msgstr ""
msgid "authorization interface"
msgstr ""
#: canaille/templates/base.html:37 canaille/templates/profile.html:32
#: canaille/templates/base.html:37 canaille/templates/profile.html:38
msgid "My profile"
msgstr ""
@ -448,7 +448,7 @@ msgid "Send again"
msgstr ""
#: canaille/templates/forgotten-password.html:40
#: canaille/templates/profile.html:92
#: canaille/templates/profile.html:98
msgid "Send"
msgstr ""
@ -466,62 +466,68 @@ msgid "Sign in"
msgstr ""
#: canaille/templates/profile.html:14
msgid "User deletion"
msgid "Account deletion"
msgstr ""
#: canaille/templates/profile.html:17
#: canaille/templates/profile.html:19
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:21
msgid ""
"Are you sure you want to delete your account? This action is unrevokable "
"and all your data will be removed forever."
msgstr ""
#: canaille/templates/admin/client_edit.html:19
#: canaille/templates/profile.html:20
#: canaille/templates/profile.html:26
msgid "Cancel"
msgstr ""
#: canaille/templates/admin/client_edit.html:20
#: canaille/templates/profile.html:21
#: canaille/templates/profile.html:27
msgid "Delete"
msgstr ""
#: canaille/templates/profile.html:30
#: canaille/templates/profile.html:36
msgid "User creation"
msgstr ""
#: canaille/templates/profile.html:34
#: canaille/templates/profile.html:40
msgid "User profile edition"
msgstr ""
#: canaille/templates/profile.html:40
#: canaille/templates/profile.html:46
msgid "Create a new user account"
msgstr ""
#: canaille/templates/profile.html:42
#: canaille/templates/profile.html:48
msgid "Edit your personal informations"
msgstr ""
#: canaille/templates/profile.html:44
#: canaille/templates/profile.html:50
msgid "Edit informations about an user"
msgstr ""
#: canaille/templates/profile.html:61
#: canaille/templates/profile.html:67
msgid "Personal information"
msgstr ""
#: canaille/templates/profile.html:80
#: canaille/templates/profile.html:86
msgid "Account information"
msgstr ""
#: canaille/templates/profile.html:96
#: canaille/templates/profile.html:102
msgid "Impersonate"
msgstr ""
#: canaille/templates/profile.html:102
#: canaille/templates/profile.html:108
msgid "Delete the user"
msgstr ""
#: canaille/templates/profile.html:104
#: canaille/templates/profile.html:110
msgid "Delete my account"
msgstr ""

View file

@ -28,13 +28,17 @@ OIDC_METADATA_FILE = "conf/openid-configuration.json"
# If you have a sentry instance, you can set its dsn here:
# SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
# If this option is set to true, when a user tries to sign in with
# If HIDE_INVALID_LOGINS is set to true, when a user tries to sign in with
# an invalid login, a message is shown saying that the login does not
# exist. If this option is set to false (the default) a message is
# exist. If HIDE_INVALID_LOGINS is set to false (the default) a message is
# shown saying that the password is wrong, but does not give a clue
# wether the login exists or not.
# HIDE_INVALID_LOGINS = false
# SELF_DELETION controls the ability for a user to delete his own
# account. The default value is true.
# SELF_DELETION = true
[LDAP]
URI = "ldap://127.0.0.1:5389"
ROOT_DN = "dc=mydomain,dc=tld"

View file

@ -138,3 +138,71 @@ def test_wrong_login(testclient, slapd_connection, user):
res.form["password"] = "incorrect horse"
res = res.form.submit(status=200)
assert "The login 'invalid' does not exist" 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)
)
testclient.app.config["SELF_DELETION"] = True
testclient.app.config["SELF_DELETION"] = False
with testclient.app.app_context():
assert User.get("temp", conn=slapd_connection) is None
with testclient.session_transaction() as sess:
assert not sess.get("user_dn")
def test_user_self_deletion(testclient, slapd_connection):
User.ocs_by_name(slapd_connection)
user = User(
objectClass=["inetOrgPerson"],
cn="Temp user",
sn="user",
uid="temp",
mail="temp@temp.com",
userPassword="{SSHA}Vmgh2jkD0idX3eZHf8RzGos31oerjGiU",
)
user.save(slapd_connection)
with testclient.session_transaction() as sess:
sess["user_dn"] = [user.dn]
testclient.app.config["SELF_DELETION"] = False
res = testclient.get("/profile/temp")
assert "Delete my account" not in res
testclient.app.config["SELF_DELETION"] = True
res = testclient.get("/profile/temp")
assert "Delete my account" in res
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:
assert not sess.get("user_dn")
testclient.app.config["SELF_DELETION"] = False

View file

@ -142,31 +142,3 @@ def test_user_creation_edition_and_deletion(
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:
assert not sess.get("user_dn")