X-Git-Url: http://git.cascardo.info/?p=cascardo%2Fipsilon.git;a=blobdiff_plain;f=ipsilon%2Fproviders%2Fsaml2idp.py;h=93dcbc6c448828e3ca29104ac286124263a4dbb0;hp=6dfb03ab1a19ac5b0a66deb3e432997d7397e277;hb=07ec779defce9b0fecf4da8c726d1b492c147626;hpb=be55bdf7ee36ad38b25b5f79fc4b82edb2557148 diff --git a/ipsilon/providers/saml2idp.py b/ipsilon/providers/saml2idp.py index 6dfb03a..93dcbc6 100644 --- a/ipsilon/providers/saml2idp.py +++ b/ipsilon/providers/saml2idp.py @@ -7,6 +7,8 @@ from ipsilon.providers.saml2.logout import LogoutRequest from ipsilon.providers.saml2.admin import Saml2AdminPage from ipsilon.providers.saml2.rest import Saml2RestBase from ipsilon.providers.saml2.provider import IdentityProvider +from ipsilon.providers.saml2.sessions import SAMLSessionFactory +from ipsilon.util.data import SAML2SessionStore from ipsilon.tools.certs import Certificate from ipsilon.tools import saml2metadata as metadata from ipsilon.tools import files @@ -129,7 +131,7 @@ class Continue(AuthenticateRequest): return self.auth(login) -class RedirectLogout(LogoutRequest): +class Logout(LogoutRequest): def GET(self, *args, **kwargs): query = cherrypy.request.query_string @@ -157,7 +159,7 @@ class SLO(ProviderPageBase): def __init__(self, *args, **kwargs): super(SLO, self).__init__(*args, **kwargs) self.debug('SLO init') - self.Redirect = RedirectLogout(*args, **kwargs) + self.Redirect = Logout(*args, **kwargs) # one week @@ -213,6 +215,7 @@ class IdpProvider(ProviderBase): self.rest = None self.page = None self.idp = None + self.sessionfactory = None self.description = """ Provides SAML 2.0 authentication infrastructure. """ @@ -270,6 +273,10 @@ Provides SAML 2.0 authentication infrastructure. """ 'default allowed attributes', 'Defines a list of allowed attributes, applied after mapping', ['*']), + pconfig.String( + 'session database url', + 'Database URL for SAML2 sessions', + 'saml2.sessions.db.sqlite'), ) if cherrypy.config.get('debug', False): import logging @@ -279,6 +286,14 @@ Provides SAML 2.0 authentication infrastructure. """ logger.addHandler(lh) logger.setLevel(logging.DEBUG) + store = SAML2SessionStore( + database_url=self.get_config_value('session database url') + ) + bt = cherrypy.process.plugins.BackgroundTask( + 60, store.remove_expired_sessions + ) + bt.start() + @property def allow_self_registration(self): return self.get_config_value('allow self registration') @@ -331,7 +346,6 @@ Provides SAML 2.0 authentication infrastructure. """ return self.get_config_value('default allowed attributes') def get_tree(self, site): - self.idp = self.init_idp() self.page = SAML2(site, self) self.admin = Saml2AdminPage(site, self) self.rest = Saml2RestBase(site, self) @@ -339,9 +353,13 @@ Provides SAML 2.0 authentication infrastructure. """ def init_idp(self): idp = None + self.sessionfactory = SAMLSessionFactory( + database_url=self.get_config_value('session database url') + ) # Init IDP data try: - idp = IdentityProvider(self) + idp = IdentityProvider(self, + sessionfactory=self.sessionfactory) except Exception, e: # pylint: disable=broad-except self.debug('Failed to init SAML2 provider: %r' % e) return None @@ -375,30 +393,23 @@ Provides SAML 2.0 authentication infrastructure. """ Logout all SP sessions when the logout comes from the IdP. For the current user only. + + Only use HTTP-Redirect to start the logout. This is guaranteed + to be supported in SAML 2. """ self.debug("IdP-initiated SAML2 logout") us = UserSession() + user = us.get_user() - saml_sessions = us.get_provider_data('saml2') - if saml_sessions is None: - self.debug("No SAML2 sessions to logout") - return - session = saml_sessions.get_next_logout(remove=False) + saml_sessions = self.sessionfactory + # pylint: disable=unused-variable + (mech, session) = saml_sessions.get_next_logout( + logout_mechs=[lasso.SAML2_METADATA_BINDING_REDIRECT]) if session is None: return - # Add a fake session to indicate where the user should - # be redirected to when all SP's are logged out. - idpurl = self._root.instance_base_url() - saml_sessions.add_session("_idp_initiated_logout", - idpurl, - "") - init_session = saml_sessions.find_session_by_provider(idpurl) - init_session.set_logoutstate(idpurl, "idp_initiated_logout", None) - saml_sessions.start_logout(init_session) - logout = self.idp.get_logout_handler() - logout.setSessionFromDump(session.session.dump()) + logout.setSessionFromDump(session.login_session) logout.initRequest(session.provider_id) try: logout.buildRequestMsg() @@ -407,6 +418,22 @@ Provides SAML 2.0 authentication infrastructure. """ raise cherrypy.HTTPRedirect(400, 'Failed to log out user: %s ' % e) + # Add a fake session to indicate where the user should + # be redirected to when all SP's are logged out. + idpurl = self._root.instance_base_url() + session_id = "_" + uuid.uuid4().hex.upper() + saml_sessions.add_session(session_id, idpurl, user.name, "", "", + [lasso.SAML2_METADATA_BINDING_REDIRECT]) + init_session = saml_sessions.get_session_by_id(session_id) + saml_sessions.start_logout(init_session, relaystate=idpurl) + + # Add the logout request id we just created to the session to be + # logged out so that when it responds we can find the right + # session. + session.set_logoutstate(request_id=logout.request.id) + saml_sessions.start_logout(session, initial=False) + + self.debug('Sending initial logout request to %s' % logout.msgUrl) raise cherrypy.HTTPRedirect(logout.msgUrl) @@ -451,8 +478,10 @@ class Installer(ProviderInstaller): help=('Metadata validity period in days ' '(default - %d)' % METADATA_DEFAULT_VALIDITY_PERIOD)) + group.add_argument('--saml2-session-dburl', + help='session database URL') - def configure(self, opts): + def configure(self, opts, changes): if opts['saml2'] != 'yes': return @@ -489,7 +518,11 @@ class Installer(ProviderInstaller): 'idp certificate file': cert.cert, 'idp key file': cert.key, 'idp nameid salt': uuid.uuid4().hex, - 'idp metadata validity': opts['saml2_metadata_validity']} + 'idp metadata validity': opts['saml2_metadata_validity'], + 'session database url': opts['saml2_session_dburl'] or + opts['database_url'] % { + 'datadir': opts['data_dir'], + 'dbname': 'saml2.sessions.db'}} po.save_plugin_config(config) # Update global config to add login plugin