Code clean upr

This commit is contained in:
Matt Wright
2012-07-10 17:24:39 -04:00
parent 00860c29d0
commit 815c8695fc
6 changed files with 109 additions and 112 deletions
+2 -2
View File
@@ -15,7 +15,7 @@ from flask import current_app as app, request, url_for
from werkzeug.local import LocalProxy
from .exceptions import UserNotFoundError, ConfirmationError, TokenExpiredError
from .utils import generate_token, send_mail
from .utils import generate_token, send_mail, get_within_delta
from .signals import user_confirmed, confirm_instructions_sent
@@ -96,7 +96,7 @@ def requires_confirmation(user):
@should_confirm_email
def confirmation_token_is_expired(user):
"""Returns `True` if the user's confirmation token is expired."""
token_expires = datetime.utcnow() - _security.confirm_email_within
token_expires = datetime.utcnow() - get_within_delta('CONFIRM_EMAIL_WITHIN')
return user.confirmation_sent_at < token_expires
+61 -90
View File
@@ -9,8 +9,6 @@
:license: MIT, see LICENSE for more details.
"""
from datetime import timedelta
from flask import current_app, Blueprint
from flask.ext.login import AnonymousUser as AnonymousUserBase, \
UserMixin as BaseUserMixin, LoginManager, current_user
@@ -19,10 +17,11 @@ from flask.ext.principal import Principal, RoleNeed, UserNeed, Identity, \
from passlib.context import CryptContext
from werkzeug.datastructures import ImmutableList
from . import views, exceptions, utils
from . import views, exceptions
from .confirmable import confirmation_token_is_expired, requires_confirmation, \
reset_confirmation_token
from .decorators import login_required
from .utils import config_value as cv, get_config
#: Default Flask-Security configuration
@@ -30,7 +29,6 @@ _default_config = {
'URL_PREFIX': None,
'FLASH_MESSAGES': True,
'PASSWORD_HASH': 'plaintext',
'AUTH_PROVIDER': 'flask.ext.security::AuthenticationProvider',
'AUTH_URL': '/auth',
'LOGOUT_URL': '/logout',
'REGISTER_URL': '/register',
@@ -136,6 +134,57 @@ def _on_identity_loaded(sender, identity):
identity.user = current_user
def _get_login_manager(app):
lm = LoginManager()
lm.anonymous_user = AnonymousUser
lm.login_view = cv('LOGIN_VIEW', app=app)
lm.user_loader(_user_loader)
lm.token_loader(_token_loader)
lm.init_app(app)
return lm
def _get_principal(app):
p = Principal(app, use_sessions=False)
p.identity_loader(_identity_loader)
return p
def _get_pwd_context(app):
pw_hash = cv('PASSWORD_HASH', app=app)
return CryptContext(schemes=[pw_hash], default=pw_hash)
def _create_blueprint(config):
bp = Blueprint('flask_security', __name__, template_folder='templates')
bp.route(config['SECURITY_AUTH_URL'],
methods=['POST'],
endpoint='authenticate')(views.authenticate)
bp.route(config['SECURITY_LOGOUT_URL'],
endpoint='logout')(login_required(views.logout))
if config['SECURITY_REGISTERABLE']:
bp.route(config['SECURITY_REGISTER_URL'],
methods=['POST'],
endpoint='register')(views.register)
if config['SECURITY_RECOVERABLE']:
bp.route(config['SECURITY_FORGOT_URL'],
methods=['POST'],
endpoint='forgot')(views.forgot)
bp.route(config['SECURITY_RESET_URL'],
methods=['POST'],
endpoint='reset')(views.reset)
if config['SECURITY_CONFIRMABLE']:
bp.route(config['SECURITY_CONFIRM_URL'],
endpoint='confirm')(views.confirm)
return bp
class Security(object):
"""The :class:`Security` class initializes the Flask-Security extension.
@@ -151,10 +200,6 @@ class Security(object):
:param app: The application.
:param datastore: An instance of a user datastore.
:param confirmable: Set to `True` to enable email confirmation
:param registerable: Set to `False` to disable registration endpoints
:param recoverable: Set to `False` to disable password recovery
endpoints
"""
if app is None or datastore is None:
return
@@ -162,96 +207,22 @@ class Security(object):
for key, value in _default_config.items():
app.config.setdefault('SECURITY_' + key, value)
login_manager = LoginManager()
login_manager.anonymous_user = AnonymousUser
login_manager.login_view = utils.config_value(app, 'LOGIN_VIEW')
login_manager.user_loader(_user_loader)
login_manager.token_loader(_token_loader)
login_manager.init_app(app)
Provider = utils.get_class_from_string(app, 'AUTH_PROVIDER')
pw_hash = utils.config_value(app, 'PASSWORD_HASH')
self.login_manager = login_manager
self.pwd_context = CryptContext(schemes=[pw_hash], default=pw_hash)
self.auth_provider = Provider()
self.principal = Principal(app, use_sessions=False)
self.principal.identity_loader(_identity_loader)
self.datastore = datastore
self.auth_provider = AuthenticationProvider()
self.login_manager = _get_login_manager(app)
self.principal = _get_principal(app)
self.pwd_context = _get_pwd_context(app)
self.auth_url = utils.config_value(app, 'AUTH_URL')
self.logout_url = utils.config_value(app, 'LOGOUT_URL')
self.reset_url = utils.config_value(app, 'RESET_URL')
self.register_url = utils.config_value(app, 'REGISTER_URL')
self.confirm_url = utils.config_value(app, 'CONFIRM_URL')
self.forgot_url = utils.config_value(app, 'FORGOT_URL')
self.post_login_view = utils.config_value(app, 'POST_LOGIN_VIEW')
self.post_logout_view = utils.config_value(app, 'POST_LOGOUT_VIEW')
self.post_register_view = utils.config_value(app, 'POST_REGISTER_VIEW')
self.post_confirm_view = utils.config_value(app, 'POST_CONFIRM_VIEW')
self.post_forgot_view = utils.config_value(app, 'POST_FORGOT_VIEW')
self.reset_password_error_view = utils.config_value(app, 'RESET_PASSWORD_ERROR_VIEW')
self.default_roles = utils.config_value(app, "DEFAULT_ROLES")
self.login_without_confirmation = utils.config_value(app, 'LOGIN_WITHOUT_CONFIRMATION')
self.confirmable = utils.config_value(app, 'CONFIRMABLE')
self.registerable = utils.config_value(app, 'REGISTERABLE')
self.recoverable = utils.config_value(app, 'RECOVERABLE')
self.trackable = utils.config_value(app, 'TRACKABLE')
self.email_sender = utils.config_value(app, 'EMAIL_SENDER')
self.token_authentication_key = utils.config_value(app, 'TOKEN_AUTHENTICATION_KEY')
self.token_authentication_header = utils.config_value(app, 'TOKEN_AUTHENTICATION_HEADER')
self.confirm_email_within_text = utils.config_value(app, 'CONFIRM_EMAIL_WITHIN')
values = self.confirm_email_within_text.split()
self.confirm_email_within = timedelta(**{values[1]: int(values[0])})
self.reset_password_within_text = utils.config_value(app, 'RESET_PASSWORD_WITHIN')
values = self.reset_password_within_text.split()
self.reset_password_within = timedelta(**{values[1]: int(values[0])})
for key, value in get_config(app).items():
setattr(self, key.lower(), value)
identity_loaded.connect_via(app)(_on_identity_loaded)
bp = Blueprint('flask_security', __name__, template_folder='templates')
bp.route(self.auth_url,
methods=['POST'],
endpoint='authenticate')(views.authenticate)
bp.route(self.logout_url,
endpoint='logout')(login_required(views.logout))
self._setup_registerable(bp) if self.registerable else None
self._setup_recoverable(bp) if self.recoverable else None
self._setup_confirmable(bp) if self.confirmable else None
app.register_blueprint(bp,
url_prefix=utils.config_value(app, 'URL_PREFIX'))
app.register_blueprint(_create_blueprint(app.config),
url_prefix=cv('URL_PREFIX', app=app))
app.security = self
def _setup_registerable(self, bp):
bp.route(self.register_url,
methods=['POST'],
endpoint='register')(views.register)
def _setup_recoverable(self, bp):
bp.route(self.forgot_url,
methods=['POST'],
endpoint='forgot')(views.forgot)
bp.route(self.reset_url,
methods=['POST'],
endpoint='reset')(views.reset)
def _setup_confirmable(self, bp):
bp.route(self.confirm_url,
endpoint='confirm')(views.confirm)
class AuthenticationProvider(object):
"""The default authentication provider implementation."""
+2 -2
View File
@@ -18,7 +18,7 @@ from .exceptions import ResetPasswordError, UserNotFoundError, \
TokenExpiredError
from .signals import password_reset, password_reset_requested, \
confirm_instructions_sent
from .utils import generate_token, send_mail
from .utils import generate_token, send_mail, get_within_delta
# Convenient references
@@ -87,7 +87,7 @@ def password_reset_token_is_expired(user):
:param user: The user to examine
"""
token_expires = datetime.utcnow() - _security.reset_password_within
token_expires = datetime.utcnow() - get_within_delta('RESET_PASSWORD_WITHIN')
return user.reset_password_sent_at < token_expires
+42 -16
View File
@@ -12,7 +12,7 @@
import base64
import os
from contextlib import contextmanager
from importlib import import_module
from datetime import timedelta
from flask import url_for, flash, current_app, request, session, render_template
from flask.ext.login import make_secure_token
@@ -38,21 +38,10 @@ def do_flash(message, category=None):
:param message: The flash message
:param category: The flash message category
"""
if config_value(current_app, 'FLASH_MESSAGES'):
if config_value('FLASH_MESSAGES'):
flash(message, category)
def get_class_from_string(app, key):
"""Get a reference to a class by its configuration key name.
:param app: The application instance to work against
:param key: The configuration key to use
"""
cv = config_value(app, key).split('::')
cm = import_module(cv[0])
return getattr(cm, cv[1])
def get_url(endpoint_or_url):
"""Returns a URL if a valid endpoint is found. Otherwise, returns the
provided value.
@@ -85,14 +74,51 @@ def find_redirect(key):
return result
def config_value(app, key, default=None):
def get_config(app):
"""Conveniently get the security configuration for the specified
application without the annoying 'SECURITY_' prefix.
:param app: The application to inspect
"""
items = app.config.items()
prefix = 'SECURITY_'
def strip_prefix(tup):
return (tup[0].replace('SECURITY_', ''), tup[1])
return dict([strip_prefix(i) for i in items if i[0].startswith(prefix)])
def config_value(key, app=None, default=None):
"""Get a Flask-Security configuration value.
:param app: The application to retrieve the configuration from
:param key: The configuration key without the prefix `SECURITY_`
:param app: An optional specific application to inspect. Defaults to Flask's
`current_app`
:param default: An optional default value if the value is not set
"""
return app.config.get('SECURITY_' + key.upper(), default)
app = app or current_app
return get_config(app).get(key.upper(), default)
def get_within_delta(key, app=None):
"""Get a timedelta object from the application configuration following
the internal convention of::
<Amount of Units> <Type of Units>
Examples of valid config values::
5 days
10 minutes
:param key: The config value key without the 'SECURITY_' prefix
:param app: Optional application to inspect. Defaults to Flask's
`current_app`
"""
txt = config_value(key, app=app)
values = txt.split()
return timedelta(**{values[1]: int(values[0])})
def send_mail(subject, recipient, template, context=None):
+1 -1
View File
@@ -152,7 +152,7 @@ def confirm():
msg = 'You did not confirm your email within %s. ' \
'A new confirmation code has been sent to %s' % (
_security.confirm_email_within_text, e.user.email)
_security.confirm_email_within, e.user.email)
do_flash(msg, 'error')
return redirect('/') # TODO: Don't redirect to root
+1 -1
View File
@@ -213,7 +213,7 @@ class ConfirmableTests(SecurityTest):
self.assertIn(e, outbox[0].html)
self.assertNotIn(token, outbox[0].html)
expire_text = self.app.security.confirm_email_within_text
expire_text = self.app.security.confirm_email_within
text = 'You did not confirm your email within %s' % expire_text
self.assertIn(text, r.data)