Source code for flask_stormpath

# -*- coding: utf-8 -*-
"""
    flask-stormpath
    ---------------

    This module provides secure user authentication and authorization for Flask
    via Stormpath (https://stormpath.com/).  It lets you log users in and out
    of your application in a database-independent fashion, along with allowing
    you to store variable user information in a JSON data store.

    No user table required! :)

    :copyright: (c) 2012 - 2015 Stormpath, Inc.
    :license: Apache, see LICENSE for more details.
"""


__version__ = '0.4.6'
__version_info__ = __version__.split('.')
__author__ = 'Stormpath, Inc.'
__license__ = 'Apache'
__copyright__ = '(c) 2012 - 2015 Stormpath, Inc.'


from flask import (
    Blueprint,
    __version__ as flask_version,
    _app_ctx_stack as stack,
    current_app,
)

from flask_login import (
    LoginManager,
    current_user,
    _get_user,
    login_required,
    login_user,
    logout_user,
)

from stormpath.client import Client
from stormpath.error import Error as StormpathError

from werkzeug.local import LocalProxy

from .context_processors import user_context_processor
from .decorators import groups_required
from .models import User
from .settings import check_settings, init_settings
from .views import (
    google_login,
    facebook_login,
    forgot,
    forgot_change,
    login,
    logout,
    register,
)


# A proxy for the current user.
user = LocalProxy(lambda: _get_user())


[docs]class StormpathManager(object): """ This object is used to hold the settings used to communicate with Stormpath. Instances of :class:`StormpathManager` are not bound to specific apps, so you can create one in the main body of your code and then bind it to your app in a factory function. """ def __init__(self, app=None): """ Initialize this extension. :param obj app: (optional) The Flask app. """ self.app = app # If the user specifies an app, let's configure go ahead and handle all # configuration stuff for the user's app. if app is not None: self.init_app(app) def init_app(self, app): """ Initialize this application. This method will handle: - Configuring application settings. - Configuring Flask-Stormpath. - Adding ourself to the user's app (so the user can reference this extension later on, if they want). :param obj app: The Flask app. """ # Initialize all of the Flask-Stormpath configuration variables and # settings. init_settings(app.config) # Check our user defined settings to ensure Flask-Stormpath is properly # configured. check_settings(app.config) # Initialize the Flask-Login extension. self.init_login(app) # Initialize all URL routes / views. self.init_routes(app) # Initialize our blueprint. This lets us do cool template stuff. blueprint = Blueprint('flask_stormpath', 'flask_stormpath', template_folder='templates') app.register_blueprint(blueprint) # Ensure the `user` context is available in templates. This makes it # really easy for developers to grab user data for display purposes in # templates. app.context_processor(user_context_processor) # Store a reference to the Flask app so we can use it later if # necessary! self.app = app def init_login(self, app): """ Initialize the Flask-Login extension. We use Flask-Login for managing sessions (primarily), so setting it up is necessary. :param obj app: The Flask app. """ app.config['REMEMBER_COOKIE_DURATION'] = app.config['STORMPATH_COOKIE_DURATION'] app.config['REMEMBER_COOKIE_DOMAIN'] = app.config['STORMPATH_COOKIE_DOMAIN'] app.login_manager = LoginManager(app) app.login_manager.user_callback = self.load_user app.stormpath_manager = self if app.config['STORMPATH_ENABLE_LOGIN']: app.login_manager.login_view = 'stormpath.login' # Make this Flask session expire automatically. app.config['PERMANENT_SESSION_LIFETIME'] = app.config['STORMPATH_COOKIE_DURATION'] def init_routes(self, app): """ Initialize our built-in routes. If the user has enabled the built-in views / routes, they will be enabled here. This behavior is fully customizable in the user's settings. :param obj app: The Flask app. """ if app.config['STORMPATH_ENABLE_REGISTRATION']: app.add_url_rule( app.config['STORMPATH_REGISTRATION_URL'], 'stormpath.register', register, methods = ['GET', 'POST'], ) if app.config['STORMPATH_ENABLE_LOGIN']: app.add_url_rule( app.config['STORMPATH_LOGIN_URL'], 'stormpath.login', login, methods = ['GET', 'POST'], ) if app.config['STORMPATH_ENABLE_FORGOT_PASSWORD']: app.add_url_rule( app.config['STORMPATH_FORGOT_PASSWORD_URL'], 'stormpath.forgot', forgot, methods = ['GET', 'POST'], ) app.add_url_rule( app.config['STORMPATH_FORGOT_PASSWORD_CHANGE_URL'], 'stormpath.forgot_change', forgot_change, methods = ['GET', 'POST'], ) if app.config['STORMPATH_ENABLE_LOGOUT']: app.add_url_rule( app.config['STORMPATH_LOGOUT_URL'], 'stormpath.logout', logout, ) if app.config['STORMPATH_ENABLE_GOOGLE']: app.add_url_rule( app.config['STORMPATH_GOOGLE_LOGIN_URL'], 'stormpath.google_login', google_login, ) if app.config['STORMPATH_ENABLE_FACEBOOK']: app.add_url_rule( app.config['STORMPATH_FACEBOOK_LOGIN_URL'], 'stormpath.facebook_login', facebook_login, ) @property def client(self): """ Lazily load the Stormpath Client object we need to access the raw Stormpath SDK. """ ctx = stack.top.app if ctx is not None: if not hasattr(ctx, 'stormpath_client'): # Create our custom user agent. This allows us to see which # version of this SDK are out in the wild! user_agent = 'stormpath-flask/%s flask/%s' % (__version__, flask_version) # If the user is specifying their credentials via a file path, # we'll use this. if self.app.config['STORMPATH_API_KEY_FILE']: ctx.stormpath_client = Client( api_key_file_location = self.app.config['STORMPATH_API_KEY_FILE'], user_agent = user_agent, cache_options = self.app.config['STORMPATH_CACHE'], ) # If the user isn't specifying their credentials via a file # path, it means they're using environment variables, so we'll # try to grab those values. else: ctx.stormpath_client = Client( id = self.app.config['STORMPATH_API_KEY_ID'], secret = self.app.config['STORMPATH_API_KEY_SECRET'], user_agent = user_agent, cache_options = self.app.config['STORMPATH_CACHE'], ) return ctx.stormpath_client @property def login_view(self): """ Return the user's Flask-Login login view, behind the scenes. """ return current_app.login_manager.login_view @login_view.setter def login_view(self, value): """ Proxy any changes to the user's login view to Flask-Login, behind the scenes. """ self.app.login_manager.login_view = value @property def application(self): """ Lazily load the Stormpath Application object we need to handle user authentication, etc. """ ctx = stack.top.app if ctx is not None: if not hasattr(ctx, 'stormpath_application'): applications = self.client.applications.search( self.app.config['STORMPATH_APPLICATION'] ) if applications is None: raise Exception('failed to find ' + self.app.config['STORMPATH_APPLICATION'] + ' application. please add it in the ' 'stormpath console') ctx.stormpath_application = applications[0] return ctx.stormpath_application @staticmethod
[docs] def load_user(account_href): """ Given an Account href (a valid Stormpath Account URL), return the associated User account object (or None). :returns: The User object or None. """ user = current_app.stormpath_manager.client.accounts.get(account_href) try: user._ensure_data() user.__class__ = User return user except StormpathError: return None