This adds a cert-find command for the dogtag backend.

Searches can be done by serial number, by subject, revocation reason, issue date, notbefore, notafter and revocation dates.

I added some basic tests for this. I made it a separate test file because the cert plugin tests do not use the declarative format and rely on the selfsign backend by default.

rob
>From f02d024abd77650b8ecf45de5215350b1602664f Mon Sep 17 00:00:00 2001
From: Rob Crittenden <[email protected]>
Date: Thu, 15 Nov 2012 10:55:33 -0500
Subject: [PATCH] Implement the cert-find command for the dogtag CA backend.

Use a new RESTful API provided by dogtag 10+. Construct an XML document
representing the search request. The output is limited to whatever dogtag
sends us, there is no way to request additional attributes other than
to read each certificate individually.

dogtag uses a boolean for each search term to indicate that it is used.
Presense of the search item is not enough, both need to be set.

The search operation is unauthenticated

https://fedorahosted.org/freeipa/ticket/2528
---
 API.txt                             |  22 ++
 VERSION                             |   2 +-
 ipalib/plugins/cert.py              | 137 +++++++++-
 ipaserver/plugins/dogtag.py         | 138 ++++++++++
 ipaserver/plugins/rabase.py         |   8 +
 tests/test_xmlrpc/test_cert_find.py | 503 ++++++++++++++++++++++++++++++++++++
 6 files changed, 807 insertions(+), 3 deletions(-)
 create mode 100644 tests/test_xmlrpc/test_cert_find.py

diff --git a/API.txt b/API.txt
index 8fbfe6f5d8da44e991b8d1a36725fc6ace1f0616..a9ee9edd841ab0862f0554b752c33d738fb82c67 100644
--- a/API.txt
+++ b/API.txt
@@ -425,6 +425,28 @@ args: 1,0,2
 arg: Any('methods*')
 output: Output('count', <type 'int'>, None)
 output: Output('results', (<type 'list'>, <type 'tuple'>), None)
+command: cert_find
+args: 0,16,4
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('exactly?', autofill=True, default=False)
+option: Str('issuedon_from?', autofill=False)
+option: Str('issuedon_to?', autofill=False)
+option: Int('max_serial_number?', autofill=False, maxvalue=2147483647)
+option: Int('min_serial_number?', autofill=False, minvalue=0)
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Int('revocation_reason?', autofill=False, maxvalue=10, minvalue=0)
+option: Str('revokedon_from?', autofill=False)
+option: Str('revokedon_to?', autofill=False)
+option: Str('subject?', autofill=False)
+option: Str('validnotafter_from?', autofill=False)
+option: Str('validnotafter_to?', autofill=False)
+option: Str('validnotbefore_from?', autofill=False)
+option: Str('validnotbefore_to?', autofill=False)
+option: Str('version?', exclude='webui')
+output: Output('count', <type 'int'>, None)
+output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Output('truncated', <type 'bool'>, None)
 command: cert_remove_hold
 args: 1,0,1
 arg: Str('serial_number')
diff --git a/VERSION b/VERSION
index 61f578dbfc9415f6f94a6612f198218c5a5e0c9a..37af5ef73b74500e0cd7397fb2c109332c049bc6 100644
--- a/VERSION
+++ b/VERSION
@@ -89,4 +89,4 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=47
+IPA_API_VERSION_MINOR=48
diff --git a/ipalib/plugins/cert.py b/ipalib/plugins/cert.py
index 3aa01621dbb519a2f0f671a8df2489c03faa6f34..4d7abd9aa3feabe4b677b93a3933260b218fecb2 100644
--- a/ipalib/plugins/cert.py
+++ b/ipalib/plugins/cert.py
@@ -24,18 +24,20 @@ if api.env.enable_ra is not True:
     # In this case, abort loading this plugin module...
     raise SkipPluginModule(reason='env.enable_ra is not True')
 import os
+import time
 from ipalib import Command, Str, Int, Bytes, Flag, File
 from ipalib import errors
 from ipalib import pkcs10
 from ipalib import x509
 from ipalib import util
+from ipalib import ngettext
 from ipalib.plugins.virtual import *
 from ipalib.plugins.service import split_principal
 import base64
 import traceback
 from ipalib.text import _
 from ipalib.request import context
-from ipalib.output import Output
+from ipalib import output
 from ipalib.plugins.service import validate_principal
 import nss.nss as nss
 from nss.error import NSPRError
@@ -60,6 +62,18 @@ In order to request a certificate:
 * The host must exist
 * The service must exist (or you use the --add option to automatically add it)
 
+SEARCHING:
+
+Certificates may be searched on by certificate subject, serial number,
+revocation reason, validity dates and the issued date.
+
+When searching on dates the _from date does a >= search and the _to date
+does a <= search. When combined these are done as an AND.
+
+Dates are treated as GMT to match the dates in the certificates.
+
+The date format is YYYY-mm-dd.
+
 EXAMPLES:
 
  Request a new certificate and add the principal:
@@ -77,6 +91,15 @@ EXAMPLES:
  Check the status of a signing request:
    ipa cert-status 10
 
+ Search for certificates by hostname:
+   ipa cert-find --subject=ipaserver.example.com
+
+ Search for revoked certificates by reason:
+   ipa cert-find --revocation-reason=5
+
+ Search for certificates based on issuance date
+   ipa cert-find --issuedon-from=2013-02-01 --issuedon-to=2013-02-07
+
 IPA currently immediately issues (or declines) all certificate requests so
 the status of a request is not normally useful. This is for future use
 or the case where a CA does not immediately issue a certificate.
@@ -100,6 +123,17 @@ http://www.ietf.org/rfc/rfc5280.txt
 
 """)
 
+def validate_pkidate(ugettext, value):
+    """
+    A date in the format of %Y-%m-%d
+    """
+    try:
+        ts = time.strptime(value, '%Y-%m-%d')
+    except ValueError, e:
+        return str(e)
+
+    return None
+
 def get_csr_hostname(csr):
     """
     Return the value of CN in the subject of the request or None
@@ -262,7 +296,7 @@ class cert_request(VirtualCommand):
     )
 
     has_output = (
-        Output('result',
+        output.Output('result',
             type=dict,
             doc=_('Dictionary mapping variable name to value'),
         ),
@@ -593,3 +627,102 @@ class cert_remove_hold(VirtualCommand):
         )
 
 api.register(cert_remove_hold)
+
+
+class cert_find(Command):
+    __doc__ = _('Search for existing certificates.')
+
+    takes_options = (
+        Str('subject?',
+            label=_('Subject'),
+            doc=_('Subject'),
+            autofill=False,
+        ),
+        Int('revocation_reason?',
+            label=_('Reason'),
+            doc=_('Reason for revoking the certificate (0-10)'),
+            minvalue=0,
+            maxvalue=10,
+            autofill=False,
+        ),
+        Int('min_serial_number?',
+            doc=_("minimum serial number"),
+            autofill=False,
+            minvalue=0,
+        ),
+        Int('max_serial_number?',
+            doc=_("maximum serial number"),
+            autofill=False,
+            maxvalue=2147483647,
+        ),
+        Flag('exactly?',
+            doc=_('match exactly with the details provided'),
+            autofill=False,
+        ),
+        Str('validnotafter_from?', validate_pkidate,
+            doc=_('Valid not after from this date'),
+            autofill=False,
+        ),
+        Str('validnotafter_to?', validate_pkidate,
+            doc=_('Valid not after to this date'),
+            autofill=False,
+        ),
+        Str('validnotbefore_from?', validate_pkidate,
+            doc=_('Valid not before from this date'),
+            autofill=False,
+        ),
+        Str('validnotbefore_to?', validate_pkidate,
+            doc=_('Valid not before to this date'),
+            autofill=False,
+        ),
+        Str('issuedon_from?', validate_pkidate,
+            doc=_('Issued on from this date'),
+            autofill=False,
+        ),
+        Str('issuedon_to?', validate_pkidate,
+            doc=_('Issued on to this date'),
+            autofill=False,
+        ),
+        Str('revokedon_from?', validate_pkidate,
+            doc=_('Revoked on from this date'),
+            autofill=False,
+        ),
+        Str('revokedon_to?', validate_pkidate,
+            doc=_('Revoked on to this date'),
+            autofill=False,
+        ),
+        Int('sizelimit?',
+            label=_('Size Limit'),
+            doc=_('Maximum number of certs returned'),
+            flags=['no_display'],
+            minvalue=0,
+            default=100,
+        ),
+    )
+
+    has_output = output.standard_list_of_entries
+    has_output_params = (
+        Str('serial_number_hex',
+            label=_('Serial number (hex)'),
+        ),
+        Str('serial_number',
+            label=_('Serial number'),
+        ),
+        Str('status',
+            label=_('Status'),
+        ),
+    )
+
+    msg_summary = ngettext(
+        '%(count)d certificate matched', '%(count)d certificates matched', 0
+    )
+
+    def execute(self, **options):
+        ret = dict(
+            result=self.Backend.ra.find(options)
+        )
+        ret['count'] = len(ret['result'])
+        ret['truncated'] = False
+        return ret
+
+api.register(cert_find)
diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
index d52bb7e980f1158dc2c6329c4127d3b7b8bfcca2..a0ebf68146b223dad2c4303f558509a6a5af38da 100644
--- a/ipaserver/plugins/dogtag.py
+++ b/ipaserver/plugins/dogtag.py
@@ -237,9 +237,14 @@ digits and nothing else follows.
 '''
 
 from lxml import etree
+import urllib
+import urllib2
 import datetime
+import time
 from ipapython.dn import DN
 from ldap.filter import escape_filter_chars
+import ipapython.dogtag
+from ipapython import ipautil
 
 # These are general status return values used when
 # CMSServlet.outputError() is invoked.
@@ -1715,4 +1720,137 @@ class ra(rabase.rabase):
 
         return cmd_result
 
+    def find(self, options):
+        """
+        Search for certificates
+
+        :param options: dictionary of search options
+        """
+
+        def convert_time(value):
+            """
+            Convert time to milliseconds to pass to dogtag
+            """
+            ts = time.strptime(value, '%Y-%m-%d')
+            return int(time.mktime(ts) * 1000)
+
+        self.debug('%s.find()', self.fullname)
+
+        # Create the root element
+        page = etree.Element('CertSearchRequest')
+
+        # Make a new document tree
+        doc = etree.ElementTree(page)
+
+        # This matches the default configuration of the pki tool.
+        booloptions = {'serialNumberRangeInUse': True,
+                       'subjectInUse': False,
+                       'matchExactly': False,
+                       'revokedByInUse': False,
+                       'revokedOnInUse': False,
+                       'revocationReasonInUse': False,
+                       'issuedByInUse': False,
+                       'issuedOnInUse': False,
+                       'validNotBeforeInUse': False,
+                       'validNotAfterInUse': False,
+                       'validityLengthInUse': False,
+                       'certTypeInUse': False}
+
+        if options.get('exactly', False):
+            booloptions['matchExactly'] = True
+
+        if 'subject' in options:
+            node = etree.SubElement(page, 'commonName')
+            node.text = options['subject']
+            booloptions['subjectInUse'] = True
+
+        if 'revocation_reason' in options:
+            node = etree.SubElement(page, 'revocationReason')
+            node.text = unicode(options['revocation_reason'])
+            booloptions['revocationReasonInUse'] = True
+
+        if 'min_serial_number' in options:
+            node = etree.SubElement(page, 'serialFrom')
+            node.text = unicode(options['min_serial_number'])
+
+        if 'max_serial_number' in options:
+            node = etree.SubElement(page, 'serialTo')
+            node.text = unicode(options['max_serial_number'])
+
+        # date_types is a tuple that consists of:
+        #   1. attribute name passed from IPA API
+        #   2. attribute name used by REST API
+        #   3. boolean to set in the REST API
+
+        date_types = (
+          ('validnotbefore_from', 'validNotBeforeFrom', 'validNotBeforeInUse'),
+          ('validnotbefore_to', 'validNotBeforeTo', 'validNotBeforeInUse'),
+          ('validnotafter_from', 'validNotAfterFrom', 'validNotAfterInUse'),
+          ('validnotafter_to', 'validNotAfterTo', 'validNotAfterInUse'),
+          ('issuedon_from', 'issuedOnFrom','issuedOnInUse'),
+          ('issuedon_to', 'issuedOnTo','issuedOnInUse'),
+          ('revokedon_from', 'revokedOnFrom','revokedOnInUse'),
+          ('revokedon_to', 'revokedOnTo','revokedOnInUse'),
+        )
+
+        for (attr, dattr, battr) in date_types:
+            if attr in options:
+                epoch = convert_time(options[attr])
+                node = etree.SubElement(page, dattr)
+                node.text = unicode(epoch)
+                booloptions[battr] = True
+
+        # Add the boolean options to our XML document
+        for opt in booloptions:
+            e = etree.SubElement(page, opt)
+            e.text = str(booloptions[opt]).lower()
+
+        payload = etree.tostring(doc, pretty_print=False, xml_declaration=True, encoding='UTF-8')
+        self.debug('%s.find(): request: %s', self.fullname, payload)
+
+        url = 'http://%s/ca/rest/certs/search?size=%d' % (ipautil.format_netloc(self.ca_host, ipapython.dogtag.configured_constants().UNSECURE_PORT), options.get('sizelimit', 100))
+
+        opener = urllib2.build_opener()
+        opener.addheaders = [('Accept-Encoding', 'gzip, deflate'),
+                             ('User-Agent', 'IPA')]
+
+        req = urllib2.Request(url=url, data=payload, headers={'Content-Type': 'application/xml'})
+        try:
+            response = opener.open(req)
+        except urllib2.HTTPError, e:
+            self.raise_certificate_operation_error('find',
+                                                   detail=e.msg)
+        except urllib2.URLError, e:
+            self.raise_certificate_operation_error('find',
+                                                   detail=e.reason)
+
+        data = response.readlines()
+        self.debug('%s.find(): response: %s', self.fullname, data)
+        parser = etree.XMLParser()
+        try:
+            doc = etree.fromstring(data[0], parser)
+        except etree.XMLSyntaxError, e:
+            self.raise_certificate_operation_error('find',
+                                                   detail=e.msg)
+
+        # Grab all the certificates
+        certs = doc.xpath('//CertDataInfo')
+
+        results = []
+
+        for cert in certs:
+            response_request = {}
+            response_request['serial_number'] = int(cert.get('id'), 16) # parse as hex
+            response_request['serial_number_hex'] = u'0x%X' % response_request['serial_number']
+
+            dn = cert.xpath('SubjectDN')
+            if len(dn) == 1:
+                response_request['subject'] = unicode(dn[0].text)
+            status = cert.xpath('Status')
+            if len(status) == 1:
+                response_request['status'] = unicode(status[0].text)
+            results.append(response_request)
+
+        return results
+    
 api.register(ra)
diff --git a/ipaserver/plugins/rabase.py b/ipaserver/plugins/rabase.py
index 369027b4367e6136330e47786c94eec1671f474b..1d8713f4a2e2113620d381ca797eadb8f3ab1627 100644
--- a/ipaserver/plugins/rabase.py
+++ b/ipaserver/plugins/rabase.py
@@ -111,3 +111,11 @@ class rabase(Backend):
         """
         raise errors.NotImplementedError(name='%s.take_certificate_off_hold' % self.name)
 
+
+    def find(self, options):
+        """
+        Search for certificates
+
+        :param options: dictionary of search options
+        """
+        raise errors.NotImplementedError(name='%s.find' % self.name)
diff --git a/tests/test_xmlrpc/test_cert_find.py b/tests/test_xmlrpc/test_cert_find.py
new file mode 100644
index 0000000000000000000000000000000000000000..ecbcebf8a1183404c5e976daf75ef99f4aa92868
--- /dev/null
+++ b/tests/test_xmlrpc/test_cert_find.py
@@ -0,0 +1,503 @@
+# Authors:
+#   Rob Crittenden <[email protected]>
+#
+# Copyright (C) 2013  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+Test the find function in the `ipalib/plugins/cert.py` module.
+"""
+
+import nose
+from ipalib import api, errors
+from ipapython.dn import DN
+from tests.test_xmlrpc import objectclasses
+from xmlrpc_test import Declarative, fuzzy_digits, fuzzy_uuid
+
+short = api.env.host.replace('.' + api.env.domain, '')
+
+# This depends on a fresh, unchanged dogtag installation.
+# This assumes, for now, that the serial numbers are incrementing. This
+# will eventually change.
+
+all_certs = [{
+              'status': u'VALID',
+              'serial_number': 1,
+              'serial_number_hex': u'0x1',
+              'subject': u'CN=Certificate Authority,O=%s' % api.env.realm,
+            },
+            {'status': u'VALID',
+             'serial_number': 2,
+             'serial_number_hex': u'0x2',
+             'subject': u'CN=OCSP Subsystem,O=%s' % api.env.realm,
+            },
+            {'status': u'VALID',
+             'serial_number': 3,
+             'serial_number_hex': u'0x3',
+             'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm),
+            },
+            {'status': u'VALID',
+             'serial_number': 4,
+             'serial_number_hex': u'0x4',
+             'subject': u'CN=CA Subsystem,O=%s' % api.env.realm,
+            },
+            {'status': u'VALID',
+             'serial_number': 5,
+             'serial_number_hex': u'0x5',
+             'subject': u'CN=CA Audit,O=%s' % api.env.realm,
+            },
+            {'status': u'VALID',
+             'serial_number': 6,
+             'serial_number_hex': u'0x6',
+             'subject': u'CN=ipa-ca-agent,O=%s' % api.env.realm,
+            },
+            {'status': u'VALID',
+             'serial_number': 7,
+             'serial_number_hex': u'0x7',
+             'subject': u'CN=IPA RA,O=%s' % api.env.realm,
+            },
+            {'status': u'VALID',
+             'serial_number': 8,
+              'serial_number_hex': u'0x8',
+              'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm),
+            },
+            {'status': u'VALID',
+             'serial_number': 9,
+             'serial_number_hex': u'0x9',
+             'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm),
+            },
+            {'status': u'VALID',
+             'serial_number': 10,
+             'serial_number_hex': u'0xA',
+             'subject': u'CN=Object Signing Cert,O=%s' % api.env.realm
+            }]
+
+class test_cert_find(Declarative):
+
+    @classmethod
+    def setUpClass(cls):
+        super(test_cert_find, cls).setUpClass()
+
+        if api.env.ra_plugin != 'dogtag':
+            raise nose.SkipTest('cert_find for dogtag CA only')
+
+    cleanup_commands = [
+    ]
+
+    tests = [
+
+        dict(
+            desc='Find all certificates',
+            command=('cert_find', [], {}),
+            expected={
+                'summary': u'10 certificates matched',
+                'count': 10,
+                'truncated': False,
+                'result': all_certs,
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates for this IPA server %r' % api.env.host,
+            command=(
+                'cert_find', [], { 'subject': api.env.host }
+            ),
+            expected={
+                'summary': u'3 certificates matched',
+                'count': 3,
+                'truncated': False,
+                'result': [{
+                    'status': u'VALID',
+                    'serial_number': 3,
+                    'serial_number_hex': u'0x3',
+                    'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm)
+                },
+                {
+                    'status': u'VALID',
+                    'serial_number': 8,
+                    'serial_number_hex': u'0x8',
+                    'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm)
+                },
+                {
+                    'status': u'VALID',
+                    'serial_number': 9,
+                    'serial_number_hex': u'0x9',
+                    'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm)
+                 }],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates for this IPA server %r, exact' % api.env.host,
+            command=(
+                'cert_find', [], { 'subject': api.env.host, 'exactly': True }
+            ),
+            expected={
+                'summary': u'3 certificates matched',
+                'count': 3,
+                'truncated': False,
+                'result': [{
+                    'status': u'VALID',
+                    'serial_number': 3,
+                    'serial_number_hex': u'0x3',
+                    'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm)
+                },
+                {
+                    'status': u'VALID',
+                    'serial_number': 8,
+                    'serial_number_hex': u'0x8',
+                    'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm)
+                },
+                {
+                    'status': u'VALID',
+                    'serial_number': 9,
+                    'serial_number_hex': u'0x9',
+                    'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm)
+                 }],
+            },
+        ),
+
+
+        dict(
+            desc='Find using server short-name name %r' % short,
+            command=(
+                'cert_find', [], { 'subject': short }
+            ),
+            expected={
+                'summary': u'3 certificates matched',
+                'count': 3,
+                'truncated': False,
+                'result': [{
+                    'status': u'VALID',
+                    'serial_number': 3,
+                    'serial_number_hex': u'0x3',
+                    'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm)
+                },
+                {
+                    'status': u'VALID',
+                    'serial_number': 8,
+                    'serial_number_hex': u'0x8',
+                    'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm)
+                },
+                {
+                    'status': u'VALID',
+                    'serial_number': 9,
+                    'serial_number_hex': u'0x9',
+                    'subject': u'CN=%s,O=%s' % (api.env.host, api.env.realm)
+                 }],
+            },
+        ),
+
+
+        dict(
+            desc='Find using exact match on server short-name name %r' % short,
+            command=(
+                'cert_find', [], { 'subject': short, 'exactly': True }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates with revocation reason 0',
+            command=(
+                'cert_find', [], { 'revocation_reason': 0 }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates with revocation reason 1',
+            command=(
+                'cert_find', [], { 'revocation_reason': 1 }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates with revocation reason 2',
+            command=(
+                'cert_find', [], { 'revocation_reason': 2 }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates with revocation reason 3',
+            command=(
+                'cert_find', [], { 'revocation_reason': 3 }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates with revocation reason 4',
+            command=(
+                'cert_find', [], { 'revocation_reason': 4 }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates with revocation reason 5',
+            command=(
+                'cert_find', [], { 'revocation_reason': 5 }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates with revocation reason 6',
+            command=(
+                'cert_find', [], { 'revocation_reason': 6 }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates with revocation reason 8',
+            command=(
+                'cert_find', [], { 'revocation_reason': 8 }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates with revocation reason 9',
+            command=(
+                'cert_find', [], { 'revocation_reason': 9 }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates with revocation reason 10',
+            command=(
+                'cert_find', [], { 'revocation_reason': 10 }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates issued since 2008',
+            command=(
+                'cert_find', [], { 'issuedon_from': u'2008-01-01' }
+            ),
+            expected={
+                'summary': u'10 certificates matched',
+                'count': 10,
+                'truncated': False,
+                'result': all_certs,
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates issued through 2008',
+            command=(
+                'cert_find', [], { 'issuedon_to': u'2008-01-01' }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates valid not before 2008',
+            command=(
+                'cert_find', [], { 'validnotbefore_from': u'2008-01-01' }
+            ),
+            expected={
+                'summary': u'10 certificates matched',
+                'count': 10,
+                'truncated': False,
+                'result': all_certs,
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates valid not before to 2100',
+            command=(
+                'cert_find', [], { 'validnotbefore_to': u'2100-01-01' }
+            ),
+            expected={
+                'summary': u'10 certificates matched',
+                'count': 10,
+                'truncated': False,
+                'result': all_certs,
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates valid not before 2100',
+            command=(
+                'cert_find', [], { 'validnotbefore_from': u'2100-01-01' }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates valid not before to 2008',
+            command=(
+                'cert_find', [], { 'validnotbefore_to': u'2008-01-01' }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates valid not after 2008',
+            command=(
+                'cert_find', [], { 'validnotafter_from': u'2008-01-01' }
+            ),
+            expected={
+                'summary': u'10 certificates matched',
+                'count': 10,
+                'truncated': False,
+                'result': all_certs,
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates valid not after to 2100',
+            command=(
+                'cert_find', [], { 'validnotafter_to': u'2100-01-01' }
+            ),
+            expected={
+                'summary': u'10 certificates matched',
+                'count': 10,
+                'truncated': False,
+                'result': all_certs,
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates valid not after 2100',
+            command=(
+                'cert_find', [], { 'validnotafter_from': u'2100-01-01' }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates valid not after to 2008',
+            command=(
+                'cert_find', [], { 'validnotafter_to': u'2008-01-01' }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+        # I could add tests on serial number here but it would be pointless
+        # since they will soon be randomly assigned.
+
+    ]
-- 
1.8.1

_______________________________________________
Freeipa-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to