AMBARI-8542. Provide a way to parse and handle Kerberos descriptors
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e7b8383b Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e7b8383b Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e7b8383b Branch: refs/heads/trunk Commit: e7b8383b5af0a39b2530dbfc24563eefd7f781c7 Parents: 1a18dde Author: Robert Levas <[email protected]> Authored: Mon Dec 8 17:44:37 2014 -0500 Committer: John Speidel <[email protected]> Committed: Mon Dec 8 17:45:52 2014 -0500 ---------------------------------------------------------------------- .../kerberos/AbstractKerberosDescriptor.java | 331 ++++++++++ .../AbstractKerberosDescriptorContainer.java | 626 +++++++++++++++++++ .../kerberos/KerberosComponentDescriptor.java | 115 ++++ .../KerberosConfigurationDescriptor.java | 205 ++++++ .../state/kerberos/KerberosDescriptor.java | 351 +++++++++++ .../state/kerberos/KerberosDescriptorType.java | 54 ++ .../kerberos/KerberosIdentityDescriptor.java | 278 ++++++++ .../kerberos/KerberosKeytabDescriptor.java | 445 +++++++++++++ .../kerberos/KerberosPrincipalDescriptor.java | 212 +++++++ .../kerberos/KerberosServiceDescriptor.java | 318 ++++++++++ .../KerberosComponentDescriptorTest.java | 206 ++++++ .../KerberosConfigurationDescriptorTest.java | 242 +++++++ .../state/kerberos/KerberosDescriptorTest.java | 328 ++++++++++ .../KerberosIdentityDescriptorTest.java | 154 +++++ .../kerberos/KerberosKeytabDescriptorTest.java | 146 +++++ .../KerberosPrincipalDescriptorTest.java | 109 ++++ .../kerberos/KerberosServiceDescriptorTest.java | 238 +++++++ 17 files changed, 4358 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptor.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptor.java new file mode 100644 index 0000000..b18e412 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptor.java @@ -0,0 +1,331 @@ +/** + * 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 com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.apache.ambari.server.AmbariException; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * AbstractKerberosDescriptor is the base class for all Kerberos*Descriptor and associated classes. + * <p/> + * It provides storage and management for the parent and name values on behalf of implementing classes. + * It also provides utility and helper methods. + */ +public abstract class AbstractKerberosDescriptor { + + /** + * a regular expression Pattern used to find "variable" placeholders in strings + */ + private static final Pattern PATTERN_VARIABLE = Pattern.compile("\\$\\{(?:(.+?)/)?(.+?)\\}"); + + /** + * An AbstractKerberosDescriptor serving as the parent (or container) for this + * AbstractKerberosDescriptor. + * <p/> + * This value may be null in the event no parent has been identified. + */ + private AbstractKerberosDescriptor parent = null; + + /** + * A String declaring the name of this AbstractKerberosDescriptor. + * <p/> + * This value may be null in the event a name (or identifier) is not relevant. + */ + private String name = null; + + /** + * Performs variable replacement on the supplied String value using values from the replacementsMap. + * <p/> + * The value is a String containing one or more "variables" in the form of ${variable_name}, such + * that "variable_name" may indicate a group identifier; else "" is used as the group. + * For example: + * <p/> + * variable_name: group: ""; property: "variable_name" + * group1/variable_name: group: "group1"; property: "variable_name" + * root/group1/variable_name: Not Supported + * <p/> + * The replacementsMap is a Map of Maps creating a (small) hierarchy of data to traverse in order + * to resolve the variable. + * <p/> + * If a variable resolves to one or more variables, that new variable(s) will be processed and replaced. + * If variable exists after a set number of iterations it is assumed that a cycle has been created + * and the process will abort returning a String in a possibly unexpected state. + * + * @param value a String containing zero or more variables to be replaced + * @param replacementsMap a Map of data used to perform the variable replacements + * @return a new String + */ + public static String replaceVariables(String value, Map<String, Map<String, String>> replacementsMap) throws AmbariException { + if ((value != null) && (replacementsMap != null) && !replacementsMap.isEmpty()) { + int count = 0; // Used to help prevent an infinite loop... + boolean replacementPerformed; + + do { + if (++count > 1000) { + throw new AmbariException(String.format("Circular reference found while replacing variables in %s", value)); + } + + Matcher matcher = PATTERN_VARIABLE.matcher(value); + StringBuffer sb = new StringBuffer(); + + replacementPerformed = false; + + while (matcher.find()) { + String type = matcher.group(1); + String name = matcher.group(2); + Map<String, String> replacements; + + if ((name != null) && !name.isEmpty()) { + if (type == null) { + replacements = replacementsMap.get(""); + } else { + replacements = replacementsMap.get(type); + } + + if (replacements != null) { + String replacement = replacements.get(name); + + if (replacement != null) { + // Escape '$' and '\' so they don't cause any issues. + matcher.appendReplacement(sb, replacement.replace("\\", "\\\\").replace("$", "\\$")); + replacementPerformed = true; + } + } + } + } + + matcher.appendTail(sb); + value = sb.toString(); + } + while (replacementPerformed); // Process the string again to make sure new variables were not introduced + } + + return value; + } + + /** + * Generates a Map of data that represents this AbstractKerberosDescriptor implementation. + * <p/> + * This method should be overwritten by AbstractKerberosDescriptor implementations to generate a + * Map of data specific to it. + * <p/> + * It is not necessary to call this method from the overriding method. + * <p/> + * The map of data generated should be setup so that it can be fed back into the implementation + * class to build a copy of it. For example: + * <p/> + * <pre> + * descriptor1 = AbstractKerberosDescriptorImpl(...) + * map = descriptor1.toMap() + * descriptor2 = AbstractKerberosDescriptor(map) + * descriptor1 should have the same data as descriptor2 + * </pre> + * + * @return a Map of date representing this AbstractKerberosDescriptor implementation + */ + public Map<String, Object> toMap() { + HashMap<String, Object> dataMap = new HashMap<String, Object>(); + String name = getName(); + + if (name != null) { + dataMap.put("name", name); + } + + return dataMap; + } + + /** + * Returns the name of this descriptor + * + * @return a String indicating the name of this descriptor + */ + public String getName() { + return name; + } + + /** + * Sets the name of this descriptor + * + * @param name a String indicating the name of this descriptor + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the parent (or container) of this descriptor + * + * @return an AbstractKerberosDescriptor representing the parent (or container) of this descriptor + * or null if no parent was set + */ + public AbstractKerberosDescriptor getParent() { + return parent; + } + + /** + * Sets the parent (or container) of this descriptor + * + * @param parent an AbstractKerberosDescriptor representing the parent (or container) of this + * descriptor or null to clear the value + */ + public void setParent(AbstractKerberosDescriptor parent) { + this.parent = parent; + } + + /** + * Test this AbstractKerberosDescriptor to see if it is a container. + * <p/> + * The default implementation always returns false. Implementing classes should override this + * method to return something different, like true. + * + * @return true if this AbstractKerberosDescriptor is a container, false otherwise + */ + public boolean isContainer() { + return false; + } + + /** + * Parses a file containing JSON-formatted text into a (generic) Map. + * + * @param file a File containing the JSON-formatted text to parse + * @return a Map of the data + * @throws FileNotFoundException if the specified File does not point to a valid file + * @throws IOException if the specified File is not a readable file + */ + protected static Map<String, Object> parseFile(File file) throws IOException { + if (file == null) { + return Collections.emptyMap(); + } else if (!file.isFile() || !file.canRead()) { + throw new IOException(String.format("%s is not a readable file", file.getAbsolutePath())); + } else { + return new Gson().fromJson(new FileReader(file), + new TypeToken<Map<String, Object>>() { + }.getType()); + } + } + + /** + * Parses a JSON-formatted String into a (generic) Map. + * + * @param json a String containing the JSON-formatted text to parse + * @return a Map of the data + */ + protected static Map<String, Object> parseJSON(String json) { + if ((json == null) || json.isEmpty()) { + return Collections.emptyMap(); + } else { + return new Gson().fromJson(json, + new TypeToken<Map<String, Object>>() { + }.getType()); + } + } + + /** + * Safely retrieves the requested value from the supplied Map + * + * @param map a Map containing the relevant data + * @param key a String declaring the item to retrieve + * @return an Object representing the requested data; or null if not found + */ + protected static Object getValue(Map<?, ?> map, String key) { + return ((map == null) || key == null) ? null : map.get(key); + } + + /** + * Safely retrieves the requested value (converted to a String) from the supplied Map + * <p/> + * The found value will be converted to a String using the {@link Object#toString()} method. + * + * @param map a Map containing the relevant data + * @param key a String declaring the item to retrieve + * @return a String representing the requested data; or null if not found + */ + protected static String getStringValue(Map<?, ?> map, String key) { + Object value = getValue(map, key); + return (value == null) ? null : value.toString(); + } + + /** + * Gets the requested AbstractKerberosDescriptor implementation using a type name and a relevant + * descriptor name. + * <p/> + * Implementation classes should override this method to handle relevant descriptor 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 + */ + protected AbstractKerberosDescriptor getDescriptor(KerberosDescriptorType type, String name) { + return null; + } + + /** + * Traverses up the hierarchy to find the "root" or "parent" container. + * <p/> + * The root AbstractKerberosDescriptor is the first descriptor in the hierarchy with a null parent. + * + * @return the AbstractKerberosDescriptor implementation that is found to be the root of the hierarchy. + */ + protected AbstractKerberosDescriptor getRoot() { + AbstractKerberosDescriptor root = this; + + while (root.getParent() != null) { + root = root.getParent(); + } + + return root; + } + + @Override + public int hashCode() { + return 37 * + ((getName() == null) + ? 0 + : getName().hashCode()); + } + + @Override + public boolean equals(Object object) { + if (object == null) { + return false; + } else if (object == this) { + return true; + } else if (object instanceof AbstractKerberosDescriptor) { + AbstractKerberosDescriptor descriptor = (AbstractKerberosDescriptor) object; + return ( + (getName() == null) + ? (descriptor.getName() == null) + : getName().equals(descriptor.getName()) + ); + } else { + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/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 new file mode 100644 index 0000000..c6389cb --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java @@ -0,0 +1,626 @@ +/* + * 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.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * 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}" + * } + * } + * } + * } + * </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 { + + /** + * 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; + + /** + * 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(KerberosDescriptorType.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(KerberosDescriptorType.CONFIGURATION.getDescriptorPluralName()); + if (list instanceof Collection) { + for (Object item : (Collection) list) { + if (item instanceof Map) { + putConfiguration(new KerberosConfigurationDescriptor((Map<?, ?>) item)); + } + } + } + } + } + + /** + * 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)} and setting the argument to + * 'false' + * + * @return the relevant List of KerberosIdentityDescriptors + */ + public List<KerberosIdentityDescriptor> getIdentities() { + try { + return getIdentities(false); + } 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; + } + } + + /** + * 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) throws AmbariException { + if (resolveReferences) { + if (identities == null) { + return Collections.emptyList(); + } else { + List<KerberosIdentityDescriptor> list = new ArrayList<KerberosIdentityDescriptor>(); + + // For each KerberosIdentityDescriptor, copy it and then attempt to find the referenced + // KerberosIdentityDescriptor. + // * If a reference is found, copy that, update it with the initial KerberosIdentityDescriptor + // and then add it to the list. + // * If a reference is not found, simply add the initial KerberosIdentityDescriptor to the list + for (KerberosIdentityDescriptor identity : identities) { + KerberosIdentityDescriptor referencedIdentity; + try { + referencedIdentity = getReferencedIdentityDescriptor(identity.getName()); + } catch (AmbariException e) { + throw new AmbariException(String.format("Invalid Kerberos identity reference: %s", identity.getName()), e); + } + + // Detach this identity from the tree... + identity = new KerberosIdentityDescriptor(identity.toMap()); + + if (referencedIdentity != null) { + KerberosIdentityDescriptor detachedIdentity = new KerberosIdentityDescriptor(referencedIdentity.toMap()); + detachedIdentity.update(identity); + list.add(detachedIdentity); + } else { + list.add(identity); + } + } + + return list; + } + } else { + return identities; + } + } + + /** + * 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(); + } + } + } + } + + /** + * 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 HashMap<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 HashMap<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); + } + + /** + * 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); + } + } + } + } + } + + /** + * 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: + * <p/> + * /identity + * /service/identity + * /service/component/identity + * + * @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) && 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 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)); + } + + 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) && (componentName != null) && !componentName.isEmpty()) { + descriptor = descriptor.getDescriptor(KerberosDescriptorType.COMPONENT, componentName); + } + } + + if (descriptor != null) { + descriptor = descriptor.getDescriptor(KerberosDescriptorType.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(KerberosDescriptorType type, String name) { + if (KerberosDescriptorType.IDENTITY == type) { + return getIdentity(name); + } else if (KerberosDescriptorType.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) { + List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); + for (KerberosIdentityDescriptor identity : identities) { + list.add(identity.toMap()); + } + map.put(KerberosDescriptorType.IDENTITY.getDescriptorPluralName(), list); + } + + if (configurations != null) { + List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); + for (KerberosConfigurationDescriptor configuration : configurations.values()) { + list.add(configuration.toMap()); + } + map.put(KerberosDescriptorType.CONFIGURATION.getDescriptorPluralName(), list); + } + + return map; + } + + @Override + public int hashCode() { + return super.hashCode() + + ((getIdentities() == null) + ? 0 + : getIdentities().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()) + ) && + ( + (getConfigurations() == null) + ? (descriptor.getConfigurations() == null) + : getConfigurations().equals(descriptor.getConfigurations()) + ); + } else { + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java new file mode 100644 index 0000000..19d3f5e --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java @@ -0,0 +1,115 @@ +/** + * 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 java.util.Map; + +/** + * KerberosComponentDescriptor implements AbstractKerberosDescriptorContainer. It contains the data + * related to a component which include the following properties: + * <ul> + * <li>name</li> + * <li>identities</li> + * <li>configurations</li> + * </ul> + * Example: + * <pre> + * { + * "name" : "COMPONENT_NAME", + * "identities" : { ... }, + * "configurations" : { ... } + * } + * </pre> + */ + +/** + * KerberosComponentDescriptor is an implementation of an AbstractKerberosDescriptorContainer that + * encapsulates the details about an Ambari component. + * <p/> + * A KerberosComponentDescriptor has the following properties: + * <ul> + * <li>identities ({@link org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer})</li> + * <li>configurations ({@link org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer})</li> + * </ul> + * <p/> + * The following (pseudo) JSON Schema will yield a valid KerberosComponentDescriptor + * <pre> + * { + * "$schema": "http://json-schema.org/draft-04/schema#", + * "title": "KerberosComponentDescriptor", + * "description": "Describes an Ambari component", + * "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}" + * } + * } + * } + * } + * </pre> + * <p/> + * In this implementation, + * {@link org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor#name} will hold the + * KerberosComponentDescriptor#name value + */ +public class KerberosComponentDescriptor extends AbstractKerberosDescriptorContainer { + + /** + * Creates a new KerberosComponentDescriptor + * <p/> + * See {@link org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor} for an + * example JSON structure that may be used to generate this map. + * + * @param data a Map of values use to populate the data for the new instance + * @see org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor + */ + public KerberosComponentDescriptor(Map<?, ?> data) { + super(data); + + // The name for this KerberosComponentDescriptor is stored in the "name" entry in the map + // This is not automatically set by the super classes. + setName(getStringValue(data, "name")); + } + + @Override + public int hashCode() { + return 35 * super.hashCode(); + } + + @Override + public boolean equals(Object object) { + return (object == this) || + ( + (object != null) && + (object.getClass() == KerberosComponentDescriptor.class) && + super.equals(object) + ); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosConfigurationDescriptor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosConfigurationDescriptor.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosConfigurationDescriptor.java new file mode 100644 index 0000000..1e33e68 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosConfigurationDescriptor.java @@ -0,0 +1,205 @@ +/** + * 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 java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * KerberosConfigurationDescriptor is an implementation of an AbstractKerberosDescriptor that + * encapsulates data related to an Ambari configuration block. + * <p/> + * A KerberosConfigurationDescriptor has the following properties: + * <ul> + * <li>type</li> + * <li>properties</li> + * </ul> + * <p/> + * The following is an example of a JSON structure that will yield a valid KerberosConfigurationDescriptor + * <pre> + * { + * "core-site": { + * "hadoop.security.authentication": "kerberos", + * "hadoop.rpc.protection": "authentication; integrity; privacy", + * "hadoop.security.authorization": "true" + * } + * } + * </pre> + * <p/> + * In this implementation, + * {@link org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor#name} will hold the + * KerberosConfigurationDescriptor#type value + */ +public class KerberosConfigurationDescriptor extends AbstractKerberosDescriptor { + /** + * A Map of the properties in this KerberosConfigurationDescriptor + */ + private Map<String, String> properties = null; + + /** + * Creates a new KerberosConfigurationDescriptor + * <p/> + * See {@link org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor} for an + * example JSON structure that may be used to generate this map. + * + * @param data a Map of values use to populate the data for the new instance + * @see org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor + */ + public KerberosConfigurationDescriptor(Map<?, ?> data) { + + if (data != null) { + Set<?> keySet = data.keySet(); + + // Only a single entry is expected... + Object key = keySet.iterator().next(); + if (key != null) { + Object object = data.get(key); + + setType(key.toString()); + + if (object instanceof Map) { + for (Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) { + Object value = entry.getValue(); + putProperty(entry.getKey().toString(), (value == null) ? null : value.toString()); + } + } + } + } + } + + /** + * Returns the type of the configuration data represented by this KerberosConfigurationDescriptor + * + * @return a String declaring the configuration type, i.e., core-site + */ + public String getType() { + return getName(); + } + + /** + * Sets the type of the configuration data represented by this KerberosConfigurationDescriptor + * + * @param type a String declaring the configuration group type, i.e., core-site + */ + public void setType(String type) { + setName(type); + } + + /** + * Gets the properties of the configuration data represented by this KerberosConfigurationDescriptor + * + * @return a Map of properties + */ + public Map<String, String> getProperties() { + return properties; + } + + /** + * Gets the value of the configuration property with the specified name + * + * @param name a String declaring the name of the property to retrieve + * @return a String or null if the property value is not found + */ + public String getProperty(String name) { + return ((name == null) || (properties == null)) ? null : properties.get(name); + } + + /** + * Adds or updates the value of the configuration property with the specified name + * <p/> + * If the property exists, it will be overwritten; else a new entry will be created. + * + * @param name a String declaring the name of the property to set + * @param value a String containing the value of the property + */ + public void putProperty(String name, String value) { + if (name == null) { + throw new IllegalArgumentException("The property name must not be null"); + } + + if (properties == null) { + properties = new HashMap<String, String>(); + } + + properties.put(name, value); + } + + /** + * Updates this KerberosConfigurationDescriptor with data from another KerberosConfigurationDescriptor + * <p/> + * Properties will be updated if the relevant updated values are not null. + * + * @param updates the KerberosConfigurationDescriptor containing the updated values + */ + public void update(KerberosConfigurationDescriptor updates) { + if (updates != null) { + setType(updates.getType()); + + Map<String, String> updatedProperties = updates.getProperties(); + if (updatedProperties != null) { + for (Map.Entry<String, String> entry : updatedProperties.entrySet()) { + putProperty(entry.getKey(), entry.getValue()); + } + } + } + } + + /** + * Creates a Map of values that can be used to create a copy of this KerberosConfigurationDescriptor + * or generate the JSON structure described in + * {@link org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor} + * + * @return a Map of values for this KerberosConfigurationDescriptor + * @see org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor + */ + @Override + public Map<String, Object> toMap() { + Map<String, Object> map = new HashMap<String, Object>(); + map.put(getName(), (properties == null) ? null : new HashMap<String, Object>(properties)); + return map; + } + + @Override + public int hashCode() { + return super.hashCode() + + ((getProperties() == null) + ? 0 + : getProperties().hashCode()); + } + + @Override + public boolean equals(Object object) { + if (object == null) { + return false; + } else if (object == this) { + return true; + } else if (object.getClass() == KerberosConfigurationDescriptor.class) { + KerberosConfigurationDescriptor descriptor = (KerberosConfigurationDescriptor) object; + return super.equals(object) && + ( + (getProperties() == null) + ? (descriptor.getProperties() == null) + : getProperties().equals(descriptor.getProperties()) + ); + } else { + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/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 new file mode 100644 index 0000000..14ba19d --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java @@ -0,0 +1,351 @@ +/** + * 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 java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * KerberosDescriptor is an implementation of an AbstractKerberosDescriptorContainer that + * encapsulates an entire Kerberos descriptor hierarchy. + * <p/> + * A KerberosDescriptor has the following properties: + * <ul> + * <li>services</li> + * <li>properties</li> + * <li>identities ({@link org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer})</li> + * <li>configurations ({@link org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer})</li> + * </ul> + * <p/> + * The following (pseudo) JSON Schema will yield a valid KerberosDescriptor + * <pre> + * { + * "$schema": "http://json-schema.org/draft-04/schema#", + * "title": "KerberosDescriptor", + * "description": "Describes a Kerberos descriptor", + * "type": "object", + * "properties": { + * "services": { + * "description": "A list of service descriptors", + * "type": "array", + * "items": { + * "title": "KerberosServiceDescriptor" + * "type": "{@link org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor}" + * } + * }, + * "properties": { + * "description": "Global properties that can be used in variable replacements", + * "type": "object", + * "patternProperties": { + * "^[\w\.\_]?$": {}" + * } + * } + * "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}" + * } + * } + * } + * } + * </pre> + * <p/> + * In this implementation, + * {@link org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor#name} will be null + */ +public class KerberosDescriptor extends AbstractKerberosDescriptorContainer { + + /** + * A Map of the "global" properties contained within this KerberosDescriptor + */ + private Map<String, String> properties = null; + + /** + * A Map of the services contained within this KerberosDescriptor + */ + private Map<String, KerberosServiceDescriptor> services = null; + + + /** + * Given a file containing JSON-formatted text, attempts to create a KerberosDescriptor + * + * @param file a File pointing to the file containing JSON-formatted text + * @return a newly created KerberosDescriptor + * @throws FileNotFoundException if the file does not point to a readable file + */ + public static KerberosDescriptor fromFile(File file) throws IOException { + return new KerberosDescriptor(parseFile(file)); + } + + /** + * Given a String containing JSON-formatted text, attempts to create a KerberosDescriptor + * + * @param json a File pointing to the file containing JSON-formatted text + * @return a newly created KerberosDescriptor + */ + public static KerberosDescriptor fromJSON(String json) { + return new KerberosDescriptor(parseJSON(json)); + } + + /** + * Creates an empty KerberosDescriptor + */ + public KerberosDescriptor() { + this(null); + } + + /** + * Creates a new KerberosDescriptor + * <p/> + * See {@link org.apache.ambari.server.state.kerberos.KerberosDescriptor} for the JSON + * Schema that may be used to generate this map. + * + * @param data a Map of values use to populate the data for the new instance + * @see org.apache.ambari.server.state.kerberos.KerberosDescriptor + */ + public KerberosDescriptor(Map<?, ?> data) { + super(data); + + if (data != null) { + Object list = data.get(KerberosDescriptorType.SERVICE.getDescriptorPluralName()); + if (list instanceof Collection) { + for (Object item : (Collection) list) { + if (item instanceof Map) { + putService(new KerberosServiceDescriptor((Map<?, ?>) item)); + } + } + } + + Object map = data.get("properties"); + if (map instanceof Map) { + for (Map.Entry<?, ?> entry : ((Map<?, ?>) map).entrySet()) { + Object value = entry.getValue(); + putProperty(entry.getKey().toString(), (value == null) ? null : value.toString()); + } + } + } + } + + /** + * Returns a Map of the KerberosServiceDescriptors in this KerberosDescriptor + * + * @return a Map of String to KerberosServiceDescriptor + */ + public Map<String, KerberosServiceDescriptor> getServices() { + return services; + } + + /** + * Gets the KerberosServiceDescriptor with the specified name + * + * @param name a String declaring the name of the KerberosServiceDescriptor to retrieve + * @return the requested KerberosServiceDescriptor or null if not found + */ + public KerberosServiceDescriptor getService(String name) { + return ((name == null) || (services == null)) ? null : services.get(name); + } + + /** + * Adds or replaces a KerberosServiceDescriptor + * <p/> + * If a KerberosServiceDescriptor with the same name already exists in the services Map, it will + * be replaced; else a new entry will be made. + * + * @param service the KerberosServiceDescriptor to put + */ + public void putService(KerberosServiceDescriptor service) { + if (service != null) { + String name = service.getName(); + + if (name == null) { + throw new IllegalArgumentException("The service name must not be null"); + } + + if (services == null) { + services = new HashMap<String, KerberosServiceDescriptor>(); + } + + services.put(name, service); + + // Set the service's parent to this KerberosDescriptor + service.setParent(this); + } + } + + /** + * Gets the Map of properties for this KerberosDescriptor + * + * @return a Map of String to String values + */ + public Map<String, String> getProperties() { + return properties; + } + + /** + * Gets the value of the property with the specified name + * + * @param name a String declaring the name of the property to retrieve + * @return a String or null if the property was not found + */ + public String getProperty(String name) { + return ((name == null) || (properties == null)) ? null : properties.get(name); + } + + /** + * Adds or updates a property value + * <p/> + * If a property exists with the specified name, replaces its value; else adds a new entry. + * + * @param name a String declaring the name of the property to put + * @param value a String containing the value of the property to put + */ + public void putProperty(String name, String value) { + if (name == null) { + throw new IllegalArgumentException("The property name must not be null"); + } + + if (properties == null) { + properties = new HashMap<String, String>(); + } + + properties.put(name, value); + } + + /** + * Updates this KerberosDescriptor with data from another KerberosDescriptor + * <p/> + * Properties will be updated if the relevant updated values are not null. + * + * @param updates the KerberosDescriptor containing the updated values + */ + public void update(KerberosDescriptor updates) { + if (updates != null) { + Map<String, KerberosServiceDescriptor> updatedServiceDescriptors = updates.getServices(); + if (updatedServiceDescriptors != null) { + for (Map.Entry<String, KerberosServiceDescriptor> entry : updatedServiceDescriptors.entrySet()) { + KerberosServiceDescriptor existing = getService(entry.getKey()); + if (existing == null) { + putService(entry.getValue()); + } else { + existing.update(entry.getValue()); + } + } + } + + Map<String, String> updatedProperties = updates.getProperties(); + if (updatedProperties != null) { + for (Map.Entry<String, String> entry : updatedProperties.entrySet()) { + putProperty(entry.getKey(), entry.getValue()); + } + } + } + + super.update(updates); + } + + /** + * 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} + * + * @return a Map of values for this KerberosDescriptor + * @see org.apache.ambari.server.state.kerberos.KerberosDescriptor + */ + @Override + public Map<String, Object> toMap() { + Map<String, Object> map = super.toMap(); + + if (services != null) { + List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); + for (KerberosServiceDescriptor service : services.values()) { + list.add(service.toMap()); + } + map.put(KerberosDescriptorType.SERVICE.getDescriptorPluralName(), list); + } + + if (properties != null) { + map.put("properties", new HashMap<String, String>(properties)); + } + + return map; + } + + /** + * Sets the parent (or container) of this descriptor + * <p/> + * This implementation prevents the parent from being externally set by always throwing an + * UnsupportedOperationException. + * + * @param parent an AbstractKerberosDescriptor representing the parent (or container) of this + */ + @Override + public void setParent(AbstractKerberosDescriptor parent) { + throw new UnsupportedOperationException("This KerberosDescriptor may not have a parent assigned to it."); + } + + @Override + public int hashCode() { + return super.hashCode() + + ((getProperties() == null) + ? 0 + : getProperties().hashCode()) + + ((getServices() == null) + ? 0 + : getServices().hashCode()); + } + + @Override + public boolean equals(Object object) { + if (object == null) { + return false; + } else if (object == this) { + return true; + } else if (object.getClass() == KerberosDescriptor.class) { + KerberosDescriptor descriptor = (KerberosDescriptor) object; + return super.equals(object) && + ( + (getProperties() == null) + ? (descriptor.getProperties() == null) + : getProperties().equals(descriptor.getProperties()) + ) && + ( + (getServices() == null) + ? (descriptor.getServices() == null) + : getServices().equals(descriptor.getServices()) + ); + } else { + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorType.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorType.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorType.java new file mode 100644 index 0000000..94c5046 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorType.java @@ -0,0 +1,54 @@ +/* + * 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; + +public enum KerberosDescriptorType { + SERVICE("service", "services"), + COMPONENT("component", "components"), + IDENTITY("identity", "identities"), + PRINCIPAL("principal", "principals"), + KEYTAB("keytab", "keytabs"), + CONFIGURATION("configuration", "configurations"); + + private final String descriptorName; + private final String descriptorPluralName; + + private KerberosDescriptorType(String descriptorName, String descriptorPluralName) { + this.descriptorName = descriptorName; + this.descriptorPluralName = descriptorPluralName; + } + + /** + * Gets the identifying name for this KerberosDescriptorType + * + * @return a String declaring the identifying name for this KerberosDescriptorType + */ + public String getDescriptorName() { + return descriptorName; + } + + /** + * Gets the identifying name for a group of this KerberosDescriptorType + * + * @return a String declaring the identifying name for a group of this KerberosDescriptorType + */ + public String getDescriptorPluralName() { + return descriptorPluralName; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java new file mode 100644 index 0000000..2e5a27d --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java @@ -0,0 +1,278 @@ +/** + * 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 java.util.Map; + +/** + * KerberosIdentityDescriptor is an implementation of an AbstractKerberosDescriptor that + * encapsulates data related to a Kerberos identity - including its principal and keytab file details. + * <p/> + * A KerberosIdentityDescriptor has the following properties: + * <ul> + * <li>name</li> + * <li>principal</li> + * <li>keytab</li> + * <li>password</li> + * </ul> + * <p/> + * The following (pseudo) JSON Schema will yield a valid KerberosIdentityDescriptor + * <pre> + * { + * "$schema": "http://json-schema.org/draft-04/schema#", + * "title": "KerberosIdentityDescriptor", + * "description": "Describes a Kerberos identity", + * "type": "object", + * "properties": { + * "name": { + * "description": "An identifying name for this identity. The name may reference another + * KerberosIdentityDescriptor by declaring the path to it", + * "type": "string" + * }, + * "principal": { + * "description": "Details about this identity's principal", + * "type": "{@link org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor}", + * } + * "keytab": { + * "description": "Details about this identity's keytab", + * "type": "{@link org.apache.ambari.server.state.kerberos.KerberosKeytabDescriptor}", + * } + * } + * "password": { + * "description": "The password to use for this identity. If not set a secure random + * password will automatically be generated", + * "type": "string" + * } + * } + * } + * </pre> + * <p/> + * In this implementation, + * {@link org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor#name} will hold the + * KerberosIdentityDescriptor#name value + */ +public class KerberosIdentityDescriptor extends AbstractKerberosDescriptor { + + /** + * The KerberosPrincipalDescriptor containing the principal details for this Kerberos identity + */ + private KerberosPrincipalDescriptor principal = null; + + /** + * The KerberosKeytabDescriptor containing the keytab details for this Kerberos identity + */ + private KerberosKeytabDescriptor keytab = null; + + /** + * A String containing the password for this Kerberos identity + * <p/> + * If this value is null or empty, a random password will be generated as necessary. + */ + private String password = null; + + /** + * Creates a new KerberosIdentityDescriptor + * <p/> + * See {@link org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor} for the JSON + * Schema that may be used to generate this map. + * + * @param data a Map of values use to populate the data for the new instance + * @see org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor + */ + public KerberosIdentityDescriptor(Map<?, ?> data) { + // The name for this KerberosIdentityDescriptor is stored in the "name" entry in the map + // This is not automatically set by the super classes. + setName(getStringValue(data, "name")); + + if (data != null) { + Object item; + + setPassword(getStringValue(data, "password")); + + item = data.get(KerberosDescriptorType.PRINCIPAL.getDescriptorName()); + if (item instanceof Map) { + setPrincipalDescriptor(new KerberosPrincipalDescriptor((Map<?, ?>) item)); + } + + item = data.get(KerberosDescriptorType.KEYTAB.getDescriptorName()); + if (item instanceof Map) { + setKeytabDescriptor(new KerberosKeytabDescriptor((Map<?, ?>) item)); + } + } + } + + /** + * Gets the KerberosPrincipalDescriptor related to this KerberosIdentityDescriptor + * + * @return the KerberosPrincipalDescriptor related to this KerberosIdentityDescriptor + */ + public KerberosPrincipalDescriptor getPrincipalDescriptor() { + return principal; + } + + /** + * Sets the KerberosPrincipalDescriptor related to this KerberosIdentityDescriptor + * + * @param principal the KerberosPrincipalDescriptor related to this KerberosIdentityDescriptor + */ + public void setPrincipalDescriptor(KerberosPrincipalDescriptor principal) { + this.principal = principal; + + if (this.principal != null) { + this.principal.setParent(this); + } + } + + /** + * Gets the KerberosKeytabDescriptor related to this KerberosIdentityDescriptor + * + * @return the KerberosKeytabDescriptor related to this KerberosIdentityDescriptor + */ + public KerberosKeytabDescriptor getKeytabDescriptor() { + return keytab; + } + + /** + * Sets the KerberosKeytabDescriptor related to this KerberosIdentityDescriptor + * + * @param keytab the KerberosKeytabDescriptor related to this KerberosIdentityDescriptor + */ + public void setKeytabDescriptor(KerberosKeytabDescriptor keytab) { + this.keytab = keytab; + + if (this.keytab != null) { + this.keytab.setParent(this); + } + } + + /** + * Gets the password for this this KerberosIdentityDescriptor + * + * @return A String containing the password for this this KerberosIdentityDescriptor + * @see #password + */ + public String getPassword() { + return password; + } + + /** + * Sets the password for this this KerberosIdentityDescriptor + * + * @param password A String containing the password for this this KerberosIdentityDescriptor + * @see #password + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * Updates this KerberosIdentityDescriptor with data from another KerberosIdentityDescriptor + * <p/> + * Properties will be updated if the relevant updated values are not null. + * + * @param updates the KerberosIdentityDescriptor containing the updated values + */ + public void update(KerberosIdentityDescriptor updates) { + if (updates != null) { + setName(updates.getName()); + + setPassword(updates.getPassword()); + + KerberosPrincipalDescriptor existingPrincipal = getPrincipalDescriptor(); + if (existingPrincipal == null) { + setPrincipalDescriptor(updates.getPrincipalDescriptor()); + } else { + existingPrincipal.update(updates.getPrincipalDescriptor()); + } + + KerberosKeytabDescriptor existingKeytabDescriptor = getKeytabDescriptor(); + if (existingKeytabDescriptor == null) { + setKeytabDescriptor(updates.getKeytabDescriptor()); + } else { + existingKeytabDescriptor.update(updates.getKeytabDescriptor()); + } + } + } + + /** + * Creates a Map of values that can be used to create a copy of this KerberosIdentityDescriptor + * or generate the JSON structure described in + * {@link org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor} + * + * @return a Map of values for this KerberosIdentityDescriptor + * @see org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor + */ + @Override + public Map<String, Object> toMap() { + Map<String, Object> dataMap = super.toMap(); + + if (principal != null) { + dataMap.put(KerberosDescriptorType.PRINCIPAL.getDescriptorName(), principal.toMap()); + } + + if (keytab != null) { + dataMap.put(KerberosDescriptorType.KEYTAB.getDescriptorName(), keytab.toMap()); + } + + if (password != null) { + dataMap.put("password", password); + } + + return dataMap; + } + + @Override + public int hashCode() { + return super.hashCode() + + ((getPrincipalDescriptor() == null) + ? 0 + : getPrincipalDescriptor().hashCode()) + + ((getKeytabDescriptor() == null) + ? 0 + : getKeytabDescriptor().hashCode()); + } + + @Override + public boolean equals(Object object) { + if (object == null) { + return false; + } else if (object == this) { + return true; + } else if (object.getClass() == KerberosIdentityDescriptor.class) { + KerberosIdentityDescriptor descriptor = (KerberosIdentityDescriptor) object; + return super.equals(object) && + ( + (getPrincipalDescriptor() == null) + ? (descriptor.getPrincipalDescriptor() == null) + : getPrincipalDescriptor().equals(descriptor.getPrincipalDescriptor()) + ) && + ( + (getKeytabDescriptor() == null) + ? (descriptor.getKeytabDescriptor() == null) + : getKeytabDescriptor().equals(descriptor.getKeytabDescriptor()) + ) && + ( + (getPassword() == null) + ? (descriptor.getPassword() == null) + : getPassword().equals(descriptor.getPassword()) + ); + } else { + return false; + } + } +}
