[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 = ''' welcome_text = '''
Hello {name}! Hello {name}!
Welcome to Flaschengeist! 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 :) Have fun :)
''' '''

View File

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

View File

@ -24,17 +24,19 @@ _permission_register = "users_register"
class UsersPlugin(Plugin): class UsersPlugin(Plugin):
def __init__(self, config): def __init__(self, cfg):
super().__init__(blueprint=users_bp, permissions=[_permission_edit, _permission_delete, _permission_set_roles]) super().__init__(blueprint=users_bp, permissions=[_permission_edit, _permission_delete, _permission_set_roles])
@users_bp.route("/users", methods=["POST"]) @users_bp.route("/users", methods=["POST"])
def register(): def register():
"""Register a new user """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`` Route: ``/users`` | Method: ``POST``
POST-data: Same as `flaschengeist.models.user.User` + ``password?: string`` POST-data: Same as `flaschengeist.models.user.User`
Returns: Returns:
JSON encoded `flaschengeist.models.user.User` or HTTP error JSON encoded `flaschengeist.models.user.User` or HTTP error
@ -49,6 +51,9 @@ def register():
data = request.get_json() data = request.get_json()
if not data: if not data:
raise BadRequest raise BadRequest
for required in ["firstname", "lastname", "mail"]:
if required not in data:
raise BadRequest("Missing required parameters")
logger.debug("Register new User...") logger.debug("Register new User...")
return make_response(jsonify(userController.register(data)), CREATED) return make_response(jsonify(userController.register(data)), CREATED)