def test_login_handler(self): sso = SSO(app=self.app) @sso.login_handler def _callback(attr): od = OrderedDict() for k, v in sorted(attr.items()): od[k] = v return six.b('{0}'.format(od)) @contextmanager def request_environ_set(app, data): def handler(sender, **kwargs): for (k, v) in data.items(): request.environ[k] = v with request_started.connected_to(handler, app): yield def run(conf, data, expected_data): self.app.config['SSO_ATTRIBUTE_MAP'] = conf with request_environ_set(self.app, data): with self.app.test_client() as c: resp = c.get(self.app.config['SSO_LOGIN_URL']) od = OrderedDict() for k, v in sorted(expected_data.items()): od[k] = v self.assertEqual(resp.data, six.b('{0}'.format(od))) conf = {'HTTP_AJP_SHIB_EPPN': (True, 'eppn'), 'HTTP_AJP_SHIB_MAIL': (False, 'mail')} data = {'HTTP_AJP_SHIB_EPPN': '*****@*****.**'} expected_data = {'eppn': '*****@*****.**', 'mail': None} run(conf, data, expected_data) conf = {'HTTP_AJP_SHIB_EPPN': (True, 'eppn'), 'HTTP_AJP_SHIB_MAIL': (False, 'mail')} data = {'HTTP_AJP_SHIB_EPPN': None} expected_data = {'eppn': None, 'mail': None} @sso.login_handler def _callback_redef(attr): assert False @sso.login_error_handler def _callback_error(attr): od = OrderedDict() for k, v in sorted(attr.items()): od[k] = v return '{0}'.format(od) run(conf, data, expected_data)
def test_login_handler(self): sso = SSO(app=self.app) @sso.login_handler def _callback(attr): return '{0}'.format(attr) @contextmanager def request_environ_set(app, data): def handler(sender, **kwargs): for (k, v) in data.items(): request.environ[k] = v with request_started.connected_to(handler, app): yield def run(conf, data, expected_data): self.app.config['SSO_ATTRIBUTE_MAP'] = conf with request_environ_set(self.app, data): with self.app.test_client() as c: resp = c.get(self.app.config['SSO_LOGIN_URL']) assert literal_eval(resp.data.decode('utf-8')) \ == expected_data conf = {'FOO': (True, 'bar'), 'BAZ': (False, 'baa')} data = {'FOO': 'foo'} expected_data = {'bar': 'foo', 'baa': None} run(conf, data, expected_data) conf = {'FOO': (True, 'bar'), 'BAZ': (True, 'baa')} data = {'FOO': 'foo', 'BAZ': 'baz'} expected_data = {'bar': 'foo', 'baa': 'baz'} run(conf, data, expected_data) conf = {'FOO': (True, 'bar'), 'BAZ': (False, 'baa')} data = {'FOO': 'foo', 'BAZ': 6} expected_data = {'bar': 'foo', 'baa': 6} run(conf, data, expected_data)
def test_invalid_attribute_map(self): SSO(app=self.app) @contextmanager def request_environ_set(app, data): def handler(sender, **kwargs): for (k, v) in data.items(): request.environ[k] = v with request_started.connected_to(handler, app): yield conf = {'FOO': (True, 'bar'), 'BAZ': (True, 'baa')} data = {'FOO': 'foo'} self.app.config['SSO_ATTRIBUTE_MAP'] = conf with request_environ_set(self.app, data): with self.app.test_client() as c: try: c.get(self.app.config['SSO_LOGIN_URL']) assert False except SSOAttributeError: assert True
def test_creation_init(self): assert 'sso' not in self.app.extensions r = SSO() r.init_app(app=self.app) assert isinstance(self.app.extensions['sso'], SSO)
app.register_blueprint(plugins) app.register_blueprint(users) app.register_blueprint(groups) app.register_blueprint(instances) app.register_blueprint(activations) app.register_blueprint(firstuser) app.register_blueprint(myip) app.register_blueprint(sessions) app.register_blueprint(variables) app.register_blueprint(quota) app.register_blueprint(locks) app.register_blueprint(import_export) app.register_blueprint(stats) if app.config['ENABLE_SHIBBOLETH_LOGIN']: sso = SSO(app=app) @sso.login_handler def login(user_info): eppn = user_info['eppn'] user = User.query.filter_by(email=eppn).first() if not user: user = create_user(eppn, password=uuid.uuid4().hex) if not user.is_active: user.is_active = True db.session.commit() if user.is_blocked: error_description = 'You have been blocked, contact your administrator' return render_template('error.html', error_title='User Blocked', error_description=error_description)
db = SQLAlchemy() csrf = CSRFProtect(app) # Map SSO attributes from ADFS to session keys under session['user'] #: Default attribute map SSO_ATTRIBUTE_MAP = { 'eppn': (True, 'eppn'), 'cn': (False, 'cn'), } app.config.setdefault('VERSION', __version__) app.config.setdefault('SSO_ATTRIBUTE_MAP', SSO_ATTRIBUTE_MAP) app.config.setdefault('SSO_LOGIN_URL', '/login') # This attaches the *flask_sso* login handler to the SSO_LOGIN_URL, ext = SSO(app=app) from flowapp import models, constants, validators from .views.admin import admin from .views.rules import rules from .views.api_v1 import api as api_v1 from .views.api_v2 import api as api_v2 from .views.api_v3 import api as api_v3 from .views.api_keys import api_keys from .auth import auth_required from .views.dashboard import dashboard # no need for csrf on api because we use JWT csrf.exempt(api_v1) csrf.exempt(api_v2)
def test_default_config(self): SSO(app=self.app) for k in dir(default_config): if k.startswith('SSO_'): assert self.app.config.get(k) == getattr(default_config, k)
def test_double_creation(self): SSO(app=self.app) self.assertRaises(RuntimeError, SSO, app=self.app)
def test_creation_old_flask(self): # Simulate old Flask (pre 0.9) del self.app.extensions SSO(app=self.app) assert isinstance(self.app.extensions['sso'], SSO)
def test_creation(self): assert 'sso' not in self.app.extensions SSO(app=self.app) assert isinstance(self.app.extensions['sso'], SSO)
def test_login_error_handler(self): sso = SSO(app=self.app) @sso.login_handler def _callback(attr): return '{0}'.format(attr) @sso.login_error_handler def _callback_error(attr): return '{0}'.format(attr) @contextmanager def request_environ_set(app, data): def handler(sender, **kwargs): for (k, v) in data.items(): request.environ[k] = v with request_started.connected_to(handler, app): yield def run(conf, data, expected_data): self.app.config['SSO_ATTRIBUTE_MAP'] = conf with request_environ_set(self.app, data): with self.app.test_client() as c: resp = c.get(self.app.config['SSO_LOGIN_URL']) self.assertEqual(resp.data.decode('utf-8'), '{0}'.format(expected_data)) conf = {'FOO': (True, 'bar'), 'BAZ': (False, 'baa')} data = {'FOO': 'foo'} expected_data = {'bar': 'foo', 'baa': None} run(conf, data, expected_data) conf = {'FOO': (True, 'bar'), 'BAZ': (True, 'baa')} data = {'FOO': 'foo', 'BAZ': 'baz'} expected_data = {'bar': 'foo', 'baa': 'baz'} run(conf, data, expected_data) conf = {'FOO': (True, 'bar'), 'BAZ': (False, 'baa')} data = {'FOO': 'foo', 'BAZ': 6} expected_data = {'bar': 'foo', 'baa': 6} run(conf, data, expected_data) conf = {'FOO': (True, 'bar'), 'BAZ': (False, 'baa')} data = {'FOO': None, 'BAZ': 6} expected_data = {'bar': None, 'baa': 6} run(conf, data, expected_data) # login handler will not be called when required attributes are missing @sso.login_handler def _callback_(attr): assert False conf = {'FOO': (True, 'bar'), 'BAZ': (False, 'baa')} data = {'FOO': None, 'BAZ': 6} expected_data = {'bar': None, 'baa': 6} run(conf, data, expected_data) # login error handler will not be called when required attributes are # present @sso.login_handler def _callback__(attr): return '{0}'.format(attr) @sso.login_error_handler def _callback_error_(attr): assert False conf = {'FOO': (True, 'bar'), 'BAZ': (False, 'baa')} data = {'FOO': 'foo', 'BAZ': 6} expected_data = {'bar': 'foo', 'baa': 6} run(conf, data, expected_data)
re.compile(r'building-[\d]+'), re.compile(r'Users by Home CERNHOME[A-Z]'), ) #: Default attribute map SSO_ATTRIBUTE_MAP = { 'ADFS_AUTHLEVEL': (False, 'authlevel'), 'ADFS_GROUP': (True, 'group'), 'ADFS_LOGIN': (True, 'nickname'), 'ADFS_ROLE': (False, 'role'), 'ADFS_EMAIL': (True, 'email'), 'ADFS_IDENTITYCLASS': (False, 'external'), 'HTTP_SHIB_AUTHENTICATION_METHOD': (False, 'authmethod'), } sso = SSO() def setup_app(app): """Setup SSO extension.""" app.config['CFG_EXTERNAL_AUTH_USING_SSO'] = True app.config.setdefault('SSO_ATTRIBUTE_MAP', SSO_ATTRIBUTE_MAP) sso.init_app(app) def fetch_groups(groups): groups = groups.split( app.config.get('CFG_EXTERNAL_AUTH_SSO_GROUPS_SEPARATOR', ';')) # Filtering out uncomfortable groups groups = [ group for group in groups if group not in app.config.get('CFG_EXTERNAL_AUTH_HIDDEN_GROUPS',
def init(cls, tracking_mode=False): """ :param tracking_mode: :type tracking_mode: bool :return: :rtype: flask.Flask """ from flask_socketio import SocketIO from flask import Flask from Config import Environment cls._app = Flask( Environment.SERVER_DATA['APP_NAME'], static_url_path="/file", static_folder=Environment.SERVER_DATA['STATIC_PATH'], template_folder=Environment.SERVER_DATA['TEMPLATE_PATH']) if 'CORS' in Environment.Logins: from flask_cors import CORS cls._app.config["CORS_ALLOW_HEADERS"] = Environment.SERVER_DATA[ 'CORS']['ALLOW_HEADERS'] cls._app.config["CORS_ALWAYS_SEND"] = Environment.SERVER_DATA[ 'CORS']['ALWAYS_SEND'] cls._app.config[ "CORS_AUTOMATIC_OPTIONS"] = Environment.SERVER_DATA['CORS'][ 'AUTOMATIC_OPTIONS'] cls._app.config["CORS_EXPOSE_HEADERS"] = Environment.SERVER_DATA[ 'CORS']['EXPOSE_HEADERS'] cls._app.config[ "CORS_INTERCEPT_EXCEPTIONS"] = Environment.SERVER_DATA['CORS'][ 'INTERCEPT_EXCEPTIONS'] cls._app.config["CORS_MAX_AGE"] = Environment.SERVER_DATA['CORS'][ 'MAX_AGE'] cls._app.config["CORS_METHODS"] = Environment.SERVER_DATA['CORS'][ 'METHODS'] cls._app.config["CORS_ORIGINS"] = Environment.SERVER_DATA['CORS'][ 'ORIGINS'] cls._app.config["CORS_RESOURCES"] = r"/*" cls._app.config["CORS_SEND_WILDCARD"] = Environment.SERVER_DATA[ 'CORS']['SEND_WILDCARD'] cls._app.config[ "CORS_SUPPORTS_CREDENTIALS"] = Environment.SERVER_DATA['CORS'][ 'SUPPORTS_CREDENTIALS'] cls._app.config["CORS_VARY_HEADER"] = Environment.SERVER_DATA[ 'CORS']['VARY_HEADER'] cors = CORS(cls._app, origins=Environment.SERVER_DATA['CORS']['ORIGINS']) if 'APP_KEY' in Environment.SERVER_DATA: from flask_wtf.csrf import CSRFProtect cls._session = Session() #cls._app.config['TESTING'] = True #cls._app.config['TEMPLATES_AUTO_RELOAD'] = True cls._app.config['SECRET_KEY'] = Environment.SERVER_DATA['APP_KEY'] cls._app.config['SESSION_TYPE'] = Environment.SERVER_DATA[ 'SESSION'] if Environment.SERVER_DATA['SESSION'] == 'filesystem': cls._app.config['SESSION_FILE_DIR'] = Environment.Services[ Environment.SERVER_DATA['SESSION']]['PATH'] if Environment.SERVER_DATA['SESSION'] == 'memcached': import pymemcache cls._app.config['SESSION_MEMCACHED'] = pymemcache.Client( (Environment.Services[Environment.SERVER_DATA['SESSION']] ['HOST'], Environment.Services[ Environment.SERVER_DATA['SESSION']]['PORT'])) if Environment.SERVER_DATA['SESSION'] == 'redis': import redis cls._app.config['SESSION_REDIS'] = redis.from_url( "%s://%s:%d/redis" % (Environment.SERVER_DATA['SESSION'], Environment.Services[ Environment.SERVER_DATA['SESSION']]['HOST'], Environment.Services[ Environment.SERVER_DATA['SESSION']]['PORT'])) if Environment.SERVER_DATA['SESSION'] == 'sqlalchemy': from Database import Database cls._app = Database.setup_sessions(cls._app) if Environment.SERVER_DATA['SESSION'] == 'mongodb': from pymongo import MongoClient db_conf = Environment.Databases[ Environment.SERVER_DATA['SESSION']] cls._app.config['SESSION_MONGODB'] = MongoClient( "%s://%s:%s@%s:%d" % (db_conf['driver'], db_conf['user'], db_conf['password'], db_conf['address'], db_conf['port'])) cls._app.config['SESSION_MONGODB_DB'] = db_conf['database'] cls._app.config['SESSION_MONGODB_COLLECT'] = db_conf[ 'collection'] cls._session.init_app(cls._app) cls._csrf = CSRFProtect() cls._csrf.init_app(cls._app) if 'SSO' in Environment.Logins: from flask_sso import SSO cls.sso = SSO() cls._app.config['SSO_LOGIN_URL'] = Environment.Logins['SSO'][ 'LOGIN_URL'] cls._app.config['SSO_LOGIN_ENDPOINT'] = Environment.Logins[ 'SSO']['LOGIN_ENDPOINT'] cls._app.config['SSO_ATTRIBUTE_MAP'] = { item: (value['value'], value['attr']) for item, value in Environment.Logins['SSO'] ['ATTRIBUTE_MAP'].items() } cls.sso.init_app(cls._app) if 'OpenID' in Environment.Logins: from Utils.Auth.openid import OpenIDConnect from flask_openid import OpenID cls.openid = OpenIDConnect() for key, value in Environment.Logins['OpenID'].items(): cls._app.config[key] = value cls.openid.init_app(cls._app) if 'LDAP' in Environment.Logins: if 'LDAP_HOST' not in Environment.Logins[ 'LDAP'] and 'LDAP_DOMAIN' in Environment.Logins['LDAP']: from activedirectory import Locator ldap = Locator() Environment.Logins['LDAP']['LDAP_HOST'] = ldap.locate_many( Environment.Logins['LDAP']['LDAP_DOMAIN'])[0] if 'LDAP_REQUIRED_GROUP' not in Environment.Logins['LDAP']: Environment.Logins['LDAP']['LDAP_REQUIRED_GROUP'] = None from Utils.Auth.ldap import LDAP for key, val in Environment.Logins['LDAP'].items(): cls._app.config[key] = val cls.ldap = LDAP(cls._app) cls._socket = SocketIO() cls._socket.init_app(cls._app) return cls._app