Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metadata Namespaces #131

Closed
msunardi opened this issue Sep 19, 2018 · 4 comments
Closed

Metadata Namespaces #131

msunardi opened this issue Sep 19, 2018 · 4 comments

Comments

@msunardi
Copy link

msunardi commented Sep 19, 2018

I cannot produce the sp metadata with the correct namespaces. I'm expecting the correct namespaces on the tags (like in sp_metadata.xml), but my metadata gives <ns#:...>. I'm looking for help. Any ideas?
I'm using python==3.6.5, django==1.11, djangosaml2==0.17.2, pysaml2==4.6.2

This is the metadata I got from localhost:8000/saml2/metadata/:

<ns0:EntityDescriptor xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ns1="urn:oasis:names:tc:SAML:metadata:algsupport" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" entityID="localhost:8000/saml2/metadata" validUntil="2018-09-20T20:13:45Z">
  <ns0:Extensions>
    <ns1:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#md5"/>
    <ns1:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#ripemd160"/>
    <ns1:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <ns1:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha224"/>
    <ns1:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
    <ns1:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>
    <ns1:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"/>
    <ns1:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
    <ns1:SigningMethod Algorithm="http://www.w3.org/2009/xmldsig11#dsa-sha256"/>
    <ns1:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-md5"/>
    <ns1:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-ripemd160"/>
    <ns1:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <ns1:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha224"/>
    <ns1:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
    <ns1:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"/>
    <ns1:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"/>
  </ns0:Extensions>
  <ns0:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <ns0:KeyDescriptor use="signing">
      <ns2:KeyInfo>
        <ns2:X509Data>
          <ns2:X509Certificate>
            <!-- omitted -->
          </ns2:X509Certificate>
        </ns2:X509Data>
      </ns2:KeyInfo>
    </ns0:KeyDescriptor>
    <ns0:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8000/saml2/ls/"/>
    <ns0:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8000/saml2/ls/post"/>
    <ns0:NameIDFormat>
    urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
    </ns0:NameIDFormat>
    <ns0:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8000/saml2/acs/" index="1"/>
    <ns0:AttributeConsumingService index="1">
      <ns0:ServiceName xml:lang="en"/>
      <ns0:RequestedAttribute FriendlyName="uid" Name="urn:oid:0.9.2342.19200300.100.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true"/>
    </ns0:AttributeConsumingService>
  </ns0:SPSSODescriptor>
  <ns0:ContactPerson contactType="technical">
    <ns0:Company>company</ns0:Company>
    <ns0:GivenName>firstname</ns0:GivenName>
    <ns0:SurName>surname</ns0:SurName>
    <ns0:EmailAddress>email@address.com</ns0:EmailAddress>
  </ns0:ContactPerson>
</ns0:EntityDescriptor>

This is my djangosaml2 settings (added to the end of the settings.py file)

INSTALLED_APPS = [
   ...
    'djangosaml2'
]

# the other settings are hidden
# ...

AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',  # same result with or without this backend
    'djangosaml2.backends.Saml2Backend',
)

LOGIN_URL = '/saml2/login'
SESSION_EXPIRE_AT_BROWSER_CLOSE = True


from os import path
import saml2
import saml2.saml
BASEDIR = path.dirname(path.abspath(__file__))
SAML_CONFIG = {
  # full path to the xmlsec1 binary programm
  'xmlsec_binary': '/usr/bin/xmlsec1',

  # your entity id, usually your subdomain plus the url to the metadata view
  'entityid': 'localhost:8000/saml2/metadata',

  # directory with attribute mapping
  'attribute_map_dir': path.join(BASEDIR, 'attribute-maps'),

  # this block states what services we provide
  'service': {
      # we are just a lonely SP
      'sp' : {
          'name': 'Federated Django sample SP',
          'name_id_format': saml2.saml.NAMEID_FORMAT_PERSISTENT,
          'endpoints': {
              # url and binding to the assetion consumer service view
              # do not change the binding or service name
              'assertion_consumer_service': [
                  ('http://localhost:8000/saml2/acs/',
                   saml2.BINDING_HTTP_POST),
                  ],
              # url and binding to the single logout service view
              # do not change the binding or service name
              'single_logout_service': [
                  ('http://localhost:8000/saml2/ls/',
                   saml2.BINDING_HTTP_REDIRECT),
                  ('http://localhost:8000/saml2/ls/post',
                   saml2.BINDING_HTTP_POST),
                  ],
              },

           # attributes that this project need to identify a user
          'required_attributes': ['uid'],

           # attributes that may be useful to have but not required
          # 'optional_attributes': ['eduPersonAffiliation'],

          # in this section the list of IdPs we talk to are defined
          'idp': {
              # we do not need a WAYF service since there is
              # only an IdP defined here. This IdP should be
              # present in our metadata

              # the keys of this dictionary are entity ids
              '<the idp entityId>': {
                  'single_sign_on_service': {
                      saml2.BINDING_HTTP_REDIRECT: '...', # some url
                      },
                  'single_logout_service': {
                      saml2.BINDING_HTTP_REDIRECT: '...', # some url
                      },
                  },
              },
          },
      },

  # where the remote metadata is stored
  'metadata': {
      'remote': [{
            "url": "..." # url to remote metadata
      }],
  },

  # set to 1 to output debugging information
  'debug': 1,

  # Signing
  'key_file': path.join(BASEDIR, 'this.key'),  # private part
  'cert_file': path.join(BASEDIR, 'this.pem'),  # public part

  # Encryption
  # 'encryption_keypairs': [{
  #     'key_file': path.join(BASEDIR, 'my_encryption_key.key'),  # private part
  #     'cert_file': path.join(BASEDIR, 'my_encryption_cert.pem'),  # public part
  # }],

  # own metadata settings
  'contact_person': [
      {'given_name': 'firstname',
       'sur_name': 'lastname',
       'company': 'company',
       'email_address': 'email@address.com',
       'contact_type': 'technical'},
      # {'given_name': 'Angel',
      #  'sur_name': 'Fernandez',
      #  'company': 'Yaco Sistemas',
      #  'email_address': 'angel@yaco.es',
      #  'contact_type': 'administrative'},
      ],
  # you can set multilanguage information here
  # 'organization': {
  #     'name': [('Yaco Sistemas', 'es'), ('Yaco Systems', 'en')],
  #     'display_name': [('Yaco', 'es'), ('Yaco', 'en')],
  #     'url': [('http://www.yaco.es', 'es'), ('http://www.yaco.com', 'en')],
  #     },
  'valid_for': 24,  # how long is our metadata valid
  }
@msunardi
Copy link
Author

I"ve tried different combinations:
python==2.7.15 x django==1.10/1.11
python==3.6.5 x django==1.10/1.11
with the same results

@msunardi
Copy link
Author

msunardi commented Sep 24, 2018

I am able to get djangosaml2 to produce the namespaces that I wanted on the metadata.

<md:EntityDescriptor xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:8000/saml2/metadata" validUntil="2018-09-21T22:58:21Z">
  <md:Extensions>
    <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#md5"/>
    <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#ripemd160"/>
    <alg:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha224"/>
    <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
    <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>
    <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"/>
    <alg:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
    <alg:SigningMethod Algorithm="http://www.w3.org/2009/xmldsig11#dsa-sha256"/>
    <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-md5"/>
    <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-ripemd160"/>
    <alg:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha224"/>
    <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
    <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"/>
    <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"/>
  </md:Extensions>
  <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <md:KeyDescriptor use="signing">
      <ds:KeyInfo>
        <ds:X509Data>
          <ds:X509Certificate>
               <!-- omitted -->
          </ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>
    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8000/saml2/ls/"/>
    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8000/saml2/ls/post"/>
    <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8000/saml2/acs/" index="1"/>
    <md:AttributeConsumingService index="1">
      <md:ServiceName xml:lang="en"/>
      <md:RequestedAttribute FriendlyName="uid" Name="urn:oid:0.9.2342.19200300.100.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true"/>
    </md:AttributeConsumingService>
  </md:SPSSODescriptor>
  <md:Organization>
     <!-- omitted -->
  </md:Organization>
  <md:ContactPerson contactType="technical">
    <!-- omitted -->
  </md:ContactPerson>
</md:EntityDescriptor>

However, this still missing the <?xml version="1.0" encoding="UTF-8"?> header part on the first line and the <md:EntityDescriptors>...</md:EntityDescriptors> tags. So I'm not sure this is correct yet.

To get this result, I had to add the following lines in djangosaml2.views.py. Basically, it needs to register the namespaces with the saml2.ElementTree, i.e. ElementTree.register_namespace(prefix, namespace)

# Add the following import on top
from saml2 import ElementTree

...

# Add the following lines at the end afer get_namespace_prefixes()
def register_namespace_prefixes():
    from saml2 import md, saml, samlp
    from saml2.extension import algsupport
    
    try:
        from saml2 import xmlenc
        from saml2 import xmldsig
    except ImportError:
        import xmlenc
        import xmldsig
    prefixes = (('saml', saml.NAMESPACE),
                ('samlp', samlp.NAMESPACE),
                ('md', md.NAMESPACE),
                ('ds', xmldsig.NAMESPACE),
                ('xenc', xmlenc.NAMESPACE),
                ('alg', algsupport.NAMESPACE))
    if hasattr(ElementTree, 'register_namespace'):
        for prefix, namespace in prefixes:
            ElementTree.register_namespace(prefix, namespace)
    else:
        for prefix, namespace in prefixes:
            ElementTree._namespace_map[namespace] = prefix
    # print("ELEMENT TREE: {}".format(ElementTree._namespace_map))

register_namespace_prefixes()

The code above was from the old Bitbucket repo of djangosaml2. Was there a reason this part is removed in the Github version of djangosaml2?

@peppelinux
Copy link
Member

This would be automatically fixed when this PR will be merged in pySAML2
IdentityPython/pysaml2#625

standing about what @c00kiemon5ter said it wouldnt take too much time

@peppelinux
Copy link
Member

I don't really know when pySAML2 dev team will merge that contribution but it's time to close this issue, because not strictly related to djangosaml2.

Please continue this thread in pySAML2 repository, I'm also there

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants