Repository: ambari Updated Branches: refs/heads/branch-2.0.0 7d7468904 -> c7e8cbaa7
AMBARI-9007. Identity references fail to deference for service-level references in Kerberos descriptor parser (rlevas) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/c7e8cbaa Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/c7e8cbaa Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/c7e8cbaa Branch: refs/heads/branch-2.0.0 Commit: c7e8cbaa73563c600c0bd6e8b3a0d57cfc2dd28d Parents: 7d74689 Author: Robert Levas <rle...@hortonworks.com> Authored: Wed Mar 11 21:14:17 2015 -0400 Committer: Robert Levas <rle...@hortonworks.com> Committed: Wed Mar 11 21:14:17 2015 -0400 ---------------------------------------------------------------------- .../AbstractKerberosDescriptorContainer.java | 127 +++++++++++++------ .../state/kerberos/KerberosDescriptor.java | 20 +++ .../HIVE/0.12.0.2.0/kerberos.json | 54 ++++---- .../state/kerberos/KerberosDescriptorTest.java | 89 +++++++++++++ ...test_get_referenced_identity_descriptor.json | 127 +++++++++++++++++++ 5 files changed, 350 insertions(+), 67 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/c7e8cbaa/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java index 2ec2cb5..874e331 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java @@ -527,10 +527,38 @@ public abstract class AbstractKerberosDescriptorContainer extends AbstractKerber * <p/> * The path value is expected to be an "absolute" path through the Kerberos Descriptor hierarchy * to some specific KerberosIdentityDescriptor. The path must be in one of the following forms: + * <ul> + * <li>/identity</li> + * <li>/service/identity</li> + * <li>/service/component/identity</li> + * </ul> * <p/> - * /identity - * /service/identity - * /service/component/identity + * If the path starts with "../", the ".." will be translated to the path of the parent item. + * In the following example, <code>../service_identity</code> will resolve to + * <code>/SERVICE/service_identity</code>: + * <pre> + * { + * "name": "SERVICE", + * "identities": [ + * { + * "name": "service_identity", + * ... + * } + * ], + * "components" : [ + * { + * "name": "COMPONENT", + * "identities": [ + * { + * "name": "./service_identity", + * ... + * }, + * ... + * ] + * } + * ] + * } + * </pre> * * @param path a String declaring the path to a KerberosIdentityDescriptor * @return a KerberosIdentityDescriptor identified by the path or null if not found @@ -539,50 +567,72 @@ public abstract class AbstractKerberosDescriptorContainer extends AbstractKerber throws AmbariException { KerberosIdentityDescriptor identityDescriptor = null; - if ((path != null) && path.startsWith("/")) { - // The name indicates it is referencing an identity somewhere in the hierarchy... try to find it. - // /[<service name>/[<component name>/]]<identity name> - String[] pathParts = path.split("/"); + if (path != null) { + if(path.startsWith("../")) { + // Resolve parent path + AbstractKerberosDescriptor parent = getParent(); - String serviceName = null; - String componentName = null; - String identityName; + path = path.substring(2); - switch (pathParts.length) { - case 3: - serviceName = pathParts[0]; - componentName = pathParts[1]; - identityName = pathParts[2]; - break; - case 2: - serviceName = pathParts[0]; - identityName = pathParts[1]; - break; - case 1: - identityName = pathParts[0]; - break; - default: - throw new AmbariException(String.format("Unexpected path length in %s", path)); + while(parent != null) { + String name = parent.getName(); + + if (name != null) { + path = String.format("/%s", name) + path; + } + + parent = parent.getParent(); + } } - if (identityName != null) { - // Start at the top of the hierarchy - AbstractKerberosDescriptor descriptor = getRoot(); + if (path.startsWith("/")) { + // The name indicates it is referencing an identity somewhere in the hierarchy... try to find it. + // /[<service name>/[<component name>/]]<identity name> + String[] pathParts = path.split("/"); + + String serviceName = null; + String componentName = null; + String identityName; + + switch (pathParts.length) { + case 4: + serviceName = pathParts[1]; + componentName = pathParts[2]; + identityName = pathParts[3]; + break; + case 3: + serviceName = pathParts[1]; + identityName = pathParts[2]; + break; + case 2: + identityName = pathParts[1]; + break; + case 1: + identityName = pathParts[0]; + break; + default: + throw new AmbariException(String.format("Unexpected path length in %s", path)); + } + + if (identityName != null) { + // Start at the top of the hierarchy + AbstractKerberosDescriptor descriptor = getRoot(); - if (descriptor != null) { - if ((serviceName != null) && !serviceName.isEmpty()) { - descriptor = descriptor.getDescriptor(KerberosDescriptorType.SERVICE, serviceName); + if (descriptor != null) { + if ((serviceName != null) && !serviceName.isEmpty()) { + descriptor = descriptor.getDescriptor(KerberosDescriptorType.SERVICE, serviceName); - if ((descriptor != null) && (componentName != null) && !componentName.isEmpty()) { - descriptor = descriptor.getDescriptor(KerberosDescriptorType.COMPONENT, componentName); + if ((descriptor != null) && (componentName != null) && !componentName.isEmpty()) { + descriptor = descriptor.getDescriptor(KerberosDescriptorType.COMPONENT, componentName); + } } - } - if (descriptor != null) { - descriptor = descriptor.getDescriptor(KerberosDescriptorType.IDENTITY, identityName); + if (descriptor != null) { + descriptor = descriptor.getDescriptor(KerberosDescriptorType.IDENTITY, identityName); - if (descriptor instanceof KerberosIdentityDescriptor) { - identityDescriptor = (KerberosIdentityDescriptor) descriptor; + if (descriptor instanceof KerberosIdentityDescriptor) { + identityDescriptor = (KerberosIdentityDescriptor) descriptor; + } } } } @@ -590,7 +640,6 @@ public abstract class AbstractKerberosDescriptorContainer extends AbstractKerber } return identityDescriptor; - } /** http://git-wip-us.apache.org/repos/asf/ambari/blob/c7e8cbaa/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java index 791b0e6..8cd9718 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java @@ -256,6 +256,26 @@ public class KerberosDescriptor extends AbstractKerberosDescriptorContainer { } /** + * Gets the requested AbstractKerberosDescriptor implementation using a type name and a relevant + * descriptor name. + * <p/> + * This implementation handles service descriptors and delegates to the AbstractKerberosDescriptor + * implementation to handle component and identity types. + * + * @param type a String indicating the type of the requested descriptor + * @param name a String indicating the name of the requested descriptor + * @return a AbstractKerberosDescriptor representing the requested descriptor or null if not found + */ + @Override + protected AbstractKerberosDescriptor getDescriptor(KerberosDescriptorType type, String name) { + if (KerberosDescriptorType.SERVICE == type) { + return getService(name); + } else { + return super.getDescriptor(type, name); + } + } + + /** * Creates a Map of values that can be used to create a copy of this KerberosDescriptor * or generate the JSON structure described in * {@link org.apache.ambari.server.state.kerberos.KerberosDescriptor} http://git-wip-us.apache.org/repos/asf/ambari/blob/c7e8cbaa/ambari-server/src/main/resources/common-services/HIVE/0.12.0.2.0/kerberos.json ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/common-services/HIVE/0.12.0.2.0/kerberos.json b/ambari-server/src/main/resources/common-services/HIVE/0.12.0.2.0/kerberos.json index ad5ee3e..e154c02 100644 --- a/ambari-server/src/main/resources/common-services/HIVE/0.12.0.2.0/kerberos.json +++ b/ambari-server/src/main/resources/common-services/HIVE/0.12.0.2.0/kerberos.json @@ -8,6 +8,25 @@ }, { "name": "/smokeuser" + }, + { + "name": "hive", + "principal": { + "value": "hive/_HOST@${realm}", + "type": "service", + "local_username": "${hive-env/hive_user}" + }, + "keytab": { + "file": "${keytab_dir}/hive.service.keytab", + "owner": { + "name": "${hive-env/hive_user}", + "access": "r" + }, + "group": { + "name": "${cluster-env/user_group}", + "access": "" + } + } } ], "configurations": [ @@ -35,23 +54,11 @@ "name": "HIVE_METASTORE", "identities": [ { - "name": "hive_metastore_hive", + "name": "../hive", "principal": { - "value": "hive/_HOST@${realm}", - "type" : "service", - "configuration": "hive-site/hive.metastore.kerberos.principal", - "local_username": "${hive-env/hive_user}" + "configuration": "hive-site/hive.metastore.kerberos.principal" }, "keytab": { - "file": "${keytab_dir}/hive.service.keytab", - "owner": { - "name": "${hive-env/hive_user}", - "access": "r" - }, - "group": { - "name": "${cluster-env/user_group}", - "access": "" - }, "configuration": "hive-site/hive.metastore.kerberos.keytab.file" } } @@ -61,23 +68,11 @@ "name": "HIVE_SERVER", "identities": [ { - "name": "hive_server_hive", + "name": "../hive", "principal": { - "value": "hive/_HOST@${realm}", - "type" : "service", - "configuration": "hive-site/hive.server2.authentication.kerberos.principal", - "local_username": "${hive-env/hive_user}" + "configuration": "hive-site/hive.server2.authentication.kerberos.principal" }, "keytab": { - "file": "${keytab_dir}/hive.service.keytab", - "owner": { - "name": "${hive-env/hive_user}", - "access": "r" - }, - "group": { - "name": "${cluster-env/user_group}", - "access": "" - }, "configuration": "hive-site/hive.server2.authentication.kerberos.keytab" } }, @@ -105,6 +100,9 @@ } } ] + }, + { + "name": "HIVE_CLIENT" } ] } http://git-wip-us.apache.org/repos/asf/ambari/blob/c7e8cbaa/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorTest.java index 97f8008..637facc 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorTest.java @@ -22,6 +22,9 @@ import junit.framework.Assert; import org.apache.ambari.server.AmbariException; import org.junit.Test; +import java.io.File; +import java.io.IOException; +import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -435,4 +438,90 @@ public class KerberosDescriptorTest { Assert.assertEquals("$c6401.ambari.apache.org", KerberosDescriptor.replaceVariables("$${host}", configurations)); } + + @Test + public void testGetReferencedIdentityDescriptor() throws IOException { + URL systemResourceURL = ClassLoader.getSystemResource("kerberos/test_get_referenced_identity_descriptor.json"); + Assert.assertNotNull(systemResourceURL); + KerberosDescriptor descriptor = KERBEROS_DESCRIPTOR_FACTORY.createInstance(new File(systemResourceURL.getFile())); + + KerberosIdentityDescriptor identity; + + // Stack-level identity + identity = descriptor.getReferencedIdentityDescriptor("/stack_identity"); + Assert.assertNotNull(identity); + Assert.assertEquals("stack_identity", identity.getName()); + + // Service-level identity + identity = descriptor.getReferencedIdentityDescriptor("/SERVICE1/service1_identity"); + Assert.assertNotNull(identity); + Assert.assertEquals("service1_identity", identity.getName()); + Assert.assertNotNull(identity.getParent()); + Assert.assertEquals("SERVICE1", identity.getParent().getName()); + + // Component-level identity + identity = descriptor.getReferencedIdentityDescriptor("/SERVICE2/SERVICE2_COMPONENT1/service2_component1_identity"); + Assert.assertNotNull(identity); + Assert.assertEquals("service2_component1_identity", identity.getName()); + Assert.assertNotNull(identity.getParent()); + Assert.assertEquals("SERVICE2_COMPONENT1", identity.getParent().getName()); + Assert.assertNotNull(identity.getParent().getParent()); + Assert.assertEquals("SERVICE2", identity.getParent().getParent().getName()); + } + + @Test + public void testGetReferencedIdentityDescriptor_NameCollisions() throws IOException { + URL systemResourceURL = ClassLoader.getSystemResource("kerberos/test_get_referenced_identity_descriptor.json"); + Assert.assertNotNull(systemResourceURL); + KerberosDescriptor descriptor = KERBEROS_DESCRIPTOR_FACTORY.createInstance(new File(systemResourceURL.getFile())); + + KerberosIdentityDescriptor identity; + + // Stack-level identity + identity = descriptor.getReferencedIdentityDescriptor("/collision"); + Assert.assertNotNull(identity); + Assert.assertEquals("collision", identity.getName()); + Assert.assertNotNull(identity.getParent()); + Assert.assertEquals(null, identity.getParent().getName()); + + // Service-level identity + identity = descriptor.getReferencedIdentityDescriptor("/SERVICE1/collision"); + Assert.assertNotNull(identity); + Assert.assertEquals("collision", identity.getName()); + Assert.assertNotNull(identity.getParent()); + Assert.assertEquals("SERVICE1", identity.getParent().getName()); + + // Component-level identity + identity = descriptor.getReferencedIdentityDescriptor("/SERVICE2/SERVICE2_COMPONENT1/collision"); + Assert.assertNotNull(identity); + Assert.assertEquals("collision", identity.getName()); + Assert.assertNotNull(identity.getParent()); + Assert.assertEquals("SERVICE2_COMPONENT1", identity.getParent().getName()); + Assert.assertNotNull(identity.getParent().getParent()); + Assert.assertEquals("SERVICE2", identity.getParent().getParent().getName()); + } + + @Test + public void testGetReferencedIdentityDescriptor_RelativePath() throws IOException { + URL systemResourceURL = ClassLoader.getSystemResource("kerberos/test_get_referenced_identity_descriptor.json"); + Assert.assertNotNull(systemResourceURL); + + KerberosDescriptor descriptor = KERBEROS_DESCRIPTOR_FACTORY.createInstance(new File(systemResourceURL.getFile())); + Assert.assertNotNull(descriptor); + + KerberosServiceDescriptor serviceDescriptor = descriptor.getService("SERVICE2"); + Assert.assertNotNull(serviceDescriptor); + + KerberosComponentDescriptor componentDescriptor = serviceDescriptor.getComponent("SERVICE2_COMPONENT1"); + Assert.assertNotNull(componentDescriptor); + + KerberosIdentityDescriptor identity; + identity = componentDescriptor.getReferencedIdentityDescriptor("../service2_identity"); + Assert.assertNotNull(identity); + Assert.assertEquals("service2_identity", identity.getName()); + Assert.assertEquals(serviceDescriptor, identity.getParent()); + + identity = serviceDescriptor.getReferencedIdentityDescriptor("../service2_identity"); + Assert.assertNull(identity); + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/c7e8cbaa/ambari-server/src/test/resources/kerberos/test_get_referenced_identity_descriptor.json ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/resources/kerberos/test_get_referenced_identity_descriptor.json b/ambari-server/src/test/resources/kerberos/test_get_referenced_identity_descriptor.json new file mode 100644 index 0000000..307a1e1 --- /dev/null +++ b/ambari-server/src/test/resources/kerberos/test_get_referenced_identity_descriptor.json @@ -0,0 +1,127 @@ +{ + "properties": { + "realm": "${kerberos-env/realm}", + "keytab_dir": "/etc/security/keytabs" + }, + "identities": [ + { + "name": "stack_identity", + "principal": { + "value": "stack@${realm}", + "type": "user" + }, + "keytab": { + "file": "${keytab_dir}/stack.keytab", + "owner": { + "name": "root", + "access": "r" + }, + "group": { + "name": "${cluster-env/user_group}", + "access": "r" + } + } + }, + { + "name": "collision", + "principal": { + "value": "stack_collision@${realm}", + "type": "user" + } + } + ], + "services" : [ + { + "name": "SERVICE1", + "identities": [ + { + "name": "service1_identity", + "principal": { + "value": "service1@${realm}", + "type": "user" + }, + "keytab": { + "file": "${keytab_dir}/service1.keytab", + "owner": { + "name": "root", + "access": "r" + }, + "group": { + "name": "${cluster-env/user_group}", + "access": "r" + } + } + }, + { + "name": "collision", + "principal": { + "value": "service1_collision@${realm}", + "type": "user" + } + } + ] + }, + { + "name": "SERVICE2", + "identities": [ + { + "name": "service2_identity", + "principal": { + "value": "service2@${realm}", + "type": "user" + }, + "keytab": { + "file": "${keytab_dir}/service2.keytab", + "owner": { + "name": "root", + "access": "r" + }, + "group": { + "name": "${cluster-env/user_group}", + "access": "r" + } + } + }, + { + "name": "collision", + "principal": { + "value": "service2_collision@${realm}", + "type": "user" + } + } + ], + "components" : [ + { + "name": "SERVICE2_COMPONENT1", + "identities": [ + { + "name": "service2_component1_identity", + "principal": { + "value": "service2_component1@${realm}", + "type": "user" + }, + "keytab": { + "file": "${keytab_dir}/service2_component1.keytab", + "owner": { + "name": "root", + "access": "r" + }, + "group": { + "name": "${cluster-env/user_group}", + "access": "r" + } + } + }, + { + "name": "collision", + "principal": { + "value": "service2_component1_collision@${realm}", + "type": "user" + } + } + ] + } + ] + } + ] +}