fix(users): Fix deleting users
Remove all internal references, e.g. sessions, attributes, password reset requests. Add hook for plugins. If not deletable remove at least all personal data
This commit is contained in:
parent
50fa39be4f
commit
d0674e8876
|
@ -1,5 +1,6 @@
|
||||||
import secrets
|
import secrets
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
from sqlalchemy import exc
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from flask.helpers import send_file
|
from flask.helpers import send_file
|
||||||
|
@ -168,11 +169,34 @@ def find_user(uid_mail):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
def delete(user):
|
@Hook
|
||||||
|
def delete_user(user: User):
|
||||||
"""Delete given user"""
|
"""Delete given user"""
|
||||||
|
# First let the backend delete the user, as this might fail
|
||||||
current_app.config["FG_AUTH_BACKEND"].delete_user(user)
|
current_app.config["FG_AUTH_BACKEND"].delete_user(user)
|
||||||
|
# Clear all easy relationships
|
||||||
|
user.avatar_ = None
|
||||||
|
user._attributes.clear()
|
||||||
|
user.roles_.clear()
|
||||||
|
user.sessions_.clear()
|
||||||
|
user.reset_requests_.clear()
|
||||||
|
db.session.commit()
|
||||||
|
try:
|
||||||
|
# Delete the user
|
||||||
db.session.delete(user)
|
db.session.delete(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
except exc.IntegrityError:
|
||||||
|
logger.error("Delete of user failed, there might be ForeignKey contraits from disabled plugins", exec_info=True)
|
||||||
|
# Remove at least all personal data
|
||||||
|
user.userid = f"__deleted_user__{user.id_}"
|
||||||
|
user.display_name = "DELETED USER"
|
||||||
|
user.firstname = ""
|
||||||
|
user.lastname = ""
|
||||||
|
user.birthday = None
|
||||||
|
user.mail = None
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def register(data):
|
def register(data):
|
||||||
|
|
|
@ -5,10 +5,9 @@ from typing import Optional
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||||
|
|
||||||
from flaschengeist.models.image import Image
|
|
||||||
|
|
||||||
from ..database import db
|
from ..database import db
|
||||||
from . import ModelSerializeMixin, UtcDateTime, Serial
|
from . import ModelSerializeMixin, UtcDateTime, Serial
|
||||||
|
from .image import Image
|
||||||
|
|
||||||
|
|
||||||
association_table = db.Table(
|
association_table = db.Table(
|
||||||
|
|
|
@ -27,6 +27,11 @@ before_update_user = HookBefore("update_user")
|
||||||
Args:
|
Args:
|
||||||
user: User object
|
user: User object
|
||||||
"""
|
"""
|
||||||
|
before_delete_user = HookBefore("delete_user")
|
||||||
|
"""Hook decorator,this is called before an user gets deleted.
|
||||||
|
Args:
|
||||||
|
user: User object
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Plugin:
|
class Plugin:
|
||||||
|
|
|
@ -6,8 +6,7 @@ Flaschengeist database (as User attribute)
|
||||||
import os
|
import os
|
||||||
import hashlib
|
import hashlib
|
||||||
import binascii
|
import binascii
|
||||||
from werkzeug.exceptions import BadRequest, NotFound
|
from werkzeug.exceptions import BadRequest
|
||||||
|
|
||||||
from flaschengeist.plugins import AuthPlugin
|
from flaschengeist.plugins import AuthPlugin
|
||||||
from flaschengeist.models.user import User, Role, Permission
|
from flaschengeist.models.user import User, Role, Permission
|
||||||
from flaschengeist.database import db
|
from flaschengeist.database import db
|
||||||
|
@ -18,6 +17,8 @@ class AuthPlain(AuthPlugin):
|
||||||
def post_install(self):
|
def post_install(self):
|
||||||
if User.query.first() is None:
|
if User.query.first() is None:
|
||||||
logger.info("Installing admin user")
|
logger.info("Installing admin user")
|
||||||
|
role = Role.query.filter(Role.name == "Superuser").first()
|
||||||
|
if role is None:
|
||||||
role = Role(name="Superuser", permissions=Permission.query.all())
|
role = Role(name="Superuser", permissions=Permission.query.all())
|
||||||
admin = User(
|
admin = User(
|
||||||
userid="admin",
|
userid="admin",
|
||||||
|
|
|
@ -2,18 +2,17 @@
|
||||||
|
|
||||||
Provides routes used to manage users
|
Provides routes used to manage users
|
||||||
"""
|
"""
|
||||||
from io import BytesIO
|
|
||||||
from http.client import NO_CONTENT, CREATED
|
from http.client import NO_CONTENT, CREATED
|
||||||
from flask import Blueprint, request, jsonify, make_response, Response, send_file
|
from flask import Blueprint, request, jsonify, make_response
|
||||||
from werkzeug.exceptions import BadRequest, Forbidden, MethodNotAllowed, NotFound
|
from werkzeug.exceptions import BadRequest, Forbidden, MethodNotAllowed, NotFound
|
||||||
|
|
||||||
from . import permissions
|
from . import permissions
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
from flaschengeist.config import config
|
from flaschengeist.config import config
|
||||||
from flaschengeist.plugins import Plugin
|
from flaschengeist.plugins import Plugin
|
||||||
from flaschengeist.models.user import User, _Avatar
|
from flaschengeist.models.user import User
|
||||||
from flaschengeist.utils.decorators import login_required, extract_session, headers
|
from flaschengeist.utils.decorators import login_required, extract_session, headers
|
||||||
from flaschengeist.controller import userController, imageController as image_controller
|
from flaschengeist.controller import userController
|
||||||
from flaschengeist.utils.HTTP import created, no_content
|
from flaschengeist.utils.HTTP import created, no_content
|
||||||
from flaschengeist.utils.datetime import from_iso_format
|
from flaschengeist.utils.datetime import from_iso_format
|
||||||
|
|
||||||
|
@ -23,6 +22,10 @@ class UsersPlugin(Plugin):
|
||||||
blueprint = Blueprint(name, __name__)
|
blueprint = Blueprint(name, __name__)
|
||||||
permissions = permissions.permissions
|
permissions = permissions.permissions
|
||||||
|
|
||||||
|
def install(self):
|
||||||
|
userController.install()
|
||||||
|
return super().install()
|
||||||
|
|
||||||
|
|
||||||
@UsersPlugin.blueprint.route("/users", methods=["POST"])
|
@UsersPlugin.blueprint.route("/users", methods=["POST"])
|
||||||
def register():
|
def register():
|
||||||
|
@ -144,7 +147,7 @@ def delete_avatar(userid, current_session):
|
||||||
if userid != current_session.user_.userid and not current_session.user_.has_permission(permissions.EDIT):
|
if userid != current_session.user_.userid and not current_session.user_.has_permission(permissions.EDIT):
|
||||||
raise Forbidden
|
raise Forbidden
|
||||||
userController.delete_avatar(user)
|
userController.delete_avatar(user)
|
||||||
return "", NO_CONTENT
|
return no_content()
|
||||||
|
|
||||||
|
|
||||||
@UsersPlugin.blueprint.route("/users/<userid>", methods=["DELETE"])
|
@UsersPlugin.blueprint.route("/users/<userid>", methods=["DELETE"])
|
||||||
|
@ -163,8 +166,8 @@ def delete_user(userid, current_session):
|
||||||
"""
|
"""
|
||||||
logger.debug("Delete user {{ {} }}".format(userid))
|
logger.debug("Delete user {{ {} }}".format(userid))
|
||||||
user = userController.get_user(userid)
|
user = userController.get_user(userid)
|
||||||
userController.delete(user)
|
userController.delete_user(user)
|
||||||
return "", NO_CONTENT
|
return no_content()
|
||||||
|
|
||||||
|
|
||||||
@UsersPlugin.blueprint.route("/users/<userid>", methods=["PUT"])
|
@UsersPlugin.blueprint.route("/users/<userid>", methods=["PUT"])
|
||||||
|
@ -217,7 +220,7 @@ def edit_user(userid, current_session):
|
||||||
|
|
||||||
userController.modify_user(user, password, new_password)
|
userController.modify_user(user, password, new_password)
|
||||||
userController.update_user(user)
|
userController.update_user(user)
|
||||||
return "", NO_CONTENT
|
return no_content()
|
||||||
|
|
||||||
|
|
||||||
@UsersPlugin.blueprint.route("/notifications", methods=["GET"])
|
@UsersPlugin.blueprint.route("/notifications", methods=["GET"])
|
||||||
|
|
Loading…
Reference in New Issue