Added: hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java?rev=1592637&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java Mon May 5 21:43:14 2014 @@ -0,0 +1,806 @@ +/** + * 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 org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.crypto.key.KeyProvider; +import org.apache.hadoop.crypto.key.kms.KMSClientProvider; +import org.apache.hadoop.minikdc.MiniKdc; +import org.apache.hadoop.security.authorize.AuthorizationException; +import org.apache.hadoop.security.ssl.KeyStoreTestUtil; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mortbay.jetty.Connector; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.security.SslSocketConnector; +import org.mortbay.jetty.webapp.WebAppContext; + +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.LoginContext; +import java.io.File; +import java.io.FileWriter; +import java.io.Writer; +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.ServerSocket; +import java.net.URI; +import java.net.URL; +import java.security.Principal; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Callable; + +public class TestKMS { + + public static File getTestDir() throws Exception { + File file = new File("dummy"); + file = file.getAbsoluteFile(); + file = file.getParentFile(); + file = new File(file, "target"); + file = new File(file, UUID.randomUUID().toString()); + if (!file.mkdirs()) { + throw new RuntimeException("Could not create test directory: " + file); + } + return file; + } + + public static Server createJettyServer(String keyStore, String password) { + try { + boolean ssl = keyStore != null; + InetAddress localhost = InetAddress.getByName("localhost"); + String host = "localhost"; + ServerSocket ss = new ServerSocket(0, 50, localhost); + int port = ss.getLocalPort(); + ss.close(); + Server server = new Server(0); + if (!ssl) { + server.getConnectors()[0].setHost(host); + server.getConnectors()[0].setPort(port); + } else { + SslSocketConnector c = new SslSocketConnector(); + c.setHost(host); + c.setPort(port); + c.setNeedClientAuth(false); + c.setKeystore(keyStore); + c.setKeystoreType("jks"); + c.setKeyPassword(password); + server.setConnectors(new Connector[]{c}); + } + return server; + } catch (Exception ex) { + throw new RuntimeException("Could not start embedded servlet container, " + + ex.getMessage(), ex); + } + } + + public static URL getJettyURL(Server server) { + boolean ssl = server.getConnectors()[0].getClass() + == SslSocketConnector.class; + try { + String scheme = (ssl) ? "https" : "http"; + return new URL(scheme + "://" + + server.getConnectors()[0].getHost() + ":" + + server.getConnectors()[0].getPort()); + } catch (MalformedURLException ex) { + throw new RuntimeException("It should never happen, " + ex.getMessage(), + ex); + } + } + + public static abstract class KMSCallable implements Callable<Void> { + private URL kmsUrl; + + protected URL getKMSUrl() { + return kmsUrl; + } + } + + protected void runServer(String keystore, String password, File confDir, + KMSCallable callable) throws Exception { + System.setProperty(KMSConfiguration.KMS_CONFIG_DIR, + confDir.getAbsolutePath()); + System.setProperty("log4j.configuration", "log4j.properties"); + Server jetty = createJettyServer(keystore, password); + try { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + URL url = cl.getResource("webapp"); + if (url == null) { + throw new RuntimeException( + "Could not find webapp/ dir in test classpath"); + } + WebAppContext context = new WebAppContext(url.getPath(), "/kms"); + jetty.addHandler(context); + jetty.start(); + url = new URL(getJettyURL(jetty), "kms"); + System.out.println("Test KMS running at: " + url); + callable.kmsUrl = url; + callable.call(); + } finally { + if (jetty != null && jetty.isRunning()) { + try { + jetty.stop(); + } catch (Exception ex) { + throw new RuntimeException("Could not stop embedded Jetty, " + + ex.getMessage(), ex); + } + } + } + } + + protected Configuration createBaseKMSConf(File keyStoreDir) throws Exception { + Configuration conf = new Configuration(false); + conf.set("hadoop.security.key.provider.path", + "jceks://file@/" + keyStoreDir.getAbsolutePath() + "/kms.keystore"); + conf.set("hadoop.kms.authentication.type", "simple"); + return conf; + } + + protected void writeConf(File confDir, Configuration conf) throws Exception { + Writer writer = new FileWriter(new File(confDir, + KMSConfiguration.KMS_SITE_XML)); + conf.writeXml(writer); + writer.close(); + + writer = new FileWriter(new File(confDir, KMSConfiguration.KMS_ACLS_XML)); + conf.writeXml(writer); + writer.close(); + + //create empty core-site.xml + writer = new FileWriter(new File(confDir, "core-site.xml")); + new Configuration(false).writeXml(writer); + writer.close(); + } + + protected URI createKMSUri(URL kmsUrl) throws Exception { + String str = kmsUrl.toString(); + str = str.replaceFirst("://", "@"); + return new URI("kms://" + str); + } + + + private static class KerberosConfiguration + extends javax.security.auth.login.Configuration { + private String principal; + private String keytab; + private boolean isInitiator; + + private KerberosConfiguration(String principal, File keytab, + boolean client) { + this.principal = principal; + this.keytab = keytab.getAbsolutePath(); + this.isInitiator = client; + } + + public static javax.security.auth.login.Configuration createClientConfig( + String principal, + File keytab) { + return new KerberosConfiguration(principal, keytab, true); + } + + private static String getKrb5LoginModuleName() { + return System.getProperty("java.vendor").contains("IBM") + ? "com.ibm.security.auth.module.Krb5LoginModule" + : "com.sun.security.auth.module.Krb5LoginModule"; + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + Map<String, String> options = new HashMap<String, String>(); + options.put("keyTab", keytab); + options.put("principal", principal); + options.put("useKeyTab", "true"); + options.put("storeKey", "true"); + options.put("doNotPrompt", "true"); + options.put("useTicketCache", "true"); + options.put("renewTGT", "true"); + options.put("refreshKrb5Config", "true"); + options.put("isInitiator", Boolean.toString(isInitiator)); + String ticketCache = System.getenv("KRB5CCNAME"); + if (ticketCache != null) { + options.put("ticketCache", ticketCache); + } + options.put("debug", "true"); + + return new AppConfigurationEntry[]{ + new AppConfigurationEntry(getKrb5LoginModuleName(), + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options)}; + } + } + + private static MiniKdc kdc; + private static File keytab; + + @BeforeClass + public static void setUpMiniKdc() throws Exception { + File kdcDir = getTestDir(); + Properties kdcConf = MiniKdc.createConf(); + kdc = new MiniKdc(kdcConf, kdcDir); + kdc.start(); + keytab = new File(kdcDir, "keytab"); + List<String> principals = new ArrayList<String>(); + principals.add("HTTP/localhost"); + principals.add("client"); + principals.add("client/host"); + for (KMSACLs.Type type : KMSACLs.Type.values()) { + principals.add(type.toString()); + } + principals.add("CREATE_MATERIAL"); + principals.add("ROLLOVER_MATERIAL"); + kdc.createPrincipal(keytab, + principals.toArray(new String[principals.size()])); + } + + @AfterClass + public static void tearDownMiniKdc() throws Exception { + if (kdc != null) { + kdc.stop(); + } + } + + private void doAs(String user, final PrivilegedExceptionAction<Void> action) + throws Exception { + Set<Principal> principals = new HashSet<Principal>(); + principals.add(new KerberosPrincipal(user)); + + //client login + Subject subject = new Subject(false, principals, + new HashSet<Object>(), new HashSet<Object>()); + LoginContext loginContext = new LoginContext("", subject, null, + KerberosConfiguration.createClientConfig(user, keytab)); + try { + loginContext.login(); + subject = loginContext.getSubject(); + Subject.doAs(subject, action); + } finally { + loginContext.logout(); + } + } + + public void testStartStop(final boolean ssl, final boolean kerberos) + throws Exception { + File testDir = getTestDir(); + Configuration conf = createBaseKMSConf(testDir); + + final String keystore; + final String password; + if (ssl) { + String sslConfDir = KeyStoreTestUtil.getClasspathDir(TestKMS.class); + KeyStoreTestUtil.setupSSLConfig(testDir.getAbsolutePath(), sslConfDir, + conf, false); + keystore = testDir.getAbsolutePath() + "/serverKS.jks"; + password = "serverP"; + } else { + keystore = null; + password = null; + } + + if (kerberos) { + conf.set("hadoop.kms.authentication.type", "kerberos"); + conf.set("hadoop.kms.authentication.kerberos.keytab", + keytab.getAbsolutePath()); + conf.set("hadoop.kms.authentication.kerberos.principal", "HTTP/localhost"); + conf.set("hadoop.kms.authentication.kerberos.name.rules", "DEFAULT"); + } + + writeConf(testDir, conf); + + runServer(keystore, password, testDir, new KMSCallable() { + @Override + public Void call() throws Exception { + Configuration conf = new Configuration(); + URL url = getKMSUrl(); + Assert.assertEquals(keystore != null, + url.getProtocol().equals("https")); + URI uri = createKMSUri(getKMSUrl()); + final KeyProvider kp = new KMSClientProvider(uri, conf); + + if (kerberos) { + for (String user : new String[]{"client", "client/host"}) { + doAs(user, new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + // getKeys() empty + Assert.assertTrue(kp.getKeys().isEmpty()); + return null; + } + }); + } + } else { + // getKeys() empty + Assert.assertTrue(kp.getKeys().isEmpty()); + } + return null; + } + }); + } + + @Test + public void testStartStopHttpPseudo() throws Exception { + testStartStop(false, false); + } + + @Test + public void testStartStopHttpsPseudo() throws Exception { + testStartStop(true, false); + } + + @Test + public void testStartStopHttpKerberos() throws Exception { + testStartStop(false, true); + } + + @Test + public void testStartStopHttpsKerberos() throws Exception { + testStartStop(true, true); + } + + @Test + public void testKMSProvider() throws Exception { + File confDir = getTestDir(); + Configuration conf = createBaseKMSConf(confDir); + writeConf(confDir, conf); + + runServer(null, null, confDir, new KMSCallable() { + @Override + public Void call() throws Exception { + Date started = new Date(); + Configuration conf = new Configuration(); + URI uri = createKMSUri(getKMSUrl()); + KeyProvider kp = new KMSClientProvider(uri, conf); + + // getKeys() empty + Assert.assertTrue(kp.getKeys().isEmpty()); + + // getKeysMetadata() empty + Assert.assertEquals(0, kp.getKeysMetadata().length); + + // createKey() + KeyProvider.Options options = new KeyProvider.Options(conf); + options.setCipher("AES/CTR/NoPadding"); + options.setBitLength(128); + options.setDescription("l1"); + KeyProvider.KeyVersion kv0 = kp.createKey("k1", options); + Assert.assertNotNull(kv0); + Assert.assertNotNull(kv0.getVersionName()); + Assert.assertNotNull(kv0.getMaterial()); + + // getKeyVersion() + KeyProvider.KeyVersion kv1 = kp.getKeyVersion(kv0.getVersionName()); + Assert.assertEquals(kv0.getVersionName(), kv1.getVersionName()); + Assert.assertNotNull(kv1.getMaterial()); + + // getCurrent() + KeyProvider.KeyVersion cv1 = kp.getCurrentKey("k1"); + Assert.assertEquals(kv0.getVersionName(), cv1.getVersionName()); + Assert.assertNotNull(cv1.getMaterial()); + + // getKeyMetadata() 1 version + KeyProvider.Metadata m1 = kp.getMetadata("k1"); + Assert.assertEquals("AES/CTR/NoPadding", m1.getCipher()); + Assert.assertEquals("AES", m1.getAlgorithm()); + Assert.assertEquals(128, m1.getBitLength()); + Assert.assertEquals(1, m1.getVersions()); + Assert.assertNotNull(m1.getCreated()); + Assert.assertTrue(started.before(m1.getCreated())); + + // getKeyVersions() 1 version + List<KeyProvider.KeyVersion> lkv1 = kp.getKeyVersions("k1"); + Assert.assertEquals(1, lkv1.size()); + Assert.assertEquals(kv0.getVersionName(), lkv1.get(0).getVersionName()); + Assert.assertNotNull(kv1.getMaterial()); + + // rollNewVersion() + KeyProvider.KeyVersion kv2 = kp.rollNewVersion("k1"); + Assert.assertNotSame(kv0.getVersionName(), kv2.getVersionName()); + Assert.assertNotNull(kv2.getMaterial()); + + // getKeyVersion() + kv2 = kp.getKeyVersion(kv2.getVersionName()); + boolean eq = true; + for (int i = 0; i < kv1.getMaterial().length; i++) { + eq = eq && kv1.getMaterial()[i] == kv2.getMaterial()[i]; + } + Assert.assertFalse(eq); + + // getCurrent() + KeyProvider.KeyVersion cv2 = kp.getCurrentKey("k1"); + Assert.assertEquals(kv2.getVersionName(), cv2.getVersionName()); + Assert.assertNotNull(cv2.getMaterial()); + eq = true; + for (int i = 0; i < kv1.getMaterial().length; i++) { + eq = eq && cv2.getMaterial()[i] == kv2.getMaterial()[i]; + } + Assert.assertTrue(eq); + + // getKeyVersions() 2 versions + List<KeyProvider.KeyVersion> lkv2 = kp.getKeyVersions("k1"); + Assert.assertEquals(2, lkv2.size()); + Assert.assertEquals(kv1.getVersionName(), lkv2.get(0).getVersionName()); + Assert.assertNotNull(lkv2.get(0).getMaterial()); + Assert.assertEquals(kv2.getVersionName(), lkv2.get(1).getVersionName()); + Assert.assertNotNull(lkv2.get(1).getMaterial()); + + // getKeyMetadata() 2 version + KeyProvider.Metadata m2 = kp.getMetadata("k1"); + Assert.assertEquals("AES/CTR/NoPadding", m2.getCipher()); + Assert.assertEquals("AES", m2.getAlgorithm()); + Assert.assertEquals(128, m2.getBitLength()); + Assert.assertEquals(2, m2.getVersions()); + Assert.assertNotNull(m2.getCreated()); + Assert.assertTrue(started.before(m2.getCreated())); + + // getKeys() 1 key + List<String> ks1 = kp.getKeys(); + Assert.assertEquals(1, ks1.size()); + Assert.assertEquals("k1", ks1.get(0)); + + // getKeysMetadata() 1 key 2 versions + KeyProvider.Metadata[] kms1 = kp.getKeysMetadata("k1"); + Assert.assertEquals(1, kms1.length); + Assert.assertEquals("AES/CTR/NoPadding", kms1[0].getCipher()); + Assert.assertEquals("AES", kms1[0].getAlgorithm()); + Assert.assertEquals(128, kms1[0].getBitLength()); + Assert.assertEquals(2, kms1[0].getVersions()); + Assert.assertNotNull(kms1[0].getCreated()); + Assert.assertTrue(started.before(kms1[0].getCreated())); + + // deleteKey() + kp.deleteKey("k1"); + + // getKey() + Assert.assertNull(kp.getKeyVersion("k1")); + + // getKeyVersions() + Assert.assertNull(kp.getKeyVersions("k1")); + + // getMetadata() + Assert.assertNull(kp.getMetadata("k1")); + + // getKeys() empty + Assert.assertTrue(kp.getKeys().isEmpty()); + + // getKeysMetadata() empty + Assert.assertEquals(0, kp.getKeysMetadata().length); + + return null; + } + }); + } + + @Test + public void testACLs() throws Exception { + final File testDir = getTestDir(); + Configuration conf = createBaseKMSConf(testDir); + conf.set("hadoop.kms.authentication.type", "kerberos"); + conf.set("hadoop.kms.authentication.kerberos.keytab", + keytab.getAbsolutePath()); + conf.set("hadoop.kms.authentication.kerberos.principal", "HTTP/localhost"); + conf.set("hadoop.kms.authentication.kerberos.name.rules", "DEFAULT"); + + for (KMSACLs.Type type : KMSACLs.Type.values()) { + conf.set(type.getConfigKey(), type.toString()); + } + conf.set(KMSACLs.Type.CREATE.getConfigKey(), + KMSACLs.Type.CREATE.toString() + ",SET_KEY_MATERIAL"); + + conf.set(KMSACLs.Type.ROLLOVER.getConfigKey(), + KMSACLs.Type.ROLLOVER.toString() + ",SET_KEY_MATERIAL"); + + writeConf(testDir, conf); + + runServer(null, null, testDir, new KMSCallable() { + @Override + public Void call() throws Exception { + final Configuration conf = new Configuration(); + conf.setInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 64); + URI uri = createKMSUri(getKMSUrl()); + final KeyProvider kp = new KMSClientProvider(uri, conf); + + //nothing allowed + doAs("client", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + kp.createKey("k", new KeyProvider.Options(conf)); + Assert.fail(); + } catch (AuthorizationException ex) { + //NOP + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + try { + kp.createKey("k", new byte[8], new KeyProvider.Options(conf)); + Assert.fail(); + } catch (AuthorizationException ex) { + //NOP + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + try { + kp.rollNewVersion("k"); + Assert.fail(); + } catch (AuthorizationException ex) { + //NOP + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + try { + kp.rollNewVersion("k", new byte[8]); + Assert.fail(); + } catch (AuthorizationException ex) { + //NOP + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + try { + kp.getKeys(); + Assert.fail(); + } catch (AuthorizationException ex) { + //NOP + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + try { + kp.getKeysMetadata("k"); + Assert.fail(); + } catch (AuthorizationException ex) { + //NOP + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + try { + kp.getKeyVersion(KMSClientProvider.buildVersionName("k", 0)); + Assert.fail(); + } catch (AuthorizationException ex) { + //NOP + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + try { + kp.getCurrentKey("k"); + Assert.fail(); + } catch (AuthorizationException ex) { + //NOP + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + try { + kp.getMetadata("k"); + Assert.fail(); + } catch (AuthorizationException ex) { + //NOP + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + try { + kp.getKeyVersions("k"); + Assert.fail(); + } catch (AuthorizationException ex) { + //NOP + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + + return null; + } + }); + + doAs("CREATE", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + KeyProvider.KeyVersion kv = kp.createKey("k0", + new KeyProvider.Options(conf)); + Assert.assertNull(kv.getMaterial()); + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + return null; + } + }); + + doAs("DELETE", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + kp.deleteKey("k0"); + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + return null; + } + }); + + doAs("SET_KEY_MATERIAL", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + KeyProvider.KeyVersion kv = kp.createKey("k1", new byte[8], + new KeyProvider.Options(conf)); + Assert.assertNull(kv.getMaterial()); + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + return null; + } + }); + + doAs("ROLLOVER", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + KeyProvider.KeyVersion kv = kp.rollNewVersion("k1"); + Assert.assertNull(kv.getMaterial()); + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + return null; + } + }); + + doAs("SET_KEY_MATERIAL", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + KeyProvider.KeyVersion kv = kp.rollNewVersion("k1", new byte[8]); + Assert.assertNull(kv.getMaterial()); + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + return null; + } + }); + + doAs("GET", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + kp.getKeyVersion("k1@0"); + kp.getCurrentKey("k1"); + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + return null; + } + }); + + doAs("GET_KEYS", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + kp.getKeys(); + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + return null; + } + }); + + doAs("GET_METADATA", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + kp.getMetadata("k1"); + kp.getKeysMetadata("k1"); + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + return null; + } + }); + + // test ACL reloading + Thread.sleep(10); // to ensure the ACLs file modifiedTime is newer + conf.set(KMSACLs.Type.CREATE.getConfigKey(), "foo"); + writeConf(testDir, conf); + + KMSWebApp.getACLs().run(); // forcing a reload by hand. + + // should not be able to create a key now + doAs("CREATE", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + KeyProvider.KeyVersion kv = kp.createKey("k2", + new KeyProvider.Options(conf)); + Assert.fail(); + } catch (AuthorizationException ex) { + //NOP + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + + return null; + } + }); + + return null; + } + }); + } + + @Test + public void testServicePrincipalACLs() throws Exception { + File testDir = getTestDir(); + Configuration conf = createBaseKMSConf(testDir); + conf.set("hadoop.kms.authentication.type", "kerberos"); + conf.set("hadoop.kms.authentication.kerberos.keytab", + keytab.getAbsolutePath()); + conf.set("hadoop.kms.authentication.kerberos.principal", "HTTP/localhost"); + conf.set("hadoop.kms.authentication.kerberos.name.rules", "DEFAULT"); + for (KMSACLs.Type type : KMSACLs.Type.values()) { + conf.set(type.getConfigKey(), " "); + } + conf.set(KMSACLs.Type.CREATE.getConfigKey(), "client"); + + writeConf(testDir, conf); + + runServer(null, null, testDir, new KMSCallable() { + @Override + public Void call() throws Exception { + final Configuration conf = new Configuration(); + conf.setInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 64); + URI uri = createKMSUri(getKMSUrl()); + final KeyProvider kp = new KMSClientProvider(uri, conf); + + doAs("client", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + KeyProvider.KeyVersion kv = kp.createKey("ck0", + new KeyProvider.Options(conf)); + Assert.assertNull(kv.getMaterial()); + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + return null; + } + }); + + doAs("client/host", new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + try { + KeyProvider.KeyVersion kv = kp.createKey("ck1", + new KeyProvider.Options(conf)); + Assert.assertNull(kv.getMaterial()); + } catch (Exception ex) { + Assert.fail(ex.toString()); + } + return null; + } + }); + return null; + } + }); + } + +}
Added: hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSACLs.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSACLs.java?rev=1592637&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSACLs.java (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSACLs.java Mon May 5 21:43:14 2014 @@ -0,0 +1,47 @@ +/** + * 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 org.apache.hadoop.conf.Configuration; +import org.junit.Assert; +import org.junit.Test; + +public class TestKMSACLs { + + @Test + public void testDefaults() { + KMSACLs acls = new KMSACLs(new Configuration(false)); + for (KMSACLs.Type type : KMSACLs.Type.values()) { + Assert.assertTrue(acls.hasAccess(type, "foo")); + } + } + + @Test + public void testCustom() { + Configuration conf = new Configuration(false); + for (KMSACLs.Type type : KMSACLs.Type.values()) { + conf.set(type.getConfigKey(), type.toString() + " "); + } + KMSACLs acls = new KMSACLs(conf); + for (KMSACLs.Type type : KMSACLs.Type.values()) { + Assert.assertTrue(acls.hasAccess(type, type.toString())); + Assert.assertFalse(acls.hasAccess(type, "foo")); + } + } + +} Added: hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSCacheKeyProvider.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSCacheKeyProvider.java?rev=1592637&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSCacheKeyProvider.java (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSCacheKeyProvider.java Mon May 5 21:43:14 2014 @@ -0,0 +1,120 @@ +/** + * 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 org.apache.hadoop.crypto.key.KeyProvider; +import org.apache.hadoop.crypto.key.kms.KMSClientProvider; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Date; + +public class TestKMSCacheKeyProvider { + + @Test + public void testCurrentKey() throws Exception { + KeyProvider.KeyVersion mockKey = Mockito.mock(KeyProvider.KeyVersion.class); + KeyProvider mockProv = Mockito.mock(KeyProvider.class); + Mockito.when(mockProv.getCurrentKey(Mockito.eq("k1"))).thenReturn(mockKey); + Mockito.when(mockProv.getCurrentKey(Mockito.eq("k2"))).thenReturn(null); + KeyProvider cache = new KMSCacheKeyProvider(mockProv, 100); + + // asserting caching + Assert.assertEquals(mockKey, cache.getCurrentKey("k1")); + Mockito.verify(mockProv, Mockito.times(1)).getCurrentKey(Mockito.eq("k1")); + Assert.assertEquals(mockKey, cache.getCurrentKey("k1")); + Mockito.verify(mockProv, Mockito.times(1)).getCurrentKey(Mockito.eq("k1")); + Thread.sleep(1200); + Assert.assertEquals(mockKey, cache.getCurrentKey("k1")); + Mockito.verify(mockProv, Mockito.times(2)).getCurrentKey(Mockito.eq("k1")); + + // asserting no caching when key is not known + cache = new KMSCacheKeyProvider(mockProv, 100); + Assert.assertEquals(null, cache.getCurrentKey("k2")); + Mockito.verify(mockProv, Mockito.times(1)).getCurrentKey(Mockito.eq("k2")); + Assert.assertEquals(null, cache.getCurrentKey("k2")); + Mockito.verify(mockProv, Mockito.times(2)).getCurrentKey(Mockito.eq("k2")); + } + + @Test + public void testKeyVersion() throws Exception { + KeyProvider.KeyVersion mockKey = Mockito.mock(KeyProvider.KeyVersion.class); + KeyProvider mockProv = Mockito.mock(KeyProvider.class); + Mockito.when(mockProv.getKeyVersion(Mockito.eq("k1@0"))).thenReturn(mockKey); + Mockito.when(mockProv.getKeyVersion(Mockito.eq("k2@0"))).thenReturn(null); + KeyProvider cache = new KMSCacheKeyProvider(mockProv, 100); + + // asserting caching + Assert.assertEquals(mockKey, cache.getKeyVersion("k1@0")); + Mockito.verify(mockProv, Mockito.times(1)).getKeyVersion(Mockito.eq("k1@0")); + Assert.assertEquals(mockKey, cache.getKeyVersion("k1@0")); + Mockito.verify(mockProv, Mockito.times(1)).getKeyVersion(Mockito.eq("k1@0")); + Thread.sleep(200); + Assert.assertEquals(mockKey, cache.getKeyVersion("k1@0")); + Mockito.verify(mockProv, Mockito.times(2)).getKeyVersion(Mockito.eq("k1@0")); + + // asserting no caching when key is not known + cache = new KMSCacheKeyProvider(mockProv, 100); + Assert.assertEquals(null, cache.getKeyVersion("k2@0")); + Mockito.verify(mockProv, Mockito.times(1)).getKeyVersion(Mockito.eq("k2@0")); + Assert.assertEquals(null, cache.getKeyVersion("k2@0")); + Mockito.verify(mockProv, Mockito.times(2)).getKeyVersion(Mockito.eq("k2@0")); + } + + @Test + public void testRollNewVersion() throws Exception { + KeyProvider.KeyVersion mockKey = Mockito.mock(KeyProvider.KeyVersion.class); + KeyProvider mockProv = Mockito.mock(KeyProvider.class); + Mockito.when(mockProv.getCurrentKey(Mockito.eq("k1"))).thenReturn(mockKey); + KeyProvider cache = new KMSCacheKeyProvider(mockProv, 100); + Assert.assertEquals(mockKey, cache.getCurrentKey("k1")); + Mockito.verify(mockProv, Mockito.times(1)).getCurrentKey(Mockito.eq("k1")); + cache.rollNewVersion("k1"); + + // asserting the cache is purged + Assert.assertEquals(mockKey, cache.getCurrentKey("k1")); + Mockito.verify(mockProv, Mockito.times(2)).getCurrentKey(Mockito.eq("k1")); + cache.rollNewVersion("k1", new byte[0]); + Assert.assertEquals(mockKey, cache.getCurrentKey("k1")); + Mockito.verify(mockProv, Mockito.times(3)).getCurrentKey(Mockito.eq("k1")); + } + + @Test + public void testDeleteKey() throws Exception { + KeyProvider.KeyVersion mockKey = Mockito.mock(KeyProvider.KeyVersion.class); + KeyProvider mockProv = Mockito.mock(KeyProvider.class); + Mockito.when(mockProv.getCurrentKey(Mockito.eq("k1"))).thenReturn(mockKey); + Mockito.when(mockProv.getKeyVersion(Mockito.eq("k1@0"))).thenReturn(mockKey); + Mockito.when(mockProv.getMetadata(Mockito.eq("k1"))).thenReturn( + new KMSClientProvider.KMSMetadata("c", 0, "l", new Date(), 1)); + KeyProvider cache = new KMSCacheKeyProvider(mockProv, 100); + Assert.assertEquals(mockKey, cache.getCurrentKey("k1")); + Mockito.verify(mockProv, Mockito.times(1)).getCurrentKey(Mockito.eq("k1")); + Assert.assertEquals(mockKey, cache.getKeyVersion("k1@0")); + Mockito.verify(mockProv, Mockito.times(1)).getKeyVersion(Mockito.eq("k1@0")); + cache.deleteKey("k1"); + + // asserting the cache is purged + Assert.assertEquals(mockKey, cache.getCurrentKey("k1")); + Mockito.verify(mockProv, Mockito.times(2)).getCurrentKey(Mockito.eq("k1")); + Assert.assertEquals(mockKey, cache.getKeyVersion("k1@0")); + Mockito.verify(mockProv, Mockito.times(2)).getKeyVersion(Mockito.eq("k1@0")); + } + +} Added: hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties?rev=1592637&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties Mon May 5 21:43:14 2014 @@ -0,0 +1,31 @@ +# +# 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. +# + +# STDOUT Appender +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p %c{1} - %m%n + +log4j.rootLogger=WARN, stdout +log4j.logger.org.apache.hadoop.conf=ERROR +log4j.logger.org.apache.hadoop.crytpo.key.kms.server=ALL +log4j.logger.com.sun.jersey.server.wadl.generators.WadlGeneratorJAXBGrammarGenerator=OFF +log4j.logger.org.apache.hadoop.security=OFF +log4j.logger.org.apache.directory.server.core=OFF +log4j.logger.org.apache.hadoop.util.NativeCodeLoader=OFF \ No newline at end of file Modified: hadoop/common/trunk/hadoop-common-project/pom.xml URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/pom.xml?rev=1592637&r1=1592636&r2=1592637&view=diff ============================================================================== --- hadoop/common/trunk/hadoop-common-project/pom.xml (original) +++ hadoop/common/trunk/hadoop-common-project/pom.xml Mon May 5 21:43:14 2014 @@ -37,6 +37,7 @@ <module>hadoop-annotations</module> <module>hadoop-nfs</module> <module>hadoop-minikdc</module> + <module>hadoop-kms</module> </modules> <build>
