ac7d03
From eb9d14debc4276f422ae55d141e30246d5943067 Mon Sep 17 00:00:00 2001
ac7d03
From: Jan Cholasta <jcholast@redhat.com>
ac7d03
Date: Thu, 30 Mar 2017 08:33:30 +0000
ac7d03
Subject: [PATCH] cert: defer cert-find result post-processing
ac7d03
ac7d03
Rather than post-processing the results of each internal search,
ac7d03
post-process the combined result.
ac7d03
ac7d03
This avoids expensive per-certificate searches when cert-find is executed
ac7d03
with the --all option on certificates which won't even be included in the
ac7d03
combined result.
ac7d03
ac7d03
https://pagure.io/freeipa/issue/6808
ac7d03
ac7d03
Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
ac7d03
---
ac7d03
 ipaserver/plugins/cert.py   | 93 +++++++++++++++++++++++++++------------------
ac7d03
 ipaserver/plugins/dogtag.py | 10 +++++
ac7d03
 2 files changed, 66 insertions(+), 37 deletions(-)
ac7d03
ac7d03
diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
ac7d03
index dfc7444ddbf31ac3c194e050af28220fc2a87a92..68402679cf0320e9c664ea89276f6c4332730a15 100644
ac7d03
--- a/ipaserver/plugins/cert.py
ac7d03
+++ b/ipaserver/plugins/cert.py
ac7d03
@@ -162,6 +162,11 @@ def normalize_pkidate(value):
ac7d03
     return datetime.datetime.strptime(value, PKIDATE_FORMAT)
ac7d03
 
ac7d03
 
ac7d03
+def convert_pkidatetime(value):
ac7d03
+    value = datetime.datetime.fromtimestamp(int(value) // 1000)
ac7d03
+    return x509.format_datetime(value)
ac7d03
+
ac7d03
+
ac7d03
 def validate_csr(ugettext, csr):
ac7d03
     """
ac7d03
     Ensure the CSR is base64-encoded and can be decoded by our PKCS#10
ac7d03
@@ -1296,18 +1301,7 @@ class cert_find(Search, CertMethod):
ac7d03
 
ac7d03
         return (DN(cert_obj.issuer), cert_obj.serial_number)
ac7d03
 
ac7d03
-    def _get_cert_obj(self, cert, all, raw, pkey_only):
ac7d03
-        obj = {'certificate': base64.b64encode(cert).decode('ascii')}
ac7d03
-
ac7d03
-        full = not pkey_only and all
ac7d03
-        if not raw:
ac7d03
-            self.obj._parse(obj, full)
ac7d03
-        if not full:
ac7d03
-            del obj['certificate']
ac7d03
-
ac7d03
-        return obj
ac7d03
-
ac7d03
-    def _cert_search(self, all, raw, pkey_only, **options):
ac7d03
+    def _cert_search(self, pkey_only, **options):
ac7d03
         result = collections.OrderedDict()
ac7d03
 
ac7d03
         try:
ac7d03
@@ -1316,15 +1310,19 @@ class cert_find(Search, CertMethod):
ac7d03
             return result, False, False
ac7d03
 
ac7d03
         try:
ac7d03
-            key = self._get_cert_key(cert)
ac7d03
+            issuer, serial_number = self._get_cert_key(cert)
ac7d03
         except ValueError:
ac7d03
             return result, True, True
ac7d03
 
ac7d03
-        result[key] = self._get_cert_obj(cert, all, raw, pkey_only)
ac7d03
+        obj = {'serial_number': serial_number}
ac7d03
+        if not pkey_only:
ac7d03
+            obj['certificate'] = base64.b64encode(cert).decode('ascii')
ac7d03
+
ac7d03
+        result[issuer, serial_number] = obj
ac7d03
 
ac7d03
         return result, False, True
ac7d03
 
ac7d03
-    def _ca_search(self, all, raw, pkey_only, exactly, **options):
ac7d03
+    def _ca_search(self, raw, pkey_only, exactly, **options):
ac7d03
         ra_options = {}
ac7d03
         for name in ('revocation_reason',
ac7d03
                      'issuer',
ac7d03
@@ -1357,7 +1355,6 @@ class cert_find(Search, CertMethod):
ac7d03
             return result, False, complete
ac7d03
 
ac7d03
         ca_objs = self.api.Command.ca_find(
ac7d03
-            all=all,
ac7d03
             timelimit=0,
ac7d03
             sizelimit=0,
ac7d03
         )['result']
ac7d03
@@ -1377,24 +1374,16 @@ class cert_find(Search, CertMethod):
ac7d03
                 obj = {'serial_number': serial_number}
ac7d03
             else:
ac7d03
                 obj = ra_obj
ac7d03
-                if all:
ac7d03
-                    obj.update(ra.get_certificate(str(serial_number)))
ac7d03
 
ac7d03
                 if not raw:
ac7d03
                     obj['issuer'] = issuer
ac7d03
                     obj['subject'] = DN(ra_obj['subject'])
ac7d03
+                    obj['valid_not_before'] = (
ac7d03
+                        convert_pkidatetime(obj['valid_not_before']))
ac7d03
+                    obj['valid_not_after'] = (
ac7d03
+                        convert_pkidatetime(obj['valid_not_after']))
ac7d03
                     obj['revoked'] = (
ac7d03
                         ra_obj['status'] in (u'REVOKED', u'REVOKED_EXPIRED'))
ac7d03
-                    if all:
ac7d03
-                        obj['certificate'] = (
ac7d03
-                            obj['certificate'].replace('\r\n', ''))
ac7d03
-                        self.obj._parse(obj)
ac7d03
-
ac7d03
-                if 'certificate_chain' in ca_obj:
ac7d03
-                    cert = x509.load_certificate(obj['certificate'])
ac7d03
-                    cert_der = cert.public_bytes(serialization.Encoding.DER)
ac7d03
-                    obj['certificate_chain'] = (
ac7d03
-                        [cert_der] + ca_obj['certificate_chain'])
ac7d03
 
ac7d03
             obj['cacn'] = ca_obj['cn'][0]
ac7d03
 
ac7d03
@@ -1402,7 +1391,7 @@ class cert_find(Search, CertMethod):
ac7d03
 
ac7d03
         return result, False, complete
ac7d03
 
ac7d03
-    def _ldap_search(self, all, raw, pkey_only, no_members, **options):
ac7d03
+    def _ldap_search(self, all, pkey_only, no_members, **options):
ac7d03
         ldap = self.api.Backend.ldap2
ac7d03
 
ac7d03
         filters = []
ac7d03
@@ -1461,26 +1450,25 @@ class cert_find(Search, CertMethod):
ac7d03
             for attr in ('usercertificate', 'usercertificate;binary'):
ac7d03
                 for cert in entry.get(attr, []):
ac7d03
                     try:
ac7d03
-                        key = self._get_cert_key(cert)
ac7d03
+                        issuer, serial_number = self._get_cert_key(cert)
ac7d03
                     except ValueError:
ac7d03
                         truncated = True
ac7d03
                         continue
ac7d03
 
ac7d03
                     try:
ac7d03
-                        obj = result[key]
ac7d03
+                        obj = result[issuer, serial_number]
ac7d03
                     except KeyError:
ac7d03
-                        obj = self._get_cert_obj(cert, all, raw, pkey_only)
ac7d03
-                        result[key] = obj
ac7d03
+                        obj = {'serial_number': serial_number}
ac7d03
+                        if not pkey_only and all:
ac7d03
+                            obj['certificate'] = (
ac7d03
+                                base64.b64encode(cert).decode('ascii'))
ac7d03
+                        result[issuer, serial_number] = obj
ac7d03
 
ac7d03
                     if not pkey_only and (all or not no_members):
ac7d03
                         owners = obj.setdefault('owner', [])
ac7d03
                         if entry.dn not in owners:
ac7d03
                             owners.append(entry.dn)
ac7d03
 
ac7d03
-        if not raw:
ac7d03
-            for obj in six.itervalues(result):
ac7d03
-                self.obj._fill_owners(obj)
ac7d03
-
ac7d03
         return result, truncated, complete
ac7d03
 
ac7d03
     def execute(self, criteria=None, all=False, raw=False, pkey_only=False,
ac7d03
@@ -1537,6 +1525,37 @@ class cert_find(Search, CertMethod):
ac7d03
             truncated = truncated or sub_truncated
ac7d03
             complete = complete or sub_complete
ac7d03
 
ac7d03
+        if not pkey_only:
ac7d03
+            ca_objs = {}
ac7d03
+            ra = self.api.Backend.ra
ac7d03
+
ac7d03
+            for key, obj in six.iteritems(result):
ac7d03
+                if all and 'cacn' in obj:
ac7d03
+                    _issuer, serial_number = key
ac7d03
+                    cacn = obj['cacn']
ac7d03
+
ac7d03
+                    try:
ac7d03
+                        ca_obj = ca_objs[cacn]
ac7d03
+                    except KeyError:
ac7d03
+                        ca_obj = ca_objs[cacn] = (
ac7d03
+                            self.api.Command.ca_show(cacn, all=True)['result'])
ac7d03
+
ac7d03
+                    obj.update(ra.get_certificate(str(serial_number)))
ac7d03
+                    if not raw:
ac7d03
+                        obj['certificate'] = (
ac7d03
+                            obj['certificate'].replace('\r\n', ''))
ac7d03
+
ac7d03
+                    if 'certificate_chain' in ca_obj:
ac7d03
+                        cert = x509.load_certificate(obj['certificate'])
ac7d03
+                        cert_der = (
ac7d03
+                            cert.public_bytes(serialization.Encoding.DER))
ac7d03
+                        obj['certificate_chain'] = (
ac7d03
+                            [cert_der] + ca_obj['certificate_chain'])
ac7d03
+
ac7d03
+                if not raw:
ac7d03
+                    self.obj._parse(obj, all)
ac7d03
+                    self.obj._fill_owners(obj)
ac7d03
+
ac7d03
         result = list(six.itervalues(result))
ac7d03
         if sizelimit > 0 and len(result) > sizelimit:
ac7d03
             if not truncated:
ac7d03
diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
ac7d03
index d1dd707f145e0f58bfa721df55513d46c14358f2..3997531032746a22243a4219250af4172e9ae5b3 100644
ac7d03
--- a/ipaserver/plugins/dogtag.py
ac7d03
+++ b/ipaserver/plugins/dogtag.py
ac7d03
@@ -1945,6 +1945,16 @@ class ra(rabase.rabase, RestClient):
ac7d03
             if len(issuer_dn) == 1:
ac7d03
                 response_request['issuer'] = unicode(issuer_dn[0].text)
ac7d03
 
ac7d03
+            not_valid_before = cert.xpath('NotValidBefore')
ac7d03
+            if len(not_valid_before) == 1:
ac7d03
+                response_request['valid_not_before'] = (
ac7d03
+                    unicode(not_valid_before[0].text))
ac7d03
+
ac7d03
+            not_valid_after = cert.xpath('NotValidAfter')
ac7d03
+            if len(not_valid_after) == 1:
ac7d03
+                response_request['valid_not_after'] = (
ac7d03
+                    unicode(not_valid_after[0].text))
ac7d03
+
ac7d03
             status = cert.xpath('Status')
ac7d03
             if len(status) == 1:
ac7d03
                 response_request['status'] = unicode(status[0].text)
ac7d03
-- 
ac7d03
2.12.2
ac7d03