On 06/24/2016 03:42 AM, Fraser Tweedale wrote:
On Tue, Jun 21, 2016 at 05:01:35PM +0200, Milan Kubík wrote:
Hi Fraser and list,

I have made changes to the test plan on the wiki [1] according to the
information in "[Testplan review] Sub CAs" thread.

I also implemented the tests in the test plan:

patch 0038 - CATracker and CA CRUD test
patch 0039 - extension to CA ACL test
patch 0040 - functional test with ACLs and certificate profile, reusing my
previous S/MIME based tests. This patch also tests for the cert-request
behavior when profile ID or CA cn are ommited.

The tests ATM do not verify the Issuer name in the certificate itself, just
from the ipa entry of the certificate.

The approach you are using::

     assert cert_info['result']['issuer'] == smime_signing_ca.ipasubjectdn

is not quite as you describe (these are virtual attributes, not
attributes of an actual entry); but the approach is valid.
The issue then is in the wording? The other approach I could have used here
is to retrieve the two certificates and compare the fields manually.
Are these virtual attributes created from the certificate itself?

Fraser, could you please verify my reasoning behind the test cases for
cert-request in the patch 40?

The tests look OK.  With the default CA / default profiles, is there
appropriate isolation between test cases to ensure that if, e.g.
some other test case adds/modifies CA ACLs such that these
expected-to-fail tests now pass, that this does not affect the
TestCertSignMIMEwithSubCA test case?

Thanks,
Fraser

The ACL, SMIME CA and S/MIME profile lifetime is constrained by the class scope
enforced by pytest.
The two test cases depend on the fact documented in the designs and that is what
cert-request fallbacks to when CA or profile ID are not provided.
Unless something changes caIPAserviceCert profile or affiliated ACL, then the test cases
are safe.

I will try to think more about corner cases here.
[1]: http://www.freeipa.org/page/V4/Sub-CAs/Test_Plan

Cheers

--
Milan Kubik

Attaching rebased patches and removing the expected fail from one of the tests as ticket 5981 has fix posted.

--
Milan Kubik

From 0458db3f7ff93a35531bec123e59ac71ce46f0b8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Milan=20Kub=C3=ADk?= <mku...@redhat.com>
Date: Fri, 17 Jun 2016 12:20:55 +0200
Subject: [PATCH 1/3] ipatests: Tracker implementation for Sub CA feature

The patch implements Tracker subclass for CA plugin
and the basic CRUD tests for the plugin entries.

https://fedorahosted.org/freeipa/ticket/4559
---
 ipatests/test_xmlrpc/objectclasses.py     |   5 +
 ipatests/test_xmlrpc/test_ca_plugin.py    | 174 ++++++++++++++++++++++++++++++
 ipatests/test_xmlrpc/tracker/ca_plugin.py | 126 ++++++++++++++++++++++
 ipatests/test_xmlrpc/xmlrpc_test.py       |   3 +
 4 files changed, 308 insertions(+)
 create mode 100644 ipatests/test_xmlrpc/test_ca_plugin.py
 create mode 100644 ipatests/test_xmlrpc/tracker/ca_plugin.py

diff --git a/ipatests/test_xmlrpc/objectclasses.py b/ipatests/test_xmlrpc/objectclasses.py
index 7050de289760ede29d057e42658c2f68d8506249..1898714513a3d64112cf2f114ca8f7e6b98ab655 100644
--- a/ipatests/test_xmlrpc/objectclasses.py
+++ b/ipatests/test_xmlrpc/objectclasses.py
@@ -221,3 +221,8 @@ caacl = [
     u'ipaassociation',
     u'ipacaacl'
 ]
+
+ca = [
+    u'top',
+    u'ipaca',
+]
diff --git a/ipatests/test_xmlrpc/test_ca_plugin.py b/ipatests/test_xmlrpc/test_ca_plugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc2f4098d29ce3fee0bef19ccdfc80a8bd872412
--- /dev/null
+++ b/ipatests/test_xmlrpc/test_ca_plugin.py
@@ -0,0 +1,174 @@
+#
+# Copyright (C) 2016  FreeIPA Contributors see COPYING for license
+#
+
+"""
+Test the `ipalib.plugins.ca` module.
+"""
+
+import pytest
+
+from ipalib import errors
+from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test, fuzzy_issuer
+
+from ipatests.test_xmlrpc.tracker.certprofile_plugin import CertprofileTracker
+from ipatests.test_xmlrpc.tracker.caacl_plugin import CAACLTracker
+from ipatests.test_xmlrpc.tracker.ca_plugin import CATracker
+
+
+@pytest.fixture(scope='module')
+def default_profile(request):
+    name = 'caIPAserviceCert'
+    desc = u'Standard profile for network services'
+    tracker = CertprofileTracker(name, store=True, desc=desc)
+    tracker.track_create()
+    return tracker
+
+
+@pytest.fixture(scope='module')
+def default_acl(request):
+    name = u'hosts_services_caIPAserviceCert'
+    tracker = CAACLTracker(name, service_category=u'all', host_category=u'all')
+    tracker.track_create()
+    tracker.attrs.update(
+        {u'ipamembercertprofile_certprofile': [u'caIPAserviceCert']})
+    return tracker
+
+
+@pytest.fixture(scope='module')
+def default_ca(request):
+    name = u'ipa'
+    desc = u'IPA CA'
+    tracker = CATracker(name, fuzzy_issuer, desc=desc)
+    tracker.track_create()
+    return tracker
+
+
+@pytest.fixture(scope='class')
+def crud_subca(request):
+    name = u'crud-subca'
+    subject = u'CN=crud subca test,O=crud testing inc'
+    tracker = CATracker(name, subject)
+
+    return tracker.make_fixture(request)
+
+
+@pytest.fixture(scope='class')
+def subject_conflict_subca(request):
+    name = u'crud-subca-2'
+    subject = u'CN=crud subca test,O=crud testing inc'
+    tracker = CATracker(name, subject)
+
+    return tracker.make_fixture(request)
+
+
+@pytest.mark.tier0
+class TestDefaultCA(XMLRPC_test):
+    def test_default_ca_present(self, default_ca):
+        default_ca.retrieve()
+
+    def test_default_ca_delete(self, default_ca):
+        with pytest.raises(errors.ProtectedEntryError):
+            default_ca.delete()
+
+
+@pytest.mark.tier1
+class TestCAbasicCRUD(XMLRPC_test):
+
+    ATTR_ERROR_MSG = u'attribute is not configurable'
+
+    def test_create(self, crud_subca):
+        crud_subca.create()
+
+    def test_retrieve(self, crud_subca):
+        crud_subca.retrieve()
+
+    def test_retrieve_all(self, crud_subca):
+        crud_subca.retrieve(all=True)
+
+    def test_delete(self, crud_subca):
+        crud_subca.delete()
+
+    def test_find(self, crud_subca):
+        crud_subca.ensure_exists()
+        crud_subca.find()
+
+    def test_modify_description(self, crud_subca):
+        new_desc = u'updated CA description'
+        crud_subca.update(
+            dict(
+                description=new_desc,
+            ),
+            expected_updates=dict(
+                description=[new_desc]
+            )
+        )
+
+    def test_modify_issuerdn(self, crud_subca):
+        bogus_issuer = u'ipacaissuerdn="cn=phony issuer,o=phony industries'
+        cmd = crud_subca.make_update_command(
+            updates=dict(setattr=bogus_issuer)
+        )
+
+        with pytest.raises(errors.ValidationError) as error:
+            cmd()
+
+        assert self.ATTR_ERROR_MSG in str(error.value)
+
+    def test_modify_subjectdn(self, crud_subca):
+        bogus_subject = u'ipacasubjectdn="cn=phony subject,o=phony industries'
+        cmd = crud_subca.make_update_command(
+            updates=dict(setattr=bogus_subject)
+        )
+
+        with pytest.raises(errors.ValidationError) as error:
+            cmd()
+
+        assert self.ATTR_ERROR_MSG in str(error.value)
+
+    def test_delete_subjectdn(self, crud_subca):
+        cmd = crud_subca.make_update_command(
+            updates=dict(delattr=u'ipacasubjectdn=%s'
+                         % crud_subca.ipasubjectdn)
+        )
+
+        with pytest.raises(errors.ValidationError) as error:
+            cmd()
+
+        assert self.ATTR_ERROR_MSG in str(error.value)
+
+    def test_add_bogus_subjectdn(self, crud_subca):
+        bogus_subject = u'ipacasubjectdn="cn=phony subject,o=phony industries'
+        cmd = crud_subca.make_update_command(
+            updates=dict(addattr=bogus_subject)
+        )
+
+        with pytest.raises(errors.ValidationError) as error:
+            cmd()
+
+        assert self.ATTR_ERROR_MSG in str(error.value)
+
+    def test_add_bogus_issuerdn(self, crud_subca):
+        bogus_issuer = u'ipacaissuerdn="cn=phony issuer,o=phony industries'
+        cmd = crud_subca.make_update_command(
+            updates=dict(addattr=bogus_issuer)
+        )
+
+        with pytest.raises(errors.ValidationError) as error:
+            cmd()
+
+        assert self.ATTR_ERROR_MSG in str(error.value)
+
+    def test_create_subca_with_conflicting_name(self, crud_subca):
+        crud_subca.ensure_exists()
+
+        cmd = crud_subca.make_create_command()
+        with pytest.raises(errors.DuplicateEntry):
+            cmd()
+
+    def test_create_subca_with_subject_conflict(
+            self, crud_subca, subject_conflict_subca):
+        crud_subca.ensure_exists()
+
+        with pytest.raises(errors.DuplicateEntry):
+            subject_conflict_subca.create()
diff --git a/ipatests/test_xmlrpc/tracker/ca_plugin.py b/ipatests/test_xmlrpc/tracker/ca_plugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..7586c771c028df9343bcf7fcdd538c19c494ed1a
--- /dev/null
+++ b/ipatests/test_xmlrpc/tracker/ca_plugin.py
@@ -0,0 +1,126 @@
+#
+# Copyright (C) 2016  FreeIPA Contributors see COPYING for license
+#
+from __future__ import absolute_import
+
+import six
+
+from ipapython.dn import DN
+from ipatests.test_xmlrpc.tracker.base import Tracker
+from ipatests.util import assert_deepequal
+from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_issuer, fuzzy_caid
+from ipatests.test_xmlrpc import objectclasses
+
+
+if six.PY3:
+    unicode = str
+
+
+class CATracker(Tracker):
+    """Implementation of a Tracker class for CA plugin."""
+
+    retrieve_keys = {
+        'dn', 'cn', 'ipacaid', 'ipacasubjectdn', 'ipacaissuerdn', 'description'
+    }
+    retrieve_all_keys = {'objectclass'} | retrieve_keys
+    create_keys = retrieve_all_keys
+    update_keys = retrieve_keys - {'dn'}
+
+    def __init__(self, name, subject, desc=u"Test generated CA",
+                 default_version=None):
+        super(CATracker, self).__init__(default_version=default_version)
+        self.attrs = {}
+        self.ipasubjectdn = subject
+        self.description = desc
+
+        self.dn = DN(('cn', name),
+                     self.api.env.container_ca,
+                     self.api.env.basedn)
+
+    def make_create_command(self, force=True):
+        """Make function that creates the plugin entry object."""
+        return self.make_command(
+            'ca_add', self.name, ipacasubjectdn=self.ipasubjectdn,
+            description=self.description
+        )
+
+    def check_create(self, result):
+        assert_deepequal(dict(
+            value=self.name,
+            summary=u'Created CA "{}"'.format(self.name),
+            result=dict(self.filter_attrs(self.create_keys))
+        ), result)
+
+    def track_create(self):
+        self.attrs = dict(
+            dn=unicode(self.dn),
+            cn=[self.name],
+            description=[self.description],
+            ipacasubjectdn=[self.ipasubjectdn],
+            ipacaissuerdn=[fuzzy_issuer],
+            ipacaid=[fuzzy_caid],
+            objectclass=objectclasses.ca
+        )
+        self.exists = True
+
+    def make_delete_command(self):
+        """Make function that deletes the plugin entry object."""
+        return self.make_command('ca_del', self.name)
+
+    def check_delete(self, result):
+        assert_deepequal(dict(
+            value=[self.name],
+            summary=u'Deleted CA "{}"'.format(self.name),
+            result=dict(failed=[])
+        ), result)
+
+    def make_retrieve_command(self, all=False, raw=False):
+        """Make function that retrieves the entry using ${CMD}_show"""
+        return self.make_command('ca_show', self.name, all=all, raw=raw)
+
+    def check_retrieve(self, result, all=False, raw=False):
+        """Check the plugin's `show` command result"""
+        if all:
+            expected = self.filter_attrs(self.retrieve_all_keys)
+        else:
+            expected = self.filter_attrs(self.retrieve_keys)
+
+        assert_deepequal(dict(
+            value=self.name,
+            summary=None,
+            result=expected
+        ), result)
+
+    def make_find_command(self, *args, **kwargs):
+        """Make function that finds the entry using ${CMD}_find
+
+        Note that the name (or other search terms) needs to be specified
+        in arguments.
+        """
+        return self.make_command('ca_find', *args, **kwargs)
+
+    def check_find(self, result, all=False, raw=False):
+        """Check the plugin's `find` command result"""
+        if all:
+            expected = self.filter_attrs(self.retrieve_all_keys)
+        else:
+            expected = self.filter_attrs(self.retrieve_keys)
+
+        assert_deepequal(dict(
+            count=1,
+            truncated=False,
+            summary=u'1 CA matched',
+            result=[expected]
+        ), result)
+
+    def make_update_command(self, updates):
+        """Make function that modifies the entry using ${CMD}_mod"""
+        return self.make_command('ca_mod', self.name, **updates)
+
+    def check_update(self, result, extra_keys=()):
+        """Check the plugin's `find` command result"""
+        assert_deepequal(dict(
+            value=self.name,
+            summary=u'Modified CA "{}"'.format(self.name),
+            result=self.filter_attrs(self.update_keys | set(extra_keys))
+        ), result)
diff --git a/ipatests/test_xmlrpc/xmlrpc_test.py b/ipatests/test_xmlrpc/xmlrpc_test.py
index c3bba9abf1079469126d4423425a32c74f886895..ddfe9c17c5ca110bc5eb66dead618383b861eda9 100644
--- a/ipatests/test_xmlrpc/xmlrpc_test.py
+++ b/ipatests/test_xmlrpc/xmlrpc_test.py
@@ -81,6 +81,9 @@ fuzzy_caacldn = Fuzzy(
     '(?i)ipauniqueid=%s,cn=caacls,cn=ca,%s' % (uuid_re, api.env.basedn)
 )
 
+# Matches internal CA ID
+fuzzy_caid = fuzzy_uuid
+
 # Matches fuzzy ipaUniqueID DN group (RDN)
 fuzzy_ipauniqueid = Fuzzy('(?i)ipauniqueid=%s' % uuid_re)
 
-- 
2.9.0

From 3d5c457d48ce54803080dd285e283f16715616e3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Milan=20Kub=C3=ADk?= <mku...@redhat.com>
Date: Tue, 21 Jun 2016 13:45:54 +0200
Subject: [PATCH 2/3] ipatests: Extend CAACL suite to cover Sub CA members

https://fedorahosted.org/freeipa/ticket/4559
---
 ipatests/test_xmlrpc/test_caacl_plugin.py    | 26 ++++++++++++++++++++++++--
 ipatests/test_xmlrpc/tracker/caacl_plugin.py | 26 +++++++++++++++++++++-----
 2 files changed, 45 insertions(+), 7 deletions(-)

diff --git a/ipatests/test_xmlrpc/test_caacl_plugin.py b/ipatests/test_xmlrpc/test_caacl_plugin.py
index f20b02b295024313008be7b75bdcae2ade70b4ff..dce12e484e4fa3a57fcd54fd9ffb60419fd161a4 100644
--- a/ipatests/test_xmlrpc/test_caacl_plugin.py
+++ b/ipatests/test_xmlrpc/test_caacl_plugin.py
@@ -14,6 +14,7 @@ from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
 from ipatests.test_xmlrpc.tracker.certprofile_plugin import CertprofileTracker
 from ipatests.test_xmlrpc.tracker.caacl_plugin import CAACLTracker
 from ipatests.test_xmlrpc.tracker.stageuser_plugin import StageUserTracker
+from ipatests.test_xmlrpc.tracker.ca_plugin import CATracker
 
 
 @pytest.fixture(scope='class')
@@ -48,12 +49,19 @@ def category_acl(request):
     name = u'category_acl'
     tracker = CAACLTracker(name, ipacertprofile_category=u'all',
                            user_category=u'all', service_category=u'all',
-                           host_category=u'all')
+                           host_category=u'all', ipaca_category=u'all')
 
     return tracker.make_fixture(request)
 
 
 @pytest.fixture(scope='class')
+def caacl_test_ca(request):
+    name = u'caacl-test-ca'
+    subject = u'CN=caacl test subca,O=test industries inc.'
+    return CATracker(name, subject).make_fixture(request)
+
+
+@pytest.fixture(scope='class')
 def staged_user(request):
     name = u'st-user'
     tracker = StageUserTracker(name, u'stage', u'test')
@@ -109,7 +117,8 @@ class TestCAACLMembers(XMLRPC_test):
             hostcategory=None,
             servicecategory=None,
             ipacertprofilecategory=None,
-            usercategory=None)
+            usercategory=None,
+            ipacacategory=None)
         category_acl.update(updates)
 
     def test_add_profile(self, category_acl, default_profile):
@@ -120,6 +129,15 @@ class TestCAACLMembers(XMLRPC_test):
         category_acl.remove_profile(certprofile=default_profile.name)
         category_acl.retrieve()
 
+    def test_add_ca(self, category_acl, caacl_test_ca):
+        caacl_test_ca.ensure_exists()
+        category_acl.add_ca(ca=caacl_test_ca.name)
+        category_acl.retrieve()
+
+    def test_remove_ca(self, category_acl, caacl_test_ca):
+        category_acl.remove_ca(ca=caacl_test_ca.name)
+        category_acl.retrieve()
+
     def test_add_invalid_value_service(self, category_acl, default_profile):
         res = category_acl.add_service(service=default_profile.name, track=False)
         assert len(res['failed']) == 1
@@ -144,6 +162,10 @@ class TestCAACLMembers(XMLRPC_test):
         res = category_acl.add_profile(certprofile=category_acl.name, track=False)
         assert len(res['failed']) == 1
 
+    def test_add_invalid_value_ca(self, category_acl):
+        res = category_acl.add_ca(ca=category_acl.name, track=False)
+        assert len(res['failed']) == 1
+
     def test_add_staged_user_to_acl(self, category_acl, staged_user):
         res = category_acl.add_user(user=staged_user.name, track=False)
         assert len(res['failed']) == 1
diff --git a/ipatests/test_xmlrpc/tracker/caacl_plugin.py b/ipatests/test_xmlrpc/tracker/caacl_plugin.py
index afe7ee0c071b6346d9a21337d8e486ff8b2891cc..79c892d27e88b3b263915cc0bf843a1235b4836e 100644
--- a/ipatests/test_xmlrpc/tracker/caacl_plugin.py
+++ b/ipatests/test_xmlrpc/tracker/caacl_plugin.py
@@ -35,10 +35,11 @@ class CAACLTracker(Tracker):
         u'memberuser_user', u'memberuser_group',
         u'memberhost_host', u'memberhost_hostgroup',
         u'memberservice_service',
-        u'ipamembercertprofile_certprofile'}
+        u'ipamembercertprofile_certprofile',
+        u'ipamemberca_ca'}
     category_keys = {
         u'ipacacategory', u'ipacertprofilecategory', u'usercategory',
-        u'hostcategory', u'servicecategory'}
+        u'hostcategory', u'servicecategory', u'ipacacategory'}
     retrieve_keys = {
         u'dn', u'cn', u'description', u'ipaenabledflag',
         u'ipamemberca', u'ipamembercertprofile', u'memberuser',
@@ -51,14 +52,15 @@ class CAACLTracker(Tracker):
     update_keys = create_keys - {u'dn'}
 
     def __init__(self, name, ipacertprofile_category=None, user_category=None,
-                 service_category=None, host_category=None, description=None,
-                 default_version=None):
+                 service_category=None, host_category=None,
+                 ipaca_category=None, description=None, default_version=None):
         super(CAACLTracker, self).__init__(default_version=default_version)
 
         self._name = name
         self.description = description
         self._categories = dict(
             ipacertprofilecategory=ipacertprofile_category,
+            ipacacategory=ipaca_category,
             usercategory=user_category,
             servicecategory=service_category,
             hostcategory=host_category)
@@ -200,7 +202,7 @@ class CAACLTracker(Tracker):
     # implemented in standalone test
     #
     # The methods implemented here will be:
-    # caacl_{add,remove}_{host, service, certprofile, user [, subca]}
+    # caacl_{add,remove}_{host, service, certprofile, user, ca}
 
     def _add_acl_component(self, command_name, keys, track):
         """ Add a resource into ACL rule and track it.
@@ -356,6 +358,20 @@ class CAACLTracker(Tracker):
 
         return self._remove_acl_component(u'caacl_remove_profile', options, track)
 
+    def add_ca(self, ca=None, track=True):
+        options = {
+            u'ipamemberca_ca':
+                {u'ca': ca}}
+
+        return self._add_acl_component(u'caacl_add_ca', options, track)
+
+    def remove_ca(self, ca=None, track=True):
+        options = {
+            u'ipamemberca_ca':
+                {u'ca': ca}}
+
+        return self._remove_acl_component(u'caacl_remove_ca', options, track)
+
     def enable(self):
         command = self.make_command(u'caacl_enable', self.name)
         self.attrs.update({u'ipaenabledflag': [u'TRUE']})
-- 
2.9.0

From 1cd2ddf454b699e45c9191456aeaf85373c1b344 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Milan=20Kub=C3=ADk?= <mku...@redhat.com>
Date: Tue, 21 Jun 2016 15:57:58 +0200
Subject: [PATCH 3/3] ipatests: Test Sub CA with CAACL and certificate profile

Test the Sub CA feature by signing a CSR with custom
certificate profile.

The test also covers 'cert-request' fallback behaviour
for missing 'cacn' and 'profile-id' options by reusing
the fixtures from the module.

https://fedorahosted.org/freeipa/ticket/4559
---
 .../test_xmlrpc/test_caacl_profile_enforcement.py  | 110 +++++++++++++++++++++
 1 file changed, 110 insertions(+)

diff --git a/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py b/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py
index 11c040966003e2ea86149359c8aacb953e9fdd37..a70d81d885cdf0b4c8bbbdaa7d6f69bfbd84719c 100644
--- a/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py
+++ b/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py
@@ -15,6 +15,7 @@ from ipatests.util import (
 from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
 from ipatests.test_xmlrpc.tracker.certprofile_plugin import CertprofileTracker
 from ipatests.test_xmlrpc.tracker.caacl_plugin import CAACLTracker
+from ipatests.test_xmlrpc.tracker.ca_plugin import CATracker
 
 from ipapython.ipautil import run
 
@@ -250,3 +251,112 @@ class TestSignWithChangedProfile(XMLRPC_test):
             with pytest.raises(errors.CertificateOperationError):
                 api.Command.cert_request(csr, principal=smime_user,
                                          profile_id=smime_profile.name)
+
+
+@pytest.fixture(scope='class')
+def smime_signing_ca(request):
+    name = u'smime-signing-ca'
+    subject = u'CN=SMIME CA,O=test industries Inc.'
+    return CATracker(name, subject).make_fixture(request)
+
+
+@pytest.mark.tier1
+class TestCertSignMIMEwithSubCA(XMLRPC_test):
+    """ Test Certificate Signing with Sub CA
+
+    The test covers following areas:
+
+     * signing a CSR with custom certificate profile
+       using a designated Sub CA
+     * Verify that the Issuer of the signed certificate
+       is the reqested CA
+     * Verify that when not set, cert-request uses the default CA.
+       This it verified by violating an ACL
+     * Verify that when not set, cert-request uses the default
+       certificate profile.
+
+    The latter two test cases are implemented in this module
+    as not to replicate the fixtures to cert plugin test module.
+    """
+
+    def test_cert_import(self, smime_profile):
+        smime_profile.ensure_exists()
+
+    def test_create_acl(self, smime_acl):
+        smime_acl.ensure_exists()
+
+    def test_create_subca(self, smime_signing_ca):
+        smime_signing_ca.ensure_exists()
+
+    def test_add_profile_to_acl(self, smime_acl, smime_profile):
+        smime_acl.add_profile(certprofile=smime_profile.name)
+
+    def test_add_subca_to_acl(self, smime_acl, smime_signing_ca):
+        smime_acl.add_ca(smime_signing_ca.name)
+
+    # rewrite to trackers, prepare elsewhere
+    def test_add_user_to_group(self, smime_group, smime_user):
+        api.Command.group_add_member(smime_group, user=smime_user)
+
+    def test_add_group_to_acl(self, smime_group, smime_acl):
+        smime_acl.add_user(group=smime_group)
+
+    def test_sign_smime_csr(self, smime_profile, smime_user, smime_signing_ca):
+        csr = generate_user_csr(smime_user)
+        with change_principal(smime_user, SMIME_USER_PW):
+            api.Command.cert_request(csr, principal=smime_user,
+                                     profile_id=smime_profile.name,
+                                     cacn=smime_signing_ca.name)
+
+    def test_sign_smime_csr_full_principal(
+            self, smime_profile, smime_user, smime_signing_ca):
+        csr = generate_user_csr(smime_user)
+        smime_user_principal = '@'.join((smime_user, api.env.realm))
+        with change_principal(smime_user, SMIME_USER_PW):
+            api.Command.cert_request(csr, principal=smime_user_principal,
+                                     profile_id=smime_profile.name,
+                                     cacn=smime_signing_ca.name)
+
+    def test_verify_cert_issuer_dn_is_subca(
+            self, smime_profile, smime_user, smime_signing_ca):
+        csr = generate_user_csr(smime_user)
+        smime_user_principal = '@'.join((smime_user, api.env.realm))
+        with change_principal(smime_user, SMIME_USER_PW):
+            cert_info = api.Command.cert_request(
+                csr, principal=smime_user_principal,
+                profile_id=smime_profile.name, cacn=smime_signing_ca.name)
+
+        assert cert_info['result']['issuer'] == smime_signing_ca.ipasubjectdn
+
+    def test_sign_smime_csr_fallback_to_default_CA(
+            self, smime_profile, smime_user, smime_signing_ca):
+        """ Attempt to sign a CSR without CA specified.
+
+        The request will satisfy SMIME_ACL via the profile ID,
+        however not specifying the CA will fallback to the IPA CA
+        for which SMIME profile isn't enabled, thus violating ACL.
+        """
+        csr = generate_user_csr(smime_user)
+        smime_user_principal = '@'.join((smime_user, api.env.realm))
+
+        with pytest.raises(errors.ACIError):
+            with change_principal(smime_user, SMIME_USER_PW):
+                api.Command.cert_request(csr, principal=smime_user_principal,
+                                         profile_id=smime_profile.name)
+
+    def test_sign_smime_csr_fallback_to_default_cert_profile(
+            self, smime_profile, smime_user, smime_signing_ca):
+        """ Attempt to sign a CSR without certificate profile specified.
+
+        Similar to previous test case.
+        By specifying only the CA to use, profile will fallback to
+        the default caIPAserviceCert profile which is not enabled
+        via ACL to be used with the CA, thus failing the request.
+        """
+        csr = generate_user_csr(smime_user)
+        smime_user_principal = '@'.join((smime_user, api.env.realm))
+
+        with pytest.raises(errors.ACIError):
+            with change_principal(smime_user, SMIME_USER_PW):
+                api.Command.cert_request(csr, principal=smime_user_principal,
+                                         cacn=smime_signing_ca.name)
-- 
2.9.0

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to