You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by co...@apache.org on 2016/09/26 15:47:59 UTC

incubator-ranger git commit: RANGER-1174 - Add tests for KMS Service and plugin

Repository: incubator-ranger
Updated Branches:
  refs/heads/master eed536b13 -> 868c62b72


RANGER-1174 - Add tests for KMS Service and plugin


Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/868c62b7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/868c62b7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/868c62b7

Branch: refs/heads/master
Commit: 868c62b729bb67b8b9db3f2146db1ea442b53ea8
Parents: eed536b
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Mon Sep 26 11:53:44 2016 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Mon Sep 26 16:40:26 2016 +0100

----------------------------------------------------------------------
 kms/pom.xml                                     |   6 +
 .../crypto/key/kms/server/DerbyTestUtils.java   |  66 +++
 .../kms/server/RangerKeyStoreProviderTest.java  | 154 +++++++
 .../key/kms/server/RangerMasterKeyTest.java     | 112 +++++
 kms/src/test/resources/kms/dbks-site.xml        |  65 +++
 kms/src/test/resources/kms/kms-site.xml         | 192 +++++++++
 plugin-kms/pom.xml                              |  24 ++
 .../kms/authorizer/DerbyTestUtils.java          |  66 +++
 .../kms/authorizer/RangerAdminClientImpl.java   |  84 ++++
 .../kms/authorizer/RangerKmsAuthorizerTest.java | 422 +++++++++++++++++++
 plugin-kms/src/test/resources/kms-policies.json | 311 ++++++++++++++
 plugin-kms/src/test/resources/kms/dbks-site.xml |  65 +++
 plugin-kms/src/test/resources/kms/kms-site.xml  | 192 +++++++++
 .../src/test/resources/ranger-kms-security.xml  |  61 +++
 pom.xml                                         |   2 +
 15 files changed, 1822 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/kms/pom.xml
----------------------------------------------------------------------
diff --git a/kms/pom.xml b/kms/pom.xml
index e73dd01..c2c24ba 100644
--- a/kms/pom.xml
+++ b/kms/pom.xml
@@ -428,6 +428,12 @@
            <artifactId>solr-solrj</artifactId>
           <version>${solr.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.derby</groupId>
+            <artifactId>derby</artifactId>
+            <version>${derby.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <pluginManagement>

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/DerbyTestUtils.java
----------------------------------------------------------------------
diff --git a/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/DerbyTestUtils.java b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/DerbyTestUtils.java
new file mode 100644
index 0000000..b8d11dd
--- /dev/null
+++ b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/DerbyTestUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.crypto.key.kms.server;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+
+public final class DerbyTestUtils {
+    
+    public static void startDerby() throws Exception {
+        // Start Apache Derby
+        Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
+        
+        Properties props = new Properties();
+        Connection conn = DriverManager.getConnection("jdbc:derby:memory:derbyDB;create=true", props);
+        
+        Statement statement = conn.createStatement();
+        statement.execute("CREATE SCHEMA KMSADMIN");
+        
+        statement.execute("SET SCHEMA KMSADMIN");
+        
+        // Create masterkey table
+        statement.execute("CREATE SEQUENCE RANGER_MASTERKEY_SEQ START WITH 1 INCREMENT BY 1");
+        String tableCreationString = "CREATE TABLE ranger_masterkey (id VARCHAR(20) NOT NULL PRIMARY KEY, create_time DATE,"
+            + "update_time DATE, added_by_id VARCHAR(20), upd_by_id VARCHAR(20),"
+            + "cipher VARCHAR(255), bitlength VARCHAR(11), masterkey VARCHAR(2048))";
+        statement.execute(tableCreationString);
+        
+        // Create keys table
+        statement.execute("CREATE SEQUENCE RANGER_KEYSTORE_SEQ START WITH 1 INCREMENT BY 1");
+        statement.execute("CREATE TABLE ranger_keystore(id VARCHAR(20) NOT NULL PRIMARY KEY, create_time DATE,"
+            + "update_time DATE, added_by_id VARCHAR(20), upd_by_id VARCHAR(20),"
+            + "kms_alias VARCHAR(255) NOT NULL, kms_createdDate VARCHAR(20), kms_cipher VARCHAR(255),"
+            + "kms_bitLength VARCHAR(20), kms_description VARCHAR(512), kms_version VARCHAR(20),"
+            + "kms_attributes VARCHAR(1024), kms_encoded VARCHAR(2048))");
+
+        conn.close();
+    }
+    
+    public static void stopDerby() throws Exception {
+        try {
+            DriverManager.getConnection("jdbc:derby:memory:derbyDB;drop=true");
+        } catch (SQLException ex) {
+            // expected
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/RangerKeyStoreProviderTest.java
----------------------------------------------------------------------
diff --git a/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/RangerKeyStoreProviderTest.java b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/RangerKeyStoreProviderTest.java
new file mode 100644
index 0000000..ff2fb2a
--- /dev/null
+++ b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/RangerKeyStoreProviderTest.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.crypto.key.kms.server;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.crypto.key.KeyProvider.KeyVersion;
+import org.apache.hadoop.crypto.key.KeyProvider.Options;
+import org.apache.hadoop.crypto.key.RangerKeyStoreProvider;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * A test for the RangerKeyStoreProvider, which is an implementation of the Hadoop KeyProvider interface, which stores keys in a database.
+ * Apache Derby is used to create the relevant tables to store the keys in for this test.
+ */
+public class RangerKeyStoreProviderTest {
+	private static final boolean UNRESTRICTED_POLICIES_INSTALLED;
+    static {
+        boolean ok = false;
+        try {
+            byte[] data = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+
+            SecretKey key192 = new SecretKeySpec(
+                new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                            0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                            0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
+                            "AES");
+            Cipher c = Cipher.getInstance("AES");
+            c.init(Cipher.ENCRYPT_MODE, key192);
+            c.doFinal(data);
+            ok = true;
+        } catch (Exception e) {
+            //
+        }
+        UNRESTRICTED_POLICIES_INSTALLED = ok;
+    }
+    
+    @BeforeClass
+    public static void startServers() throws Exception {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+        DerbyTestUtils.startDerby();
+    }
+    
+    @AfterClass
+    public static void stopServers() throws Exception {
+    	if (UNRESTRICTED_POLICIES_INSTALLED) {
+    		DerbyTestUtils.stopDerby();
+    	}
+    }
+
+    @Test
+    public void testCreateDeleteKey() throws Throwable {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+    	
+        Path configDir = Paths.get("src/test/resources/kms");
+        System.setProperty(KMSConfiguration.KMS_CONFIG_DIR, configDir.toFile().getAbsolutePath());
+        
+        Configuration conf = new Configuration();
+        RangerKeyStoreProvider keyProvider = new RangerKeyStoreProvider(conf);
+        
+        // Create a key
+        Options options = new Options(conf);
+        options.setBitLength(128);
+        options.setCipher("AES");
+        KeyVersion keyVersion = keyProvider.createKey("newkey1", options);
+        Assert.assertEquals("newkey1", keyVersion.getName());
+        Assert.assertEquals(128 / 8, keyVersion.getMaterial().length);
+        Assert.assertEquals("newkey1@0", keyVersion.getVersionName());
+        
+        keyProvider.flush();
+        Assert.assertEquals(1, keyProvider.getKeys().size());
+        keyProvider.deleteKey("newkey1");
+        
+        keyProvider.flush();
+        Assert.assertEquals(0, keyProvider.getKeys().size());
+        
+        // Try to delete a key that isn't there
+        try {
+            keyProvider.deleteKey("newkey2");
+            Assert.fail("Failure expected on trying to delete an unknown key");
+        } catch (IOException ex) {
+            // expected
+        }
+    }
+    
+    @Test
+    public void testRolloverKey() throws Throwable {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+    	
+        Path configDir = Paths.get("src/test/resources/kms");
+        System.setProperty(KMSConfiguration.KMS_CONFIG_DIR, configDir.toFile().getAbsolutePath());
+        
+        Configuration conf = new Configuration();
+        RangerKeyStoreProvider keyProvider = new RangerKeyStoreProvider(conf);
+        
+        // Create a key
+        Options options = new Options(conf);
+        options.setBitLength(192);
+        options.setCipher("AES");
+        KeyVersion keyVersion = keyProvider.createKey("newkey1", options);
+        Assert.assertEquals("newkey1", keyVersion.getName());
+        Assert.assertEquals(192 / 8, keyVersion.getMaterial().length);
+        Assert.assertEquals("newkey1@0", keyVersion.getVersionName());
+        
+        keyProvider.flush();
+        
+        // Rollover a new key
+        byte[] oldKey = keyVersion.getMaterial();
+        keyVersion = keyProvider.rollNewVersion("newkey1");
+        Assert.assertEquals("newkey1", keyVersion.getName());
+        Assert.assertEquals(192 / 8, keyVersion.getMaterial().length);
+        Assert.assertEquals("newkey1@1", keyVersion.getVersionName());
+        Assert.assertFalse(Arrays.equals(oldKey, keyVersion.getMaterial()));
+        
+        keyProvider.deleteKey("newkey1");
+        
+        keyProvider.flush();
+        Assert.assertEquals(0, keyProvider.getKeys().size());
+        
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/RangerMasterKeyTest.java
----------------------------------------------------------------------
diff --git a/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/RangerMasterKeyTest.java b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/RangerMasterKeyTest.java
new file mode 100644
index 0000000..9e86b4b
--- /dev/null
+++ b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/RangerMasterKeyTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.crypto.key.kms.server;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.hadoop.crypto.key.RangerKMSDB;
+import org.apache.hadoop.crypto.key.RangerKeyStoreProvider;
+import org.apache.hadoop.crypto.key.RangerMasterKey;
+import org.apache.ranger.kms.dao.DaoManager;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * A test for the RangerMasterKey.
+ */
+public class RangerMasterKeyTest {
+	private static final boolean UNRESTRICTED_POLICIES_INSTALLED;
+	static {
+		boolean ok = false;
+		try {
+			byte[] data = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+
+			SecretKey key192 = new SecretKeySpec(
+					new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+							0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+							0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
+					"AES");
+			Cipher c = Cipher.getInstance("AES");
+			c.init(Cipher.ENCRYPT_MODE, key192);
+			c.doFinal(data);
+			ok = true;
+		} catch (Exception e) {
+			//
+		}
+		UNRESTRICTED_POLICIES_INSTALLED = ok;
+	}
+
+    @BeforeClass
+    public static void startServers() throws Exception {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+        DerbyTestUtils.startDerby();
+    }
+    
+    @AfterClass
+    public static void stopServers() throws Exception {
+    	if (UNRESTRICTED_POLICIES_INSTALLED) {
+    		DerbyTestUtils.stopDerby();
+    	}
+    }
+    
+    @Test
+    public void testRangerMasterKey() throws Throwable {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+    	
+        Path configDir = Paths.get("src/test/resources/kms");
+        System.setProperty(KMSConfiguration.KMS_CONFIG_DIR, configDir.toFile().getAbsolutePath());
+
+        RangerKMSDB rangerkmsDb = new RangerKMSDB(RangerKeyStoreProvider.getDBKSConf());     
+        DaoManager daoManager = rangerkmsDb.getDaoManager();
+        
+        String masterKeyPassword = "password0password0password0password0password0password0password0password0"
+            + "password0password0password0password0password0password0password0password0password0password0"
+            + "password0password0password0password0password0password0password0password0password0password0";
+        
+        RangerMasterKey rangerMasterKey = new RangerMasterKey(daoManager);
+        Assert.assertTrue(rangerMasterKey.generateMasterKey(masterKeyPassword));
+        Assert.assertNotNull(rangerMasterKey.getMasterKey(masterKeyPassword));
+        
+        try {
+            rangerMasterKey.getMasterKey("badpass");
+            Assert.fail("Failure expected on retrieving a key with the wrong password");
+        } catch (Throwable t) {
+            // expected
+        }
+        
+        Assert.assertNotNull(rangerMasterKey.getMasterSecretKey(masterKeyPassword));
+        
+        try {
+            rangerMasterKey.getMasterSecretKey("badpass");
+            Assert.fail("Failure expected on retrieving a key with the wrong password");
+        } catch (Throwable t) {
+            // expected
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/kms/src/test/resources/kms/dbks-site.xml
----------------------------------------------------------------------
diff --git a/kms/src/test/resources/kms/dbks-site.xml b/kms/src/test/resources/kms/dbks-site.xml
new file mode 100755
index 0000000..dbc9576
--- /dev/null
+++ b/kms/src/test/resources/kms/dbks-site.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<configuration>
+
+  <!-- Encryption key Password -->
+  
+  <property>
+	<name>ranger.db.encrypt.key.password</name>
+    <value>Str0ngPassw0rd</value>
+    <description>
+    	Password used for encrypting Master Key
+    </description>
+  </property>
+  
+   <!-- db Details -->
+  
+  <property>
+    <name>ranger.ks.jpa.jdbc.url</name>
+    <value>jdbc:derby:memory:derbyDB;create=true</value>
+    <description>
+      URL for Database
+    </description>
+  </property>
+    
+  <property>
+    <name>ranger.ks.jpa.jdbc.user</name>
+    <value>kmsadmin</value>
+    <description>
+      Database username used for operation
+    </description>
+  </property>
+  
+  <property>
+    <name>ranger.ks.jpa.jdbc.password</name>
+    <value>kmsadmin</value>
+    <description>
+      Database user's password 
+    </description>
+  </property>
+
+  <property>
+    <name>ranger.ks.jpa.jdbc.driver</name>
+    <value>org.apache.derby.jdbc.EmbeddedDriver</value>
+    <description>
+      Driver used for database
+    </description>    
+  </property>
+  
+</configuration>

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/kms/src/test/resources/kms/kms-site.xml
----------------------------------------------------------------------
diff --git a/kms/src/test/resources/kms/kms-site.xml b/kms/src/test/resources/kms/kms-site.xml
new file mode 100644
index 0000000..5f2575a
--- /dev/null
+++ b/kms/src/test/resources/kms/kms-site.xml
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<configuration>
+
+  <!-- KMS Backend KeyProvider -->
+
+  <property>
+    <name>hadoop.kms.key.provider.uri</name>
+    <value>dbks://http@localhost:9292/kms</value>
+    <description>
+      URI of the backing KeyProvider for the KMS.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.security.keystore.JavaKeyStoreProvider.password</name>
+    <value>none</value>
+    <description>
+      If using the JavaKeyStoreProvider, the password for the keystore file.
+    </description>
+  </property>
+  
+  <!-- KMS Cache -->
+
+  <property>
+    <name>hadoop.kms.cache.enable</name>
+    <value>true</value>
+    <description>
+      Whether the KMS will act as a cache for the backing KeyProvider.
+      When the cache is enabled, operations like getKeyVersion, getMetadata,
+      and getCurrentKey will sometimes return cached data without consulting
+      the backing KeyProvider. Cached values are flushed when keys are deleted
+      or modified.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.cache.timeout.ms</name>
+    <value>600000</value>
+    <description>
+      Expiry time for the KMS key version and key metadata cache, in
+      milliseconds. This affects getKeyVersion and getMetadata.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.current.key.cache.timeout.ms</name>
+    <value>30000</value>
+    <description>
+      Expiry time for the KMS current key cache, in milliseconds. This
+      affects getCurrentKey operations.
+    </description>
+  </property>
+
+  <!-- KMS Audit -->
+
+  <property>
+    <name>hadoop.kms.audit.aggregation.window.ms</name>
+    <value>10000</value>
+    <description>
+      Duplicate audit log events within the aggregation window (specified in
+      ms) are quashed to reduce log traffic. A single message for aggregated
+      events is printed at the end of the window, along with a count of the
+      number of aggregated events.
+    </description>
+  </property>
+
+  <!-- KMS Security -->
+
+  <property>
+    <name>hadoop.kms.authentication.type</name>
+    <value>simple</value>
+    <description>
+      Authentication type for the KMS. Can be either &quot;simple&quot;
+      or &quot;kerberos&quot;.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.kerberos.keytab</name>
+    <value>${user.home}/kms.keytab</value>
+    <description>
+      Path to the keytab with credentials for the configured Kerberos principal.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.kerberos.principal</name>
+    <value>HTTP/localhost</value>
+    <description>
+      The Kerberos principal to use for the HTTP endpoint.
+      The principal must start with 'HTTP/' as per the Kerberos HTTP SPNEGO specification.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.kerberos.name.rules</name>
+    <value>DEFAULT</value>
+    <description>
+      Rules used to resolve Kerberos principal names.
+    </description>
+  </property>
+
+  <!-- Authentication cookie signature source -->
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider</name>
+    <value>random</value>
+    <description>
+      Indicates how the secret to sign the authentication cookies will be
+      stored. Options are 'random' (default), 'string' and 'zookeeper'.
+      If using a setup with multiple KMS instances, 'zookeeper' should be used.
+    </description>
+  </property>
+
+  <!-- Configuration for 'zookeeper' authentication cookie signature source -->
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider.zookeeper.path</name>
+    <value>/hadoop-kms/hadoop-auth-signature-secret</value>
+    <description>
+      The Zookeeper ZNode path where the KMS instances will store and retrieve
+      the secret from.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider.zookeeper.connection.string</name>
+    <value>#HOSTNAME#:#PORT#,...</value>
+    <description>
+      The Zookeeper connection string, a list of hostnames and port comma
+      separated.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider.zookeeper.auth.type</name>
+    <value>kerberos</value>
+    <description>
+      The Zookeeper authentication type, 'none' or 'sasl' (Kerberos).
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider.zookeeper.kerberos.keytab</name>
+    <value>/etc/hadoop/conf/kms.keytab</value>
+    <description>
+      The absolute path for the Kerberos keytab with the credentials to
+      connect to Zookeeper.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider.zookeeper.kerberos.principal</name>
+    <value>kms/#HOSTNAME#</value>
+    <description>
+      The Kerberos service principal used to connect to Zookeeper.
+    </description>
+  </property>
+  
+  <property>
+  	<name>hadoop.kms.security.authorization.manager</name>
+  	<value>org.apache.ranger.authorization.kms.authorizer.RangerKmsAuthorizer</value>
+  </property>
+  
+  <property>
+  	<name>hadoop.kms.proxyuser.ranger.groups</name>
+  	<value>*</value>
+  </property>
+  
+  <property>
+  	<name>hadoop.kms.proxyuser.ranger.hosts</name>
+  	<value>*</value>
+  </property>
+  
+  <property>
+  	<name>hadoop.kms.proxyuser.ranger.users</name>
+  	<value>*</value>
+  </property>
+</configuration>

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/plugin-kms/pom.xml
----------------------------------------------------------------------
diff --git a/plugin-kms/pom.xml b/plugin-kms/pom.xml
index 5ac7b20..c2a0439 100644
--- a/plugin-kms/pom.xml
+++ b/plugin-kms/pom.xml
@@ -56,5 +56,29 @@
     		<artifactId>httpcore</artifactId>
     		<version>${httpcomponents.httpcore.version}</version>
 		</dependency>
+        <dependency>
+            <groupId>org.apache.derby</groupId>
+            <artifactId>derby</artifactId>
+            <version>${derby.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
+            <version>${easymock.version}</version>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
+    <build>
+        <testResources>
+            <testResource>
+                <directory>src/test/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+                <filtering>true</filtering>
+            </testResource>
+        </testResources>
+    </build>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/DerbyTestUtils.java
----------------------------------------------------------------------
diff --git a/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/DerbyTestUtils.java b/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/DerbyTestUtils.java
new file mode 100644
index 0000000..6899da6
--- /dev/null
+++ b/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/DerbyTestUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ranger.authorization.kms.authorizer;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+
+public final class DerbyTestUtils {
+    
+    public static void startDerby() throws Exception {
+        // Start Apache Derby
+        Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
+        
+        Properties props = new Properties();
+        Connection conn = DriverManager.getConnection("jdbc:derby:memory:derbyDB;create=true", props);
+        
+        Statement statement = conn.createStatement();
+        statement.execute("CREATE SCHEMA KMSADMIN");
+        
+        statement.execute("SET SCHEMA KMSADMIN");
+        
+        // Create masterkey table
+        statement.execute("CREATE SEQUENCE RANGER_MASTERKEY_SEQ START WITH 1 INCREMENT BY 1");
+        String tableCreationString = "CREATE TABLE ranger_masterkey (id VARCHAR(20) NOT NULL PRIMARY KEY, create_time DATE,"
+            + "update_time DATE, added_by_id VARCHAR(20), upd_by_id VARCHAR(20),"
+            + "cipher VARCHAR(255), bitlength VARCHAR(11), masterkey VARCHAR(2048))";
+        statement.execute(tableCreationString);
+        
+        // Create keys table
+        statement.execute("CREATE SEQUENCE RANGER_KEYSTORE_SEQ START WITH 1 INCREMENT BY 1");
+        statement.execute("CREATE TABLE ranger_keystore(id VARCHAR(20) NOT NULL PRIMARY KEY, create_time DATE,"
+            + "update_time DATE, added_by_id VARCHAR(20), upd_by_id VARCHAR(20),"
+            + "kms_alias VARCHAR(255) NOT NULL, kms_createdDate VARCHAR(20), kms_cipher VARCHAR(255),"
+            + "kms_bitLength VARCHAR(20), kms_description VARCHAR(512), kms_version VARCHAR(20),"
+            + "kms_attributes VARCHAR(1024), kms_encoded VARCHAR(2048))");
+
+        conn.close();
+    }
+    
+    public static void stopDerby() throws Exception {
+        try {
+            DriverManager.getConnection("jdbc:derby:memory:derbyDB;drop=true");
+        } catch (SQLException ex) {
+            // expected
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/RangerAdminClientImpl.java
----------------------------------------------------------------------
diff --git a/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/RangerAdminClientImpl.java b/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/RangerAdminClientImpl.java
new file mode 100644
index 0000000..ebc1991
--- /dev/null
+++ b/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/RangerAdminClientImpl.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ranger.authorization.kms.authorizer;
+
+import java.io.File;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.util.List;
+
+import org.apache.ranger.admin.client.RangerAdminClient;
+import org.apache.ranger.plugin.util.GrantRevokeRequest;
+import org.apache.ranger.plugin.util.ServicePolicies;
+import org.apache.ranger.plugin.util.ServiceTags;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * A test implementation of the RangerAdminClient interface that just reads policies in from a file and returns them
+ */
+public class RangerAdminClientImpl implements RangerAdminClient {
+    private static final Logger LOG = LoggerFactory.getLogger(RangerAdminClientImpl.class);
+    private final static String cacheFilename = "kms-policies.json";
+    private Gson gson;
+
+    public void init(String serviceName, String appId, String configPropertyPrefix) {
+        Gson gson = null;
+        try {
+            gson = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").setPrettyPrinting().create();
+        } catch(Throwable excp) {
+            LOG.error("RangerAdminClientImpl: failed to create GsonBuilder object", excp);
+        }
+        this.gson = gson;
+    }
+
+    public ServicePolicies getServicePoliciesIfUpdated(long lastKnownVersion) throws Exception {
+
+        String basedir = System.getProperty("basedir");
+        if (basedir == null) {
+            basedir = new File(".").getCanonicalPath();
+        }
+
+        java.nio.file.Path cachePath = FileSystems.getDefault().getPath(basedir, "/src/test/resources/" + cacheFilename);
+        byte[] cacheBytes = Files.readAllBytes(cachePath);
+
+        return gson.fromJson(new String(cacheBytes), ServicePolicies.class);
+    }
+
+    public void grantAccess(GrantRevokeRequest request) throws Exception {
+        
+    }
+
+    public void revokeAccess(GrantRevokeRequest request) throws Exception {
+        
+    }
+
+    public ServiceTags getServiceTagsIfUpdated(long lastKnownVersion) throws Exception {
+        return null;
+        
+    }
+
+    public List<String> getTagTypes(String tagTypePattern) throws Exception {
+        return null;
+    }
+
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/RangerKmsAuthorizerTest.java
----------------------------------------------------------------------
diff --git a/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/RangerKmsAuthorizerTest.java b/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/RangerKmsAuthorizerTest.java
new file mode 100644
index 0000000..7fef432
--- /dev/null
+++ b/plugin-kms/src/test/java/org/apache/ranger/authorization/kms/authorizer/RangerKmsAuthorizerTest.java
@@ -0,0 +1,422 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ranger.authorization.kms.authorizer;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.PrivilegedExceptionAction;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+
+import org.apache.hadoop.crypto.key.kms.server.KMS.KMSOp;
+import org.apache.hadoop.crypto.key.kms.server.KMSACLsType.Type;
+import org.apache.hadoop.crypto.key.kms.server.KMSConfiguration;
+import org.apache.hadoop.crypto.key.kms.server.KMSWebApp;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authorize.AuthorizationException;
+import org.easymock.EasyMock;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Policies available from admin via:
+ * 
+ * http://localhost:6080/service/plugins/policies/download/KMSTest
+ * 
+ * The user "bob" can do anything. The group "IT" can only call the "get" methods
+ */
+public class RangerKmsAuthorizerTest {
+    
+    private static KMSWebApp kmsWebapp;
+    private static final boolean UNRESTRICTED_POLICIES_INSTALLED;
+    static {
+        boolean ok = false;
+        try {
+            byte[] data = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+
+            SecretKey key192 = new SecretKeySpec(
+                new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                            0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                            0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
+                            "AES");
+            Cipher c = Cipher.getInstance("AES");
+            c.init(Cipher.ENCRYPT_MODE, key192);
+            c.doFinal(data);
+            ok = true;
+        } catch (Exception e) {
+            //
+        }
+        UNRESTRICTED_POLICIES_INSTALLED = ok;
+    }
+    
+    @BeforeClass
+    public static void startServers() throws Exception {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+        DerbyTestUtils.startDerby();
+        
+        Path configDir = Paths.get("src/test/resources/kms");
+        System.setProperty(KMSConfiguration.KMS_CONFIG_DIR, configDir.toFile().getAbsolutePath());
+
+        // Start KMSWebApp
+        ServletContextEvent servletContextEvent = EasyMock.createMock(ServletContextEvent.class);
+        ServletContext servletContext = EasyMock.createMock(ServletContext.class);
+        EasyMock.expect(servletContextEvent.getServletContext()).andReturn(servletContext).anyTimes();
+        EasyMock.replay(servletContextEvent);
+
+        kmsWebapp = new KMSWebApp();
+        kmsWebapp.contextInitialized(servletContextEvent);
+    }
+    
+    @AfterClass
+    public static void stopServers() throws Exception {
+        DerbyTestUtils.stopDerby();
+    }
+    
+    @Test
+    public void testCreateKeys() throws Throwable {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+    	
+        // bob should have permission to create
+        final UserGroupInformation ugi = UserGroupInformation.createRemoteUser("bob");
+        ugi.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                KMSWebApp.getACLs().assertAccess(Type.CREATE, ugi, KMSOp.CREATE_KEY, "newkey1", "127.0.0.1");
+                return null;
+            }
+        });
+        
+        // "eve" should not have permission to create
+        final UserGroupInformation ugi2 = UserGroupInformation.createRemoteUser("eve");
+        ugi2.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.CREATE, ugi2, KMSOp.CREATE_KEY, "newkey2", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+        
+        // the IT group should not have permission to create
+        final UserGroupInformation ugi3 = UserGroupInformation.createUserForTesting("alice", new String[]{"IT"});
+        ugi3.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.CREATE, ugi3, KMSOp.CREATE_KEY, "newkey1", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+    }
+    
+    @Test
+    public void testDeleteKeys() throws Throwable {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+    	
+        // bob should have permission to delete
+        final UserGroupInformation ugi = UserGroupInformation.createRemoteUser("bob");
+        ugi.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                KMSWebApp.getACLs().assertAccess(Type.DELETE, ugi, KMSOp.DELETE_KEY, "newkey1", "127.0.0.1");
+                return null;
+            }
+        });
+        
+        // "eve" should not have permission to delete
+        final UserGroupInformation ugi2 = UserGroupInformation.createRemoteUser("eve");
+        ugi2.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.DELETE, ugi2, KMSOp.DELETE_KEY, "newkey1", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+        
+        // the IT group should not have permission to delete
+        final UserGroupInformation ugi3 = UserGroupInformation.createUserForTesting("alice", new String[]{"IT"});
+        ugi3.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.DELETE, ugi3, KMSOp.DELETE_KEY, "newkey1", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+        
+    }
+    
+    @Test
+    public void testRollover() throws Throwable {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+    	
+        // bob should have permission to rollover
+        final UserGroupInformation ugi = UserGroupInformation.createRemoteUser("bob");
+        ugi.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                KMSWebApp.getACLs().assertAccess(Type.ROLLOVER, ugi, KMSOp.ROLL_NEW_VERSION, "newkey1", "127.0.0.1");
+                return null;
+            }
+        });
+        
+        // "eve" should not have permission to rollover
+        final UserGroupInformation ugi2 = UserGroupInformation.createRemoteUser("eve");
+        ugi2.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.ROLLOVER, ugi2, KMSOp.ROLL_NEW_VERSION, "newkey1", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+        
+        // the IT group should not have permission to rollover
+        final UserGroupInformation ugi3 = UserGroupInformation.createUserForTesting("alice", new String[]{"IT"});
+        ugi3.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.ROLLOVER, ugi3, KMSOp.ROLL_NEW_VERSION, "newkey1", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+        
+    }
+    
+    @Test
+    public void testGetKeys() throws Throwable {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+    	
+        // bob should have permission to get keys
+        final UserGroupInformation ugi = UserGroupInformation.createRemoteUser("bob");
+        ugi.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                KMSWebApp.getACLs().assertAccess(Type.GET_KEYS, ugi, KMSOp.GET_KEYS, "newkey1", "127.0.0.1");
+                return null;
+            }
+        });
+        
+        // "eve" should not have permission to get keys
+        final UserGroupInformation ugi2 = UserGroupInformation.createRemoteUser("eve");
+        ugi2.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.GET_KEYS, ugi2, KMSOp.GET_KEYS, "newkey1", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+        
+        // the IT group should have permission to get keys
+        final UserGroupInformation ugi3 = UserGroupInformation.createUserForTesting("alice", new String[]{"IT"});
+        ugi3.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                KMSWebApp.getACLs().assertAccess(Type.GET_KEYS, ugi3, KMSOp.GET_KEYS, "newkey1", "127.0.0.1");
+                return null;
+            }
+        });
+    }
+    
+    @Test
+    public void testGetMetadata() throws Throwable {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+    	
+        // bob should have permission to get the metadata
+        final UserGroupInformation ugi = UserGroupInformation.createRemoteUser("bob");
+        ugi.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                KMSWebApp.getACLs().assertAccess(Type.GET_METADATA, ugi, KMSOp.GET_METADATA, "newkey1", "127.0.0.1");
+                return null;
+            }
+        });
+        
+        // "eve" should not have permission to get the metadata
+        final UserGroupInformation ugi2 = UserGroupInformation.createRemoteUser("eve");
+        ugi2.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.GET_METADATA, ugi2, KMSOp.GET_METADATA, "newkey1", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+        
+        // the IT group should have permission to get the metadata
+        final UserGroupInformation ugi3 = UserGroupInformation.createUserForTesting("alice", new String[]{"IT"});
+        ugi3.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                KMSWebApp.getACLs().assertAccess(Type.GET_METADATA, ugi3, KMSOp.GET_METADATA, "newkey1", "127.0.0.1");
+                return null;
+            }
+        });
+        
+    }
+  
+    @Test
+    public void testGenerateEEK() throws Throwable {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+    	
+        // bob should have permission to generate EEK
+        final UserGroupInformation ugi = UserGroupInformation.createRemoteUser("bob");
+        ugi.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                KMSWebApp.getACLs().assertAccess(Type.GENERATE_EEK, ugi, KMSOp.GENERATE_EEK, "newkey1", "127.0.0.1");
+                return null;
+            }
+        });
+        
+        // "eve" should not have permission to generate EEK
+        final UserGroupInformation ugi2 = UserGroupInformation.createRemoteUser("eve");
+        ugi2.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.GENERATE_EEK, ugi2, KMSOp.GENERATE_EEK, "newkey1", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+        
+        // the IT group should not have permission to generate EEK
+        final UserGroupInformation ugi3 = UserGroupInformation.createUserForTesting("alice", new String[]{"IT"});
+        ugi3.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.GENERATE_EEK, ugi3, KMSOp.GENERATE_EEK, "newkey1", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+        
+    }
+    
+    @Test
+    public void testDecryptEEK() throws Throwable {
+    	if (!UNRESTRICTED_POLICIES_INSTALLED) {
+    		return;
+    	}
+    	
+        // bob should have permission to generate EEK
+        final UserGroupInformation ugi = UserGroupInformation.createRemoteUser("bob");
+        ugi.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                KMSWebApp.getACLs().assertAccess(Type.DECRYPT_EEK, ugi, KMSOp.DECRYPT_EEK, "newkey1", "127.0.0.1");
+                return null;
+            }
+        });
+        
+        // "eve" should not have permission to decrypt EEK
+        final UserGroupInformation ugi2 = UserGroupInformation.createRemoteUser("eve");
+        ugi2.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.DECRYPT_EEK, ugi2, KMSOp.DECRYPT_EEK, "newkey1", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+        
+        // the IT group should not have permission to decrypt EEK
+        final UserGroupInformation ugi3 = UserGroupInformation.createUserForTesting("alice", new String[]{"IT"});
+        ugi3.doAs(new PrivilegedExceptionAction<Void>() {
+
+            public Void run() throws Exception {
+                try {
+                    KMSWebApp.getACLs().assertAccess(Type.DECRYPT_EEK, ugi3, KMSOp.DECRYPT_EEK, "newkey1", "127.0.0.1");
+                    Assert.fail("Failure expected");
+                } catch (AuthorizationException ex) {
+                    // expected
+                }
+                return null;
+            }
+        });
+        
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/plugin-kms/src/test/resources/kms-policies.json
----------------------------------------------------------------------
diff --git a/plugin-kms/src/test/resources/kms-policies.json b/plugin-kms/src/test/resources/kms-policies.json
new file mode 100644
index 0000000..479f98e
--- /dev/null
+++ b/plugin-kms/src/test/resources/kms-policies.json
@@ -0,0 +1,311 @@
+{
+  "serviceName": "KMSTest",
+  "serviceId": 17,
+  "policyVersion": 3,
+  "policyUpdateTime": "20160804-09:51:46.000-+0100",
+  "policies": [
+    {
+      "service": "KMSTest",
+      "name": "KMSTest-1-20160727162811",
+      "description": "Default Policy for Service: KMSTest",
+      "resourceSignature": "d135a88a4f4ed3a8ac7278891f15c780",
+      "isAuditEnabled": true,
+      "resources": {
+        "keyname": {
+          "values": [
+            "*"
+          ],
+          "isExcludes": false,
+          "isRecursive": false
+        }
+      },
+      "policyItems": [
+        {
+          "accesses": [
+            {
+              "type": "create",
+              "isAllowed": true
+            },
+            {
+              "type": "delete",
+              "isAllowed": true
+            },
+            {
+              "type": "rollover",
+              "isAllowed": true
+            },
+            {
+              "type": "setkeymaterial",
+              "isAllowed": true
+            },
+            {
+              "type": "get",
+              "isAllowed": true
+            },
+            {
+              "type": "getkeys",
+              "isAllowed": true
+            },
+            {
+              "type": "getmetadata",
+              "isAllowed": true
+            },
+            {
+              "type": "generateeek",
+              "isAllowed": true
+            },
+            {
+              "type": "decrypteek",
+              "isAllowed": true
+            }
+          ],
+          "users": [
+            "keyadmin"
+          ],
+          "groups": [],
+          "conditions": [],
+          "delegateAdmin": true
+        }
+      ],
+      "denyPolicyItems": [],
+      "allowExceptions": [],
+      "denyExceptions": [],
+      "dataMaskPolicyItems": [],
+      "rowFilterPolicyItems": [],
+      "id": 51,
+      "guid": "1469636891164_121_393",
+      "isEnabled": true,
+      "createdBy": "keyadmin",
+      "updatedBy": "keyadmin",
+      "createTime": "20160727-17:28:11.000-+0100",
+      "updateTime": "20160727-17:28:11.000-+0100",
+      "version": 1
+    },
+    {
+      "service": "KMSTest",
+      "name": "NewKeyPolicy",
+      "description": "",
+      "resourceSignature": "4b688c2712bb70da6227646b1948a086",
+      "isAuditEnabled": true,
+      "resources": {
+        "keyname": {
+          "values": [
+            "newkey*"
+          ],
+          "isExcludes": false,
+          "isRecursive": false
+        }
+      },
+      "policyItems": [
+        {
+          "accesses": [
+            {
+              "type": "create",
+              "isAllowed": true
+            },
+            {
+              "type": "delete",
+              "isAllowed": true
+            },
+            {
+              "type": "rollover",
+              "isAllowed": true
+            },
+            {
+              "type": "setkeymaterial",
+              "isAllowed": true
+            },
+            {
+              "type": "get",
+              "isAllowed": true
+            },
+            {
+              "type": "getkeys",
+              "isAllowed": true
+            },
+            {
+              "type": "getmetadata",
+              "isAllowed": true
+            },
+            {
+              "type": "generateeek",
+              "isAllowed": true
+            },
+            {
+              "type": "decrypteek",
+              "isAllowed": true
+            }
+          ],
+          "users": [
+            "bob"
+          ],
+          "groups": [],
+          "conditions": [],
+          "delegateAdmin": false
+        },
+        {
+          "accesses": [
+            {
+              "type": "get",
+              "isAllowed": true
+            },
+            {
+              "type": "getkeys",
+              "isAllowed": true
+            },
+            {
+              "type": "getmetadata",
+              "isAllowed": true
+            }
+          ],
+          "users": [],
+          "groups": [
+            "IT"
+          ],
+          "conditions": [],
+          "delegateAdmin": false
+        }
+      ],
+      "denyPolicyItems": [],
+      "allowExceptions": [],
+      "denyExceptions": [],
+      "dataMaskPolicyItems": [],
+      "rowFilterPolicyItems": [],
+      "id": 52,
+      "guid": "1470224184963_204_597",
+      "isEnabled": true,
+      "createdBy": "keyadmin",
+      "updatedBy": "keyadmin",
+      "createTime": "20160803-12:36:24.000-+0100",
+      "updateTime": "20160804-09:51:46.000-+0100",
+      "version": 2
+    }
+  ],
+  "serviceDef": {
+    "name": "kms",
+    "implClass": "org.apache.ranger.services.kms.RangerServiceKMS",
+    "label": "KMS",
+    "description": "KMS",
+    "options": {},
+    "configs": [
+      {
+        "itemId": 1,
+        "name": "provider",
+        "type": "string",
+        "mandatory": true,
+        "label": "KMS URL"
+      },
+      {
+        "itemId": 2,
+        "name": "username",
+        "type": "string",
+        "mandatory": true,
+        "label": "Username"
+      },
+      {
+        "itemId": 3,
+        "name": "password",
+        "type": "password",
+        "mandatory": true,
+        "label": "Password"
+      }
+    ],
+    "resources": [
+      {
+        "itemId": 1,
+        "name": "keyname",
+        "type": "string",
+        "level": 10,
+        "mandatory": true,
+        "lookupSupported": true,
+        "recursiveSupported": false,
+        "excludesSupported": false,
+        "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
+        "matcherOptions": {
+          "wildCard": "true",
+          "ignoreCase": "false"
+        },
+        "validationRegEx": "",
+        "validationMessage": "",
+        "uiHint": "",
+        "label": "Key Name",
+        "description": "Key Name"
+      }
+    ],
+    "accessTypes": [
+      {
+        "itemId": 1,
+        "name": "create",
+        "label": "Create",
+        "impliedGrants": []
+      },
+      {
+        "itemId": 2,
+        "name": "delete",
+        "label": "Delete",
+        "impliedGrants": []
+      },
+      {
+        "itemId": 3,
+        "name": "rollover",
+        "label": "Rollover",
+        "impliedGrants": []
+      },
+      {
+        "itemId": 4,
+        "name": "setkeymaterial",
+        "label": "Set Key Material",
+        "impliedGrants": []
+      },
+      {
+        "itemId": 5,
+        "name": "get",
+        "label": "Get",
+        "impliedGrants": []
+      },
+      {
+        "itemId": 6,
+        "name": "getkeys",
+        "label": "Get Keys",
+        "impliedGrants": []
+      },
+      {
+        "itemId": 7,
+        "name": "getmetadata",
+        "label": "Get Metadata",
+        "impliedGrants": []
+      },
+      {
+        "itemId": 8,
+        "name": "generateeek",
+        "label": "Generate EEK",
+        "impliedGrants": []
+      },
+      {
+        "itemId": 9,
+        "name": "decrypteek",
+        "label": "Decrypt EEK",
+        "impliedGrants": []
+      }
+    ],
+    "policyConditions": [],
+    "contextEnrichers": [],
+    "enums": [],
+    "dataMaskDef": {
+      "maskTypes": [],
+      "accessTypes": [],
+      "resources": []
+    },
+    "rowFilterDef": {
+      "accessTypes": [],
+      "resources": []
+    },
+    "id": 7,
+    "guid": "1457966375677_148_0",
+    "isEnabled": true,
+    "createTime": "20160314-14:39:35.000-+0000",
+    "updateTime": "20160314-14:39:35.000-+0000",
+    "version": 1
+  },
+  "auditMode": "audit-default"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/plugin-kms/src/test/resources/kms/dbks-site.xml
----------------------------------------------------------------------
diff --git a/plugin-kms/src/test/resources/kms/dbks-site.xml b/plugin-kms/src/test/resources/kms/dbks-site.xml
new file mode 100755
index 0000000..dbc9576
--- /dev/null
+++ b/plugin-kms/src/test/resources/kms/dbks-site.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<configuration>
+
+  <!-- Encryption key Password -->
+  
+  <property>
+	<name>ranger.db.encrypt.key.password</name>
+    <value>Str0ngPassw0rd</value>
+    <description>
+    	Password used for encrypting Master Key
+    </description>
+  </property>
+  
+   <!-- db Details -->
+  
+  <property>
+    <name>ranger.ks.jpa.jdbc.url</name>
+    <value>jdbc:derby:memory:derbyDB;create=true</value>
+    <description>
+      URL for Database
+    </description>
+  </property>
+    
+  <property>
+    <name>ranger.ks.jpa.jdbc.user</name>
+    <value>kmsadmin</value>
+    <description>
+      Database username used for operation
+    </description>
+  </property>
+  
+  <property>
+    <name>ranger.ks.jpa.jdbc.password</name>
+    <value>kmsadmin</value>
+    <description>
+      Database user's password 
+    </description>
+  </property>
+
+  <property>
+    <name>ranger.ks.jpa.jdbc.driver</name>
+    <value>org.apache.derby.jdbc.EmbeddedDriver</value>
+    <description>
+      Driver used for database
+    </description>    
+  </property>
+  
+</configuration>

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/plugin-kms/src/test/resources/kms/kms-site.xml
----------------------------------------------------------------------
diff --git a/plugin-kms/src/test/resources/kms/kms-site.xml b/plugin-kms/src/test/resources/kms/kms-site.xml
new file mode 100644
index 0000000..5f2575a
--- /dev/null
+++ b/plugin-kms/src/test/resources/kms/kms-site.xml
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<configuration>
+
+  <!-- KMS Backend KeyProvider -->
+
+  <property>
+    <name>hadoop.kms.key.provider.uri</name>
+    <value>dbks://http@localhost:9292/kms</value>
+    <description>
+      URI of the backing KeyProvider for the KMS.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.security.keystore.JavaKeyStoreProvider.password</name>
+    <value>none</value>
+    <description>
+      If using the JavaKeyStoreProvider, the password for the keystore file.
+    </description>
+  </property>
+  
+  <!-- KMS Cache -->
+
+  <property>
+    <name>hadoop.kms.cache.enable</name>
+    <value>true</value>
+    <description>
+      Whether the KMS will act as a cache for the backing KeyProvider.
+      When the cache is enabled, operations like getKeyVersion, getMetadata,
+      and getCurrentKey will sometimes return cached data without consulting
+      the backing KeyProvider. Cached values are flushed when keys are deleted
+      or modified.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.cache.timeout.ms</name>
+    <value>600000</value>
+    <description>
+      Expiry time for the KMS key version and key metadata cache, in
+      milliseconds. This affects getKeyVersion and getMetadata.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.current.key.cache.timeout.ms</name>
+    <value>30000</value>
+    <description>
+      Expiry time for the KMS current key cache, in milliseconds. This
+      affects getCurrentKey operations.
+    </description>
+  </property>
+
+  <!-- KMS Audit -->
+
+  <property>
+    <name>hadoop.kms.audit.aggregation.window.ms</name>
+    <value>10000</value>
+    <description>
+      Duplicate audit log events within the aggregation window (specified in
+      ms) are quashed to reduce log traffic. A single message for aggregated
+      events is printed at the end of the window, along with a count of the
+      number of aggregated events.
+    </description>
+  </property>
+
+  <!-- KMS Security -->
+
+  <property>
+    <name>hadoop.kms.authentication.type</name>
+    <value>simple</value>
+    <description>
+      Authentication type for the KMS. Can be either &quot;simple&quot;
+      or &quot;kerberos&quot;.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.kerberos.keytab</name>
+    <value>${user.home}/kms.keytab</value>
+    <description>
+      Path to the keytab with credentials for the configured Kerberos principal.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.kerberos.principal</name>
+    <value>HTTP/localhost</value>
+    <description>
+      The Kerberos principal to use for the HTTP endpoint.
+      The principal must start with 'HTTP/' as per the Kerberos HTTP SPNEGO specification.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.kerberos.name.rules</name>
+    <value>DEFAULT</value>
+    <description>
+      Rules used to resolve Kerberos principal names.
+    </description>
+  </property>
+
+  <!-- Authentication cookie signature source -->
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider</name>
+    <value>random</value>
+    <description>
+      Indicates how the secret to sign the authentication cookies will be
+      stored. Options are 'random' (default), 'string' and 'zookeeper'.
+      If using a setup with multiple KMS instances, 'zookeeper' should be used.
+    </description>
+  </property>
+
+  <!-- Configuration for 'zookeeper' authentication cookie signature source -->
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider.zookeeper.path</name>
+    <value>/hadoop-kms/hadoop-auth-signature-secret</value>
+    <description>
+      The Zookeeper ZNode path where the KMS instances will store and retrieve
+      the secret from.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider.zookeeper.connection.string</name>
+    <value>#HOSTNAME#:#PORT#,...</value>
+    <description>
+      The Zookeeper connection string, a list of hostnames and port comma
+      separated.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider.zookeeper.auth.type</name>
+    <value>kerberos</value>
+    <description>
+      The Zookeeper authentication type, 'none' or 'sasl' (Kerberos).
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider.zookeeper.kerberos.keytab</name>
+    <value>/etc/hadoop/conf/kms.keytab</value>
+    <description>
+      The absolute path for the Kerberos keytab with the credentials to
+      connect to Zookeeper.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider.zookeeper.kerberos.principal</name>
+    <value>kms/#HOSTNAME#</value>
+    <description>
+      The Kerberos service principal used to connect to Zookeeper.
+    </description>
+  </property>
+  
+  <property>
+  	<name>hadoop.kms.security.authorization.manager</name>
+  	<value>org.apache.ranger.authorization.kms.authorizer.RangerKmsAuthorizer</value>
+  </property>
+  
+  <property>
+  	<name>hadoop.kms.proxyuser.ranger.groups</name>
+  	<value>*</value>
+  </property>
+  
+  <property>
+  	<name>hadoop.kms.proxyuser.ranger.hosts</name>
+  	<value>*</value>
+  </property>
+  
+  <property>
+  	<name>hadoop.kms.proxyuser.ranger.users</name>
+  	<value>*</value>
+  </property>
+</configuration>

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/plugin-kms/src/test/resources/ranger-kms-security.xml
----------------------------------------------------------------------
diff --git a/plugin-kms/src/test/resources/ranger-kms-security.xml b/plugin-kms/src/test/resources/ranger-kms-security.xml
new file mode 100644
index 0000000..2fdf614
--- /dev/null
+++ b/plugin-kms/src/test/resources/ranger-kms-security.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
+<configuration xmlns:xi="http://www.w3.org/2001/XInclude">
+	<property>
+		<name>ranger.plugin.kms.policy.rest.url</name>
+		<value>http://localhost:6080</value>
+		<description>
+			URL to Ranger Admin
+		</description>
+	</property>
+
+	<property>
+		<name>ranger.plugin.kms.service.name</name>
+		<value>KMSTest</value>
+		<description>
+			Name of the Ranger service containing policies for this SampleApp instance
+		</description>
+	</property>
+
+	<property>
+        <name>ranger.plugin.kms.policy.source.impl</name>
+        <value>org.apache.ranger.authorization.kms.authorizer.RangerAdminClientImpl</value>
+        <!-- <value>org.apache.ranger.admin.client.RangerAdminRESTClient</value>--> 
+        <description>
+            Policy source.
+        </description>
+    </property>
+    
+	<property>
+		<name>ranger.plugin.kms.policy.pollIntervalMs</name>
+		<value>30000</value>
+		<description>
+			How often to poll for changes in policies?
+		</description>
+	</property>
+
+	<property>
+		<name>ranger.plugin.kms.policy.cache.dir</name>
+		<value>${project.build.directory}</value>
+		<description>
+			Directory where Ranger policies are cached after successful retrieval from the source
+		</description>
+	</property>
+
+</configuration>

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/868c62b7/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 59f7da6..6e39ace 100644
--- a/pom.xml
+++ b/pom.xml
@@ -150,6 +150,8 @@
         <commons.net.version>3.3</commons.net.version>
         <commons.pool.version>1.6</commons.pool.version>
         <curator.test.version>2.7.0</curator.test.version>
+        <derby.version>10.11.1.1</derby.version>
+        <easymock.version>3.4</easymock.version>
         <eclipse.jpa.version>2.5.2</eclipse.jpa.version>
         <findbugs.plugin.version>3.0.3</findbugs.plugin.version>
         <google.guava.version>17.0</google.guava.version>