#73 Use mapped attributes for nameid detection
Merged 8 years ago by puiterwijk. Opened 8 years ago by puiterwijk.

file modified
+29 -29
@@ -228,6 +228,32 @@ 

          authtime_notbefore = authtime - skew

          authtime_notafter = authtime + skew

  

+         # Let's first do the attribute mapping, so we could map the username

+         # Check attribute policy and perform mapping and filtering.

+         # If the SP has its own mapping or filtering policy use that

+         # instead of the global policy.

+         if (provider.attribute_mappings is not None and

+                 len(provider.attribute_mappings) > 0):

+             attribute_mappings = provider.attribute_mappings

+         else:

+             attribute_mappings = self.cfg.default_attribute_mapping

+         if (provider.allowed_attributes is not None and

+                 len(provider.allowed_attributes) > 0):

+             allowed_attributes = provider.allowed_attributes

+         else:

+             allowed_attributes = self.cfg.default_allowed_attributes

+         self.debug("Allowed attrs: %s" % allowed_attributes)

+         self.debug("Mapping: %s" % attribute_mappings)

+         policy = Policy(attribute_mappings, allowed_attributes)

+         userattrs = us.get_user_attrs()

+         mappedattrs, _ = policy.map_attributes(userattrs)

+         attributes = policy.filter_attributes(mappedattrs)

+ 

+         if '_groups' in attributes and 'groups' not in attributes:

+             attributes['groups'] = attributes['_groups']

+ 

+         self.debug("%s's attributes: %s" % (user.name, attributes))

+ 

          # TODO: get authentication type fnd name format from session

          # need to save which login manager authenticated and map it to a

          # saml2 authentication context
@@ -250,19 +276,18 @@ 

              value = hashlib.sha512()

              value.update(idpsalt)

              value.update(login.remoteProviderId)

-             value.update(user.name)

+             value.update(mappedattrs.get('_username'))

              nameid = '_' + value.hexdigest()

          elif nameidfmt == lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT:

              nameid = '_' + uuid.uuid4().hex

          elif nameidfmt == lasso.SAML2_NAME_IDENTIFIER_FORMAT_KERBEROS:

-             userattrs = us.get_user_attrs()

              nameid = userattrs.get('gssapi_principal_name')

          elif nameidfmt == lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL:

-             nameid = us.get_user().email

+             nameid = mappedattrs.get('email')

              if not nameid:

                  nameid = '%s@%s' % (user.name, self.cfg.default_email_domain)

          elif nameidfmt == lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED:

-             nameid = provider.normalize_username(user.name)

+             nameid = provider.normalize_username(mappedattrs.get('_username'))

  

          if nameid:

              login.assertion.subject.nameId.format = nameidfmt
@@ -274,31 +299,6 @@ 

              raise AuthenticationError("Unavailable Name ID type",

                                        lasso.SAML2_STATUS_CODE_AUTHN_FAILED)

  

-         # Check attribute policy and perform mapping and filtering.

-         # If the SP has its own mapping or filtering policy use that

-         # instead of the global policy.

-         if (provider.attribute_mappings is not None and

-                 len(provider.attribute_mappings) > 0):

-             attribute_mappings = provider.attribute_mappings

-         else:

-             attribute_mappings = self.cfg.default_attribute_mapping

-         if (provider.allowed_attributes is not None and

-                 len(provider.allowed_attributes) > 0):

-             allowed_attributes = provider.allowed_attributes

-         else:

-             allowed_attributes = self.cfg.default_allowed_attributes

-         self.debug("Allowed attrs: %s" % allowed_attributes)

-         self.debug("Mapping: %s" % attribute_mappings)

-         policy = Policy(attribute_mappings, allowed_attributes)

-         userattrs = us.get_user_attrs()

-         mappedattrs, _ = policy.map_attributes(userattrs)

-         attributes = policy.filter_attributes(mappedattrs)

- 

-         if '_groups' in attributes and 'groups' not in attributes:

-             attributes['groups'] = attributes['_groups']

- 

-         self.debug("%s's attributes: %s" % (user.name, attributes))

- 

          # The saml-core-2.0-os specification section 2.7.3 requires

          # the AttributeStatement element to be non-empty.

          if attributes:

file modified
+42 -7
@@ -83,14 +83,15 @@ 

      # are no unexpected MELLON_ vars, and drop the _0 version.

      data = convert_to_dict(page.text)

  

-     data.pop('MELLON_IDP')

-     data.pop('MELLON_NAME_ID')

- 

      for key in expected:

          item = data.pop('MELLON_' + key)

          if item != expected[key]:

              raise ValueError('Expected %s, got %s' % (expected[key], item))

  

+     # Ignore these attributes if they weren't tested for

+     data.pop('MELLON_IDP', None)

+     data.pop('MELLON_NAME_ID', None)

+ 

      if len(data) > 0:

          raise ValueError('Unexpected values %s' % data)

  
@@ -193,6 +194,7 @@ 

      try:

          print "testmapping: Test default mapping and attrs ...",

          expect = {

+             'NAME_ID': user,

              'fullname': 'Test User %s' % user,

              'surname': user,

              'givenname': 'Test User',
@@ -268,8 +270,8 @@ 

      else:

          print " SUCCESS"

  

+     print "testmapping: Test SP allowed atributes ...",

      try:

-         print "testmapping: Test SP allowed atributes ...",

          expect = {

              'fullname': 'Test User %s' % user,

              'surname': user,
@@ -298,14 +300,47 @@ 

      else:

          print " SUCCESS"

  

+     print "testmapping: Test SP attribute mapping ...",

+     try:

+         expect = {

+             'wholename': 'Test User %s' % user,

+             'fullname': 'Test User %s' % user,

+             'surname': user,

+             'givenname': 'Test User',

+             'email': '%s@example.com' % user,

+         }

+         check_info_plugin(sess, idpname, spurl, expect)

+     except Exception, e:  # pylint: disable=broad-except

+         print >> sys.stderr, " ERROR: %s" % repr(e)

+         sys.exit(1)

+     else:

+         print " SUCCESS"

+ 

+     print "testmapping: Set SP username mapping ...",

+     try:

+         sess.set_attributes_and_mapping(idpname,

+                                         [['*', '*'],

+                                          ['fullname', 'wholename'],

+                                          ['email', '_username']],

+                                         ['wholename', 'givenname',

+                                          'surname',

+                                          'email', 'fullname'],

+                                         sp['name'])

+     except Exception, e:  # pylint: disable=broad-except

+         print >> sys.stderr, " ERROR: %s" % repr(e)

+         sys.exit(1)

+     else:

+         print " SUCCESS"

+ 

+     print "testmapping: Test SP username mapping ...",

      try:

-         print "testmapping: Test SP attribute mapping ...",

          expect = {

              'wholename': 'Test User %s' % user,

              'fullname': 'Test User %s' % user,

              'surname': user,

              'givenname': 'Test User',

              'email': '%s@example.com' % user,

+             'NAME_ID': '%s@example.com' % user,

          }

          check_info_plugin(sess, idpname, spurl, expect)

      except Exception, e:  # pylint: disable=broad-except
@@ -325,8 +360,8 @@ 

      else:

          print " SUCCESS"

  

+     print "testmapping: Test SP attr mapping with default allowed...",

      try:

-         print "testmapping: Test SP attr mapping with default allowed...",

          expect = {

              'fullname': 'Test User %s' % user,

              'surname': user,
@@ -349,8 +384,8 @@ 

      else:

          print " SUCCESS"

  

+     print "testmapping: Test mapping, should be back to global...",

      try:

-         print "testmapping: Test mapping, should be back to global...",

          expect = {

              'namefull': 'Test User %s' % user,

              'surname': user,

no initial comment

I wonder if the form: data.pop('VALUE', None) is safest here.

Where does NAME_ID come from and why wasn't it set previously?

NAME_ID is the MELLON_NAME_ID value that it inserts by default, since the code that this expect-dir is fed into does a MELLON_ + key.

for key in expected:
item = data.pop('MELLON_' + key)

Got it. We used to ignore this in the past. Now we filter it out and only remove it if it isn't in expect.