Source code for flask_sso
# -*- coding: utf-8 -*-
#
# This file is part of Flask-SSO
# Copyright (C) 2014, 2015, 2016 CERN.
#
# Flask-SSO is free software; you can redistribute it and/or modify
# it under the terms of the Revised BSD License; see LICENSE file for
# more details.
"""Implement Shibboleth Single-Sign-On authentication.
Flask-SSO is initialized like this:
Initialization of the extension:
>>> from flask import Flask
>>> from flask_sso import SSO
>>> app = Flask('myapp')
>>> ext = SSO(app=app)
or alternatively using the factory pattern:
>>> app = Flask('myapp')
>>> ext = SSO()
>>> ext.init_app(app)
"""
from __future__ import absolute_import
from . import config
from flask import current_app, request
from flask.signals import Namespace
from .version import __version__
class SSOAttributeError(Exception):
"""General SSO Attribute error."""
# Signals
_signals = Namespace()
sso_logged_in = _signals.signal('sso-logged-in')
"""Sent when a user is logged in.
In addition to the app (which is the sender), it is passed `user`, which is
the user being logged in.
"""
[docs]class SSO(object):
"""Flask extension implementation."""
def __init__(self, app=None):
"""Initialize login callback."""
self.login_callback = None
self.login_error_callback = None
if app is not None:
self.init_app(app)
[docs] def init_app(self, app):
"""Initialize a Flask application."""
self.app = app
# Follow the Flask guidelines on usage of app.extensions
if not hasattr(app, 'extensions'):
app.extensions = {}
if 'sso' in app.extensions:
raise RuntimeError("Flask application already initialized")
app.extensions['sso'] = self
# Set default configuration
app.config.setdefault('SSO_LOGIN_URL', config.SSO_LOGIN_URL)
app.config.setdefault('SSO_LOGIN_ENDPOINT', config.SSO_LOGIN_ENDPOINT)
app.config.setdefault('SSO_ATTRIBUTE_MAP', config.SSO_ATTRIBUTE_MAP)
app.add_url_rule(app.config.get('SSO_LOGIN_URL'),
app.config.get('SSO_LOGIN_ENDPOINT'),
self.login)
[docs] def login_handler(self, callback):
"""Set the callback for the `login` method.
It takes one argument with attributes map, and should return a Flask
response.
:param callback: The callback for login.
"""
self.login_callback = callback
[docs] def login_error_handler(self, callback):
"""Set the error callback for `login` method.
It takes one argument with attributes map, and should return a Flask
response.
:param callback: The callback for login error.
"""
self.login_error_callback = callback
[docs] def login(self):
"""Implement application login endpoint for SSO."""
attrs, error = self.parse_attributes()
if error:
if self.login_error_callback:
return self.login_error_callback(attrs)
else:
raise SSOAttributeError
sso_logged_in.send(current_app._get_current_object(), attributes=attrs)
if self.login_callback:
return self.login_callback(attrs)
[docs] def parse_attributes(self):
"""Parse arguments from environment variables."""
attrs = {}
error = False
for header, attr in self.app.config['SSO_ATTRIBUTE_MAP'].items():
required, name = attr
value = request.environ.get(header, None)
attrs[name] = value
if not value or value == '':
if required:
error = True
return attrs, error
__all__ = ('SSO', '__version__')