IdentityPython / djangosaml2

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

KeyError at /saml2/logout/ #10

Closed ajsmilutin closed 7 years ago

ajsmilutin commented 8 years ago

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': 'lgs@yaco.es',
       'contact_type': 'technical'},
      {'given_name': 'Angel',
       'sur_name': 'Fernandez',
       'company': 'Yaco Sistemas',
       'email_address': 'angel@yaco.es',
       'contact_type': 'administrative'},
      ],
  # you can set multilanguage information here
  'organization': {
      'name': [('Yaco Sistemas', 'es'), ('Yaco Systems', 'en')],
      'display_name': [('Yaco', 'es'), ('Yaco', 'en')],
      'url': [('http://www.yaco.es', 'es'), ('http://www.yaco.com', 'en')],
      },
  'valid_for': 24,  # how long is our metadata valid
  }
MiguelSR commented 8 years ago

I have the same problem, but I think this pull request fixes it. If I add those lines to my cache.py file, it seems that I have the key in the object.

Now it fails with TypeError: <saml2.saml.NameID object at 0x7fcfcf494e90> is not JSON serializable but that's another story ;)

knaperek commented 8 years ago

@MiguelSR Thanks for testing it. In addition, could you please test if it works for you with PR #9 changes and the latest pysaml2 (not yet released - please install directly from the repo. Thank you for your help.

ajsmilutin commented 8 years ago

Hi everybody, thanks for the testing. Last night I have managed to find and fix the bug. I am not sure if everyghing is ok but works for me, since I have only one IdP. It si similar like one from the pull that MiguelSR pointed out. Here is how my solution looks like:

def sync(self):      
        self._set_objects(self)
        self.session.modified = True

Th change is in the class DjangoSessionCacheAdapter I will probably change it to the code from the MiguelSR push.

knaperek commented 8 years ago

Thanks @ajsmilutin, your fix is the least verbose solution, I still don't know why it is needed though. Anyway, it seems innocent otherwise and if there's really nobody who could explain it, I guess we have to take the pragmatic approach and "just include it". Would you please create a PR?

ajsmilutin commented 8 years ago

No problem @knaperek . I guess that PR means Pull request. I'll make it later today.

sheepsy90 commented 8 years ago

Even with the given fixes - I still run into the problem - I am using django 1.9 and memcache with pylibmc

koliber commented 7 years ago

Please note that the self._set_objects(self) line seems to cause https://github.com/knaperek/djangosaml2/issues/40

knaperek commented 7 years ago

@sheepsy90 what kind of problems do you experience? Would you mind sharing the details, thanks!

knaperek commented 7 years ago

Closing this for no activity. @sheepsy90 please reopen if you're still experiencing the issues in the latest djangosaml2 version.