diff --git a/code/Pipfile b/code/Pipfile index ad15fdd..b6fe635 100644 --- a/code/Pipfile +++ b/code/Pipfile @@ -12,6 +12,7 @@ flask-sqlalchemy = "*" pandas = "*" iso3166 = "*" flask-wtf = "*" +flask-login = "*" [requires] python_version = "3.8" diff --git a/code/Pipfile.lock b/code/Pipfile.lock index ea74bfb..646671d 100644 --- a/code/Pipfile.lock +++ b/code/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f4ff138e539e1c5bcdc3a3f5046b1d5087a0f0315d1a18181fc2c6eeb1227671" + "sha256": "234ca1fc7cbb10534d17febc8a9d0320ecb5c6317351bcd97e161c860201b5a5" }, "pipfile-spec": 6, "requires": { @@ -31,6 +31,13 @@ "index": "pypi", "version": "==1.1.1" }, + "flask-login": { + "hashes": [ + "sha256:c815c1ac7b3e35e2081685e389a665f2c74d7e077cb93cecabaea352da4752ec" + ], + "index": "pypi", + "version": "==0.4.1" + }, "flask-sqlalchemy": { "hashes": [ "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", diff --git a/code/app/__init__.py b/code/app/__init__.py index 6d6c9a3..94eb5a9 100644 --- a/code/app/__init__.py +++ b/code/app/__init__.py @@ -1,10 +1,12 @@ from flask import Flask from config import Config from flask_sqlalchemy import SQLAlchemy +from flask_login import LoginManager app = Flask(__name__) app.config.from_object(Config) db = SQLAlchemy(app) - +login = LoginManager(app) +login.login_view = "login" from app import routes, models diff --git a/code/app/forms.py b/code/app/forms.py new file mode 100644 index 0000000..55a4d2d --- /dev/null +++ b/code/app/forms.py @@ -0,0 +1,10 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, BooleanField, SubmitField +from wtforms.validators import DataRequired + + +class LoginForm(FlaskForm): + username = StringField("Username", validators=[DataRequired()]) + password = PasswordField("Password", validators=[DataRequired()]) + remember_me = BooleanField("Remember Me") + submit = SubmitField("Sign In") diff --git a/code/app/models.py b/code/app/models.py index 0fc8dfd..d652a02 100644 --- a/code/app/models.py +++ b/code/app/models.py @@ -1,16 +1,16 @@ -from app import db -from subprocess import run -from database.constants import DB_USER, DB_PW, DB_NAME +from app import db, login +from flask_login import UserMixin +from werkzeug.security import check_password_hash class Glacier(db.Model): - uid = db.Column(db.String(5), primary_key=True) + id = db.Column(db.String(20), primary_key=True) country = db.Column(db.String(60)) name = db.Column(db.String(60)) annual_data = db.relationship("Annual_Data") - def __init__(self, uid, country, name): - self.uid = uid + def __init__(self, id, country, name): + self.id = id self.country = country self.name = name @@ -18,7 +18,7 @@ class Glacier(db.Model): class Annual_Data(db.Model): __tablename__ = "annual_data" year = db.Column(db.Integer, primary_key=True) - uid = db.Column(db.ForeignKey("glacier.uid"), primary_key=True) + id = db.Column(db.ForeignKey("glacier.id"), primary_key=True) surface = db.Column(db.Float) length = db.Column(db.Float) elevation = db.Column(db.Float) @@ -30,15 +30,22 @@ class Annual_Data(db.Model): self.elevation = elevation -class User(db.Model): - uid = db.Column(db.Integer, primary_key=True) +class User(UserMixin, db.Model): + id = db.Column(db.Integer, primary_key=True) registration_date = db.Column( db.DateTime, nullable=False, server_default=db.func.now() ) username = db.Column(db.String(20), nullable=False, unique=True) - password = db.Column(db.String(60)) + password_hash = db.Column(db.String(128), unique=True) - def __init__(self, uid, username, password): - self.uid = uid + def __init__(self, id, username, password_hash): + self.id = id self.username = username - self.password = password + self.password_hash = password_hash + + def check_password(self, password): + return check_password_hash(self.password_hash, password) + + @login.user_loader + def load_user(id): + return User.query.get(int(id)) diff --git a/code/app/routes.py b/code/app/routes.py index 772bbc8..1eca20e 100644 --- a/code/app/routes.py +++ b/code/app/routes.py @@ -1,13 +1,42 @@ from app import app -from flask import render_template +from app.forms import LoginForm +from app.models import User +from flask import flash, redirect, render_template, url_for, request +from flask_login import current_user, login_user, logout_user, login_required +from werkzeug.urls import url_parse @app.route("/") @app.route("/index") def index(): - user = {"username": "Bolaji"} - posts = [ - {"author": {"username": "Miloud"}, "body": "Beautiful day in Meknes!"}, - {"author": {"username": "Sebtaoui"}, "body": "The Farkouss movie was lit!"}, - ] - return render_template("index.html", title="Home", user=user, posts=posts) + return render_template("index.html", title="Home Page") + + +@app.route("/login", methods=["GET", "POST"]) +def login(): + if current_user.is_authenticated: + return redirect(url_for("admin")) + form = LoginForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=form.username.data).first() + if user is None or not user.check_password(form.password.data): + flash("Invalid username or password") + return redirect(url_for("login")) + login_user(user, remember=form.remember_me.data) + next_page = request.args.get("next") + if not next_page or url_parse(next_page).netloc != "": + next_page = url_for("admin") + return redirect(next_page) + return render_template("login.html", title="Sign In", form=form) + + +@app.route("/logout") +def logout(): + logout_user() + return redirect(url_for("index")) + + +@app.route("/admin") +@login_required +def admin(): + return render_template("admin.html", title="Admin Page") diff --git a/code/app/templates/admin.html b/code/app/templates/admin.html new file mode 100644 index 0000000..2410e53 --- /dev/null +++ b/code/app/templates/admin.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} + +{% block content %} +