Only initialize the SAML IDP when actually enabled
[cascardo/ipsilon.git] / ipsilon / providers / saml2idp.py
index 6528fdf..93dcbc6 100644 (file)
@@ -8,6 +8,7 @@ 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.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
 from ipsilon.tools.certs import Certificate
 from ipsilon.tools import saml2metadata as metadata
 from ipsilon.tools import files
@@ -130,7 +131,7 @@ class Continue(AuthenticateRequest):
         return self.auth(login)
 
 
         return self.auth(login)
 
 
-class RedirectLogout(LogoutRequest):
+class Logout(LogoutRequest):
 
     def GET(self, *args, **kwargs):
         query = cherrypy.request.query_string
 
     def GET(self, *args, **kwargs):
         query = cherrypy.request.query_string
@@ -158,7 +159,7 @@ class SLO(ProviderPageBase):
     def __init__(self, *args, **kwargs):
         super(SLO, self).__init__(*args, **kwargs)
         self.debug('SLO init')
     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
 
 
 # one week
@@ -214,6 +215,7 @@ class IdpProvider(ProviderBase):
         self.rest = None
         self.page = None
         self.idp = None
         self.rest = None
         self.page = None
         self.idp = None
+        self.sessionfactory = None
         self.description = """
 Provides SAML 2.0 authentication infrastructure. """
 
         self.description = """
 Provides SAML 2.0 authentication infrastructure. """
 
@@ -271,6 +273,10 @@ Provides SAML 2.0 authentication infrastructure. """
                 'default allowed attributes',
                 'Defines a list of allowed attributes, applied after mapping',
                 ['*']),
                 '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
         )
         if cherrypy.config.get('debug', False):
             import logging
@@ -280,6 +286,14 @@ Provides SAML 2.0 authentication infrastructure. """
             logger.addHandler(lh)
             logger.setLevel(logging.DEBUG)
 
             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')
     @property
     def allow_self_registration(self):
         return self.get_config_value('allow self registration')
@@ -332,7 +346,6 @@ Provides SAML 2.0 authentication infrastructure. """
         return self.get_config_value('default allowed attributes')
 
     def get_tree(self, site):
         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)
         self.page = SAML2(site, self)
         self.admin = Saml2AdminPage(site, self)
         self.rest = Saml2RestBase(site, self)
@@ -340,9 +353,13 @@ Provides SAML 2.0 authentication infrastructure. """
 
     def init_idp(self):
         idp = None
 
     def init_idp(self):
         idp = None
+        self.sessionfactory = SAMLSessionFactory(
+            database_url=self.get_config_value('session database url')
+        )
         # Init IDP data
         try:
         # 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
         except Exception, e:  # pylint: disable=broad-except
             self.debug('Failed to init SAML2 provider: %r' % e)
             return None
@@ -376,13 +393,18 @@ Provides SAML 2.0 authentication infrastructure. """
         Logout all SP sessions when the logout comes from the IdP.
 
         For the current user only.
         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()
 
         """
         self.debug("IdP-initiated SAML2 logout")
         us = UserSession()
         user = us.get_user()
 
-        saml_sessions = SAMLSessionFactory()
-        session = saml_sessions.get_next_logout()
+        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
 
         if session is None:
             return
 
@@ -400,7 +422,8 @@ Provides SAML 2.0 authentication infrastructure. """
         # be redirected to when all SP's are logged out.
         idpurl = self._root.instance_base_url()
         session_id = "_" + uuid.uuid4().hex.upper()
         # 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, "")
+        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)
 
         init_session = saml_sessions.get_session_by_id(session_id)
         saml_sessions.start_logout(init_session, relaystate=idpurl)
 
@@ -455,6 +478,8 @@ class Installer(ProviderInstaller):
                            help=('Metadata validity period in days '
                                  '(default - %d)' %
                                  METADATA_DEFAULT_VALIDITY_PERIOD))
                            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, changes):
         if opts['saml2'] != 'yes':
 
     def configure(self, opts, changes):
         if opts['saml2'] != 'yes':
@@ -493,7 +518,11 @@ class Installer(ProviderInstaller):
                   'idp certificate file': cert.cert,
                   'idp key file': cert.key,
                   'idp nameid salt': uuid.uuid4().hex,
                   '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
         po.save_plugin_config(config)
 
         # Update global config to add login plugin