]>
git.gir.st - subscriptionfeed.git/blob - app/common/user.py
1 from werkzeug
.security
import generate_password_hash
, check_password_hash
5 from flask_login
import LoginManager
, UserMixin
, login_user
, logout_user
, login_required
, current_user
6 from flask
import Blueprint
, flash
, redirect
, render_template
, url_for
, request
8 class User(UserMixin
): # TODO: to common
9 def __init__(self
, id, name
, passwd
, token
):
16 def set_password(self
, passwd
):
17 self
.passwd
= generate_password_hash(passwd
)
18 with sqlite3
.connect(cf
['global']['database']) as conn
:
20 c
.execute("UPDATE users SET password = ? where id = ?", (self
.passwd
, self
.id,))
21 def check_password(self
, passwd
):
22 return check_password_hash(self
.passwd
, passwd
)
24 def from_id(self
, id):
25 with sqlite3
.connect(cf
['global']['database']) as conn
:
27 c
.execute("SELECT name,password,token FROM users WHERE id = ?", (id,))
29 name
, passwd
, token
= c
.fetchone()
30 except: return None # todo: ugly
31 return User(id, name
, passwd
, token
)
33 def from_name(self
, name
):
34 with sqlite3
.connect(cf
['global']['database']) as conn
:
36 c
.execute("SELECT id,password,token FROM users WHERE name=?", (name
,))
38 id, passwd
, token
= c
.fetchone()
39 except: return None # todo: ugly
40 return User(id, name
, passwd
, token
)
42 def from_token(self
, login_token
):
43 # Note: this function reads the revocable token, not the internal one!
44 with sqlite3
.connect(cf
['global']['database']) as conn
:
47 SELECT id, name, password, users.token
48 FROM users JOIN user_tokens ON users.id = user_tokens.user_id
49 WHERE user_tokens.token = ?
52 id, name
, passwd
, token
= c
.fetchone()
53 return User(id, name
, passwd
, token
)
59 login
= LoginManager()
60 login
.login_view
= 'usermgmt.login_form'
65 # in the future tokens will be invalidable by users. -> https://flask-login.readthedocs.io/en/latest/#alternative-tokens
66 return User
.from_id(id)
69 def querytoken_auth(request
):
70 if request
.args
.get('token'):
71 user
= User
.from_token(request
.args
.get('token'))
77 usermgmt
= Blueprint('usermgmt', __name__
,
78 template_folder
='templates',
79 static_folder
='static',
80 static_url_path
='/static/usermgmt')
82 @usermgmt.route('/login')
84 return render_template('login.html.j2')
86 @usermgmt.route('/login', methods
=['POST'])
88 action
= request
.form
.get('action')
90 user
= User
.from_name(request
.form
.get('user'))
91 if user
and user
.check_password(request
.form
.get('password')):
92 login_user(user
, remember
=request
.form
.get('remember'))
93 return redirect(request
.args
.get('next','/')) # xxx: non-exploitable open redirect!
94 flash('wrong username and/or password', 'error')
95 elif action
== 'register':
96 flash("open registration currently closed. ask <i>girst</i> on irc://chat.freenode.net/#invidious if you want an account.", 'info')
97 elif action
== 'logout':
99 return redirect(request
.args
.get('next','/')) # xxx: non-exploitable open redirect!
101 flash('unsupported action', 'error')
102 return redirect(url_for('usermgmt.login_form'))
104 @usermgmt.route('/manage/account')
106 def account_manager():
107 with sqlite3
.connect(cf
['global']['database']) as conn
:
113 """, (current_user
.id,))
114 result
= c
.fetchone()
116 (login_token
,) = result
119 return render_template('account_mgmt.html.j2', login_token
=login_token
)
121 @usermgmt.route('/manage/account', methods
=['POST'])
123 def manage_account():
124 token
= current_user
.token
125 action
= request
.form
.get('action')
126 if action
== 'chpwd':
127 if not current_user
.check_password(request
.form
.get('oldpasswd')):
128 flash('current password incorrect.', 'error')
130 current_user
.set_password(request
.form
.get('newpasswd'))
131 flash('password updated.', 'info')
132 if action
== 'chtok':
133 with sqlite3
.connect(cf
['global']['database']) as conn
:
134 new_token
= secrets
.token_urlsafe(16)
137 INSERT OR REPLACE INTO user_tokens (user_id, token)
139 """, (current_user
.id, new_token
))
140 flash('new token generated.', 'info')
142 flash('unsupported action', 'error')
144 return redirect(url_for('usermgmt.account_manager'))
146 # NOTE: only register blueprint _after_ adding routes!
147 app
.register_blueprint(usermgmt
)