[System] Implemented password reset function in user controller
This commit is contained in:
parent
559c8c5c9c
commit
1f93bc6d80
|
@ -1,22 +1,19 @@
|
||||||
from flask import current_app, url_for
|
import secrets
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
from flask import current_app
|
||||||
from werkzeug.exceptions import NotFound, BadRequest
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from werkzeug.exceptions import NotFound, BadRequest, Forbidden
|
||||||
|
|
||||||
from flaschengeist.utils.hook import Hook
|
|
||||||
from flaschengeist.models.user import User, Role
|
|
||||||
from flaschengeist.database import db
|
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
|
from flaschengeist.database import db
|
||||||
|
from flaschengeist.utils.hook import Hook
|
||||||
|
from flaschengeist.models.user import User, Role, _PasswordReset
|
||||||
|
from flaschengeist.controller import messageController, sessionController
|
||||||
|
|
||||||
|
|
||||||
def login_user(username, password):
|
def login_user(username, password):
|
||||||
logger.info("login user {{ {} }}".format(username))
|
logger.info("login user {{ {} }}".format(username))
|
||||||
mail = username.split("@")
|
|
||||||
mail = len(mail) == 2 and len(mail[0]) > 0 and len(mail[1]) > 0
|
|
||||||
|
|
||||||
query = User.userid == username
|
user = find_user(username)
|
||||||
if mail:
|
|
||||||
query |= User.mail == username
|
|
||||||
user = User.query.filter(query).one_or_none()
|
|
||||||
if not user:
|
if not user:
|
||||||
logger.debug("User not found in Database.")
|
logger.debug("User not found in Database.")
|
||||||
user = User(userid=username)
|
user = User(userid=username)
|
||||||
|
@ -27,6 +24,49 @@ def login_user(username, password):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def request_reset(user: User):
|
||||||
|
logger.debug(f"New password reset request for {user.userid}")
|
||||||
|
reset = _PasswordReset.query.get(user._id)
|
||||||
|
if not reset:
|
||||||
|
reset = _PasswordReset(_user_id=user._id)
|
||||||
|
db.session.add(reset)
|
||||||
|
|
||||||
|
expires = datetime.now(tz=timezone.utc)
|
||||||
|
if not reset.expires or reset.expires < expires:
|
||||||
|
expires = expires + timedelta(hours=12)
|
||||||
|
reset.expires = expires
|
||||||
|
reset.token = secrets.token_urlsafe(16)
|
||||||
|
|
||||||
|
subject = "Flaschengeist - Passwort zurücksetzten"
|
||||||
|
domain = "flaschengeist.local"
|
||||||
|
text = f"""Hallo {user.display_name},
|
||||||
|
Jemand hat das Zurücksetzen des Passworts für dein Flaschengeist Benutzerkonto angefordert.
|
||||||
|
|
||||||
|
Benutzername: {user.userid}
|
||||||
|
|
||||||
|
Falls das nicht beabsichtigt war, ignoriere diese E-Mail einfach. Es wird dann nichts passieren.
|
||||||
|
|
||||||
|
Um dein Passwort zurückzusetzen, besuche folgende Adresse, der Link ist 12 Stunden gültig:
|
||||||
|
|
||||||
|
<https://{domain}/reset?token={reset.token}>
|
||||||
|
"""
|
||||||
|
db.session.commit()
|
||||||
|
messageController.send_message(messageController.Message(user, text, subject))
|
||||||
|
|
||||||
|
|
||||||
|
def reset_password(token: str, password: str):
|
||||||
|
reset = _PasswordReset.query.filter(_PasswordReset.token == token).one_or_none()
|
||||||
|
logger.debug(f"Token is {'valid' if reset else 'invalid'}")
|
||||||
|
if not reset or reset.expires < datetime.now(tz=timezone.utc):
|
||||||
|
raise Forbidden
|
||||||
|
|
||||||
|
modify_user(reset.user, None, password)
|
||||||
|
sessionController.delete_sessions(reset.user)
|
||||||
|
|
||||||
|
db.session.delete(reset)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
@Hook
|
@Hook
|
||||||
def update_user(user):
|
def update_user(user):
|
||||||
current_app.config["FG_AUTH_BACKEND"].update_user(user)
|
current_app.config["FG_AUTH_BACKEND"].update_user(user)
|
||||||
|
@ -60,6 +100,10 @@ def modify_user(user, password, new_password=None):
|
||||||
"""
|
"""
|
||||||
current_app.config["FG_AUTH_BACKEND"].modify_user(user, password, new_password)
|
current_app.config["FG_AUTH_BACKEND"].modify_user(user, password, new_password)
|
||||||
|
|
||||||
|
if new_password:
|
||||||
|
# TODO: Password changed mail
|
||||||
|
logger.error(f"Password changed for user {user.userid}")
|
||||||
|
|
||||||
|
|
||||||
def get_users():
|
def get_users():
|
||||||
return User.query.all()
|
return User.query.all()
|
||||||
|
@ -76,6 +120,16 @@ def get_user(uid):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def find_user(uid_mail):
|
||||||
|
mail = uid_mail.split("@")
|
||||||
|
mail = len(mail) == 2 and len(mail[0]) > 0 and len(mail[1]) > 0
|
||||||
|
|
||||||
|
query = User.userid == uid_mail
|
||||||
|
if mail:
|
||||||
|
query |= User.mail == uid_mail
|
||||||
|
return User.query.filter(query).one_or_none()
|
||||||
|
|
||||||
|
|
||||||
def delete(user):
|
def delete(user):
|
||||||
current_app.config["FG_AUTH_BACKEND"].delete_user(user)
|
current_app.config["FG_AUTH_BACKEND"].delete_user(user)
|
||||||
db.session.delete(user)
|
db.session.delete(user)
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
from datetime import date
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
from typing import Optional
|
||||||
|
from datetime import date, datetime
|
||||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||||
|
|
||||||
from . import ModelSerializeMixin
|
from ..database import db
|
||||||
from flaschengeist.database import db
|
from . import ModelSerializeMixin, UtcDateTime
|
||||||
|
|
||||||
association_table = db.Table(
|
association_table = db.Table(
|
||||||
"user_x_role",
|
"user_x_role",
|
||||||
|
@ -103,6 +102,15 @@ class _UserAttribute(db.Model, ModelSerializeMixin):
|
||||||
value: any = db.Column(db.PickleType(protocol=4))
|
value: any = db.Column(db.PickleType(protocol=4))
|
||||||
|
|
||||||
|
|
||||||
|
class _PasswordReset(db.Model):
|
||||||
|
"""Table containing password reset requests"""
|
||||||
|
__tablename__ = "password_reset"
|
||||||
|
_user_id: User = db.Column("user", db.Integer, db.ForeignKey("user.id"), primary_key=True)
|
||||||
|
user: User = db.relationship("User", foreign_keys=[_user_id])
|
||||||
|
token: str = db.Column(db.String(30))
|
||||||
|
expires: datetime = db.Column(UtcDateTime)
|
||||||
|
|
||||||
|
|
||||||
class _Avatar:
|
class _Avatar:
|
||||||
"""Wrapper class for avatar binaries"""
|
"""Wrapper class for avatar binaries"""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue