Git Product home page Git Product logo

djangosaml2's Introduction

djangosaml2

CI build pypi Downloads Documentation Status License Python versions Django versions

A Django application that builds a Fully Compliant SAML2 Service Provider on top of PySAML2 library. Djangosaml2 protects your project with a SAML2 SSO Authentication.

Features:

  • HTTP-REDIRECT SSO Binding
  • HTTP-POST SSO Binding
  • SLO POST and HTTP-REDIRECT Binding
  • Discovery Service
  • embedded Wayf page with customizable html template
  • IdP Hinting
  • IdP Scoping
  • Samesite cookie

Please consult the official Documentation of djangosaml2 to get started.

Contributing

Please open Issues to start debate regarding the requested features, or the patch that you would apply. We do not use a strict submission format, please try to be more concise as possible.

The Pull Request MUST be done on the dev branch, please don't push code directly on the master branch.

Special thanks

This is a community-driven project, born as a fork and maintained by different authors at different times, such as:

djangosaml2's People

Contributors

andy-miracl avatar chander avatar francoisfreitag avatar g-as avatar ganiserb avatar goetzk avatar hutchison avatar inducer avatar jaap3 avatar jdufresne avatar jeroenvo avatar joetsoi avatar knaperek avatar lgarvey avatar liquidpele avatar lorenzogil avatar lucyeun-alation avatar marcelkornblum avatar mhindery avatar mohameda95 avatar mx-moth avatar oskarpersson avatar pandafy avatar pauldekkers avatar peppelinux avatar plojyon avatar prauscher avatar rouganstriker avatar tymees avatar webspider 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  avatar  avatar

djangosaml2's Issues

pre_user_save signal not sent

I've been trying to get the pre_user_save signal to work. I finally figured out that the problem is in the backend.py module. The call to send_robust sets the sender to the user instance object instead of the User class. when I changed "sender=user" to "sender=user.class" my code immediately started receiving the signals.

All new user creations fail when an user with username='' exists

I have set email as the main attribute in my configuration (SAML_DJANGO_USER_MAIN_ATTRIBUTE = 'email') and I have a User model where both username and email are set to unique=True.
If I already have, in the database, an user with username='' (which is the default value), all new user creations fail with the error:

Key (username)=() already exists
djangosaml2/backends.py, line 126, in authenticate
user, created = User.objects.get_or_create(**user_query_args)

From what I can understand of the code, when the user doesn't exist, authenticate() creates an user with only the main_attribute set (and all the rest left to default values), get_or_create() it, and only then populates the attributes. So, if populating the attributes fail just once, you're left with a bogus User in the db that blocks everything.

Single Logout Requires Request from Provider?

I've been debugging the Single Logout with a Salesforce SAML SSO and would love some clarification on whether I'm just missing something or whether Salesforce is dropping the ball somewhere on their end (this old comment makes me think it's salesforce).

Watching the debug statements - it gets down through logout until Line #377's "Returning form to the IdP to continue the logout process" but then I never see any of the logs outputted in do_logout_service (like "Receiving a logout response from the IdP").

The single sign out doesn't succeed unless logout_service_post or logout_service get hit with a SAML response, right?

Thanks in advance.

Missing post_binding_form.html prevents login

Hi,
This looks similar to #58 , Let me know if I should follow up there.

The SAML login view says that when the post_binding_form is missing the default will be rendered; however I just copped an error.

https://github.com/knaperek/djangosaml2/blob/2f2c9d899e498ae7b8b291a384e58fd8d5110519/djangosaml2/views.py#L75

def login(request,
[...]
          post_binding_form_template='djangosaml2/post_binding_form.html'):
[...]
    * post_binding_form_template - path to a template containing HTML form with
    hidden input elements, used to send the SAML message data when HTTP POST
    binding is being used. You can customize this template to include custom
    branding and/or text explaining the automatic redirection process. Please
    see the example template in
    templates/djangosaml2/example_post_binding_form.html
    If set to None or nonexistent template, default form from the saml2 library
    will be rendered.
Request Method: | GET
-- | --
http://127.0.0.1:8000/VIP/login/
1.8
TemplateDoesNotExist
djangosaml2/post_binding_form.html
/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/site-packages/django/template/loader.py in get_template, line 46
/Users/kgoetz/virtualenvs/django-symantecvip/bin/python
2.7.13
['/Users/kgoetz/symantectest/symantectest/attribute-maps',  '/Users/kgoetz/symantectest',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python27.zip',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/plat-darwin',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/plat-mac',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/plat-mac/lib-scriptpackages',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/lib-tk',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/lib-old',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/lib-dynload',  '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7',  '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',  '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',  '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',  '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/site-packages']
Tue, 15 Aug 2017 04:40:03 +0000

I'm not sure if the comment or behaviour is wrong, but would you be open to making the example the actual page? people will still be able to override it using the usual mechanisms.

'Unknown attribute name' - Attribute Statement not recognized

I'm using django-saml2 on python 2.7, Django 1.10 to do authentication with Google Apps SSO. Authentication happens using the NameID value, so works, but the Attribute Statement received does not seem to be recognized by django-saml2. In the logs I can see the Attributes existing and having values sent (by Google in this case). However, each of the (3 in this case) attributes throw an error 'Unknown Attribute name'.
screen shot 2016-12-22 at 15 51 51

Because of this (at least I think it is), I get an error 'The attributes dictionary is empty' later down the line (also note the empty AVA dict):

screen shot 2016-12-22 at 15 56 45

I tried debugging, but it's rather hard to find the root of the problem. Am I using somehow an erronuous configuration setting requiring an incompatible format for something?

Update CHANGES with details since 14.0

Hey there,

Just wondering if, on the next release, you could update the CHANGES file with details of the minor version changes since 14.0 as it's missing them in the PYPI details?

Cheers!

UnicodeDecodeError in setup.py python3

Collecting Django==1.11.5 (from -r /opt/app/requirements.txt (line 1))
  Downloading Django-1.11.5-py2.py3-none-any.whl (6.9MB)
Collecting django-debug-toolbar==1.8 (from -r /opt/app/requirements.txt (line 2))
  Downloading django_debug_toolbar-1.8-py2.py3-none-any.whl (205kB)
Collecting django-environ==0.4. (from -r /opt/app/requirements.txt (line 3))
  Downloading django-environ-0.4.0.tar.gz
Collecting django-extensions==1.7.9 (from -r /opt/app/requirements.txt (line 4))
  Downloading django_extensions-1.7.9-py2.py3-none-any.whl (203kB)
Collecting django-jet==1.0.6 (from -r /opt/app/requirements.txt (line 5))
  Downloading django-jet-1.0.6.tar.gz (822kB)
Collecting django-scim2==0.2.22 (from -r /opt/app/requirements.txt (line 6))
  Downloading django-scim2-0.2.22.tar.gz
Collecting djangorestframework==3.6.3 (from -r /opt/app/requirements.txt (line 7))
  Downloading djangorestframework-3.6.3-py2.py3-none-any.whl (1.3MB)
Collecting djangosaml2 (from -r /opt/app/requirements.txt (line 8))
  Downloading djangosaml2-0.16.10.tar.gz (73kB)
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-pu5rye8r/djangosaml2/setup.py", line 28, in <module>
        long_description='\n\n'.join([read('README.rst'), read('CHANGES')]),
      File "/tmp/pip-build-pu5rye8r/djangosaml2/setup.py", line 21, in read
        return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
      File "/usr/lib64/python3.6/encodings/ascii.py", line 26, in decode
        return codecs.ascii_decode(input, self.errors)[0]
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 2544: ordinal not in range(128)
    
    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-pu5rye8r/djangosaml2/

Django 1.11.5, Python 3.6.2, CentOS 7.3.1611, djangosaml2 0.16.10

'NoneType' object has no attribute 'name_qualifier'

I investigated this error and discovered it was due to being logged in via django admin not through SAML. I went looking and found it had been independently reported, investigated and closed already:
https://bitbucket.org/knaperek/djangosaml2/issues/1/getting-nonetype-object-has-no-attribute

Would it be possible to change the error to include words along the lines of "Have you authenticated via SAML?". It seems the most likely cause of the error condition being hit and would help with debugging a new install.

Why the versions pin on python-memcached

In DjangoSAML2 0.14.4 there is a python-memcached 1.48.
I have python-memcached pinned to a newer version in my project.

This version pin is removed in the master branch.

Any reason for this pin? And if not, could you release a new version?

Thanks.

Multiple attribute values

I am trying to retrieve a user's list of groups from LDAP via SAML2 attributes. I have the SAML2 assertion returning an attribute with multiple AttributeValue elements which looks like this :

      <saml:Attribute Name="some_prefix:memberOf" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
        <saml:AttributeValue>group1</saml:AttributeValue>
        <saml:AttributeValue>group2</saml:AttributeValue>
        <saml:AttributeValue>group3</saml:AttributeValue>
      </saml:Attribute>

However djangosaml2 only reads the first AttributeValue from the list (i.e. attributes[saml_attr][0] ):

... backends.py...
def update_user(self, user, attributes, attribute_mapping,
                    force_save=False):

... code omitted ...

        user_modified = False
        profile_modified = False
        for saml_attr, django_attrs in attribute_mapping.items():
            try:
                for attr in django_attrs:
                    if hasattr(user, attr):
                        modified = self._set_attribute(
                            user, attr, **attributes[saml_attr][0]**)
                        user_modified = user_modified or modified

                    elif profile is not None and hasattr(profile, attr):
                        modified = self._set_attribute(
                            profile, attr, **attributes[saml_attr][0]**)
                        profile_modified = profile_modified or modified

            except KeyError:
                # the saml attribute is missing
                pass

... code omitted ...

I have a few ideas to fix this, but wondered what thoughts you might have about this issue to avoid going down the wrong track.

Thanks

Andy

Python 3 Support

I was wondering if anyone was working on Py3 support for this library? In the setup.py it isn't specified what version this library has been set up to use.

I can volunteer some time to work in Python 3 support if nobody's jumping onto this for the moment

WinError 5

So I'm working on getting SAML with G-Suite. When going to /saml2/login, it takes me to the correct google login page, I log in, it authenticates, but when I am returned to the /saml2/acs view, it throws the following error:
PermissionError at /saml2/acs/ [WinError 5] Access is denied

I have attempted to fix this, by setting access to all project directories & files to allow Everyone to access/modify them and even ran the server as an administrator. All to no prevail.

Here is my SAML Config:
saml_config.txt

PicklingError: Can't pickle <type 'module'>: attribute lookup __builtin__.module failed

With the latest version - when using memcache as a backend (pylibmc & python-memcache) both run into a pickling error (using django 1.9). I tried different session serializers and boiled it down to something in the DjangoSessionCacheAdapter. I now are at a point where I don't know any more specific what the error could be.

Traceback (most recent call last): File "/home/rkessler/repositories/kollect/venv-kollect/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 235, in get_response response = middleware_method(request, response) File "/home/rkessler/repositories/kollect/venv-kollect/local/lib/python2.7/site-packages/django/contrib/sessions/middleware.py", line 50, in process_response request.session.save() File "/home/rkessler/repositories/kollect/venv-kollect/local/lib/python2.7/site-packages/django/contrib/sessions/backends/cache.py", line 55, in save return self.create() File "/home/rkessler/repositories/kollect/venv-kollect/local/lib/python2.7/site-packages/django/contrib/sessions/backends/cache.py", line 44, in create self.save(must_create=True) File "/home/rkessler/repositories/kollect/venv-kollect/local/lib/python2.7/site-packages/django/contrib/sessions/backends/cache.py", line 62, in save self.get_expiry_age()) File "/home/rkessler/repositories/kollect/venv-kollect/local/lib/python2.7/site-packages/django/core/cache/backends/memcached.py", line 78, in add return self._cache.add(key, value, self.get_backend_timeout(timeout)) File "/home/rkessler/repositories/kollect/venv-kollect/local/lib/python2.7/site-packages/memcache.py", line 673, in add return self._set("add", key, val, time, min_compress_len, noreply) File "/home/rkessler/repositories/kollect/venv-kollect/local/lib/python2.7/site-packages/memcache.py", line 1060, in _set return _unsafe_set() File "/home/rkessler/repositories/kollect/venv-kollect/local/lib/python2.7/site-packages/memcache.py", line 1034, in _unsafe_set store_info = self._val_to_store_info(val, min_compress_len) File "/home/rkessler/repositories/kollect/venv-kollect/local/lib/python2.7/site-packages/memcache.py", line 998, in _val_to_store_info pickler.dump(val) PicklingError: Can't pickle <type 'module'>: attribute lookup __builtin__.module failed

How to pass authentication method

Hi,

Is it possible to set the AuthnContextClassRef element?

MS ADFS 4.0 doesn't seem to have to capability to specifiy per SP authentication methods so the SP has to request a certain method.

KeyError at /saml2/logout/

I am using djangosaml2 as SP, where IdP is auth0.com. I don't have any problems logging in, The redirect works and the assertion is posted. When I try to logout i get the following error:
KeyError at /saml2/logout/ '2=urn%3Aoasis%3Anames%3Atc%3ASAML%3A1.1%3Anameid-format%3Aunspecified,4=auth0%7C56e5560a9e61a71f4694c1d8'

I have look into the code, and after assertion the object client has the field 'users' with the field under the clinent.users.cache._db with the key given above. But when I try to logout the client.users.cache_db is empty.

Here is the Traceback

Request Method: GET
Request URL: http://localhost:8000/saml2/logout/
Django Version: 1.9.5
Python Version: 2.7.6
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.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']

Traceback:

File "/home/milutin/PycharmProjects/venvs/saml_sp/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  149.                     response = self.process_exception_by_middleware(e, request)

File "/home/milutin/PycharmProjects/venvs/saml_sp/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  147.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/milutin/PycharmProjects/venvs/saml_sp/local/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  23.                 return view_func(request, *args, **kwargs)

File "/home/milutin/PycharmProjects/venvs/saml_sp/local/lib/python2.7/site-packages/djangosaml2/views.py" in logout
  292.     result = client.global_logout(subject_id)

File "/home/milutin/PycharmProjects/venvs/saml_sp/local/lib/python2.7/site-packages/saml2/client.py" in global_logout
  161.         entity_ids = self.users.issuers_of_info(name_id)

File "/home/milutin/PycharmProjects/venvs/saml_sp/local/lib/python2.7/site-packages/saml2/population.py" in issuers_of_info
  46.         return self.cache.entities(name_id)

File "/home/milutin/PycharmProjects/venvs/saml_sp/local/lib/python2.7/site-packages/saml2/cache.py" in entities
  145.         return list(self._db[cni].keys())

Exception Type: KeyError at /saml2/logout/
Exception Value: '2=urn%3Aoasis%3Anames%3Atc%3ASAML%3A1.1%3Anameid-format%3Aunspecified,4=auth0%7C56e9560e9e61a7af469fc1d8'  

and the SAML settings

SESSION_EXPIRE_AT_BROWSER_CLOSE = True

from os import path
import saml2
from saml2.saml import NAMEID_FORMAT_PERSISTENT, NAMEID_FORMAT_UNSPECIFIED

BASEDIR = path.dirname(path.abspath(__file__))

SAML_ATTRIBUTE_MAPPING = {
    'http://schemas.auth0.com/user_id': ('username',),
}

SAML_CREATE_UNKNOWN_USER = True

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': 'http://localhost:8000/saml2/metadata/',
  'allow_unknown_attributes' : 'true',
  # directory with attribute mapping
  # this block states what services we provide
  'service': {
      # we are just a lonely SP
      'sp': {
          'authn_requests_signed': 'false',
          'logout_requests_signed': 'false',
          'allow_unsolicited': 'true',
          'name': 'Djangosaml2',
          'name_id_format': NAMEID_FORMAT_UNSPECIFIED,
          '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': ['username'],

           # 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
              'https://blahblahblah.auth0.com/samlp/metadata/blahblahblahblahblahblah': {
                  'single_sign_on_service': {
                      saml2.BINDING_HTTP_REDIRECT: 'https://blahblahblah.auth0.com/samlp/blahblahblahblahblahblah',
                      },
                  'single_logout_service': {
                      saml2.BINDING_HTTP_POST: 'https://blahblahblah.auth0.com/samlp/blahblahblahblahblahblah/logout',
                      },
                  },
              },
          },
      },

  # where the remote metadata is stored
  'metadata': {
      'local': [path.join(BASEDIR, 'remote_metadata.xml')],
      },

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

  # certificate
  'key_file': '', #path.join(BASEDIR, "mycert.key"),  # private part
  'cert_file': '', # path.join(BASEDIR, "mycert.pem"),  # public part

  # own metadata settings
  'contact_person': [
      {'given_name': 'Lorenzo',
       'sur_name': 'Gil',
       'company': 'Yaco Sistemas',
       'email_address': '[email protected]',
       'contact_type': 'technical'},
      {'given_name': 'Angel',
       'sur_name': 'Fernandez',
       'company': 'Yaco Sistemas',
       'email_address': '[email protected]',
       '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
  }

Django IdP package?

I am looking for a package to implement the IdP side of the saml2 scheme to allow authentication between our django instances/projects. Unfortunately, there does not seem to be a lot out there. The best I could find was https://github.com/mobify/dj-saml-idp/tree/master/saml2idp, which also does not look very active. I have yet to try if everything there is still functional. As we already use this package for the SP side, I'd prefer having the IdP functionality in the same package as well.

Is there any interest for implementing the IdP part in this package? This way there would be a single package for 'all' saml2 parts. Having one single actively maintained repository would be a great improvement over currently many outdated, abandoned and/or incomplete codebases to be found online, with each their own approach on how to do things.

crypto library function failed,unable to get local issuer certificate

Hi,
I have configured my app according to the setting suggested and following is the code

SAML_CONFIG = {
'allow_create': True,

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': 'https://kotak.phrazor.com/saml2/metadata/',

directory with attribute mapping

'attribute_map_dir': path.join(BASEDIR, 'attribute-maps'),
'allow_unknown_attributes': True,

this block states what services we provide

'service': {
# we are just a lonely SP
'sp' : {
"allow_unsolicited": True,
'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': [
('https://kotak.phrazor.com/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': [
('https://kotak.phrazor.com/saml2/ls/',
saml2.BINDING_HTTP_REDIRECT),
('https://kotak.phrazor.com/saml2/ls/post/',
saml2.BINDING_HTTP_POST),
],
},

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

      
      # 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
          'https://localhost/simplesaml/saml2/idp/metadata.php': {
              'single_sign_on_service': {
                  saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SSOService.php',
                  },
              'single_logout_service': {
                  saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SingleLogoutService.php',
                  },
              },
          },
      },
  },

where the remote metadata is stored

'metadata': {
'local': [path.join(BASEDIR, 'remote_metadata.xml')],
},

set to 1 to output debugging information

'debug': 1,

Signing

'key_file': path.join(BASEDIR, 'mycert.key'), # private part
'cert_file': path.join(BASEDIR, 'mycert.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': 'Sabyasachi',
'sur_name': 'Nandy',
'company': 'Vphrase',
'email_address': '[email protected]',
'contact_type': 'technical'},
],

you can set multilanguage information here

'organization': {
'name': [('Vphrase', 'es'), ('Vphrase', 'en')],
'display_name': [('Vphrase', 'es'), ('Vphrase', 'en')],
'url': [('https://www.vphrase.com', 'es'), ('https://www.vphrase.com', 'en')],
},
'valid_for': 24, # how long is our metadata valid
}

After running the app, we get directed to the IPD and get back the response , but the response cant be processed , and when i Checked the error log , we get the message
saml2.response.StatusInvalidNameidPolicy: urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy from urn:oasis:names:tc:SAML:2.0:status:Requester, and this ultimately comes from the error certificate verification failed:err=20;msg=unable to get local issuer certificate.

Any help on this topic,
Can you throw some more light about the keys used and their importance for the saml2?
In the signing key, should we use your public key or the public key of the IDP ?

User replacement in login process

Hi,
I'm not sure if i'm using this package wrongly or it is a bug. I'm using different identity providers for different users in my system. It seems that i'm able to login with different user upon successful authentication in 3rd party identity provider. Login in idp with one user but replace it with another in attributes leads to successful login that another user in my system.
Djangosaml2 used only as service provider.

Here is what i mean.
User redirected to okta idp and login with email [email protected] there. Idp configured in such way that it will pass "[email protected]" (different email!) in attributes. Djangosaml2 doing authentication by info from attributes so it will login user as [email protected] on service provider side and not as [email protected]. 3rd party Idp can substitute any email in attributes and djangosaml2 let it log in. But this both users use different idp in my system - okta and onelogin respectively and should not login instead of each other.

Is this my personal configuration problem or this package doesn't support multiply idp? Is there a way to restrict users to log in only from their corresponded idp out of the box?

DEBUG 2017-05-30 13:47:44,795 response 4789 140252821366256 --- Getting Identity ---
INFO 2017-05-30 13:47:44,795 response 4789 140252821366256 Subject NameID: <?xml version='1.0' encoding='UTF-8'?>
<saml:NameID xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">[email protected]</saml:NameID>
DEBUG 2017-05-30 13:47:44,795 response 4789 140252821366256 Attribute Statement: <?xml version='1.0' encoding='UTF-8'?>
<saml:AttributeStatement xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><saml:Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml:AttributeValue xsi:type="xs:string">[email protected]</saml:AttributeValue></saml:Attribute></saml:AttributeStatement>
DEBUG 2017-05-30 13:47:44,795 response 4789 140252821366256 --- AVA: {'email': [u'[email protected]']}
INFO 2017-05-30 13:47:44,796 client_base 4789 140252821366256 --- ADDED person info ----
DEBUG 2017-05-30 13:47:44,796 views 4789 140252821366256 Trying to authenticate the user
DEBUG 2017-05-30 13:47:44,798 backends 4789 140252821366256 attributes: {'email': [u'[email protected]']}
DEBUG 2017-05-30 13:47:44,798 backends 4789 140252821366256 attribute_mapping: {'cn': ('first_name',), 'email': ('email',), 'sn': ('last_name',), 'mail': ('email',), 'Login': ('email',), 'Email': ('email',), 'uid': ('username',)}
DEBUG 2017-05-30 13:47:44,798 backends 4789 140252821366256 Retrieving existing user "[email protected]"
DEBUG 2017-05-30 13:47:44,799 backends 4789 140252821366256 Sending the pre_save signal
DEBUG 2017-05-30 13:47:44,813 views 4789 140252821366256 Sending the post_authenticated signal
DEBUG 2017-05-30 13:47:44,813 views 4789 140252821366256 Redirecting to the RelayState: /
[30/May/2017 13:47:44] "POST /saml2/acs/ HTTP/1.1" 302 0
[30/May/2017 13:47:44] "GET / HTTP/1.1" 200 70056

SAML_ATTRIBUTE_MAPPING required if SAML_DJANGO_USER_MAIN_ATTRIBUTE = 'email'

Hi,
I have the following config (this is an excerpt):

SAML_USE_NAME_ID_AS_USERNAME = False
SAML_DJANGO_USER_MAIN_ATTRIBUTE = 'email'
SAML_DJANGO_USER_MAIN_ATTRIBUTE_LOOKUP = '__iexact'
SAML_CREATE_UNKNOWN_USER = False 

If that is all, I see this in my logs:

2017-08-24 16:00:22 DEBUG saml2.response: --- AVA: {'email': [u'[email protected]']}
2017-08-24 16:00:22 DEBUG saml2.response: --- AVA: {'email': [u'[email protected]']}
2017-08-24 16:00:22 INFO saml2.client_base: --- ADDED person info ----
2017-08-24 16:00:22 INFO saml2.client_base: --- ADDED person info ----
2017-08-24 16:00:22 DEBUG djangosaml2: Trying to authenticate the user
2017-08-24 16:00:22 DEBUG djangosaml2: Trying to authenticate the user
2017-08-24 16:00:22 DEBUG djangosaml2: attributes: {'email': [u'[email protected]']}
2017-08-24 16:00:22 DEBUG djangosaml2: attributes: {'email': [u'[email protected]']}
2017-08-24 16:00:22 DEBUG djangosaml2: attribute_mapping: {'uid': ('username',)}
2017-08-24 16:00:22 DEBUG djangosaml2: attribute_mapping: {'uid': ('username',)}
2017-08-24 16:00:22 ERROR djangosaml2: Could not find saml_user value
2017-08-24 16:00:22 ERROR djangosaml2: Could not find saml_user value
2017-08-24 16:00:22 ERROR djangosaml2: The user is None
2017-08-24 16:00:22 ERROR djangosaml2: The user is None

if i Add a SAML attribute mapping

SAML_ATTRIBUTE_MAPPING = {
    'uid': ('username', ),
    'email': ('email', ),
}

Everything appears to work correctly after that

2017-08-24 16:12:17 DEBUG saml2.response: --- AVA: {'email': [u'[email protected]']}
2017-08-24 16:12:17 DEBUG saml2.response: --- AVA: {'email': [u'[email protected]']}
2017-08-24 16:12:17 INFO saml2.client_base: --- ADDED person info ----
2017-08-24 16:12:17 INFO saml2.client_base: --- ADDED person info ----
2017-08-24 16:12:17 DEBUG djangosaml2: Trying to authenticate the user
2017-08-24 16:12:17 DEBUG djangosaml2: Trying to authenticate the user
2017-08-24 16:12:17 DEBUG djangosaml2: attributes: {'email': [u'[email protected]']}
2017-08-24 16:12:17 DEBUG djangosaml2: attributes: {'email': [u'[email protected]']}
2017-08-24 16:12:17 DEBUG djangosaml2: attribute_mapping: {'uid': ('username',), 'email': ('email',)}
2017-08-24 16:12:17 DEBUG djangosaml2: attribute_mapping: {'uid': ('username',), 'email': ('email',)}
2017-08-24 16:12:17 DEBUG djangosaml2: Retrieving existing user "[email protected]"
2017-08-24 16:12:17 DEBUG djangosaml2: Retrieving existing user "[email protected]"
2017-08-24 16:12:17 DEBUG djangosaml2: Sending the pre_save signal
2017-08-24 16:12:17 DEBUG djangosaml2: Sending the pre_save signal
2017-08-24 16:12:18 DEBUG djangosaml2: Sending the post_authenticated signal
2017-08-24 16:12:18 DEBUG djangosaml2: Sending the post_authenticated signal
2017-08-24 16:12:18 DEBUG djangosaml2: Redirecting to the RelayState: /dashboard
2017-08-24 16:12:18 DEBUG djangosaml2: Redirecting to the RelayState: /dashboard

I wasn't sure if I should try and submit a one line docs fix in the README or if this should be handled in code so opening an issue for discussion.

Nose test runner tries to run functions that aren't tests.

+ exec /opt/venv/bin/python manage.py test djangosaml2
[02/Oct/2017 18:19:33] DEBUG (139871688841024) [raven.contrib.django.client.DjangoClient:267] Configuring Raven for host: <raven.conf.remote.RemoteConfig object at 0x7f365dc13240>
[02/Oct/2017 18:19:33] INFO (139871688841024) [raven.contrib.django.client.DjangoClient:214] Raven is not configured (logging is disabled). Please see the documentation for more information.
nosetests djangosaml2 --verbosity=1
Creating test database for alias 'default'...
...Method Not Allowed (GET): /saml2/acs/
.......S.S
..EE
======================================================================
ERROR: djangosaml2.tests.test_config_loader
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/opt/venv/lib64/python3.6/site-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
TypeError: test_config_loader() missing 1 required positional argument: 'request'

======================================================================
ERROR: djangosaml2.tests.test_config_loader_with_real_conf
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/opt/venv/lib64/python3.6/site-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
TypeError: test_config_loader_with_real_conf() missing 1 required positional argument: 'request'

----------------------------------------------------------------------
Ran 17 tests in 3.243s

FAILED (SKIP=2, errors=2)

I'm using nose test runner in django and it's finding your two helper functions because they are named starting with test_ and trying to run them as tests, which they aren't. So these are essentially false positives, all the real tests are actually passing.

A) if I submitted a PR prefixing these functions with _ would you accept it?

OR

B) what django test runner are all the cool kids using these days? I heard nose is unmaintained but I'm struggling to find one that supports all of JUnit xml output, coverage reporting, and logging/stdout capture.

NameID is not JSON serializable when Logout

Hello.

As we were discussing here, the PR that invoked _set_objects fixes the KeyError issue. But now I have this other problem when trying to logout:

TypeError: <saml2.saml.NameID object at 0x7fcfcf5d1190> is not JSON serializable

I've tried to debug it adding logger messages to the logout view and it reaches the point of the HttpResponseRedirect. In fact, it generates the URL (and it works pasting it in the browser). But the internals of the Django view are failing after the return because there's an object non serializable in the field NameId.

By the way I've installed the last commit version of pysaml2. And I'm using a self-hosted (and self-configured) instance of SimpleSAMLphp as IdP, maybe the error is there.

Could you help me, please?

Thanks.

Readme for IdP inconsistent with pysaml2

Hi,
I think there is an inconsistency in you readme and the pysaml2 docs concerning the idp inside a sp block.
Your example:

             # 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
              'https://localhost/simplesaml/saml2/idp/metadata.php': {
                  'single_sign_on_service': {
                      saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SSOService.php',
                      },
                  'single_logout_service': {
                      saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SingleLogoutService.php',
                      },
                  },
              },

where as pysaml2 says that the idp definition inside an sp is just a list of valid idps that you can connect to. See https://github.com/rohe/pysaml2/blob/master/doc/howto/config.rst#idp

"service": {
    "sp": {
        "idp": ["urn:mace:umu.se:saml:roland:idp"],
    }
}

I think the confusion is because pysaml2 can also be used as idp itself (instead of an SP). In that case the idp block is in the service block, not inside the sp block, and then it does support all kinds of options, like endpoints. See https://github.com/rohe/pysaml2/blob/master/doc/howto/config.rst#idp-aa-sp

I found this because I was changing the endpoints for my SP in the services->sp->idp section, and they did not have any effect. Neither in login/logout behaviour nor in the generated metadata.

Can you explain this or update the readme?

Thanks for the plugin anyway, it works great!

AssertionError with djangosaml upon logout

I noticed while trying to get SAML logouts to work that I'm getting an assertion thrown from djangosaml. It looks like this affect both HTTP_POST and HTTP_REDIRECT bindings.

djangosaml2==0.16.0
pysaml2==4.4.0

Traceback...

...
  File "/usr/local/lib/python2.7/dist-packages/djangosaml2/views.py", line 422, in do_logout_service
    return HttpResponseRedirect(get_location(http_info))
  File "/usr/local/lib/python2.7/dist-packages/djangosaml2/utils.py", line 63, in get_location
    assert header_name == 'Location'
AssertionError

TLDR; the http_info dictionary returned pysaml2 doesn't set a 'Location' header in http_info['headers']. It does however set the destination url parsed from the SAML request into http_info['url'].

Not sure if this is a bug with pysaml2 (is the 'Location' header required?) or a bug with djangosaml. As far as I can tell this hasn't ever worked.

From what I see...
The 'url' key is set to the destination here
We see only the 'Content-type' header being added here
Where djangosaml is expecting the Location header

Happy to create a PR that looks for the redirect url in the http_info['url'] unless I'm missing something here.

ACS fails to authenticate user, even with create unknown user enabled.

I am integrating djangosaml2 into our Askbot application and am running into a problem.

The issue is when after I've gone to /saml/login/ been redirected to our IdP (which is a FortiAuthenticator), logged in, then redirected back to /saml/acs/.

From the log I see:

Assertion Consumer Service started
Trying to authenticate the user
The user is None

I printed out the value of session info and the attribute mapping:

Session Info:

{'authn_info': [('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', [], '2017-02-10T19:35:39Z')], 'name_id': <saml2.saml.NameID object at 0x7f3d4b9ea310>, 'not_on_or_after': 1486756239, 'session_index': None, 'came_from': u'/', 'ava': {}, 'issuer': '**redacted**'}

Attribute Mapping:

{'uid': ('username',)}

I'm not sure what to do at this point, or how to further debug the issue.

Logout error should allow returning some meaningful info to user

While implementing logout to an application, I ended up finding that if the logout does not end up successful, all djangosaml2 will do is return HttpResponse("Error during logout"). This is problematic as it's not possible to catch or override it any way. I'd like to instead show a proper page to the user indicating that logout didn't work etc.

I'd be happy to implement a patch for this - maybe by rendering the already existing logout error template djangosaml2/logout_error.html which app builders can then override? Any opinions?

The problem is here: https://github.com/knaperek/djangosaml2/blob/master/djangosaml2/views.py#L399 (and in my case status code just isn't correct and thus this ends here)

does not work with django 2.0

After upgrading to django 2.0 I receive a http 400

Looking it the logs of the provider gives me:

Unable to Base64 decode SAML message

Bump pysaml2 version from 4.4.0 to 4.5.0

Safety report (pyup.io):

[
    [
        "pysaml2", 
        "<=4.4.0", 
        "4.4.0", 
        "pysaml2 version 4.4.0 and older accept any password when run with python optimizations enabled. This allows attackers to log in as any user without knowing their password.", 
        "35700"
    ], 
    [
        "pysaml2", 
        "<=4.4.0", 
        "4.4.0", 
        "Python package pysaml2 version 4.4.0 and earlier reuses the initialization vector across encryptions in the IDP server, resulting in weak encryption of data.", 
        "35699"
    ]
]

EDIT: there is related PR #114

Out of nowhere: SignatureError: Signature missing for response

Since a few days, we've had error messages on saml2 authentication. The IdP is Google apps, our side is Django 1.11 on Python 2.7. The login has been working for months and suddenly stopped. Therefore I'd think this is not a case of wrong configuration. I checked the metadata xml config, and it's not a case of expired validity.

The error we're seeing is:

Signature Error: Signature missing for response
XML parse error: Signature missing for response
Invalid or malformed SAML Assertion.
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/djangosaml2/views.py", line 271, in assertion_consumer_service
    response = client.parse_authn_request_response(xmlstr, BINDING_HTTP_POST, outstanding_queries)
  File "/usr/local/lib/python2.7/dist-packages/saml2/client_base.py", line 702, in parse_authn_request_response
    binding, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/saml2/entity.py", line 1138, in _parse_response
    response = response.loads(xmlstr, False, origxml=origxml)
  File "/usr/local/lib/python2.7/dist-packages/saml2/response.py", line 512, in loads
    self._loads(xmldata, decode, origxml)
  File "/usr/local/lib/python2.7/dist-packages/saml2/response.py", line 337, in _loads
    **args)
  File "/usr/local/lib/python2.7/dist-packages/saml2/sigver.py", line 1738, in correctly_signed_response
    raise SignatureError("Signature missing for response")
SignatureError: Signature missing for response

Our SAML_CONFIG contains the item "authn_requests_signed": "true" However, this has been working correctly up until now, so I don't get why this would suddenly pop up. I tried commenting out this settings, and changing it to "false" (also tried python booleans instead of string values), but to no avail. When looking at the xml structure & content of the saml message, the signature is present:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<saml2p:Response 
    xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https://our_site/saml2/acs/" ID="some_id" InResponseTo="id-36dxayd46WoIHJnmc" IssueInstant="2017-10-15T10:14:58.964Z" Version="2.0">
    <saml2:Issuer 
        xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://accounts.google.com/o/saml2?idpid=our_google_idpid
    </saml2:Issuer>
    <saml2p:Status>
        <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
    </saml2p:Status>
    <saml2:Assertion 
        xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="session_id" IssueInstant="2017-10-15T10:14:58.964Z" Version="2.0">
        <saml2:Issuer>https://accounts.google.com/o/saml2?idpid=our_google_idpid</saml2:Issuer>
        <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/2001/04/xmldsig-more#rsa-sha256"/>
                <ds:Reference URI="#session_id">
                    <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/2001/04/xmlenc#sha256"/>
                    <ds:DigestValue>Cb/UaHp9...VBQ=</ds:DigestValue>
                </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue>mi+t3nw...jJJOr3XVx6fJIcblXE3wK0LR2hw==</ds:SignatureValue>
            <ds:KeyInfo>
                <ds:X509Data>
                    <ds:X509SubjectName>ST=California,C=US,OU=Google For Work,CN=Google,L=Mountain View,O=Google Inc.</ds:X509SubjectName>
                    <ds:X509Certificate>MIIDdDCCAlygAwIBAgIGAVjyQ98LMA0GCSqGSIb3DQEBCwUAMHsxFDASBgNVBAoTC0dvb2dsZSBJ
...
n0f8fRAlU4xmQ6F7P6W0S7Cx56ZxqfWRyrpQRR6Nt5XV</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </ds:Signature>
        <saml2:Subject>
            <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">my_name_id</saml2:NameID>
            <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <saml2:SubjectConfirmationData InResponseTo="id-36dxayd46WoIHJnmc" NotOnOrAfter="2017-10-15T10:19:58.964Z" Recipient="https://our_site/saml2/acs/"/>
            </saml2:SubjectConfirmation>
        </saml2:Subject>
        <saml2:Conditions NotBefore="2017-10-15T10:09:58.964Z" NotOnOrAfter="2017-10-15T10:19:58.964Z">
            <saml2:AudienceRestriction>
                <saml2:Audience>https://our_site/saml2/metadata/</saml2:Audience>
            </saml2:AudienceRestriction>
        </saml2:Conditions>
        <saml2:AttributeStatement>
            <saml2:Attribute Name="first_name">
                <saml2:AttributeValue 
                    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:anyType">Mathieu
                </saml2:AttributeValue>
            </saml2:Attribute>
            <saml2:Attribute Name="email">
                <saml2:AttributeValue 
                    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:anyType">my_name_id
                </saml2:AttributeValue>
            </saml2:Attribute>
        </saml2:AttributeStatement>
        <saml2:AuthnStatement AuthnInstant="2017-10-15T10:02:38.000Z" SessionIndex="session_id">
            <saml2:AuthnContext>
                <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml2:AuthnContextClassRef>
            </saml2:AuthnContext>
        </saml2:AuthnStatement>
    </saml2:Assertion>
</saml2p:Response>

We've been running pip version 0.16.1 , I tried switching to 0.16.10 but that failed due to the known issue with python2 which has been fixed on git, so I also tried installing from git master. Nothing works...

Any thoughts?

pop from empty list during login

Hi,

I'm seeing an error when trying to log in. The error is fair enough as I'd misconfigured my metadata, but I feel the wording should be more conducive to tracking down the problem.

Request Method: | GET
-- | --
http://127.0.0.1:8000/VIP/login/
1.8
IndexError
pop from empty list
/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/site-packages/djangosaml2/utils.py in get_idp_sso_supported_bindings, line 49
/Users/kgoetz/virtualenvs/django-symantecvip/bin/python
2.7.13
['/Users/kgoetz/symantectest/symantectest/attribute-maps',  '/Users/kgoetz/symantectest',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python27.zip',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/plat-darwin',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/plat-mac',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/plat-mac/lib-scriptpackages',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/lib-tk',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/lib-old',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/lib-dynload',  '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7',  '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',  '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',  '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',  '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',  '/Users/kgoetz/virtualenvs/django-symantecvip/lib/python2.7/site-packages']

I hope you will consider catching the error and raising something else in its place - would 'Unable to find metadata for IdP ' still be accurate enough to use?

thanks,

Pickling error

Hi! I'm trying to use this module and I'm stuck with this exception.

At LocMemCache.add:

def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): pickled = pickle.dumps(value, pickle.HIGHEST_PROTOCOL)

value is {'_saml2_outstanding_queries': {'id-p7YNjBFUd2wO6ypw5': u'/webtools/test/'}}

I'm getting this error output:

Request Method: GET
Request URL: http://localhost:8000/webtools/saml2/login/?next=/webtools/test/

Django Version: 1.10.1
Python Version: 2.7.8
Installed Applications:
['djangosaml2',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
'pyworkflow.web.app',
'resumable']
Installed Middleware:
('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 "/home/pablo/desarrollo/scipion-web/software/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response

  1.             response = middleware_method(request, response)
    

File "/home/pablo/desarrollo/scipion-web/software/lib/python2.7/site-packages/django/contrib/sessions/middleware.py" in process_response

  1.                         request.session.save()
    

File "/home/pablo/desarrollo/scipion-web/software/lib/python2.7/site-packages/django/contrib/sessions/backends/cache.py" in save

  1.         return self.create()
    

File "/home/pablo/desarrollo/scipion-web/software/lib/python2.7/site-packages/django/contrib/sessions/backends/cache.py" in create

  1.             self.save(must_create=True)
    

File "/home/pablo/desarrollo/scipion-web/software/lib/python2.7/site-packages/django/contrib/sessions/backends/cache.py" in save

  1.                   self.get_expiry_age())
    

File "/home/pablo/desarrollo/scipion-web/software/lib/python2.7/site-packages/django/core/cache/backends/locmem.py" in add

  1.     pickled = pickle.dumps(value, pickle.HIGHEST_PROTOCOL)
    

Exception Type: PicklingError at /webtools/saml2/login/
Exception Value: Can't pickle <type 'thread.lock'>: attribute lookup thread.lock failed

Have you ever found this?

Add support for configuring the signature algorithm with HTTP post

Support for customizing the signature algorithm with HTTP redirects was added here: 441b8d1

However there is no support for customising the algorithm with HTTP posts. The only workaround is to do this:

from saml2 import xmldsig
xmldsig.sig_default = xmldsig.SIG_RSA_SHA512

Is it possible to add support for customising the signature algorithm with HTTP post bindings as well? It seems quite simple, I can perhaps add support for it in a merge request if it is as simple as I think it is.

Clarification - On adding multiple IDPs

Do this djangosaml2 package support multiple IDPs configuration ,how can I add more than one idp in settings or we need to customize the djangosaml2 configurations to support multiple IDPs.

Make a new release?

Hi, it's been a while since the last release, do you mind creating a new one?

Thank you!

Handling of authn_requests_signed

Hi,
This is a bigger post, I won't be offended if it is left for later :)

PR #7 introduced a default value to authn_requests_signed, set to False.
Upstream now default to true (has done for at least two years by the look of it). Would you be willing to align the default for djangosaml2 with pysaml2? If not, I'll put together a few sentences for the docs noting the difference.
http://pysaml2.readthedocs.io/en/latest/howto/config.html?highlight=metadata#authn-requests-signed
IdentityPython/pysaml2@0a83d58

Related but possibly its own issue:
As was mentioned in #51 binding is set based on the value of authn_requests_signed.
https://github.com/knaperek/djangosaml2/blob/master/djangosaml2/views.py#L140

I think this is what is causing me odd messages like this, where IdP None features:

Quit the server with CONTROL-C.
WARNING:djangosaml2:IDP None does not support urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect,  trying urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
[16/Aug/2017 23:56:38] "GET /VIP/login/ HTTP/1.1" 200 1508

Its no more logical when run with debug level, (I've trimmed this to what I believe are the relevant messages):

DEBUG:saml2.mdstore:service => {'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST': [{'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', '__class__': 'urn:oasis:names:tc:SAML:2.0:metadata&SingleSignOnService', 'location': 'https://login.vip.symantec.com/viplogin/saml2/post/requestconfirmidentity'}]}
DEBUG:djangosaml2:Trying binding urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect for IDP None
DEBUG:saml2.mdstore:service => {'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST': [{'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', '__class__': 'urn:oasis:names:tc:SAML:2.0:metadata&SingleSignOnService', 'location': 'https://login.vip.symantec.com/viplogin/saml2/post/requestconfirmidentity'}]}
DEBUG:saml2.mdstore:service(login.vip.symantec.com, idpsso_descriptor, single_sign_on_service, None)
DEBUG:saml2.mdstore:service => {'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST': [{'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', '__class__': 'urn:oasis:names:tc:SAML:2.0:metadata&SingleSignOnService', 'location': 'https://login.vip.symantec.com/viplogin/saml2/post/requestconfirmidentity'}]}
DEBUG:djangosaml2:Binding urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect not in IDP None supported bindings: ['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST']
WARNING:djangosaml2:IDP None does not support urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect,  trying urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
DEBUG:djangosaml2:Redirecting user to the IdP via urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST binding.

Discussion about handling 500 error on an invalid assertion

One thing we hit was that our VM time got off from the idp, and thus the "NotBefore" option in the assertion caused pysaml2 to throw an exception like so:

base Internal Server Error: /saml2/acs/
Traceback (most recent call last):
  File "/usr/lib/python2.6/site-packages/django/core/handlers/base.py", line 112, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/lib/python2.6/site-packages/django/views/decorators/http.py", line 41, in inner
    return func(request, *args, **kwargs)
  File "/usr/lib/python2.6/site-packages/django/views/decorators/csrf.py", line 57, in wrapped_view
    return view_func(*args, **kwargs)
  File "/usr/lib/python2.6/site-packages/djangosaml2/views.py", line 214, in assertion_consumer_service
    outstanding_queries)
  File "/usr/lib/python2.6/site-packages/saml2/client_base.py", line 584, in parse_authn_request_response
    binding, **kwargs)
  File "/usr/lib/python2.6/site-packages/saml2/entity.py", line 1163, in _parse_response
    response = response.verify(keys)
  File "/usr/lib/python2.6/site-packages/saml2/response.py", line 1007, in verify
    if self.parse_assertion(keys):
  File "/usr/lib/python2.6/site-packages/saml2/response.py", line 921, in parse_assertion
    if not self._assertion(assertion, False):
  File "/usr/lib/python2.6/site-packages/saml2/response.py", line 795, in _assertion
    if not self.condition_ok():
  File "/usr/lib/python2.6/site-packages/saml2/response.py", line 590, in condition_ok
    validate_before(conditions.not_before, self.timeslack)
  File "/usr/lib/python2.6/site-packages/saml2/validate.py", line 106, in validate_before
    now))
ToEarly: Can't use it yet 1472570331 <= 1472556539

This is using the latest released version of pysaml2.

My concern about this is that just throwing a 500 error is not very descriptive during a login process, and while you can override the default django 500 error view that seems like a workaround.

What seems like the best option is to catch exceptions from the call to pysaml2's assertion_consumer_service, and then display a failed login view and probably allow the override of that view or something.

Thoughts?

Does this package support encrypted assertions?

I'm trying to test encrypted assertions with onelogin and it seems that this package can't handle it properly. Or i'm doing it wrongly. I'm getting such exception:

  File "/local/lib/python2.7/site-packages/saml2/response.py", line 1095, in session_info
    authn_statement = self.assertion.authn_statement[0]
AttributeError: 'NoneType' object has no attribute 'authn_statement'

A bit of digging shows that no keys passed in djangosaml2/views.py assertion_consumer_service

    response = client.parse_authn_request_response(xmlstr, BINDING_HTTP_POST,
                                                   outstanding_queries)

As i understand it can't decrypt anything without a key and throw exception above.

If i hardcode key manually in the AuthnResponse.parse_assertion method all process goes fine

service.sp.authn_requests_signed=True causes bytes/str-related TypeError on Python 3

This is because binding automatically gets set to HTTP-POST in this case (views.py:143), which leads to this line of code in views.py:207:

'SAMLRequest': base64.b64encode(request_xml),

which fails with TypeError because request_xml is a str, not bytes.

Additionally, if I fix this line to 'SAMLRequest': base64.b64encode(request_xml.encode('utf-8')), I get this error:

TemplateDoesNotExist at /saml2/login/ 
djangosaml2/post_binding_form.html

There is an example_post_binding_form.html in the folder, but the template path is not configurable. If I copy it over to post_binding_form.html, things start working and requests are signed.

  1. Why is POST binding preferred over REDIRECT in this case? URL length considerations? Can this be configurable?
  2. Let's fix the TypeError
  3. Can the template path be configurable (and working by default, even if as plain as the example template is) ?

Thanks a lot!

Multiple loads of config slows down execution when working with larger federations

Hi all,

When working with big interfederations such as eduGAIN, on each load of the login view or the ACS, the config is loaded. Which will cause it to reload the config and parse the metadata into an internal structure on which to operate. When dealing with a lot of IdPs, this takes up to 2 minutes even. Do you think there's a solution to that? A similar issue was reported in this project:

IdentityPython/SATOSA#79

ACS - raise error saml create unkown user set to false

Requirement - If the user is not present in the database the page should be redirected to application home page
But when SAML_CREATE_UNKNOWN_USER = False the acs throws "raise PermissionDenied" error
Solution could be setting a redirect URL to application ,if the user not exist.

authn_requests_signed = True causes exception under python 2.7

It looks like the args for django.utils.six.binary_type() change depending on Python 2 or Python 3. I'm running the stock Centos 7 python 2.7.

2017-09-27 20:37:57,008 django.request ERROR: Internal Server Error: /saml2/login/
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/usr/lib/python2.7/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/usr/lib/python2.7/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/lib/python2.7/site-packages/djangosaml2/views.py", line 202, in login
    'SAMLRequest': base64.b64encode(binary_type(request_xml, 'UTF-8')),
TypeError: str() takes at most 1 argument (2 given)

Unknown attribute name

Hi,

I'm trying to setup a SSO for a Django App using SimpleSAMLphp as IDP, and djangosaml2 as SP.
The user table in Django has the following fields:

username, first_name, last_name, email

Got the following mapping in settings.py

SAML_ATTRIBUTE_MAPPING = {
    'userid': ('username', ),
    'email': ('email', ),
    'firstname': ('first_name', ),
    'lastname': ('last_name', ),
    'is_staff': ('is_staff', ),
    'is_superuser': ('is_superuser', ),
}

Which I try to map to the local user table.
So far I was not able to setup the way it should be

When using following settings:

SAML_DJANGO_USER_MAIN_ATTRIBUTE = 'email'
SAML_USE_NAME_ID_AS_USERNAME = True
SAML_CREATE_UNKNOWN_USER = True

It creates an entry with the email under email, nothing else.

When using following settings:

SAML_USE_NAME_ID_AS_USERNAME = True
SAML_CREATE_UNKNOWN_USER = True

It creates an entry with email under username, nothing else

With following settings:

SAML_DJANGO_USER_MAIN_ATTRIBUTE = 'email'
SAML_CREATE_UNKNOWN_USER = True

I get a 403 from /saml2/acs, no user setup

With the first 2 settings this is how the AttributeStatement looks like:

<saml:AttributeStatement> <saml:Attribute Name="userid"
                            NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
                            > <saml:AttributeValue xsi:type="xs:string">testuser</saml:AttributeValue> </saml:Attribute> <saml:Attribute Name="firstname"
                            NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
                            > <saml:AttributeValue xsi:type="xs:string">Test</saml:AttributeValue> </saml:Attribute> <saml:Attribute Name="lastname"
                            NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
                            > <saml:AttributeValue xsi:type="xs:string">User</saml:AttributeValue> </saml:Attribute> <saml:Attribute Name="email"
                            NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
                            > <saml:AttributeValue xsi:type="xs:string">[email protected]</saml:AttributeValue> </saml:Attribute> </saml:AttributeStatement>

So I can see all attributes.
But log says Unknown attribute name:

<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="userid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml:AttributeValue xsi:type="xs:string">testuser</saml:AttributeValue></saml:Attribute>
2018-08-04 04:58:08,653 saml2.attribute_converter:INFO Unknown attribute name: <?xml version='1.0' encoding='UTF-8'?>
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="firstname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml:AttributeValue xsi:type="xs:string">Test</saml:AttributeValue></saml:Attribute>
2018-08-04 04:58:08,653 saml2.attribute_converter:INFO Unknown attribute name: <?xml version='1.0' encoding='UTF-8'?>
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="lastname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml:AttributeValue xsi:type="xs:string">User</saml:AttributeValue></saml:Attribute>
2018-08-04 04:58:08,653 saml2.attribute_converter:INFO Unknown attribute name: <?xml version='1.0' encoding='UTF-8'?>
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml:AttributeValue xsi:type="xs:string">[email protected]</saml:AttributeValue></saml:Attribute>
2018-08-04 04:58:08,654 saml2.response:DEBUG Assertion contains no attribute statements
2018-08-04 04:58:08,654 saml2.response:DEBUG --- AVA: {}

Is there something else I need to setup?
Already using

"allow_unknown_attributes": True,

Only email can be used at the moment, no other attributes are mapped into the user table.
Any help much appreciated.

Thanks,
chris

auth backend is not atomic

It does two queries that could be done in one one get_or_create call.

In my setup in these two places a sql query is fired that changes the user model in the same request.

For me it looks like this could be mitigated by using django's defaults keyword parameter in the get_or_create call or by putting the view inside a transaction. I had problems with "corrupted" data causing problems in the database.

Push a new release

Is there any strict release schedule you adhere to? Can you push a new release that includes this MR? #30

TypeError: must be convertible to a buffer, not AuthnRequest

TypeError: must be convertible to a buffer, not AuthnRequest
(5 additional frame(s) were not displayed)
...
  File "djangosaml2/views.py", line 207, in login
    'SAMLRequest': base64.b64encode(request_xml),

TypeError: must be convertible to a buffer, not AuthnRequest

This issue is coming from the incongruent logic between two of our dependencies; djangosaml2==0.16.1 and pysaml2==4.4.0.

This issue occurs if the binding is BINDING_HTTP_POST and post_binding_form_template has a value (which it normally does).

This line (under the dependencies stated) does not return session_id, request_xml:

https://github.com/knaperek/djangosaml2/blob/60dede4/djangosaml2/views.py#L201

... it returns an id and an AuthnRequest object respectively.

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.