1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
from typing import Dict
from szurubooru import config, errors, rest
from szurubooru.func import auth, mailer, users, versions
from hashlib import md5
MAIL_SUBJECT = 'Password reset for {name}'
MAIL_BODY = (
'You (or someone else) requested to reset your password on {name}.\n'
'If you wish to proceed, click this link: {url}\n'
'Otherwise, please ignore this email.')
@rest.routes.get('/password-reset/(?P<user_name>[^/]+)/?')
def start_password_reset(
ctx: rest.Context, params: Dict[str, str]) -> rest.Response:
user_name = params['user_name']
user = users.get_user_by_name_or_email(user_name)
if not user.email:
raise errors.ValidationError(
'User %r hasn\'t supplied email. Cannot reset password.' % (
user_name))
token = auth.generate_authentication_token(user)
if 'HTTP_ORIGIN' in ctx.env:
url = ctx.env['HTTP_ORIGIN'].rstrip('/')
else:
url = ''
url += '/password-reset/%s:%s' % (user.name, token)
mailer.send_mail(
'noreply@%s' % config.config['name'],
user.email,
MAIL_SUBJECT.format(name=config.config['name']),
MAIL_BODY.format(name=config.config['name'], url=url))
return {}
def _hash(token: str) -> str:
return md5(token.encode('utf-8')).hexdigest()
@rest.routes.post('/password-reset/(?P<user_name>[^/]+)/?')
def finish_password_reset(
ctx: rest.Context, params: Dict[str, str]) -> rest.Response:
user_name = params['user_name']
user = users.get_user_by_name_or_email(user_name)
good_token = auth.generate_authentication_token(user)
token = ctx.get_param_as_string('token')
if _hash(token) != _hash(good_token):
raise errors.ValidationError('Invalid password reset token.')
new_password = users.reset_user_password(user)
versions.bump_version(user)
ctx.session.commit()
return {'password': new_password}
|