Petr Viktorin wrote:
On 02/06/2013 12:44 AM, Rob Crittenden wrote:
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

Thanks! The code works well, but I found a few issues.


These tests don't work when the full test suite is run: test_cert adds
and revokes additional certs that throw the code off.
Perhaps have the tests only query valid certs? I don't see that option
but I think it would be helpful to support.

I added some rather nasty hacks to the test to make things pass. I limit the search to 10 certificates, which is the number start with by default. There is an open dogtag ticket to return only VALID certificates so we should be able to drop this eventually.

I had to go further on one of the revocation tests, limiting it to a sizelimit of 1. The count changes every time the suite runs so this is the safest for now. It also means that one test will fail if this is the only part of the suite executed.



The API.txt check fails:
Option sizelimit? of command cert_find in ipalib, not in API file:


Int('sizelimit?', default=100, minvalue=0)

Ouch. I thought I had fixed that, obviously not. Done now.


What are --all and --raw for? Is the plan to implement --all if/when
Dogtag supports requesting additional data?

Correct, they don't do anything at the moment. I have an RFE open to return additional data from certs. Once that is done then all will make sense. I don't know that raw will ever do anything interesting here but it comes with all commands.


The format of --validnotbefore-to and friends should be mentioned in
--help text; the following is confusing:
$ ipa cert-show 1
[...]
   Not Before: Wed Feb 06 09:32:17 2013 UTC
[...]
$ ipa cert-find -h
[...]
   --validnotbefore-to=STR
                         Valid not before to this date
[...]
$ ipa cert-find --validnotbefore-to='Wed Feb 06 09:32:17 2013 UTC'
ipa: ERROR: invalid 'validnotbefore_to': time data u'Wed Feb 06 09:32:17
2013 UTC' does not match format '%Y-%m-%d'

It was listed in the top block but I added it to the usage help as well for clarity.

Could you make the help text for --exactly more specific?

Done.


Please remove the extra whitespace at the end of dogtag.py

I'd welcome a link to the design page in the commit message.


both done

rob
>From b8994fd0530ef357d79605fb4b74c6ff0eb2e536 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcrit...@redhat.com>
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

Design page: http://freeipa.org/page/V3/Cert_find

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

diff --git a/API.txt b/API.txt
index 8fbfe6f5d8da44e991b8d1a36725fc6ace1f0616..6b997f37b455366c66b34fd2df11c2acaa79d739 100644
--- a/API.txt
+++ b/API.txt
@@ -425,6 +425,29 @@ 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,17,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: Int('sizelimit?', default=100, minvalue=0)
+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..51493c34ef6bdd745bde609f42d177f4b98555b1 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 the common name exactly'),
+            autofill=False,
+        ),
+        Str('validnotafter_from?', validate_pkidate,
+            doc=_('Valid not after from this date (YYYY-mm-dd)'),
+            autofill=False,
+        ),
+        Str('validnotafter_to?', validate_pkidate,
+            doc=_('Valid not after to this date (YYYY-mm-dd)'),
+            autofill=False,
+        ),
+        Str('validnotbefore_from?', validate_pkidate,
+            doc=_('Valid not before from this date (YYYY-mm-dd)'),
+            autofill=False,
+        ),
+        Str('validnotbefore_to?', validate_pkidate,
+            doc=_('Valid not before to this date (YYYY-mm-dd)'),
+            autofill=False,
+        ),
+        Str('issuedon_from?', validate_pkidate,
+            doc=_('Issued on from this date (YYYY-mm-dd)'),
+            autofill=False,
+        ),
+        Str('issuedon_to?', validate_pkidate,
+            doc=_('Issued on to this date (YYYY-mm-dd)'),
+            autofill=False,
+        ),
+        Str('revokedon_from?', validate_pkidate,
+            doc=_('Revoked on from this date (YYYY-mm-dd)'),
+            autofill=False,
+        ),
+        Str('revokedon_to?', validate_pkidate,
+            doc=_('Revoked on to this date (YYYY-mm-dd)'),
+            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..28bf754cb578a7113931689fd509b684bae3ebb5 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..5e7b6b442f118496194a324da4f323d5d725b9d5
--- /dev/null
+++ b/tests/test_xmlrpc/test_cert_find.py
@@ -0,0 +1,531 @@
+# Authors:
+#   Rob Crittenden <rcrit...@redhat.com>
+#
+# 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
+            }]
+
+# The size limit of 10 limits the view to the first 10 certificates
+# issued. This is to prevent certificates revoked due to deleted
+# services in other tests affect the outcome of these.
+#
+# https://fedorahosted.org/pki/ticket/501 will add an option to return
+# only VALID certs so we can eventually drop this.
+
+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', [], { 'sizelimit': 10 }),
+            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, 'sizelimit': 10}
+            ),
+            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,
+                                   'sizelimit': 10 }
+            ),
+            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, 'sizelimit': 10 }
+            ),
+            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,
+                                   'sizelimit': 10 }
+            ),
+            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, 'sizelimit': 10 }
+            ),
+            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, 'sizelimit': 10 }
+            ),
+            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, 'sizelimit': 10 }
+            ),
+            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, 'sizelimit': 10 }
+            ),
+            expected={
+                'summary': u'0 certificates matched',
+                'count': 0,
+                'truncated': False,
+                'result': [],
+            },
+        ),
+
+
+# We revoke certificates as part of deleting services. There is no way in
+# advance to know how many there will be. By limiting it to one cert we
+# can somewhat expect the serial number.
+
+        dict(
+            desc='Find all certificates with revocation reason 4',
+            command=(
+                'cert_find', [], { 'revocation_reason': 4, 'sizelimit': 1 }
+            ),
+            expected={
+                'summary': u'1 certificate matched',
+                'count': 1,
+                'truncated': False,
+                'result': [{
+                    'status': u'REVOKED',
+                    'serial_number': 11,
+                    'serial_number_hex': u'0xB',
+                    'subject': u'CN=ipatestcert.%s,O=%s' % (api.env.domain, api.env.realm)
+                }]
+            },
+        ),
+
+
+        dict(
+            desc='Find all certificates with revocation reason 5',
+            command=(
+                'cert_find', [], { 'revocation_reason': 5, 'sizelimit': 10 }
+            ),
+            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, 'sizelimit': 10 }
+            ),
+            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, 'sizelimit': 10 }
+            ),
+            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, 'sizelimit': 10 }
+            ),
+            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, 'sizelimit': 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',
+                                   'sizelimit': 10 }
+            ),
+            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',
+                                   'sizelimit': 10 }
+            ),
+            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',
+                                   'sizelimit': 10  }
+            ),
+            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',
+                                   'sizelimit': 10  }
+            ),
+            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',
+                                   'sizelimit': 10  }
+            ),
+            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',
+                                   'sizelimit': 10  }
+            ),
+            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',
+                                   'sizelimit': 10  }
+            ),
+            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',
+                                   'sizelimit': 10  }
+            ),
+            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',
+                                   'sizelimit': 10  }
+            ),
+            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',
+                                   'sizelimit': 10  }
+            ),
+            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
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to