Rob Crittenden wrote:
Petr Viktorin wrote:
On 02/15/2013 10:43 PM, Rob Crittenden wrote:
Petr Viktorin wrote:
On 02/06/2013 07:23 PM, Rob Crittenden wrote:
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.

This gets rid of most of the failures, but it still fails the "certs
for
this IPA server/short name" tests if the cert from ./make-cert is
present (creating it is part of `make test`).
If make-cert is run more times, it'll revoke the previous cert, so the
test for revocation reason 4 fails as well.

It looks like when using sizelimit Dogtag will always discard *newer*
certs, ones with higher serials. Is it documented behavior or does
Dogtag just happen to do that?

It isn't documented anywhere I could find, it is just what dogtag
returns

I wonder how other people run their tests. This solution looks like it
could break easily if people do something differently :(

I'm not sure how to solve this properly. Perhaps not using Declarative,
and checking "by hand" that the wanted certs are in the response and
the
unwanted ones are not, would work better.

I ended up switching the test class. It is not a very fine-grained set
of tests, mostly searching with limits and verifying that we fall within
a reasonable range, but it is better than nothing.

rob

This works much better, thanks! Just two nitpicks now.

The patch doesn't apply well, there's a conflict in VERSION and some
added trailing whitespace,

AFAIK this would be the only (first?) test that relies on Nose's
ordering of test modules -- tests 0011 and 0030 rely on the other cert
tests running first. Please at least mention that in a comment. Or
better, move class test_cert_find to test_cert.py



Rebased the patch and removed whitespace.

I went ahead and combined this with the existing test_cert file.
Originally test_cert was only tested against lite-server but since it
works against a live dogtag server too it makes sense to combine them.

I improved the set up documentation a little bit and tried to handle all
the different configurations that one might see so that this should be
runnable against either a live server or the lite-server for both the
selfsign and dogtag backends.

This relies on the user configuring ~/.ipa/default.conf to match the
remote server. There is no way from a client to know what kind of CA
backend a server is running.

rob

And the patch.

rob

>From c06cb0899e4b0a02feadd1dbfb4bfcbe28109e9a 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.py | 312 ++++++++++++++++++++++++++++++++++++++---
 6 files changed, 598 insertions(+), 22 deletions(-)

diff --git a/API.txt b/API.txt
index 5219c51be62862c43ebe9396147ff220b33605c7..e39936974c7216c4e0d9266bbf56d0f3ba2b3f01 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 7bcfe6f9628b8193f44faa9d399e3295e3204c1f..60431df5b553aa79a75628324a421978e4688b9e 100644
--- a/VERSION
+++ b/VERSION
@@ -89,4 +89,4 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=49
+IPA_API_VERSION_MINOR=50
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.py b/tests/test_xmlrpc/test_cert.py
index 4f95ffe193c4d2a7d91d7c6d8d429f296f5e4ce1..906d1977afc6a475ed8a7a75f11a352f83b49f71 100644
--- a/tests/test_xmlrpc/test_cert.py
+++ b/tests/test_xmlrpc/test_cert.py
@@ -1,7 +1,7 @@
 # Authors:
 #   Rob Crittenden <rcrit...@redhat.com>
 #
-# Copyright (C) 2009  Red Hat
+# Copyright (C) 2009,2013  Red Hat
 # see file 'COPYING' for use and warranty information
 #
 # This program is free software; you can redistribute it and/or modify
@@ -23,7 +23,7 @@ Test the `ipalib/plugins/cert.py` module against the selfsign plugin.
 import sys
 import os
 import shutil
-from nose.tools import assert_raises  # pylint: disable=E0611
+from nose.tools import raises, assert_raises  # pylint: disable=E0611
 
 from xmlrpc_test import XMLRPC_test, assert_attr_equal
 from ipalib import api
@@ -39,29 +39,50 @@ from ipapython.dn import DN
 cert = None
 newcert = None
 
+def is_db_configured():
+    """
+    Raise an exception if we are testing against lite-server and the
+    developer cert database is configured.
+    """
+    aliasdir = api.env.dot_ipa + os.sep + 'alias' + os.sep + '.pwd'
+
+    if (api.env.xmlrpc_uri == u'http://localhost:8888/ipa/xml' and
+       not ipautil.file_exists(aliasdir)):
+        raise nose.SkipTest('developer CA not configured in %s' % aliasdir)
+
 # Test setup
 #
 # This test needs a configured CA behind it in order to work properly
-# It currently specifically tests for a self-signed CA but there is no
-# reason the test wouldn't work with a dogtag CA as well with some
-# additional work. This will change when selfsign is no longer the default CA.
 #
-# To set it up grab the 3 NSS db files from a self-signed CA from
-# /etc/httpd/alias to ~/.ipa/alias. Copy /etc/httpd/alias/pwdfile.txt to
-# ~/.ipa/alias/.pwd. Change ownership of these files too. That should do it.
+# To test against Apache directly then no changes are required. Just be
+# sure the xmlrpc_uri in ~/.ipa/default.conf points to Apache.
+#
+# To test against a selfsign or dogtag CA in the lite-server:
+#
+# - Copy the 3 NSS db files from /etc/httpd/alias to ~/.ipa/alias
+# - Copy /etc/httpd/alias/pwdfile.txt to  ~/.ipa/alias/.pwd.
+# - Change ownership of these files to be readable by you.
+#
+# The API tested depends on the value of ~/.ipa/default/ra_plugin when
+# running as the lite-server.
 
 class test_cert(XMLRPC_test):
 
+    @classmethod
+    def setUpClass(cls):
+        super(test_cert, cls).setUpClass()
+
+        if 'cert_request' not in api.Command:
+            raise nose.SkipTest('cert_request not registered')
+
+        is_db_configured()
+
     def run_certutil(self, args, stdin=None):
         new_args = ["/usr/bin/certutil", "-d", self.reqdir]
         new_args = new_args + args
         return ipautil.run(new_args, stdin)
 
     def setUp(self):
-        if 'cert_request' not in api.Command:
-            raise nose.SkipTest('cert_request not registered')
-        if not ipautil.file_exists(api.env.dot_ipa + os.sep + 'alias' + os.sep + '.pwd'):
-            raise nose.SkipTest('developer self-signed CA not configured')
         super(test_cert, self).setUp()
         self.reqdir = tempfile.mkdtemp(prefix = "tmp-")
         self.reqfile = self.reqdir + "/test.csr"
@@ -99,7 +120,7 @@ class test_cert(XMLRPC_test):
     host_fqdn = u'ipatestcert.%s' % api.env.domain
     service_princ = u'test/%s@%s' % (host_fqdn, api.env.realm)
 
-    def test_1_cert_add(self):
+    def test_0001_cert_add(self):
         """
         Test the `xmlrpc.cert_request` method without --add.
 
@@ -112,7 +133,7 @@ class test_cert(XMLRPC_test):
         with assert_raises(errors.NotFound):
             res = api.Command['cert_request'](csr, principal=self.service_princ)
 
-    def test_2_cert_add(self):
+    def test_0002_cert_add(self):
         """
         Test the `xmlrpc.cert_request` method with --add.
         """
@@ -125,7 +146,7 @@ class test_cert(XMLRPC_test):
         # save the cert for the service_show/find tests
         cert = res['certificate']
 
-    def test_3_service_show(self):
+    def test_0003_service_show(self):
         """
         Verify that service-show has the right certificate using service-show.
         """
@@ -134,7 +155,7 @@ class test_cert(XMLRPC_test):
         res = api.Command['service_show'](self.service_princ)['result']
         assert base64.b64encode(res['usercertificate'][0]) == cert
 
-    def test_4_service_find(self):
+    def test_0004_service_find(self):
         """
         Verify that service-find has the right certificate using service-find.
         """
@@ -144,7 +165,7 @@ class test_cert(XMLRPC_test):
         res = api.Command['service_find'](self.service_princ)['result']
         assert base64.b64encode(res[0]['usercertificate'][0]) == cert
 
-    def test_5_cert_renew(self):
+    def test_0005_cert_renew(self):
         """
         Issue a new certificate for a service
         """
@@ -156,7 +177,7 @@ class test_cert(XMLRPC_test):
         # save the cert for the service_show/find tests
         newcert = res['certificate']
 
-    def test_6_service_show(self):
+    def test_0006_service_show(self):
         """
         Verify the new certificate with service-show.
         """
@@ -168,7 +189,7 @@ class test_cert(XMLRPC_test):
         # And it should match the new one
         assert base64.b64encode(res['usercertificate'][0]) == newcert
 
-    def test_7_cleanup(self):
+    def test_0007_cleanup(self):
         """
         Clean up cert test data
         """
@@ -178,3 +199,256 @@ class test_cert(XMLRPC_test):
         # Verify that the service is gone
         res = api.Command['service_find'](self.service_princ)
         assert res['count'] == 0
+
+class test_cert_find(XMLRPC_test):
+
+    @classmethod
+    def setUpClass(cls):
+        super(test_cert_find, cls).setUpClass()
+
+        if 'cert_find' not in api.Command:
+            raise nose.SkipTest('cert_find not registered')
+
+        if api.env.ra_plugin != 'dogtag':
+            raise nose.SkipTest('cert_find for dogtag CA only')
+
+        is_db_configured()
+
+    """
+    Test the `cert-find` command.
+    """
+    short = api.env.host.replace('.' + api.env.domain, '')
+
+    def test_0001_find_all(self):
+        """
+        Search for all certificates.
+
+        We don't know how many we'll get but there should be at least 10
+        by default.
+        """
+        res = api.Command['cert_find']()
+        assert 'count' in res and res['count'] >= 10
+
+    def test_0002_find_CA(self):
+        """
+        Search for the CA certificate.
+        """
+        res = api.Command['cert_find'](subject=u'Certificate Authority')
+        assert 'count' in res and res['count'] == 1
+
+    def test_0003_find_OCSP(self):
+        """
+        Search for the OCSP certificate.
+        """
+        res = api.Command['cert_find'](subject=u'OCSP Subsystem')
+
+    def test_0004_find_this_host(self):
+        """
+        Find all certificates for this IPA server
+        """
+        res = api.Command['cert_find'](subject=api.env.host)
+        assert 'count' in res and res['count'] > 1
+
+    def test_0005_find_this_host_exact(self):
+        """
+        Find all certificates for this IPA server (exact)
+        """
+        res = api.Command['cert_find'](subject=api.env.host, exactly=True)
+        assert 'count' in res and res['count'] > 1
+
+    def test_0006_find_this_short_host_exact(self):
+        """
+        Find all certificates for this IPA server short name (exact)
+        """
+        res = api.Command['cert_find'](subject=self.short, exactly=True)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0007_find_revocation_reason_0(self):
+        """
+        Find all certificates with revocation reason 0
+        """
+        res = api.Command['cert_find'](revocation_reason=0)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0008_find_revocation_reason_1(self):
+        """
+        Find all certificates with revocation reason 1
+        """
+        res = api.Command['cert_find'](revocation_reason=1)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0009_find_revocation_reason_2(self):
+        """
+        Find all certificates with revocation reason 2
+        """
+        res = api.Command['cert_find'](revocation_reason=2)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0010_find_revocation_reason_3(self):
+        """
+        Find all certificates with revocation reason 3
+        """
+        res = api.Command['cert_find'](revocation_reason=3)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0011_find_revocation_reason_4(self):
+        """
+        Find all certificates with revocation reason 4
+
+        There is no way to know in advance how many revoked certificates
+        we'll have but in the context of make-test we'll have at least one.
+        """
+        res = api.Command['cert_find'](revocation_reason=4)
+        assert 'count' in res and res['count'] >= 1
+
+    def test_0012_find_revocation_reason_5(self):
+        """
+        Find all certificates with revocation reason 5
+        """
+        res = api.Command['cert_find'](revocation_reason=5)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0013_find_revocation_reason_6(self):
+        """
+        Find all certificates with revocation reason 6
+        """
+        res = api.Command['cert_find'](revocation_reason=6)
+        assert 'count' in res and res['count'] == 0
+
+    # There is no revocation reason #7
+
+    def test_0014_find_revocation_reason_8(self):
+        """
+        Find all certificates with revocation reason 8
+        """
+        res = api.Command['cert_find'](revocation_reason=8)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0015_find_revocation_reason_9(self):
+        """
+        Find all certificates with revocation reason 9
+        """
+        res = api.Command['cert_find'](revocation_reason=9)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0016_find_revocation_reason_10(self):
+        """
+        Find all certificates with revocation reason 10
+        """
+        res = api.Command['cert_find'](revocation_reason=10)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0017_find_by_issuedon(self):
+        """
+        Find all certificates issued since 2008
+        """
+        res = api.Command['cert_find'](issuedon_from=u'2008-01-01',
+                                       sizelimit=10)
+        assert 'count' in res and res['count'] == 10
+
+    def test_0018_find_through_issuedon(self):
+        """
+        Find all certificates issued through 2008
+        """
+        res = api.Command['cert_find'](issuedon_to=u'2008-01-01',
+                                       sizelimit=10)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0019_find_notvalid_before(self):
+        """
+        Find all certificates valid not before 2008
+        """
+        res = api.Command['cert_find'](validnotbefore_from=u'2008-01-01',
+                                       sizelimit=10)
+        assert 'count' in res and res['count'] == 10
+
+    def test_0020_find_notvalid_before(self):
+        """
+        Find all certificates valid not before to 2100
+        """
+        res = api.Command['cert_find'](validnotbefore_to=u'2100-01-01',
+                                       sizelimit=10)
+        assert 'count' in res and res['count'] == 10
+
+    def test_0021_find_notvalid_before(self):
+        """
+        Find all certificates valid not before 2100
+        """
+        res = api.Command['cert_find'](validnotbefore_from=u'2100-01-01',
+                                       sizelimit=10)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0022_find_notvalid_before(self):
+        """
+        Find all certificates valid not before to 2008
+        """
+        res = api.Command['cert_find'](validnotbefore_to=u'2008-01-01',
+                                       sizelimit=10)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0023_find_notvalid_after(self):
+        """
+        Find all certificates valid not after 2008
+        """
+        res = api.Command['cert_find'](validnotafter_from=u'2008-01-01',
+                                       sizelimit=10)
+        assert 'count' in res and res['count'] == 10
+
+    def test_0024_find_notvalid_after(self):
+        """
+        Find all certificates valid not after to 2100
+        """
+        res = api.Command['cert_find'](validnotafter_to=u'2100-01-01',
+                                       sizelimit=10)
+        assert 'count' in res and res['count'] == 10
+
+    def test_0025_find_notvalid_after(self):
+        """
+        Find all certificates valid not after 2100
+        """
+        res = api.Command['cert_find'](validnotafter_from=u'2100-01-01',
+                                       sizelimit=10)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0026_find_notvalid_after(self):
+        """
+        Find all certificates valid not after to 2008
+        """
+        res = api.Command['cert_find'](validnotafter_to=u'2008-01-01',
+                                       sizelimit=10)
+        assert 'count' in res and res['count'] == 0
+
+    def test_0027_sizelimit_zero(self):
+        """
+        Search with a sizelimit of 0
+        """
+        res = api.Command['cert_find'](sizelimit=0)
+        assert 'count' in res and res['count'] == 0
+
+    @raises(errors.ValidationError)
+    def test_0028_find_negative_size(self):
+        """
+        Search with a negative sizelimit
+        """
+        res = api.Command['cert_find'](sizelimit=-100)
+
+    def test_0029_search_for_notfound(self):
+        """
+        Search for a host that isn't there.
+        """
+        res = api.Command['cert_find'](subject=u'notfound')
+        assert 'count' in res and res['count'] == 0
+
+    def test_0030_search_for_testcerts(self):
+        """
+        Search for certs created in other tests
+        """
+        res = api.Command['cert_find'](subject=u'ipatestcert.%s' % api.env.domain)
+        assert 'count' in res and res['count'] >= 1
+
+    @raises(errors.ValidationError)
+    def test_0031_search_on_invalid_date(self):
+        """
+        Search using invalid date format
+        """
+        res = api.Command['cert_find'](issuedon_from=u'xyz')
-- 
1.8.1

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to