mirror of
https://github.com/wassname/flask-security.git
synced 2026-06-27 16:10:11 +08:00
126 lines
3.9 KiB
Python
126 lines
3.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
flask.ext.security.recoverable
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Flask-Security recoverable module
|
|
|
|
:copyright: (c) 2012 by Matt Wright.
|
|
:license: MIT, see LICENSE for more details.
|
|
"""
|
|
|
|
from itsdangerous import BadSignature, SignatureExpired
|
|
from flask import current_app as app, request, url_for
|
|
from werkzeug.local import LocalProxy
|
|
|
|
from .exceptions import ResetPasswordError, UserNotFoundError
|
|
from .signals import password_reset, password_reset_requested, \
|
|
reset_instructions_sent
|
|
from .utils import send_mail, get_max_age, md5, get_message, encrypt_password
|
|
|
|
|
|
# Convenient references
|
|
_security = LocalProxy(lambda: app.extensions['security'])
|
|
|
|
_datastore = LocalProxy(lambda: _security.datastore)
|
|
|
|
|
|
def send_reset_password_instructions(user, reset_token):
|
|
"""Sends the reset password instructions email for the specified user.
|
|
|
|
:param user: The user to send the instructions to
|
|
"""
|
|
url = url_for('flask_security.reset_password',
|
|
token=reset_token)
|
|
|
|
reset_link = request.url_root[:-1] + url
|
|
|
|
send_mail('Password reset instructions',
|
|
user.email,
|
|
'reset_instructions',
|
|
dict(user=user, reset_link=reset_link))
|
|
|
|
reset_instructions_sent.send(dict(user=user, token=reset_token),
|
|
app=app._get_current_object())
|
|
|
|
return True
|
|
|
|
|
|
def send_password_reset_notice(user):
|
|
"""Sends the password reset notice email for the specified user.
|
|
|
|
:param user: The user to send the notice to
|
|
"""
|
|
send_mail('Your password has been reset',
|
|
user.email,
|
|
'reset_notice',
|
|
dict(user=user))
|
|
|
|
|
|
def generate_reset_password_token(user):
|
|
"""Generates a unique reset password token for the specified user.
|
|
|
|
:param user: The user to work with
|
|
"""
|
|
data = [user.id, md5(user.password)]
|
|
return _security.reset_serializer.dumps(data)
|
|
|
|
|
|
def reset_by_token(token, password):
|
|
"""Resets the password of the user given the specified token, email and
|
|
password. If the token is invalid a `ResetPasswordError` error will be
|
|
raised. If the token is expired a `TokenExpiredError` error will be raised.
|
|
|
|
:param token: The user's reset password token
|
|
:param email: The user's email address
|
|
:param password: The user's new password
|
|
"""
|
|
serializer = _security.reset_serializer
|
|
max_age = get_max_age('RESET_PASSWORD')
|
|
|
|
try:
|
|
data = serializer.loads(token, max_age=max_age)
|
|
user = _datastore.find_user(id=data[0])
|
|
|
|
if md5(user.password) != data[1]:
|
|
raise UserNotFoundError()
|
|
|
|
user.password = encrypt_password(password,
|
|
salt=_security.password_salt,
|
|
use_hmac=_security.password_hmac)
|
|
print user.password
|
|
_datastore._save_model(user)
|
|
|
|
send_password_reset_notice(user)
|
|
|
|
password_reset.send(user, app=app._get_current_object())
|
|
|
|
return user
|
|
|
|
except SignatureExpired:
|
|
sig_okay, data = serializer.loads_unsafe(token)
|
|
raise ResetPasswordError('Password reset token expired',
|
|
user=_datastore.find_user(id=data[0]))
|
|
|
|
except BadSignature:
|
|
raise ResetPasswordError(get_message('INVALID_RESET_PASSWORD_TOKEN'))
|
|
|
|
except UserNotFoundError:
|
|
raise ResetPasswordError(get_message('INVALID_RESET_PASSWORD_TOKEN'))
|
|
|
|
|
|
def reset_password_reset_token(user):
|
|
"""Resets the specified user's reset password token and sends the user
|
|
an email with instructions explaining next steps.
|
|
|
|
:param user: The user to work with
|
|
"""
|
|
token = generate_reset_password_token(user)
|
|
|
|
send_reset_password_instructions(user, token)
|
|
|
|
password_reset_requested.send(dict(user=user, token=token),
|
|
app=app._get_current_object())
|
|
|
|
return token
|