This is an automated email from the ASF dual-hosted git repository.

mehul pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/master by this push:
     new e5afd84  RANGER-2616 : Add reencryptEncryptedKey, batch reencryption 
interface to KMS and improve logs
e5afd84 is described below

commit e5afd844a3020c6c9872481c6dd76a3489dd5c19
Author: fatimaawez <[email protected]>
AuthorDate: Tue Nov 5 16:23:07 2019 +0530

    RANGER-2616 : Add reencryptEncryptedKey, batch reencryption interface to 
KMS and improve logs
    
    Signed-off-by: Mehul Parikh <[email protected]>
---
 ...agerKeyGeneratorKeyProviderCryptoExtension.java |  22 +-
 .../apache/hadoop/crypto/key/kms/server/KMS.java   | 712 ++++++++++++---------
 .../hadoop/crypto/key/kms/server/KMSAudit.java     |   7 +-
 .../key/kms/server/KMSExceptionsProvider.java      |   7 +-
 .../crypto/key/kms/server/KMSJSONReader.java       |   7 +-
 .../crypto/key/kms/server/KMSServerJSONUtils.java  |  34 +-
 .../hadoop/crypto/key/kms/server/KMSWebApp.java    |  17 +
 .../kms/server/KeyAuthorizationKeyProvider.java    |  32 +
 .../hadoop/crypto/key/kms/server/TestKMSAudit.java |  62 +-
 9 files changed, 566 insertions(+), 334 deletions(-)

diff --git 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/EagerKeyGeneratorKeyProviderCryptoExtension.java
 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/EagerKeyGeneratorKeyProviderCryptoExtension.java
index 854c831..675a357 100644
--- 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/EagerKeyGeneratorKeyProviderCryptoExtension.java
+++ 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/EagerKeyGeneratorKeyProviderCryptoExtension.java
@@ -139,13 +139,12 @@ public class EagerKeyGeneratorKeyProviderCryptoExtension
        @Override
        public EncryptedKeyVersion reencryptEncryptedKey(EncryptedKeyVersion 
arg0)
                        throws IOException, GeneralSecurityException {
-               // TODO Auto-generated method stub
-               return null;
+      return keyProviderCryptoExtension.reencryptEncryptedKey(arg0);
        }
 
        @Override
        public void reencryptEncryptedKeys(List<EncryptedKeyVersion> arg0) 
throws IOException, GeneralSecurityException {
-               // TODO Auto-generated method stub
+      keyProviderCryptoExtension.reencryptEncryptedKeys(arg0);
        }
 
   }
@@ -165,6 +164,16 @@ public class EagerKeyGeneratorKeyProviderCryptoExtension
         new CryptoExtension(conf, keyProviderCryptoExtension));
   }
 
+  /**
+     * Roll a new version of the given key generating the material for it.
+     * <p>
+     * Due to the caching on the ValueQueue, even after a rollNewVersion call,
+     * {@link #generateEncryptedKey(String)} may still return an old key - even
+     * when we drain the queue here, the async thread may later fill in old 
keys.
+     * This is acceptable since old version keys are still able to decrypt, and
+     * client shall make no assumptions that it will get a new versioned key
+     * after rollNewVersion.
+  */
   @Override
   public KeyVersion rollNewVersion(String name)
       throws NoSuchAlgorithmException, IOException {
@@ -180,4 +189,11 @@ public class EagerKeyGeneratorKeyProviderCryptoExtension
     getExtension().drain(name);
     return keyVersion;
   }
+
+  @Override
+  public void invalidateCache(String name) throws IOException {
+    super.invalidateCache(name);
+    getExtension().drain(name);
+    }
+
 }
diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMS.java 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMS.java
index 04cc984..f67f68b 100644
--- a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMS.java
+++ b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMS.java
@@ -17,6 +17,10 @@
  */
 package org.apache.hadoop.crypto.key.kms.server;
 
+import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
+
+import org.apache.hadoop.util.KMSUtil;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.crypto.key.KeyProvider;
@@ -29,7 +33,7 @@ import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.crypto.key.kms.KMSClientProvider;
 import org.apache.hadoop.crypto.key.kms.server.KMSACLsType.Type;
 import 
org.apache.hadoop.security.token.delegation.web.HttpUserGroupInformation;
-import org.apache.hadoop.util.KMSUtil;
+import javax.ws.rs.core.UriBuilder;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
@@ -47,7 +51,6 @@ import javax.ws.rs.core.Response;
 
 import java.io.IOException;
 import java.net.URI;
-import java.net.URISyntaxException;
 import java.security.PrivilegedExceptionAction;
 import java.util.ArrayList;
 import java.util.LinkedList;
@@ -56,6 +59,10 @@ import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import static org.apache.hadoop.util.KMSUtil.checkNotEmpty;
+import static org.apache.hadoop.util.KMSUtil.checkNotNull;
 /**
  * Class providing the REST bindings, via Jersey, for the KMS.
  */
@@ -64,16 +71,18 @@ import java.util.regex.Pattern;
 public class KMS {
 
   public static enum KMSOp {
-    CREATE_KEY, DELETE_KEY, ROLL_NEW_VERSION,
+    CREATE_KEY, DELETE_KEY, ROLL_NEW_VERSION, INVALIDATE_CACHE,
     GET_KEYS, GET_KEYS_METADATA,
     GET_KEY_VERSIONS, GET_METADATA, GET_KEY_VERSION, GET_CURRENT_KEY,
-    GENERATE_EEK, DECRYPT_EEK
+    GENERATE_EEK, DECRYPT_EEK, REENCRYPT_EEK, REENCRYPT_EEK_BATCH
   }
 
   private static final String KEY_NAME_VALIDATION = 
"[a-z,A-Z,0-9](?!.*--)(?!.*__)(?!.*-_)(?!.*_-)[\\w\\-\\_]*";
 
   private KeyProviderCryptoExtension provider;
   private KMSAudit kmsAudit;
+  static final Logger LOG = LoggerFactory.getLogger(KMS.class);
+  private static final int MAX_NUM_PER_BATCH = 10000;
 
   public KMS() throws Exception {
     provider = KMSWebApp.getKeyProvider();
@@ -81,7 +90,7 @@ public class KMS {
   }
 
   private void assertAccess(Type aclType, UserGroupInformation ugi,
-      KMSOp operation, String clientIp) throws AccessControlException {
+    KMSOp operation, String clientIp) throws AccessControlException {
     KMSWebApp.getACLs().assertAccess(aclType, ugi, operation, null, clientIp);
   }
 
@@ -96,9 +105,9 @@ public class KMS {
         keyVersion.getVersionName(), null);
   }
 
-  private static URI getKeyURI(String name) throws URISyntaxException {
-    return new URI(KMSRESTConstants.SERVICE_VERSION + "/" +
-        KMSRESTConstants.KEY_RESOURCE + "/" + name);
+  private static URI getKeyURI(String domain, String keyName) {
+    return UriBuilder.fromPath("{a}/{b}/{c}")
+      .build(domain, KMSRESTConstants.KEY_RESOURCE, keyName);
   }
 
   @POST
@@ -107,133 +116,166 @@ public class KMS {
   @Produces(MediaType.APPLICATION_JSON)
   @SuppressWarnings("unchecked")
   public Response createKey(Map jsonKey, @Context HttpServletRequest request) 
throws Exception {
-    KMSWebApp.getAdminCallsMeter().mark();
-    UserGroupInformation user = HttpUserGroupInformation.get();
-    final String name = (String) jsonKey.get(KMSRESTConstants.NAME_FIELD);
-    KMSUtil.checkNotEmpty(name, KMSRESTConstants.NAME_FIELD);
-    validateKeyName(name);
-    assertAccess(Type.CREATE, user, KMSOp.CREATE_KEY, name, 
request.getRemoteAddr());
-    String cipher = (String) jsonKey.get(KMSRESTConstants.CIPHER_FIELD);
-    final String material = (String) 
jsonKey.get(KMSRESTConstants.MATERIAL_FIELD);
-    int length = (jsonKey.containsKey(KMSRESTConstants.LENGTH_FIELD))
-                 ? (Integer) jsonKey.get(KMSRESTConstants.LENGTH_FIELD) : 0;
-    String description = (String)
-        jsonKey.get(KMSRESTConstants.DESCRIPTION_FIELD);
-    Map<String, String> attributes = (Map<String, String>)
-        jsonKey.get(KMSRESTConstants.ATTRIBUTES_FIELD);
-    if (material != null) {
-      assertAccess(Type.SET_KEY_MATERIAL, user,
-          KMSOp.CREATE_KEY, name, request.getRemoteAddr());
-    }
-    final KeyProvider.Options options = new KeyProvider.Options(
+    try{
+      LOG.info("Entering createKey Method.");
+      KMSWebApp.getAdminCallsMeter().mark();
+      UserGroupInformation user = HttpUserGroupInformation.get();
+      final String name = (String) jsonKey.get(KMSRESTConstants.NAME_FIELD);
+      checkNotEmpty(name, KMSRESTConstants.NAME_FIELD);
+      validateKeyName(name);
+      assertAccess(Type.CREATE, user, KMSOp.CREATE_KEY, name, 
request.getRemoteAddr());
+      String cipher = (String) jsonKey.get(KMSRESTConstants.CIPHER_FIELD);
+      final String material;
+      material = (String) jsonKey.get(KMSRESTConstants.MATERIAL_FIELD);
+      int length = (jsonKey.containsKey(KMSRESTConstants.LENGTH_FIELD)) ? 
(Integer) jsonKey.get(KMSRESTConstants.LENGTH_FIELD) : 0;
+      String description = (String) 
jsonKey.get(KMSRESTConstants.DESCRIPTION_FIELD);
+      LOG.debug("Creating key with name {}, cipher being used{}, "
+      +"length of key {}, description of key {}", name, cipher,length, 
description);
+      Map<String, String> attributes = (Map<String, String>)
+      jsonKey.get(KMSRESTConstants.ATTRIBUTES_FIELD);
+      if (material != null) {
+        assertAccess(Type.SET_KEY_MATERIAL, user,
+        KMSOp.CREATE_KEY, name, request.getRemoteAddr());
+      }
+      final KeyProvider.Options options = new KeyProvider.Options(
         KMSWebApp.getConfiguration());
-    if (cipher != null) {
-      options.setCipher(cipher);
-    }
-    if (length != 0) {
-      options.setBitLength(length);
-    }
-    options.setDescription(description);
-    options.setAttributes(attributes);
-
-    KeyProvider.KeyVersion keyVersion = user.doAs(
+      if (cipher != null) {
+        options.setCipher(cipher);
+      }
+      if (length != 0) {
+        options.setBitLength(length);
+      }
+      options.setDescription(description);
+      options.setAttributes(attributes);
+      KeyProvider.KeyVersion keyVersion = user.doAs(
         new PrivilegedExceptionAction<KeyVersion>() {
           @Override
           public KeyVersion run() throws Exception {
-            KeyProvider.KeyVersion keyVersion = (material != null)
-              ? provider.createKey(name, Base64.decodeBase64(material), 
options)
-              : provider.createKey(name, options);
+            KeyProvider.KeyVersion keyVersion = (material != null) ? 
provider.createKey(name, Base64.decodeBase64(material),options) : 
provider.createKey(name, options);
             provider.flush();
             return keyVersion;
           }
         }
-    );
-
-    kmsAudit.ok(user, KMSOp.CREATE_KEY, name, "UserProvidedMaterial:" +
-        (material != null) + " Description:" + description);
-
-    if (!KMSWebApp.getACLs().hasAccess(Type.GET, user, 
request.getRemoteAddr())) {
-      keyVersion = removeKeyMaterial(keyVersion);
+      );
+      kmsAudit.ok(user, KMSOp.CREATE_KEY, name, "UserProvidedMaterial:" + 
(material != null) + " Description:" + description);
+      if (!KMSWebApp.getACLs().hasAccess(Type.GET, user, 
request.getRemoteAddr())) {
+        keyVersion = removeKeyMaterial(keyVersion);
+      }
+      Map json = KMSUtil.toJSON(keyVersion);
+      String requestURL = KMSMDCFilter.getURL();
+      int idx = requestURL.lastIndexOf(KMSRESTConstants.KEYS_RESOURCE);
+      requestURL = requestURL.substring(0, idx);
+      LOG.info("Exiting createKey Method.");
+      return Response.created(getKeyURI(KMSRESTConstants.SERVICE_VERSION, 
name))
+       .type(MediaType.APPLICATION_JSON)
+       .header("Location", getKeyURI(requestURL, name)).entity(json).build();
+    }
+    catch (Exception e) {
+      LOG.debug("Exception in createKey.", e);
+      throw e;
     }
-    Map json = KMSServerJSONUtils.toJSON(keyVersion);
-    String requestURL = KMSMDCFilter.getURL();
-    int idx = requestURL.lastIndexOf(KMSRESTConstants.KEYS_RESOURCE);
-    requestURL = requestURL.substring(0, idx);
-    String keyURL = requestURL + KMSRESTConstants.KEY_RESOURCE + "/" + name;
-    return Response.created(getKeyURI(name)).type(MediaType.APPLICATION_JSON).
-        header("Location", keyURL).entity(json).build();
   }
 
   private void validateKeyName(String name) {
-         Pattern pattern = Pattern.compile(KEY_NAME_VALIDATION);
-         Matcher matcher = pattern.matcher(name);
+      Pattern pattern = Pattern.compile(KEY_NAME_VALIDATION);
+      Matcher matcher = pattern.matcher(name);
          if(!matcher.matches()){
                  throw new IllegalArgumentException("Key Name : " + name +
                          ", should start with alpha/numeric letters and can 
have special characters - (hypen) or _ (underscore)");
          }
   }
 
-  @DELETE
-  @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}")
-  public Response deleteKey(@PathParam("name") final String name, @Context 
HttpServletRequest request)
-      throws Exception {
-    KMSWebApp.getAdminCallsMeter().mark();
-    UserGroupInformation user = HttpUserGroupInformation.get();
-    assertAccess(Type.DELETE, user, KMSOp.DELETE_KEY, name, 
request.getRemoteAddr());
-    KMSUtil.checkNotEmpty(name, "name");
-
-    user.doAs(new PrivilegedExceptionAction<Void>() {
-      @Override
-      public Void run() throws Exception {
-        provider.deleteKey(name);
-        provider.flush();
-        return null;
-      }
-    });
-
-    kmsAudit.ok(user, KMSOp.DELETE_KEY, name, "");
-
-    return Response.ok().build();
-  }
+    @DELETE
+    @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}")
+    public Response deleteKey(@PathParam("name") final String name, @Context 
HttpServletRequest request)
+        throws Exception {
+      try {
+        LOG.info("Entering deleteKey method.");
+        KMSWebApp.getAdminCallsMeter().mark();
+        UserGroupInformation user = HttpUserGroupInformation.get();
+        assertAccess(Type.DELETE, user, KMSOp.DELETE_KEY, name, 
request.getRemoteAddr());
+        checkNotEmpty(name, "name");
+        LOG.debug("Deleting key with name {}.", name);
+        user.doAs(new PrivilegedExceptionAction<Void>() {
+          @Override
+          public Void run() throws Exception {
+            provider.deleteKey(name);
+            provider.flush();
+            return null;
+          }
+        });
+        kmsAudit.ok(user, KMSOp.DELETE_KEY, name, "");
+        LOG.info("Exiting deleteKey method.");
+        return Response.ok().build();
+      } catch (Exception e) {
+        LOG.debug("Exception in deleteKey.", e);
+        throw e;
+        }
+    }
 
   @POST
   @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}")
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
-  public Response rolloverKey(@PathParam("name") final String name,
-      Map jsonMaterial, @Context HttpServletRequest request) throws Exception {
-    KMSWebApp.getAdminCallsMeter().mark();
-    UserGroupInformation user = HttpUserGroupInformation.get();
-    assertAccess(Type.ROLLOVER, user, KMSOp.ROLL_NEW_VERSION, name, 
request.getRemoteAddr());
-    KMSUtil.checkNotEmpty(name, "name");
-    final String material = (String)
-        jsonMaterial.get(KMSRESTConstants.MATERIAL_FIELD);
-    if (material != null) {
-      assertAccess(Type.SET_KEY_MATERIAL, user,
-          KMSOp.ROLL_NEW_VERSION, name, request.getRemoteAddr());
+  public Response rolloverKey(@PathParam("name") final String name, Map 
jsonMaterial, @Context HttpServletRequest request) throws Exception {
+    try {
+      LOG.info("Entering rolloverKey Method.");
+      KMSWebApp.getAdminCallsMeter().mark();
+      UserGroupInformation user = HttpUserGroupInformation.get();
+      assertAccess(Type.ROLLOVER, user, KMSOp.ROLL_NEW_VERSION, name, 
request.getRemoteAddr());
+      checkNotEmpty(name, "name");
+      LOG.debug("Rolling key with name {}.", name);
+      final String material = (String) 
jsonMaterial.get(KMSRESTConstants.MATERIAL_FIELD);
+      if (material != null) {
+        assertAccess(Type.SET_KEY_MATERIAL, user, KMSOp.ROLL_NEW_VERSION, 
name, request.getRemoteAddr());
+      }
+      KeyProvider.KeyVersion keyVersion = user.doAs(new 
PrivilegedExceptionAction<KeyVersion>(){
+        @Override
+        public KeyVersion run() throws Exception {KeyVersion keyVersion = 
(material != null)
+        ? provider.rollNewVersion(name, Base64.decodeBase64(material)): 
provider.rollNewVersion(name);
+          provider.flush();
+          return keyVersion;
+        }
+      });
+      kmsAudit.ok(user, KMSOp.ROLL_NEW_VERSION, name, "UserProvidedMaterial:" +
+      (material != null) + " NewVersion:" + keyVersion.getVersionName());
+      if (!KMSWebApp.getACLs().hasAccess(Type.GET, user, 
request.getRemoteAddr())) {
+        keyVersion = removeKeyMaterial(keyVersion);
+      }
+      Map json = KMSUtil.toJSON(keyVersion);
+      LOG.info("Exiting rolloverKey Method.");
+      return 
Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
+    } catch (Exception e) {
+      LOG.debug("Exception in rolloverKey.", e);
+      throw e;
     }
+  }
 
-    KeyProvider.KeyVersion keyVersion = user.doAs(
-        new PrivilegedExceptionAction<KeyVersion>() {
-          @Override
-          public KeyVersion run() throws Exception {
-            KeyVersion keyVersion = (material != null)
-              ? provider.rollNewVersion(name, Base64.decodeBase64(material))
-              : provider.rollNewVersion(name);
-            provider.flush();
-            return keyVersion;
-          }
+  @POST
+  @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}/" + 
KMSRESTConstants.INVALIDATECACHE_RESOURCE)
+  public Response invalidateCache(@PathParam("name") final String name) throws 
Exception {
+    try {
+      LOG.info("Entering invalidateCache Method.");
+      KMSWebApp.getAdminCallsMeter().mark();
+      checkNotEmpty(name, "name");
+      UserGroupInformation user = HttpUserGroupInformation.get();
+      assertAccess(Type.ROLLOVER, user, KMSOp.INVALIDATE_CACHE, name);
+      LOG.debug("Invalidating cache with key name {}.", name);
+      user.doAs(new PrivilegedExceptionAction<Void>() {
+        @Override
+        public Void run() throws Exception {
+          provider.invalidateCache(name);
+          provider.flush();
+          return null;
         }
-    );
-
-    kmsAudit.ok(user, KMSOp.ROLL_NEW_VERSION, name, "UserProvidedMaterial:" +
-        (material != null) + " NewVersion:" + keyVersion.getVersionName());
-
-    if (!KMSWebApp.getACLs().hasAccess(Type.GET, user, 
request.getRemoteAddr())) {
-      keyVersion = removeKeyMaterial(keyVersion);
+      });
+      kmsAudit.ok(user, KMSOp.INVALIDATE_CACHE, name, "");
+      LOG.info("Exiting invalidateCache for key name {}.", name);
+      return Response.ok().build();
+    } catch (Exception e) {
+      LOG.debug("Exception in invalidateCache for key name {}.", name, e);
+      throw e;
     }
-    Map json = KMSServerJSONUtils.toJSON(keyVersion);
-    return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
   }
 
   @GET
@@ -241,52 +283,65 @@ public class KMS {
   @Produces(MediaType.APPLICATION_JSON)
   public Response getKeysMetadata(@QueryParam(KMSRESTConstants.KEY)
       List<String> keyNamesList, @Context HttpServletRequest request) throws 
Exception {
-    KMSWebApp.getAdminCallsMeter().mark();
-    UserGroupInformation user = HttpUserGroupInformation.get();
-    final String[] keyNames = keyNamesList.toArray(
-        new String[keyNamesList.size()]);
-    assertAccess(Type.GET_METADATA, user, KMSOp.GET_KEYS_METADATA, 
request.getRemoteAddr());
-
-    KeyProvider.Metadata[] keysMeta = user.doAs(
-        new PrivilegedExceptionAction<KeyProvider.Metadata[]>() {
-          @Override
-          public KeyProvider.Metadata[] run() throws Exception {
-            return provider.getKeysMetadata(keyNames);
-          }
+    try {
+      LOG.info("Entering getKeysMetadata method.");
+      KMSWebApp.getAdminCallsMeter().mark();
+      UserGroupInformation user = HttpUserGroupInformation.get();
+      final String[] keyNames = keyNamesList.toArray( new 
String[keyNamesList.size()]);
+      assertAccess(Type.GET_METADATA, user, KMSOp.GET_KEYS_METADATA, 
request.getRemoteAddr());
+      KeyProvider.Metadata[] keysMeta = user.doAs(new 
PrivilegedExceptionAction<KeyProvider.Metadata[]>() {
+        @Override
+        public KeyProvider.Metadata[] run() throws Exception {
+          return provider.getKeysMetadata(keyNames);
         }
-    );
-
-    Object json = KMSServerJSONUtils.toJSON(keyNames, keysMeta);
-    kmsAudit.ok(user, KMSOp.GET_KEYS_METADATA, "");
-    return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
+      });
+      Object json = KMSServerJSONUtils.toJSON(keyNames, keysMeta);
+      kmsAudit.ok(user, KMSOp.GET_KEYS_METADATA, "");
+      LOG.info("Exiting getKeysMetadata method.");
+      return 
Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
+    } catch (Exception e) {
+      LOG.debug("Exception in getKeysmetadata.", e);
+      throw e;
+    }
   }
 
   @GET
   @Path(KMSRESTConstants.KEYS_NAMES_RESOURCE)
   @Produces(MediaType.APPLICATION_JSON)
   public Response getKeyNames(@Context HttpServletRequest request) throws 
Exception {
-    KMSWebApp.getAdminCallsMeter().mark();
-    UserGroupInformation user = HttpUserGroupInformation.get();
-    assertAccess(Type.GET_KEYS, user, KMSOp.GET_KEYS, request.getRemoteAddr());
-
-    List<String> json = user.doAs(
-        new PrivilegedExceptionAction<List<String>>() {
-          @Override
-          public List<String> run() throws Exception {
-            return provider.getKeys();
-          }
+    try {
+      LOG.info("Entering getKeyNames method.");
+      KMSWebApp.getAdminCallsMeter().mark();
+      UserGroupInformation user = HttpUserGroupInformation.get();
+      assertAccess(Type.GET_KEYS, user, KMSOp.GET_KEYS, 
request.getRemoteAddr());
+      List<String> json = user.doAs(new 
PrivilegedExceptionAction<List<String>>() {
+        @Override
+        public List<String> run() throws Exception {
+          return provider.getKeys();
         }
-    );
-
-    kmsAudit.ok(user, KMSOp.GET_KEYS, "");
-    return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
+      });
+      kmsAudit.ok(user, KMSOp.GET_KEYS, "");
+      LOG.info("Exiting getKeyNames method.");
+      return 
Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
+    } catch (Exception e) {
+      LOG.debug("Exception in getkeyNames.", e);
+      throw e;
+    }
   }
 
   @GET
   @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}")
   public Response getKey(@PathParam("name") String name, @Context 
HttpServletRequest request)
       throws Exception {
-    return getMetadata(name, request);
+    try {
+      LOG.info("Entering getKey method.");
+      LOG.debug("Getting key information for key with name {}.", name);
+      LOG.info("Exiting getKey method.");
+      return getMetadata(name, request);
+    } catch (Exception e) {
+      LOG.debug("Exception in getKey.", e);
+      throw e;
+    }
   }
 
   @GET
@@ -295,23 +350,28 @@ public class KMS {
   @Produces(MediaType.APPLICATION_JSON)
   public Response getMetadata(@PathParam("name") final String name, @Context 
HttpServletRequest request)
       throws Exception {
-    UserGroupInformation user = HttpUserGroupInformation.get();
-    KMSUtil.checkNotEmpty(name, "name");
-    KMSWebApp.getAdminCallsMeter().mark();
-    assertAccess(Type.GET_METADATA, user, KMSOp.GET_METADATA, name, 
request.getRemoteAddr());
-
-    KeyProvider.Metadata metadata = user.doAs(
+    try {
+      LOG.info("Entering getMetadata method.");
+      UserGroupInformation user = HttpUserGroupInformation.get();
+      checkNotEmpty(name, "name");
+      KMSWebApp.getAdminCallsMeter().mark();
+      assertAccess(Type.GET_METADATA, user, KMSOp.GET_METADATA, name, 
request.getRemoteAddr());
+      LOG.debug("Getting metadata for key with name {}.", name);
+      KeyProvider.Metadata metadata = user.doAs(
         new PrivilegedExceptionAction<KeyProvider.Metadata>() {
-          @Override
-          public KeyProvider.Metadata run() throws Exception {
-            return provider.getMetadata(name);
-          }
+        @Override
+        public KeyProvider.Metadata run() throws Exception {
+          return provider.getMetadata(name);
         }
-    );
-
-    Object json = KMSServerJSONUtils.toJSON(name, metadata);
-    kmsAudit.ok(user, KMSOp.GET_METADATA, name, "");
-    return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
+      });
+      Object json = KMSServerJSONUtils.toJSON(name, metadata);
+      kmsAudit.ok(user, KMSOp.GET_METADATA, name, "");
+      LOG.info("Exiting getMetadata method.");
+      return 
Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
+    } catch (Exception e) {
+      LOG.debug("Exception in getMetadata.", e);
+      throw e;
+    }
   }
 
   @GET
@@ -320,25 +380,26 @@ public class KMS {
   @Produces(MediaType.APPLICATION_JSON)
   public Response getCurrentVersion(@PathParam("name") final String name, 
@Context HttpServletRequest request)
       throws Exception {
-    UserGroupInformation user = HttpUserGroupInformation.get();
-    KMSUtil.checkNotEmpty(name, "name");
-    KMSWebApp.getKeyCallsMeter().mark();
-    assertAccess(Type.GET, user, KMSOp.GET_CURRENT_KEY, name, 
request.getRemoteAddr());
-
-    KeyVersion keyVersion = user.doAs(
-        new PrivilegedExceptionAction<KeyVersion>() {
-          @Override
-          public KeyVersion run() throws Exception {
-            return provider.getCurrentKey(name);
-          }
+    try {
+      LOG.info("Entering getCurrentVersion method.");
+      UserGroupInformation user = HttpUserGroupInformation.get();
+      checkNotEmpty(name, "name");
+      KMSWebApp.getKeyCallsMeter().mark();
+      assertAccess(Type.GET, user, KMSOp.GET_CURRENT_KEY, name, 
request.getRemoteAddr());
+      LOG.debug("Getting key version for key with name {}.", name);
+      KeyVersion keyVersion = user.doAs(new 
PrivilegedExceptionAction<KeyVersion>() {
+        @Override
+        public KeyVersion run() throws Exception {
+          return provider.getCurrentKey(name);
         }
-    );
-    Object json = KMSServerJSONUtils.toJSON(keyVersion);
-    if (keyVersion != null) {
-       kmsAudit.ok(user, KMSOp.GET_CURRENT_KEY, name, "");
-       return 
Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
-    }else{
-       return 
Response.status(404).type(MediaType.APPLICATION_JSON).entity(json).build();
+      });
+      Object json = KMSUtil.toJSON(keyVersion);
+      kmsAudit.ok(user, KMSOp.GET_CURRENT_KEY, name, "");
+      LOG.info("Exiting getCurrentVersion method.");
+      return 
Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
+    } catch (Exception e) {
+      LOG.debug("Exception in getCurrentVersion.", e);
+      throw e;
     }
   }
 
@@ -347,25 +408,29 @@ public class KMS {
   @Produces(MediaType.APPLICATION_JSON)
   public Response getKeyVersion(
       @PathParam("versionName") final String versionName, @Context 
HttpServletRequest request) throws Exception {
-    UserGroupInformation user = HttpUserGroupInformation.get();
-    KMSUtil.checkNotEmpty(versionName, "versionName");
-    KMSWebApp.getKeyCallsMeter().mark();
-    assertAccess(Type.GET, user, KMSOp.GET_KEY_VERSION, 
request.getRemoteAddr());
-
-    KeyVersion keyVersion = user.doAs(
-        new PrivilegedExceptionAction<KeyVersion>() {
-          @Override
-          public KeyVersion run() throws Exception {
-            return provider.getKeyVersion(versionName);
-          }
+    try {
+      LOG.info("Entering getKeyVersion method.");
+      UserGroupInformation user = HttpUserGroupInformation.get();
+      checkNotEmpty(versionName, "versionName");
+      KMSWebApp.getKeyCallsMeter().mark();
+      assertAccess(Type.GET, user, KMSOp.GET_KEY_VERSION, 
request.getRemoteAddr());
+      LOG.debug("Getting key with version name {}.", versionName);
+      KeyVersion keyVersion = user.doAs(new 
PrivilegedExceptionAction<KeyVersion>() {
+        @Override
+        public KeyVersion run() throws Exception {
+          return provider.getKeyVersion(versionName);
         }
-    );
-
-    if (keyVersion != null) {
-      kmsAudit.ok(user, KMSOp.GET_KEY_VERSION, keyVersion.getName(), "");
+      });
+      if (keyVersion != null) {
+        kmsAudit.ok(user, KMSOp.GET_KEY_VERSION, keyVersion.getName(), "");
+      }
+      Object json = KMSUtil.toJSON(keyVersion);
+      LOG.info("Exiting getKeyVersion method.");
+      return 
Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
+    } catch (Exception e) {
+      LOG.debug("Exception in getKeyVersion.", e);
+      throw e;
     }
-    Object json = KMSServerJSONUtils.toJSON(keyVersion);
-    return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
   }
 
   @SuppressWarnings({ "rawtypes", "unchecked" })
@@ -373,52 +438,107 @@ public class KMS {
   @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}/" +
       KMSRESTConstants.EEK_SUB_RESOURCE)
   @Produces(MediaType.APPLICATION_JSON)
-  public Response generateEncryptedKeys(
-          @PathParam("name") final String name,
-          @QueryParam(KMSRESTConstants.EEK_OP) String edekOp,
-          @DefaultValue("1")
-          @QueryParam(KMSRESTConstants.EEK_NUM_KEYS) final int numKeys, 
@Context HttpServletRequest request)
-          throws Exception {
-    UserGroupInformation user = HttpUserGroupInformation.get();
-    KMSUtil.checkNotEmpty(name, "name");
-    KMSUtil.checkNotNull(edekOp, "eekOp");
-
-    Object retJSON;
-    if (edekOp.equals(KMSRESTConstants.EEK_GENERATE)) {
-      assertAccess(Type.GENERATE_EEK, user, KMSOp.GENERATE_EEK, name, 
request.getRemoteAddr());
-
-      final List<EncryptedKeyVersion> retEdeks =
-          new LinkedList<EncryptedKeyVersion>();
-      try {
-
-        user.doAs(
-            new PrivilegedExceptionAction<Void>() {
-              @Override
-              public Void run() throws Exception {
-                for (int i = 0; i < numKeys; i++) {
-                  retEdeks.add(provider.generateEncryptedKey(name));
-                }
-                return null;
+  public Response generateEncryptedKeys( @PathParam("name") final String name,
+    @QueryParam(KMSRESTConstants.EEK_OP) String edekOp,
+    @DefaultValue("1") @QueryParam(KMSRESTConstants.EEK_NUM_KEYS) final int 
numKeys,
+    @Context HttpServletRequest request) throws Exception {
+    try {
+      LOG.info("Entering generateEncryptedKeys method.");
+      UserGroupInformation user = HttpUserGroupInformation.get();
+      checkNotEmpty(name, "name");
+      checkNotNull(edekOp, "eekOp");
+      LOG.debug("Generating encrypted key with name {}, the edek Operation is 
{}.", name, edekOp);
+      Object retJSON;
+      if (edekOp.equals(KMSRESTConstants.EEK_GENERATE)) {
+        LOG.debug("edek Operation is Generate.");
+        assertAccess(Type.GENERATE_EEK, user, KMSOp.GENERATE_EEK, 
name,request.getRemoteAddr());
+        final List<EncryptedKeyVersion> retEdeks = new 
LinkedList<EncryptedKeyVersion>();
+        try {
+          user.doAs(new PrivilegedExceptionAction<Void>() {
+            @Override
+            public Void run() throws Exception {
+              LOG.debug("Generated Encrypted key for {} number of keys.", 
numKeys);
+              for (int i = 0; i < numKeys; i++) {
+                retEdeks.add(provider.generateEncryptedKey(name));
               }
+              return null;
             }
-        );
-
-      } catch (Exception e) {
-        throw new IOException(e);
+          });
+        } catch (Exception e) {
+          LOG.error("Exception in generateEncryptedKeys:", e);
+          throw new IOException(e);
+        }
+        kmsAudit.ok(user, KMSOp.GENERATE_EEK, name, "");
+        retJSON = new ArrayList();
+        for (EncryptedKeyVersion edek : retEdeks) {
+          ((ArrayList) retJSON).add(KMSUtil.toJSON(edek));
+        }
+      } else {
+        StringBuilder error;
+        error = new StringBuilder("IllegalArgumentException Wrong ");
+        error.append(KMSRESTConstants.EEK_OP);
+        error.append(" value, it must be ");
+        error.append(KMSRESTConstants.EEK_GENERATE);
+        error.append(" or ");
+        error.append(KMSRESTConstants.EEK_DECRYPT);
+        LOG.error(error.toString());
+        throw new IllegalArgumentException(error.toString());
       }
-      kmsAudit.ok(user, KMSOp.GENERATE_EEK, name, "");
-      retJSON = new ArrayList();
-      for (EncryptedKeyVersion edek : retEdeks) {
-        ((ArrayList)retJSON).add(KMSServerJSONUtils.toJSON(edek));
+      KMSWebApp.getGenerateEEKCallsMeter().mark();
+      LOG.info("Exiting generateEncryptedKeys method.");
+      return 
Response.ok().type(MediaType.APPLICATION_JSON).entity(retJSON).build();
+    } catch (Exception e) {
+      LOG.debug("Exception in generateEncryptedKeys.", e);
+      throw e;
+    }
+  }
+  @SuppressWarnings({ "rawtypes", "unchecked" })
+    @POST
+    @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}/" +
+        KMSRESTConstants.REENCRYPT_BATCH_SUB_RESOURCE)
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON )
+    public Response reencryptEncryptedKeys(
+        @PathParam("name") final String name,
+        final List<Map> jsonPayload)
+        throws Exception {
+    try {
+      LOG.info("Entering reencryptEncryptedKeys method.");
+      final Stopwatch sw = Stopwatch.createStarted();
+      checkNotEmpty(name, "name");
+      checkNotNull(jsonPayload, "jsonPayload");
+      final UserGroupInformation user = HttpUserGroupInformation.get();
+      KMSWebApp.getReencryptEEKBatchCallsMeter().mark();
+      if (jsonPayload.size() > MAX_NUM_PER_BATCH) {
+        LOG.warn("Payload size {} too big for reencryptEncryptedKeys from"
+        + " user {}.", jsonPayload.size(), user);
+      }
+      assertAccess(Type.GENERATE_EEK, user, KMSOp.REENCRYPT_EEK_BATCH,name);
+      LOG.debug("Batch reencrypting {} Encrypted Keys for key name {}", 
jsonPayload.size(), name);
+      final List<EncryptedKeyVersion> ekvs = 
KMSUtil.parseJSONEncKeyVersions(name, jsonPayload);
+      Preconditions.checkArgument(ekvs.size() == 
jsonPayload.size(),"EncryptedKey size mismatch after parsing from json");
+      for (EncryptedKeyVersion ekv : ekvs) {
+        
Preconditions.checkArgument(name.equals(ekv.getEncryptionKeyName()),"All 
EncryptedKeys must be under the given key name " + name);
+      }
+      user.doAs(new PrivilegedExceptionAction<Void>() {
+        @Override
+        public Void run() throws Exception {
+          provider.reencryptEncryptedKeys(ekvs);
+          return null;
+        }
+      });
+      List retJSON = new ArrayList<>(ekvs.size());
+      for (EncryptedKeyVersion ekv: ekvs) {
+        retJSON.add(KMSUtil.toJSON(ekv));
       }
-    } else {
-      throw new IllegalArgumentException("Wrong " + KMSRESTConstants.EEK_OP +
-          " value, it must be " + KMSRESTConstants.EEK_GENERATE + " or " +
-          KMSRESTConstants.EEK_DECRYPT);
+      kmsAudit.ok(user, KMSOp.REENCRYPT_EEK_BATCH, name,"reencrypted " + 
ekvs.size() + " keys");
+      LOG.info("reencryptEncryptedKeys {} keys for key {} took {}", 
jsonPayload.size(), name, sw.stop());
+      LOG.info("Exiting reencryptEncryptedKeys method.");
+      return 
Response.ok().type(MediaType.APPLICATION_JSON).entity(retJSON).build();
+    } catch (Exception e) {
+      LOG.debug("Exception in reencryptEncryptedKeys.", e);
+      throw e;
     }
-    KMSWebApp.getGenerateEEKCallsMeter().mark();
-    return Response.ok().type(MediaType.APPLICATION_JSON).entity(retJSON)
-        .build();
   }
 
   @SuppressWarnings("rawtypes")
@@ -426,52 +546,66 @@ public class KMS {
   @Path(KMSRESTConstants.KEY_VERSION_RESOURCE + "/{versionName:.*}/" +
       KMSRESTConstants.EEK_SUB_RESOURCE)
   @Produces(MediaType.APPLICATION_JSON)
-  public Response decryptEncryptedKey(
+  public Response handleEncryptedKeyOp(
       @PathParam("versionName") final String versionName,
       @QueryParam(KMSRESTConstants.EEK_OP) String eekOp,
       Map jsonPayload, @Context HttpServletRequest request)
       throws Exception {
-    UserGroupInformation user = HttpUserGroupInformation.get();
-    KMSUtil.checkNotEmpty(versionName, "versionName");
-    KMSUtil.checkNotNull(eekOp, "eekOp");
-
-    final String keyName = (String) jsonPayload.get(
-        KMSRESTConstants.NAME_FIELD);
-    String ivStr = (String) jsonPayload.get(KMSRESTConstants.IV_FIELD);
-    String encMaterialStr =
-        (String) jsonPayload.get(KMSRESTConstants.MATERIAL_FIELD);
-    Object retJSON;
-    if (eekOp.equals(KMSRESTConstants.EEK_DECRYPT)) {
-      assertAccess(Type.DECRYPT_EEK, user, KMSOp.DECRYPT_EEK, keyName, 
request.getRemoteAddr());
-      KMSUtil.checkNotNull(ivStr, KMSRESTConstants.IV_FIELD);
+    try {
+      LOG.info("Entering decryptEncryptedKey method.");
+      UserGroupInformation user = HttpUserGroupInformation.get();
+      checkNotEmpty(versionName, "versionName");
+      checkNotNull(eekOp, "eekOp");
+      LOG.debug("Decrypting key for {}, the edek Operation is {}.", 
versionName, eekOp);
+      final String keyName = (String) 
jsonPayload.get(KMSRESTConstants.NAME_FIELD);
+      String ivStr = (String) jsonPayload.get(KMSRESTConstants.IV_FIELD);
+      String encMaterialStr = (String) 
jsonPayload.get(KMSRESTConstants.MATERIAL_FIELD);
+      checkNotNull(ivStr, KMSRESTConstants.IV_FIELD);
       final byte[] iv = Base64.decodeBase64(ivStr);
-      KMSUtil.checkNotNull(encMaterialStr,
-          KMSRESTConstants.MATERIAL_FIELD);
+      checkNotNull(encMaterialStr, KMSRESTConstants.MATERIAL_FIELD);
       final byte[] encMaterial = Base64.decodeBase64(encMaterialStr);
-
-      KeyProvider.KeyVersion retKeyVersion = user.doAs(
-          new PrivilegedExceptionAction<KeyVersion>() {
-            @Override
-            public KeyVersion run() throws Exception {
-              return provider.decryptEncryptedKey(
-                  new KMSClientProvider.KMSEncryptedKeyVersion(keyName,
-                      versionName, iv, KeyProviderCryptoExtension.EEK,
-                      encMaterial)
-              );
-            }
+      Object retJSON;
+      if (eekOp.equals(KMSRESTConstants.EEK_DECRYPT)) {
+        KMSWebApp.getDecryptEEKCallsMeter().mark();
+        assertAccess(Type.DECRYPT_EEK, user, KMSOp.DECRYPT_EEK, keyName, 
request.getRemoteAddr());
+        KeyProvider.KeyVersion retKeyVersion = user.doAs(new 
PrivilegedExceptionAction<KeyVersion>() {
+          @Override
+          public KeyVersion run() throws Exception {
+            return provider.decryptEncryptedKey(new 
KMSClientProvider.KMSEncryptedKeyVersion(
+            keyName, versionName, 
iv,KeyProviderCryptoExtension.EEK,encMaterial));
           }
-      );
-
-      retJSON = KMSServerJSONUtils.toJSON(retKeyVersion);
-      kmsAudit.ok(user, KMSOp.DECRYPT_EEK, keyName, "");
-    } else {
-      throw new IllegalArgumentException("Wrong " + KMSRESTConstants.EEK_OP +
-          " value, it must be " + KMSRESTConstants.EEK_GENERATE + " or " +
-          KMSRESTConstants.EEK_DECRYPT);
-    }
-    KMSWebApp.getDecryptEEKCallsMeter().mark();
-    return Response.ok().type(MediaType.APPLICATION_JSON).entity(retJSON)
-        .build();
+        });
+        retJSON = KMSUtil.toJSON(retKeyVersion);
+        kmsAudit.ok(user, KMSOp.DECRYPT_EEK, keyName, "");
+      } else if (eekOp.equals(KMSRESTConstants.EEK_REENCRYPT)) {
+        KMSWebApp.getReencryptEEKCallsMeter().mark();
+        assertAccess(Type.GENERATE_EEK, user, KMSOp.REENCRYPT_EEK, keyName);
+        EncryptedKeyVersion retEncryptedKeyVersion = user.doAs(new 
PrivilegedExceptionAction<EncryptedKeyVersion>() {
+          @Override
+          public EncryptedKeyVersion run() throws Exception {
+            return provider.reencryptEncryptedKey(new 
KMSClientProvider.KMSEncryptedKeyVersion(keyName,versionName, iv, 
KeyProviderCryptoExtension.EEK,
+              encMaterial));
+          }
+        });
+        retJSON = KMSUtil.toJSON(retEncryptedKeyVersion);
+        kmsAudit.ok(user, KMSOp.REENCRYPT_EEK, keyName, "");
+        } else {
+        StringBuilder error;
+        error = new StringBuilder("IllegalArgumentException Wrong ");
+        error.append(KMSRESTConstants.EEK_OP);
+        error.append(" value, it must be ");
+        error.append(KMSRESTConstants.EEK_GENERATE);
+        error.append(" or ");
+        error.append(KMSRESTConstants.EEK_DECRYPT);
+        LOG.error(error.toString());
+        throw new IllegalArgumentException(error.toString());
+      }
+      LOG.info("Exiting handleEncryptedKeyOp method.");
+      return 
Response.ok().type(MediaType.APPLICATION_JSON).entity(retJSON).build();
+    } catch (Exception e) {
+      LOG.debug("Exception in handleEncryptedKeyOp.", e);
+      throw e;
+}
   }
 
   @GET
@@ -480,23 +614,27 @@ public class KMS {
   @Produces(MediaType.APPLICATION_JSON)
   public Response getKeyVersions(@PathParam("name") final String name, 
@Context HttpServletRequest request)
       throws Exception {
-    UserGroupInformation user = HttpUserGroupInformation.get();
-    KMSUtil.checkNotEmpty(name, "name");
-    KMSWebApp.getKeyCallsMeter().mark();
-    assertAccess(Type.GET, user, KMSOp.GET_KEY_VERSIONS, name, 
request.getRemoteAddr());
-
-    List<KeyVersion> ret = user.doAs(
-        new PrivilegedExceptionAction<List<KeyVersion>>() {
-          @Override
-          public List<KeyVersion> run() throws Exception {
-            return provider.getKeyVersions(name);
-          }
+    try {
+      LOG.info("Entering getKeyVersions method.");
+      UserGroupInformation user = HttpUserGroupInformation.get();
+      checkNotEmpty(name, "name");
+      KMSWebApp.getKeyCallsMeter().mark();
+      assertAccess(Type.GET, user, KMSOp.GET_KEY_VERSIONS, name, 
request.getRemoteAddr());
+      LOG.debug("Getting key versions for key {}", name);
+      List<KeyVersion> ret = user.doAs(new 
PrivilegedExceptionAction<List<KeyVersion>>() {
+        @Override
+        public List<KeyVersion> run() throws Exception {
+          return provider.getKeyVersions(name);
         }
-    );
-
-    Object json = KMSServerJSONUtils.toJSON(ret);
-    kmsAudit.ok(user, KMSOp.GET_KEY_VERSIONS, name, "");
-    return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
-  }
+      });
+      Object json = KMSServerJSONUtils.toJSON(ret);
+      kmsAudit.ok(user, KMSOp.GET_KEY_VERSIONS, name, "");
+      LOG.info("Exiting getKeyVersions method.");
+      return 
Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
+    } catch (Exception e) {
+      LOG.debug("Exception in getKeyVersions.", e);
+      throw e;
+    }
+   }
 
 }
diff --git 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSAudit.java 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSAudit.java
index 56d25d2..053d7e4 100644
--- a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSAudit.java
+++ b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSAudit.java
@@ -17,6 +17,7 @@
  */
 package org.apache.hadoop.crypto.key.kms.server;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -92,7 +93,7 @@ public class KMSAudit {
 
   private static Set<KMS.KMSOp> AGGREGATE_OPS_WHITELIST = Sets.newHashSet(
     KMS.KMSOp.GET_KEY_VERSION, KMS.KMSOp.GET_CURRENT_KEY,
-    KMS.KMSOp.DECRYPT_EEK, KMS.KMSOp.GENERATE_EEK
+    KMS.KMSOp.DECRYPT_EEK, KMS.KMSOp.GENERATE_EEK, KMS.KMSOp.REENCRYPT_EEK
   );
 
   private Cache<String, AuditEvent> cache;
@@ -227,4 +228,8 @@ public class KMSAudit {
   public void shutdown() {
     executor.shutdownNow();
   }
+  @VisibleForTesting
+  void evictCacheForTesting() {
+    cache.invalidateAll();
+  }
 }
diff --git 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSExceptionsProvider.java
 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSExceptionsProvider.java
index cdca8e1..b5be765 100644
--- 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSExceptionsProvider.java
+++ 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSExceptionsProvider.java
@@ -41,9 +41,9 @@ import java.io.IOException;
 @Provider
 @InterfaceAudience.Private
 public class KMSExceptionsProvider implements ExceptionMapper<Exception> {
-  private static final Logger LOG =
+  private static Logger LOG =
       LoggerFactory.getLogger(KMSExceptionsProvider.class);
-
+  private final static Logger EXCEPTION_LOG = KMS.LOG;
   private static final String ENTER = System.getProperty("line.separator");
 
   protected Response createResponse(Response.Status status, Throwable ex) {
@@ -100,6 +100,9 @@ public class KMSExceptionsProvider implements 
ExceptionMapper<Exception> {
           KMSMDCFilter.getMethod(),
           KMSMDCFilter.getURL(), getOneLineMessage(exception));
     }
+    EXCEPTION_LOG.warn("User {} request {} {} caused exception.",
+      KMSMDCFilter.getUgi(), KMSMDCFilter.getMethod(),
+      KMSMDCFilter.getURL(), exception);
     return createResponse(status, throwable);
   }
 
diff --git 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSJSONReader.java 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSJSONReader.java
index 2b85276..59cc218 100644
--- 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSJSONReader.java
+++ 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSJSONReader.java
@@ -31,20 +31,21 @@ import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.util.Map;
+import java.util.List;
 
 @Provider
 @Consumes(MediaType.APPLICATION_JSON)
 @InterfaceAudience.Private
-public class KMSJSONReader implements MessageBodyReader<Map> {
+public class KMSJSONReader implements MessageBodyReader<Object> {
 
   @Override
   public boolean isReadable(Class<?> type, Type genericType,
       Annotation[] annotations, MediaType mediaType) {
-    return type.isAssignableFrom(Map.class);
+    return type.isAssignableFrom(Map.class) || 
type.isAssignableFrom(List.class);
   }
 
   @Override
-  public Map readFrom(Class<Map> type, Type genericType,
+  public Object readFrom(Class<Object> type, Type genericType,
       Annotation[] annotations, MediaType mediaType,
       MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
       throws IOException, WebApplicationException {
diff --git 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSServerJSONUtils.java
 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSServerJSONUtils.java
index 24af81b..f481486 100644
--- 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSServerJSONUtils.java
+++ 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSServerJSONUtils.java
@@ -17,64 +17,34 @@
  */
 package org.apache.hadoop.crypto.key.kms.server;
 
-import org.apache.commons.codec.binary.Base64;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.crypto.key.KeyProvider;
-import 
org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.EncryptedKeyVersion;
 import org.apache.hadoop.crypto.key.kms.KMSRESTConstants;
 
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import org.apache.hadoop.util.KMSUtil;
 
 /**
  * JSON utility methods for the KMS.
  */
 @InterfaceAudience.Private
 public class KMSServerJSONUtils {
-  @SuppressWarnings("unchecked")
-  public static Map toJSON(KeyProvider.KeyVersion keyVersion) {
-    Map json = new LinkedHashMap();
-    if (keyVersion != null) {
-      json.put(KMSRESTConstants.NAME_FIELD,
-          keyVersion.getName());
-      json.put(KMSRESTConstants.VERSION_NAME_FIELD,
-          keyVersion.getVersionName());
-      json.put(KMSRESTConstants.MATERIAL_FIELD,
-          Base64.encodeBase64URLSafeString(
-              keyVersion.getMaterial()));
-    }
-    return json;
-  }
 
   @SuppressWarnings("unchecked")
   public static List toJSON(List<KeyProvider.KeyVersion> keyVersions) {
     List json = new ArrayList();
     if (keyVersions != null) {
       for (KeyProvider.KeyVersion version : keyVersions) {
-        json.add(toJSON(version));
+        json.add(KMSUtil.toJSON(version));
       }
     }
     return json;
   }
 
   @SuppressWarnings("unchecked")
-  public static Map toJSON(EncryptedKeyVersion encryptedKeyVersion) {
-    Map json = new LinkedHashMap();
-    if (encryptedKeyVersion != null) {
-      json.put(KMSRESTConstants.VERSION_NAME_FIELD,
-          encryptedKeyVersion.getEncryptionKeyVersionName());
-      json.put(KMSRESTConstants.IV_FIELD,
-          Base64.encodeBase64URLSafeString(
-              encryptedKeyVersion.getEncryptedKeyIv()));
-      json.put(KMSRESTConstants.ENCRYPTED_KEY_VERSION_FIELD,
-          toJSON(encryptedKeyVersion.getEncryptedKeyVersion()));
-    }
-    return json;
-  }
-
-  @SuppressWarnings("unchecked")
   public static Map toJSON(String keyName, KeyProvider.Metadata meta) {
     Map json = new LinkedHashMap();
     if (meta != null) {
diff --git 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSWebApp.java 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSWebApp.java
index 501ee30..ae24e5b 100755
--- a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSWebApp.java
+++ b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSWebApp.java
@@ -65,6 +65,10 @@ public class KMSWebApp implements ServletContextListener {
       "generate_eek.calls.meter";
   private static final String DECRYPT_EEK_METER = METRICS_PREFIX +
       "decrypt_eek.calls.meter";
+  private static final String REENCRYPT_EEK_METER = METRICS_PREFIX +
+      "reencrypt_eek.calls.meter";
+  private static final String REENCRYPT_EEK_BATCH_METER = METRICS_PREFIX +
+        "reencrypt_eek_batch.calls.meter";
 
   private static Logger LOG;
   private static MetricRegistry metricRegistry;
@@ -77,6 +81,8 @@ public class KMSWebApp implements ServletContextListener {
   private static Meter unauthorizedCallsMeter;
   private static Meter unauthenticatedCallsMeter;
   private static Meter decryptEEKCallsMeter;
+  private static Meter reencryptEEKCallsMeter;
+  private static Meter reencryptEEKBatchCallsMeter;
   private static Meter generateEEKCallsMeter;
   private static Meter invalidCallsMeter;
   private static KMSAudit kmsAudit;
@@ -140,6 +146,10 @@ public class KMSWebApp implements ServletContextListener {
           new Meter());
       decryptEEKCallsMeter = metricRegistry.register(DECRYPT_EEK_METER,
           new Meter());
+      reencryptEEKCallsMeter = metricRegistry.register(REENCRYPT_EEK_METER,
+          new Meter());
+      reencryptEEKBatchCallsMeter = 
metricRegistry.register(REENCRYPT_EEK_BATCH_METER,
+          new Meter());
       adminCallsMeter = metricRegistry.register(ADMIN_CALLS_METER, new 
Meter());
       keyCallsMeter = metricRegistry.register(KEY_CALLS_METER, new Meter());
       invalidCallsMeter = metricRegistry.register(INVALID_CALLS_METER,
@@ -283,6 +293,13 @@ public class KMSWebApp implements ServletContextListener {
   public static Meter getDecryptEEKCallsMeter() {
     return decryptEEKCallsMeter;
   }
+  public static Meter getReencryptEEKCallsMeter() {
+    return reencryptEEKCallsMeter;
+  }
+
+  public static Meter getReencryptEEKBatchCallsMeter() {
+    return reencryptEEKBatchCallsMeter;
+  }
 
   public static Meter getUnauthorizedCallsMeter() {
     return unauthorizedCallsMeter;
diff --git 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KeyAuthorizationKeyProvider.java
 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KeyAuthorizationKeyProvider.java
index bd35a6b..fb9a261 100755
--- 
a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KeyAuthorizationKeyProvider.java
+++ 
b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KeyAuthorizationKeyProvider.java
@@ -288,6 +288,38 @@ public class KeyAuthorizationKeyProvider extends 
KeyProviderCryptoExtension {
   }
 
   @Override
+  public EncryptedKeyVersion reencryptEncryptedKey(EncryptedKeyVersion ekv)
+    throws IOException, GeneralSecurityException {
+    readLock.lock();
+    try {
+      verifyKeyVersionBelongsToKey(ekv);
+      doAccessCheck(ekv.getEncryptionKeyName(), KeyOpType.GENERATE_EEK);
+      return provider.reencryptEncryptedKey(ekv);
+    } finally {
+      readLock.unlock();
+    }
+  }
+
+  @Override
+  public void reencryptEncryptedKeys(List<EncryptedKeyVersion> ekvs)
+        throws IOException, GeneralSecurityException {
+    if (ekvs.isEmpty()) {
+      return;
+    }
+    readLock.lock();
+    try {
+      for (EncryptedKeyVersion ekv : ekvs) {
+      verifyKeyVersionBelongsToKey(ekv);
+      }
+    final String keyName = ekvs.get(0).getEncryptionKeyName();
+     doAccessCheck(keyName, KeyOpType.GENERATE_EEK);
+    provider.reencryptEncryptedKeys(ekvs);
+    } finally {
+      readLock.unlock();
+    }
+  }
+
+  @Override
   public List<KeyVersion> getKeyVersions(String name) throws IOException {
          readLock.lock();
          try {
diff --git 
a/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSAudit.java 
b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSAudit.java
index 04daeee..ced8610 100644
--- 
a/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSAudit.java
+++ 
b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSAudit.java
@@ -29,7 +29,9 @@ import org.apache.log4j.PropertyConfigurator;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.Timeout;
 import org.mockito.Mockito;
 
 public class TestKMSAudit {
@@ -51,6 +53,9 @@ public class TestKMSAudit {
     }
   }
 
+  @Rule
+  public final Timeout testTimeout = new Timeout(180000);
+
   @Before
   public void setUp() {
     originalOut = System.err;
@@ -61,7 +66,7 @@ public class TestKMSAudit {
     PropertyConfigurator.configure(Thread.currentThread().
         getContextClassLoader()
         .getResourceAsStream("log4j-kmsaudit.properties"));
-    this.kmsAudit = new KMSAudit(1000);
+    this.kmsAudit = new 
KMSAudit(KMSConfiguration.KMS_AUDIT_AGGREGATION_WINDOW_DEFAULT);
   }
 
   @After
@@ -80,6 +85,7 @@ public class TestKMSAudit {
   }
 
   @Test
+  @SuppressWarnings("checkstyle:linelength")
   public void testAggregation() throws Exception {
     UserGroupInformation luser = Mockito.mock(UserGroupInformation.class);
     Mockito.when(luser.getShortUserName()).thenReturn("luser");
@@ -88,12 +94,20 @@ public class TestKMSAudit {
     kmsAudit.ok(luser, KMSOp.DECRYPT_EEK, "k1", "testmsg");
     kmsAudit.ok(luser, KMSOp.DELETE_KEY, "k1", "testmsg");
     kmsAudit.ok(luser, KMSOp.ROLL_NEW_VERSION, "k1", "testmsg");
+    kmsAudit.ok(luser, KMSOp.INVALIDATE_CACHE, "k1", "testmsg");
     kmsAudit.ok(luser, KMSOp.DECRYPT_EEK, "k1", "testmsg");
     kmsAudit.ok(luser, KMSOp.DECRYPT_EEK, "k1", "testmsg");
     kmsAudit.ok(luser, KMSOp.DECRYPT_EEK, "k1", "testmsg");
-    Thread.sleep(1500);
+    kmsAudit.evictCacheForTesting();
     kmsAudit.ok(luser, KMSOp.DECRYPT_EEK, "k1", "testmsg");
-    Thread.sleep(1500);
+    kmsAudit.evictCacheForTesting();
+    kmsAudit.ok(luser, KMSOp.REENCRYPT_EEK, "k1", "testmsg");
+    kmsAudit.ok(luser, KMSOp.REENCRYPT_EEK, "k1", "testmsg");
+    kmsAudit.ok(luser, KMSOp.REENCRYPT_EEK, "k1", "testmsg");
+    kmsAudit.evictCacheForTesting();
+    kmsAudit.ok(luser, KMSOp.REENCRYPT_EEK_BATCH, "k1", "testmsg");
+    kmsAudit.ok(luser, KMSOp.REENCRYPT_EEK_BATCH, "k1", "testmsg");
+    kmsAudit.evictCacheForTesting();
     String out = getAndResetLogOutput();
     System.out.println(out);
     Assert.assertTrue(
@@ -102,34 +116,70 @@ public class TestKMSAudit {
             // Not aggregated !!
             + "OK\\[op=DELETE_KEY, key=k1, user=luser\\] testmsg"
             + "OK\\[op=ROLL_NEW_VERSION, key=k1, user=luser\\] testmsg"
+            + "OK\\[op=INVALIDATE_CACHE, key=k1, user=luser\\] testmsg"
             // Aggregated
             + "OK\\[op=DECRYPT_EEK, key=k1, user=luser, accessCount=6, 
interval=[^m]{1,4}ms\\] testmsg"
-            + "OK\\[op=DECRYPT_EEK, key=k1, user=luser, accessCount=1, 
interval=[^m]{1,4}ms\\] testmsg"));
+            + "OK\\[op=DECRYPT_EEK, key=k1, user=luser, accessCount=1, 
interval=[^m]{1,4}ms\\] testmsg"
+            + "OK\\[op=REENCRYPT_EEK, key=k1, user=luser, accessCount=1, 
interval=[^m]{1,4}ms\\] testmsg"
+            + "OK\\[op=REENCRYPT_EEK, key=k1, user=luser, accessCount=3, 
interval=[^m]{1,4}ms\\] testmsg"
+            + "OK\\[op=REENCRYPT_EEK_BATCH, key=k1, user=luser\\] testmsg"
+            + "OK\\[op=REENCRYPT_EEK_BATCH, key=k1, user=luser\\] testmsg"));
   }
 
   @Test
+  @SuppressWarnings("checkstyle:linelength")
   public void testAggregationUnauth() throws Exception {
     UserGroupInformation luser = Mockito.mock(UserGroupInformation.class);
     Mockito.when(luser.getShortUserName()).thenReturn("luser");
     kmsAudit.unauthorized(luser, KMSOp.GENERATE_EEK, "k2");
-    Thread.sleep(1000);
+    kmsAudit.evictCacheForTesting();
     kmsAudit.ok(luser, KMSOp.GENERATE_EEK, "k3", "testmsg");
     kmsAudit.ok(luser, KMSOp.GENERATE_EEK, "k3", "testmsg");
     kmsAudit.ok(luser, KMSOp.GENERATE_EEK, "k3", "testmsg");
     kmsAudit.ok(luser, KMSOp.GENERATE_EEK, "k3", "testmsg");
     kmsAudit.ok(luser, KMSOp.GENERATE_EEK, "k3", "testmsg");
     kmsAudit.unauthorized(luser, KMSOp.GENERATE_EEK, "k3");
+    // wait a bit so the UNAUTHORIZED-triggered cache invalidation happens.
+    Thread.sleep(1000);
     kmsAudit.ok(luser, KMSOp.GENERATE_EEK, "k3", "testmsg");
-    Thread.sleep(2000);
+    kmsAudit.evictCacheForTesting();
     String out = getAndResetLogOutput();
     System.out.println(out);
+    // The UNAUTHORIZED will trigger cache invalidation, which then triggers
+    // the aggregated OK (accessCount=5). But the order of the UNAUTHORIZED and
+    // the aggregated OK is arbitrary - no correctness concerns, but flaky 
here.
     Assert.assertTrue(
         out.matches(
             "UNAUTHORIZED\\[op=GENERATE_EEK, key=k2, user=luser\\] "
             + "OK\\[op=GENERATE_EEK, key=k3, user=luser, accessCount=1, 
interval=[^m]{1,4}ms\\] testmsg"
             + "OK\\[op=GENERATE_EEK, key=k3, user=luser, accessCount=5, 
interval=[^m]{1,4}ms\\] testmsg"
             + "UNAUTHORIZED\\[op=GENERATE_EEK, key=k3, user=luser\\] "
+            + "OK\\[op=GENERATE_EEK, key=k3, user=luser, accessCount=1, 
interval=[^m]{1,4}ms\\] testmsg")
+            || out.matches("UNAUTHORIZED\\[op=GENERATE_EEK, key=k2, 
user=luser\\] "
+            + "OK\\[op=GENERATE_EEK, key=k3, user=luser, accessCount=1, 
interval=[^m]{1,4}ms\\] testmsg"
+            + "UNAUTHORIZED\\[op=GENERATE_EEK, key=k3, user=luser\\] "
+            + "OK\\[op=GENERATE_EEK, key=k3, user=luser, accessCount=5, 
interval=[^m]{1,4}ms\\] testmsg"
             + "OK\\[op=GENERATE_EEK, key=k3, user=luser, accessCount=1, 
interval=[^m]{1,4}ms\\] testmsg"));
   }
 
+  @Test
+  public void testAuditLogFormat() throws Exception {
+        UserGroupInformation luser = Mockito.mock(UserGroupInformation.class);
+        Mockito.when(luser.getShortUserName()).thenReturn("luser");
+        kmsAudit.ok(luser, KMSOp.GENERATE_EEK, "k4", "testmsg");
+        kmsAudit.ok(luser, KMSOp.GENERATE_EEK, "testmsg");
+        kmsAudit.evictCacheForTesting();
+        kmsAudit.unauthorized(luser, KMSOp.DECRYPT_EEK, "k4");
+        kmsAudit.error(luser, "method", "url", "testmsg");
+        kmsAudit.unauthenticated("remotehost", "method", "url", "testmsg");
+        String out = getAndResetLogOutput();
+        System.out.println(out);
+        Assert.assertTrue(out.matches(
+          "OK\\[op=GENERATE_EEK, key=k4, user=luser, accessCount=1, 
interval=[^m]{1,4}ms\\] testmsg"
+           + "OK\\[op=GENERATE_EEK, user=luser\\] testmsg"
+           + "OK\\[op=GENERATE_EEK, key=k4, user=luser, accessCount=1, 
interval=[^m]{1,4}ms\\] testmsg"
+           + "UNAUTHORIZED\\[op=DECRYPT_EEK, key=k4, user=luser\\] "
+           + "ERROR\\[user=luser\\] Method:'method' Exception:'testmsg'"
+           + "UNAUTHENTICATED RemoteHost:remotehost Method:method URL:url 
ErrorMsg:'testmsg'"));
+    }
 }

Reply via email to