# HG changeset patch
# User Florent Aide <florent.aide@gmail.com>
# Date 1455123438 -3600
#      Wed Feb 10 17:57:18 2016 +0100
# Node ID e0507277b57de36f77d8d95f6f88ae8fa935093b
# Parent  9b47f2baccaede3bbd6c3d4fd314e714221d7de0
implement real attribute matching instead of using subject.nameId.content for everything

diff --git a/model/auth_saml.py b/model/auth_saml.py
--- a/model/auth_saml.py
+++ b/model/auth_saml.py
@@ -29,6 +29,15 @@
         )
         return lasso.Login(server)
 
+    def _get_matching_attr_for_provider(
+            self, cr, uid, provider_id, context=None
+    ):
+        """internal helper to fetch the matching attribute for this SAML
+        provider. Returns a unicode object.
+        """
+        provider = self.browse(cr, uid, provider_id, context=context)
+        return provider.matching_attribute
+
     def _get_auth_request(self, cr, uid, id_, state, context=None):
         """build an authentication request and give it back to our client
         WARNING: this method cannot be used for multiple ids
@@ -55,6 +64,7 @@
         'sp_pkey': fields.text(
             'Private key of our service provider (this openerpserver)'
         ),
+        'matching_attribute': fields.text('Matching Attribute', required=True),
         'enabled': fields.boolean('Enabled'),
         'css_class': fields.char('CSS class'),
         'body': fields.char(
@@ -66,6 +76,7 @@
 
     _defaults = {
         'enabled': False,
+        'matching_attribute': "subject.nameId",
         'css_class': 'zocial saml',
         'body': 'Authentic',
     }
diff --git a/model/res_users.py b/model/res_users.py
--- a/model/res_users.py
+++ b/model/res_users.py
@@ -63,29 +63,81 @@
 
         p = self.pool.get('auth.saml.provider')
         login = p._get_lasso_for_provider(cr, uid, provider, context=context)
+        matching_attribute = p._get_matching_attr_for_provider(cr, uid, provider, context=context)
 
         try:
             login.processAuthnResponseMsg(token)
         except (lasso.DsError, lasso.ProfileCannotVerifySignatureError):
             raise Exception('Lasso Profile cannot verify signature')
-        except lasso.Error, e:
+        except lasso.Error as e:
             raise Exception(repr(e))
 
         try:
             login.acceptSso()
-        except lasso.Error:
-            raise Exception('Invalid assertion')
+        except lasso.Error as error:
+            raise Exception(
+                    'Invalid assertion : %s' % lasso.strError(error[0])
+            )
 
-        # TODO use a real token validation from LASSO
-        # TODO push into the validation result a real UPN
-        validation = {'user_id': login.assertion.subject.nameId.content}
+        attrs = {}
 
-        """
-        if p.data_endpoint:
-            data = self._auth_oauth_rpc(cr, uid, p.data_endpoint, access_token)
-            validation.update(data)
-        """
+        for att_statement in login.assertion.attributeStatement:
+            for attribute in att_statement.attribute:
+                name = None
+                lformat = lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC
+                nickname = None
+                try:
+                    name = attribute.name.decode('ascii')
+                except Exception as e:
+                    _logger.warning('sso_after_response: error decoding name of \
+                        attribute %s' % attribute.dump())
+                else:
+                    try:
+                        if attribute.nameFormat:
+                            lformat = attribute.nameFormat.decode('ascii')
+                        if attribute.friendlyName:
+                            nickname = attribute.friendlyName
+                    except Exception as e:
+                        message = 'sso_after_response: name or format of an \
+                            attribute failed to decode as ascii: %s due to %s'
+                        _logger.warning(message % (attribute.dump(), str(e)))
+                    try:
+                        if name:
+                            if lformat:
+                                if nickname:
+                                    key = (name, lformat, nickname)
+                                else:
+                                    key = (name, lformat)
+                            else:
+                                key = name
+                        attrs[key] = list()
+                        for value in attribute.attributeValue:
+                            content = [a.exportToXml() for a in value.any]
+                            content = ''.join(content)
+                            attrs[key].append(content.decode('utf8'))
+                    except Exception as e:
+                        message = 'sso_after_response: value of an \
+                            attribute failed to decode as ascii: %s due to %s'
+                        _logger.warning(message % (attribute.dump(), str(e)))
 
+        matching_value = None
+        for k in attrs:
+            if isinstance(k, tuple) and k[0] == matching_attribute:
+                matching_value = attrs[k][0]
+                break
+
+        if not matching_value and matching_attribute == "subject.nameId":
+            matching_value = login.assertion.subject.nameId.content
+
+        elif not matching_value and matching_attribute != "subject.nameId":
+            raise Exception(
+                    "Matching attribute %s not found in user attrs: %s" % (
+                        matching_attribute,
+                        attrs,
+                    )
+            )
+
+        validation = {'user_id': matching_value}
         return validation
 
     def _auth_saml_signin(
diff --git a/views/auth_saml.xml b/views/auth_saml.xml
--- a/views/auth_saml.xml
+++ b/views/auth_saml.xml
@@ -25,6 +25,7 @@
                             <field name="name" />
                             <field name="enabled" />
                             <field name="body" />
+                            <field name="matching_attribute" />
                         </group>
                         <group>
                             <field name="idp_metadata" />