>From c4ac152d4b037dfb8cce0e81ab6f2f9a5181d41c Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Wed, 19 Feb 2014 09:43:24 -0500
Subject: [PATCH] Add methods to getKeyInfo and change key status

---
 base/common/python/pki/__init__.py                 |  4 ++
 base/common/python/pki/key.py                      | 36 +++++++++----
 base/common/python/pki/kraclient.py                |  2 +-
 base/common/python/pki/systemcert.py               |  2 +-
 .../src/com/netscape/certsrv/key/KeyClient.java    |  9 ++++
 .../netscape/certsrv/key/KeyNotFoundException.java | 62 ++++++++++++++++++++++
 .../src/com/netscape/certsrv/key/KeyResource.java  | 14 +++++
 base/kra/functional/drmtest.py                     | 28 +++++++---
 .../src/com/netscape/cms/servlet/test/DRMTest.java | 21 ++++++++
 .../com/netscape/cms/servlet/key/KeyService.java   | 41 ++++++++++++++
 .../src/com/netscape/cmscore/dbs/DBSSession.java   |  3 ++
 base/server/sbin/pkispawn                          |  1 -
 12 files changed, 203 insertions(+), 20 deletions(-)
 create mode 100644 base/common/src/com/netscape/certsrv/key/KeyNotFoundException.java

diff --git a/base/common/python/pki/__init__.py b/base/common/python/pki/__init__.py
index 4b18ea0ed25817272f2879790201040feccd6609..e9802a4ba44fb7854a79a5a06eb415f845130790 100644
--- a/base/common/python/pki/__init__.py
+++ b/base/common/python/pki/__init__.py
@@ -185,6 +185,9 @@ class CertNotFoundException(ResourceNotFoundException):
 class GroupNotFoundException(ResourceNotFoundException):
     ''' Group Not Found Exception: return code = 404 '''
 
+class KeyNotFoundException(ResourceNotFoundException):
+    ''' Key Not Found Exception: return code 404 '''
+
 class ProfileNotFoundException(ResourceNotFoundException):
     ''' Profile Not Found Exception: return code = 404 '''
 
@@ -202,6 +205,7 @@ EXCEPTION_MAPPINGS = {
     "com.netscape.certsrv.base.ResourceNotFoundException": "pki.ResourceNotFoundException",
     "com.netscape.certsrv.cert.CertNotFoundException": "pki.CertNotFoundException",
     "com.netscape.certsrv.group.GroupNotFoundException": "pki.GroupNotFoundException",
+    "com.netscape.certsrv.key.KeyNotFoundException": "pki.KeyNotFoundException",
     "com.netscape.certsrv.profile.ProfileNotFoundException": "pki.ProfileNotFoundException",
     "com.netscape.certsrv.request.RequestNotFoundException": "pki.RequestNotFoundException",
     "com.netscape.certsrv.base.UserNotFoundException": "pki.UserNotFoundException",
diff --git a/base/common/python/pki/key.py b/base/common/python/pki/key.py
index 30f6baeb706314147e0b806e61b312ab7cf3cea8..a321dc88b1fc4d156a0b1539eddc4bbae5753d0d 100644
--- a/base/common/python/pki/key.py
+++ b/base/common/python/pki/key.py
@@ -294,7 +294,7 @@ class KeyClient(object):
         self.keyURL = '/rest/agent/keys'
         self.keyRequestsURL = '/rest/agent/keyrequests'
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def list_keys(self, client_id=None, status=None, max_results=None,
                   max_time=None, start=None, size=None):
         ''' List/Search archived secrets in the DRM.
@@ -308,7 +308,7 @@ class KeyClient(object):
         response = self.connection.get(self.keyURL, self.headers, params=query_params)
         return KeyInfoCollection.from_json(response.json())
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def retrieve_key(self, data):
         ''' Retrieve a secret from the DRM.
 
@@ -324,7 +324,7 @@ class KeyClient(object):
         response = self.connection.post(url, keyRequest, self.headers)
         return KeyData.from_dict(response.json())
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def request_key_retrieval(self, key_id, request_id, trans_wrapped_session_key=None,
                      session_wrapped_passphrase=None, passphrase=None, nonce_data=None):
         ''' Retrieve a secret from the DRM.
@@ -357,7 +357,7 @@ class KeyClient(object):
 
         return self.retrieve_key(request)
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def list_requests(self, request_state=None, request_type=None, client_id=None,
                      start=None, page_size=None, max_results=None, max_time=None):
         ''' List/Search key requests in the DRM.
@@ -372,14 +372,14 @@ class KeyClient(object):
                                 params=query_params)
         return KeyRequestInfoCollection.from_json(response.json())
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def get_request_info(self, request_id):
         ''' Return a KeyRequestInfo object for a specific request. '''
         url = self.keyRequestsURL + '/' + request_id
         response = self.connection.get(url, self.headers)
         return KeyRequestInfo.from_dict(response.json())
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def create_request(self, request):
         ''' Submit an archival, recovery or key generation request
             to the DRM.
@@ -394,25 +394,25 @@ class KeyClient(object):
         response = self.connection.post(url, key_request, self.headers)
         return KeyRequestResponse.from_json(response.json())
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def approve_request(self, request_id):
         ''' Approve a secret recovery request '''
         url = self.keyRequestsURL + '/' + request_id + '/approve'
         return self.connection.post(url, self.headers)
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def reject_request(self, request_id):
         ''' Reject a secret recovery request. '''
         url = self.keyRequestsURL + '/' + request_id + '/reject'
         return self.connection.post(url, self.headers)
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def cancel_request(self, request_id):
         ''' Cancel a secret recovery request '''
         url = self.keyRequestsURL + '/' + request_id + '/cancel'
         return self.connection.post(url, self.headers)
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def request_recovery(self, key_id, request_id=None, session_wrapped_passphrase=None,
                         trans_wrapped_session_key=None, b64certificate=None, nonce_data=None):
         ''' Create a request to recover a secret.
@@ -433,7 +433,7 @@ class KeyClient(object):
                                      nonce_data=nonce_data)
         return self.create_request(request)
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def request_archival(self, client_id, data_type, wrapped_private_data,
                     key_algorithm=None, key_size=None):
         ''' Archive a secret (symmetric key or passphrase) on the DRM.
@@ -458,6 +458,20 @@ class KeyClient(object):
                                      key_size=key_size)
         return self.create_request(request)
 
+    @pki.handle_exceptions()
+    def get_key_info(self, key_id):
+        ''' Get the info in the KeyRecord for a specific secret in the DRM. '''
+        url = self.keyURL + '/' + key_id
+        response = self.connection.get(url, headers=self.headers)
+        return KeyInfo.from_dict(response.json())
+
+    @pki.handle_exceptions()
+    def modify_key_status(self, key_id, status):
+        ''' Modify the status of a key '''
+        url = self.keyURL + '/' + key_id
+        params = {'status':status}
+        return self.connection.post(url, None, headers=self.headers, params=params)
+
 encoder.NOTYPES['Attribute'] = pki.Attribute
 encoder.NOTYPES['AttributeList'] = pki.AttributeList
 encoder.NOTYPES['KeyArchivalRequest'] = KeyArchivalRequest
diff --git a/base/common/python/pki/kraclient.py b/base/common/python/pki/kraclient.py
index f2b7a55820dc12a6c6013d14b4075ffd27a56cd2..18707b744f0838f02ed19195543591baccf14f42 100644
--- a/base/common/python/pki/kraclient.py
+++ b/base/common/python/pki/kraclient.py
@@ -95,7 +95,7 @@ class KRAClient(object):
             return key_data, None
 
         unwrapped_key = self.crypto.symmetric_unwrap(key_data.wrappedPrivateData, session_key,
-                                                     iv=key_data.nonceData)
+                                                     nonce_iv=key_data.nonceData)
         return key_data, unwrapped_key
 
     def retrieve_key_by_passphrase(self, key_id, passphrase=None,
diff --git a/base/common/python/pki/systemcert.py b/base/common/python/pki/systemcert.py
index 8a1488deaff9682c8561ff953db7bddf123f228d..2f51de6f337f5b115937fde2ade3fdb33ff27ee6 100644
--- a/base/common/python/pki/systemcert.py
+++ b/base/common/python/pki/systemcert.py
@@ -38,7 +38,7 @@ class SystemCertClient(object):
                         'Accept': 'application/json'}
         self.cert_url = '/rest/config/cert'
 
-    @pki.handle_exceptions
+    @pki.handle_exceptions()
     def get_transport_cert(self):
         ''' Return transport certificate '''
         url = self.cert_url +  '/transport'
diff --git a/base/common/src/com/netscape/certsrv/key/KeyClient.java b/base/common/src/com/netscape/certsrv/key/KeyClient.java
index bdb84fddbf020dd9633575d5e967335e1596c3cf..af4e9abb194a9f07f3da96a531e34f0304e5e99b 100644
--- a/base/common/src/com/netscape/certsrv/key/KeyClient.java
+++ b/base/common/src/com/netscape/certsrv/key/KeyClient.java
@@ -182,4 +182,13 @@ public class KeyClient extends Client {
     public void cancelRequest(RequestId id) {
         keyRequestClient.cancelRequest(id);
     }
+
+    public KeyInfo getKeyInfo(KeyId id) {
+        Response response = keyClient.getKeyInfo(id);
+        return client.getEntity(response, KeyInfo.class);
+    }
+
+    public void modifyKeyStatus(KeyId id, String status) {
+        keyClient.modifyKeyStatus(id, status);
+    }
 }
diff --git a/base/common/src/com/netscape/certsrv/key/KeyNotFoundException.java b/base/common/src/com/netscape/certsrv/key/KeyNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..92b6f3a493f8c9a06a87eca6a50f1515d99ec438
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/key/KeyNotFoundException.java
@@ -0,0 +1,62 @@
+package com.netscape.certsrv.key;
+
+//--- BEGIN COPYRIGHT BLOCK ---
+//This program is free software; you can redistribute it and/or modify
+//it under the terms of the GNU General Public License as published by
+//the Free Software Foundation; version 2 of the License.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License along
+//with this program; if not, write to the Free Software Foundation, Inc.,
+//51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+//(C) 2007 Red Hat, Inc.
+//All rights reserved.
+//--- END COPYRIGHT BLOCK ---
+
+import com.netscape.certsrv.base.ResourceNotFoundException;
+import com.netscape.certsrv.dbs.keydb.KeyId;
+
+public class KeyNotFoundException extends ResourceNotFoundException {
+
+ private static final long serialVersionUID = -4784839378360933483L;
+
+ public KeyId keyID;
+
+ public KeyNotFoundException(KeyId keyId) {
+     this(keyId, "Key ID " + keyId.toHexString() + " not found");
+ }
+
+ public KeyNotFoundException(KeyId keyId, String message) {
+     super(message);
+     this.keyID = keyId;
+ }
+
+ public KeyNotFoundException(KeyId keyId, String message, Throwable cause) {
+     super(message, cause);
+     this.keyID = keyId;
+ }
+
+ public KeyNotFoundException(Data data) {
+     super(data);
+     keyID = new KeyId(data.getAttribute("KeyId"));
+ }
+
+ public Data getData() {
+     Data data = super.getData();
+     data.setAttribute("KeyId", keyID.toString());
+     return data;
+ }
+
+ public KeyId getKeyId() {
+     return keyID;
+ }
+
+ public void setRequestId(KeyId KeyId) {
+     this.keyID = KeyId;
+ }
+}
diff --git a/base/common/src/com/netscape/certsrv/key/KeyResource.java b/base/common/src/com/netscape/certsrv/key/KeyResource.java
index 78ce15344458f7277a92e86f19fb1f6823d981c6..5b5bf7a7444cd900f16bad8f3b078f46196c616f 100644
--- a/base/common/src/com/netscape/certsrv/key/KeyResource.java
+++ b/base/common/src/com/netscape/certsrv/key/KeyResource.java
@@ -15,6 +15,7 @@ import org.jboss.resteasy.annotations.ClientResponseType;
 
 import com.netscape.certsrv.acls.ACLMapping;
 import com.netscape.certsrv.authentication.AuthMethodMapping;
+import com.netscape.certsrv.dbs.keydb.KeyId;
 
 
 @Path("agent/keys")
@@ -38,6 +39,19 @@ public interface KeyResource {
     @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
     public Response getActiveKeyInfo(@PathParam("clientID") String clientID);
 
+    @GET
+    @Path("{id}")
+    @ClientResponseType(entityType=KeyInfo.class)
+    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+    public Response getKeyInfo(@PathParam("id") KeyId id);
+
+    @POST
+    @Path("{id}")
+    @ClientResponseType(entityType=Void.class)
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public Response modifyKeyStatus(@PathParam("id") KeyId id,
+                                    @QueryParam("status") String status);
+
     /**
      * Used to retrieve a key
      * @param data
diff --git a/base/kra/functional/drmtest.py b/base/kra/functional/drmtest.py
index 471792113c7b941de07e500e9912341f6da4f536..e5476022596b68c23bc7086124745a6890dbc85d 100644
--- a/base/kra/functional/drmtest.py
+++ b/base/kra/functional/drmtest.py
@@ -111,7 +111,7 @@ def main():
     wrapped_session_key = crypto.asymmetric_wrap(session_key, kraclient.transport_cert)
     key_data, _unwrapped_key = kraclient.retrieve_key(key_id, trans_wrapped_session_key=wrapped_session_key)
     print_key_data(key_data)
-    unwrapped_key = crypto.symmetric_unwrap(key_data.wrappedPrivateData, session_key, iv=key_data.nonceData)
+    unwrapped_key = crypto.symmetric_unwrap(key_data.wrappedPrivateData, session_key, nonce_iv=key_data.nonceData)
     key1 = base64.encodestring(unwrapped_key)
 
     # Test 7: Recover key without providing trans_wrapped_session_key
@@ -139,18 +139,34 @@ def main():
     try:
         keyrequest = kraclient.keys.get_request_info('200000034')
     except pki.RequestNotFoundException as exc:
-        print "RequestNotFoundRequestException thrown - Code:" + exc.code + " Message: " + exc.message
+        print "RequestNotFoundException thrown - Code:" + exc.code + " Message: " + exc.message
 
-    # Test 12 - Test exception on retrieve_key
-    # Note - this currently throws PKIException when it should probably throw a ResourceNotFound exception
-    # Fix in next patch.
+    # Test 12 - Test exception on retrieve_key.
     print "Try to retrieve an invalid key"
     try:
         key_data, unwrapped_key = kraclient.retrieve_key('2000003434')
+    except pki.KeyNotFoundException as exc:
+        print "KeyNotFoundException thrown - Code:" + exc.code + " Message: " + exc.message
     except pki.PKIException as exc:
-        print "PKIException thrown - Code:" + exc.code + " Message: " + exc.message
+        # note: this is broken - we should be sending KeyNotFoundException here before the recovery
+        # request is created - to be fixed in next patch
+        print "PKIException thrown - Code:" + exc.code + " Message: " + exc.message 
 
+    #Test 13 = getKeyInfo
+    print "Get key info for existing key"
+    key_info = kraclient.keys.get_key_info(key_id)
+    print_key_info(key_info)
 
+    #Test 14: change the key status
+    print "Change the key status"
+    kraclient.keys.modify_key_status(key_id, "inactive")
+    print_key_info(kraclient.keys.get_key_info(key_id))
+
+    print "Get key info for non-existent key"
+    try:
+        key_info = kraclient.keys.get_key_info('200004556')
+    except pki.KeyNotFoundException as exc:
+        print "KeyNotFoundException thrown - Code:" + exc.code + " Message: " + exc.message
 
 if __name__ == "__main__":
     main()
diff --git a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
index 5b2d39af3cc95e10a6c44b3a78505b56610988b6..52190091e8ee1f1009cadb31fe7b45c632ff2d62 100644
--- a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
+++ b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
@@ -39,6 +39,7 @@ import org.mozilla.jss.crypto.KeyGenerator;
 import org.mozilla.jss.crypto.SymmetricKey;
 import org.mozilla.jss.util.Password;
 
+import com.netscape.certsrv.base.ResourceNotFoundException;
 import com.netscape.certsrv.client.ClientConfig;
 import com.netscape.certsrv.client.PKIClient;
 import com.netscape.certsrv.dbs.keydb.KeyId;
@@ -724,6 +725,25 @@ public class DRMTest {
             log("Success: recoverd and archived keys match!");
         }
 
+        // Test 41: Get key info
+        log("getting key info for existing key");
+        printKeyInfo(keyClient.getKeyInfo(keyId));
+
+        //Test 42: Modify status
+        log("modify the key status");
+        keyClient.modifyKeyStatus(keyId, "inactive");
+        keyInfo = keyClient.getKeyInfo(keyId);
+        printKeyInfo(keyInfo);
+
+        //Test 43:  Confirm no more active keys with this ID
+        log("look for active keys with this id");
+        clientId = keyInfo.getClientID();
+        try {
+            keyInfo = keyClient.getActiveKeyInfo(clientId);
+            printKeyInfo(keyInfo);
+        } catch (ResourceNotFoundException e) {
+            log("Success: ResourceNotFound exception thrown: " + e);
+        }
     }
 
     private static void printKeyInfo(KeyInfo keyInfo) {
@@ -732,6 +752,7 @@ public class DRMTest {
         log("Key URL:   " + keyInfo.getKeyURL());
         log("Algorithm: " + keyInfo.getAlgorithm());
         log("Strength:  " + keyInfo.getSize());
+        log("Status:    " + keyInfo.getStatus());
     }
 
     private static void log(String string) {
diff --git a/base/server/cms/src/com/netscape/cms/servlet/key/KeyService.java b/base/server/cms/src/com/netscape/cms/servlet/key/KeyService.java
index d6f252f7b7d9c1e8613e2de76de9b88d56ad8468..31dd2c0aa0216e533710fd412a0abef7233bec72 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/key/KeyService.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/key/KeyService.java
@@ -47,12 +47,16 @@ import com.netscape.certsrv.base.HTTPGoneException;
 import com.netscape.certsrv.base.PKIException;
 import com.netscape.certsrv.base.ResourceNotFoundException;
 import com.netscape.certsrv.base.UnauthorizedException;
+import com.netscape.certsrv.dbs.EDBRecordNotFoundException;
+import com.netscape.certsrv.dbs.Modification;
+import com.netscape.certsrv.dbs.ModificationSet;
 import com.netscape.certsrv.dbs.keydb.IKeyRecord;
 import com.netscape.certsrv.dbs.keydb.IKeyRepository;
 import com.netscape.certsrv.dbs.keydb.KeyId;
 import com.netscape.certsrv.key.KeyData;
 import com.netscape.certsrv.key.KeyInfo;
 import com.netscape.certsrv.key.KeyInfoCollection;
+import com.netscape.certsrv.key.KeyNotFoundException;
 import com.netscape.certsrv.key.KeyRecoveryRequest;
 import com.netscape.certsrv.key.KeyRequestInfo;
 import com.netscape.certsrv.key.KeyResource;
@@ -515,4 +519,41 @@ public class KeyService extends PKIService implements KeyResource {
 
         return keyData;
     }
+
+    @Override
+    public Response getKeyInfo(KeyId keyId) {
+        IKeyRecord rec = null;
+        try {
+            rec = repo.readKeyRecord(keyId.toBigInteger());
+            KeyInfo info = createKeyDataInfo(rec);
+
+            return createOKResponse(info);
+        } catch (EDBRecordNotFoundException e) {
+            throw new KeyNotFoundException(keyId);
+        } catch (Exception e) {
+            CMS.debug("Unable to retrieve key record: " + e);
+            e.printStackTrace();
+            throw new PKIException(e.getMessage());
+        }
+    }
+
+    @Override
+    public Response modifyKeyStatus(KeyId keyId, String status) {
+        try {
+
+            ModificationSet mods = new ModificationSet();
+            mods.add(IKeyRecord.ATTR_STATUS, Modification.MOD_REPLACE,
+                    status);
+            repo.modifyKeyRecord(keyId.toBigInteger(), mods);
+            return createNoContentResponse();
+        } catch (EDBRecordNotFoundException e) {
+            throw new KeyNotFoundException(keyId);
+        } catch (Exception e) {
+            CMS.debug("Unable to retrieve key record: " + e);
+            e.printStackTrace();
+            throw new PKIException(e.getMessage());
+        }
+    }
+
+
 }
diff --git a/base/server/cmscore/src/com/netscape/cmscore/dbs/DBSSession.java b/base/server/cmscore/src/com/netscape/cmscore/dbs/DBSSession.java
index fd59e8d8f78462fc072c587aa137314607cba4ec..ad1be6602dc296ae16dddddb75a81b5de8f8c190 100644
--- a/base/server/cmscore/src/com/netscape/cmscore/dbs/DBSSession.java
+++ b/base/server/cmscore/src/com/netscape/cmscore/dbs/DBSSession.java
@@ -234,6 +234,9 @@ public class DBSSession implements IDBSSession {
             if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE)
                 throw new EDBNotAvailException(
                         CMS.getUserMessage("CMS_DBS_INTERNAL_DIR_UNAVAILABLE"));
+            if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT)
+                throw new EDBRecordNotFoundException(
+                        CMS.getUserMessage("CMS_DBS_RECORD_NOT_FOUND"));
             throw new EDBException(CMS.getUserMessage("CMS_DBS_LDAP_OP_FAILURE",
                         name + " " + e.toString()));
         }
diff --git a/base/server/sbin/pkispawn b/base/server/sbin/pkispawn
index d2905dc1931eecea556a1996d7177b29b43f8f41..dd1c4e0be5f51423ab2632b6987e781decabcfa5 100755
--- a/base/server/sbin/pkispawn
+++ b/base/server/sbin/pkispawn
@@ -335,7 +335,6 @@ def main(argv):
         sys.exit(1)
 
     # Enable 'pkispawn' logging.
-    rv = 0
     config.pki_log_dir = config.pki_root_prefix + \
                          config.PKI_DEPLOYMENT_LOG_ROOT
     config.pki_log_name = "pki" + "-" + \
-- 
1.8.4.2

