areguera / rpms / ipa

Forked from rpms/ipa 5 years ago
Clone

Blame SOURCES/0006-external-ca-profile-fix_rhbz#1731813.patch

e8574e
From d0d29ccc324bb9f95bffbe3162ee5c3c61c6086a Mon Sep 17 00:00:00 2001
e8574e
From: Fraser Tweedale <ftweedal@redhat.com>
e8574e
Date: Thu, 11 Jul 2019 15:17:04 +1000
e8574e
Subject: [PATCH] move MSCSTemplate classes to ipalib
e8574e
e8574e
As we expand the integration tests for external CA functionality, it
e8574e
is helpful (and avoids duplication) to use the MSCSTemplate*
e8574e
classes.  These currently live in ipaserver.install.cainstance, but
e8574e
ipatests is no longer permitted to import from ipaserver (see commit
e8574e
81714976e5e13131654c78eb734746a20237c933).  So move these classes to
e8574e
ipalib.
e8574e
e8574e
Part of: https://pagure.io/freeipa/issue/7548
e8574e
e8574e
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
e8574e
---
e8574e
 install/tools/ipa-ca-install.in               |   6 +-
e8574e
 ipalib/x509.py                                | 171 +++++++++++++++++
e8574e
 ipaserver/install/ca.py                       |  11 +-
e8574e
 ipaserver/install/cainstance.py               | 180 +-----------------
e8574e
 ipaserver/install/ipa_cacert_manage.py        |  14 +-
e8574e
 ipatests/test_integration/test_external_ca.py |  11 +-
e8574e
 ipatests/test_ipalib/test_x509.py             | 115 +++++++++++
e8574e
 .../test_install/test_cainstance.py           | 123 ------------
e8574e
 8 files changed, 307 insertions(+), 324 deletions(-)
e8574e
 delete mode 100644 ipatests/test_ipaserver/test_install/test_cainstance.py
e8574e
e8574e
diff --git a/install/tools/ipa-ca-install.in b/install/tools/ipa-ca-install.in
e8574e
index 0700c0c38b..ce6d5fcb52 100644
e8574e
--- a/install/tools/ipa-ca-install.in
e8574e
+++ b/install/tools/ipa-ca-install.in
e8574e
@@ -37,7 +37,7 @@ from ipaserver.install import cainstance, service
e8574e
 from ipaserver.install import custodiainstance
e8574e
 from ipaserver.masters import find_providing_server
e8574e
 from ipapython import version
e8574e
-from ipalib import api
e8574e
+from ipalib import api, x509
e8574e
 from ipalib.constants import DOMAIN_LEVEL_1
e8574e
 from ipapython.config import IPAOptionParser
e8574e
 from ipapython.ipa_log_manager import standard_logging_setup
e8574e
@@ -68,13 +68,13 @@ def parse_options():
e8574e
                       default=False, help="unattended installation never prompts the user")
e8574e
     parser.add_option("--external-ca", dest="external_ca", action="store_true",
e8574e
                       default=False, help="Generate a CSR to be signed by an external CA")
e8574e
-    ext_cas = tuple(x.value for x in cainstance.ExternalCAType)
e8574e
+    ext_cas = tuple(x.value for x in x509.ExternalCAType)
e8574e
     parser.add_option("--external-ca-type", dest="external_ca_type",
e8574e
                       type="choice", choices=ext_cas,
e8574e
                       metavar="{{{0}}}".format(",".join(ext_cas)),
e8574e
                       help="Type of the external CA. Default: generic")
e8574e
     parser.add_option("--external-ca-profile", dest="external_ca_profile",
e8574e
-                      type='constructor', constructor=cainstance.ExternalCAProfile,
e8574e
+                      type='constructor', constructor=x509.ExternalCAProfile,
e8574e
                       default=None, metavar="PROFILE-SPEC",
e8574e
                       help="Specify the certificate profile/template to use "
e8574e
                            "at the external CA")
e8574e
diff --git a/ipalib/x509.py b/ipalib/x509.py
e8574e
index ab3c5f553d..1f612a3797 100644
e8574e
--- a/ipalib/x509.py
e8574e
+++ b/ipalib/x509.py
e8574e
@@ -34,6 +34,7 @@
e8574e
 import os
e8574e
 import binascii
e8574e
 import datetime
e8574e
+import enum
e8574e
 import ipaddress
e8574e
 import ssl
e8574e
 import base64
e8574e
@@ -47,6 +48,7 @@
e8574e
     Encoding, PublicFormat, PrivateFormat, load_pem_private_key
e8574e
 )
e8574e
 import pyasn1
e8574e
+import pyasn1.error
e8574e
 from pyasn1.type import univ, char, namedtype, tag
e8574e
 from pyasn1.codec.der import decoder, encoder
e8574e
 from pyasn1_modules import rfc2315, rfc2459
e8574e
@@ -745,3 +747,172 @@ def format_datetime(t):
e8574e
     if t.tzinfo is None:
e8574e
         t = t.replace(tzinfo=UTC())
e8574e
     return unicode(t.strftime("%a %b %d %H:%M:%S %Y %Z"))
e8574e
+
e8574e
+
e8574e
+class ExternalCAType(enum.Enum):
e8574e
+    GENERIC = 'generic'
e8574e
+    MS_CS = 'ms-cs'
e8574e
+
e8574e
+
e8574e
+class ExternalCAProfile:
e8574e
+    """
e8574e
+    An external CA profile configuration.  Currently the only
e8574e
+    subclasses are for Microsoft CAs, for providing data in the
e8574e
+    "Certificate Template" extension.
e8574e
+
e8574e
+    Constructing this class will actually return an instance of a
e8574e
+    subclass.
e8574e
+
e8574e
+    Subclasses MUST set ``valid_for``.
e8574e
+
e8574e
+    """
e8574e
+    def __init__(self, s=None):
e8574e
+        self.unparsed_input = s
e8574e
+
e8574e
+    # Which external CA types is the data valid for?
e8574e
+    # A set of VALUES of the ExternalCAType enum.
e8574e
+    valid_for = set()
e8574e
+
e8574e
+    def __new__(cls, s=None):
e8574e
+        """Construct the ExternalCAProfile value.
e8574e
+
e8574e
+        Return an instance of a subclass determined by
e8574e
+        the format of the argument.
e8574e
+
e8574e
+        """
e8574e
+        # we are directly constructing a subclass; instantiate
e8574e
+        # it and be done
e8574e
+        if cls is not ExternalCAProfile:
e8574e
+            return super(ExternalCAProfile, cls).__new__(cls)
e8574e
+
e8574e
+        # construction via the base class; therefore the string
e8574e
+        # argument is required, and is used to determine which
e8574e
+        # subclass to construct
e8574e
+        if s is None:
e8574e
+            raise ValueError('string argument is required')
e8574e
+
e8574e
+        parts = s.split(':')
e8574e
+
e8574e
+        try:
e8574e
+            # Is the first part on OID?
e8574e
+            _oid = univ.ObjectIdentifier(parts[0])
e8574e
+
e8574e
+            # It is; construct a V2 template
e8574e
+            # pylint: disable=too-many-function-args
e8574e
+            return MSCSTemplateV2.__new__(MSCSTemplateV2, s)
e8574e
+
e8574e
+        except pyasn1.error.PyAsn1Error:
e8574e
+            # It is not an OID; treat as a template name
e8574e
+            # pylint: disable=too-many-function-args
e8574e
+            return MSCSTemplateV1.__new__(MSCSTemplateV1, s)
e8574e
+
e8574e
+    def __getstate__(self):
e8574e
+        return self.unparsed_input
e8574e
+
e8574e
+    def __setstate__(self, state):
e8574e
+        # explicitly call __init__ method to initialise object
e8574e
+        self.__init__(state)
e8574e
+
e8574e
+
e8574e
+class MSCSTemplate(ExternalCAProfile):
e8574e
+    """
e8574e
+    An Microsoft AD-CS Template specifier.
e8574e
+
e8574e
+    Subclasses MUST set ext_oid.
e8574e
+
e8574e
+    Subclass constructors MUST set asn1obj.
e8574e
+
e8574e
+    """
e8574e
+    valid_for = set([ExternalCAType.MS_CS.value])
e8574e
+
e8574e
+    ext_oid = None  # extension OID, as a Python str
e8574e
+    asn1obj = None  # unencoded extension data
e8574e
+
e8574e
+    def get_ext_data(self):
e8574e
+        """Return DER-encoded extension data."""
e8574e
+        return encoder.encode(self.asn1obj)
e8574e
+
e8574e
+
e8574e
+class MSCSTemplateV1(MSCSTemplate):
e8574e
+    """
e8574e
+    A v1 template specifier, per
e8574e
+    https://msdn.microsoft.com/en-us/library/cc250011.aspx.
e8574e
+
e8574e
+    ::
e8574e
+
e8574e
+        CertificateTemplateName ::= SEQUENCE {
e8574e
+           Name            UTF8String
e8574e
+        }
e8574e
+
e8574e
+    But note that a bare BMPString is used in practice.
e8574e
+
e8574e
+    """
e8574e
+    ext_oid = "1.3.6.1.4.1.311.20.2"
e8574e
+
e8574e
+    def __init__(self, s):
e8574e
+        super(MSCSTemplateV1, self).__init__(s)
e8574e
+        parts = s.split(':')
e8574e
+        if len(parts) > 1:
e8574e
+            raise ValueError(
e8574e
+                "Cannot specify certificate template version when using name.")
e8574e
+        self.asn1obj = char.BMPString(str(parts[0]))
e8574e
+
e8574e
+
e8574e
+class MSCSTemplateV2(MSCSTemplate):
e8574e
+    """
e8574e
+    A v2 template specifier, per
e8574e
+    https://msdn.microsoft.com/en-us/library/windows/desktop/aa378274(v=vs.85).aspx
e8574e
+
e8574e
+    ::
e8574e
+
e8574e
+        CertificateTemplate ::= SEQUENCE {
e8574e
+            templateID              EncodedObjectID,
e8574e
+            templateMajorVersion    TemplateVersion,
e8574e
+            templateMinorVersion    TemplateVersion OPTIONAL
e8574e
+        }
e8574e
+
e8574e
+        TemplateVersion ::= INTEGER (0..4294967295)
e8574e
+
e8574e
+    """
e8574e
+    ext_oid = "1.3.6.1.4.1.311.21.7"
e8574e
+
e8574e
+    @staticmethod
e8574e
+    def check_version_in_range(desc, n):
e8574e
+        if n < 0 or n >= 2**32:
e8574e
+            raise ValueError(
e8574e
+                "Template {} version must be in range 0..4294967295"
e8574e
+                .format(desc))
e8574e
+
e8574e
+    def __init__(self, s):
e8574e
+        super(MSCSTemplateV2, self).__init__(s)
e8574e
+
e8574e
+        parts = s.split(':')
e8574e
+
e8574e
+        obj = CertificateTemplateV2()
e8574e
+        if len(parts) < 2 or len(parts) > 3:
e8574e
+            raise ValueError(
e8574e
+                "Incorrect template specification; required format is: "
e8574e
+                "<oid>:<majorVersion>[:<minorVersion>]")
e8574e
+        try:
e8574e
+            obj['templateID'] = univ.ObjectIdentifier(parts[0])
e8574e
+
e8574e
+            major = int(parts[1])
e8574e
+            self.check_version_in_range("major", major)
e8574e
+            obj['templateMajorVersion'] = major
e8574e
+
e8574e
+            if len(parts) > 2:
e8574e
+                minor = int(parts[2])
e8574e
+                self.check_version_in_range("minor", minor)
e8574e
+                obj['templateMinorVersion'] = int(parts[2])
e8574e
+
e8574e
+        except pyasn1.error.PyAsn1Error:
e8574e
+            raise ValueError("Could not parse certificate template specifier.")
e8574e
+        self.asn1obj = obj
e8574e
+
e8574e
+
e8574e
+class CertificateTemplateV2(univ.Sequence):
e8574e
+    componentType = namedtype.NamedTypes(
e8574e
+        namedtype.NamedType('templateID', univ.ObjectIdentifier()),
e8574e
+        namedtype.NamedType('templateMajorVersion', univ.Integer()),
e8574e
+        namedtype.OptionalNamedType('templateMinorVersion', univ.Integer())
e8574e
+    )
e8574e
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
e8574e
index 6b040b311a..8fb5e3ec91 100644
e8574e
--- a/ipaserver/install/ca.py
e8574e
+++ b/ipaserver/install/ca.py
e8574e
@@ -28,7 +28,7 @@
e8574e
 from ipaplatform.paths import paths
e8574e
 from ipaserver.install import installutils, certs
e8574e
 from ipaserver.install.replication import replica_conn_check
e8574e
-from ipalib import api, errors
e8574e
+from ipalib import api, errors, x509
e8574e
 from ipapython.dn import DN
e8574e
 
e8574e
 from . import conncheck, dogtag, cainstance
e8574e
@@ -216,8 +216,7 @@ def install_check(standalone, replica_config, options):
e8574e
                 paths.ROOT_IPA_CSR)
e8574e
 
e8574e
         if not options.external_ca_type:
e8574e
-            options.external_ca_type = \
e8574e
-                cainstance.ExternalCAType.GENERIC.value
e8574e
+            options.external_ca_type = x509.ExternalCAType.GENERIC.value
e8574e
 
e8574e
         if options.external_ca_profile is not None:
e8574e
             # check that profile is valid for the external ca type
e8574e
@@ -478,13 +477,11 @@ class CAInstallInterface(dogtag.DogtagInstallInterface,
e8574e
     external_ca = master_install_only(external_ca)
e8574e
 
e8574e
     external_ca_type = knob(
e8574e
-        cainstance.ExternalCAType, None,
e8574e
-        description="Type of the external CA",
e8574e
-    )
e8574e
+        x509.ExternalCAType, None, description="Type of the external CA")
e8574e
     external_ca_type = master_install_only(external_ca_type)
e8574e
 
e8574e
     external_ca_profile = knob(
e8574e
-        type=cainstance.ExternalCAProfile,
e8574e
+        type=x509.ExternalCAProfile,
e8574e
         default=None,
e8574e
         description=(
e8574e
             "Specify the certificate profile/template to use at the "
e8574e
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
e8574e
index 6e1fc724db..2295581870 100644
e8574e
--- a/ipaserver/install/cainstance.py
e8574e
+++ b/ipaserver/install/cainstance.py
e8574e
@@ -26,7 +26,6 @@
e8574e
 import logging
e8574e
 
e8574e
 import dbus
e8574e
-import enum
e8574e
 import ldap
e8574e
 import os
e8574e
 import pwd
e8574e
@@ -39,10 +38,6 @@
e8574e
 import tempfile
e8574e
 from configparser import RawConfigParser
e8574e
 
e8574e
-from pyasn1.codec.der import encoder
e8574e
-from pyasn1.type import char, univ, namedtype
e8574e
-import pyasn1.error
e8574e
-
e8574e
 from ipalib import api
e8574e
 from ipalib import x509
e8574e
 from ipalib import errors
e8574e
@@ -80,11 +75,6 @@
e8574e
 ]
e8574e
 
e8574e
 
e8574e
-class ExternalCAType(enum.Enum):
e8574e
-    GENERIC = 'generic'
e8574e
-    MS_CS = 'ms-cs'
e8574e
-
e8574e
-
e8574e
 def check_ports():
e8574e
     """Check that dogtag ports (8080, 8443) are available.
e8574e
 
e8574e
@@ -367,7 +357,7 @@ def configure_instance(self, host_name, dm_password, admin_password,
e8574e
         if ca_type is not None:
e8574e
             self.ca_type = ca_type
e8574e
         else:
e8574e
-            self.ca_type = ExternalCAType.GENERIC.value
e8574e
+            self.ca_type = x509.ExternalCAType.GENERIC.value
e8574e
         self.external_ca_profile = external_ca_profile
e8574e
 
e8574e
         self.no_db_setup = promote
e8574e
@@ -537,12 +527,12 @@ def __spawn_instance(self):
e8574e
                 pki_ca_signing_csr_path=self.csr_file,
e8574e
             )
e8574e
 
e8574e
-            if self.ca_type == ExternalCAType.MS_CS.value:
e8574e
+            if self.ca_type == x509.ExternalCAType.MS_CS.value:
e8574e
                 # Include MS template name extension in the CSR
e8574e
                 template = self.external_ca_profile
e8574e
                 if template is None:
e8574e
                     # default template name
e8574e
-                    template = MSCSTemplateV1(u"SubCA")
e8574e
+                    template = x509.MSCSTemplateV1(u"SubCA")
e8574e
 
e8574e
                 ext_data = binascii.hexlify(template.get_ext_data())
e8574e
                 cfg.update(
e8574e
@@ -2081,170 +2071,6 @@ def update_ipa_conf():
e8574e
         parser.write(f)
e8574e
 
e8574e
 
e8574e
-class ExternalCAProfile:
e8574e
-    """
e8574e
-    An external CA profile configuration.  Currently the only
e8574e
-    subclasses are for Microsoft CAs, for providing data in the
e8574e
-    "Certificate Template" extension.
e8574e
-
e8574e
-    Constructing this class will actually return an instance of a
e8574e
-    subclass.
e8574e
-
e8574e
-    Subclasses MUST set ``valid_for``.
e8574e
-
e8574e
-    """
e8574e
-    def __init__(self, s=None):
e8574e
-        self.unparsed_input = s
e8574e
-
e8574e
-    # Which external CA types is the data valid for?
e8574e
-    # A set of VALUES of the ExternalCAType enum.
e8574e
-    valid_for = set()
e8574e
-
e8574e
-    def __new__(cls, s=None):
e8574e
-        """Construct the ExternalCAProfile value.
e8574e
-
e8574e
-        Return an instance of a subclass determined by
e8574e
-        the format of the argument.
e8574e
-
e8574e
-        """
e8574e
-        # we are directly constructing a subclass; instantiate
e8574e
-        # it and be done
e8574e
-        if cls is not ExternalCAProfile:
e8574e
-            return super(ExternalCAProfile, cls).__new__(cls)
e8574e
-
e8574e
-        # construction via the base class; therefore the string
e8574e
-        # argument is required, and is used to determine which
e8574e
-        # subclass to construct
e8574e
-        if s is None:
e8574e
-            raise ValueError('string argument is required')
e8574e
-
e8574e
-        parts = s.split(':')
e8574e
-
e8574e
-        try:
e8574e
-            # Is the first part on OID?
e8574e
-            _oid = univ.ObjectIdentifier(parts[0])
e8574e
-
e8574e
-            # It is; construct a V2 template
e8574e
-            # pylint: disable=too-many-function-args
e8574e
-            return MSCSTemplateV2.__new__(MSCSTemplateV2, s)
e8574e
-
e8574e
-        except pyasn1.error.PyAsn1Error:
e8574e
-            # It is not an OID; treat as a template name
e8574e
-            # pylint: disable=too-many-function-args
e8574e
-            return MSCSTemplateV1.__new__(MSCSTemplateV1, s)
e8574e
-
e8574e
-    def __getstate__(self):
e8574e
-        return self.unparsed_input
e8574e
-
e8574e
-    def __setstate__(self, state):
e8574e
-        # explicitly call __init__ method to initialise object
e8574e
-        self.__init__(state)
e8574e
-
e8574e
-
e8574e
-class MSCSTemplate(ExternalCAProfile):
e8574e
-    """
e8574e
-    An Microsoft AD-CS Template specifier.
e8574e
-
e8574e
-    Subclasses MUST set ext_oid.
e8574e
-
e8574e
-    Subclass constructors MUST set asn1obj.
e8574e
-
e8574e
-    """
e8574e
-    valid_for = set([ExternalCAType.MS_CS.value])
e8574e
-
e8574e
-    ext_oid = None  # extension OID, as a Python str
e8574e
-    asn1obj = None  # unencoded extension data
e8574e
-
e8574e
-    def get_ext_data(self):
e8574e
-        """Return DER-encoded extension data."""
e8574e
-        return encoder.encode(self.asn1obj)
e8574e
-
e8574e
-
e8574e
-class MSCSTemplateV1(MSCSTemplate):
e8574e
-    """
e8574e
-    A v1 template specifier, per
e8574e
-    https://msdn.microsoft.com/en-us/library/cc250011.aspx.
e8574e
-
e8574e
-    ::
e8574e
-
e8574e
-        CertificateTemplateName ::= SEQUENCE {
e8574e
-           Name            UTF8String
e8574e
-        }
e8574e
-
e8574e
-    But note that a bare BMPString is used in practice.
e8574e
-
e8574e
-    """
e8574e
-    ext_oid = "1.3.6.1.4.1.311.20.2"
e8574e
-
e8574e
-    def __init__(self, s):
e8574e
-        super(MSCSTemplateV1, self).__init__(s)
e8574e
-        parts = s.split(':')
e8574e
-        if len(parts) > 1:
e8574e
-            raise ValueError(
e8574e
-                "Cannot specify certificate template version when using name.")
e8574e
-        self.asn1obj = char.BMPString(str(parts[0]))
e8574e
-
e8574e
-
e8574e
-class MSCSTemplateV2(MSCSTemplate):
e8574e
-    """
e8574e
-    A v2 template specifier, per
e8574e
-    https://msdn.microsoft.com/en-us/library/windows/desktop/aa378274(v=vs.85).aspx
e8574e
-
e8574e
-    ::
e8574e
-
e8574e
-        CertificateTemplate ::= SEQUENCE {
e8574e
-            templateID              EncodedObjectID,
e8574e
-            templateMajorVersion    TemplateVersion,
e8574e
-            templateMinorVersion    TemplateVersion OPTIONAL
e8574e
-        }
e8574e
-
e8574e
-        TemplateVersion ::= INTEGER (0..4294967295)
e8574e
-
e8574e
-    """
e8574e
-    ext_oid = "1.3.6.1.4.1.311.21.7"
e8574e
-
e8574e
-    @staticmethod
e8574e
-    def check_version_in_range(desc, n):
e8574e
-        if n < 0 or n >= 2**32:
e8574e
-            raise ValueError(
e8574e
-                "Template {} version must be in range 0..4294967295"
e8574e
-                .format(desc))
e8574e
-
e8574e
-    def __init__(self, s):
e8574e
-        super(MSCSTemplateV2, self).__init__(s)
e8574e
-
e8574e
-        parts = s.split(':')
e8574e
-
e8574e
-        obj = CertificateTemplateV2()
e8574e
-        if len(parts) < 2 or len(parts) > 3:
e8574e
-            raise ValueError(
e8574e
-                "Incorrect template specification; required format is: "
e8574e
-                "<oid>:<majorVersion>[:<minorVersion>]")
e8574e
-        try:
e8574e
-            obj['templateID'] = univ.ObjectIdentifier(parts[0])
e8574e
-
e8574e
-            major = int(parts[1])
e8574e
-            self.check_version_in_range("major", major)
e8574e
-            obj['templateMajorVersion'] = major
e8574e
-
e8574e
-            if len(parts) > 2:
e8574e
-                minor = int(parts[2])
e8574e
-                self.check_version_in_range("minor", minor)
e8574e
-                obj['templateMinorVersion'] = int(parts[2])
e8574e
-
e8574e
-        except pyasn1.error.PyAsn1Error:
e8574e
-            raise ValueError("Could not parse certificate template specifier.")
e8574e
-        self.asn1obj = obj
e8574e
-
e8574e
-
e8574e
-class CertificateTemplateV2(univ.Sequence):
e8574e
-    componentType = namedtype.NamedTypes(
e8574e
-        namedtype.NamedType('templateID', univ.ObjectIdentifier()),
e8574e
-        namedtype.NamedType('templateMajorVersion', univ.Integer()),
e8574e
-        namedtype.OptionalNamedType('templateMinorVersion', univ.Integer())
e8574e
-    )
e8574e
-
e8574e
-
e8574e
 if __name__ == "__main__":
e8574e
     standard_logging_setup("install.log")
e8574e
     ds = dsinstance.DsInstance()
e8574e
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
e8574e
index 3f113c35bf..37dcc2befa 100644
e8574e
--- a/ipaserver/install/ipa_cacert_manage.py
e8574e
+++ b/ipaserver/install/ipa_cacert_manage.py
e8574e
@@ -65,7 +65,7 @@ def add_options(cls, parser):
e8574e
             "--external-ca", dest='self_signed',
e8574e
             action='store_false',
e8574e
             help="Sign the renewed certificate by external CA")
e8574e
-        ext_cas = tuple(x.value for x in cainstance.ExternalCAType)
e8574e
+        ext_cas = tuple(x.value for x in x509.ExternalCAType)
e8574e
         renew_group.add_option(
e8574e
             "--external-ca-type", dest="external_ca_type",
e8574e
             type="choice", choices=ext_cas,
e8574e
@@ -73,7 +73,7 @@ def add_options(cls, parser):
e8574e
             help="Type of the external CA. Default: generic")
e8574e
         renew_group.add_option(
e8574e
             "--external-ca-profile", dest="external_ca_profile",
e8574e
-            type='constructor', constructor=cainstance.ExternalCAProfile,
e8574e
+            type='constructor', constructor=x509.ExternalCAProfile,
e8574e
             default=None, metavar="PROFILE-SPEC",
e8574e
             help="Specify the certificate profile/template to use "
e8574e
                  "at the external CA")
e8574e
@@ -224,11 +224,11 @@ def renew_external_step_1(self, ca):
e8574e
         options = self.options
e8574e
 
e8574e
         if not options.external_ca_type:
e8574e
-            options.external_ca_type = cainstance.ExternalCAType.GENERIC.value
e8574e
+            options.external_ca_type = x509.ExternalCAType.GENERIC.value
e8574e
 
e8574e
-        if options.external_ca_type == cainstance.ExternalCAType.MS_CS.value \
e8574e
+        if options.external_ca_type == x509.ExternalCAType.MS_CS.value \
e8574e
                 and options.external_ca_profile is None:
e8574e
-            options.external_ca_profile = cainstance.MSCSTemplateV1(u"SubCA")
e8574e
+            options.external_ca_profile = x509.MSCSTemplateV1(u"SubCA")
e8574e
 
e8574e
         if options.external_ca_profile is not None:
e8574e
             # check that profile is valid for the external ca type
e8574e
@@ -352,11 +352,11 @@ def resubmit_request(self, ca=RENEWAL_CA_NAME, profile=None):
e8574e
         timeout = api.env.startup_timeout + 60
e8574e
 
e8574e
         cm_profile = None
e8574e
-        if isinstance(profile, cainstance.MSCSTemplateV1):
e8574e
+        if isinstance(profile, x509.MSCSTemplateV1):
e8574e
             cm_profile = profile.unparsed_input
e8574e
 
e8574e
         cm_template = None
e8574e
-        if isinstance(profile, cainstance.MSCSTemplateV2):
e8574e
+        if isinstance(profile, x509.MSCSTemplateV2):
e8574e
             cm_template = profile.unparsed_input
e8574e
 
e8574e
         logger.debug("resubmitting certmonger request '%s'", self.request_id)
e8574e
diff --git a/ipatests/test_integration/test_external_ca.py b/ipatests/test_integration/test_external_ca.py
e8574e
index a42355217d..5aa2b7bba0 100644
e8574e
--- a/ipatests/test_integration/test_external_ca.py
e8574e
+++ b/ipatests/test_integration/test_external_ca.py
e8574e
@@ -108,14 +108,14 @@ def check_ipaca_issuerDN(host, expected_dn):
e8574e
     assert "Issuer DN: {}".format(expected_dn) in result.stdout_text
e8574e
 
e8574e
 
e8574e
-def check_mscs_extension(ipa_csr, oid, value):
e8574e
+def check_mscs_extension(ipa_csr, template):
e8574e
     csr = x509.load_pem_x509_csr(ipa_csr, default_backend())
e8574e
     extensions = [
e8574e
         ext for ext in csr.extensions
e8574e
-        if ext.oid.dotted_string == oid
e8574e
+        if ext.oid.dotted_string == template.ext_oid
e8574e
     ]
e8574e
     assert extensions
e8574e
-    assert extensions[0].value.value == value
e8574e
+    assert extensions[0].value.value == template.get_ext_data()
e8574e
 
e8574e
 
e8574e
 class TestExternalCA(IntegrationTest):
e8574e
@@ -134,10 +134,7 @@ def test_external_ca(self):
e8574e
 
e8574e
         # check CSR for extension
e8574e
         ipa_csr = self.master.get_file_contents(paths.ROOT_IPA_CSR)
e8574e
-        # Values for MSCSTemplateV1('SubCA')
e8574e
-        oid = "1.3.6.1.4.1.311.20.2"
e8574e
-        value = b'\x1e\n\x00S\x00u\x00b\x00C\x00A'
e8574e
-        check_mscs_extension(ipa_csr, oid, value)
e8574e
+        check_mscs_extension(ipa_csr, ipa_x509.MSCSTemplateV1(u'SubCA'))
e8574e
 
e8574e
         # Sign CA, transport it to the host and get ipa a root ca paths.
e8574e
         root_ca_fname, ipa_ca_fname = tasks.sign_ca_and_transport(
e8574e
diff --git a/ipatests/test_ipalib/test_x509.py b/ipatests/test_ipalib/test_x509.py
e8574e
index ff7e6de2f7..284b998316 100644
e8574e
--- a/ipatests/test_ipalib/test_x509.py
e8574e
+++ b/ipatests/test_ipalib/test_x509.py
e8574e
@@ -22,7 +22,11 @@
e8574e
 """
e8574e
 
e8574e
 import base64
e8574e
+from binascii import hexlify
e8574e
+from configparser import RawConfigParser
e8574e
 import datetime
e8574e
+from io import StringIO
e8574e
+import pickle
e8574e
 
e8574e
 import pytest
e8574e
 
e8574e
@@ -268,3 +272,114 @@ def test_ipa_demo_letsencrypt(self):
e8574e
             b'0 \x06\x03U\x1d%\x01\x01\xff\x04\x160\x14\x06\x08+\x06\x01'
e8574e
             b'\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x02'
e8574e
         )
e8574e
+
e8574e
+
e8574e
+class test_ExternalCAProfile:
e8574e
+    def test_MSCSTemplateV1_good(self):
e8574e
+        o = x509.MSCSTemplateV1("MySubCA")
e8574e
+        assert hexlify(o.get_ext_data()) == b'1e0e004d007900530075006200430041'
e8574e
+
e8574e
+    def test_MSCSTemplateV1_bad(self):
e8574e
+        with pytest.raises(ValueError):
e8574e
+            x509.MSCSTemplateV1("MySubCA:1")
e8574e
+
e8574e
+    def test_MSCSTemplateV1_pickle_roundtrip(self):
e8574e
+        o = x509.MSCSTemplateV1("MySubCA")
e8574e
+        s = pickle.dumps(o)
e8574e
+        assert o.get_ext_data() == pickle.loads(s).get_ext_data()
e8574e
+
e8574e
+    def test_MSCSTemplateV2_too_few_parts(self):
e8574e
+        with pytest.raises(ValueError):
e8574e
+            x509.MSCSTemplateV2("1.2.3.4")
e8574e
+
e8574e
+    def test_MSCSTemplateV2_too_many_parts(self):
e8574e
+        with pytest.raises(ValueError):
e8574e
+            x509.MSCSTemplateV2("1.2.3.4:100:200:300")
e8574e
+
e8574e
+    def test_MSCSTemplateV2_bad_oid(self):
e8574e
+        with pytest.raises(ValueError):
e8574e
+            x509.MSCSTemplateV2("not_an_oid:1")
e8574e
+
e8574e
+    def test_MSCSTemplateV2_non_numeric_major_version(self):
e8574e
+        with pytest.raises(ValueError):
e8574e
+            x509.MSCSTemplateV2("1.2.3.4:major:200")
e8574e
+
e8574e
+    def test_MSCSTemplateV2_non_numeric_minor_version(self):
e8574e
+        with pytest.raises(ValueError):
e8574e
+            x509.MSCSTemplateV2("1.2.3.4:100:minor")
e8574e
+
e8574e
+    def test_MSCSTemplateV2_major_version_lt_zero(self):
e8574e
+        with pytest.raises(ValueError):
e8574e
+            x509.MSCSTemplateV2("1.2.3.4:-1:200")
e8574e
+
e8574e
+    def test_MSCSTemplateV2_minor_version_lt_zero(self):
e8574e
+        with pytest.raises(ValueError):
e8574e
+            x509.MSCSTemplateV2("1.2.3.4:100:-1")
e8574e
+
e8574e
+    def test_MSCSTemplateV2_major_version_gt_max(self):
e8574e
+        with pytest.raises(ValueError):
e8574e
+            x509.MSCSTemplateV2("1.2.3.4:4294967296:200")
e8574e
+
e8574e
+    def test_MSCSTemplateV2_minor_version_gt_max(self):
e8574e
+        with pytest.raises(ValueError):
e8574e
+            x509.MSCSTemplateV2("1.2.3.4:100:4294967296")
e8574e
+
e8574e
+    def test_MSCSTemplateV2_good_major(self):
e8574e
+        o = x509.MSCSTemplateV2("1.2.3.4:4294967295")
e8574e
+        assert hexlify(o.get_ext_data()) == b'300c06032a0304020500ffffffff'
e8574e
+
e8574e
+    def test_MSCSTemplateV2_good_major_minor(self):
e8574e
+        o = x509.MSCSTemplateV2("1.2.3.4:4294967295:0")
e8574e
+        assert hexlify(o.get_ext_data()) \
e8574e
+            == b'300f06032a0304020500ffffffff020100'
e8574e
+
e8574e
+    def test_MSCSTemplateV2_pickle_roundtrip(self):
e8574e
+        o = x509.MSCSTemplateV2("1.2.3.4:4294967295:0")
e8574e
+        s = pickle.dumps(o)
e8574e
+        assert o.get_ext_data() == pickle.loads(s).get_ext_data()
e8574e
+
e8574e
+    def test_ExternalCAProfile_dispatch(self):
e8574e
+        """
e8574e
+        Test that constructing ExternalCAProfile actually returns an
e8574e
+        instance of the appropriate subclass.
e8574e
+        """
e8574e
+        assert isinstance(
e8574e
+            x509.ExternalCAProfile("MySubCA"),
e8574e
+            x509.MSCSTemplateV1)
e8574e
+        assert isinstance(
e8574e
+            x509.ExternalCAProfile("1.2.3.4:100"),
e8574e
+            x509.MSCSTemplateV2)
e8574e
+
e8574e
+    def test_write_pkispawn_config_file_MSCSTemplateV1(self):
e8574e
+        template = x509.MSCSTemplateV1(u"SubCA")
e8574e
+        expected = (
e8574e
+            '[CA]\n'
e8574e
+            'pki_req_ext_oid = 1.3.6.1.4.1.311.20.2\n'
e8574e
+            'pki_req_ext_data = 1e0a00530075006200430041\n\n'
e8574e
+        )
e8574e
+        self._test_write_pkispawn_config_file(template, expected)
e8574e
+
e8574e
+    def test_write_pkispawn_config_file_MSCSTemplateV2(self):
e8574e
+        template = x509.MSCSTemplateV2(u"1.2.3.4:4294967295")
e8574e
+        expected = (
e8574e
+            '[CA]\n'
e8574e
+            'pki_req_ext_oid = 1.3.6.1.4.1.311.21.7\n'
e8574e
+            'pki_req_ext_data = 300c06032a0304020500ffffffff\n\n'
e8574e
+        )
e8574e
+        self._test_write_pkispawn_config_file(template, expected)
e8574e
+
e8574e
+    def _test_write_pkispawn_config_file(self, template, expected):
e8574e
+        """
e8574e
+        Test that the values we read from an ExternalCAProfile
e8574e
+        object can be used to produce a reasonable-looking pkispawn
e8574e
+        configuration.
e8574e
+        """
e8574e
+        config = RawConfigParser()
e8574e
+        config.optionxform = str
e8574e
+        config.add_section("CA")
e8574e
+        config.set("CA", "pki_req_ext_oid", template.ext_oid)
e8574e
+        config.set("CA", "pki_req_ext_data",
e8574e
+                   hexlify(template.get_ext_data()).decode('ascii'))
e8574e
+        out = StringIO()
e8574e
+        config.write(out)
e8574e
+        assert out.getvalue() == expected
e8574e
diff --git a/ipatests/test_ipaserver/test_install/test_cainstance.py b/ipatests/test_ipaserver/test_install/test_cainstance.py
e8574e
deleted file mode 100644
e8574e
index 02d9758e4a..0000000000
e8574e
--- a/ipatests/test_ipaserver/test_install/test_cainstance.py
e8574e
+++ /dev/null
e8574e
@@ -1,123 +0,0 @@
e8574e
-#
e8574e
-# Copyright (C) 2017  FreeIPA Contributors see COPYING for license
e8574e
-#
e8574e
-
e8574e
-from binascii import hexlify
e8574e
-from io import StringIO
e8574e
-import pickle
e8574e
-from configparser import RawConfigParser
e8574e
-import pytest
e8574e
-from ipaserver.install import cainstance
e8574e
-
e8574e
-pytestmark = pytest.mark.tier0
e8574e
-
e8574e
-
e8574e
-class test_ExternalCAProfile:
e8574e
-    def test_MSCSTemplateV1_good(self):
e8574e
-        o = cainstance.MSCSTemplateV1("MySubCA")
e8574e
-        assert hexlify(o.get_ext_data()) == b'1e0e004d007900530075006200430041'
e8574e
-
e8574e
-    def test_MSCSTemplateV1_bad(self):
e8574e
-        with pytest.raises(ValueError):
e8574e
-            cainstance.MSCSTemplateV1("MySubCA:1")
e8574e
-
e8574e
-    def test_MSCSTemplateV1_pickle_roundtrip(self):
e8574e
-        o = cainstance.MSCSTemplateV1("MySubCA")
e8574e
-        s = pickle.dumps(o)
e8574e
-        assert o.get_ext_data() == pickle.loads(s).get_ext_data()
e8574e
-
e8574e
-    def test_MSCSTemplateV2_too_few_parts(self):
e8574e
-        with pytest.raises(ValueError):
e8574e
-            cainstance.MSCSTemplateV2("1.2.3.4")
e8574e
-
e8574e
-    def test_MSCSTemplateV2_too_many_parts(self):
e8574e
-        with pytest.raises(ValueError):
e8574e
-            cainstance.MSCSTemplateV2("1.2.3.4:100:200:300")
e8574e
-
e8574e
-    def test_MSCSTemplateV2_bad_oid(self):
e8574e
-        with pytest.raises(ValueError):
e8574e
-            cainstance.MSCSTemplateV2("not_an_oid:1")
e8574e
-
e8574e
-    def test_MSCSTemplateV2_non_numeric_major_version(self):
e8574e
-        with pytest.raises(ValueError):
e8574e
-            cainstance.MSCSTemplateV2("1.2.3.4:major:200")
e8574e
-
e8574e
-    def test_MSCSTemplateV2_non_numeric_minor_version(self):
e8574e
-        with pytest.raises(ValueError):
e8574e
-            cainstance.MSCSTemplateV2("1.2.3.4:100:minor")
e8574e
-
e8574e
-    def test_MSCSTemplateV2_major_version_lt_zero(self):
e8574e
-        with pytest.raises(ValueError):
e8574e
-            cainstance.MSCSTemplateV2("1.2.3.4:-1:200")
e8574e
-
e8574e
-    def test_MSCSTemplateV2_minor_version_lt_zero(self):
e8574e
-        with pytest.raises(ValueError):
e8574e
-            cainstance.MSCSTemplateV2("1.2.3.4:100:-1")
e8574e
-
e8574e
-    def test_MSCSTemplateV2_major_version_gt_max(self):
e8574e
-        with pytest.raises(ValueError):
e8574e
-            cainstance.MSCSTemplateV2("1.2.3.4:4294967296:200")
e8574e
-
e8574e
-    def test_MSCSTemplateV2_minor_version_gt_max(self):
e8574e
-        with pytest.raises(ValueError):
e8574e
-            cainstance.MSCSTemplateV2("1.2.3.4:100:4294967296")
e8574e
-
e8574e
-    def test_MSCSTemplateV2_good_major(self):
e8574e
-        o = cainstance.MSCSTemplateV2("1.2.3.4:4294967295")
e8574e
-        assert hexlify(o.get_ext_data()) == b'300c06032a0304020500ffffffff'
e8574e
-
e8574e
-    def test_MSCSTemplateV2_good_major_minor(self):
e8574e
-        o = cainstance.MSCSTemplateV2("1.2.3.4:4294967295:0")
e8574e
-        assert hexlify(o.get_ext_data()) \
e8574e
-            == b'300f06032a0304020500ffffffff020100'
e8574e
-
e8574e
-    def test_MSCSTemplateV2_pickle_roundtrip(self):
e8574e
-        o = cainstance.MSCSTemplateV2("1.2.3.4:4294967295:0")
e8574e
-        s = pickle.dumps(o)
e8574e
-        assert o.get_ext_data() == pickle.loads(s).get_ext_data()
e8574e
-
e8574e
-    def test_ExternalCAProfile_dispatch(self):
e8574e
-        """
e8574e
-        Test that constructing ExternalCAProfile actually returns an
e8574e
-        instance of the appropriate subclass.
e8574e
-        """
e8574e
-        assert isinstance(
e8574e
-            cainstance.ExternalCAProfile("MySubCA"),
e8574e
-            cainstance.MSCSTemplateV1)
e8574e
-        assert isinstance(
e8574e
-            cainstance.ExternalCAProfile("1.2.3.4:100"),
e8574e
-            cainstance.MSCSTemplateV2)
e8574e
-
e8574e
-    def test_write_pkispawn_config_file_MSCSTemplateV1(self):
e8574e
-        template = cainstance.MSCSTemplateV1(u"SubCA")
e8574e
-        expected = (
e8574e
-            '[CA]\n'
e8574e
-            'pki_req_ext_oid = 1.3.6.1.4.1.311.20.2\n'
e8574e
-            'pki_req_ext_data = 1e0a00530075006200430041\n\n'
e8574e
-        )
e8574e
-        self._test_write_pkispawn_config_file(template, expected)
e8574e
-
e8574e
-    def test_write_pkispawn_config_file_MSCSTemplateV2(self):
e8574e
-        template = cainstance.MSCSTemplateV2(u"1.2.3.4:4294967295")
e8574e
-        expected = (
e8574e
-            '[CA]\n'
e8574e
-            'pki_req_ext_oid = 1.3.6.1.4.1.311.21.7\n'
e8574e
-            'pki_req_ext_data = 300c06032a0304020500ffffffff\n\n'
e8574e
-        )
e8574e
-        self._test_write_pkispawn_config_file(template, expected)
e8574e
-
e8574e
-    def _test_write_pkispawn_config_file(self, template, expected):
e8574e
-        """
e8574e
-        Test that the values we read from an ExternalCAProfile
e8574e
-        object can be used to produce a reasonable-looking pkispawn
e8574e
-        configuration.
e8574e
-        """
e8574e
-        config = RawConfigParser()
e8574e
-        config.optionxform = str
e8574e
-        config.add_section("CA")
e8574e
-        config.set("CA", "pki_req_ext_oid", template.ext_oid)
e8574e
-        config.set("CA", "pki_req_ext_data",
e8574e
-                   hexlify(template.get_ext_data()).decode('ascii'))
e8574e
-        out = StringIO()
e8574e
-        config.write(out)
e8574e
-        assert out.getvalue() == expected
e8574e
From e632b220798833bcd65c6b266610c800ed0914d7 Mon Sep 17 00:00:00 2001
e8574e
From: Fraser Tweedale <ftweedal@redhat.com>
e8574e
Date: Fri, 12 Jul 2019 13:13:02 +1000
e8574e
Subject: [PATCH] install: fix --external-ca-profile option
e8574e
e8574e
Commit dd47cfc75a69618f486abefb70f2649ebf8264e7 removed the ability
e8574e
to set pki_req_ext_oid and pki_req_ext_data in the pkispawn config.
e8574e
This results in the --external-ca-profile option never setting the
e8574e
requested values in the CSR (the default V1 template type specifying
e8574e
"SubCA" is always used).
e8574e
e8574e
Remove relevant fields from both ipaca_default.ini and
e8574e
ipaca_customize.ini.  This allows the IPA framework to set the
e8574e
values (i.e. when --external-ca-type=ms-cs and
e8574e
--external-ca-profile=... demand it).  It also allows users to
e8574e
override the pki_req_ext_* settings.
e8574e
e8574e
Part of: https://pagure.io/freeipa/issue/7548
e8574e
Related: https://pagure.io/freeipa/issue/5608
e8574e
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
e8574e
---
e8574e
 install/share/ipaca_customize.ini | 5 -----
e8574e
 install/share/ipaca_default.ini   | 1 -
e8574e
 2 files changed, 6 deletions(-)
e8574e
e8574e
diff --git a/install/share/ipaca_customize.ini b/install/share/ipaca_customize.ini
e8574e
index 130ec2c102..6d58579af8 100644
e8574e
--- a/install/share/ipaca_customize.ini
e8574e
+++ b/install/share/ipaca_customize.ini
e8574e
@@ -93,11 +93,6 @@ pki_ca_signing_key_type=%(ipa_ca_key_type)s
e8574e
 pki_ca_signing_signing_algorithm=%(ipa_ca_signing_algorithm)s
e8574e
 pki_ca_signing_token=%(pki_token_name)s
e8574e
 
e8574e
-# MS subca request ext data
e8574e
-pki_req_ext_oid=1.3.6.1.4.1.311.20.2
e8574e
-pki_req_ext_critical=False
e8574e
-pki_req_ext_data=1E0A00530075006200430041
e8574e
-
e8574e
 ## ocspSigningCert cert-pki-ca
e8574e
 pki_ocsp_signing_key_algorithm=%(ipa_key_algorithm)s
e8574e
 pki_ocsp_signing_key_size=%(ipa_key_size)s
e8574e
diff --git a/install/share/ipaca_default.ini b/install/share/ipaca_default.ini
e8574e
index fedc1b9a74..2b9900286e 100644
e8574e
--- a/install/share/ipaca_default.ini
e8574e
+++ b/install/share/ipaca_default.ini
e8574e
@@ -115,7 +115,6 @@ pki_ca_starting_crl_number=0
e8574e
 
e8574e
 pki_external=False
e8574e
 pki_external_step_two=False
e8574e
-pki_req_ext_add=False
e8574e
 
e8574e
 pki_external_pkcs12_path=%(pki_pkcs12_path)s
e8574e
 pki_external_pkcs12_password=%(pki_pkcs12_password)s
e8574e
From 71af731b3069fa1b2c0b51a3b917b5bc4da54350 Mon Sep 17 00:00:00 2001
e8574e
From: Fraser Tweedale <ftweedal@redhat.com>
e8574e
Date: Fri, 12 Jul 2019 13:24:51 +1000
e8574e
Subject: [PATCH] Fix use of incorrect variable
e8574e
e8574e
Part of: https://pagure.io/freeipa/issue/7548
e8574e
Related: https://pagure.io/freeipa/issue/5608
e8574e
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
e8574e
---
e8574e
 ipaserver/install/dogtaginstance.py | 2 +-
e8574e
 1 file changed, 1 insertion(+), 1 deletion(-)
e8574e
e8574e
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
e8574e
index cc75d89746..5dca721d6c 100644
e8574e
--- a/ipaserver/install/dogtaginstance.py
e8574e
+++ b/ipaserver/install/dogtaginstance.py
e8574e
@@ -853,7 +853,7 @@ def _verify_immutable(self, config, immutable_settings, filename):
e8574e
         if errs:
e8574e
             raise ValueError(
e8574e
                 '{} overrides immutable options:\n{}'.format(
e8574e
-                    filename, '\n'.join(errors)
e8574e
+                    filename, '\n'.join(errs)
e8574e
                 )
e8574e
             )
e8574e
 
e8574e
From 83ed05725110de19a7098678274ecaaaf6a2c9c9 Mon Sep 17 00:00:00 2001
e8574e
From: Fraser Tweedale <frase@frase.id.au>
e8574e
Date: Wed, 20 Feb 2019 18:34:33 +1100
e8574e
Subject: [PATCH] Add more tests for --external-ca-profile handling
e8574e
e8574e
Add tests for remaining untested scenarios of --external-ca-profile
e8574e
handling in ipa-server-install.
e8574e
e8574e
ipa-ca-install and ipa-cacert-manage remain untested at present.
e8574e
e8574e
Fixes: https://pagure.io/freeipa/issue/7548
e8574e
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
e8574e
---
e8574e
 ipatests/test_integration/test_external_ca.py | 97 ++++++++++++++++++-
e8574e
 1 file changed, 95 insertions(+), 2 deletions(-)
e8574e
e8574e
diff --git a/ipatests/test_integration/test_external_ca.py b/ipatests/test_integration/test_external_ca.py
e8574e
index 5aa2b7bba0..dc9a09b43b 100644
e8574e
--- a/ipatests/test_integration/test_external_ca.py
e8574e
+++ b/ipatests/test_integration/test_external_ca.py
e8574e
@@ -74,10 +74,10 @@ def match_in_journal(host, string, since='today', services=('certmonger',)):
e8574e
     return match
e8574e
 
e8574e
 
e8574e
-def install_server_external_ca_step1(host, extra_args=()):
e8574e
+def install_server_external_ca_step1(host, extra_args=(), raiseonerr=True):
e8574e
     """Step 1 to install the ipa server with external ca"""
e8574e
     return tasks.install_master(
e8574e
-        host, external_ca=True, extra_args=extra_args
e8574e
+        host, external_ca=True, extra_args=extra_args, raiseonerr=raiseonerr,
e8574e
     )
e8574e
 
e8574e
 
e8574e
@@ -478,3 +478,96 @@ def test_master_install_ca2(self):
e8574e
             'certutil', '-L', '-d', paths.PKI_TOMCAT_ALIAS_DIR,
e8574e
             '-n', cert_nick])
e8574e
         assert "CN=RootCA2" in result.stdout_text
e8574e
+
e8574e
+
e8574e
+def _step1_profile(master, s):
e8574e
+    return install_server_external_ca_step1(
e8574e
+        master,
e8574e
+        extra_args=['--external-ca-type=ms-cs', f'--external-ca-profile={s}'],
e8574e
+        raiseonerr=False,
e8574e
+    )
e8574e
+
e8574e
+
e8574e
+def _test_invalid_profile(master, profile):
e8574e
+    result = _step1_profile(master, profile)
e8574e
+    assert result.returncode != 0
e8574e
+    assert '--external-ca-profile' in result.stderr_text
e8574e
+
e8574e
+
e8574e
+def _test_valid_profile(master, profile_cls, profile):
e8574e
+    result = _step1_profile(master, profile)
e8574e
+    assert result.returncode == 0
e8574e
+    ipa_csr = master.get_file_contents(paths.ROOT_IPA_CSR)
e8574e
+    check_mscs_extension(ipa_csr, profile_cls(profile))
e8574e
+
e8574e
+
e8574e
+class TestExternalCAProfileV1(IntegrationTest):
e8574e
+    """
e8574e
+    Test that --external-ca-profile=Foo gets propagated to the CSR.
e8574e
+
e8574e
+    The default template extension when --external-ca-type=ms-cs,
e8574e
+    a V1 extension with value "SubCA", already gets tested by the
e8574e
+    ``TestExternalCA`` class.
e8574e
+
e8574e
+    We only need to do Step 1 of installation, then check the CSR.
e8574e
+
e8574e
+    """
e8574e
+    def test_invalid_v1_template(self):
e8574e
+        _test_invalid_profile(self.master, 'NotAnOid:1')
e8574e
+
e8574e
+    def test_valid_v1_template(self):
e8574e
+        _test_valid_profile(
e8574e
+            self.master, ipa_x509.MSCSTemplateV1, 'TemplateOfAwesome')
e8574e
+
e8574e
+
e8574e
+class TestExternalCAProfileV2MajorOnly(IntegrationTest):
e8574e
+    """
e8574e
+    Test that V2 template specifiers without minor version get
e8574e
+    propagated to CSR.  This class also tests all error modes in
e8574e
+    specifying a V2 template, those being:
e8574e
+
e8574e
+    - no major version specified
e8574e
+    - too many parts specified (i.e. major, minor, and then some more)
e8574e
+    - major version is not an int
e8574e
+    - major version is negative
e8574e
+    - minor version is not an int
e8574e
+    - minor version is negative
e8574e
+
e8574e
+    We only need to do Step 1 of installation, then check the CSR.
e8574e
+
e8574e
+    """
e8574e
+    def test_v2_template_too_few_parts(self):
e8574e
+        _test_invalid_profile(self.master, '1.2.3.4')
e8574e
+
e8574e
+    def test_v2_template_too_many_parts(self):
e8574e
+        _test_invalid_profile(self.master, '1.2.3.4:100:200:300')
e8574e
+
e8574e
+    def test_v2_template_major_version_not_int(self):
e8574e
+        _test_invalid_profile(self.master, '1.2.3.4:wat:200')
e8574e
+
e8574e
+    def test_v2_template_major_version_negative(self):
e8574e
+        _test_invalid_profile(self.master, '1.2.3.4:-1:200')
e8574e
+
e8574e
+    def test_v2_template_minor_version_not_int(self):
e8574e
+        _test_invalid_profile(self.master, '1.2.3.4:100:wat')
e8574e
+
e8574e
+    def test_v2_template_minor_version_negative(self):
e8574e
+        _test_invalid_profile(self.master, '1.2.3.4:100:-2')
e8574e
+
e8574e
+    def test_v2_template_valid_major_only(self):
e8574e
+        _test_valid_profile(
e8574e
+            self.master, ipa_x509.MSCSTemplateV2, '1.2.3.4:100')
e8574e
+
e8574e
+
e8574e
+class TestExternalCAProfileV2MajorMinor(IntegrationTest):
e8574e
+    """
e8574e
+    Test that V2 template specifiers _with_ minor version get
e8574e
+    propagated to CSR.  All error modes of V2 template specifiers
e8574e
+    were tested in ``TestExternalCAProfileV2Major``.
e8574e
+
e8574e
+    We only need to do Step 1 of installation, then check the CSR.
e8574e
+
e8574e
+    """
e8574e
+    def test_v2_template_valid_major_minor(self):
e8574e
+        _test_valid_profile(
e8574e
+            self.master, ipa_x509.MSCSTemplateV2, '1.2.3.4:100:200')
e8574e
From a627df87c31e4d8399bd9fab43c0c4772ddd8955 Mon Sep 17 00:00:00 2001
e8574e
From: Fraser Tweedale <ftweedal@redhat.com>
e8574e
Date: Thu, 11 Jul 2019 20:22:33 +1000
e8574e
Subject: [PATCH] Collapse --external-ca-profile tests into single class
e8574e
e8574e
To avoid having to spawn new CI hosts for each kind of
e8574e
--external-ca-profile argument we are testing, collapse the three
e8574e
separate test classes into one.  Uninstall the half-installed IPA
e8574e
after each section of tests.
e8574e
e8574e
This change is in response to review comment
e8574e
https://github.com/freeipa/freeipa/pull/2852#pullrequestreview-220442170.
e8574e
e8574e
Part of: https://pagure.io/freeipa/issue/7548
e8574e
e8574e
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
e8574e
---
e8574e
 ipatests/test_integration/test_external_ca.py | 34 ++++++++++++++-----
e8574e
 1 file changed, 26 insertions(+), 8 deletions(-)
e8574e
e8574e
diff --git a/ipatests/test_integration/test_external_ca.py b/ipatests/test_integration/test_external_ca.py
e8574e
index dc9a09b43b..714aebd4a8 100644
e8574e
--- a/ipatests/test_integration/test_external_ca.py
e8574e
+++ b/ipatests/test_integration/test_external_ca.py
e8574e
@@ -501,8 +501,18 @@ def _test_valid_profile(master, profile_cls, profile):
e8574e
     check_mscs_extension(ipa_csr, profile_cls(profile))
e8574e
 
e8574e
 
e8574e
-class TestExternalCAProfileV1(IntegrationTest):
e8574e
+class TestExternalCAProfileScenarios(IntegrationTest):
e8574e
     """
e8574e
+    Test the various --external-ca-profile scenarios.
e8574e
+    This test is broken into sections, with each section first
e8574e
+    testing invalid arguments, then a valid argument, and finally
e8574e
+    uninstalling the half-installed IPA.
e8574e
+
e8574e
+    """
e8574e
+
e8574e
+    '''
e8574e
+    Tranche 1: version 1 templates.
e8574e
+
e8574e
     Test that --external-ca-profile=Foo gets propagated to the CSR.
e8574e
 
e8574e
     The default template extension when --external-ca-type=ms-cs,
e8574e
@@ -511,7 +521,7 @@ class TestExternalCAProfileV1(IntegrationTest):
e8574e
 
e8574e
     We only need to do Step 1 of installation, then check the CSR.
e8574e
 
e8574e
-    """
e8574e
+    '''
e8574e
     def test_invalid_v1_template(self):
e8574e
         _test_invalid_profile(self.master, 'NotAnOid:1')
e8574e
 
e8574e
@@ -519,9 +529,12 @@ def test_valid_v1_template(self):
e8574e
         _test_valid_profile(
e8574e
             self.master, ipa_x509.MSCSTemplateV1, 'TemplateOfAwesome')
e8574e
 
e8574e
+    def test_uninstall_1(self):
e8574e
+        tasks.uninstall_master(self.master)
e8574e
+
e8574e
+    '''
e8574e
+    Tranche 2: V2 templates without minor version.
e8574e
 
e8574e
-class TestExternalCAProfileV2MajorOnly(IntegrationTest):
e8574e
-    """
e8574e
     Test that V2 template specifiers without minor version get
e8574e
     propagated to CSR.  This class also tests all error modes in
e8574e
     specifying a V2 template, those being:
e8574e
@@ -535,7 +548,7 @@ class TestExternalCAProfileV2MajorOnly(IntegrationTest):
e8574e
 
e8574e
     We only need to do Step 1 of installation, then check the CSR.
e8574e
 
e8574e
-    """
e8574e
+    '''
e8574e
     def test_v2_template_too_few_parts(self):
e8574e
         _test_invalid_profile(self.master, '1.2.3.4')
e8574e
 
e8574e
@@ -558,16 +571,21 @@ def test_v2_template_valid_major_only(self):
e8574e
         _test_valid_profile(
e8574e
             self.master, ipa_x509.MSCSTemplateV2, '1.2.3.4:100')
e8574e
 
e8574e
+    def test_uninstall_2(self):
e8574e
+        tasks.uninstall_master(self.master)
e8574e
+
e8574e
+    '''
e8574e
+    Tranche 3: V2 templates with minor version.
e8574e
 
e8574e
-class TestExternalCAProfileV2MajorMinor(IntegrationTest):
e8574e
-    """
e8574e
     Test that V2 template specifiers _with_ minor version get
e8574e
     propagated to CSR.  All error modes of V2 template specifiers
e8574e
     were tested in ``TestExternalCAProfileV2Major``.
e8574e
 
e8574e
     We only need to do Step 1 of installation, then check the CSR.
e8574e
 
e8574e
-    """
e8574e
+    '''
e8574e
     def test_v2_template_valid_major_minor(self):
e8574e
         _test_valid_profile(
e8574e
             self.master, ipa_x509.MSCSTemplateV2, '1.2.3.4:100:200')
e8574e
+
e8574e
+    # this is the end; no need to uninstall.
e8574e
From 740964c3c47fd2cd216c233d8d9df1840eaa01ee Mon Sep 17 00:00:00 2001
e8574e
From: Fraser Tweedale <ftweedal@redhat.com>
e8574e
Date: Thu, 11 Jul 2019 20:27:02 +1000
e8574e
Subject: [PATCH] ci: add --external-ca-profile tests to nightly
e8574e
e8574e
Part of: https://pagure.io/freeipa/issue/7548
e8574e
e8574e
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
e8574e
---
e8574e
 ipatests/prci_definitions/nightly_f28.yaml        | 12 ++++++++++++
e8574e
 ipatests/prci_definitions/nightly_f29.yaml        | 12 ++++++++++++
e8574e
 ipatests/prci_definitions/nightly_master.yaml     | 12 ++++++++++++
e8574e
 ipatests/prci_definitions/nightly_master_pki.yaml | 12 ++++++++++++
e8574e
 ipatests/prci_definitions/nightly_rawhide.yaml    | 12 ++++++++++++
e8574e
 5 files changed, 60 insertions(+)
e8574e
e8574e
diff --git a/ipatests/prci_definitions/nightly_f28.yaml b/ipatests/prci_definitions/nightly_f28.yaml
e8574e
index fe86730444..d1605e6b5c 100644
e8574e
--- a/ipatests/prci_definitions/nightly_f28.yaml
e8574e
+++ b/ipatests/prci_definitions/nightly_f28.yaml
e8574e
@@ -75,6 +75,18 @@ jobs:
e8574e
         timeout: 3600
e8574e
         topology: *master_1repl
e8574e
 
e8574e
+  fedora-28/external_ca_templates:
e8574e
+    requires: [fedora-28/build]
e8574e
+    priority: 50
e8574e
+    job:
e8574e
+      class: RunPytest
e8574e
+      args:
e8574e
+        build_url: '{fedora-28/build_url}'
e8574e
+        test_suite: test_integration/test_external_ca.py::TestExternalCAProfileScenarios
e8574e
+        template: *ci-master-f28
e8574e
+        timeout: 3600
e8574e
+        topology: *master_1repl
e8574e
+
e8574e
   fedora-28/test_topologies:
e8574e
     requires: [fedora-28/build]
e8574e
     priority: 50
e8574e
diff --git a/ipatests/prci_definitions/nightly_f29.yaml b/ipatests/prci_definitions/nightly_f29.yaml
e8574e
index 57c1b624fe..ed88eb15c8 100644
e8574e
--- a/ipatests/prci_definitions/nightly_f29.yaml
e8574e
+++ b/ipatests/prci_definitions/nightly_f29.yaml
e8574e
@@ -75,6 +75,18 @@ jobs:
e8574e
         timeout: 3600
e8574e
         topology: *master_1repl
e8574e
 
e8574e
+  fedora-29/external_ca_templates:
e8574e
+    requires: [fedora-29/build]
e8574e
+    priority: 50
e8574e
+    job:
e8574e
+      class: RunPytest
e8574e
+      args:
e8574e
+        build_url: '{fedora-29/build_url}'
e8574e
+        test_suite: test_integration/test_external_ca.py::TestExternalCAProfileScenarios
e8574e
+        template: *ci-master-f29
e8574e
+        timeout: 3600
e8574e
+        topology: *master_1repl
e8574e
+
e8574e
   fedora-29/test_topologies:
e8574e
     requires: [fedora-29/build]
e8574e
     priority: 50
e8574e
diff --git a/ipatests/prci_definitions/nightly_master.yaml b/ipatests/prci_definitions/nightly_master.yaml
e8574e
index dc63f37426..0a66a13490 100644
e8574e
--- a/ipatests/prci_definitions/nightly_master.yaml
e8574e
+++ b/ipatests/prci_definitions/nightly_master.yaml
e8574e
@@ -75,6 +75,18 @@ jobs:
e8574e
         timeout: 3600
e8574e
         topology: *master_1repl
e8574e
 
e8574e
+  fedora-30/external_ca_templates:
e8574e
+    requires: [fedora-30/build]
e8574e
+    priority: 50
e8574e
+    job:
e8574e
+      class: RunPytest
e8574e
+      args:
e8574e
+        build_url: '{fedora-30/build_url}'
e8574e
+        test_suite: test_integration/test_external_ca.py::TestExternalCAProfileScenarios
e8574e
+        template: *ci-master-f30
e8574e
+        timeout: 3600
e8574e
+        topology: *master_1repl
e8574e
+
e8574e
   fedora-30/test_topologies:
e8574e
     requires: [fedora-30/build]
e8574e
     priority: 50
e8574e
diff --git a/ipatests/prci_definitions/nightly_master_pki.yaml b/ipatests/prci_definitions/nightly_master_pki.yaml
e8574e
index 1bb0af0244..ed2e38d3ed 100644
e8574e
--- a/ipatests/prci_definitions/nightly_master_pki.yaml
e8574e
+++ b/ipatests/prci_definitions/nightly_master_pki.yaml
e8574e
@@ -75,6 +75,18 @@ jobs:
e8574e
         timeout: 3600
e8574e
         topology: *master_1repl
e8574e
 
e8574e
+  fedora-29/external_ca_templates:
e8574e
+    requires: [fedora-29/build]
e8574e
+    priority: 50
e8574e
+    job:
e8574e
+      class: RunPytest
e8574e
+      args:
e8574e
+        build_url: '{fedora-29/build_url}'
e8574e
+        test_suite: test_integration/test_external_ca.py::TestExternalCAProfileScenarios
e8574e
+        template: *pki-master-f29
e8574e
+        timeout: 3600
e8574e
+        topology: *master_1repl
e8574e
+
e8574e
   fedora-29/test_vault:
e8574e
     requires: [fedora-29/build]
e8574e
     priority: 50
e8574e
diff --git a/ipatests/prci_definitions/nightly_rawhide.yaml b/ipatests/prci_definitions/nightly_rawhide.yaml
e8574e
index 301878467c..14433fcc0a 100644
e8574e
--- a/ipatests/prci_definitions/nightly_rawhide.yaml
e8574e
+++ b/ipatests/prci_definitions/nightly_rawhide.yaml
e8574e
@@ -75,6 +75,18 @@ jobs:
e8574e
         timeout: 3600
e8574e
         topology: *master_1repl
e8574e
 
e8574e
+  fedora-rawhide/external_ca_templates:
e8574e
+    requires: [fedora-rawhide/build]
e8574e
+    priority: 50
e8574e
+    job:
e8574e
+      class: RunPytest
e8574e
+      args:
e8574e
+        build_url: '{fedora-rawhide/build_url}'
e8574e
+        test_suite: test_integration/test_external_ca.py::TestExternalCAProfileScenarios
e8574e
+        template: *ci-master-frawhide
e8574e
+        timeout: 3600
e8574e
+        topology: *master_1repl
e8574e
+
e8574e
   fedora-rawhide/test_topologies:
e8574e
     requires: [fedora-rawhide/build]
e8574e
     priority: 50
e8574e
From 011c5283cec28ea4361eff5d2ee98da9cd3db41a Mon Sep 17 00:00:00 2001
e8574e
From: Fraser Tweedale <ftweedal@redhat.com>
e8574e
Date: Thu, 11 Jul 2019 20:27:02 +1000
e8574e
Subject: [PATCH] ci: add --external-ca-profile tests to gating
e8574e
e8574e
Part of: https://pagure.io/freeipa/issue/7548
e8574e
e8574e
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
e8574e
---
e8574e
 ipatests/prci_definitions/gating.yaml | 12 ++++++++++++
e8574e
 1 file changed, 12 insertions(+)
e8574e
e8574e
diff --git a/ipatests/prci_definitions/gating.yaml b/ipatests/prci_definitions/gating.yaml
e8574e
index 4d0107d956..81fa4bba10 100644
e8574e
--- a/ipatests/prci_definitions/gating.yaml
e8574e
+++ b/ipatests/prci_definitions/gating.yaml
e8574e
@@ -87,6 +87,18 @@ jobs:
e8574e
         timeout: 3600
e8574e
         topology: *master_1repl
e8574e
 
e8574e
+  fedora-30/external_ca_templates:
e8574e
+    requires: [fedora-30/build]
e8574e
+    priority: 50
e8574e
+    job:
e8574e
+      class: RunPytest
e8574e
+      args:
e8574e
+        build_url: '{fedora-30/build_url}'
e8574e
+        test_suite: test_integration/test_external_ca.py::TestExternalCAProfileScenarios
e8574e
+        template: *ci-master-f30
e8574e
+        timeout: 3600
e8574e
+        topology: *master_1repl
e8574e
+
e8574e
   fedora-30/test_topologies:
e8574e
     requires: [fedora-30/build]
e8574e
     priority: 50