Code cleanup

This commit is contained in:
Matt Wright
2012-08-17 14:46:30 -04:00
parent a39f46854e
commit 2bd19f999d
8 changed files with 117 additions and 168 deletions
+1 -2
View File
@@ -20,6 +20,5 @@ from .decorators import auth_token_required, http_auth_required, \
from .forms import ForgotPasswordForm, LoginForm, RegisterForm, \
ResetPasswordForm, PasswordlessLoginForm
from .signals import confirm_instructions_sent, password_reset, \
password_reset_requested, reset_instructions_sent, user_confirmed, \
user_registered
reset_password_instructions_sent, user_confirmed, user_registered
from .utils import login_user, logout_user, url_for_security
+4 -17
View File
@@ -26,12 +26,13 @@ _security = LocalProxy(lambda: app.extensions['security'])
_datastore = LocalProxy(lambda: _security.datastore)
def send_confirmation_instructions(user, token):
def send_confirmation_instructions(user):
"""Sends the confirmation instructions email for the specified user.
:param user: The user to send the instructions to
:param token: The confirmation token
"""
token = generate_confirmation_token(user)
url = url_for_security('confirm_email', token=token)
confirmation_link = request.url_root[:-1] + url
@@ -43,6 +44,8 @@ def send_confirmation_instructions(user, token):
confirm_instructions_sent.send(user, app=app._get_current_object())
return token
def generate_confirmation_token(user):
"""Generates a unique confirmation token for the specified user.
@@ -92,19 +95,3 @@ def confirm_by_token(token):
except BadSignature:
raise ConfirmationError(get_message('INVALID_CONFIRMATION_TOKEN')[0])
def reset_confirmation_token(user):
"""Resets the specified user's confirmation token and sends the user
an email with instructions explaining next steps.
:param user: The user to work with
"""
token = generate_confirmation_token(user)
user.confirmed_at = None
_datastore._save_model(user)
send_confirmation_instructions(user, token)
return token
-1
View File
@@ -49,7 +49,6 @@ _default_config = {
'POST_REGISTER_VIEW': None,
'POST_CONFIRM_VIEW': None,
'POST_RESET_VIEW': None,
'RESET_PASSWORD_ERROR_VIEW': None,
'UNAUTHORIZED_VIEW': None,
'DEFAULT_ROLES': [],
'CONFIRMABLE': False,
+11 -11
View File
@@ -16,7 +16,7 @@ from werkzeug.local import LocalProxy
from .exceptions import PasswordlessLoginError
from .signals import login_instructions_sent
from .utils import send_mail, md5, get_max_age, login_user, get_message, \
url_for_security
url_for_security, get_url
# Convenient references
@@ -47,6 +47,7 @@ def send_login_instructions(user, next):
def generate_login_token(user, next):
next = next or get_url(_security.post_login_view)
data = [user.id, md5(user.password), next]
return _security.login_serializer.dumps(data)
@@ -56,20 +57,19 @@ def login_by_token(token):
max_age = get_max_age('LOGIN')
try:
data = serializer.loads(token, max_age=max_age)
user = _datastore.find_user(id=data[0])
user_id, pw, next = serializer.loads(token, max_age=max_age)
user = _datastore.find_user(id=user_id)
login_user(user, True)
return user, data[2]
return user, next
except SignatureExpired:
sig_okay, data = serializer.loads_unsafe(token)
user_id, pw, next = data
user = _datastore.find_user(id=data[0])
msg = get_message('LOGIN_EXPIRED',
within=_security.login_within,
email=user.email)[0]
raise PasswordlessLoginError(msg, user=user, next=data[2])
within = _security.login_within
msg = get_message('LOGIN_EXPIRED', within=within, email=user.email)
raise PasswordlessLoginError(msg[0], user=user, next=next)
except BadSignature:
raise PasswordlessLoginError(get_message('INVALID_LOGIN_TOKEN')[0])
msg = get_message('INVALID_LOGIN_TOKEN')
raise PasswordlessLoginError(msg[0])
+7 -23
View File
@@ -13,9 +13,8 @@ from itsdangerous import BadSignature, SignatureExpired
from flask import current_app as app, request
from werkzeug.local import LocalProxy
from .exceptions import ResetPasswordError, UserNotFoundError
from .signals import password_reset, password_reset_requested, \
reset_instructions_sent
from .exceptions import ResetPasswordError
from .signals import password_reset, reset_password_instructions_sent
from .utils import send_mail, get_max_age, md5, get_message, encrypt_password, \
url_for_security
@@ -26,12 +25,13 @@ _security = LocalProxy(lambda: app.extensions['security'])
_datastore = LocalProxy(lambda: _security.datastore)
def send_reset_password_instructions(user, reset_token):
def send_reset_password_instructions(user):
"""Sends the reset password instructions email for the specified user.
:param user: The user to send the instructions to
"""
url = url_for_security('reset_password', token=reset_token)
token = generate_reset_password_token(user)
url = url_for_security('reset_password', token=token)
reset_link = request.url_root[:-1] + url
@@ -40,8 +40,8 @@ def send_reset_password_instructions(user, reset_token):
'reset_instructions',
dict(user=user, reset_link=reset_link))
reset_instructions_sent.send(dict(user=user, token=reset_token),
app=app._get_current_object())
reset_password_instructions_sent.send(dict(user=user, token=token),
app=app._get_current_object())
def send_password_reset_notice(user):
@@ -99,19 +99,3 @@ def reset_by_token(token, password):
except BadSignature:
raise ResetPasswordError(get_message('INVALID_RESET_PASSWORD_TOKEN')[0])
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
+1 -3
View File
@@ -24,6 +24,4 @@ login_instructions_sent = signals.signal("login-instructions-sent")
password_reset = signals.signal("password-reset")
password_reset_requested = signals.signal("password-reset-requested")
reset_instructions_sent = signals.signal("reset-instructions-sent")
reset_password_instructions_sent = signals.signal("password-reset-instructions-sent")
+3 -3
View File
@@ -24,7 +24,7 @@ from flask.ext.principal import Identity, AnonymousIdentity, identity_changed
from werkzeug.local import LocalProxy
from .core import current_user
from .signals import user_registered, password_reset_requested, \
from .signals import user_registered, reset_password_instructions_sent, \
login_instructions_sent
@@ -292,9 +292,9 @@ def capture_reset_password_requests(reset_password_sent_at=None):
def _on(request, app):
reset_requests.append(request)
password_reset_requested.connect(_on)
reset_password_instructions_sent.connect(_on)
try:
yield reset_requests
finally:
password_reset_requested.disconnect(_on)
reset_password_instructions_sent.disconnect(_on)
+90 -108
View File
@@ -14,7 +14,8 @@ from flask import current_app as app, redirect, request, \
from werkzeug.datastructures import MultiDict
from werkzeug.local import LocalProxy
from flask_security.confirmable import confirm_by_token, reset_confirmation_token
from flask_security.confirmable import confirm_by_token, \
send_confirmation_instructions
from flask_security.decorators import login_required
from flask_security.exceptions import ConfirmationError, BadCredentialsError, \
ResetPasswordError, PasswordlessLoginError
@@ -22,11 +23,11 @@ from flask_security.forms import LoginForm, RegisterForm, ForgotPasswordForm, \
ResetPasswordForm, SendConfirmationForm, PasswordlessLoginForm
from flask_security.passwordless import send_login_instructions, login_by_token
from flask_security.recoverable import reset_by_token, \
reset_password_reset_token
send_reset_password_instructions
from flask_security.signals import user_registered
from flask_security.utils import get_url, get_post_login_redirect, do_flash, \
get_message, config_value, login_user, logout_user, url_for_security, \
anonymous_user_required
get_message, config_value, login_user, logout_user, \
anonymous_user_required, url_for_security as url_for
# Convenient references
@@ -64,55 +65,61 @@ def _json_auth_error(msg):
return resp
def _commit_data(response=None):
def _commit(response=None):
_datastore._commit()
return response
def _ctx(endpoint):
return _security._run_ctx_processor(endpoint)
def authenticate():
"""View function which handles an authentication request."""
confirm_url = None
form_data = MultiDict(request.json) if request.json else request.form
form = LoginForm(form_data)
after_this_request(_commit_data)
form = LoginForm(request.form)
user, msg, confirm_url = None, None, None
if request.json:
form = LoginForm(MultiDict(request.json))
try:
user = _security.auth_provider.authenticate(form)
if login_user(user, remember=form.remember.data):
if request.json:
return _json_auth_ok(user)
return redirect(get_post_login_redirect())
raise BadCredentialsError(get_message('DISABLED_ACCOUNT')[0])
except ConfirmationError, e:
msg = str(e)
confirm_url = url_for_security('send_confirmation', email=e.user.email)
confirm_url = url_for('send_confirmation', email=e.user.email)
except BadCredentialsError, e:
msg = str(e)
if user:
if login_user(user, remember=form.remember.data):
after_this_request(_commit)
if request.json:
return _json_auth_ok(user)
return redirect(get_post_login_redirect())
msg = get_message('DISABLED_ACCOUNT')[0]
_logger.debug('Unsuccessful authentication attempt: %s' % msg)
if request.json:
return _json_auth_error(msg)
do_flash(msg, 'error')
return redirect(confirm_url or url_for_security('login'))
return redirect(confirm_url or url_for('login'))
@anonymous_user_required
def login():
"""View function for login view"""
form = PasswordlessLoginForm() if _security.passwordless else LoginForm()
template = 'send_login' if _security.passwordless else 'login'
return render_template('security/%s.html' % template, login_form=form,
**_security._run_ctx_processor('login'))
tmp, form = '', LoginForm
if _security.passwordless:
tmp, form = 'send_', PasswordlessLoginForm
return render_template('security/%slogin.html' % tmp,
login_form=form(),
**_ctx('login'))
@login_required
@@ -120,11 +127,10 @@ def logout():
"""View function which handles a logout request."""
logout_user()
_logger.debug('User logged out')
return redirect(request.args.get('next', None) or
get_url(_security.post_logout_view))
next_url = request.args.get('next', None)
post_logout_url = get_url(_security.post_logout_view)
return redirect(next_url or post_logout_url)
@anonymous_user_required
@@ -133,51 +139,51 @@ def register():
form = RegisterForm(csrf_enabled=not app.testing)
if form.validate_on_submit():
# Create user
u = _datastore.create_user(**form.to_dict())
if not form.validate_on_submit():
return render_template('security/register.html',
register_user_form=form,
**_ctx('register'))
# Save the user so the ID is created
_commit_data()
token = None
user = _datastore.create_user(**form.to_dict())
_commit()
# Send confirmation instructions if necessary
t = reset_confirmation_token(u) if _security.confirmable else None
if _security.confirmable:
token = send_confirmation_instructions(user)
do_flash(*get_message('CONFIRM_REGISTRATION', email=user.email))
data = dict(user=u, confirm_token=t)
user_registered.send(data, app=app._get_current_object())
user_registered.send(dict(user=user, confirm_token=token),
app=app._get_current_object())
_logger.debug('User %s registered' % u)
_logger.debug('User %s registered' % user)
# Login the user if allowed
if not _security.confirmable or _security.login_without_confirmation:
login_user(u)
if not _security.confirmable or _security.login_without_confirmation:
after_this_request(_commit)
login_user(user)
if _security.confirmable:
do_flash(*get_message('CONFIRM_REGISTRATION', email=u.email))
post_register_url = get_url(_security.post_register_view)
post_login_url = get_url(_security.post_login_view)
return redirect(get_url(_security.post_register_view) or
get_url(_security.post_login_view))
return render_template('security/register.html',
register_user_form=form,
**_security._run_ctx_processor('register'))
return redirect(post_register_url or post_login_url)
@anonymous_user_required
def send_login():
"""View function that sends login instructions for passwordless login"""
form = PasswordlessLoginForm()
form = PasswordlessLoginForm()
user = _datastore.find_user(**form.to_dict())
if user.is_active():
send_login_instructions(user, form.next.data)
do_flash(*get_message('LOGIN_EMAIL_SENT', email=user.email))
msg = get_message('LOGIN_EMAIL_SENT', email=user.email)
else:
do_flash(*get_message('DISABLED_ACCOUNT'))
msg = get_message('DISABLED_ACCOUNT')
return render_template('security/send_login.html', login_form=form,
**_security._run_ctx_processor('send_login'))
do_flash(*msg)
return render_template('security/send_login.html',
login_form=form,
**_ctx('send_login'))
@anonymous_user_required
@@ -186,18 +192,14 @@ def token_login(token):
try:
user, next = login_by_token(token)
except PasswordlessLoginError, e:
if e.user:
send_login_instructions(e.user, e.next)
do_flash(str(e), 'error')
return redirect(request.referrer or url_for_security('login'))
return redirect(request.referrer or url_for('login'))
do_flash(*get_message('PASSWORDLESS_LOGIN_SUCCESSFUL'))
return redirect(next or get_url(_security.post_login_view))
return redirect(next)
@anonymous_user_required
@@ -208,45 +210,35 @@ def send_confirmation():
if form.validate_on_submit():
user = _datastore.find_user(**form.to_dict())
reset_confirmation_token(user)
send_confirmation_instructions(user)
_logger.debug('%s request confirmation instructions' % user)
do_flash(*get_message('CONFIRMATION_REQUEST', email=user.email))
return render_template('security/send_confirmation.html',
reset_confirmation_form=form,
**_security._run_ctx_processor('send_confirmation'))
**_ctx('send_confirmation'))
def confirm_email(token):
"""View function which handles a email confirmation request."""
after_this_request(_commit_data)
after_this_request(_commit)
try:
user = confirm_by_token(token)
_logger.debug('%s confirmed their email' % user)
except ConfirmationError, e:
msg = (str(e), 'error')
_logger.debug('Confirmation error: ' + msg[0])
_logger.debug('Confirmation error: %s' % e)
if e.user:
reset_confirmation_token(e.user)
do_flash(*msg)
return redirect(get_url(_security.confirm_error_view) or
url_for_security('send_confirmation'))
send_confirmation_instructions(e.user)
do_flash(str(e), 'error')
confirm_error_url = get_url(_security.confirm_error_view)
return redirect(confirm_error_url or url_for('send_confirmation'))
_logger.debug('%s confirmed their email' % user)
do_flash(*get_message('EMAIL_CONFIRMED'))
login_user(user, True)
return redirect(get_url(_security.post_confirm_view) or
get_url(_security.post_login_view))
post_confirm_url = get_url(_security.post_confirm_view)
post_login_url = get_url(_security.post_login_view)
return redirect(post_confirm_url or post_login_url)
@anonymous_user_required
@@ -257,11 +249,8 @@ def forgot_password():
if form.validate_on_submit():
user = _datastore.find_user(**form.to_dict())
reset_password_reset_token(user)
send_reset_password_instructions(user)
_logger.debug('%s requested to reset their password' % user)
do_flash(*get_message('PASSWORD_RESET_REQUEST', email=user.email))
if _security.post_forgot_view:
@@ -272,49 +261,42 @@ def forgot_password():
return render_template('security/forgot_password.html',
forgot_password_form=form,
**_security._run_ctx_processor('forgot_password'))
**_ctx('forgot_password'))
@anonymous_user_required
def reset_password(token):
"""View function that handles a reset password request."""
next, msg = None, None
form = ResetPasswordForm(csrf_enabled=not app.testing)
if form.validate_on_submit():
try:
user = reset_by_token(token=token, **form.to_dict())
_logger.debug('%s reset their password' % user)
do_flash(*get_message('PASSWORD_RESET'))
login_user(user)
return redirect(get_url(_security.post_reset_view) or
get_url(_security.post_login_view))
msg = get_message('PASSWORD_RESET')
next = (get_url(_security.post_reset_view) or
get_url(_security.post_login_view))
except ResetPasswordError, e:
msg = (str(e), 'error')
_logger.debug('Password reset error: ' + msg[0])
if e.user:
reset_password_reset_token(e.user)
send_reset_password_instructions(e.user)
msg = get_message('PASSWORD_RESET_EXPIRED',
within=_security.reset_password_within,
email=e.user.email)
_logger.debug('Password reset error: ' + msg[0])
do_flash(*msg)
do_flash(*msg)
if _security.reset_password_error_view:
return redirect(get_url(_security.reset_password_error_view))
if next:
login_user(user)
_logger.debug('%s reset their password' % user)
return redirect(next)
return render_template('security/reset_password.html',
reset_password_form=form,
reset_password_token=token,
**_security._run_ctx_processor('reset_password'))
**_ctx('reset_password'))
def create_blueprint(app, name, import_name, **kwargs):