IdentityPython / djangosaml2

Django SAML2 Service Provider based on pySAML2
Apache License 2.0
258 stars 143 forks source link

IdP is missing when using MDQ as metadata source #325

Closed cheoppy closed 2 years ago

cheoppy commented 2 years ago

I'm trying to configure an SSO login similar to EduGAIN, using an MDQ service as the source of the metadata, but I cannot manage to make it work.

It always ends up in the next exception:

Internal Server Error: /saml2/login/
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.7/dist-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.7/dist-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python3.7/dist-packages/django/views/generic/base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "/usr/local/lib/python3.7/dist-packages/djangosaml2/views.py", line 250, in get
    raise IdPConfigurationMissing("IdP is missing or its metadata is expired.")
djangosaml2.exceptions.IdPConfigurationMissing: IdP is missing or its metadata is expired.

The metadata part of the config:

    'metadata': {
        'mdq': [{
            "url": "https://mdx-2020.eduid.hu/",
            "cert": path.join(BASEDIR, 'saml_certs/href-metadata-signer-2020.crt')
        }]
    },

I have tried to look up an example IdP manually in the MDQ, which works fine, here's the minimal working example which shows that the MDQ configured above is working:

import copy

from django.conf import settings
from django.core.management.base import BaseCommand
from saml2.config import SPConfig

class Command(BaseCommand):
    def handle(self, *args, **options):
        conf = SPConfig()
        conf.load(copy.deepcopy(settings.SAML_CONFIG))
        for metadata in conf.metadata.metadata.values():
            mdx = metadata
            entity2check = 'https://idp.elte.hu/auth/saml2/idp/metadata.php'
            r = mdx.service(entity2check, 'idpsso_descriptor', 'single_sign_on_service')
            self.stdout.write("%s" % r)

which outputs:

{'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect': 
[{'__class__': 'urn:oasis:names:tc:SAML:2.0:metadata&SingleSignOnService', 
'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', 
'location': 'https://idp.elte.hu/auth/saml2/idp/SSOService.php'}]}

where I can see that it actually did parse the IdP data.

What am I missing from the configuration? What else is needed to use the MDQ as an IdP metadata source?

peppelinux commented 2 years ago

Hi @cheoppy

Put a breakpoint here https://github.com/IdentityPython/djangosaml2/blob/5f956aab8d262cba84f1c52d78b710e059d9007d/djangosaml2/utils.py#L41

you should be alble to get all the idps available (even through MDQ).

It seems that you didn0t get any of them, here https://github.com/IdentityPython/djangosaml2/blob/5f956aab8d262cba84f1c52d78b710e059d9007d/djangosaml2/views.py#L249

cheoppy commented 2 years ago

Indeed I haven't got any available idps, here's the traceback provided by django at this point: image

The configured_idps is empty. How can it be empty when I use MDQ?

peppelinux commented 2 years ago

I use djangosaml2 with a MDQ, so I'd suggest to put a breakpoint to check what's happens in your configuration

cheoppy commented 2 years ago

I did some debugging, and found the following concerning the metadata section of the config:

config.metadata:

type: <class 'saml2.mdstore.MetadataStore'>
value (as string): {https://mdx-2020.eduid.hu/: dict_items([])}

config.metadata.metadata:

type: dict
value (as string): {'https://mdx-2020.eduid.hu/': <saml2.mdstore.MetaDataMDX object at 0x7fa453d45ac8>}

config.metadata.metadata['https://mdx-2020.eduid.hu/']:

type: <class 'saml2.mdstore.MetaDataMDX'>: 
value (as string): dict_items([])

result = metadata.any("idpsso_descriptor", "single_sign_on_service") (L48): empty dict

I seems to me that the saml2.mdstore.MetaDataMDX is picked up correctly from the config, but it looks empty and thus no idps are found when I initiate the login. What else should I look for?

cheoppy commented 2 years ago

@peppelinux I've been doing some tests and come up with a solution, see https://github.com/IdentityPython/djangosaml2/pull/327 . I found that the MetaDataMDX object will be always empty unless a lookup is made, which will fetch the appropriate idp and djangosaml2 will see it as a legit metadata source. My minimal working fix may not be the best solution, but it works when the only and primary metadata source is MDQ.

peppelinux commented 2 years ago

got it @cheoppy, thank you I've just requested some easy changes before merging it and have a new release

peppelinux commented 2 years ago

not at least, can we converge also to the following issue? https://github.com/IdentityPython/djangosaml2/issues/273

we can pre-select the supported idps and pass to the MDX. Just to tell you if you find something usefull also for you

peppelinux commented 2 years ago

Done here https://github.com/IdentityPython/djangosaml2/releases/tag/v1.4.0