[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
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from werkzeug.exceptions import NotFound, BadRequest
|
||||
import secrets
|
||||
from flask import current_app
|
||||
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.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):
|
||||
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
|
||||
if mail:
|
||||
query |= User.mail == username
|
||||
user = User.query.filter(query).one_or_none()
|
||||
user = find_user(username)
|
||||
if not user:
|
||||
logger.debug("User not found in Database.")
|
||||
user = User(userid=username)
|
||||
|
@ -27,6 +24,49 @@ def login_user(username, password):
|
|||
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
|
||||
def 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)
|
||||
|
||||
if new_password:
|
||||
# TODO: Password changed mail
|
||||
logger.error(f"Password changed for user {user.userid}")
|
||||
|
||||
|
||||
def get_users():
|
||||
return User.query.all()
|
||||
|
@ -76,6 +120,16 @@ def get_user(uid):
|
|||
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):
|
||||
current_app.config["FG_AUTH_BACKEND"].delete_user(user)
|
||||
db.session.delete(user)
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
from datetime import date
|
||||
from typing import Optional
|
||||
|
||||
from flask import url_for
|
||||
from typing import Optional
|
||||
from datetime import date, datetime
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
|
||||
from . import ModelSerializeMixin
|
||||
from flaschengeist.database import db
|
||||
from ..database import db
|
||||
from . import ModelSerializeMixin, UtcDateTime
|
||||
|
||||
association_table = db.Table(
|
||||
"user_x_role",
|
||||
|
@ -103,6 +102,15 @@ class _UserAttribute(db.Model, ModelSerializeMixin):
|
|||
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:
|
||||
"""Wrapper class for avatar binaries"""
|
||||
|
||||
|
|
Loading…
Reference in New Issue