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.

> 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

> [1]: http://www.freeipa.org/page/V4/Sub-CAs/Test_Plan
> 
> Cheers
> 
> -- 
> Milan Kubik
> 

> From 83153e3c7c4a28ba170633054b59b2d6a70807c0 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    | 176 
> ++++++++++++++++++++++++++++++
>  ipatests/test_xmlrpc/tracker/ca_plugin.py | 126 +++++++++++++++++++++
>  ipatests/test_xmlrpc/xmlrpc_test.py       |   3 +
>  4 files changed, 310 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..a638a0c9a5a611687d4ae707f4b067bef2ef2229
> --- /dev/null
> +++ b/ipatests/test_xmlrpc/test_ca_plugin.py
> @@ -0,0 +1,176 @@
> +#
> +# 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()
> +
> +    @pytest.mark.xfail(reason='https://fedorahosted.org/freeipa/ticket/5981',
> +                       strict=True)
> +    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 e81e5074c439071019fb12627ff8f0945ba2f9d1 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 b3e43108f1822fd83c602a7eecec933889531d1b 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