>From d937b106ae27523133141a26ffa9837edccf749e Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Mon, 1 Sep 2014 12:53:28 -0400
Subject: [PATCH] Fix kra-connector-remove

The code to remove the connector from the pki CLI was found to be broken
because of invalid message type (partly due to void returns).

On uninstall, we need to remove the kra-connector from all relevant CA's
in the security domain.  The best way to do this is to keep kra-connector
info in LDAP, so that only one call is needed.  Until that change has
been made, we are adding a hack to remove the connector from all CA's
in the secutrity domain (if it exists).

Due to issues with proxy configurations, we will continue to use sslget
and a url-encoded-form version of the servlet.

In addition, it was found that when removing a KRA from a shared subsystem,
the updateDomainXML servlet was erroneously returning failure when it
was unsuccessful in removing a non-existent user from a group.

Ticket 1113
---
 .../server/ca/rest/KRAConnectorService.java        | 12 +--
 .../certsrv/system/KRAConnectorClient.java         |  6 +-
 .../certsrv/system/KRAConnectorResource.java       | 14 ++--
 .../com/netscape/cms/servlet/base/PKIService.java  |  3 +-
 .../cms/servlet/csadmin/UpdateDomainXML.java       |  8 +-
 .../python/pki/server/deployment/pkihelper.py      | 85 +++++++++++++++++-----
 .../python/pki/server/deployment/pkimessages.py    |  2 +
 .../python/pki/server/deployment/pkiparser.py      |  2 +-
 8 files changed, 95 insertions(+), 37 deletions(-)

diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/KRAConnectorService.java b/base/ca/src/org/dogtagpki/server/ca/rest/KRAConnectorService.java
index 93e571aa2934e54403d95292069f216fd905bfd4..0216558bf6fb64d6bdb2a13dbb04954015dd0824 100644
--- a/base/ca/src/org/dogtagpki/server/ca/rest/KRAConnectorService.java
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/KRAConnectorService.java
@@ -20,7 +20,6 @@ package org.dogtagpki.server.ca.rest;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Request;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
@@ -51,13 +50,14 @@ public class KRAConnectorService extends PKIService implements KRAConnectorResou
     private HttpServletRequest servletRequest;
 
     @Override
-    public void addConnector(KRAConnectorInfo info) {
+    public Response addConnector(KRAConnectorInfo info) {
 
         if (info == null) throw new BadRequestException("KRA connector info is null.");
 
         try {
             KRAConnectorProcessor processor = new KRAConnectorProcessor(getLocale(headers));
             processor.addConnector(info);
+            return createNoContentResponse();
         } catch (EBaseException e) {
             e.printStackTrace();
             throw new PKIException(e.getMessage());
@@ -65,7 +65,7 @@ public class KRAConnectorService extends PKIService implements KRAConnectorResou
     }
 
     @Override
-    public void removeConnector(String host, String port) {
+    public Response removeConnector(String host, String port) {
 
         if (host == null) throw new BadRequestException("KRA connector host is null.");
         if (port == null) throw new BadRequestException("KRA connector port is null.");
@@ -73,6 +73,7 @@ public class KRAConnectorService extends PKIService implements KRAConnectorResou
         try {
             KRAConnectorProcessor processor = new KRAConnectorProcessor(getLocale(headers));
             processor.removeConnector(host, port);
+            return createNoContentResponse();
         } catch (EBaseException e) {
             e.printStackTrace();
             throw new PKIException(e.getMessage());
@@ -80,9 +81,8 @@ public class KRAConnectorService extends PKIService implements KRAConnectorResou
     }
 
     @Override
-    public void addConnector(MultivaluedMap<String, String> form) {
-        KRAConnectorInfo info = new KRAConnectorInfo(form);
-        addConnector(info);
+    public Response removeConnectorForm(String host, String port) {
+        return removeConnector(host, port);
     }
 
     @Override
diff --git a/base/common/src/com/netscape/certsrv/system/KRAConnectorClient.java b/base/common/src/com/netscape/certsrv/system/KRAConnectorClient.java
index a90d370c77c8a68475e31ef02035ec1a9c229254..7abb1bde853e125a9cf6c16f0e192441e1946827 100644
--- a/base/common/src/com/netscape/certsrv/system/KRAConnectorClient.java
+++ b/base/common/src/com/netscape/certsrv/system/KRAConnectorClient.java
@@ -42,11 +42,13 @@ public class KRAConnectorClient extends Client {
     }
 
     public void addConnector(KRAConnectorInfo info) {
-        kraConnectorClient.addConnector(info);
+        Response response = kraConnectorClient.addConnector(info);
+        client.getEntity(response, Void.class);
     }
 
     public void removeConnector(String host, String port) {
-        kraConnectorClient.removeConnector(host, port);
+        Response response = kraConnectorClient.removeConnector(host, port);
+        client.getEntity(response, Void.class);
     }
 
     public KRAConnectorInfo getConnectorInfo() {
diff --git a/base/common/src/com/netscape/certsrv/system/KRAConnectorResource.java b/base/common/src/com/netscape/certsrv/system/KRAConnectorResource.java
index 7e624134d8ac14c9677d7f6c2a0e8ed9a31d6b43..2bf2f19580e5a97b0d2613a2df5032677d5b0484 100644
--- a/base/common/src/com/netscape/certsrv/system/KRAConnectorResource.java
+++ b/base/common/src/com/netscape/certsrv/system/KRAConnectorResource.java
@@ -22,8 +22,8 @@ import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 
 import org.jboss.resteasy.annotations.ClientResponseType;
@@ -41,17 +41,19 @@ public interface KRAConnectorResource {
 
     @POST
     @Path("add")
-    public void addConnector(KRAConnectorInfo info);
+    @ClientResponseType(entityType=Void.class)
+    public Response addConnector(KRAConnectorInfo info);
 
     @POST
-    @Path("add")
-    @Consumes({ MediaType.APPLICATION_FORM_URLENCODED })
-    public void addConnector(MultivaluedMap<String, String> form);
+    @Path("remove")
+    @ClientResponseType(entityType=Void.class)
+    public Response removeConnector(@QueryParam("host") String host, @QueryParam("port") String port);
 
     @POST
     @Path("remove")
     @Consumes({ MediaType.APPLICATION_FORM_URLENCODED })
-    public void removeConnector(@FormParam("host") String host, @FormParam("port") String port);
+    @ClientResponseType(entityType=Void.class)
+    public Response removeConnectorForm(@FormParam("host") String host, @FormParam("port") String port);
 
     @GET
     @ClientResponseType(entityType = KRAConnectorInfo.class)
diff --git a/base/server/cms/src/com/netscape/cms/servlet/base/PKIService.java b/base/server/cms/src/com/netscape/cms/servlet/base/PKIService.java
index b958791bbc30c96bcc65af737cc47130e2a37f3a..2fe78bf2a272509641f26517479f82f4dead845d 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/base/PKIService.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/base/PKIService.java
@@ -57,7 +57,8 @@ public class PKIService {
 
     public static List<MediaType> MESSAGE_FORMATS = Arrays.asList(
             MediaType.APPLICATION_XML_TYPE,
-            MediaType.APPLICATION_JSON_TYPE
+            MediaType.APPLICATION_JSON_TYPE,
+            MediaType.APPLICATION_FORM_URLENCODED_TYPE
     );
 
     public final static int MIN_FILTER_LENGTH = 3;
diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/UpdateDomainXML.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/UpdateDomainXML.java
index b783c359b5551f595736b312488c6b5fe35f4644..bd48f477dd35a0bdc2e32629f2faa0a3c3bb6af1 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/UpdateDomainXML.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/UpdateDomainXML.java
@@ -96,7 +96,9 @@ public class UpdateDomainXML extends CMSServlet {
             conn = connFactory.getConn();
             conn.delete(dn);
         } catch (LDAPException e) {
-            if (e.getLDAPResultCode() != LDAPException.NO_SUCH_OBJECT) {
+            int resultCode = e.getLDAPResultCode();
+            if ((resultCode != LDAPException.NO_SUCH_OBJECT) &&
+                (resultCode != LDAPException.NO_SUCH_ATTRIBUTE)){
                 status = FAILED;
                 CMS.debug("Failed to delete entry" + e.toString());
             }
@@ -129,7 +131,9 @@ public class UpdateDomainXML extends CMSServlet {
             conn = connFactory.getConn();
             conn.modify(dn, mod);
         } catch (LDAPException e) {
-            if (e.getLDAPResultCode() != LDAPException.NO_SUCH_OBJECT) {
+            int resultCode = e.getLDAPResultCode();
+            if ((resultCode != LDAPException.NO_SUCH_OBJECT) &&
+                (resultCode != LDAPException.NO_SUCH_ATTRIBUTE)){
                 status = FAILED;
                 CMS.debug("Failed to modify entry" + e.toString());
             }
diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py
index 823a2071170e37a91ac0ec012188e0d390270d92..afb7eea290c346d69cf2952e2844c2bdcd8ec4c4 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -27,6 +27,7 @@ import os
 import fileinput
 import random
 import re
+import requests.exceptions
 import shutil
 from shutil import Error, WindowsError
 import string
@@ -2649,20 +2650,9 @@ class KRAConnector:
                 self.mdict['pki_target_cs_cfg'])
             krahost = cs_cfg.get('service.machineName')
             kraport = cs_cfg.get('pkicreate.secure_port')
-            cahost = cs_cfg.get('cloning.ca.hostname')
-            caport = cs_cfg.get('cloning.ca.httpsport')
-            if cahost is None or\
-               caport is None:
-                config.pki_log.warning(
-                    log.PKIHELPER_KRACONNECTOR_UPDATE_FAILURE,
-                    extra=config.PKI_INDENTATION_LEVEL_2)
-                config.pki_log.error(
-                    log.PKIHELPER_UNDEFINED_CA_HOST_PORT,
-                    extra=config.PKI_INDENTATION_LEVEL_2)
-                if critical_failure:
-                    raise Exception(log.PKIHELPER_UNDEFINED_CA_HOST_PORT)
-                else:
-                    return
+            proxy_secure_port = cs_cfg.get('proxy.securePort', '')
+            if proxy_secure_port != '':
+                kraport = proxy_secure_port
 
             # retrieve subsystem nickname
             subsystemnick = cs_cfg.get('kra.cert.subsystem.nickname')
@@ -2703,9 +2693,49 @@ class KRAConnector:
                 else:
                     return
 
-            self.execute_using_sslget(
-                caport, cahost, subsystemnick,
-                token_pwd, krahost, kraport)
+            # Note: this is a hack to resolve Trac Ticket 1113
+            # We need to remove the KRA connector data from all relevant clones,
+            # but we have no way of easily identifying which instances are
+            # the right ones.  Instead, We will attempt to remove the KRA
+            # connector from all CAs in the security domain.
+            # The better - and long term solution is to store the connector
+            # configuration in LDAP so that updating one clone will
+            # automatically update the rest.
+            # TODO(alee): Fix this logic once we move connector data to LDAP
+
+            # get a list of all the CA's in the security domain
+            # noinspection PyBroadException
+            # pylint: disable-msg=W0703
+            sechost = cs_cfg.get('securitydomain.host')
+            secport = cs_cfg.get('securitydomain.httpsadminport')
+            try:
+                ca_list = self.get_ca_list_from_security_domain(
+                    sechost, secport)
+            except Exception as e:
+                config.pki_log.error(
+                    "unable to access security domain. Continuing .. " + str(e),
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                ca_list = []
+
+            for ca in ca_list:
+                ca_host = ca.hostname
+                ca_port = ca.secure_port
+
+                # catching all exceptions because we do not want to break if
+                # the auth is not successful or servers are down.  In the
+                # worst case, we will time out anyways.
+                # noinspection PyBroadException
+                # pylint: disable-msg=W0703
+                try:
+                    self.execute_using_sslget(
+                        ca_port, ca_host, subsystemnick,
+                        token_pwd, krahost, kraport)
+                except Exception:
+                    # ignore exceptions
+                    config.pki_log.warning(
+                        log.PKIHELPER_KRACONNECTOR_DEREGISTER_FAILURE_4,
+                        str(krahost), str(kraport), str(ca_host), str(ca_port),
+                        extra=config.PKI_INDENTATION_LEVEL_2)
 
         except subprocess.CalledProcessError as exc:
             config.pki_log.warning(
@@ -2719,6 +2749,24 @@ class KRAConnector:
                 raise
         return
 
+    @staticmethod
+    def get_ca_list_from_security_domain(sechost, secport):
+        sd_connection = pki.client.PKIConnection(
+            protocol='https',
+            hostname=sechost,
+            port=secport,
+            subsystem='ca')
+        sd = pki.system.SecurityDomainClient(sd_connection)
+        try:
+            info = sd.get_security_domain_info()
+        except requests.exceptions.HTTPError as e:
+            config.pki_log.info(
+                "unable to access security domain through REST interface.  " +
+                "Trying old interface. " + str(e),
+                extra=config.PKI_INDENTATION_LEVEL_2)
+            info = sd.get_old_security_domain_info()
+        return info.systems['CA'].hosts.values()
+
     def execute_using_pki(
             self, caport, cahost, subsystemnick,
             token_pwd, krahost, kraport, critical_failure=False):
@@ -2732,8 +2780,7 @@ class KRAConnector:
                    "ca-kraconnector-del", krahost, str(kraport)]
 
         output = subprocess.check_output(command,
-                                         stderr=subprocess.STDOUT,
-                                         shell=True)
+                                         stderr=subprocess.STDOUT)
 
         error = re.findall("ClientResponseFailure:(.*?)", output)
         if error:
diff --git a/base/server/python/pki/server/deployment/pkimessages.py b/base/server/python/pki/server/deployment/pkimessages.py
index 8b5ce3f2cf1a17f447b8d05e39daa9e7e4ce4dd6..1c21e8689824e8ff8b9f17f086dd8dd9de4bde0a 100644
--- a/base/server/python/pki/server/deployment/pkimessages.py
+++ b/base/server/python/pki/server/deployment/pkimessages.py
@@ -216,6 +216,8 @@ PKIHELPER_KRACONNECTOR_UPDATE_CONTACT = \
 PKIHELPER_KRACONNECTOR_UPDATE_FAILURE = "Failed to update KRA connector on CA"
 PKIHELPER_KRACONNECTOR_UPDATE_FAILURE_2 = \
     "Failed to update KRA connector for %s:%s"
+PKIHELPER_KRACONNECTOR_DEREGISTER_FAILURE_4 = \
+    "Failed to deregister KRA connector %s:%s from CA %s:%s"
 PKIHELPER_LINK_S_2 = "ln -s %s %s"
 PKIHELPER_MKDIR_1 = "mkdir -p %s"
 PKIHELPER_MODIFY_DIR_1 = "modifying '%s'"
diff --git a/base/server/python/pki/server/deployment/pkiparser.py b/base/server/python/pki/server/deployment/pkiparser.py
index 3b2a99c9fad8487bf11d9d18002361826fb3859f..1f6cbe0b4d50c921c14a3f70b942c677b17d2088 100644
--- a/base/server/python/pki/server/deployment/pkiparser.py
+++ b/base/server/python/pki/server/deployment/pkiparser.py
@@ -27,7 +27,7 @@ import ldap
 import logging
 import os
 import random
-import requests
+import requests.exceptions
 import string
 import subprocess
 import xml.etree.ElementTree as ET
-- 
1.9.3

