URL: https://github.com/freeipa/freeipa/pull/736
Author: felipevolpone
 Title: #736: Fixing the cert-request command comparing whole email address 
case-sensitively.
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/736/head:pr736
git checkout pr736
From 6210297824b61c20e3ca70dff3c48ffd47aee29e Mon Sep 17 00:00:00 2001
From: felipe barreto <fbarreto@localhost.localdomain>
Date: Wed, 26 Apr 2017 11:08:35 -0300
Subject: [PATCH 1/3] Fixing the cert-request comparing whole email address
 case-sensitively.

Now, the cert-request command compares the domain part of the
email case-insensitively.

https://pagure.io/freeipa/issue/5919
---
 ipaserver/plugins/cert.py                | 20 +++++++++++++++++++-
 ipatests/test_xmlrpc/test_cert_plugin.py | 25 +++++++++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
index 9f90107..a0b2b83 100644
--- a/ipaserver/plugins/cert.py
+++ b/ipaserver/plugins/cert.py
@@ -705,7 +705,8 @@ def execute(self, csr, all=False, raw=False, chain=False, **kw):
             # fail if any email addr from DN does not appear in ldap entry
             email_addrs = csr_obj.subject.get_attributes_for_oid(
                     cryptography.x509.oid.NameOID.EMAIL_ADDRESS)
-            if len(set(email_addrs) - set(principal_obj.get('mail', []))) > 0:
+            if not _emails_are_valid(email_addrs,
+                                     principal_obj.get('mail', [])):
                 raise errors.ValidationError(
                     name='csr',
                     error=_(
@@ -860,6 +861,23 @@ def execute(self, csr, all=False, raw=False, chain=False, **kw):
         )
 
 
+def _emails_are_valid(cert_emails, principal_emails):
+    """
+    Checks if any email addr from DN does not appear in ldap entry,
+    comparing the domain part case-insensitively.
+    """
+
+    def lower_domain(email):
+        return email.split('@')[0] + '@' + email.split('@')[1].lower()
+
+    principal_emails_lower = [lower_domain(email) for email in principal_emails]
+
+    email_addrs = [attr.value for attr in cert_emails]
+    cert_emails_lower = [lower_domain(email) for email in email_addrs]
+
+    return not any(set(cert_emails_lower) - set(principal_emails_lower))
+
+
 def principal_to_principal_type(principal):
     if principal.is_user:
         return USER
diff --git a/ipatests/test_xmlrpc/test_cert_plugin.py b/ipatests/test_xmlrpc/test_cert_plugin.py
index 0b8277b..cd8ee7b 100644
--- a/ipatests/test_xmlrpc/test_cert_plugin.py
+++ b/ipatests/test_xmlrpc/test_cert_plugin.py
@@ -253,6 +253,31 @@ def test_00010_cleanup(self):
         res = api.Command['service_find'](self.service_princ)
         assert res['count'] == 0
 
+    def test_00011_email_are_valid(self):
+        from ipaserver.plugins.cert import _emails_are_valid
+        from collections import namedtuple
+        NameAttribute = namedtuple('NameAttribute', 'value')
+
+        cert = [NameAttribute(u'a...@email.com')]
+        result = _emails_are_valid(cert, [u'a...@email.com'])
+        assert True == result, result
+
+        cert = [NameAttribute(u'a...@email.com')]
+        result = _emails_are_valid(cert, [u'a...@email.com', u'anot...@email.com'])
+        assert True == result, result
+
+        cert = [NameAttribute(u'a...@email.com'), NameAttribute('anot...@email.com')]
+        result = _emails_are_valid(cert, [u'a...@email.com'])
+        assert False == result, result
+
+        result = _emails_are_valid([], [u'a...@email.com'])
+        assert True == result, result
+
+        cert = [NameAttribute(u'a...@email.com')]
+        result = _emails_are_valid(cert, [])
+        assert False == result, result
+
+
 
 @pytest.mark.tier1
 class test_cert_find(XMLRPC_test):

From 943a287a71384e10d2f13e8402907f0d3d10a085 Mon Sep 17 00:00:00 2001
From: felipe barreto <fbarreto@localhost.localdomain>
Date: Tue, 2 May 2017 12:29:46 -0300
Subject: [PATCH 2/3] Checking the emails in SAN extension

---
 ipaserver/plugins/cert.py                | 31 ++++++++++++++++++++-----------
 ipatests/test_xmlrpc/test_cert_plugin.py | 28 +++++++++++++++++-----------
 2 files changed, 37 insertions(+), 22 deletions(-)

diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
index a0b2b83..88cf6d4 100644
--- a/ipaserver/plugins/cert.py
+++ b/ipaserver/plugins/cert.py
@@ -705,7 +705,11 @@ def execute(self, csr, all=False, raw=False, chain=False, **kw):
             # fail if any email addr from DN does not appear in ldap entry
             email_addrs = csr_obj.subject.get_attributes_for_oid(
                     cryptography.x509.oid.NameOID.EMAIL_ADDRESS)
-            if not _emails_are_valid(email_addrs,
+
+            san_email_addrs = csr_obj.extensions.get_extension_for_oid(
+                    cryptography.x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
+
+            if not _emails_are_valid(email_addrs, san_email_addrs,
                                      principal_obj.get('mail', [])):
                 raise errors.ValidationError(
                     name='csr',
@@ -861,21 +865,26 @@ def execute(self, csr, all=False, raw=False, chain=False, **kw):
         )
 
 
-def _emails_are_valid(cert_emails, principal_emails):
-    """
-    Checks if any email addr from DN does not appear in ldap entry,
-    comparing the domain part case-insensitively.
-    """
+def _emails_are_valid(subject_emails, san_emails, principal_emails):
+    if not any(principal_emails):
+        return False
 
     def lower_domain(email):
-        return email.split('@')[0] + '@' + email.split('@')[1].lower()
+        if '@' not in email:
+            return ''
+
+        email_splited = email.split('@')
+        return '{}@{}'.format(email_splited[0], email_splited[1].lower())
 
-    principal_emails_lower = [lower_domain(email) for email in principal_emails]
+    principal_emails_lower = map(lower_domain, principal_emails)
+    subject_emails = [attr.value for attr in subject_emails]
+    san_emails = [attr.value for attr in san_emails]
 
-    email_addrs = [attr.value for attr in cert_emails]
-    cert_emails_lower = [lower_domain(email) for email in email_addrs]
+    subject_emails_lower = map(lower_domain, subject_emails)
+    san_emails_lower = map(lower_domain, san_emails)
 
-    return not any(set(cert_emails_lower) - set(principal_emails_lower))
+    return (set(principal_emails_lower).issubset(subject_emails_lower) or
+            set(principal_emails_lower).issubset(san_emails_lower))
 
 
 def principal_to_principal_type(principal):
diff --git a/ipatests/test_xmlrpc/test_cert_plugin.py b/ipatests/test_xmlrpc/test_cert_plugin.py
index cd8ee7b..b76d3f1 100644
--- a/ipatests/test_xmlrpc/test_cert_plugin.py
+++ b/ipatests/test_xmlrpc/test_cert_plugin.py
@@ -256,27 +256,33 @@ def test_00010_cleanup(self):
     def test_00011_email_are_valid(self):
         from ipaserver.plugins.cert import _emails_are_valid
         from collections import namedtuple
-        NameAttribute = namedtuple('NameAttribute', 'value')
+        NameAttr = namedtuple('NameAttr', 'value')
 
-        cert = [NameAttribute(u'a...@email.com')]
-        result = _emails_are_valid(cert, [u'a...@email.com'])
+        subject_addrs = [NameAttr(u'a...@email.com')]
+        result = _emails_are_valid(subject_addrs, [], [u'a...@email.com'])
         assert True == result, result
 
-        cert = [NameAttribute(u'a...@email.com')]
-        result = _emails_are_valid(cert, [u'a...@email.com', u'anot...@email.com'])
+        san_addrs = [NameAttr(u'a...@email.com'), NameAttr(u'anot...@email.com')]
+        result = _emails_are_valid([], san_addrs, [u'a...@email.com'])
         assert True == result, result
 
-        cert = [NameAttribute(u'a...@email.com'), NameAttribute('anot...@email.com')]
-        result = _emails_are_valid(cert, [u'a...@email.com'])
+        result = _emails_are_valid([], [], [u'a...@email.com'])
         assert False == result, result
 
-        result = _emails_are_valid([], [u'a...@email.com'])
-        assert True == result, result
+        subject_addrs = [NameAttr(u'a...@email.com')]
+        san_addrs = [NameAttr(u'a...@email.com')]
+        result = _emails_are_valid(subject_addrs, san_addrs, [])
+        assert False == result, result
 
-        cert = [NameAttribute(u'a...@email.com')]
-        result = _emails_are_valid(cert, [])
+        subject_addrs = [NameAttr(u'invalidEmailAddress')]
+        san_addrs = [NameAttr(u'va...@email.com')]
+        result = _emails_are_valid(subject_addrs, san_addrs, [])
         assert False == result, result
 
+        subject_addrs = [NameAttr(u'va...@email.com')]
+        san_addrs = [NameAttr(u'invalidEmailAddress')]
+        result = _emails_are_valid(subject_addrs, san_addrs, [])
+        assert False == result, result
 
 
 @pytest.mark.tier1

From 47edf4b1f51fcc9d7b432b8d59ee9af1063f97ee Mon Sep 17 00:00:00 2001
From: felipe barreto <fbarreto@localhost.localdomain>
Date: Tue, 2 May 2017 12:44:02 -0300
Subject: [PATCH 3/3] Adding code comments and docstrings

---
 ipaserver/plugins/cert.py                | 5 +++++
 ipatests/test_xmlrpc/test_cert_plugin.py | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
index 88cf6d4..2b812fb 100644
--- a/ipaserver/plugins/cert.py
+++ b/ipaserver/plugins/cert.py
@@ -866,6 +866,11 @@ def execute(self, csr, all=False, raw=False, chain=False, **kw):
 
 
 def _emails_are_valid(subject_emails, san_emails, principal_emails):
+    """
+    Checks if any email addr from DN or SAN extension does not
+    appear in ldap entry, comparing the domain part case-insensitively.
+    """
+
     if not any(principal_emails):
         return False
 
diff --git a/ipatests/test_xmlrpc/test_cert_plugin.py b/ipatests/test_xmlrpc/test_cert_plugin.py
index b76d3f1..bc3be91 100644
--- a/ipatests/test_xmlrpc/test_cert_plugin.py
+++ b/ipatests/test_xmlrpc/test_cert_plugin.py
@@ -254,6 +254,11 @@ def test_00010_cleanup(self):
         assert res['count'] == 0
 
     def test_00011_email_are_valid(self):
+        """
+        Verify the different scenarios when checking if any email addr
+        from DN or SAN extension does not appear in ldap entry.
+        """
+
         from ipaserver.plugins.cert import _emails_are_valid
         from collections import namedtuple
         NameAttr = namedtuple('NameAttr', 'value')
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to