#!/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/>.
+# Copyright (C) 2014 Ipsilon project Contributors, for license see COPYING
+import datetime
from ipsilon.tools.certs import Certificate
from lxml import etree
import lasso
'x509': lasso.SAML2_NAME_IDENTIFIER_FORMAT_X509,
}
+SAML2_SERVICE_MAP = {
+ 'sso-post': ('SingleSignOnService',
+ lasso.SAML2_METADATA_BINDING_POST),
+ 'sso-redirect': ('SingleSignOnService',
+ lasso.SAML2_METADATA_BINDING_REDIRECT),
+ 'sso-soap': ('SingleSignOnService',
+ lasso.SAML2_METADATA_BINDING_SOAP),
+ 'logout-redirect': ('SingleLogoutService',
+ lasso.SAML2_METADATA_BINDING_REDIRECT),
+ 'slo-soap': ('SingleLogoutService',
+ lasso.SAML2_METADATA_BINDING_SOAP),
+ 'response-post': ('AssertionConsumerService',
+ lasso.SAML2_METADATA_BINDING_POST)
+}
EDESC = '{%s}EntityDescriptor' % lasso.SAML2_METADATA_HREF
NSMAP = {
IDP_ROLE = 'idp'
SP_ROLE = 'sp'
-SSO_SERVICE = 'SingleSignOnService'
-LOGOUT_SERVICE = 'SingleLogoutService'
-ASSERTION_SERVICE = 'AssertionConsumerService'
+
+# Expire metadata weekly by default
+MIN_EXP_DEFAULT = 7
def mdElement(_parent, _tag, **kwargs):
class Metadata(object):
- def __init__(self, role=None):
+ def __init__(self, role=None, expiration=None):
self.root = etree.Element(EDESC, nsmap=NSMAP)
self.entityid = None
self.role = None
self.set_role(role)
+ self.set_expiration(expiration)
def set_entity_id(self, url):
self.entityid = url
self.role.set('protocolSupportEnumeration', lasso.SAML2_PROTOCOL_HREF)
return self.role
+ def set_expiration(self, exp):
+ if exp is None:
+ self.root.set('cacheDuration', "P%dD" % (MIN_EXP_DEFAULT))
+ return
+ elif isinstance(exp, datetime.date):
+ d = datetime.datetime.combine(exp, datetime.date.min.time())
+ elif isinstance(exp, datetime.datetime):
+ d = exp
+ elif isinstance(exp, datetime.timedelta):
+ d = datetime.datetime.now() + exp
+ else:
+ raise TypeError('Invalid expiration date type')
+
+ self.root.set('validUntil', d.isoformat())
+
def add_cert(self, certdata, use):
desc = mdElement(self.role, 'KeyDescriptor')
desc.set('use', use)
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)
+ def add_service(self, service, location, **kwargs):
+ svc = mdElement(self.role, service[0])
+ svc.set('Binding', service[1])
svc.set('Location', location)
+ for key, value in kwargs.iteritems():
+ svc.set(key, value)
def add_allowed_name_format(self, name_format):
nameidfmt = mdElement(self.role, 'NameIDFormat')
nameidfmt.text = name_format
- def output(self, path):
+ def output(self, path=None):
data = etree.tostring(self.root, xml_declaration=True,
encoding='UTF-8', pretty_print=True)
- with open(path, 'w') as f:
- f.write(data)
+ if path is None:
+ return data
+ else:
+ with open(path, 'w') as f:
+ f.write(data)
if __name__ == '__main__':
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,
+ idp.add_service(SAML2_SERVICE_MAP['sso-post'],
'https://ipsilon.example.com/idp/saml2/POST')
- idp.add_service(SSO_SERVICE, lasso.SAML2_METADATA_BINDING_REDIRECT,
+ idp.add_service(SAML2_SERVICE_MAP['sso-redirect'],
'https://ipsilon.example.com/idp/saml2/Redirect')
for k in SAML2_NAMEID_MAP:
idp.add_allowed_name_format(SAML2_NAMEID_MAP[k])
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,
+ sp.add_service(SAML2_SERVICE_MAP['logout-redirect'],
'https://ipsilon.example.com/samlsp/logout')
- sp.add_service(ASSERTION_SERVICE, lasso.SAML2_METADATA_BINDING_POST,
+ sp.add_service(SAML2_SERVICE_MAP['response-post'],
'https://ipsilon.example.com/samlsp/postResponse')
md_file = os.path.join(tmpdir, 'metadata.xml')
sp.output(md_file)