Add Metadata Generator helper class
[cascardo/ipsilon.git] / ipsilon / providers / saml2idp.py
old mode 100755 (executable)
new mode 100644 (file)
index 3f3ab87..298a205
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-#
 # Copyright (C) 2014  Simo Sorce <simo@redhat.com>
 #
 # see file 'COPYING' for use and warranty information
 # Copyright (C) 2014  Simo Sorce <simo@redhat.com>
 #
 # see file 'COPYING' for use and warranty information
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from ipsilon.providers.common import ProviderBase, ProviderPageBase
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from ipsilon.providers.common import ProviderBase, ProviderPageBase
-from ipsilon.providers.common import FACILITY
 from ipsilon.providers.saml2.auth import AuthenticateRequest
 from ipsilon.providers.saml2.auth import AuthenticateRequest
-from ipsilon.providers.saml2.admin import AdminPage
+from ipsilon.providers.saml2.admin import Saml2AdminPage
 from ipsilon.providers.saml2.provider import IdentityProvider
 from ipsilon.tools.certs import Certificate
 from ipsilon.tools import saml2metadata as metadata
 from ipsilon.tools import files
 from ipsilon.util.user import UserSession
 from ipsilon.util.plugin import PluginObject
 from ipsilon.providers.saml2.provider import IdentityProvider
 from ipsilon.tools.certs import Certificate
 from ipsilon.tools import saml2metadata as metadata
 from ipsilon.tools import files
 from ipsilon.util.user import UserSession
 from ipsilon.util.plugin import PluginObject
+from ipsilon.util import config as pconfig
 import cherrypy
 import lasso
 import os
 import cherrypy
 import lasso
 import os
@@ -60,8 +58,8 @@ class Continue(AuthenticateRequest):
 
         session = UserSession()
         user = session.get_user()
 
         session = UserSession()
         user = session.get_user()
-        session.nuke_data('login', 'Return')
-        self.stage = session.get_data('saml2', 'stage')
+        transdata = self.trans.retrieve()
+        self.stage = transdata['saml2_stage']
 
         if user.is_anonymous:
             self._debug("User is marked anonymous?!")
 
         if user.is_anonymous:
             self._debug("User is marked anonymous?!")
@@ -70,11 +68,11 @@ class Continue(AuthenticateRequest):
 
         self._debug('Continue auth for %s' % user.name)
 
 
         self._debug('Continue auth for %s' % user.name)
 
-        dump = session.get_data('saml2', 'Request')
-        if not dump:
+        if 'saml2_request' not in transdata:
             self._debug("Couldn't find Request dump?!")
             # TODO: Return to SP with auth failed error
             raise cherrypy.HTTPError(400)
             self._debug("Couldn't find Request dump?!")
             # TODO: Return to SP with auth failed error
             raise cherrypy.HTTPError(400)
+        dump = transdata['saml2_request']
 
         try:
             login = self.cfg.idp.get_login_handler(dump)
 
         try:
             login = self.cfg.idp.get_login_handler(dump)
@@ -113,81 +111,56 @@ class SAML2(ProviderPageBase):
     def __init__(self, *args, **kwargs):
         super(SAML2, self).__init__(*args, **kwargs)
         self.metadata = Metadata(*args, **kwargs)
     def __init__(self, *args, **kwargs):
         super(SAML2, self).__init__(*args, **kwargs)
         self.metadata = Metadata(*args, **kwargs)
-
-        # Init IDP data
-        try:
-            self.cfg.idp = IdentityProvider(self.cfg)
-        except Exception, e:  # pylint: disable=broad-except
-            self._debug('Failed to init SAML2 provider: %r' % e)
-            return
-
-        # Import all known applications
-        data = self.cfg.get_data()
-        for idval in data:
-            sp = data[idval]
-            if 'type' not in sp or sp['type'] != 'SP':
-                continue
-            if 'name' not in sp or 'metadata' not in sp:
-                continue
-            try:
-                self.cfg.idp.add_provider(sp)
-            except Exception, e:  # pylint: disable=broad-except
-                self._debug('Failed to add SP %s: %r' % (sp['name'], e))
-
         self.SSO = SSO(*args, **kwargs)
 
 
 class IdpProvider(ProviderBase):
 
         self.SSO = SSO(*args, **kwargs)
 
 
 class IdpProvider(ProviderBase):
 
-    def __init__(self):
-        super(IdpProvider, self).__init__('saml2', 'saml2')
+    def __init__(self, *pargs):
+        super(IdpProvider, self).__init__('saml2', 'saml2', *pargs)
+        self.admin = None
         self.page = None
         self.idp = None
         self.description = """
 Provides SAML 2.0 authentication infrastructure. """
 
         self.page = None
         self.idp = None
         self.description = """
 Provides SAML 2.0 authentication infrastructure. """
 
-        self._options = {
-            'idp storage path': [
-                """ Path to data storage accessible by the IdP """,
-                'string',
-                '/var/lib/ipsilon/saml2'
-            ],
-            'idp metadata file': [
-                """ The IdP Metadata file genearated at install time. """,
-                'string',
-                'metadata.xml'
-            ],
-            'idp certificate file': [
-                """ The IdP PEM Certificate genearated at install time. """,
-                'string',
-                'certificate.pem'
-            ],
-            'idp key file': [
-                """ The IdP Certificate Key genearated at install time. """,
-                'string',
-                'certificate.key'
-            ],
-            'allow self registration': [
-                """ Allow authenticated users to register applications. """,
-                'boolean',
-                True
-            ],
-            'default allowed nameids': [
-                """Default Allowed NameIDs for Service Providers. """,
-                'list',
-                ['persistent', 'transient', 'email', 'kerberos', 'x509']
-            ],
-            'default nameid': [
-                """Default NameID used by Service Providers. """,
-                'string',
-                'persistent'
-            ],
-            'default email domain': [
-                """Default email domain, for users missing email property.""",
-                'string',
-                'example.com'
-            ]
-        }
+        self.new_config(
+            self.name,
+            pconfig.String(
+                'idp storage path',
+                'Path to data storage accessible by the IdP.',
+                '/var/lib/ipsilon/saml2'),
+            pconfig.String(
+                'idp metadata file',
+                'The IdP Metadata file genearated at install time.',
+                'metadata.xml'),
+            pconfig.String(
+                'idp certificate file',
+                'The IdP PEM Certificate genearated at install time.',
+                'certificate.pem'),
+            pconfig.String(
+                'idp key file',
+                'The IdP Certificate Key genearated at install time.',
+                'certificate.key'),
+            pconfig.Condition(
+                'allow self registration',
+                'Allow authenticated users to register applications.',
+                True),
+            pconfig.Choice(
+                'default allowed nameids',
+                'Default Allowed NameIDs for Service Providers.',
+                metadata.SAML2_NAMEID_MAP.keys(),
+                ['persistent', 'transient', 'email', 'kerberos', 'x509']),
+            pconfig.Pick(
+                'default nameid',
+                'Default NameID used by Service Providers.',
+                metadata.SAML2_NAMEID_MAP.keys(),
+                'persistent'),
+            pconfig.String(
+                'default email domain',
+                'Used for users missing the email property.',
+                'example.com'),
+        )
         if cherrypy.config.get('debug', False):
             import logging
             import sys
         if cherrypy.config.get('debug', False):
             import logging
             import sys
@@ -232,16 +205,70 @@ Provides SAML 2.0 authentication infrastructure. """
         return self.get_config_value('default email domain')
 
     def get_tree(self, site):
         return self.get_config_value('default email domain')
 
     def get_tree(self, site):
+        self.idp = self.init_idp()
         self.page = SAML2(site, self)
         self.page = SAML2(site, self)
-        self.admin = AdminPage(site, self)
+        self.admin = Saml2AdminPage(site, self)
         return self.page
 
         return self.page
 
+    def init_idp(self):
+        idp = None
+        # Init IDP data
+        try:
+            idp = IdentityProvider(self)
+        except Exception, e:  # pylint: disable=broad-except
+            self._debug('Failed to init SAML2 provider: %r' % e)
+            return None
+
+        # Import all known applications
+        data = self.get_data()
+        for idval in data:
+            sp = data[idval]
+            if 'type' not in sp or sp['type'] != 'SP':
+                continue
+            if 'name' not in sp or 'metadata' not in sp:
+                continue
+            try:
+                idp.add_provider(sp)
+            except Exception, e:  # pylint: disable=broad-except
+                self._debug('Failed to add SP %s: %r' % (sp['name'], e))
+
+        return idp
+
+    def on_enable(self):
+        super(IdpProvider, self).on_enable()
+        self.idp = self.init_idp()
+        if hasattr(self, 'admin'):
+            if self.admin:
+                self.admin.add_sps()
+
+
+class IdpMetadataGenerator(object):
+
+    def __init__(self, url, idp_cert):
+        self.meta = metadata.Metadata(metadata.IDP_ROLE)
+        self.meta.set_entity_id('%s/saml2/metadata' % url)
+        self.meta.add_certs(idp_cert, idp_cert)
+        self.meta.add_service(metadata.SAML2_SERVICE_MAP['sso-post'],
+                              '%s/saml2/SSO/POST' % url)
+        self.meta.add_service(metadata.SAML2_SERVICE_MAP['sso-redirect'],
+                              '%s/saml2/SSO/Redirect' % url)
+        self.meta.add_allowed_name_format(
+            lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT)
+        self.meta.add_allowed_name_format(
+            lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT)
+        self.meta.add_allowed_name_format(
+            lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL)
+
+    def output(self, path=None):
+        return self.meta.output(path)
+
 
 class Installer(object):
 
 
 class Installer(object):
 
-    def __init__(self):
+    def __init__(self, *pargs):
         self.name = 'saml2'
         self.ptype = 'provider'
         self.name = 'saml2'
         self.ptype = 'provider'
+        self.pargs = pargs
 
     def install_args(self, group):
         group.add_argument('--saml2', choices=['yes', 'no'], default='yes',
 
     def install_args(self, group):
         group.add_argument('--saml2', choices=['yes', 'no'], default='yes',
@@ -261,39 +288,31 @@ class Installer(object):
         cert.generate('idp', opts['hostname'])
 
         # Generate Idp Metadata
         cert.generate('idp', opts['hostname'])
 
         # Generate Idp Metadata
-        url = 'https://' + opts['hostname'] + '/' + opts['instance'] + '/saml2'
-        meta = metadata.Metadata(metadata.IDP_ROLE)
-        meta.set_entity_id(url + '/metadata')
-        meta.add_certs(cert, cert)
-        meta.add_service(metadata.SAML2_SERVICE_MAP['sso-post'],
-                         url + 'SSO/POST')
-        meta.add_service(metadata.SAML2_SERVICE_MAP['sso-redirect'],
-                         url + 'SSO/Redirect')
-
-        meta.add_allowed_name_format(
-            lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT)
-        meta.add_allowed_name_format(
-            lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT)
-        meta.add_allowed_name_format(
-            lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL)
+        proto = 'https'
+        if opts['secure'].lower() == 'no':
+            proto = 'http'
+        url = '%s://%s/%s' % (proto, opts['hostname'], opts['instance'])
+        meta = IdpMetadataGenerator(url, cert)
         if 'krb' in opts and opts['krb'] == 'yes':
         if 'krb' in opts and opts['krb'] == 'yes':
-            meta.add_allowed_name_format(
+            meta.meta.add_allowed_name_format(
                 lasso.SAML2_NAME_IDENTIFIER_FORMAT_KERBEROS)
 
         meta.output(os.path.join(path, 'metadata.xml'))
 
         # Add configuration data to database
                 lasso.SAML2_NAME_IDENTIFIER_FORMAT_KERBEROS)
 
         meta.output(os.path.join(path, 'metadata.xml'))
 
         # Add configuration data to database
-        po = PluginObject()
+        po = PluginObject(*self.pargs)
         po.name = 'saml2'
         po.wipe_data()
         po.name = 'saml2'
         po.wipe_data()
-
-        po.wipe_config_values(FACILITY)
+        po.wipe_config_values()
         config = {'idp storage path': path,
                   'idp metadata file': 'metadata.xml',
                   'idp certificate file': cert.cert,
                   'idp key file': cert.key}
         config = {'idp storage path': path,
                   'idp metadata file': 'metadata.xml',
                   'idp certificate file': cert.cert,
                   'idp key file': cert.key}
-        po.set_config(config)
-        po.save_plugin_config(FACILITY)
+        po.save_plugin_config(config)
+
+        # Update global config to add login plugin
+        po.is_enabled = True
+        po.save_enabled_state()
 
         # Fixup permissions so only the ipsilon user can read these files
         files.fix_user_dirs(path, opts['system_user'])
 
         # Fixup permissions so only the ipsilon user can read these files
         files.fix_user_dirs(path, opts['system_user'])