/**
* ============LICENSE_START=======================================================
* org.onap.aai
* ================================================================================
* Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed 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.
* ============LICENSE_END=========================================================
*/
package org.onap.aai.serialization.db;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.onap.aai.exceptions.AAIException;
import org.onap.aai.introspection.Introspector;
import org.onap.aai.logging.LogFormatTools;
import org.onap.aai.serialization.engines.TransactionalGraphEngine;
import org.onap.aai.serialization.engines.query.QueryEngine;
import org.onap.aai.util.AAIConfigProxy;
import org.onap.aai.util.AAIConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ImpliedDelete class is responsible for deleting children
* of a parent vertex if the client explicitly asks to remove them
* It will check if they are allowed and based on that information
* it will decide whether to proceed with the deletion or
* throw an exception back to the requester if they are not allowed
*
* It implements the AAIProxy Interface and any calls to the
* AAIConfig should be using the proxy methods and any new
* methods that needs to be invoked should be added there first
*
* @see org.onap.aai.util.AAIConfigProxy
*/
public class ImpliedDelete implements AAIConfigProxy {
private static final Logger LOGGER = LoggerFactory.getLogger(ImpliedDelete.class);
private static final String IMPLICIT_DELETE = "Implicit DELETE";
private static final String STAR = "*";
private TransactionalGraphEngine engine;
private DBSerializer serializer;
public ImpliedDelete(TransactionalGraphEngine engine, DBSerializer serializer) {
this.engine = engine;
this.serializer = serializer;
}
/**
* Checks if the user is allowed to execute implied delete
* If they are allowed to do the delete, then for all the dependent vertices
* it will identify all the deletable vertices to them
* and log them based on the following aaiconfig properties:
*
* aai.implied.delete.log.enabled=true
* aai.implied.delete.log.limit=-1
*
* Above properties are the default assumption of the code if they are not overwritten
* So the code will log for every vertex it is about to delete
* If there are thousands of vertexes that get implicitly deleted,
* its preferable that the operation timeout rather than
* someone accidentally deleting thousands of children
*
* @param id - Identifier of the vertex whose children could be potentially deleted
* @param sot - source of truth of who the requester who is making the request
* @param objectType - type of the parent object whose children are being deleted
* @param dependentVertexes - list of children vertexes
* @throws AAIException if the user is not allowed to implicitly delete children
*/
public List execute(Object id, String sot, String objectType, List dependentVertexes)
throws AAIException {
if (dependentVertexes != null && !dependentVertexes.isEmpty()) {
// Find all the deletable vertices from the dependent vertices that should be deleted
// So for each of the following dependent vertices,
// we will use the edge properties and do the cascade delete
QueryEngine queryEngine = this.engine.getQueryEngine();
List impliedDeleteVertices = queryEngine.findDeletable(dependentVertexes);
if (this.allow(sot, objectType)) {
int impliedDeleteCount = impliedDeleteVertices.size();
LOGGER.warn(
"For the vertex with id {}, doing an implicit delete on update will delete total of {} vertexes",
id, impliedDeleteCount);
String impliedDeleteLogEnabled = get(AAIConstants.AAI_IMPLIED_DELETE_LOG_ENABLED, "true");
int impliedDeleteLogLimit = getInt(AAIConstants.AAI_IMPLIED_DELETE_LOG_LIMIT, "-1");
if (impliedDeleteLogLimit == -1) {
impliedDeleteLogLimit = Integer.MAX_VALUE;
}
// If the logging is enabled for implied delete
// then log the payload in the latest format
if ("true".equals(impliedDeleteLogEnabled) && impliedDeleteCount <= impliedDeleteLogLimit) {
for (Vertex vertex : impliedDeleteVertices) {
Introspector introspector = null;
try {
introspector = serializer.getLatestVersionView(vertex);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Implied delete object in json format {}", introspector.marshal(false));
}
} catch (Exception ex) {
LOGGER.warn(
"Encountered an exception during retrieval of vertex properties with vertex-id {} -> {}",
id, LogFormatTools.getStackTop(ex));
}
}
}
} else {
LOGGER.error("User {} is not allowed to implicit delete on parent object {}", sot, objectType);
throw new AAIException("AAI_9109");
}
return impliedDeleteVertices;
} else {
// Return null or an empty list back to the user based on input
return dependentVertexes;
}
}
public void delete(List vertices) {
// After all the appropriate logging, calling the serializer delete to delete the affected vertices
if (vertices != null && !vertices.isEmpty()) {
serializer.delete(vertices);
}
}
/**
* Checks the property in the aaiconfig properties
* to see if the user is allowed to do implicit delete
*
* Expecting the aaiconfig.properties to have following type of properties
*
*
* aai.implied.delete.whitelist.sdnc=*
* aai.implied.delete.whitelist.sdc='pserver','vserver'
*
*
* So in the above code, the expectation is for any of the following user:
*
*
* - SDC
* - SDc
* - Sdc
* - sDc
* - SdC
* - sdC
* - sdc
*
*
* They are allowed to delete the children of pserver and vserver by implicit delete
*
* Note: The reason the property values are placed inside the single quotes is
* so if there is an object called volume and there is another object called volume-group
* when doing an contains it can falsely allow volume-group children to be implicitly deleted
* and the cost of turning the string into an array and then searching if its inside it
* or loading into an set which is unnecessary and it could potentially be done for every request
*
* @param sourceOfTruth - the original requester that the request is coming from,
* derived from HTTP Header X-FromAppId
* @param parentObjectType - parent object in which they are trying to do the implicit delete against
*
* @return true - if the requester is allowed to implicit delete against the object type
* false - if they are not allowed
*/
private boolean allow(String sourceOfTruth, String parentObjectType) {
Objects.requireNonNull(sourceOfTruth);
Objects.requireNonNull(parentObjectType);
String propertyName = AAIConstants.AAI_IMPLIED_DELETE_WHITELIST + sourceOfTruth.toLowerCase();
String whitelist = get(propertyName, StringUtils.EMPTY);
if (whitelist.isEmpty()) {
return false;
}
if (STAR.equals(whitelist)) {
return true;
}
if (whitelist.contains("'" + parentObjectType + "'")) {
return true;
}
return false;
}
}