From 8d15bb599d3db62e52f5f7979a524855ecbe3749 Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Thu, 26 Jun 2014 23:12:27 +0800
Subject: [PATCH] Add ability to create database as subtree of existing tree

This patch adds the ability to create a subsystem that uses
an existing subtree to create the internal basedn.  This is useful
for instance, for IPA which will use the original o=ipaca as the
top level DN for a KRA, which will be situated at o=ipadrm, o=ipaca.

The patch also allows such a system to be cloned, but not to setup the
replication agreements, on the assumption that the data is already being
replicated at the top-level DN or some higher level.

The patch also contains some minor cleanups - removing unused imports and
removal of an invalid reference in the python code.

Ticket 1051
---
 .../server/ca/rest/CAInstallerService.java         |   4 +-
 .../certsrv/system/ConfigurationRequest.java       |  40 ++-
 .../src/com/netscape/cmstools/cli/MainCLI.java     |   1 -
 .../server/kra/rest/KRAInstallerService.java       |   2 +-
 .../cms/servlet/csadmin/ConfigurationUtils.java    | 387 +++++++++++++--------
 .../dogtagpki/server/rest/SystemConfigService.java |  28 +-
 .../netscape/cmscore/connector/HttpConnector.java  |   3 +-
 base/server/etc/default.cfg                        |   2 +
 .../python/pki/server/deployment/pkihelper.py      |   3 +-
 9 files changed, 295 insertions(+), 175 deletions(-)

diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/CAInstallerService.java b/base/ca/src/org/dogtagpki/server/ca/rest/CAInstallerService.java
index cc3c465859a3e63874f05591c7411549a6287ff6..3808d806af401885a516c58d023f7e406e25d994 100644
--- a/base/ca/src/org/dogtagpki/server/ca/rest/CAInstallerService.java
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/CAInstallerService.java
@@ -40,7 +40,7 @@ public class CAInstallerService extends SystemConfigService {
         super.finalizeConfiguration(request);
 
         try {
-             if (!request.getIsClone().equals("true")) {
+             if (!request.getIsClone()) {
                  ConfigurationUtils.updateNextRanges();
              }
 
@@ -50,7 +50,7 @@ public class CAInstallerService extends SystemConfigService {
         }
 
         try {
-            if (request.getIsClone().equals("true") && ConfigurationUtils.isSDHostDomainMaster(cs)) {
+            if (request.getIsClone() && ConfigurationUtils.isSDHostDomainMaster(cs)) {
                 // cloning a domain master CA, the clone is also master of its domain
                 cs.putString("securitydomain.host", CMS.getEEHost());
                 cs.putString("securitydomain.httpport", CMS.getEENonSSLPort());
diff --git a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java
index fc85d3d65a75ecbcd8600299b977c358b9c15a77..88c7bc987251b33720c7a0435946f659db506fac 100644
--- a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java
+++ b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java
@@ -55,6 +55,7 @@ public class ConfigurationRequest {
     private static final String DSHOST = "dsHost";
     private static final String DSPORT = "dsPort";
     private static final String BASEDN = "basedn";
+    private static final String CREATE_NEW_DB = "createNewDB";
     private static final String BINDDN = "binddn";
     private static final String DATABASE = "database";
     private static final String SECURECONN = "secureConn";
@@ -63,6 +64,7 @@ public class ConfigurationRequest {
     private static final String CLONE_REPLICATION_PORT = "cloneReplicationPort";
     private static final String REPLICATE_SCHEMA = "replicateSchema";
     private static final String REPLICATION_SECURITY = "replicationSecurity";
+    private static final String SETUP_REPLICATION = "setupReplication";
     private static final String ISSUING_CA = "issuingCa";
     private static final String BACKUP_KEYS = "backupKeys";
     private static final String BACKUP_FILE = "backupFile";
@@ -156,6 +158,9 @@ public class ConfigurationRequest {
     protected String baseDN;
 
     @XmlElement
+    protected String createNewDB;
+
+    @XmlElement
     protected String bindDN;
 
     @XmlElement
@@ -183,6 +188,9 @@ public class ConfigurationRequest {
     protected String replicationSecurity;
 
     @XmlElement
+    protected String setupReplication;
+
+    @XmlElement
     protected List<SystemCertData> systemCerts;
 
     @XmlElement
@@ -297,6 +305,7 @@ public class ConfigurationRequest {
         dsHost = form.getFirst(DSHOST);
         dsPort = form.getFirst(DSPORT);
         baseDN = form.getFirst(BASEDN);
+        createNewDB = form.getFirst(CREATE_NEW_DB);
         bindDN = form.getFirst(BINDDN);
         database = form.getFirst(DATABASE);
         secureConn = form.getFirst(SECURECONN);
@@ -305,6 +314,7 @@ public class ConfigurationRequest {
         cloneReplicationPort = form.getFirst(CLONE_REPLICATION_PORT);
         replicateSchema = form.getFirst(REPLICATE_SCHEMA);
         replicationSecurity = form.getFirst(REPLICATION_SECURITY);
+        setupReplication = form.getFirst(SETUP_REPLICATION);
         //TODO - figure out how to get the cert requests
         issuingCA = form.getFirst(ISSUING_CA);
         backupFile = form.getFirst(BACKUP_FILE);
@@ -409,8 +419,8 @@ public class ConfigurationRequest {
         this.securityDomainPassword = securityDomainPassword;
     }
 
-    public String getIsClone() {
-        return isClone;
+    public boolean getIsClone() {
+        return (isClone!= null) && (isClone.equalsIgnoreCase("true"));
     }
 
     public void setIsClone(String isClone) {
@@ -621,6 +631,18 @@ public class ConfigurationRequest {
         this.replicationSecurity = replicationSecurity;
     }
 
+    public boolean getSetupReplication() {
+        // default to true
+        if (setupReplication == null) {
+            return true;
+        }
+        return setupReplication.equalsIgnoreCase("true");
+    }
+
+    public void setSetupReplication(String setupReplication) {
+        this.setupReplication = setupReplication;
+    }
+
     /**
      * @return the database
      */
@@ -963,6 +985,18 @@ public class ConfigurationRequest {
         this.sharedDBUserDN = sharedDBUserDN;
     }
 
+    public boolean getCreateNewDB() {
+        // default to true
+        if (createNewDB == null) {
+            return true;
+        }
+        return createNewDB.equalsIgnoreCase("true");
+    }
+
+    public void setCreateNewDB(String createNewDB) {
+        this.createNewDB = createNewDB;
+    }
+
     @Override
     public String toString() {
         return "ConfigurationRequest [pin=XXXX" +
@@ -1020,6 +1054,8 @@ public class ConfigurationRequest {
                ", generateSubsystemCert=" + generateSubsystemCert +
                ", sharedDB=" +  sharedDB +
                ", sharedDBUserDN=" + sharedDBUserDN +
+               ", createNewDB=" + createNewDB +
+               ", setupReplication=" + setupReplication +
                "]";
     }
 
diff --git a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
index 9943d18338deb9680997013c8d4859736e1bba71..e83d440da745bd31b084e0c072a5c094a3f749e6 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
@@ -27,7 +27,6 @@ import java.util.HashSet;
 
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
-import org.apache.commons.cli.Options;
 import org.apache.commons.lang.StringUtils;
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.CryptoManager.NotInitializedException;
diff --git a/base/kra/src/org/dogtagpki/server/kra/rest/KRAInstallerService.java b/base/kra/src/org/dogtagpki/server/kra/rest/KRAInstallerService.java
index e2587237a9b880d58b585ce5f29871c1a145b8c6..762bb0d5c70c9f562e2195052a49a226c386a4b5 100644
--- a/base/kra/src/org/dogtagpki/server/kra/rest/KRAInstallerService.java
+++ b/base/kra/src/org/dogtagpki/server/kra/rest/KRAInstallerService.java
@@ -54,7 +54,7 @@ public class KRAInstallerService extends SystemConfigService {
         }
 
         try {
-             if (!request.getIsClone().equals("true")) {
+             if (!request.getIsClone()) {
                  ConfigurationUtils.updateNextRanges();
              }
 
diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
index e486555a911e69c5796b87c257bf9d34bf0d4271..94a74c5d63f31def1349ec1550865c2aa11bed01 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
@@ -24,8 +24,8 @@ import java.io.CharConversionException;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.io.InputStreamReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.math.BigInteger;
 import java.net.ConnectException;
@@ -47,6 +47,7 @@ import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.security.interfaces.RSAPublicKey;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.StringTokenizer;
@@ -343,12 +344,12 @@ public class ConfigurationUtils {
 
         // Ignore the "UNTRUSTED_ISSUER" validity status
         // during PKI instance creation since we are
-        // utilizing an untrusted temporary CA cert.
+        // utilizing an untrusted temporary CA certificate.
         connection.addIgnoredCertStatus(SSLCertificateApprovalCallback.ValidityStatus.UNTRUSTED_ISSUER);
 
         // Ignore the "CA_CERT_INVALID" validity status
         // during PKI instance creation since we are
-        // utilizing an untrusted temporary CA cert.
+        // utilizing an untrusted temporary CA certificate.
         connection.addIgnoredCertStatus(SSLCertificateApprovalCallback.ValidityStatus.CA_CERT_INVALID);
 
         AccountClient accountClient = new AccountClient(client, "ca");
@@ -1209,7 +1210,10 @@ public class ConfigurationUtils {
         IConfigStore cs = CMS.getConfigStore();
         String baseDN = cs.getString("internaldb.basedn");
         String database = cs.getString("internaldb.database", "");
-        String remove = cs.getString("preop.database.removeData", "false");
+        String select = cs.getString("preop.subsystem.select", "");
+        boolean remove = cs.getBoolean("preop.database.removeData", false);
+        boolean createNewDB = cs.getBoolean("preop.database.createNewDB", true);
+        boolean setupReplication = cs.getBoolean("preop.database.setupReplication", true);
 
         IConfigStore dbCfg = cs.getSubStore("internaldb");
         ILdapConnFactory dbFactory = CMS.getLdapBoundConnFactory();
@@ -1217,178 +1221,74 @@ public class ConfigurationUtils {
         LDAPConnection conn = dbFactory.getConn();
 
         try {
-            // check if base entry already exists
-            LDAPEntry baseEntry = null;
-            try {
-                CMS.debug("populateDB: Checking subtree " + baseDN + ".");
-                baseEntry = conn.read(baseDN);
-                CMS.debug("populateDB: Subtree " + baseDN + " already exists.");
+            if (createNewDB) {
+                // check if base entry already exists
+                LDAPEntry baseEntry = getBaseDNEntry(baseDN, remove, conn);
 
-                if (remove.equals("false")) {
-                    throw new EBaseException("The base DN (" + baseDN + ") has already been used. " +
-                            "Please confirm to remove and reuse this base DN.");
-                }
+                // check if mapping entry already exists
+                String mappingDN = "cn=\"" + baseDN + "\",cn=mapping tree, cn=config";
+                LDAPEntry mappingEntry = getMappingDNEntry(baseDN, remove, conn, mappingDN);
 
-            } catch (LDAPException e) {
-                if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT) {
-                    CMS.debug("populateDB: Subtree " + baseDN + " does not exist.");
-                } else {
-                    CMS.debug("populateDB: " + e);
-                    throw new EBaseException("Failed to determine if base DN exists: " + e);
-                }
-            }
+                // check if the database already exists
+                String databaseDN = "cn=" + LDAPUtil.escapeRDNValue(database) +
+                        ",cn=ldbm database, cn=plugins, cn=config";
+                LDAPEntry databaseEntry = getDatabaseEntry(database, remove, conn, databaseDN);
 
-            // check if mapping entry already exists
-            String mappingDN = "cn=\"" + baseDN + "\",cn=mapping tree, cn=config";
-            LDAPEntry mappingEntry = null;
-            try {
-                CMS.debug("populateDB: Checking subtree " + baseDN + " mapping.");
-                mappingEntry = conn.read(mappingDN);
-                CMS.debug("populateDB: Mapping for subtree " + baseDN + " already exists.");
+                // check if database is used by another subtree
+                confirmNoConflictingMappingsForDB(baseDN, database, conn);
 
-                if (remove.equals("false")) {
-                    throw new EBaseException("The base DN (" + baseDN + ") has already been used. " +
-                            "Please confirm to remove and reuse this base DN.");
+                // delete mapping entry
+                if (mappingEntry != null) {
+                    CMS.debug("populateDB: Deleting mapping " + mappingDN);
+                    deleteSubtree(conn, mappingDN);
                 }
 
-            } catch (LDAPException e) {
-                if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT) {
-                    CMS.debug("populateDB: Mapping for subtree " + baseDN + " does not exist.");
-                } else {
-                    CMS.debug("populateDB: " + e);
-                    throw new EBaseException("Failed to determine if mapping entry exists: " + e);
+                // delete the database including the subtree data
+                if (databaseEntry != null) {
+                    CMS.debug("populateDB: Deleting database " + database);
+                    deleteSubtree(conn, databaseDN);
                 }
-            }
 
-            // check if the database already exists
-            String databaseDN = "cn=" + LDAPUtil.escapeRDNValue(database) + ",cn=ldbm database, cn=plugins, cn=config";
-            LDAPEntry databaseEntry = null;
-            try {
-                CMS.debug("populateDB: Checking database " + database + ".");
-                databaseEntry = conn.read(databaseDN);
-                CMS.debug("populateDB: Database " + database + " already exists.");
-
-                if (remove.equals("false")) {
-                    throw new EBaseException("The database (" + database + ") already exists. " +
-                            "Please confirm to remove and reuse this database.");
+                // delete subtree data in case it's stored by another database
+                if (baseEntry != null) {
+                    CMS.debug("populateDB: Deleting subtree " + baseDN);
+                    deleteSubtree(conn, baseDN);
                 }
 
-            } catch (LDAPException e) {
-                if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT) {
-                    CMS.debug("populateDB: Database " + database + " does not exist.");
+                createDatabase(baseDN, database, conn, databaseDN);
+                createDatabaseMapping(baseDN, database, conn, mappingDN);
+                createBaseDN(baseDN, conn);
+            } else {
+                if (select.equals("clone") && !setupReplication) {
+                    // cloning a system where the database is a subtree of an existing tree
+                    // and not setting up replication agreements.  The assumption then is
+                    // that the data is already replicated.  No need to set up the base DN
                 } else {
-                    CMS.debug("populateDB: " + e);
-                    throw new EBaseException("Failed to determine if database exists: " + e);
-                }
-            }
-
-            // check if database is used by another subtree
-            try {
-                CMS.debug("populateDB: Checking other subtrees using database " + database + ".");
-                LDAPSearchResults res = conn.search(
-                        "cn=mapping tree, cn=config", LDAPConnection.SCOPE_ONE,
-                        "nsslapd-backend=" + LDAPUtil.escapeFilter(database),
-                        null, false, (LDAPSearchConstraints)null);
-
-                while (res.hasMoreElements()) {
-                    LDAPEntry entry = res.next();
-
-                    LDAPAttribute cn = entry.getAttribute("cn");
-                    String dn = cn.getStringValueArray()[0];
-                    if (LDAPDN.equals(baseDN, dn)) continue;
-
-                    CMS.debug("populateDB: Database " + database + " is used by " + dn + ".");
-                    throw new EBaseException("The database (" + database + ") is used by another base DN. " +
-                            "Please use a different database name.");
-                }
-
-                CMS.debug("populateDB: Database " + database + " is not used by another subtree.");
+                    // check if base entry already exists
+                    LDAPEntry baseEntry = getBaseDNEntry(baseDN, remove, conn);
 
-            } catch (LDAPException e) {
-                CMS.debug("populateDB: " + e);
-                throw new EBaseException("Failed to check database mapping: " + e);
-            }
-
-            // delete mapping entry
-            if (mappingEntry != null) {
-                CMS.debug("populateDB: Deleting mapping " + mappingDN);
-                deleteSubtree(conn, mappingDN);
-            }
-
-            // delete the database including the subtree data
-            if (databaseEntry != null) {
-                CMS.debug("populateDB: Deleting database " + database);
-                deleteSubtree(conn, databaseDN);
-            }
-
-            // delete subtree data in case it's stored by another database
-            if (baseEntry != null) {
-                CMS.debug("populateDB: Deleting subtree " + baseDN);
-                deleteSubtree(conn, baseDN);
-            }
+                    // delete subtree data in case it's stored by another database
+                    if (baseEntry != null) {
+                        CMS.debug("populateDB: Deleting subtree " + baseDN);
+                        deleteSubtree(conn, baseDN);
+                    }
 
-            // create database
-            try {
-                LDAPAttributeSet attrs = new LDAPAttributeSet();
-                String oc[] = { "top", "extensibleObject", "nsBackendInstance" };
-                attrs.add(new LDAPAttribute("objectClass", oc));
-                attrs.add(new LDAPAttribute("cn", database));
-                attrs.add(new LDAPAttribute("nsslapd-suffix", baseDN));
-                LDAPEntry entry = new LDAPEntry(databaseDN, attrs);
-                conn.add(entry);
-            } catch (LDAPException e) {
-                CMS.debug("populateDB: Unable to add " + databaseDN + ": " + e);
-                throw new EBaseException("Failed to create the database: " + e, e);
-            }
-
-            // define subtree
-            try {
-                LDAPAttributeSet attrs = new LDAPAttributeSet();
-                String oc2[] = { "top", "extensibleObject", "nsMappingTree" };
-                attrs.add(new LDAPAttribute("objectClass", oc2));
-                attrs.add(new LDAPAttribute("cn", baseDN));
-                attrs.add(new LDAPAttribute("nsslapd-backend", database));
-                attrs.add(new LDAPAttribute("nsslapd-state", "Backend"));
-                LDAPEntry entry = new LDAPEntry(mappingDN, attrs);
-                conn.add(entry);
-            } catch (LDAPException e) {
-                CMS.debug("populateDB: Unable to add " + mappingDN + ": " + e);
-                throw new EBaseException("Failed to create subtree: " + e, e);
-            }
-
-            // create root entry
-            try {
-                CMS.debug("Creating base DN: " + baseDN);
-                String dns3[] = LDAPDN.explodeDN(baseDN, false);
-                StringTokenizer st = new StringTokenizer(dns3[0], "=");
-                String n = st.nextToken();
-                String v = st.nextToken();
-                LDAPAttributeSet attrs = new LDAPAttributeSet();
-                String oc3[] = { "top", "domain" };
-                if (n.equals("o")) {
-                    oc3[1] = "organization";
-                } else if (n.equals("ou")) {
-                    oc3[1] = "organizationalUnit";
+                    checkParentExists(baseDN, conn);
+                    createBaseDN(baseDN, conn);
                 }
-                attrs.add(new LDAPAttribute("objectClass", oc3));
-                attrs.add(new LDAPAttribute(n, v));
-
-                LDAPEntry entry = new LDAPEntry(baseDN, attrs);
-                conn.add(entry);
-            } catch (LDAPException e) {
-                CMS.debug("populateDB: Unable to add " + baseDN + ": " + e);
-                throw new EBaseException("Failed to create root entry: " + e, e);
             }
 
             try {
-                String select = cs.getString("preop.subsystem.select", "");
                 if (select.equals("clone")) {
                     // in most cases, we want to replicate the schema and therefore
                     // NOT add it here.  We provide this option though in case the
                     // clone already has schema and we want to replicate back to the
                     // master.
+                    // On the other hand, if we are not setting up replication, then we
+                    // are assuming that replication is already taken care of, and schema
+                    // has already been replicated.  No need to add.
                     boolean replicateSchema = cs.getBoolean("preop.internaldb.replicateSchema", true);
-                    if (! replicateSchema) {
+                    if (!replicateSchema || !setupReplication) {
                         importLDIFS("preop.internaldb.schema.ldif", conn);
                     }
                     importLDIFS("preop.internaldb.ldif", conn);
@@ -1407,12 +1307,191 @@ public class ConfigurationUtils {
                 CMS.debug("Failed to import ldif files: " + e);
                 throw new EBaseException("Failed to import ldif files: " + e, e);
             }
-
         } finally {
             releaseConnection(conn);
         }
     }
 
+    private static void createBaseDN(String baseDN, LDAPConnection conn) throws EBaseException {
+        try {
+            CMS.debug("Creating base DN: " + baseDN);
+            String dns3[] = LDAPDN.explodeDN(baseDN, false);
+            StringTokenizer st = new StringTokenizer(dns3[0], "=");
+            String n = st.nextToken();
+            String v = st.nextToken();
+            LDAPAttributeSet attrs = new LDAPAttributeSet();
+            String oc3[] = { "top", "domain" };
+            if (n.equals("o")) {
+                oc3[1] = "organization";
+            } else if (n.equals("ou")) {
+                oc3[1] = "organizationalUnit";
+            }
+            attrs.add(new LDAPAttribute("objectClass", oc3));
+            attrs.add(new LDAPAttribute(n, v));
+
+            LDAPEntry entry = new LDAPEntry(baseDN, attrs);
+            conn.add(entry);
+        } catch (LDAPException e) {
+            CMS.debug("createBaseDN: Unable to add " + baseDN + ": " + e);
+            throw new EBaseException("Failed to create root entry: " + e, e);
+        }
+    }
+
+    private static void createDatabaseMapping(String baseDN, String database, LDAPConnection conn, String mappingDN)
+            throws EBaseException {
+        try {
+            LDAPAttributeSet attrs = new LDAPAttributeSet();
+            String oc2[] = { "top", "extensibleObject", "nsMappingTree" };
+            attrs.add(new LDAPAttribute("objectClass", oc2));
+            attrs.add(new LDAPAttribute("cn", baseDN));
+            attrs.add(new LDAPAttribute("nsslapd-backend", database));
+            attrs.add(new LDAPAttribute("nsslapd-state", "Backend"));
+            LDAPEntry entry = new LDAPEntry(mappingDN, attrs);
+            conn.add(entry);
+        } catch (LDAPException e) {
+            CMS.debug("createDatabaseMapping: Unable to add " + mappingDN + ": " + e);
+            throw new EBaseException("Failed to create subtree: " + e, e);
+        }
+    }
+
+    private static void createDatabase(String baseDN, String database, LDAPConnection conn, String databaseDN)
+            throws EBaseException {
+        try {
+            LDAPAttributeSet attrs = new LDAPAttributeSet();
+            String oc[] = { "top", "extensibleObject", "nsBackendInstance" };
+            attrs.add(new LDAPAttribute("objectClass", oc));
+            attrs.add(new LDAPAttribute("cn", database));
+            attrs.add(new LDAPAttribute("nsslapd-suffix", baseDN));
+            LDAPEntry entry = new LDAPEntry(databaseDN, attrs);
+            conn.add(entry);
+        } catch (LDAPException e) {
+            CMS.debug("createDatabase: Unable to add " + databaseDN + ": " + e);
+            throw new EBaseException("Failed to create the database: " + e, e);
+        }
+    }
+
+    private static void confirmNoConflictingMappingsForDB(String baseDN, String database, LDAPConnection conn)
+            throws EBaseException {
+        try {
+            CMS.debug("confirmMappings: Checking other subtrees using database " + database + ".");
+            LDAPSearchResults res = conn.search(
+                    "cn=mapping tree, cn=config", LDAPConnection.SCOPE_ONE,
+                    "nsslapd-backend=" + LDAPUtil.escapeFilter(database),
+                    null, false, (LDAPSearchConstraints)null);
+
+            while (res.hasMoreElements()) {
+                LDAPEntry entry = res.next();
+
+                LDAPAttribute cn = entry.getAttribute("cn");
+                String dn = cn.getStringValueArray()[0];
+                if (LDAPDN.equals(baseDN, dn)) continue;
+
+                CMS.debug("confirmMappings: Database " + database + " is used by " + dn + ".");
+                throw new EBaseException("The database (" + database + ") is used by another base DN. " +
+                        "Please use a different database name.");
+            }
+
+            CMS.debug("confirmMappings: Database " + database + " is not used by another subtree.");
+
+        } catch (LDAPException e) {
+            CMS.debug("populateDB: " + e);
+            throw new EBaseException("Failed to check database mapping: " + e);
+        }
+    }
+
+    private static LDAPEntry getDatabaseEntry(String database, boolean remove, LDAPConnection conn, String databaseDN)
+            throws EBaseException {
+        LDAPEntry databaseEntry = null;
+        try {
+            CMS.debug("getDatabaseEntry: Checking database " + database + ".");
+            databaseEntry = conn.read(databaseDN);
+            CMS.debug("getDatabaseEntry: Database " + database + " already exists.");
+
+            if (!remove) {
+                throw new EBaseException("The database (" + database + ") already exists. " +
+                        "Please confirm to remove and reuse this database.");
+            }
+
+        } catch (LDAPException e) {
+            if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT) {
+                CMS.debug("getDatabaseEntry: Database " + database + " does not exist.");
+            } else {
+                CMS.debug("getDatabaseEntry: " + e);
+                throw new EBaseException("Failed to determine if database exists: " + e);
+            }
+        }
+        return databaseEntry;
+    }
+
+    private static LDAPEntry getMappingDNEntry(String baseDN, boolean remove, LDAPConnection conn, String mappingDN)
+            throws EBaseException {
+        LDAPEntry mappingEntry = null;
+        try {
+            CMS.debug("getMappingDNEntry: Checking subtree " + baseDN + " mapping.");
+            mappingEntry = conn.read(mappingDN);
+            CMS.debug("getMapppingDNEntry: Mapping for subtree " + baseDN + " already exists.");
+
+            if (!remove) {
+                throw new EBaseException("The base DN (" + baseDN + ") has already been used. " +
+                        "Please confirm to remove and reuse this base DN.");
+            }
+
+        } catch (LDAPException e) {
+            if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT) {
+                CMS.debug("getMappingDNEntry: Mapping for subtree " + baseDN + " does not exist.");
+            } else {
+                CMS.debug("getMappingDNEntry: " + e);
+                throw new EBaseException("Failed to determine if mapping entry exists: " + e);
+            }
+        }
+        return mappingEntry;
+    }
+
+    private static LDAPEntry getBaseDNEntry(String baseDN, boolean remove, LDAPConnection conn) throws EBaseException {
+        LDAPEntry baseEntry = null;
+        try {
+            CMS.debug("getBaseDNEntry: Checking subtree " + baseDN + ".");
+            baseEntry = conn.read(baseDN);
+            CMS.debug("getBaseDNEntry: Subtree " + baseDN + " already exists.");
+
+            if (!remove) {
+                throw new EBaseException("The base DN (" + baseDN + ") has already been used. " +
+                        "Please confirm to remove and reuse this base DN.");
+            }
+
+        } catch (LDAPException e) {
+            if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT) {
+                CMS.debug("getBaseDNEntry: Subtree " + baseDN + " does not exist.");
+            } else {
+                CMS.debug("getBaseDNEntry: " + e);
+                throw new EBaseException("Failed to determine if base DN exists: " + e);
+            }
+        }
+        return baseEntry;
+    }
+
+    private static void checkParentExists(String baseDN, LDAPConnection conn) throws EBaseException {
+        String[] dns = LDAPDN.explodeDN(baseDN, false);
+        if (dns.length == 1 ) {
+            CMS.debug("checkParentExists: no parent in baseDN: " + baseDN);
+            throw new EBaseException("Invalid BaseDN. No parent DN in " + baseDN);
+        }
+        String parentDN = Arrays.toString(Arrays.copyOfRange(dns, 1, dns.length));
+        parentDN = parentDN.substring(1,parentDN.length() -1);
+        try {
+            CMS.debug("checkParentExists: Checking parent " + parentDN + ".");
+            conn.read(parentDN);
+            CMS.debug("checkParentExists: Parent entry " + parentDN + " exists.");
+        } catch (LDAPException e) {
+            if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT) {
+                throw new EBaseException("Parent entry " + parentDN + "does not exist");
+            } else {
+                CMS.debug("checkParentExists: " + e);
+                throw new EBaseException("Failed to determine if base DN exists: " + e);
+            }
+        }
+    }
+
     public static void importLDIFS(String param, LDAPConnection conn) throws IOException, EPropertyNotFound,
             EBaseException {
         IConfigStore cs = CMS.getConfigStore();
diff --git a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java
index 3aad2a232843e9575fb6daecd86f2f825fbbae50..1f373f5810c76b0d4e44f863bf78a7ab529c95d2 100644
--- a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java
+++ b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java
@@ -449,7 +449,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
                     ConfigurationUtils.handleCertRequest(cs, tag, cert);
                 }
 
-                if (request.getIsClone().equals("true")) {
+                if (request.getIsClone()) {
                     ConfigurationUtils.updateCloneConfig();
                 }
 
@@ -538,7 +538,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
     }
 
     public void configureAdministrator(ConfigurationRequest data, ConfigurationResponse response) {
-        if (!data.getIsClone().equals("true")) {
+        if (!data.getIsClone()) {
             try {
                 X509CertImpl admincerts[] = new X509CertImpl[1];
                 ConfigurationUtils.createAdmin(data.getAdminUID(), data.getAdminEmail(),
@@ -603,13 +603,15 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
         cs.putString("internaldb.database", data.getDatabase());
         cs.putString("internaldb.basedn", data.getBaseDN());
         cs.putString("internaldb.ldapauth.bindDN", data.getBindDN());
-        cs.putString("internaldb.ldapconn.secureConn", (data.getSecureConn().equals("on") ? "true" : "false"));
+        cs.putBoolean("internaldb.ldapconn.secureConn", data.getSecureConn().equals("on"));
         cs.putString("preop.database.removeData", data.getRemoveData());
+        cs.putBoolean("preop.database.createNewDB", data.getCreateNewDB());
+        cs.putBoolean("preop.database.setupReplication", data.getSetupReplication());
     }
 
     public void initializeDatabase(ConfigurationRequest data) {
 
-        if (data.getIsClone().equals("true")) {
+        if (data.getIsClone() && data.getSetupReplication()) {
             String masterhost = "";
             String masterport = "";
             String masterbasedn = "";
@@ -663,7 +665,9 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
             passwordFile = cs.getString("passwordFile");
             psStore = CMS.createFileConfigStore(passwordFile);
             psStore.putString("internaldb", data.getBindpwd());
-            psStore.putString("replicationdb", replicationpwd);
+            if (data.getSetupReplication()) {
+                psStore.putString("replicationdb", replicationpwd);
+            }
             psStore.commit(false);
 
             if (!data.getStepTwo()) {
@@ -676,7 +680,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
                 }
                 cs.commit(false);
 
-                if (data.getIsClone().equals("true")) {
+                if (data.getIsClone() && data.getSetupReplication()) {
                     CMS.debug("Start setting up replication.");
                     ConfigurationUtils.setupReplication();
                 }
@@ -692,7 +696,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
     }
 
     public void configureHierarchy(ConfigurationRequest data) {
-        if (csType.equals("CA") && data.getIsClone().equals("false")) {
+        if (csType.equals("CA") && !data.getIsClone()) {
             if (data.getHierarchy().equals("root")) {
                 cs.putString("preop.hierarchy.select", "root");
                 cs.putString("hierarchy.select", "Root");
@@ -736,7 +740,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
                     cs.putInteger("preop.ca.httpsport", port);
                     cs.putInteger("preop.ca.httpsadminport", admin_port);
 
-                    if (!data.getIsClone().equals("true")) {
+                    if (!data.getIsClone()) {
                         ConfigurationUtils.importCertChain(host, admin_port, "/ca/admin/ca/getCertChain", "ca");
                     }
 
@@ -909,7 +913,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
         cs.putString("preop.subsystem.name", request.getSubsystemName());
 
         // is this a clone of another subsystem?
-        if (request.getIsClone().equals("false")) {
+        if (!request.getIsClone()) {
             cs.putString("preop.subsystem.select", "new");
             cs.putString("subsystem.select", "New");
 
@@ -971,7 +975,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
             if (!csType.equals("KRA")) {
                 throw new BadRequestException("Stand-alone PKI " + csType + " subsystems are currently NOT supported!");
             }
-            if ((data.getIsClone() != null) && (data.getIsClone().equals("true"))) {
+            if (data.getIsClone()) {
                 throw new BadRequestException("A stand-alone PKI subsystem cannot be a clone");
             }
         }
@@ -1017,7 +1021,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
             throw new BadRequestException("Invalid or no subsystem name provided");
         }
 
-        if ((data.getIsClone() != null) && (data.getIsClone().equals("true"))) {
+        if (data.getIsClone()) {
             String cloneUri = data.getCloneUri();
             if (cloneUri == null) {
                 throw new BadRequestException("Clone selected, but no clone URI provided");
@@ -1115,7 +1119,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
             throw new BadRequestException("Hierarchy is required for CA, not provided");
         }
 
-        if (data.getIsClone().equals("false")) {
+        if (!data.getIsClone()) {
             if ((data.getAdminUID() == null) || (data.getAdminUID().length() == 0)) {
                 throw new BadRequestException("Admin UID not provided");
             }
diff --git a/base/server/cmscore/src/com/netscape/cmscore/connector/HttpConnector.java b/base/server/cmscore/src/com/netscape/cmscore/connector/HttpConnector.java
index 2a51b22656b1a8ab96bb5a9aef3087ef2321697a..9b6f8dd9361aad5fcd8248af8795a55559ff31e6 100644
--- a/base/server/cmscore/src/com/netscape/cmscore/connector/HttpConnector.java
+++ b/base/server/cmscore/src/com/netscape/cmscore/connector/HttpConnector.java
@@ -28,13 +28,12 @@ import com.netscape.certsrv.connector.IHttpConnection;
 import com.netscape.certsrv.connector.IHttpPKIMessage;
 import com.netscape.certsrv.connector.IRemoteAuthority;
 import com.netscape.certsrv.connector.IResender;
-import com.netscape.certsrv.logging.ILogger;
 import com.netscape.certsrv.request.IRequest;
 import com.netscape.certsrv.request.RequestId;
 import com.netscape.certsrv.request.RequestStatus;
+import com.netscape.cmsutil.http.HttpResponse;
 import com.netscape.cmsutil.http.JssSSLSocketFactory;
 import com.netscape.cmsutil.net.ISocketFactory;
-import com.netscape.cmsutil.http.HttpResponse;
 
 public class HttpConnector implements IConnector {
     protected IAuthority mSource = null;
diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg
index 87c197baa59f2880c8079741f3daceaf4326cfcd..7ecbf10af72bed8d9465dc19e713d552024fc2ee 100644
--- a/base/server/etc/default.cfg
+++ b/base/server/etc/default.cfg
@@ -87,6 +87,7 @@ pki_client_database_purge=True
 pki_client_dir=%(home_dir)s/.dogtag/%(pki_instance_name)s
 pki_client_pkcs12_password=
 pki_ds_bind_dn=cn=Directory Manager
+pki_ds_create_new_db=True
 pki_ds_ldap_port=389
 pki_ds_ldaps_port=636
 pki_ds_password=
@@ -212,6 +213,7 @@ pki_clone_replicate_schema=True
 pki_clone_replication_master_port=
 pki_clone_replication_clone_port=
 pki_clone_replication_security=None
+pki_clone_setup_replication=True
 pki_clone_uri=
 pki_enable_access_log=True
 pki_enable_java_debugger=False
diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py
index 6e230ba1a7b3e0927c441f34576fc723329224d3..dd10b8d771a5c84c3d26f1f8af56e372f076b028 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -1359,7 +1359,6 @@ class Directory:
                     config.pki_log.debug(
                         log.PKIHELPER_IS_A_DIRECTORY_1, name,
                         extra=config.PKI_INDENTATION_LEVEL_3)
-                    name = os.path.join(root, name)
                     # chmod <dir_perms> <name>
                     config.pki_log.debug(log.PKIHELPER_CHMOD_2,
                                          dir_perms, name,
@@ -3930,6 +3929,7 @@ class ConfigClient:
         if self.mdict['pki_clone_replication_clone_port']:
             data.cloneReplicationPort = \
                 self.mdict['pki_clone_replication_clone_port']
+        data.setupReplication = self.mdict['pki_clone_setup_replication']
 
     def set_hierarchy_parameters(self, data):
         if self.subsystem == "CA":
@@ -3963,6 +3963,7 @@ class ConfigClient:
         data.bindDN = self.mdict['pki_ds_bind_dn']
         data.database = self.mdict['pki_ds_database']
         data.bindpwd = self.mdict['pki_ds_password']
+        data.createNewDB = self.mdict['pki_ds_create_new_db']
         if config.str2bool(self.mdict['pki_ds_remove_data']):
             data.removeData = "true"
         else:
-- 
1.9.3

