Skip to main content

SAML2 support based on PyXB

Project description

This package provides support for SAML2 based on pyxb.

pyxb (http://pypi.python.org/pypi/PyXB) generates a Python class collection for an XML schema and provides means to convert between associated Python instances and xml documents. It is used to generate and parse SAML2 messages.

The package adds support for digital signatures and SAML2 bindings and metadata management.

Dependencies

PyXB

This package’s version has been tested with version 1.1.4 of pyxb. It may not work with other versions.

Class collections generated by pyxb tend to be very version dependent. Thus, they must usually be regenerated when the pyxb version has changed.

The current package’s version mostly uses class collections from the wssplat and saml20 bundles of pyxb. Thus, they have a good chance to be updated together with pyxb. However, the pyxb saml2 bundle lacks support for the so called SAML2 context classes. This package contains class collections generated with pyxb==1.1.4. They need to be regenerated if the pyxb version changes (and you use those classes – which is not very likely). The gen.sh script in subpackage pyxb can provide clues how to regenerate them.

A bug in pyxb 1.1.4 (http://sourceforge.net/apps/trac/pyxb/ticket/130#comment:4) prevents its installation via package managers (such as easy_install, pip, zc.buildout) for Python versions without os.path.relpath (this applies e.g. to Python 2.4). I work around this problem by downloading the pyxb source, apply patch http://sourceforge.net/apps/trac/pyxb/attachment/ticket/130/pyxb_setup.patch to it and then egg install the result. After this, dm.saml2 can be installed normally.

dm.xmlsec.binding

Check its installation notes should you face related installation problems.

Example

This section provides a simple example on how to create, sign and verify an assertion with this package.

Always ensure, the xmlsec library is initialized. Otherwise, it signing/ signature verification can fail with dubious messages.

>>> import dm.xmlsec.binding as xmlsec
>>> xmlsec.initialize()

We now build an assertion as Python object.

>>> import pyxb.binding.datatypes as xs
>>> from dm.saml2.pyxb.assertion import (NameID, Assertion, Subject, \
...      AuthnStatement, AttributeStatement, AuthnContext, AuthnContextClassRef, \
...      Attribute, AttributeValue, \
...      CreateFromDocument
...                                      )
>>> from datetime import datetime
>>>
>>> issuer = NameID('http://bfd.de')
>>> ass = Assertion(issuer)
>>>
>>> subject = Subject(NameID('Dieter Maurer'))
>>>
>>> ass.Subject = subject
>>>
>>> authn = AuthnStatement(
...   None,
...   AuthnContext(AuthnContextClassRef('urn:oasis:names:tc:SAML2:2.0:ac:classes:Password')),
...   AuthnInstant=datetime.utcnow(),
...   )
>>>
>>> ass.AuthnStatement.append(authn)
>>>
>>> att = AttributeStatement(
...   # does not yet work perfectly -- needs further analysis
...   Attribute(xs.string('Dieter', _element=AttributeValue), Name='Firstname'),
...   Attribute(xs.string('Maurer', _element=AttributeValue), Name='Lastname'),
...   )
>>>
>>> ass.AttributeStatement.append(att)

Now it looks like this (not yet signed).

>>> unsigned_ass = ass.toxml()
>>> print unsigned_ass
<?xml version="1.0" ?><ns1:Assertion ID="_fb6dc6ac-9ee6-4a1f-8010-6dba6e0d9746" IssueInstant="2012-07-06T07:24:53.262859" Version="2.0" xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ns1:Issuer>http://bfd.de</ns1:Issuer><ns1:Subject><ns1:NameID>Dieter Maurer</ns1:NameID></ns1:Subject><ns1:AuthnStatement AuthnInstant="2012-07-06T07:24:53.282142"><ns1:AuthnContext><ns1:AuthnContextClassRef>urn:oasis:names:tc:SAML2:2.0:ac:classes:Password</ns1:AuthnContextClassRef></ns1:AuthnContext></ns1:AuthnStatement><ns1:AttributeStatement><ns1:Attribute Name="Firstname"><ns1:AttributeValue xsi:type="ns2:string">Dieter</ns1:AttributeValue></ns1:Attribute><ns1:Attribute Name="Lastname"><ns1:AttributeValue xsi:type="ns2:string">Maurer</ns1:AttributeValue></ns1:Attribute></ns1:AttributeStatement></ns1:Assertion>

We define the signature context to support signing.

>>> from dm.saml2.signature import default_sign_context
>>> default_sign_context.add_key(xmlsec.Key.load('key.pem',  xmlsec.KeyDataFormatPem, None), issuer.value())

We request that ass gets signed on serialization, serialize and look at the result.

>>> ass.request_signature()
>>> signed = ass.toxml()
>>> print signed
<?xml version="1.0" ?><ns1:Assertion ID="_fb6dc6ac-9ee6-4a1f-8010-6dba6e0d9746" IssueInstant="2012-07-06T07:24:53.262859" Version="2.0" xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" xmlns:ns3="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ns1:Issuer>http://bfd.de</ns1:Issuer><ns2:Signature><ns2:SignedInfo><ns2:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ns2:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ns2:Reference URI="#_fb6dc6ac-9ee6-4a1f-8010-6dba6e0d9746"><ns2:Transforms><ns2:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ns2:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ns2:Transforms><ns2:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ns2:DigestValue>6P0dLnMLJCe22YuRD1Mngiprz6k=</ns2:DigestValue></ns2:Reference></ns2:SignedInfo><ns2:SignatureValue>liaBBIVjk73x5spJrvfYg1Sa3VGnOqz0zqDKQr7qoLNg5/pzZ8llQEXQsbvw6zLh
26UnQ6D3KWvvabw9vpRqzLA21ykNUPqEGtZPMiQynvpdRSeTbg5ZyVBGYCL7ww19
MiEKryDwPI56I/3z4Le7KFZ4qpPPUptodQ4mm1PVsyA=</ns2:SignatureValue></ns2:Signature><ns1:Subject><ns1:NameID>Dieter Maurer</ns1:NameID></ns1:Subject><ns1:AuthnStatement AuthnInstant="2012-07-06T07:24:53.282142"><ns1:AuthnContext><ns1:AuthnContextClassRef>urn:oasis:names:tc:SAML2:2.0:ac:classes:Password</ns1:AuthnContextClassRef></ns1:AuthnContext></ns1:AuthnStatement><ns1:AttributeStatement><ns1:Attribute Name="Firstname"><ns1:AttributeValue xsi:type="ns3:string">Dieter</ns1:AttributeValue></ns1:Attribute><ns1:Attribute Name="Lastname"><ns1:AttributeValue xsi:type="ns3:string">Maurer</ns1:AttributeValue></ns1:Attribute></ns1:AttributeStatement></ns1:Assertion>

Now, we look how the verification can be done. We first set up a verification context.

>>> from dm.saml2.signature import default_verify_context
>>> default_verify_context.add_key(xmlsec.Key.load('pubkey.pem',  xmlsec.KeyDataFormatPem, None), issuer.value())

Calling CreateFromDocument will verify any (available) signatures and raise an exception when a verification fails. Verification always uses the Issuer to select the key from the verification context. To check whether a signature was verified at the instance, verified_signature can be called.

>>> verified_ass = CreateFromDocument(signed)
>>> verified_ass.verified_signature()
True

You can use pydoc, the Python builtin help or look at the source to find out more about this package.

Notes

Note that signature creation and verification will fail with an obscure error message from xmlsec when xmlsec is not properly initialized. Do not forget to call dm.xmlsec.binding.initialize().

History

2.1

Signature support for the HTTP redirect binding.

2.0

Version 2.0 uses dm.xmlsec.binding as Python binding to the XML security library, rather then the no longer maintained pyxmlsec. This drastically facilitates installation.

1.0

Initial release based on pyxmlsec.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dm.saml2-2.1.1.tar.gz (340.0 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page