]> git.gir.st - subscriptionfeed.git/blob - app/common/user.py
token auth: don't 500 if user doesn't exist
[subscriptionfeed.git] / app / common / user.py
1 from werkzeug.security import generate_password_hash, check_password_hash
2 from .common import cf
3 import sqlite3
4 from flask_login import LoginManager, UserMixin, login_user, logout_user
5 from flask import Blueprint, flash, redirect, render_template, url_for, request
6
7 class User(UserMixin): # TODO: to common
8 def __init__(self, id, name, passwd, token):
9 self.id = id
10 self.name = name
11 self.passwd = passwd
12 self.token = token
13 def get_id(self):
14 return self.token
15 def set_password(self, passwd):
16 self.passwd = generate_password_hash(passwd)
17 # ^TODO: store changes to database
18 def check_password(self, passwd):
19 return check_password_hash(self.passwd, passwd)
20 @classmethod
21 def from_id(self, id):
22 with sqlite3.connect(cf['global']['database']) as conn:
23 c = conn.cursor()
24 c.execute("SELECT name,password,token FROM users WHERE id = ?", (id,))
25 try:
26 name, passwd, token = c.fetchone()
27 except: return None # todo: ugly
28 return User(id, name, passwd, token)
29 @classmethod
30 def from_name(self, name):
31 with sqlite3.connect(cf['global']['database']) as conn:
32 c = conn.cursor()
33 c.execute("SELECT id,password,token FROM users WHERE name=?", (name,))
34 try:
35 id, passwd, token = c.fetchone()
36 except: return None # todo: ugly
37 return User(id, name, passwd, token)
38 @classmethod
39 def from_token(self, token):
40 with sqlite3.connect(cf['global']['database']) as conn:
41 c = conn.cursor()
42 c.execute("SELECT id,name,password FROM users WHERE token=?", (token,))
43 try:
44 id, name, passwd, = c.fetchone()
45 except: return None # todo: ugly
46 return User(id, name, passwd, token)
47
48
49 def init_login(app):
50 login = LoginManager()
51 login.login_view = 'usermgmt.login_form'
52 login.init_app(app)
53
54 @login.user_loader
55 def load_user(token):
56 # in the future tokens will be invalidable by users. -> https://flask-login.readthedocs.io/en/latest/#alternative-tokens
57 return User.from_token(token)
58
59 @login.request_loader
60 def querytoken_auth(request):
61 if request.args.get('token'):
62 user = User.from_token(request.args.get('token'))
63 if user:
64 login_user(user)
65 return user
66 return None
67
68 usermgmt = Blueprint('usermgmt', __name__,
69 template_folder='templates',
70 static_folder='static',
71 static_url_path='/static/usermgmt')
72
73 @usermgmt.route('/login')
74 def login_form():
75 return render_template('login.html.j2')
76
77 @usermgmt.route('/login', methods=['POST'])
78 def do_login():
79 action = request.form.get('action')
80 if action == 'login':
81 user = User.from_name(request.form.get('user'))
82 if user and user.check_password(request.form.get('password')):
83 login_user(user, remember=request.form.get('remember'))
84 return redirect(url_for('youtube.index')) # XXX: don't hardcode routes of other blueprints!
85 flash('wrong username and/or password', 'error')
86 elif action == 'register':
87 flash("open registration currently closed. ask <i>girst</i> on irc://chat.freenode.net/#invidious if you want an account.", 'info')
88 elif action == 'logout':
89 logout_user()
90 return redirect(url_for('youtube.index')) # XXX: don't hardcode routes of other blueprints!
91 else:
92 flash('unsupported action', 'error')
93 return redirect(url_for('usermgmt.login_form'))
94
95 # NOTE: only register blueprint _after_ adding routes!
96 app.register_blueprint(usermgmt)
Imprint / Impressum