Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(package): enabling postopend init_app for all managers #338

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions flask_user/db_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .db_adapters import PynamoDbAdapter, DynamoDbAdapter, MongoDbAdapter, SQLDbAdapter
from flask_user import current_user, ConfigError


class DBManager(object):
"""Manage DB objects."""

Expand All @@ -21,23 +22,31 @@ def __init__(self, app, db, UserClass, UserEmailClass=None, UserInvitationClass=
UserInvitationClass: Optional UserInvitation class for user-invitation feature.
RoleClass: For testing purposes only.
"""
self.app = app
self.db = db
self.UserClass = UserClass
self.UserEmailClass = UserEmailClass
self.UserInvitationClass = UserInvitationClass
self.RoleClass = RoleClass

self.user_manager = app.user_manager
self.app = app
self.user_manager = None
self.db_adapter = None

if app:
self.init_app(app)

def init_app(self, app):

self.app = app
self.user_manager = app.user_manager

# Check if db is a SQLAlchemy instance
if self.db_adapter is None:
try:
from flask_sqlalchemy import SQLAlchemy

if isinstance(db, SQLAlchemy):
self.db_adapter = SQLDbAdapter(app, db)
if isinstance(self.db, SQLAlchemy):
self.db_adapter = SQLDbAdapter(app, self.db)
except ImportError:
pass # Ignore ImportErrors

Expand All @@ -46,18 +55,18 @@ def __init__(self, app, db, UserClass, UserEmailClass=None, UserInvitationClass=
try:
from flask_mongoengine import MongoEngine

if isinstance(db, MongoEngine):
self.db_adapter = MongoDbAdapter(app, db)
if isinstance(self.db, MongoEngine):
self.db_adapter = MongoDbAdapter(app, self.db)
except ImportError:
pass # Ignore ImportErrors

# Check if db is a Flywheel instance
if self.db_adapter is None: # pragma: no cover
if self.db_adapter is None: # pragma: no cover
try:
from flask_flywheel import Flywheel

if isinstance(db, Flywheel):
self.db_adapter = DynamoDbAdapter(app, db)
if isinstance(self.db, Flywheel):
self.db_adapter = DynamoDbAdapter(app, self.db)
except ImportError:
pass # Ignore ImportErrors

Expand All @@ -66,18 +75,17 @@ def __init__(self, app, db, UserClass, UserEmailClass=None, UserInvitationClass=
try:
from pynamodb.models import Model

if issubclass(UserClass, Model):
if issubclass(self.UserClass, Model):
self.db_adapter = PynamoDbAdapter(app)
except ImportError:
pass # Ignore ImportErrors
pass # Ignore ImportErrors

# Check self.db_adapter
if self.db_adapter is None:
raise ConfigError(
'No Flask-SQLAlchemy, Flask-MongoEngine or Flask-Flywheel installed and no Pynamo Model in use.'\
'No Flask-SQLAlchemy, Flask-MongoEngine or Flask-Flywheel installed and no Pynamo Model in use.' \
' You must install one of these Flask extensions.')


def add_user_role(self, user, role_name):
"""Associate a role name with a user."""

Expand Down Expand Up @@ -256,14 +264,12 @@ def username_is_available(self, new_username):
# Return False otherwise.
return self.find_user_by_username(new_username) == None


# def delete_role_name(self, role_name):
# if isinstance(self.db_adapter, SQLDbAdapter):
# role = self.db_adapter.find_first_object(self.user_manager.db_manager.RoleClass, name=role_name)
# if role:
# self.db_adapter.delete_object(role)


# Database management methods
# ---------------------------

Expand All @@ -277,4 +283,3 @@ def drop_all_tables(self):
.. warning:: ALL DATA WILL BE LOST. Use only for automated testing.
"""
return self.db_adapter.drop_all_tables()

9 changes: 7 additions & 2 deletions flask_user/password_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ def __init__(self, app):
``password_manager = PasswordManager('bcrypt')``
"""

# Create a passlib CryptContext
self.app = app
self.user_manager = app.user_manager
self.user_manager = None
if app:
self.init_app(app)

# Create a passlib CryptContext
self.password_crypt_context = CryptContext(
schemes=self.user_manager.USER_PASSLIB_CRYPTCONTEXT_SCHEMES,
**self.user_manager.USER_PASSLIB_CRYPTCONTEXT_KEYWORDS)

def init_app(self, app):
self.app = app
self.user_manager = app.user_manager

def hash_password(self, password):
"""Hash plaintext ``password`` using the ``password_hash`` specified in the constructor.
Expand Down
6 changes: 5 additions & 1 deletion flask_user/token_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@ class TokenManager(object):

# *** Public methods ***

def __init__(self, app):
def __init__(self, app=None):
"""Check config settings and initialize the Fernet encryption cypher.

Fernet is basically AES128 in CBC mode, with a timestamp and a signature.

Args:
app(Flask): The Flask application instance.
"""
self.app = app
if app:
self.init_app(app)

def init_app(self, app):
self.app = app

# Use the applications's SECRET_KEY if flask_secret_key is not specified.
Expand Down
113 changes: 76 additions & 37 deletions flask_user/user_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
# Copyright (c) 2013 Ling Thio'

import datetime
import string

from flask import abort, Blueprint, current_app, Flask, session
from flask import Blueprint, Flask, abort, current_app, session
from flask_login import LoginManager
from wtforms import ValidationError

from . import ConfigError
from . import forms
from . import ConfigError, forms
from .db_manager import DBManager
from .email_adapters.smtp_email_adapter import SMTPEmailAdapter
from .email_manager import EmailManager
from .password_manager import PasswordManager
from .token_manager import TokenManager
Expand All @@ -25,10 +26,21 @@
# The UserManager is implemented across several source code files.
# Mixins are used to aggregate all member functions into the one UserManager class for ease of customization.
class UserManager(UserManager__Settings, UserManager__Utils, UserManager__Views):
""" Customizable User Authentication and Management.
"""

def __init__(self, app, db, UserClass, **kwargs):
"""Customizable User Authentication and Management."""

def __init__(
self,
app=None,
db=None,
UserClass=None,
login_manager=None,
token_manager=None,
email_manager=None,
email_adapter=None,
password_manager=None,
db_manager=None,
**kwargs
):
"""
Args:
app(Flask): The Flask application instance.
Expand All @@ -50,17 +62,45 @@ def __init__(self, app, db, UserClass, **kwargs):
Customizable UserManager methods
"""

#see http://flask.pocoo.org/docs/0.12/extensiondev/#the-extension-code """
# see http://flask.pocoo.org/docs/0.12/extensiondev/#the-extension-code """

self.app = app
self.db = db
self.login_manager = login_manager
self.babel = None

# Required managers
self.token_manager = token_manager
self.email_manager = email_manager
self.email_adapter = email_adapter
self.password_manager = password_manager
self.db_manager = db_manager

# Set default form classes
# ------------------------
self.AddEmailFormClass = forms.AddEmailForm
self.ChangePasswordFormClass = forms.ChangePasswordForm
self.ChangeUsernameFormClass = forms.ChangeUsernameForm
self.EditUserProfileFormClass = forms.EditUserProfileForm
self.ForgotPasswordFormClass = forms.ForgotPasswordForm
self.InviteUserFormClass = forms.InviteUserForm
self.LoginFormClass = forms.LoginForm
self.RegisterFormClass = forms.RegisterForm
self.ResendEmailConfirmationFormClass = forms.ResendEmailConfirmationForm
self.ResetPasswordFormClass = forms.ResetPasswordForm

if app:
self.init_app(app, db, UserClass, **kwargs)

def init_app(
self, app, db, UserClass,
self,
app,
db,
UserClass,
UserInvitationClass=None,
UserEmailClass=None,
RoleClass=None, # Only used for testing
):
RoleClass=None, # Only used for testing
):

# See http://flask.pocoo.org/docs/0.12/extensiondev/#the-extension-code
# Perform Class type checking
Expand Down Expand Up @@ -120,8 +160,12 @@ def advance_session_timeout():

# Configure Flask-Login
# --------------------
# Setup default LoginManager using Flask-Login
self.login_manager = LoginManager(app)
# Init login manager instance passed during init,
# or setup default LoginManager using Flask-Login
if self.login_manager:
self.login_manager.init_app(app)
else:
self.login_manager = LoginManager(app)
self.login_manager.login_view = 'user.login'

# Flask-Login calls this function to retrieve a User record by token.
Expand All @@ -132,8 +176,9 @@ def load_user_by_user_token(user_token):

# Configure Flask-BabelEx
# -----------------------
self.babel = app.extensions.get('babel', None)
self.babel = app.extensions.get("babel", None)
from .translation_utils import init_translations

init_translations(self.babel)

# Configure Jinja2
Expand Down Expand Up @@ -163,38 +208,33 @@ def call_or_get(function_or_property):
blueprint = Blueprint('flask_user', __name__, template_folder='templates')
app.register_blueprint(blueprint)

# Set default form classes
# ------------------------
self.AddEmailFormClass = forms.AddEmailForm
self.ChangePasswordFormClass = forms.ChangePasswordForm
self.ChangeUsernameFormClass = forms.ChangeUsernameForm
self.EditUserProfileFormClass = forms.EditUserProfileForm
self.ForgotPasswordFormClass = forms.ForgotPasswordForm
self.InviteUserFormClass = forms.InviteUserForm
self.LoginFormClass = forms.LoginForm
self.RegisterFormClass = forms.RegisterForm
self.ResendEmailConfirmationFormClass = forms.ResendEmailConfirmationForm
self.ResetPasswordFormClass = forms.ResetPasswordForm

# Set default managers
# Setup managers. Init app for managers passed during __init__,
# create default for nonexistent once
# --------------------
# Setup DBManager
self.db_manager = DBManager(app, db, UserClass, UserEmailClass, UserInvitationClass, RoleClass)
if self.db_manager:
self.db_manager.init_app(app)
else:
self.db_manager = DBManager(
app, db, UserClass, UserEmailClass, UserInvitationClass, RoleClass
)

# Setup PasswordManager
self.password_manager = PasswordManager(app)
if self.password_manager:
self.password_manager.init_app(app)
else:
self.password_manager = PasswordManager(app)

# Set default EmailAdapter
if self.USER_ENABLE_EMAIL:
from .email_adapters.smtp_email_adapter import SMTPEmailAdapter
self.email_adapter = SMTPEmailAdapter(app)

# Setup EmailManager
if self.USER_ENABLE_EMAIL:
self.email_manager = EmailManager(app)

# Setup TokenManager
self.token_manager = TokenManager(app)
if self.token_manager:
self.token_manager.init_app(app)
else:
self.token_manager = TokenManager(app)

# Allow developers to customize UserManager
self.customize(app)
Expand All @@ -205,9 +245,8 @@ def call_or_get(function_or_property):
# Configure a list of URLs to route to their corresponding view method.
self._add_url_routes(app)


def customize(self, app):
""" Override this method to customize properties.
"""Override this method to customize properties.

Example::

Expand Down