From 420113c518db51bcd8f75847a8ddf2686a321e97 Mon Sep 17 00:00:00 2001 From: girst Date: Fri, 5 Jun 2020 21:05:20 +0200 Subject: [PATCH] initial version of user authentication using flask-login --- app/frontend.py | 72 ++++++++++++++++++++++++++++++++++++++++- config/requirements.txt | 1 + 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/app/frontend.py b/app/frontend.py index 82f87d7..354262b 100644 --- a/app/frontend.py +++ b/app/frontend.py @@ -7,7 +7,9 @@ import sqlite3 import secrets import requests from urllib.parse import parse_qs +from werkzeug.security import generate_password_hash, check_password_hash from flask import Flask, render_template, request, redirect, flash, url_for, jsonify, g +from flask_login import LoginManager, UserMixin, current_user, login_user, logout_user, login_required from common import * @@ -16,14 +18,17 @@ if 'secret_key' in cf['frontend']: app.secret_key = base64.b64decode(cf['frontend']['secret_key']) else: app.secret_key = secrets.token_bytes(16) +login = LoginManager(app) +login.login_view = 'login_form' @app.route('/') def index(): return redirect(url_for('feed'), code=302) @app.route('/feed/subscriptions') +@login_required def feed(): - token = request.args.get('token', 'guest') + token = current_user.token # request.args.get('token', 'guest') page = int(request.args.get('page', 0)) with sqlite3.connect(cf['global']['database']) as conn: c = conn.cursor() @@ -233,6 +238,39 @@ def reddit(subreddit="videos"): after = r.json()['data']['after'] return render_template('reddit.html.j2', subreddit=subreddit, rows=videos, before=before, after=after, count=count) +@app.route('/login') +def login_form(): + if current_user.is_authenticated: + return """ + +
+ +
+ """ + else: + return """ + +
+
+
+ + +
+ """ +@app.route('/logout', methods=['POST']) +def do_logout(): + logout_user() + return redirect(url_for('index')) + +@app.route('/login', methods=['POST']) +def do_login(): + user = User.from_name(request.form.get('user')) + if user and user.check_password(request.form.get('password')): + login_user(user, remember=request.form.get('remember',False)) + return redirect(url_for('index')) + flash(('error', 'wrong username and/or password')) + return redirect(url_for('login_form')) + def get_cipher(): # reload cipher from database every 1 hour if 'cipher' not in g or time.time() - g.get('cipher_updated', 0) > 1 * 60 * 60: @@ -244,6 +282,38 @@ def get_cipher(): return g.cipher +class User(UserMixin): + def __init__(self, id, name, passwd, token): + self.id = id + self.name = name + self.passwd = passwd + self.token = token + def set_password(self, passwd): + self.passwd = generate_password_hash(passwd) + # ^TODO: store changes to database + def check_password(self, passwd): + return check_password_hash(self.passwd, passwd) + @classmethod + def from_id(self, id): + with sqlite3.connect(cf['global']['database']) as conn: + c = conn.cursor() + c.execute("SELECT name,password,token FROM users WHERE id = ?", (id,)) + name, passwd, token = c.fetchone() + return User(id, name, passwd, token) + # ^TODO: not found + @classmethod + def from_name(self, name): + with sqlite3.connect(cf['global']['database']) as conn: + c = conn.cursor() + c.execute("SELECT id,password,token FROM users WHERE name=?", (name,)) + id, passwd, token = c.fetchone() + return User(id, name, passwd, token) + # ^TODO: not found + +@login.user_loader +def load_user(id): + return User.from_id(int(id)) + #@app.teardown_appcontext #def teardown_db(): # db = g.pop('db', None) diff --git a/config/requirements.txt b/config/requirements.txt index 12c6d56..dc5f062 100644 --- a/config/requirements.txt +++ b/config/requirements.txt @@ -1,4 +1,5 @@ Flask +flask-login gunicorn requests requests-cache -- 2.39.3