Giter Club home page Giter Club logo

djangosaml2idp's Introduction

djangosaml2idp

PyPi

PyPI - Python Version

PyPI - Django Version

Documentation Status

Apache 2.0 License

Code coverage

djangosaml2idp implements the Identity Provider side of the SAML2 protocol for Django. It builds on top of PySAML2, and is production-ready.

Any contributions, feature requests, proposals, ideas ... are welcome! See the CONTRIBUTING document for some info.

Installation

PySAML2 uses XML Security Library binary to sign SAML assertions, so you need to install it either through your operating system package or by compiling the source code. It doesn't matter where the final executable is installed because you will need to set the full path to it in the configuration stage. XmlSec is available (at least) for Debian, OSX and Alpine Linux.

Now you can install the djangosaml2idp package using pip. This will also install PySAML2 and its dependencies automatically:

pip install djangosaml2idp

Configuration & Usage

The first thing you need to do is add djangosaml2idp to the list of installed apps:

INSTALLED_APPS = (
    'django.contrib.admin',
    'djangosaml2idp',
    ...
)

Now include djangosaml2idp in your project by adding it in the url config:

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^idp/', include('djangosaml2idp.urls')),
    url(r'^admin/', admin.site.urls),
    ...
]

Run the migrations for the app.

In your Django settings, configure your IdP. Configuration follows the PySAML2 configuration. The IdP from the example project looks like this:

import saml2
from saml2.saml import NAMEID_FORMAT_EMAILADDRESS, NAMEID_FORMAT_UNSPECIFIED
from saml2.sigver import get_xmlsec_binary

LOGIN_URL = '/login/'
BASE_URL = 'http://localhost:9000/idp'

SAML_IDP_CONFIG = {
    'debug' : DEBUG,
    'xmlsec_binary': get_xmlsec_binary(['/opt/local/bin', '/usr/bin']),
    'entityid': '%s/metadata' % BASE_URL,
    'description': 'Example IdP setup',

    'service': {
        'idp': {
            'name': 'Django localhost IdP',
            'endpoints': {
                'single_sign_on_service': [
                    ('http://localhost:9000/idp/sso/post/', saml2.BINDING_HTTP_POST),
                    ('http://localhost:9000/idp/sso/redirect/', saml2.BINDING_HTTP_REDIRECT),
                ],
                "single_logout_service": [
                    ("http://localhost:9000/idp/slo/post/", saml2.BINDING_HTTP_POST),
                    ("http://localhost:9000/idp/slo/redirect/", saml2.BINDING_HTTP_REDIRECT)
                ],
            },
            'name_id_format': [NAMEID_FORMAT_EMAILADDRESS, NAMEID_FORMAT_UNSPECIFIED],
            'sign_response': True,
            'sign_assertion': True,
            'want_authn_requests_signed': True,
        },
    },

    # Signing
    'key_file': BASE_DIR + '/certificates/private.key',
    'cert_file': BASE_DIR + '/certificates/public.cert',
    # Encryption
    'encryption_keypairs': [{
        'key_file': BASE_DIR + '/certificates/private.key',
        'cert_file': BASE_DIR + '/certificates/public.cert',
    }],
    'valid_for': 365 * 24,
}

Notice the configuration requires a private key and public certificate to be available on the filesystem in order to sign and encrypt messages.

Next the Service Providers and their configuration need to be added, this is done via the Django admin interface. Add an entry for each SP which speaks to thie IdP. Add a copy of the local metadata xml, or set a remote metadata url. Add an attribute mapping for user attributes to SAML fields or leave the default mapping which will be prefilled.

Several attributes can be overriden per SP. If they aren't overridden explicitly, they will use the 'global' settings which can be configured for your Django installation. If those aren't set, some defaults will be used, as indicated in the admin when you configre a SP. The resulting configuration of a SP, with merged settings of its own and the instance settings and defaults, is shown in the admin as a summary.

Further optional configuration options

In the SAML_IDP_SPCONFIG setting you can define a processor, its value being a string with dotted path to a class. This is a hook to customize some access control checks. By default, the included BaseProcessor is used, which allows every user to login on the IdP. You can customize this behaviour by subclassing the BaseProcessor and overriding its has_access(self, request) method. This method should return true or false, depending if the user has permission to log in for the SP / IdP. The processor has the SP entity ID available as self._entity_id, and received the request (with an authenticated request.user on it) as parameter to the has_access function. This way, you should have the necessary flexibility to perform whatever checks you need. An example processor subclass can be found in the IdP of the included example. Use this metadata xml to configure your SP. Place the metadata xml from that SP in the location specified in the config dict (sp_metadata.xml in the example above).

Without custom setting, users will be identified by the USERNAME_FIELD property on the user Model you use. By Django defaults this will be the username. You can customize which field is used for the identifier by adding SAML_IDP_DJANGO_USERNAME_FIELD to your settings with as value the attribute to use on your user instance.

Other settings you can set as defaults to be used if not overriden by an SP are SAML_AUTHN_SIGN_ALG, SAML_AUTHN_DIGEST_ALG, and SAML_ENCRYPT_AUTHN_RESPONSE. They can be set if desired in the django settings, in which case they will be used for all ServiceProviders configuration on this instance if they don't override it. E.g.:

SAML_AUTHN_SIGN_ALG = saml2.xmldsig.SIG_RSA_SHA256
SAML_AUTHN_DIGEST_ALG = saml2.xmldsig.DIGEST_SHA256

In case your SP does not properly expose validuntil in metadata, you can provide fallback setting for it using:

SAML_IDP_FALLBACK_EXPIRATION_DAYS = 30

The default value for the fields processor and attribute_mapping in the ServiceProvider can be set via the settings (the values displayed here are the defaults):

SAML_IDP_SP_FIELD_DEFAULT_PROCESSOR = 'djangosaml2idp.processors.BaseProcessor'
SAML_IDP_SP_FIELD_DEFAULT_ATTRIBUTE_MAPPING = {"email": "email", "first_name": "first_name", "last_name": "last_name", "is_staff": "is_staff", "is_superuser": "is_superuser"}

Customizing error handling

djangosaml2idp renders a very basic error page if it encounters an error, indicating an error occured, which error, and possibly an extra message. The HTTP status code is dependant on which error occured. It also logs the exception with error severity. You can customize this by using the SAML_IDP_ERROR_VIEW_CLASS setting. Set this to a dotted import path to your custom (class based) view in order to use that one. You'll likely want this to use your own template and styling to display and error message. If you subclass the provided djangosaml2idp.error_views.SamlIDPErrorView, you have the following variables available for use in the template:

exception

the exception instance that occurred

exception_type

the class of the exception that occurred

exception_msg

the message from the exception (by doing str(exception))

extra_message

if no specific exception given, a message indicating something went wrong, or an additional message next to the exception_msg

The simplest override is to subclass the SamlIDPErrorView and only using your own error template. You can use any Class-Based-View for this; it's not necessary to subclass the builtin error view. The example project contains a ready to use example of this; uncomment the SAML_IDP_ERROR_VIEW_CLASS setting and it will use a custom view with custom template.

Multi Factor Authentication support

There are three main components to adding multiple factor support.

  1. Subclass djangosaml2idp.processors.BaseProcessor as outlined above. You will need to override the enable_multifactor() method to check whether or not multifactor should be enabled for a user. (If it should allways be enabled for all users simply hard code to True). By default it unconditionally returns False and no multifactor is enforced.
  2. Sublass the djangosaml2idp.views.ProcessMultiFactorView view to make the appropriate calls for your environment. Implement your custom verification logic in the multifactor_is_valid method: this could call a helper script, an internal SMS triggering service, a data source only the IdP can access or an external second factor provider (e.g. Symantec VIP). By default this view will log that it was called then redirect.

3. Add an entry to settings.py with a string representing the path to your multifactor view. The first package should be the app name: SAML_IDP_MULTIFACTOR_VIEW = "this.is.the.path.to.your.multifactor.view

Running the test suite

Install the dev dependencies in requirements-dev.txt:

pip install -r requirements-dev.txt

Run the test suite from the project root:

tox -e format  # to run linting
tox -e py3.7-django3.0  # to run the tests
tox -e typing  # to run typechecking, this is allowed to fail

Tests will be ran using CI when opening a merge request as well.

Example project

The directory example_project contains a barebone demo setup to demonstrate the login-logout functionality. It consists of a Service Provider implemented with djangosaml2 and an Identity Provider using djangosaml2idp. The readme in that folder contains more information on how to run it.

djangosaml2idp's People

Contributors

amertz08 avatar askvortsov1 avatar bendavis78 avatar challet avatar dependabot[bot] avatar goetzk avatar jlunger-arcweb avatar josephnuthalapati2244 avatar lgarvey avatar mhindery avatar mjholtkamp avatar nijel avatar peppelinux avatar pix666 avatar pyup-bot avatar saundersmatt avatar sheilatron avatar urth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

djangosaml2idp's Issues

Binding is not always BINDING_HTTP_POST

https://github.com/OTA-Insight/djangosaml2idp/blob/master/djangosaml2idp/views.py#L68

djangosaml2idp (naively?) seems to assume the type is always BINDING_HTTP_POST (and not BINDING_HTTP_REDIRECT). This causes the mechanism to fail when the request is deflated (compressed), as the pysaml library uses that parameter to decide whether to deflate the request or not.

This probably has something to do with this line in code:

# TODO Add http redirect logic based on https://github.com/rohe/pysaml2/blob/master/example/idp2_repoze/idp.py#L327

But I think it's worth mentioning here that I ran into this issue.

[Security] TODO verify signature

In views.LoginProcessView we can read

        if "SigAlg" in request.session and "Signature" in request.session:
            _certs = self.IDP.metadata.certs(req_info.message.issuer.text, "any", "signing")
            verified_ok = False
            for cert in _certs:
                # TODO implement
                # if verify_redirect_signature(_info, self.IDP.sec.sec_backend, cert):
                #    verified_ok = True
                #    break

Should we have to deal with pySAML2 internals to validate the signature or should we have to code a specialized function to do this?

[Additional Features] Attribute policies, attribute rewrite, agreement screen

Hi, first of all thank you very much for having merged my contributions.
I'd like to discuss with your help some of following notes:

  • attribute filter/policies: actually found in official pySAML2 docs!;
  • possibility to dinamically rewrite or create additional attributes before relasing them to SP;
  • user agreement screen, to inform users about the attributes that are going to be released to the SP;

I agree that attribute transformation and rewrite should be delegated to pySAML2, so I opended this feature request. Therefore the agreement screen could be a good topic to discuss on.

[CONTRIBUTING] pep8 code style

I'm moving to implement Agreement screen and SLO and a contributor told me that we should not use pep8 style in the code, thread here: askvortsov1#1 (comment)

Should we put this information in CONTRIBUTING.md or should we instead move to a pep8 compliance?

I apologize I'd prefer the second one :)

ForceAuthn

If an AuthnRequest came with forceauthn = true, it means that the user must renew his autentication through login form

Login stalls at redirect if certificates are expired

I tried logging in with certificates that expired over the weekend but instead of seeing the page with a redirect button i just got a white screen and redirection stopped at /idp/login/process/. I'm guessing something in the certificate checking done by djangosaml2idp doesn't correctly handle expired certs.

The relevant bits of my log seem to be this:

2017-08-24 13:40:37 ERROR saml2.mdstore: Entity descriptor (entity id:https://dev-site/VIP/metadata/) to old
2017-08-24 13:40:37 ERROR saml2.mdstore: Entity descriptor (entity id:https://dev-site/VIP/metadata/) to old

2017-08-24 13:40:37 DEBUG saml2.sigver: ==== Certs from metadata ==== None: [] ====
2017-08-24 13:40:37 DEBUG saml2.sigver: ==== Certs from metadata ==== None: [] ====
2017-08-24 13:40:37 INFO saml2.request: EXCEPTION: None
2017-08-24 13:40:37 INFO saml2.request: EXCEPTION: None
2017-08-24 13:40:37 ERROR saml2.request: Response was not correctly signed
2017-08-24 13:40:37 ERROR saml2.request: Response was not correctly signed

Unable to sign response

An error occurred while docking with Ali Cloud
Uploading image.pngโ€ฆ
I think itโ€˜s possible that djangosaml2idp doesn't support signatures to Response

[Security] IDP shouldn't answer to SP saml request with unsupported name ID formats

in https://github.com/OTA-Insight/djangosaml2idp/blob/master/djangosaml2idp/views.py#L232
or in https://github.com/askvortsov1/djangosaml2idp/blob/master/djangosaml2idp/views.py#L146

The IDP return an UNSPECIFIED format if neither the sp or the idp nameid format was found.
In addition, a check about the availability of the requested name id format, idp side, hasn't been made.

This should raise an handled exception

NoneType' object has no attribute 'format'

Hello,
I am trying to integrate this library with Tableau, to do SAML authentication. I followed the documentation steps. but after login, the error below appears on the screen

Error during SAML2 authentication
AttributeError
'NoneType' object has no attribute 'format'

I saw that this error happens because the value name_id_policy is empty, and the library try to do resp_args['name_id_policy'].format
Does anyone have any idea why this occurs?

This is my SP metadata:

<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" ID="Lj.RwoZKDQsM6i2XUKS1y3T.U4f" cacheDuration="PT1440M" entityID="https://backendplataforma-development.azurewebsites.net">
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
ds:SignedInfo
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1" />
<ds:Reference URI="#Lj.RwoZKDQsM6i2XUKS1y3T.U4f">
ds:Transforms
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
ds:DigestValue+lWZYmzydT1spQiK4RHV6P9AIGP8V0Q7a70q/PKLC5A=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
ds:SignatureValuexxx</ds:SignatureValue>
</ds:Signature>
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" WantAuthnRequestsSigned="false">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
ds:X509Data
ds:X509Certificatexxx</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
md:NameIDFormaturn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
md:NameIDFormaturn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://backendplataforma-development.azurewebsites.net/idp/sso/post" />
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://backendplataforma-development.azurewebsites.net/idp/sso/redirect" />
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Name="username" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" />
</md:IDPSSODescriptor>
<md:ContactPerson contactType="administrative">
md:CompanyTableau PingFed Demo</md:Company>
</md:ContactPerson>
</md:EntityDescriptor>

And this is my IDP 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="https://backendplataforma-development.azurewebsites.net/idp/metadata" validUntil="2020-08-13T14:41:28Z">
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:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<ns0:KeyDescriptor use="signing">
ns2:KeyInfo
ns2:X509Data
ns2:X509Certificatexxxxx</ns2:X509Certificate>
</ns2:X509Data>
</ns2:KeyInfo>
</ns0:KeyDescriptor>
<ns0:KeyDescriptor use="encryption">
ns2:KeyInfo
ns2:X509Data
ns2:X509Certificatexxx</ns2:X509Certificate>
</ns2:X509Data>
</ns2:KeyInfo>
</ns0:KeyDescriptor>
ns0:NameIDFormat
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
</ns0:NameIDFormat>
ns0:NameIDFormat
urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
</ns0:NameIDFormat>
<ns0:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://xxx/idp/sso/post"/>
<ns0:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://xxx/idp/sso/redirect"/>
</ns0:IDPSSODescriptor>

[Attribute Mappings] Add django mappings in example idp project

See:
https://github.com/peppelinux/Django-Identity/blob/master/django-saml2-idp/django_saml2_idp/idp/saml2_config/attribute-maps/django_saml_uri.py

otherwise AttributeValue will not have FriendlyName and oid correctly valued. Some SP will reject these if not valued correctly!

This is an example of mine implementation that have many soruces, as LDAP.
You should change uid with username here:
https://github.com/peppelinux/Django-Identity/blob/master/django-saml2-idp/django_saml2_idp/idp/saml2_config/attribute-maps/django_saml_uri.py#L11

exception on missing optional metadata ValidUntil

When adding my SP via the Admin interface and clicking the save button I get the exception shown below. Metadata at the bottom of this message.
Django side is the IdP.
SP is running simplesamlphp 1.18.3
validUntil is optional, yet code seems to assume it is present.

Context:
django 2.2.12
djangosaml2idp 0.7.2
pysaml2 5.0.0
(if more context is needed, just ask)

Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python3.6/site-packages/django/contrib/admin/options.py", line 606, in wrapper
return self.admin_site.admin_view(view)(*args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/django/utils/decorators.py", line 142, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/django/contrib/admin/sites.py", line 223, in inner
return view(request, *args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/django/contrib/admin/options.py", line 1645, in add_view
return self.changeform_view(request, None, form_url, extra_context)
File "/usr/local/lib/python3.6/site-packages/django/utils/decorators.py", line 45, in _wrapper
return bound_method(*args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/django/utils/decorators.py", line 142, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/django/contrib/admin/options.py", line 1529, in changeform_view
return self._changeform_view(request, object_id, form_url, extra_context)
File "/usr/local/lib/python3.6/site-packages/django/contrib/admin/options.py", line 1572, in _changeform_view
self.save_model(request, new_object, form, not add)
File "/usr/local/lib/python3.6/site-packages/django/contrib/admin/options.py", line 1088, in save_model
obj.save()
File "/usr/local/lib/python3.6/site-packages/djangosaml2idp/models.py", line 155, in save
self.metadata_expiration_dt = extract_validuntil_from_metadata(self.local_metadata).replace(tzinfo=None)
File "/usr/local/lib/python3.6/site-packages/djangosaml2idp/utils.py", line 69, in extract_validuntil_from_metadata
raise ValidationError(f'Could not extra ValidUntil timestamp from metadata: {e}')
django.core.exceptions.ValidationError: ["Could not extra ValidUntil timestamp from metadata: 'validUntil'"]

SP metadata:

<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://wiki.handboogsport.st-visir.nl/saml/module.php/saml/sp/metadata.php/default-sp">
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol">
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://wiki.handboogsport.st-visir.nl/saml/module.php/saml/sp/saml2-logout.php/default-sp"/>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://wiki.handboogsport.st-visir.nl/saml/module.php/saml/sp/saml2-logout.php/default-sp"/>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://wiki.handboogsport.st-visir.nl/saml/module.php/saml/sp/saml2-acs.php/default-sp" index="0"/>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post" Location="https://wiki.handboogsport.st-visir.nl/saml/module.php/saml/sp/saml1-acs.php/default-sp" index="1"/>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://wiki.handboogsport.st-visir.nl/saml/module.php/saml/sp/saml2-acs.php/default-sp" index="2"/>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01" Location="https://wiki.handboogsport.st-visir.nl/saml/module.php/saml/sp/saml1-acs.php/default-sp/artifact" index="3"/>
</md:SPSSODescriptor>
<md:ContactPerson contactType="technical">
md:GivenNameRamon</md:GivenName>
md:EmailAddress[email protected]</md:EmailAddress>
</md:ContactPerson>
</md:EntityDescriptor>

Bug: Not able to run example setup

I am struggling to run the example setup. I am getting error while authenticating via idp.

Error: saml2.response.IncorrectlySigned

Here is the traceback:
[18/Jun/2020 13:41:26] "GET /login/?next=/idp/login/process/ HTTP/1.1" 200 965
SSO requested to IDP with binding urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
--- SAML request [

<ns0:AuthnRequest xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" ID="id-TJfxWL3KnCY1jnvb1" Version="2.0" IssueInstant="2020-06-18T13:41:31Z" Destination="http://localhost:9000/idp/sso/post/" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="http://localhost:8000/saml2/acs/">
<ns1:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://localhost:8000/saml2/metadata/</ns1:Issuer>
<ns2:Signature Id="Signature1">
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="#id-TJfxWL3KnCY1jnvb1">
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:DigestValueqO5aq+5OQ4TSO6MJEYqR0mhWLXk=</ns2:DigestValue>
</ns2:Reference>
</ns2:SignedInfo>
ns2:SignatureValueEaeXe9FiFF/A+3fpWG+RsLEM/qMns9CXmVxE2Icjhbn6bII7M8E1bpoQbYtqjLuY
Ps6NOuUo1O/d1N4iQujn1fYQOCcBQgcoOlRndsHAbm/JDIKXvkV8rmI/+HR9Dezx
PQZ8pznF4qW8HcZ0rpsff3/Gz3dBvmQo6e/awl1clNxz1qmyN5/9TO/vF+0WI2RK
QLPBcDTkBpP9NM9Z3P1i+JGHwyOKhOjSK7glTguiIcz9PCbM8rwNiqm0qLF++T/N
NR+rXgwce+MZ6vJh4IQu2bJtqbWKdtSC5g1ftTuV8e3Heb4kngDHRg6YxOOM0J1Y
seod7jbza2umY3Gkt55N1g==</ns2:SignatureValue>
ns2:KeyInfo
ns2:X509Data
ns2:X509CertificateMIIDCTCCAfGgAwIBAgIJAL6KBRFZctKRMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAMMEHNwLmxvY2FsaG9zdC5jb20wHhcNMTgwODA4MTgxNzQ1WhcNMjgwODA1MTgxNzQ1WjAbMRkwFwYDVQQDDBBzcC5sb2NhbGhvc3QuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnWvPHrAXZ8S5TrrPwPk744sQqIh8x8tX+PGtJpZJOidAVnB5vGnZZa5rvu6Es+M15CAD+cz1GAsBegAz4FVP12I3anotCwnbnb0BvdrCD8zVBeMfq8MhWFLm09HoAh7STWyDi0+0BAgCFPyCvfBvNK5j9N0HyJeMLDnju7PWAAHr7rget5FNw6NWxfjlJiowSPpqVfTSjJcKTcByttnmLn8MPs5N5YhPX2CqFWnn3mzl/s7Xc3AQH7xCHGfQX738060vkm72ufPIKi9+onABHJYMx3p5TI9j/O7g06YoypB4k839odCCyoxcfsJwjKGrZnvEB5Eo8ZQJbd3kfSLu5QIDAQABo1AwTjAdBgNVHQ4EFgQUEC1n3lbl5TZO9gjy4VeOuKvtqTswHwYDVR0jBBgwFoAUEC1n3lbl5TZO9gjy4VeOuKvtqTswDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAKNPZhQA3028/tGF51RW8vBeHBry2AxSF3ZCgdZwszFMV6ls6/9ZwbYHE+yN2ehNXTxdOfbZIy90Vy0UHMfJk3syKPxQeAM7/1udWk53UfXbwWuyhwUSdDh02H8oXORMcM2MOj5Cl/qcW5X7dbhFkgzRfyTAebonhe8CSfN7uXGgkzO8ft7ndk1uK2hKL5VWH1e/gTegFYm/CMjB/Cwhr+226oz1bjulbU4VJ+RUkrlV6+puCcyBhcv8ZR3f6YAGHzsUt7RNjLeyEuqquA1QbwmlkfeV9NOO9aBpbA5SQc+43CunQe1rLsWoIpCcjSmxYH1qVD7tHLhI7yBqPv5RQUA==</ns2:X509Certificate>
</ns2:X509Data>
</ns2:KeyInfo>
</ns2:Signature>
<ns0:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" AllowCreate="false"/>
</ns0:AuthnRequest>
] ---
[18/Jun/2020 13:41:31] "POST /idp/sso/post/ HTTP/1.1" 302 0
[18/Jun/2020 13:41:31] "GET /idp/login/process/ HTTP/1.1" 302 0
[18/Jun/2020 13:41:31] "GET /login/?next=/idp/login/process/ HTTP/1.1" 200 965
[18/Jun/2020 13:41:37] "POST /login/ HTTP/1.1" 302 0
Response was not correctly signed
{}
Traceback (most recent call last):
File "/home/sowmya/TestProjects/djangosaml2idp/example_setup/idp/djangosaml2idp/views.py", line 236, in get
req_info = idp_server.parse_authn_request(request.session['SAMLRequest'], binding)
File "/home/sowmya/.virtualenvs/idp/lib/python3.8/site-packages/saml2/server.py", line 230, in parse_authn_request
return self._parse_request(enc_request, AuthnRequest,
File "/home/sowmya/.virtualenvs/idp/lib/python3.8/site-packages/saml2/entity.py", line 846, in _parse_request
_request = _request.loads(xmlstr, binding, origdoc=enc_request,
File "/home/sowmya/.virtualenvs/idp/lib/python3.8/site-packages/saml2/request.py", line 95, in loads
return self._loads(xmldata, binding, origdoc, must,
File "/home/sowmya/.virtualenvs/idp/lib/python3.8/site-packages/saml2/request.py", line 59, in _loads
raise IncorrectlySigned()
saml2.response.IncorrectlySigned
Internal Server Error: /idp/login/process/
[18/Jun/2020 13:41:37] "GET /idp/login/process/ HTTP/1.1" 500 326

On debugging, I found the saml.mdstore.Metadata object is not getting entity_id
def certs(self, entity_id, descriptor, use="signing"):
'''
Returns certificates for the given Entity
'''
ent = self[entity_id]

Failing at this part

I do not know where I am erring, I have not touched any of the code and am running as is
Python 3.8.5
Django 3.0.7

Can anybody push me towards right direction?

Importing issue

@mhindery
Hello, I'm trying to import ServiceProvider into my project, but when I do the following:
from djangosaml2idp.models import *
the following error happens
ImportError: No module named models, Any idea what's happening here?
Also, there's no file named models.py in the path after I do pip install djangosaml2idp, your help will be highly appreciated.

MDQ metadata configuration doesn't works

Configuring djangosaml2idp in this way:

    'metadata': [{
        # TODO in djangosaml2idp!
        "mdq": [
            {
                "url": "http://sp1.testunical.it:8001/",
                "cert": "",
            },
        ],

djangosaml2idp.utils.get_idp_config will get IDP.metadata as None.
I think that we should move in this direction:

https://github.com/IdentityPython/pysaml2/blob/a0c8cb289b03c700ca166ab282719976cb273a91/docs/howto/config.rst#metadata

MDQ protocol is the only way to manage a large number of entity with a centralized Service.

[Additional feature] Default sign and enc algorithm for authn responses and SLO

In pySAML2 the default sign and enc algorithm is the poor sha1.
I pushed a PR to get this improvement but I think that it will take a lot of time, this is here: IdentityPython/pysaml2#597

As djangosaml2 already does it we could introduce the possibility to force alternative xmlsec1 supported algs. djangosaml2 uses this method:
https://github.com/knaperek/djangosaml2/blob/master/djangosaml2/views.py#L179
In other words in pySAML2 each assertion builder method (request or response) have the sign_alg and dig_alg arguments. in djangosaml2idp we could simply put these in the call, if any sign/enc alg alternative is configured in settings.py.

There could be also this alternative as a global patch (to be tested):
IdentityPython/djangosaml2#115

What do you think about this? Sha1 is too weak and some federation simply rejects this.

Styling

Change defaults

I am working on a project where I want to configure some defaults like the default Processor to be used and default Attribute mapping. However I can't seem to find a way to easily change these. Can anyone point me in a direction on how to do this?

Bug: TypeError: unhashable type: 'RequestedAuthnContext'

After such request:

[19/Mar/2019 21:10:58] "GET /idp/sso/redirect?SAMLRequest=fVNNj9owEL3vr0C5J44XwrIWRKLQDyQKEaQ99FKZeOhacuzUnuzSf1%2FHYSmtuvjgSON5b2bem0wdr1XD5i0%2B6R38bMHh3cCfU620Y%2BFxFrVWM8OddEzzGhzDiu3nn9fsPklZYw2ayqjoH9htFHcOLEqje9hqOYu2m%2Ffr7cfV5juMKM1Gk3FGsxRSfqDjDOBhKIajQzqhgo75IX1IM95Dv4J1nmcWedo%2BUljzLAXYja86i0CcElW7BP1kybmacy2stEOu0eNS%2Bhinw5g%2BlveU0ZRlk2993tJDpOYY6J8QG0aIcyYxDWgQbaAkUjRdkFgQ0kKFlx6CKu%2BkFlL%2FuC3GoU9y7FNZFnGx3Zc9yfxVpIXRrq3B7sE%2Bywq%2B7NaXfq7HI9y7SCpTNwoQCDY87pwgPVse7mkXYUEBm7%2FJEWA1IBcceeJNnZJr3B%2BmhnUqr5aFUbL6FeLd%2BWBszfHtqWlCQ0SK%2BBhSWatdA5U8ShDRhWaulHlZWODojUTbQjQgfxU%2FbyyIsL9eJoQTDhZeAW6l62yDE%2Fee5BfKfv7r9IXyy7iDY35zXytWdXk%2BXPjPi7Gis9j7DaK03DdvLJ5F%2Bi953zW50XZ%2B9%2Fp8%2FTPmvwE%3D&RelayState=ssotest HTTP/1.0" 302 0

I've got an error:

Internal Server Error: /idp/login/process/
Traceback (most recent call last):
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner
    response = get_response(request)
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 128, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/django/views/generic/base.py", line 69, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/django/utils/decorators.py", line 62, in _wrapper
    return bound_func(*args, **kwargs)
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/django/utils/decorators.py", line 58, in bound_func
    return func.__get__(self, type(self))(*args2, **kwargs2)
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/django/contrib/auth/mixins.py", line 52, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/djangosaml2idp/views.py", line 83, in dispatch
    return super(IdPHandlerViewMixin, self).dispatch(request, *args, **kwargs)
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/django/views/generic/base.py", line 89, in dispatch
    return handler(request, *args, **kwargs)
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/djangosaml2idp/views.py", line 153, in get
    AUTHN_BROKER.add(authn_context_class_ref(req_authn_context), "")
  File "/Users/dmitry/.pyenv/versions/sso-edx-venv/lib/python3.6/site-packages/saml2/authn_context/__init__.py", line 96, in add
    self.db["key"][key].append(_ref)
TypeError: unhashable type: 'RequestedAuthnContext'
[19/Mar/2019 21:10:58] "GET /idp/login/process/ HTTP/1.0" 500 136835

Could someone help me please? is it a bug or i do something wrong?

Multi Factor Authentication support

I wish to implement multi factor authentication and did also find the documentation page, but to be honest I don't see how to implement this. In my project I am already using a custom authentication module and a custom middleware to enforce users to the login page, token select page and token pin input page until a valid session exists.

I suppose my token select view and token pin input view need to subclass djangosaml2idp.views.ProcessMultiFactorView? Is it possible to have more than one view subclassing ProcessMultiFactorView?

Do you mind extending the documentation with an example?

Many thanks,
Raoul

[Bug] get_nameid_email() got an unexpected keyword argument 'sp_entityid'

I get the following error when I try an SP initiated login.

[djangosaml2idp.views] [views.py:90] {'exception': TypeError("get_nameid_email() got an unexpected keyword argument 'sp_entityid'",), 'status': 500}

This issue seems to be because kwargs is passed to the get_nameid_email( callback function returned by getattr ) in line 73. The get_nameid_email function definition does not specify kwargs as a parameter.

Bug: SAMLRequest Session Lost

I've been bumping into a really odd bug lately, and I can't seem to get anywhere with it, wanted to ask if you have any ideas @mhindery

The error is:
KeyError at /idp/login/process/'SAMLRequest'

It occurs at LoginProcessView, when the SAMLRequest is pulled from the session after authentication. It seems that for some odd reason, in some cases, the request session is wiped after being set in the sso entry view, but not immediately? Here's what I've tested so far:

  • I go to the SSO entry view with a valid SAMLRequest. It's stored in the session (tested by printing to console).
  • I get redirected to a login page (it's not an issue with the login page, as I manually added in a redirect to a completely blank page, and the same issue occured). Prior to page load, the request session is still there (tested by printing to console).
  • If I redirect the page, or visit any other page, the request session is gone. Essentially, it looks like sometimes, something is prompting the browser to unset the session cookie after visiting the sso entry page.

This bug is "fixed" on the client side by clearing cache and cookies. While I know this, it's also been happening to my end users which is an issue. I'm particularly confused because I'm not sure what exactly causes this to spring up. Any thoughts?

Better exception handling

There are a few places where we're throwing Exception as well as ValueError. I think for sure with the generic Exception we should be more specific about what is being thrown. If we want to define our own exceptions that would probably be the path. I think this would be much clearer as to what is going on.

Service Provider metadata - store in DB instead of using config files?

I'm not actually using this project - more I'm investigating options - but I have what is probably a naive question.

Your documentation says you have to provide configuration for every Service Provider in a Python dictionary. Would it make more sense to store this metadata using a Django model in the DB so this process is dynamic? Or is that a design decision that can't easily be modified?

Thanks

Trying to intgrate with AWS cognito

Getting error on Successful login

UnknownSystemEntity at /idp/login/process/
urn:amazon:cognito:sp:us-east-2_KstJKAXym

image

I know it might be out of scope for this package as it is not mentioned anywhere that it is for AWS. But I am hoping you can point me out in the right direction as SAML is quite new for me.

Thanks

[Feature Request] Management command to download, validate and store SP metadata

We actually use wget/curl to download a metadata and store this in our project's relative path.
This work if the metadata file is a valid xml file, if this file is a 404 page-not-found instead, the IDP will incour in a general Exception. This will block the IDP globally in error 500.

In a real production scenario we would:

  • have a list of metadata sp url and for each of them:
    • download a metadata inMemory
    • create a mdstore to validate xml
    • validate certs (if they are expired...)
    • then store as file

Some changements in settings.py would be also came, there's need of:

  • a list of sp metadata download url as list of federated SP
  • a brand new definition of sp metadata like follows:
 'metadata': [{
        "class": "saml2.mdstore.MetaDataFile",
        "metadata": [call_to_function_that_returns_a_list_of_previoously_validated_fpath_metadata()],
    }],

My idea is to create a django management command to accomplish this, at this moment I'm on it.
A cron script should run this scheduled check and update. Any Ideas?

Assersion Signature Verification

Hello can you point me on where exactly in the example project , the signature verification on the SAMLResponse content is being done?
I know you are using xmlsecurity but can't identify where this check is done in the code, i only find the import of xmlsec in settings.py

Thanks

[Additional feature] User definitions and attributes from different sources

There would be the possibility to get users attributes from different sources, different Databases.
Actually the User is authenticated through the available Django Auth backends and then its identity and attributes will be fetched from the USER model as default, after that they have been saved as a new instance or updated as well. This is a quite common scenario.

In my case I have to deal with LDAP user definitions, throught this:
https://github.com/peppelinux/django-ldap-academia-ou-manager

I'm thinnking about a custom Processor, I'm also asking here if there would be the need of some more examples in the Documentation, just to show that "Yes, It can do ALSO this!".
An idea could be represented as follow

SAML_IDP_SPCONFIG = {
    'http://localhost:8000/saml2/metadata/': {
        'processor': 'djangosaml2idp.processors.BaseProcessor',
        'attribute_mapping': {
            # DJANGO: SAML
            'is_staff': 'is_staff',
            'is_superuser': 'is_superuser',
            'email': {'name': 'get_last_email', 'from': 'ldap_peoples.models.LdapAcademiaUser', 'lookup': ('email', '__in', 'emails')}
            'first_name': {'name': 'first_name', 'from': '$app_name.models.$ModelName', rewrite_rules: [all_lowercase, ], 'lookup': ('username', '__iexact', 'username') }
            'last_name':  {'name': 'last_name', 'from': '$app_name.models.$ModelName', rewrite_rules: [all_lowercase, ], 'lookup': ('username', '__iexact', 'nameid') }
            'eppn': {'name': 'eduPersonPrincipalName', 'from': 'ldap_peoples.models.LdapAcademiaUser', rewrite_rules: [add_scoped_org_name, ],  'lookup': ('username', '__iexact', 'uid') }
            'givenName':  {'rewrite_rules': [name_surname_concat, as_title] }
        }
    }
}

Where:
from: could be the Model where to get this attribute
name: could be a field name, or a class method or a property of the from: model name. This could help us to define a specilized method to gathering something more regarding 1-to-Many and FK relationships.
rewrite_rules: a list of concatenated rewrite functions that takes all the attributes to clean, modify, concatenate and do whatever action over them then returning the one (it could be also a list() in SAML context)
lookup: a tuple containing (USER field name, 'exact_to', 'Destination app.model.field_name')

Also see that we have legacy definitions as 'is_staff': 'is_staff',, so the Processors then have to check if that mapping is a string or a dictionary and deal with the easy or the complex way.

The Processor could have also to recheck pysaml2 config policies to prevent that a rewrite rule could bypass one of these.

Probably defining a custom method in the model would be more easy to deal with, instead of complex mapping definitions. I already talked about attr_rewrite rules here

[Improvement] Generic template to Handle errors messages and links

I'd like to manage errors messages, as HttpResponseBadRequest or other raw HttpResponse messages with a template, to get them styled and good looking.

My purpose start from the following structure:

  1. Create a custom_message.html template, in it we'll manage three variables:
    1. Message (title)
    2. Description (some words to explain what matters)
    3. web link (Button to get out from there)

Often in a SAML IDP when it get in a stale request or in a timeout, returns with strange errors. In django we can manage this easily and so I'd like to get it ready this way :)

[Bug] render_login_html_to_string() missing 1 required positional argument: 'template_name'

I get this error during an SP initiated Login.

render_login_html_to_string() missing 1 required positional argument: 'template_name'

Here is the stack trace

[django.request] Internal Server Error: /saml2/login/process/
Traceback (most recent call last):
File "/Users/admin/Virtual_Envs/myproject/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/Users/admin/Virtual_Envs/myproject/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/admin/Virtual_Envs/myproject/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/admin/Virtual_Envs/myproject/lib/python3.6/site-packages/django/views/generic/base.py", line 71, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/admin/Virtual_Envs/myproject/lib/python3.6/site-packages/django/utils/decorators.py", line 45, in _wrapper
return bound_method(*args, **kwargs)
File "/Users/admin/Virtual_Envs/myproject/lib/python3.6/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "/Users/admin/Virtual_Envs/myproject/lib/python3.6/site-packages/django/contrib/auth/mixins.py", line 52, in dispatch
return super().dispatch(request, *args, **kwargs)
File "/Users/admin/Virtual_Envs/myproject/lib/python3.6/site-packages/djangosaml2idp/views.py", line 102, in dispatch
return super().dispatch(request, *args, **kwargs)
File "/Users/admin/Virtual_Envs/myproject/lib/python3.6/site-packages/django/views/generic/base.py", line 97, in dispatch
return handler(request, *args, **kwargs)
File "/Users/admin/Virtual_Envs/myproject/lib/python3.6/site-packages/djangosaml2idp/views.py", line 291, in get
relay_state=request.session['RelayState'])
File "/Users/admin/Virtual_Envs/myproject/lib/python3.6/site-packages/djangosaml2idp/views.py", line 210, in create_html_response
"data": self.render_login_html_to_string(context=context, request=request),
TypeError: render_login_html_to_string() missing 1 required positional argument: 'template_name'

The issue seems to be because template_name is not passed to render_login_html_to_string()

IndexError at /saml2/login/

I tried using your examples in Docker but when I clicked "Login" I got the following error. Was I supposed to configure the SP? I assumed it was already configured. Thanks

Environment:


Request Method: GET
Request URL: http://localhost:8000/saml2/login/

Django Version: 1.11.13
Python Version: 2.7.14
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'djangosaml2']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "/usr/lib/python2.7/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/usr/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/usr/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/usr/lib/python2.7/site-packages/djangosaml2/views.py" in login
  148.     supported_bindings = get_idp_sso_supported_bindings(selected_idp, config=conf)

File "/usr/lib/python2.7/site-packages/djangosaml2/utils.py" in get_idp_sso_supported_bindings
  49.         idp_entity_id = list(available_idps(config).keys()).pop()

Exception Type: IndexError at /saml2/login/
Exception Value: pop from empty list

Add option not to use error handler

Sometimes, it might be better to let exceptions reach Django so that the debugging system can be used. A setting should added for whether to use djangosaml2idp's error handling system

[Additional feature] NameID formats and custom implementations

Hi, I would start from this good discussion I found here:
https://stackoverflow.com/questions/11693297/what-are-the-different-nameid-format-used-for

We should deal with a way to manage PersistentID based on a local (database) storage.
I have to check some other pysaml2 internals before doing some implementation but, before all, here we are for discussion

Here also a reference about how Shibboleth does this:
https://wiki.shibboleth.net/confluence/display/IDP30/NameIDGenerationConfiguration

sp_name_qualifier is acs destination instead of entity_id?

First of all, thanks for the tool, sorry if this is a silly question,

I have my IDP settings containing:
'name_id_format': [NAMEID_FORMAT_EMAILADDRESS, NAMEID_FORMAT_UNSPECIFIED]

and my service provider metadata contains
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>

my auth library (onelogin's python-saml) has this config:
"NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",

When i try to process the SAMLResponse, i got this error:

Traceback (most recent call last):
  File ".../test_saml.py", line 58, in test_full_saml
    auth.process_response()
  File ".../.tox/py34/lib/python3.4/site-packages/onelogin/saml2/auth.py", line 107, in process_response
    self.__nameid = response.get_nameid()
  File ".../.tox/py34/lib/python3.4/site-packages/onelogin/saml2/response.py", line 457, in get_nameid
    nameid_data = self.get_nameid_data()
  File ".../.tox/py34/lib/python3.4/site-packages/onelogin/saml2/response.py", line 443, in get_nameid_data
    OneLogin_Saml2_ValidationError.SP_NAME_QUALIFIER_NAME_MISMATCH
nose.proxy.OneLogin_Saml2_ValidationError: The SPNameQualifier value mistmatch the SP entityID value.

And i found out that it is expecting my entityID and not my acs destination, which are not the same:
(From my sp_metadata.xml)
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="..." cacheDuration="..." entityID="https://my-sp.example.org/metadata/">
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://my-sp.example.org/?acs" index="1"/>

I found out this code
name_id=NameID(format=resp_args['name_id_policy'].format, sp_name_qualifier=resp_args['destination'], text=user_id),
at https://github.com/OTA-Insight/djangosaml2idp/blob/master/djangosaml2idp/views.py#L149

and fixed my problem by passing
sp_name_qualifier=resp_args['sp_entity_id']

So i'm wondering if my service provider or my identity is not setup properly or your use case just happend to have the entity id and the acs being the same?

I may have got something wrong, any help is appreciated!

Persistent ID

In some cases, I feel like having a persistent ID could be valuable. @mhindery Wanted to run my idea for execution by you before making a PR:

  1. Set up a PersistentID model that has a OneToOne relationship with the AUTH_USER model
  2. If NAMEID is requested to be Persistent, check if the user has a persistentid. If so, send that. If not, make a new one, with the actual persistentid being a randomly generated uuid.

[Additional Feature] SLO views (Single Logout)

We should be able to expose a correct web resource for managing Single Logout requests, in HTTP-POST (SOAP), from SP.

Actually we have only these

    path('sso/post', views.sso_entry, name="saml_login_post"),
    path('sso/redirect', views.sso_entry, name="saml_login_redirect"),
    path('sso/init', views.SSOInitView.as_view(), name="saml_idp_init"),
    path('login/process/', views.LoginProcessView.as_view(), name='saml_login_process'),
    path('login/process_multi_factor/', views.ProcessMultiFactorView.as_view(), name='saml_multi_factor'),
    path('metadata/', views.metadata, name='saml2_idp_metadata'),
]

Could we develop also the following, I apologize that I still have to read some more source code from pysaml2 example folder to get this more defined here.

    path('slo/post', views.sso_entry, name="saml_slo_post"),
    path('slo/init', views.SLOInitView.as_view(), name="saml_slo_init"),

Any ideas or suggestions?

IdP initiated login

Hi,
Does this project support creating an IdP initiated login request?

While Symantec don't spell it out, I believe that is what is required for using their VIP 2nd factor only service - it is described at 'VIP_Login.pdf' on https://support.symantec.com/en_US/article.INFO3479.html (page 7).

I am able use Symantecs IdP ("First and second factor VIP login") or I'm able to use djangosaml2idp to authenticate users successfully. Now I'm trying to tie the two together :)

In case its relevant, I'm using djangopysaml2 for my SP.

thanks,

No module named 'djangosaml2idp'

I tried to run docker-compose up for the example setup but i get this error:

djangosaml2idp_idp exited with code 1
djangosaml2idp_idp | Traceback (most recent call last):
djangosaml2idp_idp | File "manage.py", line 22, in
djangosaml2idp_idp | execute_from_command_line(sys.argv)
djangosaml2idp_idp | File "/usr/local/lib/python3.6/site-packages/django/core/management/init.py", line 381, in execute_from_command_line
djangosaml2idp_idp | utility.execute()
djangosaml2idp_idp | File "/usr/local/lib/python3.6/site-packages/django/core/management/init.py", line 357, in execute
djangosaml2idp_idp | django.setup()
djangosaml2idp_idp | File "/usr/local/lib/python3.6/site-packages/django/init.py", line 24, in setup
djangosaml2idp_idp | apps.populate(settings.INSTALLED_APPS)
djangosaml2idp_idp | File "/usr/local/lib/python3.6/site-packages/django/apps/registry.py", line 91, in populate
djangosaml2idp_idp | app_config = AppConfig.create(entry)
djangosaml2idp_idp | File "/usr/local/lib/python3.6/site-packages/django/apps/config.py", line 90, in create
djangosaml2idp_idp | module = import_module(entry)
djangosaml2idp_idp | File "/usr/local/lib/python3.6/importlib/init.py", line 126, in import_module
djangosaml2idp_idp | return _bootstrap._gcd_import(name[level:], package, level)
djangosaml2idp_idp | File "", line 994, in _gcd_import
djangosaml2idp_idp | File "", line 971, in _find_and_load
djangosaml2idp_idp | File "", line 953, in _find_and_load_unlocked
djangosaml2idp_idp | ModuleNotFoundError: No module named 'djangosaml2idp'

SP requested a name_id_format that is not supported in the IDP with samltest.id

Error during SAML2 authentication
ImproperlyConfigured
SP requested a name_id_format that is not supported in the IDP

I tested the IDP with https://samltest.id's SP and it returned the error above.

Line 164 in the views.py file returns None
sp_config['name_id_format'] = policy.format

I'm not sure whether it's because samltest.id is returning a nameID format that is not compatible with Python's saml2 or something else.

Example fails with Error during SAML2 authentication

I manage to go around the #81 by running both instances manually instead of using docker-compose.

  1. Run both instances with its migrations.
  2. go to SP url, login with a user created on the SP database and:

Screen Shot 2020-03-10 at 9 27 24 PM

  1. Follow steps to regenerate certificates -> same error

Ability to define custom SAML Server class

Would be nice to be able to define a custom Server class used out of the saml2 library. We use AWS XRay as an APM and often have to override methods on classes in packages to annotate the values we want to see.

Maybe something like this.

# settings.py
SAML2_SERVER_CLASS = "path.to.MyCustomClass"

Then IdPHandlerViewMixin could use the custom one or default to the standard class.

1.0.0 Release Roadmap

Djangosaml2idp Roadmap

Considering the quantity of modifications that @peppelinux and I have been adding to this project, we thought it would be a good idea to put together a formal roadmap documenting what we've done so far, and what we would like to include for release.

Main Ideas

  • Code Readability and Quality
  • Comprehensive Test Coverage
  • New Features
  • Security
  • Assorted Bugfixes
  • CI/CD and Release Logistics

Code Readability and Quality

  • Minimizing Repetition (DRY)
  • Code Style Enforcement
  • Comprehensive Documentation
  • Improved Debug Logging

Comprehensive Test Coverage

  • Tests for utils
  • Tests with a simulated SAMLRequest
  • Other Tests

New Features

  • User Agreement Screen
  • Override global configs per-SP
  • SLO Support
  • Better Error Handling

Security

  • Check signature validation for incoming SAMLRequests

Assorted Bugfixes

  • Assorted Bugfixes

CI/CD and Release Logistics

  • Do we want to add bots for code coverage/etc?

Interested in a PR?

Hi,

I've forked your repo and made a few amends including some changes to make the module more extensible. I'm also building a set of tests.

https://github.com/lgarvey/djangosaml2idp

I'm wondering if you'd be interested in reviewing, and potentially merging these? If so I'll raise a PR.

Cheers,
Lyndon

Custom Processors not working

We need to send a few additional attributes in our usecase. However , we seem to be getting a crash when we use a custom processor. Additionally , only the username attribute is present in the SAML response. Could you look into this? I see that there's a recent commit that I believe addresses this issue.

If I'm right , could you fast track the process and make the latest release soon? We would really appreciate it. Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.