| /* |
| * 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.ambari.server.state.kerberos; |
| |
| import org.apache.ambari.server.AmbariException; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.TreeMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.regex.Pattern; |
| |
| /** |
| * AbstractKerberosDescriptorContainer is an abstract class implementing AbstractKerberosDescriptor |
| * and providing facility to handle common descriptor container functionality. |
| * <p/> |
| * Each AbstractKerberosDescriptorContainer contains identities and configurations as well as a |
| * name and a parent (which is inherited from AbstractKerberosDescriptor). |
| * <p/> |
| * An AbstractKerberosDescriptorContainer has the following properties: |
| * <ul> |
| * <li>identities</li> |
| * <li>configurations</li> |
| * </ul> |
| * <p/> |
| * The following (pseudo) JSON Schema will yield a valid AbstractKerberosDescriptorContainer |
| * <pre> |
| * { |
| * "$schema": "http://json-schema.org/draft-04/schema#", |
| * "title": "AbstractKerberosDescriptorContainer", |
| * "description": "Describes an AbstractKerberosDescriptorContainer", |
| * "type": "object", |
| * "properties": { |
| * "identities": { |
| * "description": "A list of Kerberos identity descriptors", |
| * "type": "array", |
| * "items": { |
| * "title": "KerberosIdentityDescriptor" |
| * "type": "{@link org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor}" |
| * } |
| * }, |
| * "configurations": { |
| * "description": "A list of relevant configuration blocks", |
| * "type": "array", |
| * "items": { |
| * "title": "KerberosConfigurationDescriptor" |
| * "type": "{@link org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor}" |
| * } |
| * }, |
| * "auth_to_local": { |
| * "description": "A list of configuration properties declaring which properties are auth-to-local values |
| * "type": "array", |
| * "items": { |
| * "title": "String" |
| * "type": "{@link java.lang.String}" |
| * } |
| * } |
| * } |
| * } |
| * </pre> |
| * <p/> |
| * This implementation does not set the |
| * {@link org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor#name} value, it is |
| * left up to the implementing class to do so. |
| */ |
| public abstract class AbstractKerberosDescriptorContainer extends AbstractKerberosDescriptor { |
| |
| /** |
| * Regular expression pattern used to parse auth_to_local property specifications into the following |
| * parts: |
| * <ul> |
| * <li>configuration type (optional, if _global_)</li> |
| * <li>property name</li> |
| * <li>concatenation type (optional, if using the default behavior)</li> |
| * </ul> |
| */ |
| public static final Pattern AUTH_TO_LOCAL_PROPERTY_SPECIFICATION_PATTERN = Pattern.compile("^(?:(.+?)/)?(.+?)(?:\\|(.+?))?$"); |
| |
| /** |
| * A List of KerberosIdentityDescriptors contained in this AbstractKerberosDescriptorContainer |
| */ |
| private List<KerberosIdentityDescriptor> identities = null; |
| |
| /** |
| * A Map of KerberosConfigurationDescriptors contained in this AbstractKerberosDescriptorContainer |
| */ |
| private Map<String, KerberosConfigurationDescriptor> configurations = null; |
| |
| /** |
| * A Set of configuration identifiers (config-type/property_name) that indicate which properties |
| * contain auth_to_local values. |
| */ |
| private Set<String> authToLocalProperties = null; |
| |
| /** |
| * Constructs a new AbstractKerberosDescriptorContainer |
| * <p/> |
| * This constructor must be called from the constructor(s) of the implementing classes |
| * |
| * @param data a Map of data used for collecting groups of common descriptors |
| */ |
| protected AbstractKerberosDescriptorContainer(Map<?, ?> data) { |
| if (data != null) { |
| Object list; |
| |
| // (Safely) Get the set of KerberosIdentityDescriptors |
| list = data.get(Type.IDENTITY.getDescriptorPluralName()); |
| if (list instanceof Collection) { |
| for (Object item : (Collection) list) { |
| if (item instanceof Map) { |
| putIdentity(new KerberosIdentityDescriptor((Map<?, ?>) item)); |
| } |
| } |
| } |
| |
| // (Safely) Get the set of KerberosConfigurationDescriptors |
| list = data.get(Type.CONFIGURATION.getDescriptorPluralName()); |
| if (list instanceof Collection) { |
| for (Object item : (Collection) list) { |
| if (item instanceof Map) { |
| putConfiguration(new KerberosConfigurationDescriptor((Map<?, ?>) item)); |
| } |
| } |
| } |
| |
| // (Safely) Get the set of KerberosConfigurationDescriptors |
| list = data.get(Type.AUTH_TO_LOCAL_PROPERTY.getDescriptorPluralName()); |
| if (list instanceof Collection) { |
| for (Object item : (Collection) list) { |
| if (item instanceof String) { |
| putAuthToLocalProperty((String) item); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns the child containers associated with this container. |
| * |
| * @return an immutable collection of {@link AbstractKerberosDescriptorContainer}s |
| */ |
| public abstract Collection<? extends AbstractKerberosDescriptorContainer> getChildContainers(); |
| |
| /** |
| * Returns a specific named child container |
| * |
| * @param name the name of the child container to retrieve |
| * @return an {@link AbstractKerberosDescriptorContainer} |
| */ |
| public abstract AbstractKerberosDescriptorContainer getChildContainer(String name); |
| |
| /** |
| * Returns the raw List of KerberosIdentityDescriptors contained within this |
| * AbstractKerberosDescriptorContainer. |
| * <p/> |
| * The returned KerberosIdentityDescriptors are not merged with data from referenced |
| * KerberosConfigurationDescriptors. This is the same calling |
| * {@link AbstractKerberosDescriptorContainer#getIdentities(boolean, Map)} and setting the |
| * argument to 'false' |
| * |
| * @return the relevant List of KerberosIdentityDescriptors |
| */ |
| public List<KerberosIdentityDescriptor> getIdentities() { |
| try { |
| return getIdentities(false, null); |
| } catch (AmbariException e) { |
| // AmbariException will not be thrown unless an error occurs while trying to dereference |
| // identities. This method does not attempt to dereference identities. |
| return null; |
| } |
| } |
| |
| /** |
| * Set the {@link KerberosIdentityDescriptor} for this {@link AbstractKerberosDescriptorContainer}. |
| * |
| * @param identities a {@link List} of {@link KerberosIdentityDescriptor}s |
| */ |
| public void setIdentities(List<KerberosIdentityDescriptor> identities) { |
| this.identities = (identities == null) |
| ? null |
| : new ArrayList<KerberosIdentityDescriptor>(identities); |
| } |
| |
| /** |
| * Returns a List of KerberosIdentityDescriptors contained within this |
| * AbstractKerberosDescriptorContainer. |
| * <p/> |
| * If resolveReferences is true, a "detached" set of KerberosIdentityDescriptors are returned. |
| * Any KerberosIdentityDescriptor that implies it references some other KerberosIdentityDescriptor |
| * in the hierarchy will be resolved. Meaning, if the name of the KerberosIdentityDescriptor |
| * indicates a path to some other KerberosIdentityDescriptor (i.e, /spnego, /HDFS/NAMENODE/nn, etc...) |
| * the referenced KerberosIdentityDescriptor is found, detached (or copied), and updated with |
| * the information from the initial KerberosIdentityDescriptor. Because of this, all of the |
| * KerberosIdentityDescriptors to be included are copied into the resulting list, and dissociating |
| * them with the rest of the hierarchy such that changes to them will not be reflected within the |
| * entire KerberosDescriptor tree. |
| * <p/> |
| * If resolveReferences is false, the raw List of KerberosIdentityDescriptors are returned. This |
| * data is not manipulated by resolving references and therefore it may be missing data, however |
| * this List is of "attached" descriptors, so changes will be reflected within the KerberosDescriptor |
| * hierarchy. |
| * |
| * @param resolveReferences a Boolean value indicating whether to resolve references (true) or not |
| * (false) |
| * @return a List of the requested KerberosIdentityDescriptors |
| */ |
| public List<KerberosIdentityDescriptor> getIdentities(boolean resolveReferences, Map<String, Object> contextForFilter) throws AmbariException { |
| if (identities == null) { |
| return null; |
| } else { |
| List<KerberosIdentityDescriptor> list = new ArrayList<KerberosIdentityDescriptor>(); |
| |
| for (KerberosIdentityDescriptor identity : identities) { |
| KerberosIdentityDescriptor identityToAdd; |
| |
| if (resolveReferences) { |
| // Dereference this KerberosIdentityDescriptor, if necessary |
| identityToAdd = dereferenceIdentity(identity); |
| } else { |
| identityToAdd = identity; |
| } |
| |
| // Make sure this Kerberos Identity is not to be filtered out based on its "when" clause |
| if ((identityToAdd != null) && ((contextForFilter == null) || identityToAdd.shouldInclude(contextForFilter))) { |
| list.add(identityToAdd); |
| } |
| } |
| |
| return list; |
| } |
| } |
| |
| /** |
| * Return a KerberosIdentityDescriptor with the specified name. |
| * |
| * @param name a String declaring the name of the descriptor to retrieve |
| * @return the requested KerberosIdentityDescriptor |
| */ |
| public KerberosIdentityDescriptor getIdentity(String name) { |
| KerberosIdentityDescriptor identity = null; |
| |
| if ((name != null) && (identities != null)) { |
| // Iterate over the List of KerberosIdentityDescriptors to find one with the requested name |
| // If one is found, break out of the loop. |
| for (KerberosIdentityDescriptor descriptor : identities) { |
| if (name.equals(descriptor.getName())) { |
| identity = descriptor; |
| break; |
| } |
| } |
| } |
| |
| return identity; |
| } |
| |
| /** |
| * Adds the specified KerberosIdentityDescriptor to the list of KerberosIdentityDescriptor. |
| * <p/> |
| * This method attempts to ensure that the names or KerberosIdentityDescriptors are unique within |
| * the List. |
| * |
| * @param identity the KerberosIdentityDescriptor to add |
| */ |
| public void putIdentity(KerberosIdentityDescriptor identity) { |
| if (identity != null) { |
| String name = identity.getName(); |
| |
| if (identities == null) { |
| identities = new ArrayList<KerberosIdentityDescriptor>(); |
| } |
| |
| // If the identity has a name, ensure that one one with that name is in the List |
| // Note: this cannot be enforced since any AbstractKerberosDescriptor+'s name property can be |
| // changed |
| if ((name != null) && !name.isEmpty()) { |
| removeIdentity(identity.getName()); |
| } |
| |
| identities.add(identity); |
| |
| // Set the identity's parent to this AbstractKerberosDescriptorContainer |
| identity.setParent(this); |
| } |
| } |
| |
| /** |
| * Remove all KerberosIdentityDescriptors have have the specified name |
| * <p/> |
| * One or more KerberosIdentityDescriptors may be removed if multiple KerberosIdentityDescriptors |
| * have the same name. |
| * |
| * @param name a String declaring the name of the descriptors to remove |
| */ |
| public void removeIdentity(String name) { |
| if ((name != null) && (identities != null)) { |
| Iterator<KerberosIdentityDescriptor> iterator = identities.iterator(); |
| |
| while (iterator.hasNext()) { |
| KerberosIdentityDescriptor identity = iterator.next(); |
| if (name.equals(identity.getName())) { |
| identity.setParent(null); |
| iterator.remove(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Sets the {@link Map} of {@link KerberosConfigurationDescriptor}s for this |
| * {@link AbstractKerberosDescriptorContainer}. |
| * |
| * @param configurations a {@link Map} of {@link KerberosConfigurationDescriptor}s |
| */ |
| public void setConfigurations(Map<String, KerberosConfigurationDescriptor> configurations) { |
| this.configurations = (configurations == null) |
| ? null |
| : new TreeMap<String, KerberosConfigurationDescriptor>(configurations); |
| } |
| |
| /** |
| * Returns a Map of raw KerberosConfigurationDescriptors contained within this |
| * AbstractKerberosDescriptorContainer. |
| * <p/> |
| * The returned KerberosConfigurationDescriptors are not merged with data from KerberosDescriptor |
| * hierarchy. This is the same calling |
| * {@link AbstractKerberosDescriptorContainer#getConfigurations(boolean)} and setting the argument |
| * to 'false' |
| * |
| * @return a List of KerberosConfigurationDescriptors |
| */ |
| public Map<String, KerberosConfigurationDescriptor> getConfigurations() { |
| return getConfigurations(false); |
| } |
| |
| /** |
| * Returns a Map of KerberosConfigurationDescriptors contained within this |
| * AbstractKerberosDescriptorContainer. |
| * <p/> |
| * If includeInherited is true, the Map will contain "detached" KerberosConfigurationDescriptor |
| * instances, but the data will be merged with all relevant KerberosConfigurationDescriptors within |
| * the hierarchy. Data higher in the hierarchy (the service level is higher in the hierarchy than |
| * the component level) will be overwritten by data lower in the hierarchy. |
| * <p/> |
| * If includeInherited is false, the Map will consist of attached (non-merged) |
| * KerberosConfigurationDescriptors. This data is not manipulated by merging with data within the |
| * KerberosDescriptor hierarchy and therefore it may be missing data, however this Map is of |
| * "attached" descriptors, so changes will be reflected within the KerberosDescriptor |
| * hierarchy. |
| * |
| * @param includeInherited a Boolean value indicating whether to include configuration within the |
| * KerberosDescriptor hierarchy (true) or not (false) |
| * @return a Map of Strings to KerberosConfigurationDescriptors, where the key is the type |
| * (core-site, etc...) |
| */ |
| public Map<String, KerberosConfigurationDescriptor> getConfigurations(boolean includeInherited) { |
| if (includeInherited) { |
| Map<String, KerberosConfigurationDescriptor> mergedConfigurations = new TreeMap<String, KerberosConfigurationDescriptor>(); |
| List<Map<String, KerberosConfigurationDescriptor>> configurationSets = new ArrayList<Map<String, KerberosConfigurationDescriptor>>(); |
| AbstractKerberosDescriptor currentDescriptor = this; |
| |
| // Walk up the hierarchy and collect the configuration sets. |
| while (currentDescriptor != null) { |
| if (currentDescriptor.isContainer()) { |
| Map<String, KerberosConfigurationDescriptor> configurations = ((AbstractKerberosDescriptorContainer) currentDescriptor).getConfigurations(); |
| |
| if (configurations != null) { |
| configurationSets.add(configurations); |
| } |
| } |
| |
| currentDescriptor = currentDescriptor.getParent(); |
| } |
| |
| // Reverse the collection so that we can merge from top to bottom |
| Collections.reverse(configurationSets); |
| |
| for (Map<String, KerberosConfigurationDescriptor> map : configurationSets) { |
| for (Map.Entry<String, KerberosConfigurationDescriptor> entry : map.entrySet()) { |
| // For each configuration type, copy it and determine if an entry exists or not. |
| // ** If one exists, merge the current data into the existing data (potentially |
| // overwriting values). |
| // ** If one does not exist, simply add a copy of the current one to the Map |
| String currentType = entry.getKey(); |
| KerberosConfigurationDescriptor currentConfiguration = entry.getValue(); |
| |
| if (currentConfiguration != null) { |
| KerberosConfigurationDescriptor detachedConfiguration = new KerberosConfigurationDescriptor(currentConfiguration.toMap()); |
| KerberosConfigurationDescriptor mergedConfiguration = mergedConfigurations.get(entry.getKey()); |
| |
| if (mergedConfiguration == null) { |
| mergedConfigurations.put(currentType, detachedConfiguration); |
| } else { |
| mergedConfiguration.update(detachedConfiguration); |
| } |
| } |
| } |
| } |
| |
| return mergedConfigurations; |
| } else { |
| return configurations; |
| } |
| } |
| |
| /** |
| * Adds the specified KerberosConfigurationDescriptor to the list of KerberosConfigurationDescriptors. |
| * <p/> |
| * If an entry exists of the same configuration type, it will be overwritten. |
| * |
| * @param configuration the KerberosConfigurationDescriptor to add |
| */ |
| public void putConfiguration(KerberosConfigurationDescriptor configuration) { |
| if (configuration != null) { |
| String type = configuration.getType(); |
| |
| if (type == null) { |
| throw new IllegalArgumentException("The configuration type must not be null"); |
| } |
| |
| if (configurations == null) { |
| configurations = new TreeMap<String, KerberosConfigurationDescriptor>(); |
| } |
| |
| configurations.put(type, configuration); |
| |
| // Set the configuration's parent to this AbstractKerberosDescriptorContainer |
| configuration.setParent(this); |
| } |
| } |
| |
| /** |
| * Returns the requested KerberosConfigurationDescriptor |
| * |
| * @param name a String declaring the name of the descriptor to retrieve |
| * @return the requested KerberosConfigurationDescriptor or null if not found |
| */ |
| public KerberosConfigurationDescriptor getConfiguration(String name) { |
| return ((name == null) || (configurations == null)) ? null : configurations.get(name); |
| } |
| |
| /** |
| * Adds the specified property name to the set of <code>auth_to_local</code> property names. |
| * <p/> |
| * Each <code>auth_to_local</code> property name is expected to be in the following format: |
| * <code>config-type/property_name</code>` |
| * |
| * @param authToLocalProperty the auth_to_local property to add |
| */ |
| public void putAuthToLocalProperty(String authToLocalProperty) { |
| if (authToLocalProperty != null) { |
| if (authToLocalProperties == null) { |
| authToLocalProperties = new TreeSet<String>(); |
| } |
| |
| authToLocalProperties.add(authToLocalProperty); |
| } |
| } |
| |
| /** |
| * Sets the set of <code>auth_to_local</code> property names. |
| * |
| * @param authToLocalProperties a Set of String values; or null if not set |
| */ |
| public void setAuthToLocalProperties(Set<String> authToLocalProperties) { |
| this.authToLocalProperties = (authToLocalProperties == null) |
| ? null |
| : new TreeSet<String>(authToLocalProperties); |
| } |
| |
| /** |
| * Gets the set of <code>auth_to_local</code> property names. |
| * |
| * @return a Set of String values; or null if not set |
| */ |
| public Set<String> getAuthToLocalProperties() { |
| return authToLocalProperties; |
| } |
| |
| /** |
| * Test this AbstractKerberosDescriptor to see if it is a container. |
| * <p/> |
| * This implementation always returns true since it implements a descriptor container. |
| * |
| * @return true if this AbstractKerberosDescriptor is a container, false otherwise |
| */ |
| public boolean isContainer() { |
| return true; |
| } |
| |
| /** |
| * Updates this AbstractKerberosDescriptorContainer using information from the supplied |
| * AbstractKerberosDescriptorContainer. |
| * <p/> |
| * Information from updates will overwrite information in this AbstractKerberosDescriptorContainer. |
| * More specifically, the name of this AbstractKerberosDescriptorContainer may be updated as well |
| * as each individual KerberosIdentityDescriptor and KerberosConfigurationDescriptor contained |
| * within it. Any new KerberosIdentityDescriptors and KerberosConfigurationDescriptors will be |
| * appended to there appropriate lists. |
| * |
| * @param updates an AbstractKerberosDescriptorContainer containing the updates to this |
| * AbstractKerberosDescriptorContainer |
| */ |
| public void update(AbstractKerberosDescriptorContainer updates) { |
| if (updates != null) { |
| String updatedName = updates.getName(); |
| if (updatedName != null) { |
| setName(updatedName); |
| } |
| |
| Map<String, KerberosConfigurationDescriptor> updatedConfigurations = updates.getConfigurations(); |
| if (updatedConfigurations != null) { |
| for (Map.Entry<String, KerberosConfigurationDescriptor> entry : updatedConfigurations.entrySet()) { |
| KerberosConfigurationDescriptor existingConfiguration = getConfiguration(entry.getKey()); |
| |
| // Copy this descriptor so we don't alter the hierarchy of existing data we don't intend to change |
| KerberosConfigurationDescriptor clone = new KerberosConfigurationDescriptor(entry.getValue().toMap()); |
| |
| if (existingConfiguration == null) { |
| putConfiguration(clone); |
| } else { |
| existingConfiguration.update(clone); |
| } |
| } |
| } |
| |
| List<KerberosIdentityDescriptor> updatedIdentities = updates.getIdentities(); |
| if (updatedIdentities != null) { |
| for (KerberosIdentityDescriptor updatedIdentity : updatedIdentities) { |
| KerberosIdentityDescriptor existing = getIdentity(updatedIdentity.getName()); |
| |
| // Copy this descriptor so we don't alter the hierarchy of existing data we don't intend to change |
| KerberosIdentityDescriptor clone = new KerberosIdentityDescriptor(updatedIdentity.toMap()); |
| |
| if (existing == null) { |
| putIdentity(clone); |
| } else { |
| existing.update(clone); |
| } |
| } |
| } |
| |
| Set<String> updatedAuthToLocalProperties = updates.getAuthToLocalProperties(); |
| if (updatedAuthToLocalProperties != null) { |
| for (String updatedAuthToLocalProperty : updatedAuthToLocalProperties) { |
| putAuthToLocalProperty(updatedAuthToLocalProperty); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Attempts to find the KerberosIdentityDescriptor at the specified path. |
| * <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/> |
| * 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 |
| */ |
| protected KerberosIdentityDescriptor getReferencedIdentityDescriptor(String path) |
| throws AmbariException { |
| KerberosIdentityDescriptor identityDescriptor = null; |
| |
| if (path != null) { |
| if (path.startsWith("../")) { |
| // Resolve parent path |
| AbstractKerberosDescriptor parent = getParent(); |
| |
| path = path.substring(2); |
| |
| while (parent != null) { |
| String name = parent.getName(); |
| |
| if (name != null) { |
| path = String.format("/%s", name) + path; |
| } |
| |
| parent = parent.getParent(); |
| } |
| } |
| |
| 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(Type.SERVICE, serviceName); |
| |
| if ((descriptor != null) && (componentName != null) && !componentName.isEmpty()) { |
| descriptor = descriptor.getDescriptor(Type.COMPONENT, componentName); |
| } |
| } |
| |
| if (descriptor != null) { |
| descriptor = descriptor.getDescriptor(Type.IDENTITY, identityName); |
| |
| if (descriptor instanceof KerberosIdentityDescriptor) { |
| identityDescriptor = (KerberosIdentityDescriptor) descriptor; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return identityDescriptor; |
| } |
| |
| /** |
| * Gets the requested AbstractKerberosDescriptor implementation using a type name and a relevant |
| * descriptor name. |
| * <p/> |
| * This implementation handles identity and configuration descriptors within this |
| * AbstractKerberosDescriptorContainer. |
| * <p/> |
| * Implementing classes should override this to handle other types, but call this method to |
| * ensure no data is missed. |
| * |
| * @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(Type type, String name) { |
| if (Type.IDENTITY == type) { |
| return getIdentity(name); |
| } else if (Type.CONFIGURATION == type) { |
| return getConfiguration(name); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Creates a Map of values that can be used to create a copy of this AbstractKerberosDescriptorContainer |
| * or generate the JSON structure described in |
| * {@link org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer} |
| * |
| * @return a Map of values for this AbstractKerberosDescriptorContainer |
| * @see org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer |
| */ |
| @Override |
| public Map<String, Object> toMap() { |
| Map<String, Object> map = super.toMap(); |
| |
| if (identities != null) { |
| // Use a TreeMap to force the identities definitions to be ordered by name, alphebetically. |
| // This helps with readability and comparisons. |
| Map<String, Map<String, Object>> list = new TreeMap<String, Map<String, Object>>(); |
| for (KerberosIdentityDescriptor identity : identities) { |
| list.put(identity.getName(), identity.toMap()); |
| } |
| map.put(Type.IDENTITY.getDescriptorPluralName(), list.values()); |
| } |
| |
| if (configurations != null) { |
| // Use a TreeMap to force the configurations to be ordered by configuration type, alphebetically. |
| // This helps with readability and comparisons. |
| Map<String, Map<String, Object>> list = new TreeMap<String, Map<String, Object>>(); |
| for (KerberosConfigurationDescriptor configuration : configurations.values()) { |
| list.put(configuration.getType(), configuration.toMap()); |
| } |
| map.put(Type.CONFIGURATION.getDescriptorPluralName(), list.values()); |
| } |
| |
| if (authToLocalProperties != null) { |
| map.put(Type.AUTH_TO_LOCAL_PROPERTY.getDescriptorPluralName(), authToLocalProperties); |
| } |
| |
| return map; |
| } |
| |
| @Override |
| public int hashCode() { |
| return super.hashCode() + |
| ((getIdentities() == null) |
| ? 0 |
| : getIdentities().hashCode()) + |
| ((getAuthToLocalProperties() == null) |
| ? 0 |
| : getAuthToLocalProperties().hashCode()) + |
| ((getConfigurations() == null) |
| ? 0 |
| : getConfigurations().hashCode()); |
| } |
| |
| @Override |
| public boolean equals(Object object) { |
| if (object == null) { |
| return false; |
| } else if (object == this) { |
| return true; |
| } else if (object instanceof AbstractKerberosDescriptorContainer) { |
| AbstractKerberosDescriptorContainer descriptor = (AbstractKerberosDescriptorContainer) object; |
| return super.equals(object) && |
| ( |
| (getIdentities() == null) |
| ? (descriptor.getIdentities() == null) |
| : getIdentities().equals(descriptor.getIdentities()) |
| ) && |
| ( |
| (getAuthToLocalProperties() == null) |
| ? (descriptor.getAuthToLocalProperties() == null) |
| : getAuthToLocalProperties().equals(descriptor.getAuthToLocalProperties()) |
| ) && |
| ( |
| (getConfigurations() == null) |
| ? (descriptor.getConfigurations() == null) |
| : getConfigurations().equals(descriptor.getConfigurations()) |
| ); |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Recursively dereference a referenced identity. |
| * <p> |
| * Follows the path of references such that the top-most identity definition (one with no pointer |
| * to a referenced identity) contains the base information which is copied and updated with the |
| * referencing identity's data. The composite identity is then update with the next referencing |
| * identity's data, and so on until the initial identity is encountered. |
| * |
| * @param identity the initial identity to dereference |
| * @return a (disconnected) {@link KerberosIdentityDescriptor} built by traversing the identity |
| * references; or the input identity if it does not reference any other identities. |
| * @throws AmbariException |
| */ |
| private KerberosIdentityDescriptor dereferenceIdentity(KerberosIdentityDescriptor identity) throws AmbariException { |
| KerberosIdentityDescriptor dereferencedIdentity = null; |
| |
| if (identity != null) { |
| KerberosIdentityDescriptor referencedIdentity; |
| try { |
| if (identity.getReference() != null) { |
| referencedIdentity = getReferencedIdentityDescriptor(identity.getReference()); |
| } else { |
| // For backwards compatibility, see if the identity's name indicates a reference... |
| referencedIdentity = getReferencedIdentityDescriptor(identity.getName()); |
| } |
| } catch (AmbariException e) { |
| throw new AmbariException(String.format("Invalid Kerberos identity reference: %s", identity.getReference()), e); |
| } |
| |
| if (referencedIdentity != null) { |
| dereferencedIdentity = dereferenceIdentity(referencedIdentity); // Dereference the "parent"... |
| |
| if (dereferencedIdentity != null) { |
| dereferencedIdentity.update(identity); |
| } else { |
| dereferencedIdentity = new KerberosIdentityDescriptor(referencedIdentity.toMap()); |
| dereferencedIdentity.update(identity); |
| } |
| } else { |
| dereferencedIdentity = new KerberosIdentityDescriptor(identity.toMap()); |
| } |
| } |
| |
| return dereferencedIdentity; |
| } |
| } |