diff --git a/app.py b/app.py index 109b6da..e39c17e 100644 --- a/app.py +++ b/app.py @@ -1,9 +1,11 @@ import os -from flask import Flask, render_template, redirect +from flask import Flask, render_template, redirect, url_for, request, flash from flask_migrate import Migrate +from werkzeug.security import generate_password_hash, check_password_hash +from flask_login import (LoginManager, login_user, login_required, logout_user, current_user) from misc import datetime, date, time, currDay -from db import (db, Period, Task, Event) -from forms import (TaskForm, EventForm, PeriodForm) +from db import (db, Period, Task, Event, User) +from forms import (TaskForm, EventForm, PeriodForm, SignupForm, LoginForm) from create_events import createEvents basedir = os.path.abspath(os.path.dirname(__file__)) @@ -19,22 +21,73 @@ db.init_app(app) migrate = Migrate() migrate.init_app(app, db) +# Authentication stuff -from auth import auth as auth_blueprint -app.register_blueprint(auth_blueprint) +login_manager = LoginManager() +login_manager.login_view = 'login' +login_manager.init_app(app) +signup_enabled = False + +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) + +# Index route @app.route('/') def index(): createEvents(db, currDay, Period, Event) return redirect('/events') +# Authentication routes +@app.route('/login', methods=['GET', 'POST']) +def login(): + form = LoginForm() + if form.validate_on_submit(): + userName = form.userName.data + password = form.password.data + + user = User.query.filter_by(userName=userName).first() + if not user or not check_password_hash(user.password, password): + flash('Credentials incorrect! Please try again') + return redirect(url_for('login')) + login_user(user) + return redirect(url_for('events')) + return render_template('login.html', form=form) + +@app.route('/createaccount', methods=['GET', 'POST']) +def createAccount(): + if signup_enabled == True: + form = SignupForm() + if form.validate_on_submit(): + usernameExists = User.query.filter_by(userName=form.userName.data).first() + if usernameExists: + flash('Username already exists') + return redirect(url_for('createAccount')) + new_user = User(email=form.email.data, userName=form.userName.data, realName=form.realName.data, + password=generate_password_hash(form.password.data, method='sha256')) + db.session.add(new_user) + db.session.commit() + + return redirect(url_for('login')) + return render_template('createAccount.html', form=form) + else: + return 'Account creation is currently disabled' +@app.route('/logout') +@login_required +def logout(): + logout_user() + return redirect(url_for('index')) + # Periods routes @app.route('/periods') +@login_required def periods(): periods = Period.query.all() return render_template('periods.html', periods=periods, datetime=datetime) @app.route('/period/new', methods=('GET', 'POST')) +@login_required def newPeriod(): form = PeriodForm() if form.validate_on_submit(): @@ -49,6 +102,7 @@ def newPeriod(): @app.route('/period/edit/', methods=('GET', 'POST')) +@login_required def editPeriod(periodNum): period = Period.query.get_or_404(periodNum) form = PeriodForm(obj=period) @@ -60,6 +114,7 @@ def editPeriod(periodNum): return render_template('editPeriod.html', period=period, form=form, datetime=datetime) @app.post('/period/delete/') +@login_required def delete_period(periodNum): period = Period.query.get_or_404(periodNum) db.session.delete(period) @@ -69,6 +124,7 @@ def delete_period(periodNum): # Events routes @app.route('/events') +@login_required def events(): events = Event.query.all() periods = Period.query.all() @@ -77,6 +133,7 @@ def events(): return render_template('events.html', events=events, periods=periods, datetime=datetime, date=date) @app.route('/event/edit//', methods=('GET', 'POST')) +@login_required def editEvent(event_id): event = Event.query.get_or_404(event_id) form = EventForm(obj=event) @@ -91,16 +148,19 @@ def editEvent(event_id): # Tasks routes @app.route('/tasks') +@login_required def tasks(): tasks = Task.query.all() return render_template('tasks.html', str=str, tasks=tasks, datetime=datetime, date=date) @app.route('/task//') +@login_required def task(task_id): task = Task.query.get_or_404(task_id) return render_template('task.html', str=str, task=task, datetime=datetime, date=date) @app.route('/task/new', methods=('GET', 'POST')) +@login_required def newTask(): form = TaskForm() if form.validate_on_submit(): @@ -113,6 +173,7 @@ def newTask(): return render_template('newtask.html', form=form) @app.route('/task//edit', methods=('GET', 'POST')) +@login_required def editTask(task_id): task = Task.query.get_or_404(task_id) form = TaskForm(obj=task) @@ -123,6 +184,7 @@ def editTask(task_id): return redirect(f'/task/{task_id}') return render_template('edittask.html', task=task, form=form) @app.post('/task//delete') +@login_required def delete_task(task_id): task = Task.query.get_or_404(task_id) db.session.delete(task) diff --git a/auth.py b/auth.py deleted file mode 100644 index 3e971fd..0000000 --- a/auth.py +++ /dev/null @@ -1,12 +0,0 @@ -from flask import Blueprint, render_template -from app import db - -auth = Blueprint('auth', __name__) - -@auth.route('/login') -def login(): - return render_template('login.html') - -@auth.route('/logout') -def logout(): - return 'Logout' \ No newline at end of file diff --git a/db.py b/db.py index 5c492a7..6d380b0 100644 --- a/db.py +++ b/db.py @@ -1,4 +1,5 @@ from flask_sqlalchemy import SQLAlchemy +from flask_login import UserMixin db = SQLAlchemy() @@ -36,7 +37,7 @@ class Event(db.Model): return f'' -class User(db.Model): +class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) userName = db.Column(db.String(1000)) email = db.Column(db.String(100), unique=True) diff --git a/forms.py b/forms.py index 1a264a6..928c5fb 100644 --- a/forms.py +++ b/forms.py @@ -1,7 +1,7 @@ from db import Task from flask_wtf import FlaskForm from wtforms import (StringField, DateField, TimeField, TextAreaField, IntegerField, BooleanField, - RadioField) + RadioField, EmailField, PasswordField) from wtforms.validators import InputRequired, Length from wtforms_sqlalchemy.orm import QuerySelectField @@ -19,4 +19,14 @@ class EventForm(FlaskForm): class PeriodForm(FlaskForm): weekendSchedule = BooleanField(label='Include on Weekends?', false_values=None) - periodTime = TimeField('Time', format="%H:%M") \ No newline at end of file + periodTime = TimeField('Time', format="%H:%M") + +class SignupForm(FlaskForm): + userName = StringField('Username', validators=[InputRequired()]) + password = PasswordField('Password', validators=[InputRequired()]) + realName = StringField('Real Name') + email = EmailField('Email Address') + +class LoginForm(FlaskForm): + userName = StringField('Username', validators=[InputRequired()]) + password = PasswordField('Password', validators=[InputRequired()]) \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index b6fb1cb..ba71787 100644 --- a/templates/base.html +++ b/templates/base.html @@ -29,6 +29,14 @@ +
+ {% if current_user.is_authenticated %} + Logout {{current_user.userName}} + {% endif %} + {% if not current_user.is_authenticated %} + Login + {% endif %} +

diff --git a/templates/createAccount.html b/templates/createAccount.html new file mode 100644 index 0000000..e27cd3a --- /dev/null +++ b/templates/createAccount.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} + +{% block content %} +

{% block title %} Create Account {% endblock %}

+{% with messages = get_flashed_messages() %} +{% if messages %} +
+ {{ messages[0] }}. Go to login page. +
+{% endif %} +{% endwith %} +
+ {{ form.csrf_token }} +

+ {{ form.userName.label }} + {{ form.userName }} +

+

+ {{ form.password.label }} + {{ form.password }} +

+

+ {{ form.realName.label }} + {{ form.realName }} +

+

+ {{ form.email.label }} + {{ form.email }} +

+

+ +

+
+{% endblock %} \ No newline at end of file diff --git a/templates/login.html b/templates/login.html index c006214..ed2c19e 100644 --- a/templates/login.html +++ b/templates/login.html @@ -1,29 +1,26 @@ {% extends "base.html" %} {% block content %} -
-

Login

-
-
-
-
- -
-
- -
-
- -
-
-
- -
- -
+

{% block title %} Login {% endblock %}

+{% with messages = get_flashed_messages() %} +{% if messages %} +
+ {{ messages[0] }}
-
+{% endif %} +{% endwith %} +
+ {{ form.csrf_token }} +

+ {{ form.userName.label }} + {{ form.userName }} +

+

+ {{ form.password.label }} + {{ form.password }} +

+

+ +

+
{% endblock %} \ No newline at end of file