From 7b1196f09d5ecc1af69cc04ddb0f66f9acd5fc40 Mon Sep 17 00:00:00 2001 From: Julian Lobbes Date: Thu, 17 Nov 2022 00:50:14 +0100 Subject: [PATCH] refactor(usermanager): migrate to flask-wtf --- lumi2/templates/usermanager/user_detail.html | 35 +++-- lumi2/usermanager.py | 140 +++++++------------ requirements.txt | 3 + 3 files changed, 75 insertions(+), 103 deletions(-) diff --git a/lumi2/templates/usermanager/user_detail.html b/lumi2/templates/usermanager/user_detail.html index 87e298a..8b5d013 100644 --- a/lumi2/templates/usermanager/user_detail.html +++ b/lumi2/templates/usermanager/user_detail.html @@ -4,18 +4,29 @@

User: {{ user.username }}

profile picture for user {{ user.username }}
- - - - - - - - - - - - + {{ form.csrf_token }} + + {{ form.email.label }} + {{ form.email }} + + {{ form.first_name.label }} + {{ form.first_name }} + + {{ form.last_name.label }} + {{ form.last_name }} + + {{ form.display_name.label }} + {{ form.display_name }} + + {{ form.password.label }} + {{ form.password }} + + {{ form.password_confirmation.label }} + {{ form.password_confirmation }} + + {{ form.picture.label }} + {{ form.picture }} +
{% endblock content %} diff --git a/lumi2/usermanager.py b/lumi2/usermanager.py index 57ce00a..646a502 100644 --- a/lumi2/usermanager.py +++ b/lumi2/usermanager.py @@ -4,9 +4,13 @@ from pathlib import Path from tempfile import TemporaryDirectory from flask import ( - Blueprint, render_template, abort, request, flash + Blueprint, render_template, abort, request, flash, redirect ) from PIL import Image, UnidentifiedImageError +from flask_wtf import FlaskForm +from flask_wtf.file import FileField, FileAllowed +from wtforms import ValidationError, StringField, PasswordField +from wtforms.validators import InputRequired, Email, EqualTo import lumi2.ldap as ldap from lumi2.usermodel import User, Group @@ -22,52 +26,39 @@ def index(): return render_template('usermanager/index.html') -class InvalidImageException(Exception): - """Raised when an image's filename or contents are invalid.""" - pass +class UserEditForm(FlaskForm): + @staticmethod + def validate_name(form, field) -> None: + if not User.is_valid_person_name(field.data): + raise ValidationError("Invalid name.") - -def _get_image_from_uploaded_file(file) -> Image.Image: - """Extracts a JPEG image from a file submitted via POST request. - - The file's file extension and content is checked for validity as a JPEG image. - - Parameters - ---------- - file - A file object taken from a POST request. - - Returns - ------- - PIL.Image.Image - A valid JPEG Image object. - - Raises - ------ - InvalidImageException - When the file's file extension or contents are not valid for a JPEG image. - """ - - def _file_extension_is_valid(filename: str): - allowed_extensions = ["jpg", "jpeg"] - if '.' not in filename: - return False - if filename.rsplit('.', 1)[1].lower() not in allowed_extensions: - return False - return True - - if not _file_extension_is_valid(file.filename): - raise InvalidImageException("Invalid file extension.") - - with TemporaryDirectory() as tempdir: - path_to_file = Path(tempdir) / "upload.jpg" - file.save(path_to_file) - try: - return Image.open(path_to_file, formats=['JPEG']) - except UnidentifiedImageError: - raise InvalidImageException( - "Image is either not a JPEG, or its contents are corrupted." - ) + email = StringField( + 'Email', + [InputRequired(), Email()] + ) + first_name = StringField( + 'First Name', + [InputRequired(), validate_name] + ) + last_name = StringField( + 'Last Name', + [InputRequired(), validate_name] + ) + display_name = StringField( + 'Nick Name', + [InputRequired(), validate_name] + ) + password = PasswordField( + 'Password', + [EqualTo('password_confirmation', message='Passwords must match')], + ) + password_confirmation = PasswordField( + 'Password (repeat)', + ) + picture = FileField( + 'Picture', + [FileAllowed(['jpg', 'jpeg'], 'JPEG images only.')] + ) @bp.route("/user/", methods=("GET", "POST")) @@ -87,52 +78,19 @@ def user_detail(username: str): user._generate_static_images() - if request.method == 'POST': - form_is_valid = True + # data = { + # "email": user.email, + # "first_name": user.first_name, + # "last_name": user.last_name, + # "display_name": user.display_name, + # } - if request.form['email']: - user.email = request.form['email'] - if not User.is_valid_email(user.email): - flash("Invalid email address.") - form_is_valid = False + form = UserEditForm(obj=user) + if form.validate_on_submit(): + conn.unbind() - if request.form['first_name']: - user.first_name = request.form['first_name'] - if not User.is_valid_person_name(user.first_name): - flash("Invalid first name.") - form_is_valid = False - - if request.form['last_name']: - user.last_name = request.form['last_name'] - if not User.is_valid_person_name(user.last_name): - flash("Invalid last name.") - form_is_valid = False - - if request.form['display_name']: - user.display_name = request.form['display_name'] - if not User.is_valid_person_name(user.display_name): - flash("Invalid nickname.") - form_is_valid = False - - if request.form['password']: - user.password_hash = User.generate_password_hash(request.form['password']) - - new_picture = None - if 'picture' in request.files: - file = request.files['picture'] - if len(file.filename): - try: - new_picture = _get_image_from_uploaded_file(file) - user.picture = new_picture - except InvalidImageException as e: - flash(f"Invalid picture: {e}") - form_is_valid = False - - if form_is_valid: - ldap.update_user(conn, user) - flash("User information was updated!") - if new_picture is not None: - ldap.get_user(conn, user.username)._generate_static_images(force=True) + # TODO update user + return redirect(request.url) conn.unbind() - return render_template('usermanager/user_detail.html', user=user) + return render_template('usermanager/user_detail.html', form=form, user=user) diff --git a/requirements.txt b/requirements.txt index 65b5d78..4452050 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,6 @@ ldap3==2.9.1 pytest==7.2.0 coverage==6.5.0 Pillow==9.3.0 +WTForms==3.0.1 +wtforms[email]==3.0.1 +Flask-WTF==1.0.1