From f57c6f07dac7bf4bd8c8e95bd4a46dc84fca6dda Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Wed, 2 Nov 2016 11:20:34 -0400
Subject: [PATCH 335/338] Modify retrieval and archival mechanisms in KRA REST

When clients call retrieveKey(), three possible alternatives
now obtain:
1. client passes in an approved request. Request is processed
   and the secret is retrieved.
2. client passes in key_id and wrapping parameters and either:
  a) request can be processed immediately and synchronously
     and request is created, and secret is returned.
  b) request cannot be processed immediately.  Recovery request
     is created and request_id returned to the client

Depending on server configuration, the requests in case (2a)
will be stored in ldap or will be ephemeral (in memory only).

More complicated realm based logic to determine if requests
can be processed synchronously or ephemerally will be added in
a later patch.
---
 .../src/com/netscape/certsrv/key/KeyClient.java    |   9 +-
 .../netscape/certsrv/key/KeyRecoveryRequest.java   |  11 +-
 .../certsrv/kra/IKeyRecoveryAuthority.java         |  24 +-
 .../netscape/certsrv/request/IRequestQueue.java    |  12 +
 .../com/netscape/certsrv/request/RequestId.java    |   4 +
 .../src/com/netscape/kra/KeyRecoveryAuthority.java |  39 ++-
 .../com/netscape/kra/SecurityDataProcessor.java    |   6 +-
 .../netscape/kra/SecurityDataRecoveryService.java  |   1 -
 .../server/kra/rest/KeyRequestService.java         |  11 -
 .../org/dogtagpki/server/kra/rest/KeyService.java  | 361 ++++++++++-----------
 .../netscape/cms/servlet/key/KeyRequestDAO.java    |  71 ++--
 .../netscape/cmscore/request/ARequestQueue.java    |  20 +-
 .../com/netscape/cmscore/request/RequestQueue.java |   8 +
 13 files changed, 336 insertions(+), 241 deletions(-)

diff --git a/base/common/src/com/netscape/certsrv/key/KeyClient.java b/base/common/src/com/netscape/certsrv/key/KeyClient.java
index cb35922f6978b87823d3033a4b807f945842712a..0403ccbbb38b8df892e203f6d512b77f63dc0463 100644
--- a/base/common/src/com/netscape/certsrv/key/KeyClient.java
+++ b/base/common/src/com/netscape/certsrv/key/KeyClient.java
@@ -402,12 +402,8 @@ public class KeyClient extends Client {
             throw new IllegalArgumentException("A transport cert wrapped session key cannot be null.");
         }
 
-        KeyRequestResponse keyData = recoverKey(keyId, null, null, null, null);
-        approveRequest(keyData.getRequestId());
-
         KeyRecoveryRequest recoveryRequest = new KeyRecoveryRequest();
         recoveryRequest.setKeyId(keyId);
-        recoveryRequest.setRequestId(keyData.getRequestId());
         recoveryRequest.setTransWrappedSessionKey(Utils.base64encode(transWrappedSessionKey));
 
         return retrieveKeyData(recoveryRequest);
@@ -491,6 +487,7 @@ public class KeyClient extends Client {
         KeyRecoveryRequest data = new KeyRecoveryRequest();
         data.setKeyId(keyId);
         data.setRequestId(requestId);
+
         if (transWrappedSessionKey != null) {
             data.setTransWrappedSessionKey(Utils.base64encode(transWrappedSessionKey));
         }
@@ -549,8 +546,8 @@ public class KeyClient extends Client {
      *             IOException, CertificateEncodingException, InvalidKeyException, InvalidAlgorithmParameterException,
      *             BadPaddingException, IllegalBlockSizeException
      */
-    public KeyRequestResponse archivePassphrase(String clientKeyId, String passphrase, String realm) throws Exception {
-
+    public KeyRequestResponse archivePassphrase(String clientKeyId, String passphrase, String realm)
+            throws Exception {
         // Default algorithm OID for DES_EDE3_CBC
         String algorithmOID = EncryptionAlgorithm.DES3_CBC.toOID().toString();
         byte[] nonceData = CryptoUtil.getNonceData(8);
diff --git a/base/common/src/com/netscape/certsrv/key/KeyRecoveryRequest.java b/base/common/src/com/netscape/certsrv/key/KeyRecoveryRequest.java
index e12b414b139f61d721286a359587ad240ed82bf5..31a2c5005beeb843af6cdde1fbd9bc4331a6dc60 100644
--- a/base/common/src/com/netscape/certsrv/key/KeyRecoveryRequest.java
+++ b/base/common/src/com/netscape/certsrv/key/KeyRecoveryRequest.java
@@ -75,7 +75,10 @@ public class KeyRecoveryRequest extends ResourceMessage {
      * @return the keyId
      */
     public KeyId getKeyId() {
-        return new KeyId(attributes.get(KEY_ID));
+        String id = attributes.get(KEY_ID);
+        if (id != null)
+            return new KeyId(attributes.get(KEY_ID));
+        return null;
     }
 
     /**
@@ -89,7 +92,10 @@ public class KeyRecoveryRequest extends ResourceMessage {
      * @return the requestId
      */
     public RequestId getRequestId() {
-        return new RequestId(attributes.get(REQUEST_ID));
+        String id = attributes.get(REQUEST_ID);
+        if (id != null)
+            return new RequestId(attributes.get(REQUEST_ID));
+        return null;
     }
 
     /**
@@ -171,7 +177,6 @@ public class KeyRecoveryRequest extends ResourceMessage {
         attributes.put(PASSPHRASE, passphrase);
     }
 
-
     public static KeyRecoveryRequest valueOf(String string) throws Exception {
         try {
             return ResourceMessage.unmarshal(string, KeyRecoveryRequest.class);
diff --git a/base/common/src/com/netscape/certsrv/kra/IKeyRecoveryAuthority.java b/base/common/src/com/netscape/certsrv/kra/IKeyRecoveryAuthority.java
index acc3214bbec24baa777426bec337fed0943c9c8b..0ecd7a7b099dbddcf029b9806a1f393e1c600e85 100644
--- a/base/common/src/com/netscape/certsrv/kra/IKeyRecoveryAuthority.java
+++ b/base/common/src/com/netscape/certsrv/kra/IKeyRecoveryAuthority.java
@@ -21,8 +21,6 @@ import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.Vector;
 
-import netscape.security.x509.X500Name;
-
 import org.mozilla.jss.crypto.CryptoToken;
 
 import com.netscape.certsrv.base.EBaseException;
@@ -30,6 +28,7 @@ import com.netscape.certsrv.base.ISubsystem;
 import com.netscape.certsrv.dbs.keydb.IKeyRepository;
 import com.netscape.certsrv.dbs.replicadb.IReplicaIDRepository;
 import com.netscape.certsrv.policy.IPolicyProcessor;
+import com.netscape.certsrv.request.IRequest;
 import com.netscape.certsrv.request.IRequestListener;
 import com.netscape.certsrv.request.IRequestQueue;
 import com.netscape.certsrv.request.RequestId;
@@ -37,6 +36,8 @@ import com.netscape.certsrv.security.Credential;
 import com.netscape.certsrv.security.IStorageKeyUnit;
 import com.netscape.certsrv.security.ITransportKeyUnit;
 
+import netscape.security.x509.X500Name;
+
 /**
  * An interface represents key recovery authority. The
  * key recovery authority is responsibile for archiving
@@ -318,4 +319,23 @@ public interface IKeyRecoveryAuthority extends ISubsystem {
      * Retrieves the transport certificate.
      */
     public org.mozilla.jss.crypto.X509Certificate getTransportCert();
+
+    /**
+     * Process synchronous archival and recovery requests
+     */
+    public void processSynchronousRequest(IRequest request) throws EBaseException;
+
+    /**
+     * Are ephemeral requests enabled for SECURITY_DATA recovery and archival
+     * @param realm authz realm
+     * @return
+     */
+    public boolean isEphemeral(String realm);
+
+    /**
+     * Is the SECURITY_DATA retrieval synchronous?
+     * @param realm
+     * @return
+     */
+    public boolean isRetrievalSynchronous(String realm);
 }
diff --git a/base/common/src/com/netscape/certsrv/request/IRequestQueue.java b/base/common/src/com/netscape/certsrv/request/IRequestQueue.java
index 844f90f18f627103a7e1a5751f39702c65cc4923..1152a247a6f543654925c9c6b7a772163fbe8b27 100644
--- a/base/common/src/com/netscape/certsrv/request/IRequestQueue.java
+++ b/base/common/src/com/netscape/certsrv/request/IRequestQueue.java
@@ -38,6 +38,18 @@ import com.netscape.certsrv.dbs.repository.IRepository;
 public interface IRequestQueue {
 
     /**
+     * Create a new Request object and assign a request ID.
+     * See newRequest() for details.
+     *
+     * @param requestType - request type
+     * @param ephemeral - is the request ephemeral?
+     * @return new request
+     * @exception EBaseException failed to create new request
+     */
+    public IRequest newRequest(String requestType, boolean ephemeral)
+            throws EBaseException;
+
+    /**
      * Creates a new request object. A request id is
      * assigned to it - see IRequest.getRequestId, and
      * the status is set to RequestStatus.BEGIN
diff --git a/base/common/src/com/netscape/certsrv/request/RequestId.java b/base/common/src/com/netscape/certsrv/request/RequestId.java
index 6f31a18be0c06730bdc32e8c560837cb4aa0044a..d3e956ba35163c6abd1e15e96717eb71c6981c2d 100644
--- a/base/common/src/com/netscape/certsrv/request/RequestId.java
+++ b/base/common/src/com/netscape/certsrv/request/RequestId.java
@@ -77,6 +77,10 @@ public class RequestId implements Serializable {
         value = BigInteger.valueOf(id);
     }
 
+    public RequestId(long id) {
+        value = BigInteger.valueOf(id);
+    }
+
     /**
      * Converts the RequestId into its BigInteger representation.
      * <p>
diff --git a/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java b/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java
index cda3ba659b935139f23f5bae2ac6b1a41c060a8f..f41f88e2546a9ff3ccf8bd01022dec9b2ec3c143 100644
--- a/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java
+++ b/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java
@@ -1231,7 +1231,7 @@ public class KeyRecoveryAuthority implements IAuthority, IKeyService, IKeyRecove
      * @param kid key identifier
      * @param creds list of recovery agent credentials
      * @param password password of the PKCS12 package
-     * @param cert certficate that will be put in PKCS12
+     * @param cert certificate that will be put in PKCS12
      * @param delivery file, mail or something else
      * @return executed request
      * @exception EBaseException failed to recover key
@@ -1252,6 +1252,43 @@ public class KeyRecoveryAuthority implements IAuthority, IKeyService, IKeyRecove
     }
 
     /**
+     * Execute synchronous request
+     * (TODO(alee): should we do this in a separate thread?
+     * @throws EBaseException
+     */
+    public void processSynchronousRequest(IRequest request) throws EBaseException {
+        SecurityDataProcessor processor = new SecurityDataProcessor(this);
+        switch(request.getRequestType()){
+            case IRequest.SECURITY_DATA_ENROLLMENT_REQUEST:
+                processor.archive(request);
+                break;
+            case IRequest.SECURITY_DATA_RECOVERY_REQUEST:
+                processor.recover(request);
+                break;
+            default:
+                throw new EBaseException("Unsupported synchronous request type: " + request.getRequestType());
+        }
+    }
+
+    public boolean isEphemeral(String realm) {
+        try {
+            return mConfig.getBoolean("ephemeral", false);
+        } catch (EBaseException e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    public boolean isRetrievalSynchronous(String realm) {
+        try {
+            return getNoOfRequiredSecurityDataRecoveryAgents() == 1;
+        } catch (EBaseException e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    /**
      * Recovers key for end-entities.
      *
      * @param creds list of credentials
diff --git a/base/kra/src/com/netscape/kra/SecurityDataProcessor.java b/base/kra/src/com/netscape/kra/SecurityDataProcessor.java
index 70e712dc691a6964851c974734a859ed95ebeff9..36da8c4ee040e627b0f3262aefa1cb99414a7bd4 100644
--- a/base/kra/src/com/netscape/kra/SecurityDataProcessor.java
+++ b/base/kra/src/com/netscape/kra/SecurityDataProcessor.java
@@ -81,6 +81,7 @@ public class SecurityDataProcessor {
         this.kra = kra;
         transportUnit = kra.getTransportKeyUnit();
         storageUnit = kra.getStorageKeyUnit();
+        keyRepository = kra.getKeyRepository();
     }
 
     public boolean archive(IRequest request)
@@ -247,8 +248,7 @@ public class SecurityDataProcessor {
             throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_STATE"));
         }
 
-        IKeyRepository storage = kra.getKeyRepository();
-        BigInteger serialNo = storage.getNextSerialNumber();
+        BigInteger serialNo = keyRepository.getNextSerialNumber();
 
         if (serialNo == null) {
             kra.log(ILogger.LL_FAILURE,
@@ -273,7 +273,7 @@ public class SecurityDataProcessor {
 
         CMS.debug("KRA adding Security Data key record " + serialNo);
 
-        storage.addKeyRecord(rec);
+        keyRepository.addKeyRecord(rec);
 
         auditArchivalRequestProcessed(auditSubjectID, ILogger.SUCCESS, requestId,
                 clientKeyId, serialNo.toString(), "None");
diff --git a/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java b/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java
index 5b846567983c091b69338a1fc79c52ef76721ae0..0c7b4b70e685338b31f2f5d27c819c863ef998c5 100644
--- a/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java
+++ b/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java
@@ -29,7 +29,6 @@ import com.netscape.certsrv.request.IService;
  *
  * @version $Revision$, $Date$
  */
-@SuppressWarnings("deprecation")
 public class SecurityDataRecoveryService implements IService {
 
     private IKeyRecoveryAuthority kra = null;
diff --git a/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java b/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java
index 103b78923ee9200bed2e0c8608168f35c311df9c..f6918509a0107b6f917e66538c49a4e30c23c978 100644
--- a/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java
+++ b/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java
@@ -202,20 +202,9 @@ public class KeyRequestService extends PKIService implements KeyRequestResource
         //If data has been provided, we need at least the wrapped session key,
         //or the command is invalid.
 
-        if (data == null) {
-            throw new BadRequestException("Invalid request.");
-        }
-        if (data.getCertificate() == null &&
-            data.getTransWrappedSessionKey() == null &&
-            data.getSessionWrappedPassphrase() != null) {
-            throw new BadRequestException("No wrapped session key.");
-        }
         KeyRequestDAO dao = new KeyRequestDAO();
         KeyRequestResponse response;
         try {
-            if (getRequestor() == null) {
-                throw new UnauthorizedException("Recovery must be initiated by an agent");
-            }
             response = (data.getCertificate() != null)?
                     dao.submitAsyncKeyRecoveryRequest(data, uriInfo, getRequestor(), getAuthToken()):
                     dao.submitRequest(data, uriInfo, getRequestor(), getAuthToken());
diff --git a/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java b/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java
index 74b58b8a2e3c036925e12114d578a4069b136c74..bef03c5f1109d5971fa38997eeeed424fbae9919 100644
--- a/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java
+++ b/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java
@@ -62,7 +62,6 @@ 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;
 import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
 import com.netscape.certsrv.kra.IKeyService;
@@ -102,12 +101,18 @@ public class KeyService extends PKIService implements KeyResource {
 
     public static final int DEFAULT_MAXRESULTS = 100;
     public static final int DEFAULT_MAXTIME = 10;
+    public static final String ATTR_SERIALNO = "serialNumber";
 
     private IKeyRepository repo;
     private IKeyRecoveryAuthority kra;
     private IRequestQueue queue;
     private IKeyService service;
 
+    //parameters for auditing
+    private RequestId requestId;
+    private KeyId keyId;
+    private String auditInfo;
+
     public KeyService() {
         kra = ( IKeyRecoveryAuthority ) CMS.getSubsystem( "kra" );
         repo = kra.getKeyRepository();
@@ -117,119 +122,158 @@ public class KeyService extends PKIService implements KeyResource {
 
     /**
      * Used to retrieve a key
+     *
+     * There are two use cases here:
+     * 1. asynchronous requests
+     * 2. synchronous requests
      * @param data
      * @return
      */
     @Override
     public Response retrieveKey(KeyRecoveryRequest data) {
+        boolean synchronous = false;
+        boolean ephemeral = false;
+        String realm = null;
 
-        CMS.debug("KeyService.retrieveKey()");
-        String auditInfo = "KeyService.retrieveKey";
+        auditInfo = "KeyService.retrieveKey";
+        CMS.debug(auditInfo);
 
         if (data == null) {
-            String message = "Missing key recovery request";
-            CMS.debug(message);
-            auditRetrieveKey(ILogger.FAILURE, "None", "None", auditInfo + ";" + message);
-            throw new BadRequestException(message);
+            errorOut("Missing key recovery request", new BadRequestException("Missing key Recovery Request"));
         }
 
-        RequestId requestID = data.getRequestId();
-        CMS.debug("KeyService: request ID: " + requestID);
+        // get or create request
+        IRequest request = null;
+        requestId = data.getRequestId();
+        if (requestId != null) {
+            // this is an asynchronous request
+            // We assume that the request is valid and has been approved
+            CMS.debug("KeyService: request ID: " + requestId);
+            auditInfo += ";requestID=" + requestId.toString();
 
-        if (requestID != null)
-            auditInfo = auditInfo + ": requestID=" + requestID.toString();
+            try {
+                request = queue.findRequest(requestId);
+            } catch (EBaseException e) {
+                errorOut(e.getMessage(), new PKIException(e.getMessage(), e));
+            }
 
-        KeyId keyId = data.getKeyId();
-        CMS.debug("KeyService: key ID: " + keyId);
-        if (keyId != null)
-            auditInfo = auditInfo + "; keyID=" + keyId.toString();
+            if (request == null) {
+                errorOut("No request found", new BadRequestException("No request found"));
+            }
 
-        IRequest request;
-        try {
-            request = queue.findRequest(requestID);
+            keyId = new KeyId(request.getExtDataInString(ATTR_SERIALNO));
+            auditInfo += ";keyID=" + keyId.toString();
 
-        } catch (EBaseException e) {
-            CMS.debug(e);
-            auditRetrieveKey(ILogger.FAILURE, requestID, null, auditInfo + ";" + e.getMessage());
-            throw new PKIException(e.getMessage(), e);
+            data.setKeyId(keyId);
+        } else {
+            keyId = data.getKeyId();
+            if (keyId == null) {
+                errorOut("Missing key ID", new BadRequestException("Missing key Recovery Request"));
+            }
+
+            auditInfo += ";keyID=" + keyId.toString();
+
+            // TODO(alee): get the realm from the key record
+            synchronous = kra.isRetrievalSynchronous(realm);
+            ephemeral = kra.isEphemeral(realm);
+
+            auditInfo += ";synchronous=" + Boolean.toString(synchronous);
+            auditInfo += ";ephemeral=" + Boolean.toString(ephemeral);
+
+            KeyRequestDAO reqDAO = new KeyRequestDAO();
+            try {
+                request = reqDAO.createRecoveryRequest(data, uriInfo, getRequestor(), getAuthToken(), ephemeral);
+            } catch (EBaseException e) {
+                errorOut(e.getMessage(), new PKIException(e.getMessage(), e));
+            }
+
+            requestId = request.getRequestId();
+            auditInfo += ";requestID=" + requestId.toString();
         }
 
+        data.setRequestId(requestId);
+
         String type = request.getRequestType();
         CMS.debug("KeyService: request type: " + type);
-        auditInfo = auditInfo + "; request type:" + type;
+        auditInfo += ";request type:" + type;
 
-        KeyData keyData;
+        // process request
+        KeyData keyData = null;
         try {
-            if (IRequest.KEYRECOVERY_REQUEST.equals(type)) {
-                keyData = recoverKey(data);
-
-            } else {
-                keyId = validateRequest(data);
-                keyData = getKey(keyId, data);
+            switch(type) {
+                case IRequest.KEYRECOVERY_REQUEST:
+                    keyData = recoverKey(data);
+                    break;
+                case IRequest.SECURITY_DATA_RECOVERY_REQUEST:
+                    if (synchronous)  request.setRequestStatus(RequestStatus.APPROVED);
+                    validateRequest(data, request);
+                    keyData = getKey(keyId, request, data, synchronous, ephemeral);
+                    break;
+                default:
+                    errorOut("Invalid request type: " + type,
+                             new BadRequestException("Invalid request type: " + type));
             }
-
         } catch (Exception e) {
-            CMS.debug(e);
-            auditRetrieveKey(ILogger.FAILURE, requestID, keyId, auditInfo + ";" + e.getMessage());
-            throw new PKIException(e.getMessage(), e);
+            errorOut(e.getMessage(), new PKIException(e.getMessage(), e));
         }
 
         if (keyData == null) {
-            CMS.debug("KeyService: No key record");
-            auditRetrieveKey(ILogger.FAILURE, requestID, keyId, auditInfo + "; No key record");
-            throw new HTTPGoneException("No key record.");
+            errorOut("No key record", new HTTPGoneException("No key record."));
         }
 
         CMS.debug("KeyService: key retrieved");
-
-        auditRetrieveKey(ILogger.SUCCESS, requestID, keyId, auditInfo);
-
+        auditRetrieveKey(ILogger.SUCCESS);
         return createOKResponse(keyData);
     }
 
     // retrieval - used to test integration with a browser
     @Override
     public Response retrieveKey(MultivaluedMap<String, String> form) {
-        String method = "KeyService.retrieveKey with form: ";
-        CMS.debug(method + "begins.");
+        CMS.debug("KeyService.retrieveKey with form: begins.");
         KeyRecoveryRequest data = new KeyRecoveryRequest(form);
         return retrieveKey(data);
     }
 
-    public KeyData getKey(KeyId keyId, KeyRecoveryRequest data) throws EBaseException {
-        String method = "KeyService.getKey: ";
-        String auditInfo = null;
+    public KeyData getKey(KeyId keyId, IRequest request, KeyRecoveryRequest data,
+            boolean synchronous, boolean ephemeral) throws EBaseException {
+        String method = "KeyService.getKey:";
+        auditInfo = method;
         KeyData keyData;
         CMS.debug(method + "begins.");
-        RequestId rId = data.getRequestId();
 
-        String transWrappedSessionKey;
-        String sessionWrappedPassphrase;
-
-        IRequest request = queue.findRequest(rId);
+        if (data == null) {
+            CMS.debug(method + "KeyRecoveryRequest is null");
+            return null;
+        }
 
         if (request == null) {
             CMS.debug(method + "request null");
             return null;
         }
 
-     // get wrapped key
+        // get wrapped key
         IKeyRecord rec = repo.readKeyRecord(keyId.toBigInteger());
         if (rec == null) {
             CMS.debug(method + "key record null");
-
             return null;
         }
 
-        Hashtable<String, Object> requestParams = kra.getVolatileRequest(
-                request.getRequestId());
+        auditInfo += ";keyID=" + keyId.toString();
+        auditInfo += ";requestID=" + requestId.toString();
+        auditInfo += ";synchronous=" + Boolean.toString(synchronous);
+        auditInfo += ";ephemeral=" + Boolean.toString(ephemeral);
 
+
+        // get data from the KeyRecoveryRequest
+        String transWrappedSessionKey   = data.getTransWrappedSessionKey();
+        String sessionWrappedPassphrase = data.getSessionWrappedPassphrase();
+
+        // get data from IRequest
+        Hashtable<String, Object> requestParams = kra.getVolatileRequest(request.getRequestId());
         if(requestParams == null) {
-            auditInfo = method + "Can't obtain Volatile requestParams in getKey!";
-            CMS.debug(auditInfo);
-            throw new EBaseException(auditInfo);
+            errorOut("Can't obtain Volatile requestParams in getKey!",
+                     new PKIException("Can't obtain Volatile requestParams in getKey!"));
         }
-
         String sessWrappedKeyData = (String) requestParams.get(IRequest.SECURITY_DATA_SESS_WRAPPED_DATA);
         String passWrappedKeyData = (String) requestParams.get(IRequest.SECURITY_DATA_PASS_WRAPPED_DATA);
         String nonceData = (String) requestParams.get(IRequest.SECURITY_DATA_IV_STRING_OUT);
@@ -239,23 +283,12 @@ public class KeyService extends PKIService implements KeyResource {
             //package, either session key wrapped or pass wrapped, into the request.
             //Request already has been processed.
             keyData = new KeyData();
-
         } else {
             // The request has not yet been processed, let's see if the RecoveryRequestData contains
             // the info now needed to process the recovery request.
 
-            transWrappedSessionKey   = data.getTransWrappedSessionKey();
-            sessionWrappedPassphrase = data.getSessionWrappedPassphrase();
             nonceData = data.getNonceData();
 
-            if (transWrappedSessionKey == null) {
-                //There must be at least a transWrappedSessionKey input provided.
-                //The command AND the request have provided insufficient data, end of the line.
-                auditInfo = method + "Can't retrieve key, insufficient input data!";
-                CMS.debug(auditInfo);
-                throw new EBaseException(auditInfo);
-            }
-
             if (sessionWrappedPassphrase != null) {
                 requestParams.put(IRequest.SECURITY_DATA_SESS_PASS_PHRASE, sessionWrappedPassphrase);
             }
@@ -269,22 +302,23 @@ public class KeyService extends PKIService implements KeyResource {
             }
 
             try {
-                // Has to be in this state or it won't go anywhere.
-                request.setRequestStatus(RequestStatus.BEGIN);
-                queue.processRequest(request);
+                if (!synchronous) {
+                    // Has to be in this state or it won't go anywhere.
+                    request.setRequestStatus(RequestStatus.BEGIN);
+                    queue.processRequest(request);
+                } else {
+                    kra.processSynchronousRequest(request);
+                }
             } catch (EBaseException e) {
-                auditInfo = method + e.getMessage();
                 kra.destroyVolatileRequest(request.getRequestId());
-                throw new EBaseException(auditInfo);
+                errorOut(e.getMessage(), new PKIException(e.getMessage(), e));
             }
 
-            nonceData = null;
+            // get the results of the operations
             keyData = new KeyData();
-
             sessWrappedKeyData = (String) requestParams.get(IRequest.SECURITY_DATA_SESS_WRAPPED_DATA);
             passWrappedKeyData = (String) requestParams.get(IRequest.SECURITY_DATA_PASS_WRAPPED_DATA);
             nonceData = (String) requestParams.get(IRequest.SECURITY_DATA_IV_STRING_OUT);
-
         }
 
         if (sessWrappedKeyData != null) {
@@ -310,93 +344,49 @@ public class KeyService extends PKIService implements KeyResource {
 
         kra.destroyVolatileRequest(request.getRequestId());
 
-        queue.markAsServiced(request);
+        if (!synchronous) {
+            queue.markAsServiced(request);
+        } else {
+            request.setRequestStatus(RequestStatus.COMPLETE);
+            if (! ephemeral) {
+                // stores the request in LDAP
+                queue.updateRequest(request);
+            }
+        }
 
         return keyData;
     }
 
-    private KeyId validateRequest(KeyRecoveryRequest data) {
+    private void validateRequest(KeyRecoveryRequest data, IRequest request) {
         String method = "KeyService.validateRequest: ";
         CMS.debug(method + "begins.");
-        String logMessage = null;
-
-        // confirm request exists
-        RequestId reqId = data.getRequestId();
-        if (reqId == null) {
-            // log error
-            logMessage = "Request id not found.";
-            CMS.debug(logMessage);
-            throw new BadRequestException(logMessage);
-        }
 
         // confirm that at least one wrapping method exists
         // There must be at least the wrapped session key method.
         if ((data.getTransWrappedSessionKey() == null)) {
-            // log error
-            logMessage = "No wrapping method found.";
-            CMS.debug(logMessage);
-
-            throw new BadRequestException(logMessage);
-        }
-
-        KeyRequestDAO reqDAO = new KeyRequestDAO();
-        KeyRequestInfo reqInfo;
-        try {
-            reqInfo = reqDAO.getRequest(reqId, uriInfo, getAuthToken());
-        } catch (EBaseException e1) {
-            // failed to get request
-            logMessage = "failed to get request";
-            CMS.debug(logMessage);
-
-            e1.printStackTrace();
-            throw new PKIException(logMessage + e1.getMessage(), e1);
-        }
-        if (reqInfo == null) {
-            // request not found
-            logMessage = "No request information available.";
-            CMS.debug(logMessage);
-
-            throw new HTTPGoneException(logMessage);
+            errorOut("No wrapping method found.", new BadRequestException("No wrapping method found."));
         }
 
-        //confirm request is of the right type
-        String type = reqInfo.getRequestType();
-        if (!type.equals(IRequest.SECURITY_DATA_RECOVERY_REQUEST)) {
-            // log error
-            logMessage = "Invalid request type";
-            CMS.debug(logMessage);
-            throw new BadRequestException(logMessage);
+        // confirm that the keyIDs match
+        String keyID = request.getExtDataInString(ATTR_SERIALNO);
+        if (!data.getKeyId().toString().contentEquals(keyID)) {
+            errorOut("Key IDs do not match.", new UnauthorizedException("Key IDs do not match"));
         }
 
         //confirm that retriever is originator of request, else throw 401
         String retriever = servletRequest.getUserPrincipal().getName();
-        IRequest request;
-        try {
-            request = queue.findRequest(reqId);
-        } catch (EBaseException e) {
-            e.printStackTrace();
-            logMessage = e.getMessage();
-            CMS.debug(logMessage);
-
-            throw new PKIException(logMessage, e);
-        }
         String originator = request.getExtDataInString(IRequest.ATTR_REQUEST_OWNER);
         if (! originator.equals(retriever)) {
-            logMessage = "Data for recovery requests can only be retrieved by the originators of the request";
-            CMS.debug(logMessage);
-            throw new UnauthorizedException(logMessage);
+            errorOut("Data can only be retrieved by the originators of the request",
+                     new UnauthorizedException("Data can only be retrieved by the originators of the request"));
         }
 
         // confirm request is in approved state
-        RequestStatus status = reqInfo.getRequestStatus();
+        RequestStatus status = request.getRequestStatus();
         if (!status.equals(RequestStatus.APPROVED)) {
-            // log error
-            logMessage = "Unauthorized request.  Recovery request not approved.";
-            CMS.debug(logMessage);
-            throw new UnauthorizedException(logMessage);
+            errorOut("Recovery request not approved.",
+                     new UnauthorizedException("Recovery request not approved."));
         }
-
-        return reqInfo.getKeyId();
     }
 
     /**
@@ -405,16 +395,14 @@ public class KeyService extends PKIService implements KeyResource {
     @Override
     public Response listKeys(String clientKeyID, String status, Integer maxResults, Integer maxTime,
             Integer start, Integer size, String realm) {
-        String method = "KeyService.listKeys: ";
-        CMS.debug(method + "begins.");
-
+        CMS.debug("KeyService.listKeys: begins.");
         return createOKResponse(listKeyInfos(clientKeyID, status, maxResults, maxTime, start, size, realm));
     }
 
     public KeyInfoCollection listKeyInfos(String clientKeyID, String status, Integer maxResults, Integer maxTime,
             Integer start, Integer size, String realm) {
         String method = "KeyService.listKeyInfos: ";
-        String auditInfo = "KeyService.listKeyInfos; status =" + status;
+        auditInfo = "KeyService.listKeyInfos; status =" + status;
         CMS.debug(method + "begins.");
 
         start = start == null ? 0 : start;
@@ -444,7 +432,7 @@ public class KeyService extends PKIService implements KeyResource {
         try {
             Enumeration<IKeyRecord> e = repo.searchKeys(filter, maxResults, maxTime);
             if (e == null) {
-                auditRetrieveKey(ILogger.SUCCESS, null, clientKeyID, auditInfo);
+                auditRetrieveKey(ILogger.SUCCESS);
                 return infos;
             }
 
@@ -475,12 +463,9 @@ public class KeyService extends PKIService implements KeyResource {
             }
 
         } catch (EBaseException e) {
-            auditRetrieveKey(ILogger.FAILURE, null, clientKeyID, e.getMessage() + auditInfo);
-
-            e.printStackTrace();
-            throw new PKIException(e.getMessage(), e);
+            errorOut(e.getMessage(), new PKIException(e.getMessage(), e));
         }
-        auditRetrieveKey(ILogger.SUCCESS, null, clientKeyID, auditInfo);
+        auditRetrieveKey(ILogger.SUCCESS);
 
         return infos;
     }
@@ -488,7 +473,7 @@ public class KeyService extends PKIService implements KeyResource {
     @Override
     public Response getActiveKeyInfo(String clientKeyID) {
         String method = "KeyService.getActiveKeyInfo: ";
-        String auditInfo = "KeyService.getActiveKeyInfo";
+        auditInfo = "KeyService.getActiveKeyInfo";
         CMS.debug(method + "begins.");
 
         KeyInfoCollection infos = listKeyInfos(
@@ -517,15 +502,13 @@ public class KeyService extends PKIService implements KeyResource {
                     throw new PKIException(e.toString(), e);
                 }
 
-                auditRetrieveKey(ILogger.SUCCESS, null, clientKeyID, auditInfo);
+                auditRetrieveKey(ILogger.SUCCESS);
 
                 return createOKResponse(info);
             }
         }
-        String message = "Key not found.";
-        auditRetrieveKey(ILogger.FAILURE, null, clientKeyID, message + auditInfo);
-
-        throw new ResourceNotFoundException(auditInfo + ":" + message);
+        errorOut("Key not found.", new ResourceNotFoundException("Key not found"));
+        return null;  //needed to keep compiler happy
     }
 
     public KeyInfo createKeyDataInfo(IKeyRecord rec, boolean getPublicKey) throws EBaseException {
@@ -590,23 +573,30 @@ public class KeyService extends PKIService implements KeyResource {
         return filter;
     }
 
-    public void auditRetrieveKey(String status, RequestId requestID, KeyId keyID, String reason) {
-        auditRetrieveKey(status, requestID != null ? requestID.toString(): "null",
-                keyID != null ? keyID.toString(): "null", reason);
-    }
-
-    public void auditRetrieveKey(String status, String requestID, String keyID, String reason) {
+    public void auditRetrieveKey(String status, String reason) {
         String msg = CMS.getLogMessage(
                 LOGGING_SIGNED_AUDIT_SECURITY_DATA_RETRIEVE_KEY,
                 servletRequest.getUserPrincipal().getName(),
                 status,
-                requestID,
-                keyID,
-                reason);
+                requestId != null ? requestId.toString(): "null",
+                keyId != null ? keyId.toString(): "null",
+                (reason != null) ? auditInfo + ";" + reason : auditInfo
+        );
         auditor.log(msg);
     }
 
-    public void auditKeyStatusChange(String status, String keyID, String oldKeyStatus, String newKeyStatus, String info) {
+    public void auditRetrieveKey(String status) {
+        auditRetrieveKey(status, null);
+    }
+
+    public void errorOut(String message, RuntimeException exception) {
+        CMS.debug(message);
+        auditRetrieveKey(ILogger.FAILURE, message);
+        throw exception;
+    }
+
+    public void auditKeyStatusChange(String status, String keyID, String oldKeyStatus,
+            String newKeyStatus, String info) {
         String msg = CMS.getLogMessage(
                 LOGGING_SIGNED_AUDIT_KEY_STATUS_CHANGE,
                 servletRequest.getUserPrincipal().getName(),
@@ -625,7 +615,7 @@ public class KeyService extends PKIService implements KeyResource {
      */
     private KeyData recoverKey(KeyRecoveryRequest data) throws UnauthorizedException, HTTPGoneException {
         String method = "KeyService.recoverKey: ";
-        String auditInfo = "KeyService.recoverKey";
+        auditInfo = "KeyService.recoverKey";
         CMS.debug(method + "begins.");
 
         // confirm request exists
@@ -637,15 +627,13 @@ public class KeyService extends PKIService implements KeyResource {
         } catch (EBaseException e) {
         }
         if (request == null) {
-            auditInfo = method + "No request record.";
-            throw new HTTPGoneException(auditInfo);
+            errorOut("No request record", new HTTPGoneException("No request record"));
         }
         String type = request.getRequestType();
         RequestStatus status = request.getRequestStatus();
         if (!IRequest.KEYRECOVERY_REQUEST.equals(type) ||
             !status.equals(RequestStatus.APPROVED)) {
-            auditInfo = method + "Unauthorized request.";
-            throw new UnauthorizedException(auditInfo);
+            errorOut("Request not approved", new UnauthorizedException("Request not approved"));
         }
 
         String passphrase = data.getPassphrase();
@@ -655,8 +643,8 @@ public class KeyService extends PKIService implements KeyResource {
         } catch (EBaseException e) {
         }
         if (pkcs12 == null) {
-            auditInfo = method + "pkcs12 null; Key not recovered.";
-            throw new HTTPGoneException(auditInfo);
+            errorOut("pkcs12 null; Key not recovered.",
+                     new HTTPGoneException("pkcs12 null; Key not recovered"));
         }
         String pkcs12base64encoded = Utils.base64encode(pkcs12);
 
@@ -675,7 +663,7 @@ public class KeyService extends PKIService implements KeyResource {
     @Override
     public Response getKeyInfo(KeyId keyId) {
         String method = "KeyService.getKeyInfo: ";
-        String auditInfo = "KeyService.getKeyInfo";
+        auditInfo = "KeyService.getKeyInfo";
         CMS.debug(method + "begins.");
 
         IKeyRecord rec = null;
@@ -683,24 +671,18 @@ public class KeyService extends PKIService implements KeyResource {
             rec = repo.readKeyRecord(keyId.toBigInteger());
             authz.checkRealm(rec.getRealm(), getAuthToken(), rec.getOwnerName(), "certServer.kra.key", "read");
             KeyInfo info = createKeyDataInfo(rec, true);
-            auditRetrieveKey(ILogger.SUCCESS, null, keyId, auditInfo);
+            auditRetrieveKey(ILogger.SUCCESS);
 
             return createOKResponse(info);
         } catch (EAuthzAccessDenied e) {
-            auditInfo = method + "Unauthorized access for key record";
-            auditRetrieveKey(ILogger.FAILURE, null, keyId, auditInfo);
-            throw new UnauthorizedException(auditInfo, e);
+            errorOut("Unauthorized access for key record",
+                      new UnauthorizedException("Unauthorized access for key record", e));
         } catch (EDBRecordNotFoundException e) {
-            auditInfo = method + e.getMessage();
-            auditRetrieveKey(ILogger.FAILURE, null, keyId, auditInfo);
-            throw new KeyNotFoundException(keyId, "key not found", e);
+            errorOut(e.getMessage(), new KeyNotFoundException(keyId, "key not found", e));
         } catch (Exception e) {
-            auditInfo = method + "Unable to retrieve key record: " + e.getMessage();
-            auditRetrieveKey(ILogger.FAILURE, null, keyId, auditInfo);
-            CMS.debug(auditInfo);
-            e.printStackTrace();
-            throw new PKIException(e.getMessage(), e);
+            errorOut("Unable to retrieve key record: " + e.getMessage(), new PKIException(e.getMessage(), e));
         }
+        return null; // keep compiler happy
     }
 
     private IAuthToken getAuthToken() {
@@ -714,7 +696,7 @@ public class KeyService extends PKIService implements KeyResource {
     public Response modifyKeyStatus(KeyId keyId, String status) {
         String method = "KeyService.modifyKeyStatus: ";
         //TODO: what was the original status?  find it and record that in Info as well
-        String auditInfo = "KeyService.modifyKeyStatus";
+        auditInfo = "KeyService.modifyKeyStatus";
 
         CMS.debug(method + "begins.");
         IKeyRecord rec = null;
@@ -748,5 +730,8 @@ public class KeyService extends PKIService implements KeyResource {
         }
     }
 
+    private String getRequestor() {
+        return servletRequest.getUserPrincipal().getName();
+    }
 
 }
diff --git a/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java b/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java
index 8238c10f13a5a4eaceeb10d97f520c1cd1727e88..bab7d3d142dc44bd1c4c3624b7d5c88aa2a2dc5b 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java
@@ -202,7 +202,7 @@ public class KeyRequestDAO extends CMSRequestDAO {
             throw new BadRequestException("Can not archive already active existing key!");
         }
 
-        IRequest request = queue.newRequest(IRequest.SECURITY_DATA_ENROLLMENT_REQUEST);
+        IRequest request = queue.newRequest(IRequest.SECURITY_DATA_ENROLLMENT_REQUEST, kra.isEphemeral(realm));
 
         if (pkiArchiveOptions != null) {
             request.setExtData(REQUEST_ARCHIVE_OPTIONS, pkiArchiveOptions);
@@ -227,34 +227,36 @@ public class KeyRequestDAO extends CMSRequestDAO {
             request.setRealm(realm);
         }
 
-        queue.processRequest(request);
-
-        queue.markAsServiced(request);
+        if (!kra.isEphemeral(realm)) {
+            queue.processRequest(request);
+            queue.markAsServiced(request);
+        } else {
+            kra.processSynchronousRequest(request);
+        }
 
         return createKeyRequestResponse(request, uriInfo);
     }
 
-    /**
-     * Submits a key recovery request.
-     *
-     * @param data
-     * @param uriInfo
-     * @param requestor
-     * @param authToken
-     * @return info on the recovery request created
-     * @throws EBaseException
-     */
-    public KeyRequestResponse submitRequest(KeyRecoveryRequest data, UriInfo uriInfo, String requestor,
-            IAuthToken authToken)
-            throws EBaseException {
-        // set data using request.setExtData(field, data)
+    public IRequest createRecoveryRequest(KeyRecoveryRequest data, UriInfo uriInfo, String requestor,
+            IAuthToken authToken, boolean ephemeral) throws EBaseException{
+        if (data == null) {
+            throw new BadRequestException("Invalid request.");
+        }
+
+        /*if (data.getCertificate() == null &&
+            data.getTransWrappedSessionKey() == null &&
+            data.getSessionWrappedPassphrase() != null) {
+            throw new BadRequestException("No wrapped session key.");
+        }*/
+
+        if (requestor == null) {
+            throw new UnauthorizedException("Recovery must be initiated by an agent");
+        }
 
         String wrappedSessionKeyStr = data.getTransWrappedSessionKey();
         String wrappedPassPhraseStr = data.getSessionWrappedPassphrase();
         String nonceDataStr = data.getNonceData();
 
-        IRequest request = queue.newRequest(IRequest.SECURITY_DATA_RECOVERY_REQUEST);
-
         KeyId keyId = data.getKeyId();
         IKeyRecord rec = null;
         try {
@@ -271,12 +273,18 @@ public class KeyRequestDAO extends CMSRequestDAO {
             throw new UnauthorizedException("Agent not authorized by realm", e);
         }
 
+        IRequest request = queue.newRequest(IRequest.SECURITY_DATA_RECOVERY_REQUEST, ephemeral);
+
+        if (rec.getRealm() != null) {
+            request.setRealm(rec.getRealm());
+        }
+
         Hashtable<String, Object> requestParams;
 
         requestParams = ((IKeyRecoveryAuthority) authority).createVolatileRequest(request.getRequestId());
 
         if (requestParams == null) {
-            throw new EBaseException("Can not create Volatile params in submitRequest!");
+            throw new EBaseException("Can not create Volatile params in createRecoveryRequest!");
         }
 
         CMS.debug("Create volatile  params for recovery request. " + requestParams);
@@ -298,10 +306,25 @@ public class KeyRequestDAO extends CMSRequestDAO {
         request.setExtData(IRequest.ATTR_REQUEST_OWNER, requestor);
         request.setExtData(IRequest.ATTR_APPROVE_AGENTS, requestor);
 
-        if (rec.getRealm() != null) {
-            request.setRealm(rec.getRealm());
-        }
+        // do we need to destroy the requestParams?
 
+        return request;
+    }
+
+    /**
+     * Submits a key recovery request.
+     *
+     * @param data
+     * @param uriInfo
+     * @param requestor
+     * @param authToken
+     * @return info on the recovery request created
+     * @throws EBaseException
+     */
+    public KeyRequestResponse submitRequest(KeyRecoveryRequest data, UriInfo uriInfo, String requestor,
+            IAuthToken authToken)
+            throws EBaseException {
+        IRequest request = createRecoveryRequest(data, uriInfo, requestor, authToken, false);
         queue.processRequest(request);
 
         return createKeyRequestResponse(request, uriInfo);
diff --git a/base/server/cmscore/src/com/netscape/cmscore/request/ARequestQueue.java b/base/server/cmscore/src/com/netscape/cmscore/request/ARequestQueue.java
index fae48f2f7fd275f107fa0c10407ec0c9b9dd1b8b..014c0857bda26ad57a5a3db0f908c0ffe4223164 100644
--- a/base/server/cmscore/src/com/netscape/cmscore/request/ARequestQueue.java
+++ b/base/server/cmscore/src/com/netscape/cmscore/request/ARequestQueue.java
@@ -105,6 +105,11 @@ public abstract class ARequestQueue
             throws EBaseException;
 
     /**
+     * Create a new synchronous request ID
+     */
+    protected abstract RequestId newEphemeralRequestId();
+
+    /**
      * Read a request from the persistant store. (abstract)
      * <p>
      * This function is called to create the in-memory version of a request object.
@@ -243,18 +248,29 @@ public abstract class ARequestQueue
         return r;
     }
 
+    public IRequest newRequest(String requestType) throws EBaseException {
+        return newRequest(requestType, false);
+    }
+
     /**
      * Implements IRequestQueue.newRequest
      * <p>
      *
      * @see IRequestQueue#newRequest
      */
-    public IRequest newRequest(String requestType)
+    public IRequest newRequest(String requestType, boolean ephemeral)
             throws EBaseException {
         if (requestType == null) {
             throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_REQUEST_TYPE", "null"));
         }
-        RequestId rId = newRequestId();
+
+        RequestId rId = null;
+        if(! ephemeral) {
+            rId = newRequestId();
+        } else {
+            rId = newEphemeralRequestId();
+        }
+
         IRequest r = createRequest(rId, requestType);
 
         // Commented out the lock call because unlock is never called.
diff --git a/base/server/cmscore/src/com/netscape/cmscore/request/RequestQueue.java b/base/server/cmscore/src/com/netscape/cmscore/request/RequestQueue.java
index 6159a2ed50ceb8882c1a3932fbe9a8524d55ca99..d7e7c6e0b66f9b68d0416f4777468e0612381a5a 100644
--- a/base/server/cmscore/src/com/netscape/cmscore/request/RequestQueue.java
+++ b/base/server/cmscore/src/com/netscape/cmscore/request/RequestQueue.java
@@ -18,6 +18,7 @@
 package com.netscape.cmscore.request;
 
 import java.math.BigInteger;
+import java.security.SecureRandom;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.Hashtable;
@@ -46,6 +47,7 @@ import com.netscape.cmscore.util.Debug;
 public class RequestQueue
         extends ARequestQueue
         implements IRequestMod {
+
     // ARequestQueue.newRequestId
     protected RequestId newRequestId()
             throws EBaseException {
@@ -57,6 +59,12 @@ public class RequestQueue
         return rid;
     }
 
+    protected RequestId newEphemeralRequestId() {
+        long id = System.currentTimeMillis() * 10000 + new SecureRandom().nextInt(10000);
+        RequestId rid = new RequestId(id);
+        return rid;
+    }
+
     protected IRequest readRequest(RequestId id) {
         RequestRecord record;
 
-- 
2.7.4

