refactor(usermanager): migrate to flask-wtf

This commit is contained in:
Julian Lobbes 2022-11-17 00:50:14 +01:00
parent 997327338e
commit 7b1196f09d
3 changed files with 75 additions and 103 deletions

View File

@ -4,18 +4,29 @@
<h1>User: {{ user.username }}</h1> <h1>User: {{ user.username }}</h1>
<img src="{{ url_for('static', filename='images/users/' + user.username + '/full.jpg') }}" alt="profile picture for user {{ user.username }}"> <img src="{{ url_for('static', filename='images/users/' + user.username + '/full.jpg') }}" alt="profile picture for user {{ user.username }}">
<form method="post" enctype="multipart/form-data"> <form method="post" enctype="multipart/form-data">
<label for="email">Email</label> {{ form.csrf_token }}
<input name="email" id="email" type="email" placeholder="{{ user.email }}">
<label for="first_name">First name</label> {{ form.email.label }}
<input name="first_name" id="first_name" placeholder="{{ user.first_name }}"> {{ form.email }}
<label for="last_name">Last name</label>
<input name="last_name" id="last_name" placeholder="{{ user.last_name }}"> {{ form.first_name.label }}
<label for="display_name">Nickname</label> {{ form.first_name }}
<input name="display_name" id="display_name" placeholder="{{ user.display_name }}">
<label for="password">Password</label> {{ form.last_name.label }}
<input name="password" id="password" type="password" placeholder="********"> {{ form.last_name }}
<label for="picture">Picture</label>
<input name="picture" id="picture" type="file" accept="image/jpeg"> {{ form.display_name.label }}
{{ form.display_name }}
{{ form.password.label }}
{{ form.password }}
{{ form.password_confirmation.label }}
{{ form.password_confirmation }}
{{ form.picture.label }}
{{ form.picture }}
<input type="submit" value="Update"> <input type="submit" value="Update">
</form> </form>
{% endblock content %} {% endblock content %}

View File

@ -4,9 +4,13 @@ from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from flask import ( from flask import (
Blueprint, render_template, abort, request, flash Blueprint, render_template, abort, request, flash, redirect
) )
from PIL import Image, UnidentifiedImageError 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 import lumi2.ldap as ldap
from lumi2.usermodel import User, Group from lumi2.usermodel import User, Group
@ -22,51 +26,38 @@ def index():
return render_template('usermanager/index.html') return render_template('usermanager/index.html')
class InvalidImageException(Exception): class UserEditForm(FlaskForm):
"""Raised when an image's filename or contents are invalid.""" @staticmethod
pass def validate_name(form, field) -> None:
if not User.is_valid_person_name(field.data):
raise ValidationError("Invalid name.")
email = StringField(
def _get_image_from_uploaded_file(file) -> Image.Image: 'Email',
"""Extracts a JPEG image from a file submitted via POST request. [InputRequired(), Email()]
)
The file's file extension and content is checked for validity as a JPEG image. first_name = StringField(
'First Name',
Parameters [InputRequired(), validate_name]
---------- )
file last_name = StringField(
A file object taken from a POST request. 'Last Name',
[InputRequired(), validate_name]
Returns )
------- display_name = StringField(
PIL.Image.Image 'Nick Name',
A valid JPEG Image object. [InputRequired(), validate_name]
)
Raises password = PasswordField(
------ 'Password',
InvalidImageException [EqualTo('password_confirmation', message='Passwords must match')],
When the file's file extension or contents are not valid for a JPEG image. )
""" password_confirmation = PasswordField(
'Password (repeat)',
def _file_extension_is_valid(filename: str): )
allowed_extensions = ["jpg", "jpeg"] picture = FileField(
if '.' not in filename: 'Picture',
return False [FileAllowed(['jpg', 'jpeg'], 'JPEG images only.')]
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."
) )
@ -87,52 +78,19 @@ def user_detail(username: str):
user._generate_static_images() user._generate_static_images()
if request.method == 'POST': # data = {
form_is_valid = True # "email": user.email,
# "first_name": user.first_name,
# "last_name": user.last_name,
# "display_name": user.display_name,
# }
if request.form['email']: form = UserEditForm(obj=user)
user.email = request.form['email'] if form.validate_on_submit():
if not User.is_valid_email(user.email): conn.unbind()
flash("Invalid email address.")
form_is_valid = False
if request.form['first_name']: # TODO update user
user.first_name = request.form['first_name'] return redirect(request.url)
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)
conn.unbind() conn.unbind()
return render_template('usermanager/user_detail.html', user=user) return render_template('usermanager/user_detail.html', form=form, user=user)

View File

@ -3,3 +3,6 @@ ldap3==2.9.1
pytest==7.2.0 pytest==7.2.0
coverage==6.5.0 coverage==6.5.0
Pillow==9.3.0 Pillow==9.3.0
WTForms==3.0.1
wtforms[email]==3.0.1
Flask-WTF==1.0.1