Use a stateful object instead of arbitrary assignment of extension on app object

This commit is contained in:
Matt Wright
2012-07-16 19:07:19 -04:00
parent 54da59f003
commit f170cb434c
12 changed files with 66 additions and 38 deletions
+3 -3
View File
@@ -149,8 +149,8 @@ def create_sqlalchemy_app(auth_config=None, register_blueprint=True):
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='dynamic'))
Security(app, SQLAlchemyUserDatastore(db, User, Role),
register_blueprint=register_blueprint)
app.security = Security(app, SQLAlchemyUserDatastore(db, User, Role),
register_blueprint=register_blueprint)
if not register_blueprint:
from example import security
@@ -192,7 +192,7 @@ def create_mongoengine_app(auth_config=None):
authentication_token = db.StringField(max_length=255)
roles = db.ListField(db.ReferenceField(Role), default=[])
Security(app, MongoEngineUserDatastore(db, User, Role))
app.security = Security(app, MongoEngineUserDatastore(db, User, Role))
@app.before_first_request
def before_first_request():
+2 -2
View File
@@ -21,9 +21,9 @@ from .signals import user_confirmed, confirm_instructions_sent
# Convenient references
_security = LocalProxy(lambda: app.security)
_security = LocalProxy(lambda: app.extensions['security'])
_datastore = LocalProxy(lambda: app.security.datastore)
_datastore = LocalProxy(lambda: _security.datastore)
def send_confirmation_instructions(user, token):
+38 -20
View File
@@ -17,11 +17,15 @@ from flask.ext.principal import Principal, RoleNeed, UserNeed, Identity, \
identity_loaded
from passlib.context import CryptContext
from werkzeug.datastructures import ImmutableList
from werkzeug.local import LocalProxy
from . import views, exceptions
from .confirmable import requires_confirmation
from .utils import config_value as cv, get_config
# Convenient references
_security = LocalProxy(lambda: current_app.extensions['security'])
#: Default Flask-Security configuration
_default_config = {
@@ -75,7 +79,7 @@ _default_flash_messages = {
def _user_loader(user_id):
try:
return current_app.security.datastore.with_id(user_id)
return _security.datastore.with_id(user_id)
except Exception, e:
current_app.logger.error('Error getting user: %s' % e)
return None
@@ -83,7 +87,7 @@ def _user_loader(user_id):
def _token_loader(token):
try:
return current_app.security.datastore.find_user(remember_token=token)
return _security.datastore.find_user(remember_token=token)
except Exception, e:
current_app.logger.error('Error getting user: %s' % e)
return None
@@ -189,6 +193,13 @@ class AnonymousUser(AnonymousUserBase):
return False
class _SecurityState(object):
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key.lower(), value)
class Security(object):
"""The :class:`Security` class initializes the Flask-Security extension.
@@ -196,7 +207,11 @@ class Security(object):
:param datastore: An instance of a user datastore.
"""
def __init__(self, app=None, datastore=None, **kwargs):
self.init_app(app, datastore, **kwargs)
self.app = app
self.datastore = datastore
if app is not None and datastore is not None:
self.init_app(app, datastore, **kwargs)
def init_app(self, app, datastore, register_blueprint=True):
"""Initializes the Flask-Security extension for the specified
@@ -205,8 +220,6 @@ class Security(object):
:param app: The application.
:param datastore: An instance of a user datastore.
"""
if app is None or datastore is None:
return
for key, value in _default_config.items():
app.config.setdefault('SECURITY_' + key, value)
@@ -214,18 +227,6 @@ class Security(object):
for key, value in _default_flash_messages.items():
app.config.setdefault('SECURITY_MSG_' + key, value)
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.reset_serializer = _get_reset_serializer(app)
self.confirm_serializer = _get_confirm_serializer(app)
self.token_auth_serializer = _get_token_auth_serializer(app)
for key, value in get_config(app).items():
setattr(self, key.lower(), value)
identity_loaded.connect_via(app)(_on_identity_loaded)
if register_blueprint:
@@ -234,13 +235,30 @@ class Security(object):
url_prefix=cv('URL_PREFIX', app=app))
app.register_blueprint(bp)
app.security = self
if not hasattr(app, 'extensions'):
app.extensions = {}
kwargs = {}
for key, value in get_config(app).items():
kwargs[key.lower()] = value
app.extensions['security'] = _SecurityState(
app=app,
datastore=datastore,
auth_provider=AuthenticationProvider(),
login_manager=_get_login_manager(app),
principal=_get_principal(app),
pwd_context=_get_pwd_context(app),
reset_serializer=_get_reset_serializer(app),
confirm_serializer=_get_confirm_serializer(app),
token_auth_serializer=_get_token_auth_serializer(app),
**kwargs)
class AuthenticationProvider(object):
"""The default authentication provider implementation."""
def _get_user(self, username_or_email):
datastore = current_app.security.datastore
datastore = _security.datastore
try:
return datastore.find_user(email=username_or_email)
@@ -286,7 +304,7 @@ class AuthenticationProvider(object):
raise exceptions.BadCredentialsError('Account requires confirmation')
# compare passwords
if current_app.security.pwd_context.verify(password, user.password):
if _security.pwd_context.verify(password, user.password):
return user
# bad match
+7 -2
View File
@@ -10,9 +10,13 @@
"""
from flask import current_app
from werkzeug.local import LocalProxy
from . import exceptions, utils
# Convenient references
_security = LocalProxy(lambda: current_app.extensions['security'])
class UserDatastore(object):
"""Abstracted user datastore. Always extend this class and implement the
@@ -88,7 +92,7 @@ class UserDatastore(object):
password = kwargs.get('password', None)
kwargs.setdefault('active', True)
kwargs.setdefault('roles', current_app.security.default_roles)
kwargs.setdefault('roles', _security.default_roles)
if email is None:
raise exceptions.UserCreationError('Missing email argument')
@@ -105,8 +109,9 @@ class UserDatastore(object):
kwargs['roles'] = roles
pwd_context = current_app.security.pwd_context
pwd_context = _security.pwd_context
pw = kwargs['password']
if not pwd_context.identify(pw):
kwargs['password'] = pwd_context.encrypt(pw)
+1 -1
View File
@@ -21,7 +21,7 @@ from .exceptions import UserNotFoundError
# Convenient references
_security = LocalProxy(lambda: current_app.security)
_security = LocalProxy(lambda: current_app.extensions['security'])
_logger = LocalProxy(lambda: current_app.logger)
+1 -1
View File
@@ -18,7 +18,7 @@ from .exceptions import UserNotFoundError
# Convenient reference
_datastore = LocalProxy(lambda: app.security.datastore)
_datastore = LocalProxy(lambda: app.extensions['security'].datastore)
def valid_user_email(form, field):
+2 -2
View File
@@ -20,9 +20,9 @@ from .utils import send_mail, get_max_age, md5, get_message
# Convenient references
_security = LocalProxy(lambda: app.security)
_security = LocalProxy(lambda: app.extensions['security'])
_datastore = LocalProxy(lambda: app.security.datastore)
_datastore = LocalProxy(lambda: _security.datastore)
def send_reset_password_instructions(user, reset_token):
+1 -1
View File
@@ -15,7 +15,7 @@ from werkzeug.local import LocalProxy
from flask_security import views
_datastore = LocalProxy(lambda: current_app.security.datastore)
_datastore = LocalProxy(lambda: current_app.extensions['security'].datastore)
def pprint(obj):
+2 -2
View File
@@ -16,9 +16,9 @@ from .utils import md5
# Convenient references
_security = LocalProxy(lambda: app.security)
_security = LocalProxy(lambda: app.extensions['security'])
_datastore = LocalProxy(lambda: app.security.datastore)
_datastore = LocalProxy(lambda: _security.datastore)
def generate_authentication_token(user):
+6 -1
View File
@@ -17,10 +17,15 @@ from datetime import datetime, timedelta
from flask import url_for, flash, current_app, request, session, render_template
from flask.ext.login import make_secure_token
from werkzeug.local import LocalProxy
from .signals import user_registered, password_reset_requested
# Convenient references
_security = LocalProxy(lambda: current_app.extensions['security'])
def md5(data):
return hashlib.md5(data).hexdigest()
@@ -150,7 +155,7 @@ def send_mail(subject, recipient, template, context=None):
context = context or {}
msg = Message(subject,
sender=current_app.security.email_sender,
sender=_security.email_sender,
recipients=[recipient])
base = 'security/email'
+2 -2
View File
@@ -33,9 +33,9 @@ from flask_security.utils import get_url, get_post_login_redirect, do_flash, \
# Convenient references
_security = LocalProxy(lambda: app.security)
_security = LocalProxy(lambda: app.extensions['security'])
_datastore = LocalProxy(lambda: app.security.datastore)
_datastore = LocalProxy(lambda: _security.datastore)
_logger = LocalProxy(lambda: app.logger)
+1 -1
View File
@@ -279,7 +279,7 @@ class ExpiredConfirmationTest(SecurityTest):
self.assertIn(e, outbox[0].html)
self.assertNotIn(token, outbox[0].html)
expire_text = self.app.security.confirm_email_within
expire_text = self.AUTH_CONFIG['SECURITY_CONFIRM_EMAIL_WITHIN']
text = 'You did not confirm your account within %s' % expire_text
self.assertIn(text, r.data)