forked from Github-Mirrors/canaille
Users can delete their own accounts. #35
This commit is contained in:
parent
801031f3b1
commit
cbe06cc128
9 changed files with 166 additions and 90 deletions
|
@ -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`
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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") }}
|
||||
|
|
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: 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"
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in a new issue