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