mirror of
https://github.com/Xevion/runnerspace.git
synced 2025-12-08 10:08:24 -06:00
Migrate app to use WTForms for auth form validation
- Not finished yet, major styling breakage in this commit - Also encapsulated GET & POST requests of /login and /signup routes into one route.
This commit is contained in:
2
app.py
2
app.py
@@ -44,7 +44,7 @@ def create_app():
|
|||||||
from routes import blueprint as routes_blueprint
|
from routes import blueprint as routes_blueprint
|
||||||
app.register_blueprint(routes_blueprint)
|
app.register_blueprint(routes_blueprint)
|
||||||
|
|
||||||
from forms import blueprint as forms_blueprint
|
from route_forms import blueprint as forms_blueprint
|
||||||
app.register_blueprint(forms_blueprint)
|
app.register_blueprint(forms_blueprint)
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
|
|||||||
53
auth.py
53
auth.py
@@ -1,4 +1,4 @@
|
|||||||
from flask import Blueprint, flash, redirect, request, url_for
|
from flask import Blueprint, flash, redirect, request, url_for, render_template
|
||||||
from flask_login import login_required, login_user, logout_user, current_user
|
from flask_login import login_required, login_user, logout_user, current_user
|
||||||
from werkzeug.security import check_password_hash, generate_password_hash
|
from werkzeug.security import check_password_hash, generate_password_hash
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ def edit_profile_post(username: str):
|
|||||||
form = EditProfileForm(request.form)
|
form = EditProfileForm(request.form)
|
||||||
user = User.query.filter_by(username=username).first_or_404()
|
user = User.query.filter_by(username=username).first_or_404()
|
||||||
|
|
||||||
if current_user.is_admin or user.id == current_user.id
|
if current_user.is_admin or user.id == current_user.id:
|
||||||
if form.validate():
|
if form.validate():
|
||||||
user.about_me = form.about_me.data
|
user.about_me = form.about_me.data
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@@ -22,38 +22,45 @@ def edit_profile_post(username: str):
|
|||||||
return redirect(url_for('main.view_user', username=user.username))
|
return redirect(url_for('main.view_user', username=user.username))
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/login', methods=['POST'])
|
@blueprint.route('/login', methods=['GET', 'POST'])
|
||||||
def login_post():
|
def login():
|
||||||
form = LoginForm(request.form)
|
form = LoginForm(request.form)
|
||||||
user = User.query.filter_by(username=form.data.username).first()
|
|
||||||
|
|
||||||
# check if the user actually exists, and compare password given
|
if request.method == 'POST' and form.validate():
|
||||||
if not user or not check_password_hash(user.password, form.password.data):
|
user = User.query.filter_by(username=form.username.data).first()
|
||||||
flash('Please check your login details and try again.')
|
|
||||||
return redirect(url_for('main.login'))
|
|
||||||
|
|
||||||
login_user(user, remember=form.remember.data)
|
# check if the user actually exists, and compare password given
|
||||||
return redirect(url_for('main.index'))
|
if not user or not check_password_hash(user.password, form.password.data):
|
||||||
|
flash('Please check your login details and try again.')
|
||||||
|
return redirect(url_for('auth.login'))
|
||||||
|
|
||||||
|
login_user(user, remember=form.remember.data)
|
||||||
|
return redirect(url_for('auth.index'))
|
||||||
|
|
||||||
|
return render_template('pages/auth/login.html', form=form)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/signup', methods=['POST'])
|
@blueprint.route('/signup', methods=['GET', 'POST'])
|
||||||
def signup_post():
|
def signup():
|
||||||
# validate and add user to db
|
# validate and add user to db
|
||||||
form = RegistrationForm(request.form)
|
form = RegistrationForm(request.form)
|
||||||
|
|
||||||
user = User.query.filter_by(username=form.username.data).first() # Check if the username is already in use
|
if request.method == 'POST' and form.validate():
|
||||||
if user: # redirect back to sign-up page
|
user = User.query.filter_by(username=form.username.data).first() # Check if the username is already in use
|
||||||
flash('This username is already in use.')
|
if user: # redirect back to sign-up page
|
||||||
return redirect(url_for('main.signup'))
|
flash('This username is already in use.')
|
||||||
|
return redirect(url_for('auth.signup'))
|
||||||
|
|
||||||
# Create new user with form data
|
# Create new user with form data
|
||||||
new_user = User(username=form.username.data, name=form.name.data, password=generate_password_hash(form.password.data, method='sha256'))
|
new_user = User(username=form.username.data, name=form.name.data,
|
||||||
|
password=generate_password_hash(form.password.data, method='sha256'))
|
||||||
|
# Add new user to db
|
||||||
|
db.session.add(new_user)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
# Add new user to db
|
return redirect(url_for('auth.login'))
|
||||||
db.session.add(new_user)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return redirect(url_for('main.login'))
|
return render_template('pages/auth/signup.html', form=form)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/logout')
|
@blueprint.route('/logout')
|
||||||
|
|||||||
74
forms.py
74
forms.py
@@ -1,63 +1,31 @@
|
|||||||
from flask import Blueprint, flash, redirect, request, url_for
|
from wtforms import Form, BooleanField, StringField, PasswordField, validators
|
||||||
from flask_login import current_user, login_required
|
|
||||||
from profanity_filter import ProfanityFilter
|
|
||||||
|
|
||||||
from database import db
|
|
||||||
from models import User, Post, Comment
|
|
||||||
|
|
||||||
blueprint = Blueprint('forms', __name__)
|
|
||||||
pf = ProfanityFilter()
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/user/<username>/edit', methods=['POST'])
|
class RegistrationForm(Form):
|
||||||
@login_required
|
username = StringField('Username', [validators.Length(min=4, max=25)])
|
||||||
def edit_profile_post(username):
|
name = StringField('Name', [validators.Length(min=2, max=35)])
|
||||||
user = db.session.query(User).filter_by(username=username).first_or_404()
|
password = PasswordField('New Password', [
|
||||||
|
validators.DataRequired(),
|
||||||
# Allow admins to edit profiles, but deny other users
|
validators.EqualTo('confirm', message='Passwords must match')
|
||||||
if not current_user.is_admin and current_user.id != user.id:
|
])
|
||||||
return redirect(url_for('main.user', username=username))
|
confirm = PasswordField('Repeat Password')
|
||||||
|
accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()])
|
||||||
user.about_me = request.form.get('about-me', user.about_me)
|
|
||||||
user.name = request.form.get('name', user.name)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
flash('Successfully updated profile.')
|
|
||||||
return redirect(url_for('main.edit_user', username=username))
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/feed/new', methods=['POST'])
|
class LoginForm(Form):
|
||||||
@login_required
|
username = StringField('Username', [validators.DataRequired()])
|
||||||
def new_post():
|
password = StringField('Password', [validators.DataRequired()])
|
||||||
post_text = request.form.get('text')
|
remember_me = BooleanField('Remember Me', [validators.Optional()])
|
||||||
|
|
||||||
post = Post(author=current_user.id, text=post_text)
|
|
||||||
db.session.add(post)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return redirect(url_for('main.view_post', post_id=post.id))
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/feed/<post_id>/comment', methods=['POST'])
|
class EditProfileForm(Form):
|
||||||
@login_required
|
name = RegistrationForm.name
|
||||||
def add_comment(post_id: int):
|
about_me = StringField('About Me', [validators.Optional()])
|
||||||
post = Post.query.get_or_404(post_id)
|
|
||||||
|
|
||||||
comment_text: str = request.form.get('comment-text')
|
|
||||||
|
|
||||||
if len(comment_text) > 50:
|
class NewPostForm(Form):
|
||||||
flash('Cannot have more than 50 characters of text.')
|
text = StringField('Text', [validators.Length(min=15, max=1000)])
|
||||||
return redirect(url_for('main.view_post', post_id=post_id))
|
|
||||||
elif len(comment_text) < 5:
|
|
||||||
flash('Your comment must have at least 5 characters of text.')
|
|
||||||
return redirect(url_for('main.view_post', post_id=post_id))
|
|
||||||
|
|
||||||
if not pf.is_clean(comment_text):
|
|
||||||
flash('Sorry, profanity is not allowed on runnerspace.')
|
|
||||||
return redirect(url_for('main.view_post', post_id=post_id))
|
|
||||||
|
|
||||||
comment = Comment(post=post.id, author=current_user.id, text=comment_text)
|
class NewCommentForm(Form):
|
||||||
db.session.add(comment)
|
text = StringField('Text', [validators.Length(min=5, max=50)])
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return redirect(url_for('main.view_post', post_id=post.id))
|
|
||||||
|
|||||||
61
route_forms.py
Normal file
61
route_forms.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
from flask import Blueprint, flash, redirect, request, url_for
|
||||||
|
from flask_login import current_user, login_required
|
||||||
|
from profanity_filter import ProfanityFilter
|
||||||
|
from forms import RegistrationForm, EditProfileForm, NewPostForm, NewCommentForm
|
||||||
|
from database import db
|
||||||
|
from models import User, Post, Comment
|
||||||
|
|
||||||
|
blueprint = Blueprint('forms', __name__)
|
||||||
|
pf = ProfanityFilter()
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/user/<username>/edit', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_profile_post(username):
|
||||||
|
user = db.session.query(User).filter_by(username=username).first_or_404()
|
||||||
|
|
||||||
|
# Allow admins to edit profiles, but deny other users
|
||||||
|
if not current_user.is_admin and current_user.id != user.id:
|
||||||
|
return redirect(url_for('main.view_user', username=username))
|
||||||
|
|
||||||
|
form = RegistrationForm(request.form)
|
||||||
|
if form.validate():
|
||||||
|
user.about_me = form.about_me.data
|
||||||
|
user.name = form.name.data
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
flash('Successfully updated profile.')
|
||||||
|
return redirect(url_for('main.edit_user', username=username))
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/feed/new', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def new_post():
|
||||||
|
form = NewPostForm(request.form)
|
||||||
|
|
||||||
|
if form.validate():
|
||||||
|
post = Post(author=current_user.id, text=form.text.data)
|
||||||
|
db.session.add(post)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return redirect(url_for('main.view_post', post_id=post.id))
|
||||||
|
else:
|
||||||
|
redirect(url_for('main.feed'))
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/feed/<post_id>/comment', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def add_comment(post_id: int):
|
||||||
|
post = Post.query.get_or_404(post_id)
|
||||||
|
form = NewCommentForm(request.form)
|
||||||
|
|
||||||
|
if form.validate():
|
||||||
|
if not pf.is_clean(form.text.data):
|
||||||
|
flash('Sorry, profanity is not allowed on runnerspace.')
|
||||||
|
return redirect(url_for('main.view_post', post_id=post_id))
|
||||||
|
|
||||||
|
comment = Comment(post=post.id, author=current_user.id, text=form.text.data)
|
||||||
|
db.session.add(comment)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return redirect(url_for('main.view_post', post_id=post.id))
|
||||||
10
routes.py
10
routes.py
@@ -68,7 +68,6 @@ def edit_user(username: str):
|
|||||||
return render_template('pages/user_edit.html', user=user)
|
return render_template('pages/user_edit.html', user=user)
|
||||||
return redirect(url_for('main.view_user', username=username))
|
return redirect(url_for('main.view_user', username=username))
|
||||||
|
|
||||||
|
|
||||||
# @blueprint.route('/blogs')
|
# @blueprint.route('/blogs')
|
||||||
# def blogs():
|
# def blogs():
|
||||||
# return render_template('pages/blogs.html')
|
# return render_template('pages/blogs.html')
|
||||||
@@ -77,12 +76,3 @@ def edit_user(username: str):
|
|||||||
# @blueprint.route('/groups')
|
# @blueprint.route('/groups')
|
||||||
# def groups():
|
# def groups():
|
||||||
# return render_template('pages/groups.html')
|
# return render_template('pages/groups.html')
|
||||||
|
|
||||||
@blueprint.route('/login', methods=['GET'])
|
|
||||||
def login():
|
|
||||||
return render_template('pages/auth/login.html')
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/signup', methods=['GET'])
|
|
||||||
def signup():
|
|
||||||
return render_template('pages/auth/signup.html')
|
|
||||||
|
|||||||
@@ -131,10 +131,10 @@ form.login-form {
|
|||||||
border: 1px solid darkblue;
|
border: 1px solid darkblue;
|
||||||
padding: 0.7em;
|
padding: 0.7em;
|
||||||
}
|
}
|
||||||
form .field {
|
form field {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
form .field .checkbox {
|
form field .checkbox {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
form button {
|
form button {
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ form {
|
|||||||
padding: 0.7em;
|
padding: 0.7em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field {
|
field {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
|
||||||
.checkbox {
|
.checkbox {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<a href="{{ url_for('main.edit_user', username=current_user.username) }}"><i class="fas fa-cog fa-1x"></i></a>
|
<a href="{{ url_for('main.edit_user', username=current_user.username) }}"><i class="fas fa-cog fa-1x"></i></a>
|
||||||
| <a href="{{ url_for('auth.logout') }}">Logout</a>
|
| <a href="{{ url_for('auth.logout') }}">Logout</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('main.login') }}">Login</a> or <a href="{{ url_for('main.signup') }}">Sign-up</a>!
|
<a href="{{ url_for('auth.login') }}">Login</a> or <a href="{{ url_for('auth.signup') }}">Sign-up</a>!
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
{% macro render_field(field) %}
|
{% macro render_field(field) %}
|
||||||
<dt>{{ field.label }}
|
<field>
|
||||||
<dd>{{ field(**kwargs)|safe }}
|
<label>{{ field.label }}</label>
|
||||||
{% if field.errors %}
|
{{ field(**kwargs)|safe }}
|
||||||
<ul class=errors>
|
{% if field.errors %}
|
||||||
{% for error in field.errors %}
|
<ul class=errors>
|
||||||
<li>{{ error }}</li>
|
{% for error in field.errors %}
|
||||||
{% endfor %}
|
<li>{{ error }}</li>
|
||||||
</ul>
|
{% endfor %}
|
||||||
{% endif %}
|
</ul>
|
||||||
</dd>
|
{% endif %}
|
||||||
|
<br>
|
||||||
|
</field>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{% extends 'layouts/index.html' %}
|
{% extends 'layouts/index.html' %}
|
||||||
|
{% from 'macros.html' import render_field %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% with messages = get_flashed_messages() %}
|
{% with messages = get_flashed_messages() %}
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
@@ -7,29 +9,14 @@
|
|||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<form method="POST" action="{{ url_for('auth.login_post') }}" class="login-form">
|
<form method="POST" action="{{ url_for('auth.login') }}" class="login-form">
|
||||||
<div class="field">
|
{{ render_field(form.username) }}
|
||||||
<div class="control">
|
{{ render_field(form.password) }}
|
||||||
Username
|
{{ render_field(form.remember_me) }}
|
||||||
<input class="input is-large" type="text" name="username" placeholder="Username" autofocus="true">
|
<p><input type=submit value=Login>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="control">
|
|
||||||
Password
|
|
||||||
<input class="input is-large" type="password" name="password" placeholder="Password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label class="checkbox">
|
|
||||||
<input type="checkbox">
|
|
||||||
Remember me
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<button class="button">Login</button>
|
|
||||||
</form>
|
</form>
|
||||||
<p class="form-subtext">
|
<p class="form-subtext">
|
||||||
Don't have a login? <a href="{{ url_for('main.signup') }}">Sign-up</a> instead!
|
Don't have a login? <a href="{{ url_for('auth.signup') }}">Sign-up</a> instead!
|
||||||
</p>
|
</p>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -1,45 +1,19 @@
|
|||||||
{% extends 'layouts/index.html' %}
|
{% extends 'layouts/index.html' %}
|
||||||
|
{% from "macros.html" import render_field %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% with messages = get_flashed_messages() %}
|
<form method=post class="login-form">
|
||||||
{% if messages %}
|
<dl>
|
||||||
<span class="error-message">
|
{{ render_field(form.username) }}
|
||||||
{{ messages[0] }}. Go to <a href="{{ url_for('main.login') }}">login page</a>.
|
{{ render_field(form.name) }}
|
||||||
</span>
|
{{ render_field(form.password) }}
|
||||||
{% endif %}
|
{{ render_field(form.confirm) }}
|
||||||
{% endwith %}
|
{{ render_field(form.accept_tos) }}
|
||||||
|
</dl>
|
||||||
<form method="POST" action="{{ url_for('auth.signup_post') }}" class="login-form">
|
<p><input type=submit value=Register>
|
||||||
<div class="field">
|
|
||||||
<div class="control">
|
|
||||||
Username
|
|
||||||
<input class="input" type="text" name="username" placeholder="Username" autofocus>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="control">
|
|
||||||
Name
|
|
||||||
<input class="input" type="text" name="name" placeholder="Name" autofocus>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="control">
|
|
||||||
Password
|
|
||||||
<input class="input" type="password" name="password" placeholder="Password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="control">
|
|
||||||
Confirm Password
|
|
||||||
<input class="input" type="password" name="confirm" placeholder="Confirm Password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button>Sign Up</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p class="form-subtext">
|
<p class="form-subtext">
|
||||||
Already have a login? <a href="{{ url_for('main.login') }}">Login</a> instead!
|
Already have a login? <a href="{{ url_for('auth.login') }}">Login</a> instead!
|
||||||
</p>
|
</p>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
Reference in New Issue
Block a user