summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md10
-rw-r--r--flaskr/__init__.py2
-rw-r--r--flaskr/auth.py102
3 files changed, 112 insertions, 2 deletions
diff --git a/README.md b/README.md
index 17876c2..6433ba2 100644
--- a/README.md
+++ b/README.md
@@ -24,4 +24,12 @@ Concepts:
* flask.open_resource: From app package path
* request: HTML request???
* Factory function > registered functions and blueprints.
-* Blueprint > views & other code \ No newline at end of file
+* Blueprint: groups views & other code
+* View: function that returns HTML
+* flask.session: dict that stores data across requests (cookies) (securely
+ signed with SECRET_KEY)
+* *endpoint*: name associated with a view, `<blueprint_name>.<view_function_name>`
+
+Gotchas:
+
+* Remember to call db.commit() after modifying DB (DML). \ No newline at end of file
diff --git a/flaskr/__init__.py b/flaskr/__init__.py
index 19d5a1e..dffc858 100644
--- a/flaskr/__init__.py
+++ b/flaskr/__init__.py
@@ -25,7 +25,7 @@ def create_app(test_config=None):
# Routes
@app.route('/')
- def hello():
+ def index():
return 'Hello, World!'
# Register functions and blueprints
diff --git a/flaskr/auth.py b/flaskr/auth.py
new file mode 100644
index 0000000..3689eba
--- /dev/null
+++ b/flaskr/auth.py
@@ -0,0 +1,102 @@
+"""Authentication blueprint"""
+
+import functools
+
+from flask import (
+ Blueprint, flash, g, redirect, render_template, request, session, url_for
+)
+from werkzeug.security import check_password_hash, generate_password_hash
+from flaskr.db import get_db
+
+bp = Blueprint('auth', __name__, url_prefix='/auth')
+
[email protected]('/register', methods=('GET', 'POST'))
+def register():
+ if request.method == 'POST':
+ # Form validation
+ username = request.form['username']
+ password = request.form['password']
+ db = get_db()
+ error = None
+
+ if not username:
+ error = 'Username is required.'
+ elif not password:
+ error = 'Password is required.'
+
+ if error is None:
+ try:
+ # NOTE: don't use f-string here. Use `?` placeholders so that
+ # database library can escape the fields
+ # (otherwise SQL injection vulnerability)
+ db.execute(
+ "INSERT INTO user (username, password) VALUES (?, ?)",
+ (username, generate_password_hash(password)),
+ )
+ db.commit()
+ except db.IntegrityError:
+ error = f"User {username} is already registered."
+ else:
+ return redirect(url_for("auth.login"))
+
+ flash(error)
+
+ return render_template('auth/register.html')
+
+
[email protected]('/login', methods=('GET', 'POST'))
+def login():
+ if request.method == 'POST':
+ username = request.form['username']
+ password = request.form['password']
+ db = get_db()
+ error = None
+ user = db.execute(
+ 'SELECT * FROM user WHERE username = ?', (username,)
+ ).fetchone()
+
+ if user is None:
+ error = 'Incorrect username.'
+ elif not check_password_hash(user['password'], password):
+ error = 'Incorrect password.'
+
+ if error is None:
+ session.clear()
+ session['user_id'] = user['id']
+ return redirect(url_for('index'))
+
+ flash(error)
+
+ return render_template('auth/login.html')
+
+
+# runs before the view function, no matter what URL is requested
[email protected]_app_request
+def load_logged_in_user():
+ user_id = session.get('user_id')
+
+ if user_id is None:
+ g.user = None
+ else:
+ g.user = get_db().execute(
+ 'SELECT * FROM user WHERE id = ?', (user_id,)
+ ).fetchone()
+
+
+def logout():
+ session.clear()
+ return redirect(url_for('index'))
+
+
+# Define decorator to require authentication in other views
+def login_required(view):
+ """view is a function that returns HTML (and is part of a blueprint)"""
+ @functools.wraps(view)
+ def wrapped_view(**kwargs):
+ if g.user is None:
+ return redirect(url_for('auth.login'))
+
+ return view(**kwargs)
+
+ return wrapped_view \ No newline at end of file