# HG changeset patch
# User Florent Aide <florent.aide@gmail.com>
# Date 1455123438 -3600
#      Wed Feb 10 17:57:18 2016 +0100
# Branch odoo8
# Node ID d67d84c6ebc084d85a777e17a4431b1c1a3b78ec
# Parent  010cf843cdd99a559a100571396c36af096541af
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
@@ -33,6 +33,16 @@
         return lasso.Login(server)
 
     @api.multi
+    def _get_matching_attr_for_provider(self):
+        """internal helper to fetch the matching attribute for this SAML
+        provider. Returns a unicode object.
+        """
+
+        self.ensure_one()
+
+        return self.matching_attribute
+
+    @api.multi
     def _get_auth_request(self, state):
         """build an authentication request and give it back to our client
         WARNING: this method cannot be used for multiple ids
@@ -58,6 +68,11 @@
     sp_pkey = fields.Text(
         'Private key of our service provider (this openerpserver)'
     )
+    matching_attribute = fields.Text(
+        string='Matching Attribute',
+        default='subject.nameId',
+        required=True,
+    ),
     enabled = fields.Boolean('Enabled', default=False)
     sequence = fields.Integer('Sequence')
     css_class = fields.Char('CSS Class')
diff --git a/model/res_users.py b/model/res_users.py
--- a/model/res_users.py
+++ b/model/res_users.py
@@ -72,6 +72,7 @@
         # we are not yet logged in, so the userid cannot have access to the
         # fields we need yet
         login = p.sudo()._get_lasso_for_provider()
+        matching_attribute = p._get_matching_attr_for_provider()
 
         try:
             login.processAuthnResponseMsg(token)
@@ -79,17 +80,76 @@
             raise Exception('Lasso Profile cannot verify signature')
         except lasso.ProfileStatusNotSuccessError:
             raise Exception('Profile Status Not Success Error')
-        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])
+            )
+
+        attrs = {}
 
-        # TODO use a real token validation from LASSO
-        # TODO push into the validation result a real UPN
-        return {'user_id': login.assertion.subject.nameId.content}
+        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
 
     @api.multi
     def _auth_saml_signin(self, provider, validation, saml_response):
diff --git a/views/auth_saml.xml b/views/auth_saml.xml
--- a/views/auth_saml.xml
+++ b/views/auth_saml.xml
@@ -43,6 +43,7 @@
                             <field name="name" />
                             <field name="enabled" />
                             <field name="body" />
+                            <field name="matching_attribute" />
                         </group>
                         <group>
                             <field name="idp_metadata" />