/* Copyright 2012-2015 SAP SE
*
* 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.
*/
package eu.aniketos.securebpmn.features;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import org.eclipse.bpmn2.BoundaryEvent;
import org.eclipse.bpmn2.CallActivity;
import org.eclipse.bpmn2.FlowElement;
import org.eclipse.bpmn2.FlowNode;
import org.eclipse.bpmn2.InclusiveGateway;
import org.eclipse.bpmn2.StartEvent;
import org.eclipse.bpmn2.SubProcess;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.ICustomContext;
import org.eclipse.graphiti.features.custom.AbstractCustomFeature;
import org.eclipse.graphiti.mm.pictograms.ContainerShape;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;
import eu.aniketos.securebpmn.satmc.SatmcFact;
import eu.aniketos.securebpmn.satmc.SatmcFunction;
import eu.aniketos.securebpmn.satmc.SatmcMessage;
import eu.aniketos.securebpmn.satmc.SatmcTraceStep;
import eu.aniketos.securebpmn.satmc.Summary;
import eu.aniketos.securebpmn.util.DialogUtil;
import eu.aniketos.securebpmn.validation.SCVMValidationConstants;
import eu.aniketos.securebpmn.validation.ValidateAslanRunnable;
import eu.aniketos.securebpmn.visualization.ActionType;
import eu.aniketos.securebpmn.visualization.VisualizationElement;
import eu.aniketos.securebpmn.visualization.rbac.AttackTracePlayer;
import eu.aniketos.securebpmn.visualization.rbac.AttackTraceStep;
import eu.aniketos.securebpmn.visualization.rbac.RbacVisualization;
/**
* This feature performs an analysis of the access control specification and
* constraints contained in the process and notifies the user, if violations
* were found or not. This feature is not intended for direct use, you should
* use the class ValidateAslanLocalFeature or ValidateAslanWebFeature, depending
* on how you want SATMC to be executed.
*
*
*/
public class ValidateAslanFeature extends AbstractCustomFeature {
public ValidateAslanFeature(IFeatureProvider fp) {
super(fp);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.graphiti.features.impl.AbstractFeature#getName()
*/
@Override
public String getName() {
return "Validate ASLan"; //$NON-NLS-1$
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.graphiti.features.custom.AbstractCustomFeature#getDescription
* ()
*/
@Override
public String getDescription() {
return "Validate ASLan using SATMC"; //$NON-NLS-1$
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.graphiti.features.custom.AbstractCustomFeature#canExecute
* (org.eclipse.graphiti.features.context.ICustomContext)
*/
@Override
public boolean canExecute(ICustomContext context) {
return true;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.graphiti.features.custom.ICustomFeature#execute(org.eclipse
* .graphiti.features.context.ICustomContext)
*/
public void execute(ICustomContext context) {
this.execute(true);
}
/**
* Starts the analysis of the ASLan representation and reacts according to
* the result, either displaying a message to the user or starting the
* attack trace visualization if an attack was found.
*
* @param local
* true if the local SATMC binary should be used, false if the
* SATMC web service should be used.
*/
public void execute(boolean local) {
// check for unsupported elements.
List<String> unsupportedElements = getUnsupportedElementTypes();
if (unsupportedElements.size() > 0) {
// ask user what to do
StringBuilder ueMessage = new StringBuilder();
ueMessage
.append("The diagram contains the following currently unsupported elements:\n");
for (String ue : unsupportedElements) {
ueMessage.append(ue.endsWith("Impl") ? ue.substring(0,
ue.length() - 4) : ue);
ueMessage.append("\n");
}
ueMessage
.append("\nThe analysis result might be incorrect. Continue anyway?");
String[] ueButtons = { "Continue", "Cancel" };
int ueContinue = DialogUtil.openMessageDialog(
"Unsupported elements found!", ueMessage.toString(),
DialogUtil.WARNING, ueButtons, 1);
if (ueContinue == 1)
return;
}
// get & parse result
SatmcMessage result = new SatmcMessage(null, null, null, null);
try {
final IProgressService progressService = PlatformUI.getWorkbench()
.getProgressService();
final ValidateAslanRunnable runnable = new ValidateAslanRunnable(
getDiagram(), result, local);
progressService.busyCursorWhile(runnable);
} catch (Exception e) {
e.printStackTrace();
}
// check if validation/parsing was successful
if (result.summary == null) {
return;
} else if (result.summary == Summary.ERROR) {
DialogUtil.openMessageDialog("Error", "SATMC Error",
DialogUtil.ERROR);
return;
} else if (result.summary == Summary.INCONCLUSIVE) {
DialogUtil.openMessageDialog("Validation inconclusive",
"SATMC analysis was inconclusive.", DialogUtil.INFO);
return;
} else if (result.summary == Summary.NO_ATTACK_FOUND) {
DialogUtil.openMessageDialog("No attack found", "No attack found!",
DialogUtil.INFO);
return;
} else if (result.summary == Summary.UNKNOWN) {
DialogUtil.openMessageDialog("Unknown result",
"SATMC result is unknown!", DialogUtil.WARNING);
return;
} else if (result.summary == Summary.ATTACK_FOUND) {
List<AttackTraceStep> traceList = extractElementIDsAndDescription(result);
DialogUtil.openMessageDialog("Attack found",
"Attack found!\n\nViolated goal: " + result.goal,
DialogUtil.ERROR);
// set result in storage
RbacVisualization.getInstance().setResult(result);
findBusinessObjectsAndPictogramElements(traceList);
// create & store player
RbacVisualization.getInstance().setPlayer(
new AttackTracePlayer(getDiagram(), traceList));
// open view
try {
PlatformUI
.getWorkbench()
.getActiveWorkbenchWindow()
.getActivePage()
.showView(
"eu.aniketos.securebpmn.validation.view");
} catch (PartInitException e) {
e.printStackTrace();
}
}
}
/**
* Replaces the task instance IDs with the task IDs in the violated goal of
* the result. Extracts the attack trace steps and for each step the task
* ID, the type of the step (claim, execute or violation) and the
* description of the step.
*
* @param result
* The SATMC result that should be analyzed and modified.
* @return A List of AttackTraceSteps, each step containing the task ID,
* type and description.
*/
private List<AttackTraceStep> extractElementIDsAndDescription(
SatmcMessage result) {
List<AttackTraceStep> traceList = new ArrayList<AttackTraceStep>();
// dummy step for StartEvent
traceList.add(new AttackTraceStep());
// get number of tasks in goal
int tasksInGoal = 0;
for (SatmcFact fact : result.goal.args) {
if (fact instanceof SatmcFunction) {
SatmcFunction func = (SatmcFunction) fact;
if (func.name.equals("n") && func.args.size() == 1) {
tasksInGoal++;
}
}
}
int goalTasksStartIndex = result.goal.args.size() - tasksInGoal;
// Goals:
// name: sod; args: 0. user 1. task1 2. task2
// name: bod; args: 0. user1 1. user2 2. task1 3. task2
// Rules:
// name: h_TaskExecution; args: 0. user 1. role 2. task 3. tInstance
// 4. inData 5. outData
// name: atask_execution; args: 0. task 1. tInstance 2. inData 3.
// outData
// name: authorizeTaskExecution; args: 0. user 1. role 2. task 3.
// tInstance
for (SatmcTraceStep sts : result.trace) {
AttackTraceStep traceElement = new AttackTraceStep();
String stepText = "";
for (SatmcFunction rule : sts.rules) {
if (rule.args.size() > 5
&& rule.name
.equals(SCVMValidationConstants.HUMAN_TASK_RULE_NAME)) {
// UserTask
// replace taskInstances with taskIDs in goal
if (result.goal.args.size() > 2
&& result.goal.name
.startsWith(SCVMValidationConstants.GOAL_SOD_PREFIX)) {
for (int i = goalTasksStartIndex; i < result.goal.args
.size(); i++) {
if (rule.args.get(3).toString()
.equals(result.goal.args.get(i).toString())) {
result.goal.args.set(i, rule.args.get(2));
}
}
} else if (result.goal.args.size() > 3
&& result.goal.name
.startsWith(SCVMValidationConstants.GOAL_BOD_PREFIX)) {
for (int i = goalTasksStartIndex; i < result.goal.args
.size(); i++) {
if (rule.args.get(3).toString()
.equals(result.goal.args.get(i).toString())) {
result.goal.args.set(i, rule.args.get(2));
}
}
}
traceElement.addInvolvedElement(new VisualizationElement(
rule.args.get(2).toString(), ActionType.EXECUTE));
stepText += "Human task \"" + rule.args.get(2).toString()
+ "\" executed by user \""
+ rule.args.get(0).toString() + "\" with role \""
+ rule.args.get(1).toString() + "\".\n";
} else if (rule.args.size() > 3
&& rule.name
.equals(SCVMValidationConstants.AUTOMATED_TASK_RULE_NAME)) {
// any other Task
traceElement.addInvolvedElement(new VisualizationElement(
rule.args.get(0).toString(), ActionType.EXECUTE));
stepText += "Automated task \""
+ rule.args.get(0).toString() + "\" executed.\n";
} else if (rule.args.size() > 3
&& rule.name
.equals(SCVMValidationConstants.TASK_AUTHORIZATION_RULE_NAME)) {
// claiming of a Task
traceElement.addInvolvedElement(new VisualizationElement(
rule.args.get(2).toString(), ActionType.CLAIM));
stepText += "User \"" + rule.args.get(0).toString()
+ "\" claimed task \""
+ rule.args.get(2).toString() + "\" using role \""
+ rule.args.get(1).toString() + "\".\n";
} else {
// check for gateway
String[] ruleNameParts = rule.name.split("_");
if (ruleNameParts.length > 1) {
if (ruleNameParts[0].equals("w")
&& ruleNameParts[1].contains("gateway")) {
// AND-split or AND-join
traceElement
.addInvolvedElement(new VisualizationElement(
ruleNameParts[1],
ActionType.WORKFLOW));
stepText += "Passing parallel gateway \""
+ ruleNameParts[1] + "\".\n";
} else if (ruleNameParts[0].contains("gateway")
&& ruleNameParts[1].startsWith("branch")) {
// XOR-split or XOR-join
traceElement
.addInvolvedElement(new VisualizationElement(
ruleNameParts[0],
ActionType.WORKFLOW));
stepText += "Passing exclusive gateway \""
+ ruleNameParts[0] + "\".\n";
}
}
}
}
if (traceElement.getInvolvedElements().size() > 0) {
traceElement.setDescription(stepText);
traceList.add(traceElement);
}
}
// step for goal violation highlighting
AttackTraceStep goalStep = new AttackTraceStep();
goalStep.setDescription("Violation of goal \"" + result.goal.toString()
+ "\".");
if (result.goal.args.size() > 2
&& result.goal.name
.startsWith(SCVMValidationConstants.GOAL_SOD_PREFIX)) {
// SoD
for (int i = goalTasksStartIndex; i < result.goal.args.size(); i++) {
goalStep.addInvolvedElement(new VisualizationElement(
result.goal.args.get(i).toString(),
ActionType.VIOLATION));
}
String sodElementID = result.goal.name.substring(
SCVMValidationConstants.GOAL_SOD_PREFIX.length(),
result.goal.name.lastIndexOf("_"));
goalStep.addInvolvedElement(new VisualizationElement(sodElementID,
ActionType.VIOLATION));
} else if (result.goal.args.size() > 3
&& result.goal.name
.startsWith(SCVMValidationConstants.GOAL_BOD_PREFIX)) {
// BoD
for (int i = goalTasksStartIndex; i < result.goal.args.size(); i++) {
goalStep.addInvolvedElement(new VisualizationElement(
result.goal.args.get(i).toString(),
ActionType.VIOLATION));
}
String bodElementID = result.goal.name.substring(
SCVMValidationConstants.GOAL_BOD_PREFIX.length(),
result.goal.name.lastIndexOf("_"));
goalStep.addInvolvedElement(new VisualizationElement(bodElementID,
ActionType.VIOLATION));
}
traceList.add(goalStep);
return traceList;
}
/**
* Finds the BusinessObjects and PictogramElements for each task ID in the
* provided lists and adds these to the single elements in the list.
*
* @param traceList
* The List of AttackTraceSteps for which the BOs and PEs should
* be found.
*/
private void findBusinessObjectsAndPictogramElements(
List<AttackTraceStep> traceList) {
// get diagram elements
List<FlowElement> diagramElements = new ArrayList<FlowElement>();
for (EObject object : getDiagram().eResource().getContents()) {
if (object instanceof FlowElement)
diagramElements.add((FlowElement) object);
}
for (AttackTraceStep traceStep : traceList) {
for (VisualizationElement element : traceStep.getInvolvedElements()) {
// find BO
for (FlowElement bo : diagramElements) {
if (bo.getId().equals(element.getId())) {
element.setbObject(bo);
break;
}
}
if (element.getbObject() == null) {
System.err
.println("[SCVM-RBAC] No FlowElement found for ID \""
+ element.getId() + "\"!");
}
// find corresponding PE
List<PictogramElement> pElements = Graphiti.getLinkService()
.getPictogramElements(getDiagram(),
element.getbObject());
for (PictogramElement pElement : pElements) {
if (pElement instanceof ContainerShape) {
ContainerShape cs = (ContainerShape) pElement;
element.setpElement(cs);
}
}
if (element.getpElement() == null) {
System.err
.println("[SCVM-RBAC] No PictogramElement found for ID \""
+ element.getId() + "\"!");
}
}
}
// get and add start event
// there should only be one FlowNode after a StartEvent
StartEvent startEvent = null;
// find start event
if (traceList.size() > 1) {
List<VisualizationElement> firstElements = traceList.get(1)
.getInvolvedElements();
if (firstElements.size() > 0) {
FlowNode firstNode = (FlowNode) firstElements.get(0)
.getbObject();
if (firstNode.getIncoming().size() > 0) {
FlowNode start = firstNode.getIncoming().get(0)
.getSourceRef();
if (start instanceof StartEvent) {
startEvent = (StartEvent) start;
}
}
}
}
// create entry if we found it
if (startEvent != null) {
VisualizationElement startElement = new VisualizationElement(
startEvent.getId(), ActionType.WORKFLOW);
startElement.setbObject(startEvent);
// find corresponding pictogram element
List<PictogramElement> pElements = Graphiti.getLinkService()
.getPictogramElements(getDiagram(), startEvent);
for (PictogramElement pElement : pElements) {
// change element representation
if (pElement instanceof ContainerShape) {
ContainerShape cs = (ContainerShape) pElement;
startElement.setpElement(cs);
}
}
traceList.get(0).addInvolvedElement(startElement);
traceList.get(0).setDescription("The process has been started.");
}
}
/**
* Returns a list with the names of the BPMN 2.0 elements that are currently
* not supported by the analysis.
*
* @return A List with the names of the unsupported elements as Strings.
*/
private List<String> getUnsupportedElementTypes() {
List<String> res = new ArrayList<String>();
for (EObject object : getDiagram().eResource().getContents()) {
if (object instanceof SubProcess || object instanceof CallActivity
|| object instanceof InclusiveGateway
|| object instanceof BoundaryEvent) {
res.add(object.getClass().getSimpleName());
}
}
// remove duplicates
HashSet<String> h = new HashSet<String>(res);
res.clear();
res.addAll(h);
return res;
}
}