blob: f1d5c358b34291496b76d21f313b983b8baff62c [file] [log] [blame]
/**
* 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.view;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.persist.Transactional;
import org.apache.ambari.server.orm.entities.PermissionEntity;
import org.apache.ambari.server.orm.entities.ViewEntity;
import org.apache.ambari.server.orm.entities.ViewInstanceEntity;
import org.apache.ambari.server.view.configuration.ParameterConfig;
import org.apache.ambari.server.view.configuration.ViewConfig;
import org.apache.ambari.server.view.events.EventImpl;
import org.apache.ambari.server.view.persistence.DataStoreImpl;
import org.apache.ambari.server.view.persistence.DataStoreModule;
import org.apache.ambari.server.view.validation.ValidationException;
import org.apache.ambari.view.AmbariStreamProvider;
import org.apache.ambari.view.ClusterType;
import org.apache.ambari.view.DataStore;
import org.apache.ambari.view.ImpersonatorSetting;
import org.apache.ambari.view.MaskException;
import org.apache.ambari.view.Masker;
import org.apache.ambari.view.ResourceProvider;
import org.apache.ambari.view.SecurityException;
import org.apache.ambari.view.SystemException;
import org.apache.ambari.view.URLConnectionProvider;
import org.apache.ambari.view.ViewContext;
import org.apache.ambari.view.ViewController;
import org.apache.ambari.view.ViewDefinition;
import org.apache.ambari.view.ViewInstanceDefinition;
import org.apache.ambari.view.cluster.Cluster;
import org.apache.ambari.view.events.Event;
import org.apache.ambari.view.events.Listener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.directory.api.util.Strings;
import org.apache.hadoop.security.authentication.util.KerberosName;
import org.apache.hadoop.security.authentication.util.KerberosUtil;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.ParseErrorException;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
/**
* View context implementation.
*/
public class ViewContextImpl implements ViewContext, ViewController {
/**
* Logger.
*/
private static final Log LOG = LogFactory.getLog(ViewContextImpl.class);
public static final String HADOOP_SECURITY_AUTH_TO_LOCAL = "hadoop.security.auth_to_local";
public static final String CORE_SITE = "core-site";
public static final String HDFS_AUTH_TO_LOCAL = "hdfs.auth_to_local";
/**
* The associated view definition.
*/
private final ViewEntity viewEntity;
/**
* The associated view definition.
*/
private final ViewInstanceEntity viewInstanceEntity;
/**
* The view registry.
*/
private final ViewRegistry viewRegistry;
/**
* The data store.
*/
private DataStore dataStore = null;
/**
* Masker for properties.
*/
private Masker masker;
/**
* The velocity context used to evaluate property templates.
*/
private final VelocityContext velocityContext;
// ---- Constructors -------------------------------------------------------
/**
* Construct a view context from the given view instance entity.
*
* @param viewInstanceEntity the view entity
* @param viewRegistry the view registry
*/
public ViewContextImpl(ViewInstanceEntity viewInstanceEntity, ViewRegistry viewRegistry) {
this(viewInstanceEntity.getViewEntity(), viewInstanceEntity, viewRegistry);
}
/**
* Construct a view context from the given view entity.
*
* @param viewEntity the view entity
* @param viewRegistry the view registry
*/
public ViewContextImpl(ViewEntity viewEntity, ViewRegistry viewRegistry) {
this(viewEntity, null, viewRegistry);
}
private ViewContextImpl(ViewEntity viewEntity, ViewInstanceEntity viewInstanceEntity, ViewRegistry viewRegistry) {
this.viewEntity = viewEntity;
this.viewInstanceEntity = viewInstanceEntity;
this.viewRegistry = viewRegistry;
this.masker = getMasker(viewEntity.getClassLoader(), viewEntity.getConfiguration());
this.velocityContext = initVelocityContext();
}
// ----- ViewContext -------------------------------------------------------
@Override
public String getViewName() {
return viewEntity.getCommonName();
}
@Override
public ViewDefinition getViewDefinition() {
return viewEntity;
}
@Override
public String getInstanceName() {
return viewInstanceEntity == null ? null : viewInstanceEntity.getName();
}
@Override
public ViewInstanceDefinition getViewInstanceDefinition() {
return viewInstanceEntity;
}
@Override
public Map<String, String> getProperties() {
if (viewInstanceEntity == null) {
return null;
} else {
return Collections.unmodifiableMap(getPropertyValues());
}
}
@Transactional
@Override
public void putInstanceData(String key, String value) {
checkInstance();
ViewInstanceEntity updateInstance =
viewRegistry.getViewInstanceEntity(viewInstanceEntity.getViewName(), viewInstanceEntity.getInstanceName());
if (updateInstance != null) {
updateInstance.putInstanceData(key, value);
try {
viewRegistry.updateViewInstance(updateInstance);
} catch (SystemException e) {
String msg = "Caught exception updating the view instance.";
LOG.error(msg, e);
throw new IllegalStateException(msg, e);
} catch (ValidationException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
}
@Override
public String getInstanceData(String key) {
return viewInstanceEntity == null ? null :
viewInstanceEntity.getInstanceDataMap().get(key);
}
@Override
public Map<String, String> getInstanceData() {
return viewInstanceEntity == null ? null :
Collections.unmodifiableMap(viewInstanceEntity.getInstanceDataMap());
}
@Override
public void removeInstanceData(String key) {
checkInstance();
viewRegistry.removeInstanceData(viewInstanceEntity, key);
}
@Override
public String getAmbariProperty(String key) {
return viewInstanceEntity == null ? null :
viewInstanceEntity.getViewEntity().getAmbariProperty(key);
}
@Override
public ResourceProvider<?> getResourceProvider(String type) {
return viewInstanceEntity == null ? null :
viewInstanceEntity.getResourceProvider(type);
}
@Override
public String getUsername() {
String shortName = getLoggedinUser();
try {
String authToLocalRules = getAuthToLocalRules();
//Getting ambari server realm. Ideally this should come from user
String defaultRealm = KerberosUtil.getDefaultRealm();
if(Strings.isNotEmpty(authToLocalRules) && Strings.isNotEmpty(defaultRealm)){
synchronized (KerberosName.class){
KerberosName.setRules(authToLocalRules);
shortName = new KerberosName(shortName+"@"+defaultRealm).getShortName();
}
}
} catch (InvocationTargetException e) {
LOG.debug("Failed to get default realm",e);
}catch (Exception e){
LOG.warn("Failed to apply auth_to_local rules. "+e.getMessage());
LOG.debug("Failed to apply auth_to_local rules",e);
}
return shortName;
}
private String getAuthToLocalRules(){
Cluster cluster = getCluster();
String authToLocalRules = null;
if (cluster != null) {
authToLocalRules = cluster.getConfigurationValue(CORE_SITE, HADOOP_SECURITY_AUTH_TO_LOCAL);
}else if(viewInstanceEntity != null) {
authToLocalRules = viewInstanceEntity.getPropertyMap().get(HDFS_AUTH_TO_LOCAL);
}
return authToLocalRules;
}
@Override
public String getLoggedinUser(){
return viewInstanceEntity != null ? viewInstanceEntity.getUsername() : null;
}
@Override
public void hasPermission(String userName, String permissionName) throws org.apache.ambari.view.SecurityException {
if (userName == null || userName.length() == 0) {
throw new SecurityException("No user name specified.");
}
if (permissionName == null || permissionName.length() == 0) {
throw new SecurityException("No permission name specified.");
}
if (viewInstanceEntity == null) {
throw new SecurityException("There is no instance associated with the view context");
}
PermissionEntity permissionEntity = viewEntity.getPermission(permissionName);
if (permissionEntity == null) {
throw new SecurityException("The permission " + permissionName + " is not defined for " + viewEntity.getName());
}
if (!viewRegistry.hasPermission(permissionEntity, viewInstanceEntity.getResource(), userName)) {
throw new SecurityException("The user " + userName + " has not been granted permission " + permissionName);
}
}
@Override
public org.apache.ambari.view.URLStreamProvider getURLStreamProvider() {
return viewRegistry.createURLStreamProvider(this);
}
@Override
public URLConnectionProvider getURLConnectionProvider() {
return viewRegistry.createURLStreamProvider(this);
}
@Override
public synchronized AmbariStreamProvider getAmbariStreamProvider() {
return viewRegistry.createAmbariStreamProvider();
}
@Override
public AmbariStreamProvider getAmbariClusterStreamProvider() {
Long clusterHandle = viewInstanceEntity.getClusterHandle();
ClusterType clusterType = viewInstanceEntity.getClusterType();
AmbariStreamProvider clusterStreamProvider = null;
if(clusterHandle != null && clusterType == ClusterType.LOCAL_AMBARI){
clusterStreamProvider = getAmbariStreamProvider();
} else if(clusterHandle != null && clusterType == ClusterType.REMOTE_AMBARI){
clusterStreamProvider = viewRegistry.createRemoteAmbariStreamProvider(clusterHandle);
}
return clusterStreamProvider;
}
@Override
public synchronized DataStore getDataStore() {
if (viewInstanceEntity != null) {
if (dataStore == null) {
Injector injector = Guice.createInjector(new DataStoreModule(viewInstanceEntity));
dataStore = injector.getInstance(DataStoreImpl.class);
}
}
return dataStore;
}
@Override
public Collection<ViewDefinition> getViewDefinitions() {
return Collections.<ViewDefinition>unmodifiableCollection(viewRegistry.getDefinitions());
}
@Override
public Collection<ViewInstanceDefinition> getViewInstanceDefinitions() {
Collection<ViewInstanceEntity> instanceDefinitions = new HashSet<ViewInstanceEntity>();
for (ViewEntity viewEntity : viewRegistry.getDefinitions()) {
instanceDefinitions.addAll(viewRegistry.getInstanceDefinitions(viewEntity));
}
return Collections.<ViewInstanceDefinition>unmodifiableCollection(instanceDefinitions);
}
@Override
public ViewController getController() {
return this;
}
@Override
public HttpImpersonatorImpl getHttpImpersonator() {
return new HttpImpersonatorImpl(this);
}
@Override
public ImpersonatorSetting getImpersonatorSetting() {
return new ImpersonatorSettingImpl(this);
}
@Override
public Cluster getCluster() {
return viewRegistry.getCluster(viewInstanceEntity);
}
// ----- ViewController ----------------------------------------------------
@Override
public void fireEvent(String eventId, Map<String, String> eventProperties) {
Event event = viewInstanceEntity == null ?
new EventImpl(eventId, eventProperties, viewEntity) :
new EventImpl(eventId, eventProperties, viewInstanceEntity);
viewRegistry.fireEvent(event);
}
@Override
public void registerListener(Listener listener, String viewName) {
viewRegistry.registerListener(listener, viewName, null);
}
@Override
public void registerListener(Listener listener, String viewName, String viewVersion) {
viewRegistry.registerListener(listener, viewName, viewVersion);
}
@Override
public void unregisterListener(Listener listener, String viewName) {
viewRegistry.unregisterListener(listener, viewName, null);
}
@Override
public void unregisterListener(Listener listener, String viewName, String viewVersion) {
viewRegistry.unregisterListener(listener, viewName, viewVersion);
}
// ----- helper methods ----------------------------------------------------
// check for an associated instance
private void checkInstance() {
if (viewInstanceEntity == null) {
throw new IllegalStateException("No instance is associated with the context.");
}
}
private Masker getMasker(ClassLoader cl, ViewConfig viewConfig) {
try {
return viewConfig.getMaskerClass(cl).newInstance();
} catch (Exception e) {
throw new InstantiationError("Could not create masker instance.");
}
}
/**
* Get the property values for the associated view instance.
*
* @return the property values for the instance
*/
private Map<String, String> getPropertyValues() {
Map<String, String> properties = viewInstanceEntity.getPropertyMap();
Map<String, ParameterConfig> parameters = new HashMap<String, ParameterConfig>();
for (ParameterConfig paramConfig : viewEntity.getConfiguration().getParameters()) {
parameters.put(paramConfig.getName(), paramConfig);
}
Cluster cluster = getCluster();
for (Entry<String, String> entry: properties.entrySet()) {
String propertyName = entry.getKey();
String propertyValue = entry.getValue();
ParameterConfig parameterConfig = parameters.get(propertyName);
if (parameterConfig != null) {
String clusterConfig = parameterConfig.getClusterConfig();
if (clusterConfig != null && cluster != null) {
propertyValue = getClusterConfigurationValue(cluster, clusterConfig);
} else {
if (parameterConfig.isMasked()) {
try {
propertyValue = masker.unmask(propertyValue);
} catch (MaskException e) {
LOG.error("Failed to unmask view property", e);
}
}
}
}
properties.put(propertyName, evaluatePropertyTemplates(propertyValue));
}
return properties;
}
/**
* Get a specified configuration value from the given cluster.
*
* @param cluster the cluster
* @param clusterConfig the cluster configuration identifier
*
* @return the configuration value or <code>null</code> if the desired configuration can not be found
*/
private static String getClusterConfigurationValue(Cluster cluster, String clusterConfig) {
if (clusterConfig != null) {
String[] parts = clusterConfig.split("/");
if (parts.length == 2) {
return cluster.getConfigurationValue(parts[0], parts[1]);
}
}
return null;
}
/**
* Evaluate any templates in the given property value.
*
* @param rawValue original string with parameters in formal or shorthand notation
*
* @return the evaluated property value
*/
private String evaluatePropertyTemplates(String rawValue) {
if (rawValue != null) {
try {
Writer templateWriter = new StringWriter();
Velocity.evaluate(velocityContext, templateWriter, rawValue, rawValue);
return templateWriter.toString();
} catch (ParseErrorException e) {
LOG.warn(String.format("Error during parsing '%s' parameter. Leaving original value.", rawValue));
}
}
return rawValue;
}
/**
* Instantiate and initialize context for parameters processing using Velocity.
*
* @return initialized context instance
*/
private VelocityContext initVelocityContext() {
VelocityContext context = new VelocityContext();
context.put("username",
new ParameterResolver() {
@Override
protected String getValue() {
return viewContext.getUsername();
}
});
context.put("viewName",
new ParameterResolver() {
@Override
protected String getValue() {
return viewContext.getViewName();
}
});
context.put("instanceName",
new ParameterResolver() {
@Override
protected String getValue() {
return viewContext.getInstanceName();
}
});
context.put("loggedinUser",
new ParameterResolver() {
@Override
protected String getValue() {
return viewContext.getLoggedinUser();
}
});
return context;
}
// ----- Inner class : ParameterResolver -------------------------------
/**
* Represents basic parameter resolver to obtain fields of ViewContext at runtime.
*/
private abstract class ParameterResolver {
protected final ViewContext viewContext = ViewContextImpl.this;
protected abstract String getValue();
@Override
public String toString() {
String value = getValue();
return value == null ? "" : value;
}
}
}