/* 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 org.activiti.designer.security.property;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.activiti.designer.util.eclipse.ActivitiUiUtil;
import org.activiti.designer.util.property.ActivitiPropertySection;
import org.eclipse.bpmn2.Activity;
import org.eclipse.bpmn2.DataInput;
import org.eclipse.bpmn2.DataOutput;
import org.eclipse.bpmn2.ItemAwareElement;
import org.eclipse.bpmn2.ServiceTask;
import org.eclipse.bpmn2.Task;
import org.eclipse.bpmn2.UserTask;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.graphiti.mm.pictograms.Diagram;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.ui.editor.DiagramEditor;
import org.eclipse.securebpmn2.Action;
import org.eclipse.securebpmn2.ActivityAuthorizationConstraint;
import org.eclipse.securebpmn2.AtomicActivityAction;
import org.eclipse.securebpmn2.AtomicItemAwareElementAction;
import org.eclipse.securebpmn2.CompositeItemAwareElementAction;
import org.eclipse.securebpmn2.ItemAwareElementAction;
import org.eclipse.securebpmn2.NeedToKnow;
import org.eclipse.securebpmn2.Permission;
import org.eclipse.securebpmn2.Role;
import org.eclipse.securebpmn2.Securebpmn2Factory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertyConstants;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
import eu.aniketos.securebpmn.ntk.NeedToKnowUtil;
import eu.aniketos.securebpmn.util.SecurityUtil;
/**
* Creates and controls the need-to-know tab in the properties view.
*
*
*/
public class PropertyNtkSection extends ActivitiPropertySection implements
ITabbedPropertyConstants {
private CCombo actionCombo;
private CCombo processVariableCombo;
private CCombo roleCombo;
private Table table;
private Composite buttonBox;
private Button addButton;
private Button removeButton;
private int checkCount = 0;
private SelectionListener selectionListener;
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#createControls
* (org.eclipse.swt.widgets.Composite,
* org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage)
*/
@Override
public void createControls(Composite parent,
TabbedPropertySheetPage tabbedPropertySheetPage) {
super.createControls(parent, tabbedPropertySheetPage);
// general vars
TabbedPropertySheetWidgetFactory factory = getWidgetFactory();
Composite composite = factory.createFlatFormComposite(parent);
FormData data;
// setup action combo box
actionCombo = factory.createCCombo(composite, SWT.NONE);
for (String actionName : NeedToKnowUtil
.getItemAwareElementActionNames()) {
actionCombo.add(actionName);
}
data = new FormData();
data.left = new FormAttachment(0, 120);
data.right = new FormAttachment(80, 0);
data.top = new FormAttachment(0, VSPACE);
actionCombo.setLayoutData(data);
// actionCombo.addFocusListener(listener);
// setup action label
CLabel actionLabel = factory.createCLabel(composite, "Action :"); //$NON-NLS-1$
data = new FormData();
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(actionCombo, -HSPACE);
data.top = new FormAttachment(actionCombo, 0, SWT.CENTER);
actionLabel.setLayoutData(data);
// setup process variable combo box
processVariableCombo = factory.createCCombo(composite, SWT.NONE);
data = new FormData();
data.left = new FormAttachment(0, 120);
data.right = new FormAttachment(80, 0);
data.top = new FormAttachment(actionCombo, VSPACE);
processVariableCombo.setLayoutData(data);
// processVariableCombo.addFocusListener(listener);
// setup process variable label
CLabel processVariableLabel = factory.createCLabel(composite,
"Process Variable :"); //$NON-NLS-1$
data = new FormData();
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(processVariableCombo, -HSPACE);
data.top = new FormAttachment(processVariableCombo, 0, SWT.CENTER);
processVariableLabel.setLayoutData(data);
// setup role combo box
roleCombo = factory.createCCombo(composite, SWT.NONE);
for (Role role : SecurityUtil.getRoles(getDiagram())) {
roleCombo.add(role.getName());
}
data = new FormData();
data.left = new FormAttachment(0, 120);
data.right = new FormAttachment(80, 0);
data.top = new FormAttachment(processVariableCombo, VSPACE);
roleCombo.setLayoutData(data);
// roleCombo.addFocusListener(listener);
// setup role label
CLabel roleLabel = factory.createCLabel(composite, "Role :"); //$NON-NLS-1$
data = new FormData();
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(roleCombo, -HSPACE);
data.top = new FormAttachment(roleCombo, 0, SWT.CENTER);
roleLabel.setLayoutData(data);
// setup table label
CLabel tableLabel = factory
.createCLabel(composite, "NtK Permissions :"); //$NON-NLS-1$
data = new FormData();
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(0, 100);
data.top = new FormAttachment(roleCombo, VSPACE);
tableLabel.setLayoutData(data);
// setup permission table part 1
data = new FormData(200, 140);
data.left = new FormAttachment(tableLabel, 10);
data.right = new FormAttachment(80, 0);
data.top = new FormAttachment(roleCombo, VSPACE);
table = new Table(composite, SWT.CHECK | SWT.BORDER | SWT.V_SCROLL
| SWT.H_SCROLL);
table.setLayoutData(data);
table.setLinesVisible(true);
table.setHeaderVisible(true);
// setup table buttons
buttonBox = getButtonBoxControl(composite);
data = new FormData();
data.left = new FormAttachment(table, -HSPACE);
data.right = new FormAttachment(100, 0);
data.top = new FormAttachment(roleLabel, 0);
buttonBox.setLayoutData(data);
buttonBox.setVisible(true);
// buttonBox.addFocusListener(listener);
// setup permission table part 2
String[] titles = { " ", "Name", "Process Variable", "Action", "Roles" };
for (int i = 0; i < titles.length; i++) {
TableColumn column = new TableColumn(table, SWT.NONE);
column.setText(titles[i]);
column.setWidth(170);
}
table.getColumn(0).setWidth(30);
table.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
if (event.detail == SWT.CHECK) {
TableItem item = (TableItem) event.item;
if (item.getChecked() == true) {
checkCount++;
} else {
checkCount--;
}
}
selectionChanged();
}
});
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#refresh()
*/
@Override
public void refresh() {
// try to get the roles if combo is still empty
if (roleCombo.getItems().length == 0) {
for (Role role : SecurityUtil.getRoles(getDiagram())) {
roleCombo.add(role.getName());
}
}
// update task IO specifications
System.out.print("[SCVM-BPMN] updating task IO specifications...");
final Diagram diagram = getDiagram();
DiagramEditor diagramEditor = (DiagramEditor) getDiagramEditor();
TransactionalEditingDomain editingDomain = diagramEditor
.getEditingDomain();
ActivitiUiUtil.runModelChange(new Runnable() {
public void run() {
final List<Task> tasks = new ArrayList<Task>();
for (EObject obj : diagram.eResource().getContents()) {
if (obj instanceof Task) {
tasks.add((Task) obj);
}
}
for (Task t : tasks) {
NeedToKnowUtil.updateIOSpecification(t, diagram);
}
}
}, editingDomain, "Model Update");
System.out.println("done!");
// refresh process variable combo box
List<String> accessedProcessVariables = new ArrayList<String>();
;
PictogramElement pe = getSelectedPictogramElement();
if (pe != null) {
Object bo = Graphiti.getLinkService()
.getBusinessObjectForLinkedPictogramElement(pe);
if (bo instanceof UserTask) {
accessedProcessVariables = NeedToKnowUtil
.getAccessedProcessVariableNames((UserTask) bo);
updateTable((Activity) bo);
} else if (bo instanceof ServiceTask) {
accessedProcessVariables = NeedToKnowUtil
.getAccessedProcessVariableNames((ServiceTask) bo);
updateTable((Activity) bo);
}
} else {
return;
}
processVariableCombo.removeAll();
for (String processVariableName : accessedProcessVariables) {
processVariableCombo.add(processVariableName);
}
}
/**
* Creates the Composite holding the Buttons for adding and removing
* need-to-know Permissions.
*
* @param parent
* The parent Composite that the new Composite will be placed on.
* @return A Composite holding the add/remove Buttons
*/
private Composite getButtonBoxControl(Composite parent) {
if (buttonBox == null) {
buttonBox = new Composite(parent, SWT.NULL);
GridLayout layout = new GridLayout();
layout.marginWidth = 0;
buttonBox.setLayout(layout);
buttonBox.setBackground(Display.getDefault().getSystemColor(
SWT.COLOR_WHITE));
addButton = createPushButton(buttonBox, "Add");
removeButton = createPushButton(buttonBox, "Remove");
buttonBox.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent event) {
addButton = null;
removeButton = null;
buttonBox = null;
}
});
}
selectionChanged();
return buttonBox;
}
/**
* Enables the remove Button when at least one element in the table is
* selected and disables it when no elements are selected.
*/
private void selectionChanged() {
removeButton.setEnabled(checkCount > 0);
}
/**
* Helper method to create and set up a Button.
*
* @param parent
* The parent Composite the Button will be placed on.
* @param key
* The text that will be displayed on the Button.
* @return A newly created Button.
*/
private Button createPushButton(Composite parent, String key) {
Button button = new Button(parent, SWT.PUSH);
button.setText(key);
button.setFont(parent.getFont());
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.widthHint = 40;
button.setLayoutData(data);
button.addSelectionListener(getSelectionListener());
return button;
}
/**
* Returns the SelectionListener and creates one if no Listener is present.
*
* @return The SelectionListener for this class.
*/
private SelectionListener getSelectionListener() {
if (selectionListener == null) {
createSelectionListener();
}
return selectionListener;
}
/**
* Creates a SelectionListener that manages the Button functionality.
*/
private void createSelectionListener() {
selectionListener = new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
Widget widget = event.widget;
if (widget == addButton) {
addPressed();
} else if (widget == removeButton) {
removePressed();
} else if (widget == table) {
selectionChanged();
}
}
};
}
/**
* Contains the code that is executed when the "add" Button is pressed. In
* particular, it creates a new NeedToKnow Permission or updates the
* existing one.
*/
private void addPressed() {
if (actionCombo.getText() == "" || processVariableCombo.getText() == ""
|| roleCombo.getText() == "")
return;
PictogramElement pe = getSelectedPictogramElement();
if (pe != null) {
Object bo = Graphiti.getLinkService()
.getBusinessObjectForLinkedPictogramElement(pe);
if (bo instanceof Activity) {
DiagramEditor diagramEditor = (DiagramEditor) getDiagramEditor();
TransactionalEditingDomain editingDomain = diagramEditor
.getEditingDomain();
ActivitiUiUtil.runModelChange(new Runnable() {
public void run() {
Object bo = Graphiti.getLinkService()
.getBusinessObjectForLinkedPictogramElement(
getSelectedPictogramElement());
if (bo == null) {
return;
} else {
// check for existing NtK permissions
Activity activity = (Activity) bo;
NeedToKnow selectedNtk = null;
for (ActivityAuthorizationConstraint ac : activity
.getActivityAuthorizationConstraints()) {
if (selectedNtk != null)
break;
for (Permission p : ac.getPermissions()) {
if (selectedNtk != null)
break;
if (p instanceof NeedToKnow) {
NeedToKnow candidateNtk = (NeedToKnow) p;
for (Action a : p.getActions()) {
if (selectedNtk != null)
break;
if (a instanceof ItemAwareElementAction) {
ItemAwareElementAction iaea = (ItemAwareElementAction) a;
if (iaea.getActionName()
.equalsIgnoreCase(
actionCombo
.getText())) {
// action is the same
if (iaea instanceof AtomicItemAwareElementAction) {
// check directly
if (iaea.getItemAwareElement() == null)
continue;
if (actionCombo
.getText()
.equalsIgnoreCase(
"read")
&& iaea.getItemAwareElement() instanceof DataInput
&& iaea.getItemAwareElement()
.getId()
.equals(NeedToKnowUtil.ID_PREFIX_INPUT
+ processVariableCombo
.getText())) {
selectedNtk = candidateNtk;
break;
} else if (actionCombo
.getText()
.equalsIgnoreCase(
"write")
&& iaea.getItemAwareElement() instanceof DataOutput
&& iaea.getItemAwareElement()
.getId()
.equals(NeedToKnowUtil.ID_PREFIX_OUTPUT
+ processVariableCombo
.getText())) {
selectedNtk = candidateNtk;
break;
}
} else {
// check subactions
boolean containsRead = false;
boolean containsWrite = false;
for (ItemAwareElementAction inner_iaea : ((CompositeItemAwareElementAction) iaea)
.getItemAwareElementActions()) {
if (inner_iaea instanceof AtomicActivityAction) {
if (containsRead
&& containsWrite)
break;
if (inner_iaea
.getItemAwareElement() == null)
continue;
if (inner_iaea
.getItemAwareElement() instanceof DataInput
&& inner_iaea
.getItemAwareElement()
.getId()
.equals(NeedToKnowUtil.ID_PREFIX_INPUT
+ processVariableCombo
.getText())) {
containsRead = true;
}
if (inner_iaea
.getItemAwareElement() instanceof DataOutput
&& inner_iaea
.getItemAwareElement()
.getId()
.equals(NeedToKnowUtil.ID_PREFIX_OUTPUT
+ processVariableCombo
.getText())) {
containsWrite = true;
}
}
}
if (containsRead
&& containsWrite) {
selectedNtk = candidateNtk;
break;
}
}
}
}
}
}
}
}
if (selectedNtk == null) {
// combination not present, create
selectedNtk = Securebpmn2Factory.eINSTANCE
.createNeedToKnow();
selectedNtk.setId(UUID.randomUUID().toString());
selectedNtk.setPName("Perm-" + activity.getId()
+ "-" + processVariableCombo.getText()
+ "-" + actionCombo.getText());
Role targetRole = null;
for (Role role : SecurityUtil
.getRoles(getDiagram())) {
if (role.getName().equalsIgnoreCase(
roleCombo.getText())) {
targetRole = role;
break;
}
}
if (!getDiagram().eResource().getContents()
.contains(targetRole)
&& targetRole != null) {
getDiagram().eResource().getContents()
.add(targetRole);
}
selectedNtk.getRoles().add(targetRole);
getDiagram().eResource().getContents()
.add(selectedNtk);
ActivityAuthorizationConstraint activityAC = Securebpmn2Factory.eINSTANCE
.createActivityAuthorizationConstraint();
activityAC.setId(UUID.randomUUID().toString());
activityAC.getPermissions().add(selectedNtk);
activityAC.getActivities().add(activity);
getDiagram().eResource().getContents()
.add(activityAC);
if (actionCombo.getText().equals("read/write")) {
// create composite action
AtomicItemAwareElementAction readAction = Securebpmn2Factory.eINSTANCE
.createAtomicItemAwareElementAction();
readAction.setId(UUID.randomUUID()
.toString());
readAction.setActionName("read");
ItemAwareElement readElement = findItemAwareElement(
processVariableCombo.getText(),
false);
if (readElement != null) {
readAction
.setItemAwareElement(readElement);
} else {
System.err
.println("[SCVM-BPMN] ItemAwareElement for variable "
+ processVariableCombo
.getText()
+ "/read does not exist!");
}
getDiagram().eResource().getContents()
.add(readAction);
AtomicItemAwareElementAction writeAction = Securebpmn2Factory.eINSTANCE
.createAtomicItemAwareElementAction();
writeAction.setId(UUID.randomUUID()
.toString());
writeAction.setActionName("write");
ItemAwareElement writeElement = findItemAwareElement(
processVariableCombo.getText(),
true);
if (writeElement != null) {
writeAction
.setItemAwareElement(writeElement);
} else {
System.err
.println("[SCVM-BPMN] ItemAwareElement for variable "
+ processVariableCombo
.getText()
+ "/write does not exist!");
}
getDiagram().eResource().getContents()
.add(writeAction);
CompositeItemAwareElementAction rwAction = Securebpmn2Factory.eINSTANCE
.createCompositeItemAwareElementAction();
rwAction.setId(UUID.randomUUID().toString());
rwAction.setActionName("read/write");
rwAction.getItemAwareElementActions().add(
readAction);
rwAction.getItemAwareElementActions().add(
writeAction);
rwAction.getPermissions().add(selectedNtk);
getDiagram().eResource().getContents()
.add(rwAction);
} else {
// create atomic action
AtomicItemAwareElementAction iaeAction = Securebpmn2Factory.eINSTANCE
.createAtomicItemAwareElementAction();
iaeAction.setId(UUID.randomUUID()
.toString());
iaeAction.setActionName(actionCombo
.getText());
iaeAction.getPermissions().add(selectedNtk);
// add ItemAwareElement
ItemAwareElement iaElement = findItemAwareElement(
processVariableCombo.getText(),
actionCombo.getText()
.equals("read") ? false
: true);
if (iaElement != null) {
iaeAction
.setItemAwareElement(iaElement);
} else {
System.err
.println("[SCVM-BPMN] ItemAwareElement for variable "
+ processVariableCombo
.getText()
+ "/"
+ actionCombo.getText()
+ " does not exist!");
}
getDiagram().eResource().getContents()
.add(iaeAction);
}
} else {
// update existing ntk: add role
Role targetRole = null;
for (Role role : SecurityUtil
.getRoles(getDiagram())) {
if (role.getName().equalsIgnoreCase(
roleCombo.getText())) {
targetRole = role;
break;
}
}
if (!getDiagram().eResource().getContents()
.contains(targetRole)
&& targetRole != null) {
getDiagram().eResource().getContents()
.add(targetRole);
}
selectedNtk.getRoles().add(targetRole);
}
updateTable(activity);
}
}
}, editingDomain, "Adding NtK Permission");
}
}
}
/**
* Helper method for retrieving the ItemAwareElement, in particular the
* DataInput or DataOutput element, for a given process variable name and
* the type of access (read or write).
*
* @param processVariable
* The name of the process variable.
* @param isWriteable
* The type of access to the variable. true for write access,
* false for read access.
* @return The ItemAwareElement corresponding to the process variable
* access.
*/
private ItemAwareElement findItemAwareElement(String processVariable,
boolean isWriteable) {
for (EObject o : getDiagram().eResource().getContents()) {
if (o instanceof DataInput && !isWriteable) {
final DataInput in = (DataInput) o;
if (in.getId().equals(
NeedToKnowUtil.ID_PREFIX_INPUT + processVariable))
return in;
} else if (o instanceof DataOutput && isWriteable) {
final DataOutput out = (DataOutput) o;
if (out.getId().equals(
NeedToKnowUtil.ID_PREFIX_OUTPUT + processVariable))
return out;
}
}
return null;
}
/**
* Contains the code that is executed when the "remove" Button is pressed.
* In particular, it removes the NeedToKnow Permission from the Diagram.
*/
private void removePressed() {
PictogramElement pe = getSelectedPictogramElement();
if (pe != null) {
Object bo = Graphiti.getLinkService()
.getBusinessObjectForLinkedPictogramElement(pe);
if (bo instanceof Activity) {
DiagramEditor diagramEditor = (DiagramEditor) getDiagramEditor();
TransactionalEditingDomain editingDomain = diagramEditor
.getEditingDomain();
ActivitiUiUtil.runModelChange(new Runnable() {
public void run() {
Object bobj = Graphiti.getLinkService()
.getBusinessObjectForLinkedPictogramElement(
getSelectedPictogramElement());
if (bobj == null) {
return;
} else {
// remove the selected
// permission/activitiyAC/IAEAction
Activity activity = (Activity) bobj;
for (TableItem tItem : table.getItems()) {
List<ActivityAuthorizationConstraint> removeList = new ArrayList<ActivityAuthorizationConstraint>();
if (tItem.getChecked()) {
for (ActivityAuthorizationConstraint actAC : activity
.getActivityAuthorizationConstraints()) {
if (actAC.getPermissions().size() > 0) {
Permission p = actAC
.getPermissions().get(0);
if (p.getPName().equals(
tItem.getText(1))) {
for (Action a : p.getActions()) {
getDiagram().eResource()
.getContents()
.remove(a);
}
p.getActions().clear();
p.getRoles().clear();
p.getAuthorizationConstraints()
.clear();
getDiagram().eResource()
.getContents()
.remove(p);
removeList.add(actAC);
getDiagram().eResource()
.getContents()
.remove(actAC);
}
}
}
}
for (ActivityAuthorizationConstraint actAC : removeList) {
activity.getActivityAuthorizationConstraints()
.remove(actAC);
}
}
updateTable(activity);
}
}
}, editingDomain, "Removing NtK Permission");
}
}
}
/**
* Creates the table entries for a given activity.
*
* @param activity
* The activity for which the entries should be generated.
*/
private void updateTable(Activity activity) {
table.removeAll();
for (ActivityAuthorizationConstraint aac : activity
.getActivityAuthorizationConstraints()) {
if (aac.getPermissions().size() > 0
&& aac.getPermissions().get(0) instanceof NeedToKnow) {
Permission p = aac.getPermissions().get(0);
String[] nameParts = p.getPName().split("-");
TableItem ti = new TableItem(table, SWT.NONE);
String[] itemText = { "", p.getPName(), nameParts[2],
nameParts[3], "" };
for (Role r : p.getRoles()) {
if (itemText[4].length() == 0) {
itemText[4] += r.getName();
} else {
itemText[4] += ", " + r.getName();
}
}
ti.setText(itemText);
}
}
selectionChanged();
}
}