add user account deletion (right to be forgotten)
This commit is contained in:
@@ -4,7 +4,12 @@ from flask import (
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
from ..db import db
|
from ..db import db
|
||||||
from ..lib.babycode import babycode_to_html, BABYCODE_VERSION
|
from ..lib.babycode import babycode_to_html, BABYCODE_VERSION
|
||||||
from ..models import Users, Sessions, Subscriptions, Avatars, PasswordResetLinks, InviteKeys, BookmarkCollections, BookmarkedThreads, Mentions, PostHistory
|
from ..models import (
|
||||||
|
Users, Sessions, Subscriptions,
|
||||||
|
Avatars, PasswordResetLinks, InviteKeys,
|
||||||
|
BookmarkCollections, BookmarkedThreads,
|
||||||
|
Mentions, PostHistory,
|
||||||
|
)
|
||||||
from ..constants import InfoboxKind, PermissionLevel
|
from ..constants import InfoboxKind, PermissionLevel
|
||||||
from ..auth import digest, verify
|
from ..auth import digest, verify
|
||||||
from wand.image import Image
|
from wand.image import Image
|
||||||
@@ -197,6 +202,53 @@ def get_prefers_theme():
|
|||||||
|
|
||||||
return session['theme']
|
return session['theme']
|
||||||
|
|
||||||
|
|
||||||
|
def anonymize_user(user_id):
|
||||||
|
deleted_user = Users.find({'username': 'deleteduser'})
|
||||||
|
|
||||||
|
from ..models import Threads, Posts
|
||||||
|
from ..lib.babycode import sanitize
|
||||||
|
threads = Threads.findall({'user_id': user_id})
|
||||||
|
posts = Posts.findall({'user_id': user_id})
|
||||||
|
|
||||||
|
revs_q = """SELECT DISTINCT m.revision_id FROM mentions m
|
||||||
|
WHERE m.mentioned_user_id = ?"""
|
||||||
|
|
||||||
|
mentioned_revs = db.query(revs_q, int(user_id))
|
||||||
|
with db.transaction():
|
||||||
|
for thread in threads:
|
||||||
|
thread.update({'user_id': int(deleted_user.id)})
|
||||||
|
for post in posts:
|
||||||
|
post.update({'user_id': int(deleted_user.id)})
|
||||||
|
|
||||||
|
revs = {}
|
||||||
|
for rev in mentioned_revs:
|
||||||
|
ph = PostHistory.find({'id': int(rev['revision_id'])})
|
||||||
|
ms = Mentions.findall({
|
||||||
|
'mentioned_user_id': int(user_id),
|
||||||
|
'revision_id': int(rev['revision_id'])
|
||||||
|
})
|
||||||
|
data = {
|
||||||
|
'text': sanitize(ph.original_markup),
|
||||||
|
'mentions': ms,
|
||||||
|
}
|
||||||
|
data['mentions'] = sorted(data['mentions'], key=lambda x: int(x.end_index), reverse=True)
|
||||||
|
revs[rev['revision_id']] = data
|
||||||
|
|
||||||
|
for rev_id, data in revs.items():
|
||||||
|
text = data['text']
|
||||||
|
for mention in data['mentions']:
|
||||||
|
text = text[:mention.start_index] + '@deleteduser' + text[mention.end_index:]
|
||||||
|
mention.delete()
|
||||||
|
|
||||||
|
res = babycode_to_html(text)
|
||||||
|
ph = PostHistory.find({'id': int(rev_id)})
|
||||||
|
ph.update({
|
||||||
|
'original_markup': text.unescape(),
|
||||||
|
'content': res.result,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@bp.get("/log_in")
|
@bp.get("/log_in")
|
||||||
@redirect_if_logged_in(".page", username = lambda: get_active_user().username)
|
@redirect_if_logged_in(".page", username = lambda: get_active_user().username)
|
||||||
def log_in():
|
def log_in():
|
||||||
@@ -756,3 +808,34 @@ def bookmark_collections(username):
|
|||||||
|
|
||||||
collections = target_user.get_bookmark_collections()
|
collections = target_user.get_bookmark_collections()
|
||||||
return render_template('users/bookmark_collections.html', collections=collections)
|
return render_template('users/bookmark_collections.html', collections=collections)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.get('/<username>/delete-account')
|
||||||
|
@login_required
|
||||||
|
@redirect_to_own
|
||||||
|
def delete_page(username):
|
||||||
|
target_user = get_active_user()
|
||||||
|
|
||||||
|
return render_template('users/delete_page.html')
|
||||||
|
|
||||||
|
|
||||||
|
@bp.post('/<username>/delete-account')
|
||||||
|
@login_required
|
||||||
|
@redirect_to_own
|
||||||
|
def delete_page_confirm(username):
|
||||||
|
target_user = get_active_user()
|
||||||
|
|
||||||
|
password = request.form.get('password', default='')
|
||||||
|
|
||||||
|
if not verify(target_user.password_hash, password):
|
||||||
|
flash('Incorrect password.', InfoboxKind.ERROR)
|
||||||
|
return redirect(url_for('.delete_page', username=username))
|
||||||
|
|
||||||
|
anonymize_user(target_user.id)
|
||||||
|
sessions = Sessions.findall({'user_id': int(target_user.id)})
|
||||||
|
for session_obj in sessions:
|
||||||
|
session_obj.delete()
|
||||||
|
|
||||||
|
session.clear()
|
||||||
|
target_user.delete()
|
||||||
|
return redirect(url_for('topics.all_topics'))
|
||||||
|
|||||||
15
app/templates/users/delete_page.html
Normal file
15
app/templates/users/delete_page.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block title %}delete confirmation{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="darkbg login-container">
|
||||||
|
<h1>Confirm account deletion</h1>
|
||||||
|
<p>Are you sure you want to delete your account on {{ config.SITE_NAME }}? <strong>This action is irreversible.</strong> Your posts and threads will remain accessible to preserve history but will be de-personalized, showing up as authored by a system user. Posts that @mention you will also mention the system user instead.</p>
|
||||||
|
<p>If you wish for any and all content relating to you to be removed, you will have to ask {{ config.SITE_NAME }}'s administrators separately.</p>
|
||||||
|
<p>If you are sure, please confirm your current password below.</p>
|
||||||
|
<form method="post">
|
||||||
|
<label for="password">Confirm password</label>
|
||||||
|
<input type="password" id="password" name="password" required autocomplete="current-password">
|
||||||
|
<input class="critical" type="submit" value="Delete account">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -43,5 +43,8 @@
|
|||||||
<input type="password" id="new_password2" name="new_password2" pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_])(?!.*\s).{10,}" title="10+ chars with: 1 uppercase, 1 lowercase, 1 number, 1 special char, and no spaces" required autocomplete="new-password"><br>
|
<input type="password" id="new_password2" name="new_password2" pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_])(?!.*\s).{10,}" title="10+ chars with: 1 uppercase, 1 lowercase, 1 number, 1 special char, and no spaces" required autocomplete="new-password"><br>
|
||||||
<input class="warn" type="submit" value="Change password">
|
<input class="warn" type="submit" value="Change password">
|
||||||
</form>
|
</form>
|
||||||
|
<div>
|
||||||
|
<a class="linkbutton critical" href="{{ url_for('users.delete_page', username=active_user.username) }}">Delete account</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user