Move accessory functions to a generic tools module
authorSimo Sorce <simo@redhat.com>
Wed, 9 Apr 2014 18:02:08 +0000 (14:02 -0400)
committerSimo Sorce <simo@redhat.com>
Fri, 11 Apr 2014 21:25:54 +0000 (17:25 -0400)
This will allow to easly share the module with install tools, without the
need to install server side modules in clients

Signed-off-by: Simo Sorce <simo@redhat.com>
ipsilon/providers/saml2/certs.py [deleted file]
ipsilon/providers/saml2/metadata.py [deleted file]
ipsilon/providers/saml2/provider.py
ipsilon/providers/saml2idp.py
ipsilon/tools/__init__.py [new file with mode: 0644]
ipsilon/tools/certs.py [new file with mode: 0755]
ipsilon/tools/saml2metadata.py [new file with mode: 0755]
setup.py

diff --git a/ipsilon/providers/saml2/certs.py b/ipsilon/providers/saml2/certs.py
deleted file mode 100755 (executable)
index dc08e08..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (C) 2014  Simo Sorce <simo@redhat.com>
-#
-# see file 'COPYING' for use and warranty information
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-from subprocess import Popen
-import os
-import string
-
-
-class Certificate(object):
-
-    def __init__(self, path=None):
-        self.subject = None
-        self.path = path
-        self.key = None
-        self.cert = None
-
-    def generate(self, prefix, subject):
-        self.key = '%s.key' % prefix
-        self.cert = '%s.pem' % prefix
-        self.subject = '/CN=%s' % subject
-        command = ['openssl',
-                   'req', '-x509', '-batch', '-days', '1825',
-                   '-newkey', 'rsa:2048', '-nodes', '-subj', self.subject,
-                   '-keyout', os.path.join(self.path, self.key),
-                   '-out', os.path.join(self.path, self.cert)]
-        proc = Popen(command)
-        proc.wait()
-
-    def get_cert(self):
-        if not self.cert:
-            raise NameError('Invalid certificate name: %s' % self.cert)
-        with open(os.path.join(self.path, self.cert), 'r') as f:
-            cert = f.readlines()
-
-        #poor man stripping of BEGIN/END lines
-        if cert[0] == '-----BEGIN CERTIFICATE-----\n':
-            cert = cert[1:]
-        if cert[-1] == '-----END CERTIFICATE-----\n':
-            cert = cert[:-1]
-
-        return string.join(cert)
diff --git a/ipsilon/providers/saml2/metadata.py b/ipsilon/providers/saml2/metadata.py
deleted file mode 100755 (executable)
index 0effd4c..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (C) 2014  Simo Sorce <simo@redhat.com>
-#
-# see file 'COPYING' for use and warranty information
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-from ipsilon.providers.saml2.certs import Certificate
-from lxml import etree
-import lasso
-
-
-EDESC = '{%s}EntityDescriptor' % lasso.SAML2_METADATA_HREF
-NSMAP = {
-    'md': lasso.SAML2_METADATA_HREF,
-    'ds': lasso.DS_HREF
-}
-
-IDPDESC = 'IDPSSODescriptor'
-SPDESC = 'SPSSODescriptor'
-
-IDP_ROLE = 'idp'
-SP_ROLE = 'sp'
-
-SSO_SERVICE = 'SingleSignOnService'
-LOGOUT_SERVICE = 'SingleLogoutService'
-ASSERTION_SERVICE = 'AssertionConsumerService'
-
-
-def mdElement(_parent, _tag, **kwargs):
-    tag = '{%s}%s' % (lasso.SAML2_METADATA_HREF, _tag)
-    return etree.SubElement(_parent, tag, **kwargs)
-
-
-def dsElement(_parent, _tag, **kwargs):
-    tag = '{%s}%s' % (lasso.DS_HREF, _tag)
-    return etree.SubElement(_parent, tag, **kwargs)
-
-
-class Metadata(object):
-
-    def __init__(self, role=None):
-        self.root = etree.Element(EDESC, nsmap=NSMAP)
-        self.entityid = None
-        self.role = None
-        self.set_role(role)
-
-    def set_entity_id(self, url):
-        self.entityid = url
-        self.root.set('entityID', url)
-
-    def set_role(self, role):
-        if role is None:
-            return
-        elif role == IDP_ROLE:
-            description = IDPDESC
-        elif role == SP_ROLE:
-            description = SPDESC
-        else:
-            raise ValueError('invalid role: %s' % role)
-        self.role = mdElement(self.root, description)
-        self.role.set('protocolSupportEnumeration', lasso.SAML2_PROTOCOL_HREF)
-        return self.role
-
-    def add_cert(self, certdata, use):
-        desc = mdElement(self.role, 'KeyDescriptor')
-        desc.set('use', use)
-        info = dsElement(desc, 'KeyInfo')
-        data = dsElement(info, 'X509Data')
-        cert = dsElement(data, 'X509Certificate')
-        cert.text = certdata
-
-    def add_certs(self, signcert=None, enccert=None):
-        if signcert:
-            self.add_cert(signcert.get_cert(), 'signing')
-        if enccert:
-            self.add_cert(enccert.get_cert(), 'encryption')
-
-    def add_service(self, svctype, binding, location):
-        svc = mdElement(self.role, svctype)
-        svc.set('Binding', binding)
-        svc.set('Location', location)
-
-    def add_allowed_name_format(self, name_format):
-        nameidfmt = mdElement(self.role, 'NameIDFormat')
-        nameidfmt.text = name_format
-
-    def output(self, path):
-        data = etree.tostring(self.root, xml_declaration=True,
-                              encoding='UTF-8', pretty_print=True)
-        with open(path, 'w') as f:
-            f.write(data)
-
-
-if __name__ == '__main__':
-    from ipsilon.providers.saml2.provider import NAMEID_MAP
-    import tempfile
-    import shutil
-    import os
-
-    tmpdir = tempfile.mkdtemp()
-
-    try:
-        # Test IDP generation
-        sign_cert = Certificate(tmpdir)
-        sign_cert.generate('idp-signing-cert', 'idp.ipsilon.example.com')
-        enc_cert = Certificate(tmpdir)
-        enc_cert.generate('idp-encryption-cert', 'idp.ipsilon.example.com')
-        idp = Metadata()
-        idp.set_entity_id('https://ipsilon.example.com/idp/metadata')
-        idp.set_role(IDP_ROLE)
-        idp.add_certs(sign_cert, enc_cert)
-        idp.add_service(SSO_SERVICE, lasso.SAML2_METADATA_BINDING_POST,
-                        'https://ipsilon.example.com/idp/saml2/POST')
-        idp.add_service(SSO_SERVICE, lasso.SAML2_METADATA_BINDING_REDIRECT,
-                        'https://ipsilon.example.com/idp/saml2/Redirect')
-        for k in NAMEID_MAP:
-            idp.add_allowed_name_format(NAMEID_MAP[k])
-        md_file = os.path.join(tmpdir, 'metadata.xml')
-        idp.output(md_file)
-        with open(md_file) as fd:
-            text = fd.read()
-        print '==================== IDP ===================='
-        print text
-        print '============================================='
-
-        # Test SP generation
-        sign_cert = Certificate(tmpdir)
-        sign_cert.generate('sp-signing-cert', 'sp.ipsilon.example.com')
-        sp = Metadata()
-        sp.set_entity_id('https://ipsilon.example.com/samlsp/metadata')
-        sp.set_role(SP_ROLE)
-        sp.add_certs(sign_cert)
-        sp.add_service(LOGOUT_SERVICE, lasso.SAML2_METADATA_BINDING_REDIRECT,
-                       'https://ipsilon.example.com/samlsp/logout')
-        sp.add_service(ASSERTION_SERVICE, lasso.SAML2_METADATA_BINDING_POST,
-                       'https://ipsilon.example.com/samlsp/postResponse')
-        md_file = os.path.join(tmpdir, 'metadata.xml')
-        sp.output(md_file)
-        with open(md_file) as fd:
-            text = fd.read()
-        print '===================== SP ===================='
-        print text
-        print '============================================='
-
-    finally:
-        shutil.rmtree(tmpdir)
index 73ff005..7d47363 100755 (executable)
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from ipsilon.providers.common import ProviderException
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from ipsilon.providers.common import ProviderException
+from ipsilon.tools.saml2metadata import SAML2_NAMEID_MAP
 import cherrypy
 import lasso
 
 
 import cherrypy
 import lasso
 
 
-NAMEID_MAP = {
-    'email': lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL,
-    'encrypted': lasso.SAML2_NAME_IDENTIFIER_FORMAT_ENCRYPTED,
-    'entity': lasso.SAML2_NAME_IDENTIFIER_FORMAT_ENTITY,
-    'kerberos': lasso.SAML2_NAME_IDENTIFIER_FORMAT_KERBEROS,
-    'persistent': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
-    'transient': lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT,
-    'unspecified': lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED,
-    'windows': lasso.SAML2_NAME_IDENTIFIER_FORMAT_WINDOWS,
-    'x509': lasso.SAML2_NAME_IDENTIFIER_FORMAT_X509,
-}
-
-
 class InvalidProviderId(ProviderException):
 
     def __init__(self, code):
 class InvalidProviderId(ProviderException):
 
     def __init__(self, code):
@@ -129,14 +117,14 @@ class ServiceProvider(object):
     def get_valid_nameid(self, nip):
         self._debug('Requested NameId [%s]' % (nip.format,))
         if nip.format is None:
     def get_valid_nameid(self, nip):
         self._debug('Requested NameId [%s]' % (nip.format,))
         if nip.format is None:
-            return NAMEID_MAP[self.default_nameid]
+            return SAML2_NAMEID_MAP[self.default_nameid]
         elif nip.format == lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED:
         elif nip.format == lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED:
-            return NAMEID_MAP[self.default_nameid]
+            return SAML2_NAMEID_MAP[self.default_nameid]
         else:
             allowed = self.allowed_nameids
             self._debug('Allowed NameIds %s' % (repr(allowed)))
             for nameid in allowed:
         else:
             allowed = self.allowed_nameids
             self._debug('Allowed NameIds %s' % (repr(allowed)))
             for nameid in allowed:
-                if nip.format == NAMEID_MAP[nameid]:
+                if nip.format == SAML2_NAMEID_MAP[nameid]:
                     return nip.format
         raise NameIdNotAllowed(nip.format)
 
                     return nip.format
         raise NameIdNotAllowed(nip.format)
 
index 1922c53..87cc7f6 100755 (executable)
@@ -21,9 +21,9 @@ from ipsilon.providers.common import ProviderBase, ProviderPageBase
 from ipsilon.providers.common import FACILITY
 from ipsilon.providers.saml2.auth import AuthenticateRequest
 from ipsilon.providers.saml2.admin import AdminPage
 from ipsilon.providers.common import FACILITY
 from ipsilon.providers.saml2.auth import AuthenticateRequest
 from ipsilon.providers.saml2.admin import AdminPage
-from ipsilon.providers.saml2.certs import Certificate
 from ipsilon.providers.saml2.provider import IdentityProvider
 from ipsilon.providers.saml2.provider import IdentityProvider
-from ipsilon.providers.saml2 import metadata
+from ipsilon.tools.certs import Certificate
+from ipsilon.tools import saml2metadata as metadata
 from ipsilon.util.user import UserSession
 from ipsilon.util.plugin import PluginObject
 import cherrypy
 from ipsilon.util.user import UserSession
 from ipsilon.util.plugin import PluginObject
 import cherrypy
diff --git a/ipsilon/tools/__init__.py b/ipsilon/tools/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ipsilon/tools/certs.py b/ipsilon/tools/certs.py
new file mode 100755 (executable)
index 0000000..dc08e08
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2014  Simo Sorce <simo@redhat.com>
+#
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from subprocess import Popen
+import os
+import string
+
+
+class Certificate(object):
+
+    def __init__(self, path=None):
+        self.subject = None
+        self.path = path
+        self.key = None
+        self.cert = None
+
+    def generate(self, prefix, subject):
+        self.key = '%s.key' % prefix
+        self.cert = '%s.pem' % prefix
+        self.subject = '/CN=%s' % subject
+        command = ['openssl',
+                   'req', '-x509', '-batch', '-days', '1825',
+                   '-newkey', 'rsa:2048', '-nodes', '-subj', self.subject,
+                   '-keyout', os.path.join(self.path, self.key),
+                   '-out', os.path.join(self.path, self.cert)]
+        proc = Popen(command)
+        proc.wait()
+
+    def get_cert(self):
+        if not self.cert:
+            raise NameError('Invalid certificate name: %s' % self.cert)
+        with open(os.path.join(self.path, self.cert), 'r') as f:
+            cert = f.readlines()
+
+        #poor man stripping of BEGIN/END lines
+        if cert[0] == '-----BEGIN CERTIFICATE-----\n':
+            cert = cert[1:]
+        if cert[-1] == '-----END CERTIFICATE-----\n':
+            cert = cert[:-1]
+
+        return string.join(cert)
diff --git a/ipsilon/tools/saml2metadata.py b/ipsilon/tools/saml2metadata.py
new file mode 100755 (executable)
index 0000000..fc2e02c
--- /dev/null
@@ -0,0 +1,171 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2014  Simo Sorce <simo@redhat.com>
+#
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from ipsilon.tools.certs import Certificate
+from lxml import etree
+import lasso
+
+
+SAML2_NAMEID_MAP = {
+    'email': lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL,
+    'encrypted': lasso.SAML2_NAME_IDENTIFIER_FORMAT_ENCRYPTED,
+    'entity': lasso.SAML2_NAME_IDENTIFIER_FORMAT_ENTITY,
+    'kerberos': lasso.SAML2_NAME_IDENTIFIER_FORMAT_KERBEROS,
+    'persistent': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
+    'transient': lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT,
+    'unspecified': lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED,
+    'windows': lasso.SAML2_NAME_IDENTIFIER_FORMAT_WINDOWS,
+    'x509': lasso.SAML2_NAME_IDENTIFIER_FORMAT_X509,
+}
+
+
+EDESC = '{%s}EntityDescriptor' % lasso.SAML2_METADATA_HREF
+NSMAP = {
+    'md': lasso.SAML2_METADATA_HREF,
+    'ds': lasso.DS_HREF
+}
+
+IDPDESC = 'IDPSSODescriptor'
+SPDESC = 'SPSSODescriptor'
+
+IDP_ROLE = 'idp'
+SP_ROLE = 'sp'
+
+SSO_SERVICE = 'SingleSignOnService'
+LOGOUT_SERVICE = 'SingleLogoutService'
+ASSERTION_SERVICE = 'AssertionConsumerService'
+
+
+def mdElement(_parent, _tag, **kwargs):
+    tag = '{%s}%s' % (lasso.SAML2_METADATA_HREF, _tag)
+    return etree.SubElement(_parent, tag, **kwargs)
+
+
+def dsElement(_parent, _tag, **kwargs):
+    tag = '{%s}%s' % (lasso.DS_HREF, _tag)
+    return etree.SubElement(_parent, tag, **kwargs)
+
+
+class Metadata(object):
+
+    def __init__(self, role=None):
+        self.root = etree.Element(EDESC, nsmap=NSMAP)
+        self.entityid = None
+        self.role = None
+        self.set_role(role)
+
+    def set_entity_id(self, url):
+        self.entityid = url
+        self.root.set('entityID', url)
+
+    def set_role(self, role):
+        if role is None:
+            return
+        elif role == IDP_ROLE:
+            description = IDPDESC
+        elif role == SP_ROLE:
+            description = SPDESC
+        else:
+            raise ValueError('invalid role: %s' % role)
+        self.role = mdElement(self.root, description)
+        self.role.set('protocolSupportEnumeration', lasso.SAML2_PROTOCOL_HREF)
+        return self.role
+
+    def add_cert(self, certdata, use):
+        desc = mdElement(self.role, 'KeyDescriptor')
+        desc.set('use', use)
+        info = dsElement(desc, 'KeyInfo')
+        data = dsElement(info, 'X509Data')
+        cert = dsElement(data, 'X509Certificate')
+        cert.text = certdata
+
+    def add_certs(self, signcert=None, enccert=None):
+        if signcert:
+            self.add_cert(signcert.get_cert(), 'signing')
+        if enccert:
+            self.add_cert(enccert.get_cert(), 'encryption')
+
+    def add_service(self, svctype, binding, location):
+        svc = mdElement(self.role, svctype)
+        svc.set('Binding', binding)
+        svc.set('Location', location)
+
+    def add_allowed_name_format(self, name_format):
+        nameidfmt = mdElement(self.role, 'NameIDFormat')
+        nameidfmt.text = name_format
+
+    def output(self, path):
+        data = etree.tostring(self.root, xml_declaration=True,
+                              encoding='UTF-8', pretty_print=True)
+        with open(path, 'w') as f:
+            f.write(data)
+
+
+if __name__ == '__main__':
+    import tempfile
+    import shutil
+    import os
+
+    tmpdir = tempfile.mkdtemp()
+
+    try:
+        # Test IDP generation
+        sign_cert = Certificate(tmpdir)
+        sign_cert.generate('idp-signing-cert', 'idp.ipsilon.example.com')
+        enc_cert = Certificate(tmpdir)
+        enc_cert.generate('idp-encryption-cert', 'idp.ipsilon.example.com')
+        idp = Metadata()
+        idp.set_entity_id('https://ipsilon.example.com/idp/metadata')
+        idp.set_role(IDP_ROLE)
+        idp.add_certs(sign_cert, enc_cert)
+        idp.add_service(SSO_SERVICE, lasso.SAML2_METADATA_BINDING_POST,
+                        'https://ipsilon.example.com/idp/saml2/POST')
+        idp.add_service(SSO_SERVICE, lasso.SAML2_METADATA_BINDING_REDIRECT,
+                        'https://ipsilon.example.com/idp/saml2/Redirect')
+        for k in SAML2_NAMEID_MAP:
+            idp.add_allowed_name_format(SAML2_NAMEID_MAP[k])
+        md_file = os.path.join(tmpdir, 'metadata.xml')
+        idp.output(md_file)
+        with open(md_file) as fd:
+            text = fd.read()
+        print '==================== IDP ===================='
+        print text
+        print '============================================='
+
+        # Test SP generation
+        sign_cert = Certificate(tmpdir)
+        sign_cert.generate('sp-signing-cert', 'sp.ipsilon.example.com')
+        sp = Metadata()
+        sp.set_entity_id('https://ipsilon.example.com/samlsp/metadata')
+        sp.set_role(SP_ROLE)
+        sp.add_certs(sign_cert)
+        sp.add_service(LOGOUT_SERVICE, lasso.SAML2_METADATA_BINDING_REDIRECT,
+                       'https://ipsilon.example.com/samlsp/logout')
+        sp.add_service(ASSERTION_SERVICE, lasso.SAML2_METADATA_BINDING_POST,
+                       'https://ipsilon.example.com/samlsp/postResponse')
+        md_file = os.path.join(tmpdir, 'metadata.xml')
+        sp.output(md_file)
+        with open(md_file) as fd:
+            text = fd.read()
+        print '===================== SP ===================='
+        print text
+        print '============================================='
+
+    finally:
+        shutil.rmtree(tmpdir)
index 846698b..3de7faa 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -27,7 +27,8 @@ setup(
     version = '0.1',
     license = 'GPLv3+',
     packages = ['ipsilon', 'ipsilon.admin', 'ipsilon.login', 'ipsilon.util',
     version = '0.1',
     license = 'GPLv3+',
     packages = ['ipsilon', 'ipsilon.admin', 'ipsilon.login', 'ipsilon.util',
-                'ipsilon.providers', 'ipsilon.providers.saml2'],
+                'ipsilon.providers', 'ipsilon.providers.saml2',
+                'ipsilon.tools'],
     data_files = [('share/man/man7', ["man/ipsilon.7"]),
                   ('share/doc/ipsilon', ['COPYING']),
                   ('share/doc/ipsilon/examples', ['examples/ipsilon.conf',
     data_files = [('share/man/man7', ["man/ipsilon.7"]),
                   ('share/doc/ipsilon', ['COPYING']),
                   ('share/doc/ipsilon/examples', ['examples/ipsilon.conf',