/**
* 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.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.ObjectNotFoundException;
import org.apache.ambari.server.StaticallyInject;
import org.apache.ambari.server.controller.AmbariManagementController;
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.RequestStatus;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.Resource.Type;
import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
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.orm.dao.WidgetDAO;
import org.apache.ambari.server.orm.entities.PermissionEntity;
import org.apache.ambari.server.orm.entities.WidgetEntity;
import org.apache.ambari.server.orm.entities.WidgetLayoutUserWidgetEntity;
import org.apache.ambari.server.security.authorization.AmbariGrantedAuthority;
import org.apache.ambari.server.security.authorization.AuthorizationHelper;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import com.google.gson.Gson;
import com.google.inject.Inject;
/**
* Resource provider for repository versions resources.
*/
@StaticallyInject
public class WidgetResourceProvider extends AbstractControllerResourceProvider {
// ----- Property ID constants ---------------------------------------------
public static final String WIDGET_ID_PROPERTY_ID = PropertyHelper.getPropertyId("WidgetInfo", "id");
public static final String WIDGET_CLUSTER_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("WidgetInfo", "cluster_name");
public static final String WIDGET_WIDGET_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("WidgetInfo", "widget_name");
public static final String WIDGET_WIDGET_TYPE_PROPERTY_ID = PropertyHelper.getPropertyId("WidgetInfo", "widget_type");
public static final String WIDGET_TIME_CREATED_PROPERTY_ID = PropertyHelper.getPropertyId("WidgetInfo", "time_created");
public static final String WIDGET_AUTHOR_PROPERTY_ID = PropertyHelper.getPropertyId("WidgetInfo", "author");
public static final String WIDGET_DESCRIPTION_PROPERTY_ID = PropertyHelper.getPropertyId("WidgetInfo", "description");
public static final String WIDGET_SCOPE_PROPERTY_ID = PropertyHelper.getPropertyId("WidgetInfo", "scope");
public static final String WIDGET_METRICS_PROPERTY_ID = PropertyHelper.getPropertyId("WidgetInfo", "metrics");
public static final String WIDGET_VALUES_PROPERTY_ID = PropertyHelper.getPropertyId("WidgetInfo", "values");
public static final String WIDGET_PROPERTIES_PROPERTY_ID = PropertyHelper.getPropertyId("WidgetInfo", "properties");
public enum SCOPE {
CLUSTER,
USER
}
@SuppressWarnings("serial")
private static Set<String> pkPropertyIds = new HashSet<String>() {
{
add(WIDGET_ID_PROPERTY_ID);
}
};
@SuppressWarnings("serial")
public static Set<String> propertyIds = new HashSet<String>() {
{
add(WIDGET_ID_PROPERTY_ID);
add(WIDGET_WIDGET_NAME_PROPERTY_ID);
add(WIDGET_WIDGET_TYPE_PROPERTY_ID);
add(WIDGET_TIME_CREATED_PROPERTY_ID);
add(WIDGET_CLUSTER_NAME_PROPERTY_ID);
add(WIDGET_AUTHOR_PROPERTY_ID);
add(WIDGET_DESCRIPTION_PROPERTY_ID);
add(WIDGET_SCOPE_PROPERTY_ID);
add(WIDGET_METRICS_PROPERTY_ID);
add(WIDGET_VALUES_PROPERTY_ID);
add(WIDGET_PROPERTIES_PROPERTY_ID);
}
};
@SuppressWarnings("serial")
public static Map<Type, String> keyPropertyIds = new HashMap<Type, String>() {
{
put(Type.Widget, WIDGET_ID_PROPERTY_ID);
put(Type.Cluster, WIDGET_CLUSTER_NAME_PROPERTY_ID);
put(Type.User, WIDGET_AUTHOR_PROPERTY_ID);
}
};
@Inject
private static WidgetDAO widgetDAO;
@Inject
private static Gson gson;
/**
* Create a new resource provider.
*
*/
public WidgetResourceProvider(AmbariManagementController managementController) {
super(propertyIds, keyPropertyIds, managementController);
}
@Override
public RequestStatus createResources(final Request request)
throws SystemException,
UnsupportedPropertyException,
ResourceAlreadyExistsException,
NoSuchParentResourceException {
Set<Resource> associatedResources = new HashSet<>();
for (final Map<String, Object> properties : request.getProperties()) {
WidgetEntity widgetEntity = createResources(new Command<WidgetEntity>() {
@Override
public WidgetEntity invoke() throws AmbariException {
final String[] requiredProperties = {
WIDGET_CLUSTER_NAME_PROPERTY_ID,
WIDGET_WIDGET_NAME_PROPERTY_ID,
WIDGET_WIDGET_TYPE_PROPERTY_ID,
WIDGET_SCOPE_PROPERTY_ID
};
for (String propertyName: requiredProperties) {
if (properties.get(propertyName) == null) {
throw new AmbariException("Property " + propertyName + " should be provided");
}
}
final WidgetEntity entity = new WidgetEntity();
String clusterName = properties.get(WIDGET_CLUSTER_NAME_PROPERTY_ID).toString();
String scope = properties.get(WIDGET_SCOPE_PROPERTY_ID).toString();
if (!isScopeAllowedForUser(scope)) {
throw new AccessDeniedException("Only cluster operator can create widgets with cluster scope");
}
entity.setWidgetName(properties.get(WIDGET_WIDGET_NAME_PROPERTY_ID).toString());
entity.setWidgetType(properties.get(WIDGET_WIDGET_TYPE_PROPERTY_ID).toString());
entity.setClusterId(getManagementController().getClusters().getCluster(clusterName).getClusterId());
entity.setScope(scope);
String metrics = (properties.containsKey(WIDGET_METRICS_PROPERTY_ID)) ?
gson.toJson(properties.get(WIDGET_METRICS_PROPERTY_ID)) : null;
entity.setMetrics(metrics);
entity.setAuthor(getAuthorName(properties));
String description = (properties.containsKey(WIDGET_DESCRIPTION_PROPERTY_ID)) ?
properties.get(WIDGET_DESCRIPTION_PROPERTY_ID).toString() : null;
entity.setDescription(description);
String values = (properties.containsKey(WIDGET_VALUES_PROPERTY_ID)) ?
gson.toJson(properties.get(WIDGET_VALUES_PROPERTY_ID)) : null;
entity.setWidgetValues(values);
Map<String, Object> widgetPropertiesMap = new HashMap<>();
for (Map.Entry<String, Object> entry : properties.entrySet()) {
if (PropertyHelper.getPropertyCategory(entry.getKey()).equals(WIDGET_PROPERTIES_PROPERTY_ID)) {
widgetPropertiesMap.put(PropertyHelper.getPropertyName(entry.getKey()), entry.getValue());
}
}
String widgetProperties = (widgetPropertiesMap.isEmpty()) ?
null : gson.toJson(widgetPropertiesMap);
entity.setProperties(widgetProperties);
widgetDAO.create(entity);
notifyCreate(Type.Widget, request);
return entity;
}
});
Resource resource = new ResourceImpl(Type.Widget);
resource.setProperty(WIDGET_ID_PROPERTY_ID, widgetEntity.getId());
associatedResources.add(resource);
}
return getRequestStatus(null, associatedResources);
}
@Override
public Set<Resource> getResources(Request request, Predicate predicate)
throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
final Set<Resource> resources = new HashSet<>();
final Set<String> requestedIds = getRequestPropertyIds(request, predicate);
final Set<Map<String, Object>> propertyMaps = getPropertyMaps(predicate);
String author = AuthorizationHelper.getAuthenticatedName();
List<WidgetEntity> requestedEntities = new ArrayList<>();
for (Map<String, Object> propertyMap: propertyMaps) {
if (propertyMap.get(WIDGET_ID_PROPERTY_ID) != null) {
final Long id;
try {
id = Long.parseLong(propertyMap.get(WIDGET_ID_PROPERTY_ID).toString());
} catch (Exception ex) {
throw new SystemException("WidgetLayout should have numerical id");
}
final WidgetEntity entity = widgetDAO.findById(id);
if (entity == null) {
throw new NoSuchResourceException("WidgetLayout with id " + id + " does not exists");
}
if (!(entity.getAuthor().equals(author) || entity.getScope().equals(SCOPE.CLUSTER.name()))) {
throw new AccessDeniedException("User must be author of the widget or widget must have cluster scope");
}
requestedEntities.add(entity);
} else {
requestedEntities.addAll(widgetDAO.findByScopeOrAuthor(author, SCOPE.CLUSTER.name()));
}
}
for (WidgetEntity entity: requestedEntities) {
final Resource resource = new ResourceImpl(Type.Widget);
resource.setProperty(WIDGET_ID_PROPERTY_ID, entity.getId());
resource.setProperty(WIDGET_WIDGET_NAME_PROPERTY_ID, entity.getWidgetName());
resource.setProperty(WIDGET_WIDGET_TYPE_PROPERTY_ID, entity.getWidgetType());
setResourceProperty(resource, WIDGET_METRICS_PROPERTY_ID, entity.getMetrics(), requestedIds);
setResourceProperty(resource, WIDGET_TIME_CREATED_PROPERTY_ID, entity.getTimeCreated(), requestedIds);
resource.setProperty(WIDGET_AUTHOR_PROPERTY_ID, entity.getAuthor());
setResourceProperty(resource, WIDGET_DESCRIPTION_PROPERTY_ID, entity.getDescription(), requestedIds);
resource.setProperty(WIDGET_SCOPE_PROPERTY_ID, entity.getScope());
setResourceProperty(resource, WIDGET_VALUES_PROPERTY_ID, entity.getWidgetValues(), requestedIds);
setResourceProperty(resource, WIDGET_PROPERTIES_PROPERTY_ID, entity.getProperties(), requestedIds);
String clusterName = null;
try {
clusterName = getManagementController().getClusters().getClusterById(entity.getClusterId()).getClusterName();
} catch (AmbariException e) {
throw new SystemException(e.getMessage());
}
setResourceProperty(resource, WIDGET_CLUSTER_NAME_PROPERTY_ID, clusterName, requestedIds);
resources.add(resource);
}
return resources;
}
@Override
public RequestStatus updateResources(Request request, Predicate predicate)
throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
final Set<Map<String, Object>> propertyMaps = request.getProperties();
modifyResources(new Command<Void>() {
@Override
public Void invoke() throws AmbariException {
for (Map<String, Object> propertyMap : propertyMaps) {
final Long id;
try {
id = Long.parseLong(propertyMap.get(WIDGET_ID_PROPERTY_ID).toString());
} catch (Exception ex) {
throw new AmbariException("Widget should have numerical id");
}
final WidgetEntity entity = widgetDAO.findById(id);
if (entity == null) {
throw new ObjectNotFoundException("There is no widget with id " + id);
}
if (StringUtils.isNotBlank(ObjectUtils.toString(propertyMap.get(WIDGET_WIDGET_NAME_PROPERTY_ID)))) {
entity.setWidgetName(propertyMap.get(WIDGET_WIDGET_NAME_PROPERTY_ID).toString());
}
if (StringUtils.isNotBlank(ObjectUtils.toString(propertyMap.get(WIDGET_WIDGET_TYPE_PROPERTY_ID)))) {
entity.setWidgetType(propertyMap.get(WIDGET_WIDGET_TYPE_PROPERTY_ID).toString());
}
if (StringUtils.isNotBlank(ObjectUtils.toString(propertyMap.get(WIDGET_METRICS_PROPERTY_ID)))) {
entity.setMetrics(gson.toJson(propertyMap.get(WIDGET_METRICS_PROPERTY_ID)));
}
entity.setAuthor(getAuthorName(propertyMap));
if (StringUtils.isNotBlank(ObjectUtils.toString(propertyMap.get(WIDGET_DESCRIPTION_PROPERTY_ID)))) {
entity.setDescription(propertyMap.get(WIDGET_DESCRIPTION_PROPERTY_ID).toString());
}
if (StringUtils.isNotBlank(ObjectUtils.toString(propertyMap.get(WIDGET_SCOPE_PROPERTY_ID)))) {
String scope = propertyMap.get(WIDGET_SCOPE_PROPERTY_ID).toString();
if (!isScopeAllowedForUser(scope)) {
throw new AmbariException("Only cluster operator can create widgets with cluster scope");
}
entity.setScope(scope);
}
if (StringUtils.isNotBlank(ObjectUtils.toString(propertyMap.get(WIDGET_VALUES_PROPERTY_ID)))) {
entity.setWidgetValues(gson.toJson(propertyMap.get(WIDGET_VALUES_PROPERTY_ID)));
}
Map<String, Object> widgetPropertiesMap = new HashMap<>();
for (Map.Entry<String, Object> entry : propertyMap.entrySet()) {
if (PropertyHelper.getPropertyCategory(entry.getKey()).equals(WIDGET_PROPERTIES_PROPERTY_ID)) {
widgetPropertiesMap.put(PropertyHelper.getPropertyName(entry.getKey()), entry.getValue());
}
}
if (!widgetPropertiesMap.isEmpty()) {
entity.setProperties(gson.toJson(widgetPropertiesMap));
}
widgetDAO.merge(entity);
}
return null;
}
});
return getRequestStatus(null);
}
@Override
public RequestStatus deleteResources(Request request, Predicate predicate)
throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
final Set<Map<String, Object>> propertyMaps = getPropertyMaps(predicate);
final List<WidgetEntity> entitiesToBeRemoved = new ArrayList<>();
for (Map<String, Object> propertyMap : propertyMaps) {
final Long id;
try {
id = Long.parseLong(propertyMap.get(WIDGET_ID_PROPERTY_ID).toString());
} catch (Exception ex) {
throw new SystemException("Widget should have numerical id");
}
final WidgetEntity entity = widgetDAO.findById(id);
if (entity == null) {
throw new NoSuchResourceException("There is no widget with id " + id);
}
entitiesToBeRemoved.add(entity);
}
for (WidgetEntity entity: entitiesToBeRemoved) {
if (entity.getListWidgetLayoutUserWidgetEntity() != null) {
for (WidgetLayoutUserWidgetEntity layoutUserWidgetEntity : entity.getListWidgetLayoutUserWidgetEntity()) {
if (layoutUserWidgetEntity.getWidgetLayout().getListWidgetLayoutUserWidgetEntity() != null) {
layoutUserWidgetEntity.getWidgetLayout().getListWidgetLayoutUserWidgetEntity().remove(layoutUserWidgetEntity);
}
}
}
widgetDAO.remove(entity);
}
return getRequestStatus(null);
}
@Override
protected Set<String> getPKPropertyIds() {
return pkPropertyIds;
}
private boolean isScopeAllowedForUser(String scope) {
if (scope.equals(WidgetEntity.USER_SCOPE)) {
return true;
}
// Only cluster operators are allowed to create widgets with cluster scope
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.getAuthentication().getAuthorities();
boolean hasPermissionForClusterScope = false;
for (GrantedAuthority grantedAuthority : securityContext.getAuthentication().getAuthorities()) {
if (((AmbariGrantedAuthority) grantedAuthority).getPrivilegeEntity().getPermission().getId()
== PermissionEntity.AMBARI_ADMINISTRATOR_PERMISSION ||
((AmbariGrantedAuthority) grantedAuthority).getPrivilegeEntity().getPermission().getId()
== PermissionEntity.CLUSTER_ADMINISTRATOR_PERMISSION) {
hasPermissionForClusterScope = true;
}
}
if (hasPermissionForClusterScope) {
return true;
} else {
return false;
}
}
private String getAuthorName(Map<String, Object> properties) {
if (StringUtils.isNotBlank(ObjectUtils.toString(properties.get(WIDGET_AUTHOR_PROPERTY_ID)))) {
return properties.get(WIDGET_AUTHOR_PROPERTY_ID).toString();
}
return getManagementController().getAuthName();
}
}