/*
* 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.controller.internal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.KerberosHelper;
import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
import org.apache.ambari.server.controller.spi.NoSuchResourceException;
import org.apache.ambari.server.controller.spi.Predicate;
import org.apache.ambari.server.controller.spi.Request;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.Resource.Type;
import org.apache.ambari.server.controller.spi.SystemException;
import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
import org.apache.ambari.server.controller.utilities.PropertyHelper;
import org.apache.ambari.server.security.authorization.AuthorizationHelper;
import org.apache.ambari.server.security.authorization.ResourceType;
import org.apache.ambari.server.security.authorization.RoleAuthorization;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
import org.apache.commons.lang.StringUtils;
/**
* A read-only resource provider to get a the Kerberos Descriptor relevant to the cluster.
* <p/>
* The following types are available
* <ul>
* <li>STACK - the default descriptor for the relevant stack</li>
* <li>USER - the user-supplied updates to be applied to the default descriptor</li>
* <li>COMPOSITE - the default descriptor for the relevant stack with the the user-supplied updates applied</li>
* </ul>
*/
public class ClusterKerberosDescriptorResourceProvider extends ReadOnlyResourceProvider {
/**
* A directive to indicate whether or not the <code>when</code> clauses for Kerberos Identity
* definitions are to be evaluated or not. By default, they are not evaluated since historically
* this provider returned unprocessed Kerberos descriptors.
*/
public static final String DIRECTIVE_EVALUATE_WHEN_CLAUSE = "evaluate_when";
/**
* A directive to indicate additional services to consider when evaluating <code>when</code>
* clauses. This value is expcected to be a comma-delimited string that is only used when
* <code>evaluate_when</code> is <code>true</code>.
*/
public static final String DIRECTIVE_ADDITIONAL_SERVICES = "additional_services";
// ----- Property ID constants ---------------------------------------------
public static final String CLUSTER_KERBEROS_DESCRIPTOR_CLUSTER_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("KerberosDescriptor", "cluster_name");
public static final String CLUSTER_KERBEROS_DESCRIPTOR_TYPE_PROPERTY_ID = PropertyHelper.getPropertyId("KerberosDescriptor", "type");
public static final String CLUSTER_KERBEROS_DESCRIPTOR_DESCRIPTOR_PROPERTY_ID = PropertyHelper.getPropertyId("KerberosDescriptor", "kerberos_descriptor");
private static final Set<String> PK_PROPERTY_IDS;
private static final Set<String> PROPERTY_IDS;
private static final Map<Type, String> KEY_PROPERTY_IDS;
private static final Set<RoleAuthorization> REQUIRED_GET_AUTHORIZATIONS = EnumSet.of(RoleAuthorization.CLUSTER_TOGGLE_KERBEROS,
RoleAuthorization.CLUSTER_VIEW_CONFIGS,
RoleAuthorization.HOST_VIEW_CONFIGS,
RoleAuthorization.SERVICE_VIEW_CONFIGS);
static {
Set<String> set;
set = new HashSet<>();
set.add(CLUSTER_KERBEROS_DESCRIPTOR_CLUSTER_NAME_PROPERTY_ID);
set.add(CLUSTER_KERBEROS_DESCRIPTOR_TYPE_PROPERTY_ID);
PK_PROPERTY_IDS = Collections.unmodifiableSet(set);
set = new HashSet<>();
set.add(CLUSTER_KERBEROS_DESCRIPTOR_CLUSTER_NAME_PROPERTY_ID);
set.add(CLUSTER_KERBEROS_DESCRIPTOR_TYPE_PROPERTY_ID);
set.add(CLUSTER_KERBEROS_DESCRIPTOR_DESCRIPTOR_PROPERTY_ID);
PROPERTY_IDS = Collections.unmodifiableSet(set);
HashMap<Type, String> map = new HashMap<>();
map.put(Type.Cluster, CLUSTER_KERBEROS_DESCRIPTOR_CLUSTER_NAME_PROPERTY_ID);
map.put(Type.ClusterKerberosDescriptor, CLUSTER_KERBEROS_DESCRIPTOR_TYPE_PROPERTY_ID);
KEY_PROPERTY_IDS = Collections.unmodifiableMap(map);
}
/**
* Create a new resource provider.
*/
public ClusterKerberosDescriptorResourceProvider(AmbariManagementController managementController) {
super(PROPERTY_IDS, KEY_PROPERTY_IDS, managementController);
}
@Override
public Set<Resource> getResources(Request request, Predicate predicate)
throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
// Ensure the authenticated use has access to this data for any cluster...
AuthorizationHelper.verifyAuthorization(ResourceType.CLUSTER, null, REQUIRED_GET_AUTHORIZATIONS);
Set<String> requestedIds = getRequestPropertyIds(request, predicate);
Set<Resource> resources = new HashSet<>();
AmbariManagementController managementController = getManagementController();
Clusters clusters = managementController.getClusters();
for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
String clusterName = getClusterName(propertyMap);
Cluster cluster;
try {
cluster = clusters.getCluster(clusterName);
if (cluster == null) {
throw new NoSuchParentResourceException(String.format("A cluster with the name %s does not exist.", clusterName));
}
} catch (AmbariException e) {
throw new NoSuchParentResourceException(String.format("A cluster with the name %s does not exist.", clusterName));
}
// Ensure the authenticated use has access to this data for the requested cluster...
AuthorizationHelper.verifyAuthorization(ResourceType.CLUSTER, cluster.getResourceId(), REQUIRED_GET_AUTHORIZATIONS);
KerberosHelper.KerberosDescriptorType kerberosDescriptorType = getKerberosDescriptorType(propertyMap);
if (kerberosDescriptorType == null) {
for (KerberosHelper.KerberosDescriptorType type : KerberosHelper.KerberosDescriptorType.values()) {
resources.add(toResource(clusterName, type, null, requestedIds));
}
} else {
KerberosDescriptor kerberosDescriptor;
try {
KerberosHelper kerberosHelper = getManagementController().getKerberosHelper();
Map<String, String> requestInfoProperties= request.getRequestInfoProperties();
kerberosDescriptor = kerberosHelper.getKerberosDescriptor(kerberosDescriptorType,
cluster,
getEvaluateWhen(requestInfoProperties),
getAdditionalServices(requestInfoProperties));
} catch (AmbariException e) {
throw new SystemException("An unexpected error occurred building the cluster's composite Kerberos Descriptor", e);
}
if (kerberosDescriptor != null) {
resources.add(toResource(clusterName, kerberosDescriptorType, kerberosDescriptor, requestedIds));
}
}
}
return resources;
}
@Override
protected Set<String> getPKPropertyIds() {
return PK_PROPERTY_IDS;
}
/**
* Retrieves the cluster name from the request property map.
*
* @param propertyMap the request property map
* @return a cluster name
* @throws IllegalArgumentException if the cluster name value is missing or empty.
*/
private String getClusterName(Map<String, Object> propertyMap) {
String clusterName = (String) propertyMap.get(CLUSTER_KERBEROS_DESCRIPTOR_CLUSTER_NAME_PROPERTY_ID);
if (StringUtils.isEmpty(clusterName)) {
throw new IllegalArgumentException("Invalid argument, cluster name is required");
}
return clusterName;
}
/**
* Retrieves the Kerberos descriptor type from the request property map, if one was specified.
* <p/>
* See {@link KerberosHelper.KerberosDescriptorType}
* for expected values.
*
* @param propertyMap the request property map
* @return a KerberosDescriptorType; or null is not specified in the request propery map
* @throws IllegalArgumentException if the Kerberos descriptor type value is specified but not an expected value.
*/
private KerberosHelper.KerberosDescriptorType getKerberosDescriptorType(Map<String, Object> propertyMap) {
String type = (String) propertyMap.get(CLUSTER_KERBEROS_DESCRIPTOR_TYPE_PROPERTY_ID);
KerberosHelper.KerberosDescriptorType kerberosDescriptorType = null;
if (!StringUtils.isEmpty(type)) {
try {
kerberosDescriptorType = KerberosHelper.KerberosDescriptorType.valueOf(type.trim().toUpperCase());
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid argument, kerberos descriptor type of 'STACK', 'USER', or 'COMPOSITE' is required");
}
}
return kerberosDescriptorType;
}
/**
* Determine if <code>when</code> chauses should be evaluated or not.
* <p>
* This is determined by the existance and value of the <code>evaluate_when</code> directive.
* If it exists and is set to "true", then <code>when</code> clauses should be evaluated, else
* they should not.
*
* @param requestInfoProperties a map a request info properties
* @return true if <code>when</code> clauses are to be evaluted; false otherwise
*/
private boolean getEvaluateWhen(Map<String, String> requestInfoProperties) {
return (requestInfoProperties != null) && "true".equalsIgnoreCase(requestInfoProperties.get(DIRECTIVE_EVALUATE_WHEN_CLAUSE));
}
/**
* Get the optional list of services indicated as <code>additional_servides</code> which are to be
* used in addtion to currently installed services when evaluating <code>when</code> clauses.
*
* @param requestInfoProperties a map a request info properties
* @return a collection of service names, or <code>null</code> if not specified
*/
private Collection<String> getAdditionalServices(Map<String, String> requestInfoProperties) {
if (requestInfoProperties != null) {
String value = requestInfoProperties.get(DIRECTIVE_ADDITIONAL_SERVICES);
if(!StringUtils.isEmpty(value)) {
return Arrays.asList(value.split("\\s*,\\s*"));
}
}
return null;
}
/**
* Creates a new resource from the given cluster name, alias, and persist values.
*
* @param clusterName a cluster name
* @param kerberosDescriptorType a Kerberos descriptor type
* @param kerberosDescriptor a Kerberos descriptor
* @param requestedIds the properties to include in the resulting resource instance
* @return a resource
*/
private Resource toResource(String clusterName, KerberosHelper.KerberosDescriptorType kerberosDescriptorType,
KerberosDescriptor kerberosDescriptor, Set<String> requestedIds) {
Resource resource = new ResourceImpl(Type.ClusterKerberosDescriptor);
setResourceProperty(resource, CLUSTER_KERBEROS_DESCRIPTOR_CLUSTER_NAME_PROPERTY_ID, clusterName, requestedIds);
if (kerberosDescriptorType != null) {
setResourceProperty(resource, CLUSTER_KERBEROS_DESCRIPTOR_TYPE_PROPERTY_ID, kerberosDescriptorType.name(), requestedIds);
}
if (kerberosDescriptor != null) {
setResourceProperty(resource, CLUSTER_KERBEROS_DESCRIPTOR_DESCRIPTOR_PROPERTY_ID, kerberosDescriptor.toMap(), requestedIds);
}
return resource;
}
}