On 07/04/2016 08:57 AM, Fraser Tweedale wrote:
Hi Milan,

Yes, we can :)  Two issues, outlined below.


1)
Running the tests, I get error in
test_create_subca_with_subject_conflict cleanup::

     ____________ ERROR at teardown of 
TestCAbasicCRUD.test_create_subca_with_subject_conflict _____________

         def cleanup():
             created = self.exists
             try:
                 del_command()

     <snip>
     E               NotFound: crud-subca-2: Certificate Authority not found


I do not know testing framework very well but it looks like
track_create() sets 'self.exists = True' before the create command
throws the (expected) DuplicateEntry error.  (These are called from
create() in the tracker 'base' class).  Later, cleanup() catches a
NotFound but re-throws it because it believes the entry should have
existed.


2)
the usercert.conf.tmpl does not like a subject base with spaces in
it, i.e. if 'openssl req' config template gets formatted like:

     [ dn ]
     commonName = "alice"
     o=IPA.LOCAL 201606201330

then 'openssl req' fails with nasty error like:

     140644791924600:error:0D06407A:asn1 encoding 
routines:a2d_ASN1_OBJECT:first num too large:a_object.c:108:
     140644791924600:error:0B083077:x509 certificate 
routines:X509_NAME_ENTRY_create_by_txt:invalid field name:x509name.c:295:name=o

and CalledProcessError gets raised and the test fails.

Simplest solution is to simply remove the '{ipacertbase}' from the
template, because AFAIK it is not needed and parsing and formatting
the certbase (which could have multiple AVAs) is more complex than
the test calls for, IMO.


Thanks,
Fraser
Hi, thanks.

I must have missed the first issue after I removed the expected fail marker. I have fixed it now.

As for the usercert template, this code is older than the issues at hand. I do not remember why exactly I used that
option in the openssl config. I have removed that in a new patch.


--
Milan Kubik

From ac2fa02ef8f4d7290d8b7dbdd12ebf84d85d71f6 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    | 175 ++++++++++++++++++++++++++++++
 ipatests/test_xmlrpc/tracker/ca_plugin.py | 126 +++++++++++++++++++++
 ipatests/test_xmlrpc/xmlrpc_test.py       |   3 +
 4 files changed, 309 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 134a08803f3abca1124c4d26274d9e3fc981b941..1ea020b18f975f717eb9d9d5399d8e9fb40264cb 100644
--- a/ipatests/test_xmlrpc/objectclasses.py
+++ b/ipatests/test_xmlrpc/objectclasses.py
@@ -222,3 +222,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..1e0e52ff748a5dc4faeab6526783ae95237ba73b
--- /dev/null
+++ b/ipatests/test_xmlrpc/test_ca_plugin.py
@@ -0,0 +1,175 @@
+#
+# 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)
+
+    # Should not get created, no need to delete
+    return tracker
+
+
+@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 501c37ce43926d5116813e9910b47c064e71bf4e 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 0c4f8b4b037ffa16d8ed180e19fb82d1a5e58a74 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

From 4a83f768f6b1a4381170c8fd8a48b20b76f5a45a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Milan=20Kub=C3=ADk?= <mku...@redhat.com>
Date: Thu, 7 Jul 2016 15:35:23 +0200
Subject: [PATCH] ipatests: remove ipacertbase option from test CSR
 configuration

The issue was found during test review. If the cert base contains
spaces, openssl req fails.

https://fedorahosted.org/freeipa/ticket/4559
---
 ipatests/test_xmlrpc/data/usercert.conf.tmpl           | 1 -
 ipatests/test_xmlrpc/test_caacl_profile_enforcement.py | 6 ------
 2 files changed, 7 deletions(-)

diff --git a/ipatests/test_xmlrpc/data/usercert.conf.tmpl b/ipatests/test_xmlrpc/data/usercert.conf.tmpl
index 22804be851c2180d14d299b3bb150077d1a6458e..7a736a80e2d7f9a50fca93a8a24a8d04ed23b20e 100644
--- a/ipatests/test_xmlrpc/data/usercert.conf.tmpl
+++ b/ipatests/test_xmlrpc/data/usercert.conf.tmpl
@@ -7,7 +7,6 @@ req_extensions = exts
 
 [ dn ]
 commonName = "{username}"
-{ipacertbase}
 
 [ exts ]
 subjectAltName=email:{username}@{ipadomain}
diff --git a/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py b/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py
index a70d81d885cdf0b4c8bbbdaa7d6f69bfbd84719c..a73e8453947c6b87b23f533f46b8f799cfa3e427 100644
--- a/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py
+++ b/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py
@@ -30,18 +30,12 @@ CERT_OPENSSL_CONFIG_TEMPLATE = os.path.join(BASE_DIR, 'data/usercert.conf.tmpl')
 CERT_RSA_PRIVATE_KEY_PATH = os.path.join(BASE_DIR, 'data/usercert-priv-key.pem')
 
 
-CERT_SUBJECT_BASE = (
-    api.Command.config_show()
-    ['result']['ipacertificatesubjectbase'][0]
-)
-
 SMIME_USER_INIT_PW = u'Change123'
 SMIME_USER_PW = u'Secret123'
 
 
 def generate_user_csr(username, domain=None):
     csr_values = dict(
-        ipacertbase=CERT_SUBJECT_BASE,
         ipadomain=domain if domain else api.env.domain,
         username=username)
 
-- 
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