]> git.gir.st - subscriptionfeed.git/blob - app/common/user.py
add &t= to redirect-to-watch
[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, login_required, current_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 with sqlite3.connect(cf['global']['database']) as conn:
18 c = conn.cursor()
19 c.execute("UPDATE users SET password = ? where id = ?", (self.passwd, self.id,))
20 def check_password(self, passwd):
21 return check_password_hash(self.passwd, passwd)
22 @classmethod
23 def from_id(self, id):
24 with sqlite3.connect(cf['global']['database']) as conn:
25 c = conn.cursor()
26 c.execute("SELECT name,password,token FROM users WHERE id = ?", (id,))
27 try:
28 name, passwd, token = c.fetchone()
29 except: return None # todo: ugly
30 return User(id, name, passwd, token)
31 @classmethod
32 def from_name(self, name):
33 with sqlite3.connect(cf['global']['database']) as conn:
34 c = conn.cursor()
35 c.execute("SELECT id,password,token FROM users WHERE name=?", (name,))
36 try:
37 id, passwd, token = c.fetchone()
38 except: return None # todo: ugly
39 return User(id, name, passwd, token)
40 @classmethod
41 def from_token(self, token):
42 with sqlite3.connect(cf['global']['database']) as conn:
43 c = conn.cursor()
44 c.execute("SELECT id,name,password FROM users WHERE token=?", (token,))
45 try:
46 id, name, passwd, = c.fetchone()
47 except: return None # todo: ugly
48 return User(id, name, passwd, token)
49
50
51 def init_login(app):
52 login = LoginManager()
53 login.login_view = 'usermgmt.login_form'
54 login.init_app(app)
55
56 @login.user_loader
57 def load_user(token):
58 # in the future tokens will be invalidable by users. -> https://flask-login.readthedocs.io/en/latest/#alternative-tokens
59 return User.from_token(token)
60
61 @login.request_loader
62 def querytoken_auth(request):
63 if request.args.get('token'):
64 user = User.from_token(request.args.get('token'))
65 if user:
66 login_user(user)
67 return user
68 return None
69
70 usermgmt = Blueprint('usermgmt', __name__,
71 template_folder='templates',
72 static_folder='static',
73 static_url_path='/static/usermgmt')
74
75 @usermgmt.route('/login')
76 def login_form():
77 return render_template('login.html.j2')
78
79 @usermgmt.route('/login', methods=['POST'])
80 def do_login():
81 action = request.form.get('action')
82 if action == 'login':
83 user = User.from_name(request.form.get('user'))
84 if user and user.check_password(request.form.get('password')):
85 login_user(user, remember=request.form.get('remember'))
86 return redirect(request.args.get('next','/')) # xxx: non-exploitable open redirect!
87 flash('wrong username and/or password', 'error')
88 elif action == 'register':
89 flash("open registration currently closed. ask <i>girst</i> on irc://chat.freenode.net/#invidious if you want an account.", 'info')
90 elif action == 'logout':
91 logout_user()
92 return redirect(request.args.get('next','/')) # xxx: non-exploitable open redirect!
93 else:
94 flash('unsupported action', 'error')
95 return redirect(url_for('usermgmt.login_form'))
96
97 @usermgmt.route('/manage/account')
98 def account_manager():
99 return render_template('account_mgmt.html.j2')
100
101 @usermgmt.route('/manage/account', methods=['POST'])
102 @login_required
103 def manage_account():
104 token = current_user.token
105 action = request.form.get('action')
106 if action == 'chpwd':
107 if not current_user.check_password(request.form.get('oldpasswd')):
108 flash('current password incorrect.', 'error')
109 else:
110 current_user.set_password(request.form.get('newpasswd'))
111 flash('password updated.', 'info')
112 else:
113 flash('unsupported action', 'error')
114
115 return redirect(url_for('usermgmt.account_manager'))
116
117 # NOTE: only register blueprint _after_ adding routes!
118 app.register_blueprint(usermgmt)
Imprint / Impressum