Package: dm.saml2
This package provides support for SAML2 based onpyxb.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.DependenciesPyXBClass collections generated bypyxbtend to be very version dependent.
Thus, they must usually be regenerated when thepyxbversion has changed.The current package’s version mostly uses class collections from
thewssplatandsaml20bundles ofpyxb. Thus, they
have a good chance to be updated together withpyxb.
However, thepyxbsaml2bundle lacks support for
the so called SAML2 context classes. This package contains
class collections generated withpyxb==1.1.4. They need to
be regenerated if thepyxbversion changes (and you use those classes –
which is not very likely).
Thegen.shscript in subpackagepyxbcan provide clues how to regenerate them.The originalPyXBauthor (Peter A. Bigot) hasabandoned the project.
The latest release1.2.6is no longer compatible with modern
Python versions (at least fromPython 3.10on).
To handle this situation,dm.saml2no longer declares thePyXBdependency in itssetup.py. This allows the use aPyXBreplacement,
potentially named differently.
If you want to usedm.saml2with Python 3.10+, you must provide
such a replacement.I have not yet found aPython 3.10compatible replacement with
the requiredwssplatandsaml20bundles.
For a customer project, I used the following approach:
I started with thePyXB-CTC 1.3.0 source distribution, fixed there a few remainingPython 3.10incompatibilities (essentially replacing references tocollectionsby corresponding ones tocollections.abc) and generated the missingwssplatandsaml20bundles.dm.xmlsec.bindingCheck its installation notes should you face related installation problems.ExampleThis section provides a simple example on how to create, sign and
verify an assertion with this package.Always ensure, thexmlseclibrary 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 thatassgets 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())CallingCreateFromDocumentwill verify any (available) signatures and raise
an exception when a verification fails. Verification always uses
theIssuerto select the key from the verification context.
To check whether a signature
was verified at the instance,verified_signaturecan be called.>>> verified_ass = CreateFromDocument(signed)
>>> verified_ass.verified_signature()
TrueYou can usepydoc, the Python builtinhelpor look at the source
to find out more about this package.ConfigurationStarting with version 3.1, the package provides an elementary way to
configure parts of the signature generation and verification process.
This may become necessary when SAML partner entities have special
requirements in this regard.You do this kind of configuration by importing the moduledm.saml2.configand overriding some of the variables defined there.As an example, let us assume that one of your SAML2 partner entities
usesDsaSha1as the signature method (rather than the defaultRsaSha1). Without special configuration, the verification will fail.
Thexmlsecstack trace will give hints (somewhat cryptic) towards
a disabled transform. The configuration to get those SAML2 messages
verified could look like:>>> from dm.saml2 import config
>>> from dm.xmlsec.binding import TransformDsaSha1
>>> config.signature_transforms += (TransformDsaSha1, )This configuration tells the package thatDsaSha1is an
acceptable signature method.The configuration should happen once - before the first signature
creation/verification.NotesNote that signature creation and verification will fail with an obscure
error message fromxmlsecwhenxmlsecis not properly
initialized. Do not forget to calldm.xmlsec.binding.initialize().History3.2.3This version is identical to3.2.2with the exception
that it no longer declares itsPyXBdependency in itssetup.py.
This allows the use of a (potentially differently named)PyXBreplacement. Such a replacement becomes necessary forPython 3.10+because the latestPyXBrelease is no longer compatible.
For Python versions up to3.9usedm.saml2==3.2.2.3.2Python 3 compatibityAdded test suiteFixes in thehttpartifactbinding.3.1.3Added a 10 s timeout inmetadata.EntityByUrl.get_metadata_document.
It can be changed by changing the variableENTITY_BY_URL_TIMEOUTin modulemetadata.3.1.2Support (limited) type conversion inutil.xs_convert_from_xml.3.1.1Adapt to the changed binding for “plural” elements (maxoccurs> 1)
in newerPyXBversions.3.1Elementary configuration support for the signature generation/verification
process.Support for use ofSha256andRsaSha256as digest or
signature method, respcectively.3.0Switch todatetimevalues explicitely in the UTC time zone (rather
than naivedatetimevalues which implicitely use the UTC time zone).This also affects the lexical representation of SAML time values: they
now use theZtimezone suffix.2.1Signature support for the HTTP redirect binding.
Note: for this to work, you need at least version 1.3 ofdm.xmlsec.bindingwhich requireslxml >= 3.0;
as Plone 4.x still useslxml 2.x, this version dependency
is not declared insetup.py.2.0Version 2.0 usesdm.xmlsec.bindingas Python binding to the XML
security library, rather then the no longer maintainedpyxmlsec.
This drastically facilitates installation.1.0Initial release based onpyxmlsec.