/** * 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 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; 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 com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.persist.Transactional; /** * 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<>(); 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<>(); 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; } } }