[System][Plugin] users: Send users a link to set their own password and initially set random password

This commit is contained in:
Ferdinand Thiessen 2021-01-21 14:08:06 +01:00
parent f42d5956db
commit 69ec4472c3
3 changed files with 41 additions and 25 deletions

View File

@ -41,6 +41,11 @@ welcome_subject = "Welcome to Flaschengeist {name}"
welcome_text = '''
Hello {name}!
Welcome to Flaschengeist!
Your username is {username}, please set your password:
{password_link}
(If the link expires, please just use the Forgot-Password-function).
Have fun :)
'''

View File

@ -11,6 +11,23 @@ from flaschengeist.models.user import User, Role, _PasswordReset
from flaschengeist.controller import messageController, sessionController
def _generate_password_reset(user):
"""Generate a password reset link for the user"""
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(24)
db.session.commit()
return reset
def login_user(username, password):
logger.info("login user {{ {} }}".format(username))
@ -27,17 +44,7 @@ def login_user(username, password):
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(24)
db.session.commit()
reset = _generate_password_reset(user)
subject = str(config["MESSAGES"]["password_subject"]).format(name=user.display_name, username=user.userid)
text = str(config["MESSAGES"]["password_text"]).format(
@ -161,25 +168,24 @@ def delete(user):
def register(data):
for required in ["firstname", "lastname", "mail"]:
if required not in data:
raise BadRequest("Missing required parameters")
allowed_keys = User().serialize().keys()
values = {key: value for key, value in data.items() if key in allowed_keys}
roles = values.pop("roles", [])
user = User(**values)
set_roles(user, roles)
current_app.config["FG_AUTH_BACKEND"].create_user(user, data["password"])
password = secrets.token_bytes(16)
current_app.config["FG_AUTH_BACKEND"].create_user(user, password)
db.session.add(user)
db.session.commit()
if user.mail and len(user.mail) > 3:
reset = _generate_password_reset(user)
subject = str(config["MESSAGES"]["welcome_subject"]).format(name=user.display_name, username=user.userid)
text = str(config["MESSAGES"]["welcome_text"]).format(
name=user.display_name,
username=user.userid,
password_link=f'https://{config["FLASCHENGEIST"]["domain"]}/reset?token={reset.token}'
)
messageController.send_message(messageController.Message(user, text, subject))

View File

@ -24,17 +24,19 @@ _permission_register = "users_register"
class UsersPlugin(Plugin):
def __init__(self, config):
def __init__(self, cfg):
super().__init__(blueprint=users_bp, permissions=[_permission_edit, _permission_delete, _permission_set_roles])
@users_bp.route("/users", methods=["POST"])
def register():
"""Register a new user
The password will be set to a random string of at lease 16byte entropy.
The user will receive a mail containing a link to set their own password.
Route: ``/users`` | Method: ``POST``
POST-data: Same as `flaschengeist.models.user.User` + ``password?: string``
POST-data: Same as `flaschengeist.models.user.User`
Returns:
JSON encoded `flaschengeist.models.user.User` or HTTP error
@ -49,6 +51,9 @@ def register():
data = request.get_json()
if not data:
raise BadRequest
for required in ["firstname", "lastname", "mail"]:
if required not in data:
raise BadRequest("Missing required parameters")
logger.debug("Register new User...")
return make_response(jsonify(userController.register(data)), CREATED)