Source code for velruse.app

import os

from anykeystore import create_store_from_settings

from pyramid.config import Configurator
from pyramid.exceptions import ConfigurationError
from pyramid.response import Response

from velruse.app.utils import generate_token
from velruse.app.utils import redirect_form


log = __import__('logging').getLogger(__name__)


def auth_complete_view(context, request):
    endpoint = request.registry.settings.get('endpoint')
    token = generate_token()
    storage = request.registry.velruse_store
    result_data = {
        'provider_type': context.provider_type,
        'provider_name': context.provider_name,
        'profile': context.profile,
        'credentials': context.credentials,
    }
    storage.store(token, result_data, expires=300)
    form = redirect_form(endpoint, token)
    return Response(body=form)


def auth_denied_view(context, request):
    endpoint = request.registry.settings.get('endpoint')
    token = generate_token()
    storage = request.registry.velruse_store
    error_dict = {
        'provider_type': context.provider_type,
        'provider_name': context.provider_name,
        'error': context.reason,
    }
    storage.store(token, error_dict, expires=300)
    form = redirect_form(endpoint, token)
    return Response(body=form)


def auth_info_view(request):
    # TODO: insecure URL, must be protected behind a firewall
    storage = request.registry.velruse_store
    token = request.GET.get('token')
    try:
        return storage.retrieve(token)
    except KeyError:
        log.info('auth_info requested invalid token "%s"')
        request.response.status = 400
        return None


[docs]def default_setup(config): """Configure Velruse's session factory and backend storage. The default setup uses Pyramid's ``UnencryptedCookieSessionFactoryConfig`` for storing session data. Relevant settings: ``session.secret`` controls the secret used when signing the session cookies and will be randomly generated if unspecified. ``session.cookie_name`` is the name of the cookie stored on a client's browser and will default to 'velruse.session'. ``store.*`` settings are used by the `anykeystore` library to construct a storage backend for user credentials. If no storage settings are specified then an in-memory storage backend will be used. """ from pyramid.session import UnencryptedCookieSessionFactoryConfig log.info('Using an unencrypted cookie-based session. This can be ' 'changed by pointing the "velruse.setup" setting at a different ' 'function for configuring the session factory.') settings = config.registry.settings secret = settings.get('session.secret') cookie_name = settings.get('session.cookie_name', 'velruse.session') if secret is None: log.warn('Configuring unencrypted cookie-based session with a ' 'random secret which will invalidate old cookies when ' 'restarting the app.') secret = ''.join('%02x' % ord(x) for x in os.urandom(16)) log.info('autogenerated session secret: %s', secret) factory = UnencryptedCookieSessionFactoryConfig( secret, cookie_name=cookie_name) config.set_session_factory(factory) # setup backing storage storage_string = settings.get('store', 'memory') settings['store.store'] = storage_string store = create_store_from_settings(settings, prefix='store.') config.register_velruse_store(store)
[docs]def register_velruse_store(config, storage): """Add key/value store for Velruse to the Pyramid application. This function is registered with Pyramid and can be used via ``config.register_velruse_store(storage)``. ``storage`` should be an instance of an `anykeystore` backend. """ config.registry.velruse_store = storage
settings_adapter = { 'bitbucket': 'add_bitbucket_login_from_settings', 'douban': 'add_douban_login_from_settings', 'facebook': 'add_facebook_login_from_settings', 'github': 'add_github_login_from_settings', 'google': 'add_google_login_from_settings', 'google_oauth2': 'add_google_oauth2_login_from_settings', 'lastfm': 'add_lastfm_login_from_settings', 'linkedin': 'add_linkedin_login_from_settings', 'live': 'add_live_login_from_settings', 'qq': 'add_qq_login_from_settings', 'renren': 'add_renren_login_from_settings', 'taobao': 'add_taobao_login_from_settings', 'twitter': 'add_twitter_login_from_settings', 'weibo': 'add_weibo_login_from_settings', } def find_providers(settings): providers = set() for k in settings: if k.startswith('provider.'): k = k[9:].split('.', 1)[0] providers.add(k) return providers def load_provider(config, provider): settings = config.registry.settings impl = settings.get('provider.%s.impl' % provider) or provider login_cfg = settings_adapter.get(impl) if login_cfg is None: raise ConfigurationError( 'could not find configuration method for provider %s' '' % provider) loader = getattr(config, login_cfg) loader(prefix='provider.%s.' % provider) def includeme(config): """Add the Velruse standalone app configuration to a Pyramid app.""" settings = config.registry.settings config.add_directive('register_velruse_store', register_velruse_store) # setup application setup = settings.get('setup') or default_setup if setup: config.include(setup) # include supported providers for provider in settings_adapter: config.include('velruse.providers.%s' % provider) # configure requested providers for provider in find_providers(settings): load_provider(config, provider) # check for required settings if not settings.get('endpoint'): raise ConfigurationError( 'missing required setting "endpoint"') # add views config.add_view( auth_complete_view, context='velruse.AuthenticationComplete') config.add_view( auth_denied_view, context='velruse.AuthenticationDenied') config.add_view( auth_info_view, name='auth_info', request_param='format=json', renderer='json')
[docs]def make_app(global_conf, **settings): """Construct a complete WSGI app. This function is compatible with the `PasteDeploy` WSGI application factory API, which is also used by Pyramid's ``pserve`` script. Example INI file: .. code-block:: ini [server:main] use = egg:Paste#http host = 0.0.0.0 port = 80 [composite:main] use = egg:Paste#urlmap / = YOURAPP /velruse = velruse [app:velruse] use = egg:velruse setup = myapp.setup_velruse endpoint = http://example.com/logged_in store = redis store.host = localhost store.port = 6379 store.db = 0 store.key_prefix = velruse_ustore provider.facebook.consumer_key = KMfXjzsA2qVUcnnRn3vpnwWZ2pwPRFZdb provider.facebook.consumer_secret = ULZ6PkJbsqw2GxZWCIbOEBZdkrb9XwgXNjRy provider.facebook.scope = email provider.tw.impl = twitter provider.tw.consumer_key = ULZ6PkJbsqw2GxZWCIbOEBZdkrb9XwgXNjRy provider.tw.consumer_secret = eoCrFwnpBWXjbim5dyG6EP7HzjhQzFsMAcQOEK [app:YOURAPP] use = egg:YOURAPP full_stack = true static_files = true """ config = Configurator(settings=settings) config.include(includeme) return config.make_wsgi_app()