def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.parse_remote('http://google.es') try: data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://www.testshib.org/metadata/testshib-providers.xml') except URLError: xml = self.file_contents(join(self.data_path, 'metadata', 'testshib-providers.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml) self.assertTrue(data is not None and data is not {}) expected_settings_json = """ { "sp": { "NameIDFormat": "urn:mace:shibboleth:1.0:nameIdentifier" }, "idp": { "entityId": "https://idp.testshib.org/idp/shibboleth", "singleSignOnService": { "url": "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data)
def __init__(self): if current_app.config['SAML_ENABLED']: from onelogin.saml2.auth import OneLogin_Saml2_Auth from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser self.idp_timestamp = datetime.now() self.OneLogin_Saml2_Auth = OneLogin_Saml2_Auth self.OneLogin_Saml2_IdPMetadataParser = OneLogin_Saml2_IdPMetadataParser self.idp_data = None if 'SAML_IDP_ENTITY_ID' in current_app.config: self.idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote( current_app.config['SAML_METADATA_URL'], entity_id=current_app.config.get('SAML_IDP_ENTITY_ID', None), required_sso_binding=current_app. config['SAML_IDP_SSO_BINDING']) else: self.idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote( current_app.config['SAML_METADATA_URL'], entity_id=current_app.config.get('SAML_IDP_ENTITY_ID', None)) if self.idp_data is None: current_app.logger.info( 'SAML: IDP Metadata initial load failed') exit(-1)
def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.parse_remote( 'http://google.es') try: data = OneLogin_Saml2_IdPMetadataParser.parse_remote( 'https://idp.testshib.org/idp/shibboleth') except URLError: xml = self.file_contents( join(self.data_path, 'metadata', 'testshib-providers.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml) self.assertTrue(data is not None and data is not {}) expected_settings_json = """ { "sp": { "NameIDFormat": "urn:mace:shibboleth:1.0:nameIdentifier" }, "idp": { "entityId": "https://idp.testshib.org/idp/shibboleth", "x509cert": "MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryhm3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEmlGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBnxoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTHot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQIDAQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQwEoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzROZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QPdRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOTMVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhORkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqXMLRKhDgdmA==", "singleSignOnService": { "url": "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data)
def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.parse_remote( 'http://google.es') try: data = OneLogin_Saml2_IdPMetadataParser.parse_remote( 'https://www.testshib.org/metadata/testshib-providers.xml') except URLError: xml = self.file_contents( join(self.data_path, 'metadata', 'testshib-providers.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml) self.assertTrue(data is not None and data is not {}) expected_settings_json = """ { "sp": { "NameIDFormat": "urn:mace:shibboleth:1.0:nameIdentifier" }, "idp": { "entityId": "https://idp.testshib.org/idp/shibboleth", "singleSignOnService": { "url": "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data)
def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.parse_remote('http://google.es') try: data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://idp.testshib.org/idp/shibboleth') except URLError: xml = self.file_contents(join(self.data_path, 'metadata', 'testshib-providers.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml) self.assertTrue(data is not None and data is not {}) expected_settings_json = """ { "sp": { "NameIDFormat": "urn:mace:shibboleth:1.0:nameIdentifier" }, "idp": { "x509cert": "MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryhm3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEmlGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBnxoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTHot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQIDAQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQwEoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzROZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QPdRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOTMVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhORkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqXMLRKhDgdmA==", "entityId": "https://idp.testshib.org/idp/shibboleth", "singleSignOnService": { "url": "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data)
def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.parse_remote('http://google.es') data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://www.testshib.org/metadata/testshib-providers.xml') self.assertTrue(data is not None and data is not {}) expected_data = {'sp': {'NameIDFormat': 'urn:mace:shibboleth:1.0:nameIdentifier'}, 'idp': {'singleLogoutService': {'url': 'https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO'}, 'entityId': 'https://idp.testshib.org/idp/shibboleth'}} self.assertEqual(expected_data, data)
def retrieve_idp_data(): global idp_data, idp_timestamp if 'SAML_IDP_SSO_BINDING' in app.config: new_idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None), required_sso_binding=app.config['SAML_IDP_SSO_BINDING']) else: new_idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None)) if new_idp_data is not None: idp_data = new_idp_data idp_timestamp = datetime.now() print("SAML: IDP Metadata successfully retrieved from: " + app.config['SAML_METADATA_URL']) else: print("SAML: IDP Metadata could not be retrieved")
def get_idp_settings(): idp_metadata_xml = settings.SAML2_IDP_METADATA_XML idp_metadata_url = settings.SAML2_IDP_METADATA_URL logger.debug('Start getting IDP configuration') xml_idp_settings = None try: if idp_metadata_xml.strip(): xml_idp_settings = IdPMetadataParse.parse(idp_metadata_xml) except Exception as err: logger.warning('Failed to get IDP metadata XML settings, error: %s', str(err)) url_idp_settings = None try: if idp_metadata_url.strip(): url_idp_settings = IdPMetadataParse.parse_remote( idp_metadata_url, timeout=20 ) except Exception as err: logger.warning('Failed to get IDP metadata URL settings, error: %s', str(err)) idp_settings = url_idp_settings or xml_idp_settings if idp_settings is None: msg = 'Unable to resolve IDP settings. ' tip = 'Please contact your administrator to check system settings,' \ 'or login using other methods.' logger.error(msg) raise OneLogin_Saml2_Error(msg + tip, OneLogin_Saml2_Error.SETTINGS_INVALID) logger.debug('IDP settings obtained successfully') return idp_settings
def _settings_from_url(self, metadata_url): idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote(metadata_url)["idp"] sp_settings = { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", "assertionConsumerService": { "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTPS-POST", "url": "%s/saml/acs" % self.domain }, "x509cert": self.x509cert, "privateKey": self.private_key, "entityId": "%s/saml/metadata" % self.domain, "singleLogoutService": { "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", "url": "%s/saml/sls" % self.domain } } security_settings = { # requestedAuthnContext needs to be OFF when 2FA is enabled. "requestedAuthnContext": not self.enable_two_factor } return { "strict": self.strict, "debug": self.debug, "sp": sp_settings, "idp": idp_settings, "security": security_settings, }
def _build_configuration(self, idp): """Update default config with the ones read from configuration.""" def update(d, u): for k, v in u.items(): if isinstance(v, collections.Mapping): d[k] = update(d.get(k, {}), v) else: d[k] = v return d def make_handler(handler, default=None): handler = handler if handler else default return import_string(handler) if handler and isinstance( handler, string_types) else handler config = _default_config(idp) update(config, self.app.config['SSO_SAML_IDPS'][idp]) # Read IdP config from file or URL if any if config['settings_url']: external_conf = OneLogin_Saml2_IdPMetadataParser.parse_remote( config['settings_url']) config['settings']['idp'].update(external_conf.get('idp')) if config['settings_file_path']: with open(config['settings_file_path'], 'r') as idp: external_conf = OneLogin_Saml2_IdPMetadataParser.parse( idp.read()) config['settings']['idp'].update(external_conf.get('idp')) # Load certificate and key if config['sp_cert_file']: with open(config['sp_cert_file'], 'r') as cf: cert = cf.read() config['settings']['sp']['x509cert'] = cert if config['sp_key_file']: with open(config['sp_key_file'], 'r') as cf: cert = cf.read() config['settings']['sp']['privateKey'] = cert # Import handlers is present config['settings_handler'] = make_handler( config['settings_handler'], self.app.config.get('SSO_SAML_DEFAUTL_SETTINGS_HANDLER')) config['login_handler'] = make_handler( config['login_handler'], self.app.config.get('SSO_SAML_DEFAUTL_LOGIN_HANDLER')) config['logout_handler'] = make_handler( config['logout_handler'], self.app.config.get('SSO_SAML_DEFAUTL_LOGOUT_HANDLER')) config['acs_handler'] = make_handler( config['acs_handler'], self.app.config.get('SSO_SAML_DEFAUTL_ACS_HANDLER')) config['sls_handler'] = make_handler( config['sls_handler'], self.app.config.get('SSO_SAML_DEFAUTL_SLS_HANDLER')) return config
def __init__(self, **kwargs): super().__init__(**kwargs) if self.auto_IdP_metadata: idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote( self.auto_IdP_metadata) self.saml_settings = OneLogin_Saml2_IdPMetadataParser.merge_settings( self.saml_settings, idp_data)
def retreive_idp_data(): global idp_data, idp_timestamp new_idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL']) if new_idp_data is not None: idp_data = new_idp_data idp_timestamp = datetime.now() print("SAML: IDP Metadata successfully retreived from: " + app.config['SAML_METADATA_URL']) else: print("SAML: IDP Metadata could not be retreived")
def _get_saml_settings(app): """Generate the internal config file for OneLogin""" insecure = app.config['SAML_IDP_INSECURE'] cert_file = app.config['SAML_CERT_FILE'] key_file = app.config['SAML_KEY_FILE'] requests_signed = app.config['SAML_REQUESTS_SIGNED'] saml_idp_metadata_url = app.config['SAML_IDP_METADATA_URL'] saml_idp_metadata_file = app.config['SAML_IDP_METADATA_FILE'] if saml_idp_metadata_file: with open(saml_idp_metadata_file, 'r') as idp: remote = OneLogin_Saml2_IdPMetadataParser.parse(idp.read()) else: remote = OneLogin_Saml2_IdPMetadataParser.parse_remote( saml_idp_metadata_url, validate_cert=not insecure ) s = { "strict": True, "debug": True, "sp": { "entityId": flask.url_for('sso.metadata', _external=True), "assertionConsumerService": { "url": flask.url_for('sso.acs', _external=True), "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, "singleLogoutService": { "url": flask.url_for('sso.sls', _external=True), "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, }, "security": { "authnRequestsSigned": requests_signed, "logoutRequestSigned": requests_signed } } s.setdefault('sp', {}).update(remote.get('sp')) s.setdefault('idp', {}).update(remote.get('idp')) if requests_signed: with open(cert_file, 'r') as cf: cert = cf.read() with open(key_file, 'r') as kf: key = kf.read() s['sp'].update({ "x509cert": cert, "privateKey": key }) return s
def _parse_saml_settings(idp_metadata, idp_entity_id): if os.path.isfile(idp_metadata): warnings.warn( "Please prepend 'file://' to indicate a local SAML2 IdP file", DeprecationWarning) with open(idp_metadata, 'r', encoding='utf-8') as f: idp_settings = Saml2Parser.parse(f.read(), entity_id=idp_entity_id) elif parse.urlparse(idp_metadata)[0] in ('http', 'https', 'file'): idp_settings = Saml2Parser.parse_remote( url=idp_metadata, validate_cert=False, entity_id=idp_entity_id) else: idp_settings = Saml2Parser.parse(idp_metadata, entity_id=idp_entity_id) return idp_settings
async def init_saml_auth(request): saml_config = copy.deepcopy( config.get("get_user_by_saml_settings.saml_settings", {})) idp_metadata_url = config.get("get_user_by_saml_settings.idp_metadata_url") if idp_metadata_url: idp_metadata = OneLogin_Saml2_IdPMetadataParser.parse_remote( idp_metadata_url) saml_config = dict_merge(saml_config, idp_metadata) auth = await sync_to_async(OneLogin_Saml2_Auth)( request, saml_config, custom_base_path=config.get("get_user_by_saml_settings.saml_path"), ) return auth
def _get_saml_idp_settings(app): config = app.config.copy() saml_idp_metadata_file = config.setdefault('SAML_IDP_METADATA_FILE', None) saml_idp_metadata_url = config.setdefault('SAML_IDP_METADATA_URL', None) insecure = config.setdefault('SAML_IDP_INSECURE', False) if saml_idp_metadata_file: with open(saml_idp_metadata_file, 'r') as idp: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse(idp.read()) else: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote( saml_idp_metadata_url, validate_cert=not insecure) return idp_settings
def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.parse_remote( 'http://google.es') data = OneLogin_Saml2_IdPMetadataParser.parse_remote( 'https://www.testshib.org/metadata/testshib-providers.xml') self.assertTrue(data is not None and data is not {}) expected_data = { 'sp': { 'NameIDFormat': 'urn:mace:shibboleth:1.0:nameIdentifier' }, 'idp': { 'singleLogoutService': { 'url': 'https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO' }, 'entityId': 'https://idp.testshib.org/idp/shibboleth' } } self.assertEqual(expected_data, data)
def ready(self): """Pull settings from Django and defaults and configure SAML settings for use.""" from . import settings as defaults from django.conf import settings from django.core.cache import cache for name in dir(defaults): if name.isupper() and not hasattr(settings, name): setattr(settings, name, getattr(defaults, name)) settings.SAML_SETTINGS = { 'strict': settings.SAML_STRICT, 'debug': settings.SAML_DEBUG } if settings.SAML_SP is None: raise ImproperlyConfigured("SAML_SP must be defined") settings.SAML_SETTINGS['sp'] = settings.SAML_SP if settings.SAML_IDP is None and settings.SAML_IDP_URL is None and settings.SAML_IDP_FILE is None: raise ImproperlyConfigured( "One must be defined: SAML_IDP, SAML_IDP_URL, SAML_IDP_FILE") if settings.SAML_IDP is not None: settings.SAML_SETTINGS['idp'] = settings.SAML_IDP elif settings.SAML_IDP_URL is not None: idp_data = cache.get('SAML_IDP_INFO', None) if idp_data is None: idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote( settings.SAML_IDP_URL) cache.set('SAML_IDP_INFO', idp_data, settings.SAML_IDP_METADATA_TIMEOUT) settings.SAML_SETTINGS['idp'] = idp_data['idp'] elif settings.SAML_IDP_FILE is not None: f = open(settings.SAML_IDP_FILE, 'r') idp_data = OneLogin_Saml2_IdPMetadataParser.parse(f.read()) f.close() settings.SAML_SETTINGS['idp'] = idp_data['idp'] settings.SAML_SETTINGS['security'] = settings.SAML_SECURITY if settings.SAML_CONTACT is not None: settings.SAML_SETTINGS['contactPerson'] = settings.SAML_CONTACT if settings.SAML_ORGANIZATION is not None: settings.SAML_SETTINGS['organization'] = settings.SAML_ORGANIZATION settings.ONELOGIN_SAML_SETTINGS = OneLogin_Saml2_Settings( settings.SAML_SETTINGS, settings.SAML_BASE_DIRECTORY)
def _settings_from_url(self, metadata_url): idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote( metadata_url)["idp"] sp_settings = { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", "assertionConsumerService": { "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTPS-POST", "url": "%s/saml/acs" % self.domain }, "x509cert": self.x509cert, "privateKey": self.private_key, "entityId": "%s/saml/metadata" % self.domain, "singleLogoutService": { "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", "url": "%s/saml/sls" % self.domain } } return {"strict": True, "sp": sp_settings, "idp": idp_settings}
def remote_metadata(self): """Load the IdP metadata from the remote server and cache it for future accesses""" cache_key = '%s-idp-metadata' % self.name cached_metadata = cache.get(cache_key) if cached_metadata: idp_config = json.loads(cached_metadata) else: idp_config = OneLogin_Saml2_IdPMetadataParser.parse_remote( self.metadata_url) idp = idp_config['idp'] cert = self.find_valid_certificate(idp) out = { 'entity_id': idp['entityId'], 'url': idp['singleSignOnService']['url'], 'x509cert': cert, } cache.set(cache_key, json.dumps(idp_config), timeout=24 * 3600) return out
def _get_saml_settings(): # load settings from environment settings = current_app.config.get('SAML_SETTINGS') # if settings doesn't exist and a file is provided, load the file settings_file = current_app.config.get('SAML_SETTINGS_FILE') if not settings and settings_file: with open(settings_file, 'r') as json_data_file: settings = json.load(json_data_file) # if saml metadata url is provided, load idp settings via metadata idp_metadata_url = current_app.config.get('SAML_METADATA_URL') idp_metadata_entity_id = current_app.config.get('SAML_METADATA_ENTITY_ID', None) if idp_metadata_url: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote( idp_metadata_url, entity_id=idp_metadata_entity_id ) settings = OneLogin_Saml2_IdPMetadataParser.merge_settings(settings, idp_settings) return settings
def _get_saml_settings(): # load settings from environment settings = current_app.config.get('SAML_SETTINGS') # if settings doesn't exist and a file is provided, load the file settings_file = current_app.config.get('SAML_SETTINGS_FILE') if not settings and settings_file: with open(settings_file, 'r') as json_data_file: settings = json.load(json_data_file) # if saml metadata url is provided, load idp settings via metadata idp_metadata_url = current_app.config.get('SAML_METADATA_URL') idp_metadata_entity_id = current_app.config.get('SAML_METADATA_ENTITY_ID', None) if idp_metadata_url: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote( idp_metadata_url, entity_id=idp_metadata_entity_id) settings = OneLogin_Saml2_IdPMetadataParser.merge_settings( settings, idp_settings) return settings
def initialize(self, *args, **kwargs): super().initialize(*args, **kwargs) self.log.info("Initializing AuthHub") self.parse_command_line(*args, **kwargs) if self.generate_config: return self.log.info("Loading config") self.load_config_file(self.config_file) if self.auto_IdP_metadata: self.log.info("Getting the IdP metadata.") idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote( self.auto_IdP_metadata) self.saml_settings = OneLogin_Saml2_IdPMetadataParser.merge_settings( self.saml_settings, idp_data) self.init_logging() self.init_db() self.init_secrets() self.init_handlers() self.init_tornado_settings() self.init_tornado()
def handle(self, request, helper): missing_values = error_value = error_url = False id_or_metadata_url = url = '' if 'action_save' in request.POST: id_or_metadata_url = request.POST['id_or_metadata_url'] # Get metadata url if an app_id was provided if id_or_metadata_url and id_or_metadata_url.isdigit(): id_or_metadata_url = ONELOGIN_METADATA_URL_PREFIX + id_or_metadata_url validate_url = URLValidator() try: validate_url(id_or_metadata_url) url = id_or_metadata_url try: data = OneLogin_Saml2_IdPMetadataParser.parse_remote(url) if data and 'idp' in data: idp_data = SAML2Provider.extract_idp_data_from_parsed_data(data) form2 = OneLoginSAMLForm(idp_data) if form2.is_valid(): helper.bind_state('idp', idp_data) helper.bind_state('contact', request.user.email) return helper.next_step() else: missing_values = form2.errors.keys except Exception: error_url = True except ValidationError: error_value = True return self.respond('sentry_auth_onelogin/select-idp.html', { 'error_value': error_value, 'error_url': error_url, 'missing_values': missing_values, 'id_or_metadata_url': id_or_metadata_url })
from app import app from distutils.version import StrictVersion from urllib.parse import urlparse from datetime import datetime, timedelta from threading import Thread from .certutil import KEY_FILE, CERT_FILE if app.config['SAML_ENABLED']: from onelogin.saml2.auth import OneLogin_Saml2_Auth from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser idp_timestamp = datetime(1970, 1, 1) idp_data = None if 'SAML_IDP_ENTITY_ID' in app.config: idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None), required_sso_binding=app.config['SAML_IDP_SSO_BINDING']) else: idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None)) if idp_data is None: print('SAML: IDP Metadata initial load failed') exit(-1) idp_timestamp = datetime.now() def get_idp_data(): global idp_data, idp_timestamp lifetime = timedelta(minutes=app.config['SAML_METADATA_CACHE_LIFETIME']) if idp_timestamp+lifetime < datetime.now(): background_thread = Thread(target=retrieve_idp_data) background_thread.start() return idp_data
import hashlib from app import app from certutil import * from distutils.version import StrictVersion from datetime import datetime, timedelta from threading import Thread if app.config['SAML_ENABLED']: from onelogin.saml2.auth import OneLogin_Saml2_Auth from onelogin.saml2.utils import OneLogin_Saml2_Utils from onelogin.saml2.settings import OneLogin_Saml2_Settings from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser idp_timestamp = datetime(1970, 1, 1) idp_data = None idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote( app.config['SAML_METADATA_URL']) if idp_data is None: print('SAML: IDP Metadata initial load failed') exit(-1) idp_timestamp = datetime.now() def get_idp_data(): global idp_data, idp_timestamp lifetime = timedelta(minutes=app.config['SAML_METADATA_CACHE_LIFETIME']) if idp_timestamp + lifetime < datetime.now(): background_thread = Thread(target=retreive_idp_data) background_thread.start() return idp_data
def handle_sso_command(cmd): if cmd['prefix'] not in ['dashboard sso enable saml2', 'dashboard sso disable', 'dashboard sso status', 'dashboard sso show saml2', 'dashboard sso setup saml2']: return -errno.ENOSYS, '', '' if not python_saml_imported: python_saml_name = 'python3-saml' if sys.version_info >= (3, 0) else 'python-saml' return -errno.EPERM, '', 'Required library not found: `{}`'.format(python_saml_name) if cmd['prefix'] == 'dashboard sso enable saml2': try: OneLogin_Saml2_Settings(SSO_DB.saml2.onelogin_settings) except OneLogin_Saml2_Error: return -errno.EPERM, '', 'Single Sign-On is not configured: ' \ 'use `ceph dashboard sso setup saml2`' SSO_DB.protocol = 'saml2' SSO_DB.save() return 0, 'SSO is "enabled" with "SAML2" protocol.', '' if cmd['prefix'] == 'dashboard sso disable': SSO_DB.protocol = '' SSO_DB.save() return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso status': if SSO_DB.protocol == 'saml2': return 0, 'SSO is "enabled" with "SAML2" protocol.', '' return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso show saml2': return 0, json.dumps(SSO_DB.saml2.to_dict()), '' if cmd['prefix'] == 'dashboard sso setup saml2': ceph_dashboard_base_url = cmd['ceph_dashboard_base_url'] idp_metadata = cmd['idp_metadata'] idp_username_attribute = _get_optional_attr(cmd, 'idp_username_attribute', 'uid') idp_entity_id = _get_optional_attr(cmd, 'idp_entity_id', None) sp_x_509_cert = _get_optional_attr(cmd, 'sp_x_509_cert', '') sp_private_key = _get_optional_attr(cmd, 'sp_private_key', '') if sp_x_509_cert and not sp_private_key: return -errno.EINVAL, '', 'Missing parameter `sp_private_key`.' if not sp_x_509_cert and sp_private_key: return -errno.EINVAL, '', 'Missing parameter `sp_x_509_cert`.' has_sp_cert = sp_x_509_cert != "" and sp_private_key != "" try: # pylint: disable=undefined-variable FileNotFoundError except NameError: # pylint: disable=redefined-builtin FileNotFoundError = IOError try: f = open(sp_x_509_cert, 'r') sp_x_509_cert = f.read() f.close() except FileNotFoundError: pass try: f = open(sp_private_key, 'r') sp_private_key = f.read() f.close() except FileNotFoundError: pass try: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote(idp_metadata, validate_cert=False, entity_id=idp_entity_id) # pylint: disable=broad-except except Exception: try: f = open(idp_metadata, 'r') idp_metadata = f.read() f.close() except FileNotFoundError: pass try: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse(idp_metadata, entity_id=idp_entity_id) # pylint: disable=broad-except except Exception: return -errno.EINVAL, '', 'Invalid parameter `idp_metadata`.' url_prefix = prepare_url_prefix(mgr.get_config('url_prefix', default='')) settings = { 'sp': { 'entityId': '{}{}/auth/saml2/metadata'.format(ceph_dashboard_base_url, url_prefix), 'assertionConsumerService': { 'url': '{}{}/auth/saml2'.format(ceph_dashboard_base_url, url_prefix), 'binding': "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, 'attributeConsumingService': { 'serviceName': "Ceph Dashboard", "serviceDescription": "Ceph Dashboard Service", "requestedAttributes": [ { "name": idp_username_attribute, "isRequired": True } ] }, 'singleLogoutService': { 'url': '{}{}/auth/saml2/logout'.format(ceph_dashboard_base_url, url_prefix), 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' }, "x509cert": sp_x_509_cert, "privateKey": sp_private_key }, 'security': { "nameIdEncrypted": has_sp_cert, "authnRequestsSigned": has_sp_cert, "logoutRequestSigned": has_sp_cert, "logoutResponseSigned": has_sp_cert, "signMetadata": has_sp_cert, "wantMessagesSigned": has_sp_cert, "wantAssertionsSigned": has_sp_cert, "wantAssertionsEncrypted": has_sp_cert, "wantNameIdEncrypted": has_sp_cert, "metadataValidUntil": '', "wantAttributeStatement": False } } settings = OneLogin_Saml2_IdPMetadataParser.merge_settings(settings, idp_settings) SSO_DB.saml2.onelogin_settings = settings SSO_DB.protocol = 'saml2' SSO_DB.save() return 0, json.dumps(SSO_DB.saml2.onelogin_settings), '' return -errno.ENOSYS, '', ''
def handle_sso_command(cmd): if cmd['prefix'] not in [ 'dashboard sso enable saml2', 'dashboard sso disable', 'dashboard sso status', 'dashboard sso show saml2', 'dashboard sso setup saml2' ]: return -errno.ENOSYS, '', '' if not python_saml_imported: python_saml_name = 'python3-saml' if sys.version_info >= ( 3, 0) else 'python-saml' return -errno.EPERM, '', 'Required library not found: `{}`'.format( python_saml_name) if cmd['prefix'] == 'dashboard sso enable saml2': try: OneLogin_Saml2_Settings(mgr.SSO_DB.saml2.onelogin_settings) except OneLogin_Saml2_Error: return -errno.EPERM, '', 'Single Sign-On is not configured: ' \ 'use `ceph dashboard sso setup saml2`' mgr.SSO_DB.protocol = 'saml2' mgr.SSO_DB.save() return 0, 'SSO is "enabled" with "SAML2" protocol.', '' if cmd['prefix'] == 'dashboard sso disable': mgr.SSO_DB.protocol = '' mgr.SSO_DB.save() return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso status': if mgr.SSO_DB.protocol == 'saml2': return 0, 'SSO is "enabled" with "SAML2" protocol.', '' return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso show saml2': return 0, json.dumps(mgr.SSO_DB.saml2.to_dict()), '' if cmd['prefix'] == 'dashboard sso setup saml2': ceph_dashboard_base_url = cmd['ceph_dashboard_base_url'] idp_metadata = cmd['idp_metadata'] idp_username_attribute = _get_optional_attr(cmd, 'idp_username_attribute', 'uid') idp_entity_id = _get_optional_attr(cmd, 'idp_entity_id', None) sp_x_509_cert = _get_optional_attr(cmd, 'sp_x_509_cert', '') sp_private_key = _get_optional_attr(cmd, 'sp_private_key', '') if sp_x_509_cert and not sp_private_key: return -errno.EINVAL, '', 'Missing parameter `sp_private_key`.' if not sp_x_509_cert and sp_private_key: return -errno.EINVAL, '', 'Missing parameter `sp_x_509_cert`.' has_sp_cert = sp_x_509_cert != "" and sp_private_key != "" try: # pylint: disable=undefined-variable FileNotFoundError except NameError: # pylint: disable=redefined-builtin FileNotFoundError = IOError try: f = open(sp_x_509_cert, 'r', encoding='utf-8') if six.PY3 else \ open(sp_x_509_cert, 'rb') sp_x_509_cert = f.read() f.close() except FileNotFoundError: pass try: f = open(sp_private_key, 'r', encoding='utf-8') if six.PY3 else \ open(sp_private_key, 'rb') sp_private_key = f.read() f.close() except FileNotFoundError: pass try: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote( idp_metadata, validate_cert=False, entity_id=idp_entity_id) # pylint: disable=broad-except except Exception: try: f = open(idp_metadata, 'r', encoding='utf-8') if six.PY3 else \ open(idp_metadata, 'rb') idp_metadata = f.read() f.close() except FileNotFoundError: pass try: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse( idp_metadata, entity_id=idp_entity_id) # pylint: disable=broad-except except Exception: return -errno.EINVAL, '', 'Invalid parameter `idp_metadata`.' url_prefix = prepare_url_prefix( mgr.get_module_option('url_prefix', default='')) settings = { 'sp': { 'entityId': '{}{}/auth/saml2/metadata'.format(ceph_dashboard_base_url, url_prefix), 'assertionConsumerService': { 'url': '{}{}/auth/saml2'.format(ceph_dashboard_base_url, url_prefix), 'binding': "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, 'attributeConsumingService': { 'serviceName': "Ceph Dashboard", "serviceDescription": "Ceph Dashboard Service", "requestedAttributes": [{ "name": idp_username_attribute, "isRequired": True }] }, 'singleLogoutService': { 'url': '{}{}/auth/saml2/logout'.format(ceph_dashboard_base_url, url_prefix), 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' }, "x509cert": sp_x_509_cert, "privateKey": sp_private_key }, 'security': { "nameIdEncrypted": has_sp_cert, "authnRequestsSigned": has_sp_cert, "logoutRequestSigned": has_sp_cert, "logoutResponseSigned": has_sp_cert, "signMetadata": has_sp_cert, "wantMessagesSigned": has_sp_cert, "wantAssertionsSigned": has_sp_cert, "wantAssertionsEncrypted": has_sp_cert, "wantNameIdEncrypted": False, # Not all Identity Providers support this. "metadataValidUntil": '', "wantAttributeStatement": False } } settings = OneLogin_Saml2_IdPMetadataParser.merge_settings( settings, idp_settings) mgr.SSO_DB.saml2.onelogin_settings = settings mgr.SSO_DB.protocol = 'saml2' mgr.SSO_DB.save() return 0, json.dumps(mgr.SSO_DB.saml2.onelogin_settings), '' return -errno.ENOSYS, '', ''
def handle_sso_command(cmd): if cmd['prefix'] not in ['dashboard sso enable saml2', 'dashboard sso disable', 'dashboard sso status', 'dashboard sso show saml2', 'dashboard sso setup saml2']: return -errno.ENOSYS, '', '' if not python_saml_imported: return -errno.EPERM, '', 'Required library not found: `python3-saml`' if cmd['prefix'] == 'dashboard sso enable saml2': try: Saml2Settings(mgr.SSO_DB.saml2.onelogin_settings) except Saml2Error: return -errno.EPERM, '', 'Single Sign-On is not configured: ' \ 'use `ceph dashboard sso setup saml2`' mgr.SSO_DB.protocol = 'saml2' mgr.SSO_DB.save() return 0, 'SSO is "enabled" with "SAML2" protocol.', '' if cmd['prefix'] == 'dashboard sso disable': mgr.SSO_DB.protocol = '' mgr.SSO_DB.save() return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso status': if mgr.SSO_DB.protocol == 'saml2': return 0, 'SSO is "enabled" with "SAML2" protocol.', '' return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso show saml2': return 0, json.dumps(mgr.SSO_DB.saml2.to_dict()), '' if cmd['prefix'] == 'dashboard sso setup saml2': ceph_dashboard_base_url = cmd['ceph_dashboard_base_url'] idp_metadata = cmd['idp_metadata'] idp_username_attribute = _get_optional_attr(cmd, 'idp_username_attribute', 'uid') idp_entity_id = _get_optional_attr(cmd, 'idp_entity_id', None) sp_x_509_cert_path = _get_optional_attr(cmd, 'sp_x_509_cert', '') sp_private_key_path = _get_optional_attr(cmd, 'sp_private_key', '') if sp_x_509_cert_path and not sp_private_key_path: return -errno.EINVAL, '', 'Missing parameter `sp_private_key`.' if not sp_x_509_cert_path and sp_private_key_path: return -errno.EINVAL, '', 'Missing parameter `sp_x_509_cert`.' has_sp_cert = sp_x_509_cert_path != "" and sp_private_key_path != "" try: with open(sp_x_509_cert_path, 'r') as f: sp_x_509_cert = f.read() except FileNotFoundError: sp_x_509_cert = '' try: with open(sp_private_key_path, 'r') as f: sp_private_key = f.read() except FileNotFoundError: sp_private_key = '' if os.path.isfile(idp_metadata): warnings.warn( "Please prepend 'file://' to indicate a local SAML2 IdP file", DeprecationWarning) with open(idp_metadata, 'r') as f: idp_settings = Saml2Parser.parse(f.read(), entity_id=idp_entity_id) elif parse.urlparse(idp_metadata)[0] in ('http', 'https', 'file'): idp_settings = Saml2Parser.parse_remote( url=idp_metadata, validate_cert=False, entity_id=idp_entity_id) else: idp_settings = Saml2Parser.parse(idp_metadata, entity_id=idp_entity_id) url_prefix = prepare_url_prefix(mgr.get_module_option('url_prefix', default='')) settings = { 'sp': { 'entityId': '{}{}/auth/saml2/metadata'.format(ceph_dashboard_base_url, url_prefix), 'assertionConsumerService': { 'url': '{}{}/auth/saml2'.format(ceph_dashboard_base_url, url_prefix), 'binding': "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, 'attributeConsumingService': { 'serviceName': "Ceph Dashboard", "serviceDescription": "Ceph Dashboard Service", "requestedAttributes": [ { "name": idp_username_attribute, "isRequired": True } ] }, 'singleLogoutService': { 'url': '{}{}/auth/saml2/logout'.format(ceph_dashboard_base_url, url_prefix), 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' }, "x509cert": sp_x_509_cert, "privateKey": sp_private_key }, 'security': { "nameIdEncrypted": has_sp_cert, "authnRequestsSigned": has_sp_cert, "logoutRequestSigned": has_sp_cert, "logoutResponseSigned": has_sp_cert, "signMetadata": has_sp_cert, "wantMessagesSigned": has_sp_cert, "wantAssertionsSigned": has_sp_cert, "wantAssertionsEncrypted": has_sp_cert, "wantNameIdEncrypted": has_sp_cert, "metadataValidUntil": '', "wantAttributeStatement": False } } settings = Saml2Parser.merge_settings(settings, idp_settings) mgr.SSO_DB.saml2.onelogin_settings = settings mgr.SSO_DB.protocol = 'saml2' mgr.SSO_DB.save() return 0, json.dumps(mgr.SSO_DB.saml2.onelogin_settings), '' return -errno.ENOSYS, '', ''
def _build_configuration(self, idp): """Update default config with the ones read from configuration.""" def update(d, u): for k, v in u.items(): if isinstance(v, collections.Mapping): d[k] = update(d.get(k, {}), v) else: d[k] = v return d def make_handler(handler, default=None): handler = handler if handler else default return (import_string(handler) if handler and isinstance(handler, string_types) else handler) config = _default_config(idp) update(config, self.app.config["SSO_SAML_IDPS"][idp]) # Read IdP config from file or URL if any if config["settings_url"]: external_conf = OneLogin_Saml2_IdPMetadataParser.parse_remote( config["settings_url"]) config["settings"]["idp"].update(external_conf.get("idp")) if config["settings_file_path"]: with open(config["settings_file_path"], "r") as idp: file = config["settings_file_path"] # xml format if file.endswith(".xml"): external_conf = OneLogin_Saml2_IdPMetadataParser.parse( idp.read()) # json format elif file.endswith(".json"): external_conf = json.loads(idp.read()) config["settings"]["idp"].update(external_conf.get("idp")) # Load certificate and key if config["sp_cert_file"]: with open(config["sp_cert_file"], "r") as cf: cert = cf.read() config["settings"]["sp"]["x509cert"] = cert if config["sp_key_file"]: with open(config["sp_key_file"], "r") as cf: cert = cf.read() config["settings"]["sp"]["privateKey"] = cert # Import handlers is present config["settings_handler"] = make_handler( config["settings_handler"], self.app.config.get("SSO_SAML_DEFAUTL_SETTINGS_HANDLER"), ) config["login_handler"] = make_handler( config["login_handler"], self.app.config.get("SSO_SAML_DEFAUTL_LOGIN_HANDLER"), ) config["logout_handler"] = make_handler( config["logout_handler"], self.app.config.get("SSO_SAML_DEFAUTL_LOGOUT_HANDLER"), ) config["acs_handler"] = make_handler( config["acs_handler"], self.app.config.get("SSO_SAML_DEFAUTL_ACS_HANDLER")) config["sls_handler"] = make_handler( config["sls_handler"], self.app.config.get("SSO_SAML_DEFAUTL_SLS_HANDLER")) return config