Skip to content

Commit

Permalink
Feat/dfd trust boundary (#25)
Browse files Browse the repository at this point in the history
* DFD: added a draggable & resizable trust boundary node type (yet stateless)

* DFD: intruduces trust boundaries (coded the foundation)
  • Loading branch information
idumitru-cds authored Nov 3, 2023
1 parent 21ccad8 commit 7da8b11
Show file tree
Hide file tree
Showing 17 changed files with 512 additions and 41 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"react-draggable": "^4.4.6",
"react-markdown": "*",
"react-resize-observer": "^1.1.1",
"react-rnd": "^10.4.1",
"react-router-dom": "*",
"react-scripts": "5.0.1",
"react-simply-carousel": "*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export class CanvasWrapper extends React.Component<ICanvasWrapperProps, IState>
} = this.state;

function handleDrag(event, data: DraggableData) {
console.log('onDragCanvas', config);
onDragCanvas({ config, event, data });
};

Expand Down Expand Up @@ -133,7 +134,6 @@ export class CanvasWrapper extends React.Component<ICanvasWrapperProps, IState>
onDrop={ (e) => {
const data = JSON.parse(e.dataTransfer.getData(REACT_FLOW_CHART));
if (data) {
console.log('dropped data: ', data);
onCanvasDrop({
data,
position: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import {
IOnDragNode, IOnLinkCancel, IOnLinkClick, IOnLinkComplete, IOnLinkMouseEnter,
IOnLinkMouseLeave, IOnLinkMove, IOnLinkStart, IOnNodeClick, IOnNodeDoubleClick, IOnNodeSizeChange, IOnLabelDoubleClick,
IOnPortPositionChange, IPortDefaultProps,
IPortsDefaultProps, ISelectedOrHovered, LinkDefault, LinkWrapper, NodeDefault, NodeInnerDefault, NodeWrapper, PortDefault, PortsDefault,
IPortsDefaultProps, ISelectedOrHovered, LinkDefault, LinkWrapper,
NodeDefault, NodeInnerDefault, NodeWrapper, PortDefault, PortsDefault, IOnTrustBoundaryClick, IOnDragTrustBoundary,
} from '../../';
import { ITrustBoundaryDefaultProps, TrustBoundaryDefault, TrustBoundaryWrapper } from '../TrustBoundary';

export interface IFlowChartCallbacks {
onDragNode: IOnDragNode;
Expand All @@ -42,6 +44,8 @@ export interface IFlowChartCallbacks {
onNodeDoubleClick: IOnNodeDoubleClick;
onNodeSizeChange: IOnNodeSizeChange;
onLabelDoubleClick: IOnLabelDoubleClick;
onTrustBoundaryClick: IOnTrustBoundaryClick;
onDragTrustBoundary: IOnDragTrustBoundary;
};

export interface IFlowChartComponents {
Expand All @@ -52,6 +56,7 @@ export interface IFlowChartComponents {
Port?: React.FunctionComponent<IPortDefaultProps>;
Node?: React.FunctionComponent<INodeDefaultProps>;
Link?: React.FunctionComponent<ILinkDefaultProps>;
TrustBoundary?: React.FunctionComponent<ITrustBoundaryDefaultProps>;
};

export interface IFlowChartProps {
Expand Down Expand Up @@ -101,6 +106,8 @@ export const FlowChart = (props: IFlowChartProps) => {
onNodeDoubleClick,
onNodeSizeChange,
onLabelDoubleClick,
onTrustBoundaryClick,
onDragTrustBoundary,
},
Components: {
CanvasOuter = CanvasOuterDefault,
Expand All @@ -110,13 +117,15 @@ export const FlowChart = (props: IFlowChartProps) => {
Port = PortDefault,
Node = NodeDefault,
Link = LinkDefault,
TrustBoundary = TrustBoundaryDefault,
} = {},
config = {},
} = props;
const { links, nodes, selected, hovered, offset } = chart;
const { links, nodes, trustBoundaries, selected, hovered, offset } = chart;
const canvasCallbacks = { onDragCanvas, onCanvasClick, onDeleteKey, onCanvasDrop };
const linkCallbacks = { onLinkMouseEnter, onLinkMouseLeave, onLinkClick, onLabelDoubleClick };
const nodeCallbacks = { onDragNode, onNodeClick, onNodeSizeChange, onNodeDoubleClick };
const trustBoundaryCallbacks = { onTrustBoundaryClick, onDragTrustBoundary };
const portCallbacks = { onPortPositionChange, onLinkStart, onLinkMove, onLinkComplete, onLinkCancel };

const nodesInView = Object.keys(nodes).filter((nodeId) => {
Expand All @@ -141,6 +150,15 @@ export const FlowChart = (props: IFlowChartProps) => {
);
});

const trustBoundariesInView = Object.keys(trustBoundaries).filter((trustBoundaryId) => {
const defaultTrustBoundarySize = { width: 100, height: 100 };
const { x, y } = trustBoundaries[trustBoundaryId].position;
const size = trustBoundaries[trustBoundaryId].size || defaultTrustBoundarySize;

return x + offset.x + size.width > 0 && x + offset.x < canvasSize.width &&
y + offset.y + size.height > 0 && y + offset.y < canvasSize.height;
});

return (
<CanvasWrapper
config={config}
Expand Down Expand Up @@ -200,6 +218,21 @@ export const FlowChart = (props: IFlowChartProps) => {
);
})
}
{ trustBoundariesInView.map((trustBoundaryId) => {
const isSelected = selected.type === 'trustBoundary' && selected.id === trustBoundaryId;
return (
<TrustBoundaryWrapper
config={config}
key={trustBoundaryId}
Component={TrustBoundary}
trustBoundary={trustBoundaries[trustBoundaryId]}
offset={chart.offset}
isSelected={isSelected}
{...trustBoundaryCallbacks}
/>
);
})
}
</CanvasWrapper>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import * as React from 'react';
import styled from 'styled-components';
import mapValues from '../../container/utils/mapValues';
import { FlowChart, IChart, IConfig, IFlowChartComponents, IOnNodeClick, IOnNodeDoubleClick, IOnLabelDoubleClick, IOnLinkClick } from '../..';
import { FlowChart, IChart, IConfig, IFlowChartComponents, IOnNodeClick, IOnNodeDoubleClick, IOnLabelDoubleClick, IOnLinkClick, IOnTrustBoundaryClick, IOnDragTrustBoundary } from '../..';
import {
onDragNode, onDragCanvas, onLinkStart, onLinkMove, onLinkComplete,
onLinkCancel, onLinkMouseEnter, onLinkMouseLeave,
Expand Down Expand Up @@ -133,6 +133,7 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
...props.initialValue,
preNodes: Object.keys(props.initialValue.nodes),
preLinks: Object.keys(props.initialValue.links),
preTrustBoundaries: Object.keys(props.initialValue.trustBoundaries),
isModelShow: false,
showModelName: '',
nodeName: '',
Expand All @@ -146,10 +147,17 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
securityFeatures: [],
threats: [],
nodeRoleOption: '',
trustBoundaryName: '',
trustBoundaryId: '',
trustBoundaryDescription: '',
trustBoundaryOutOfScope: false,
trustBoundaryOutOfScopeReason: '',
linkLabel: '',
newNodeId: '',
clickNodeId: '',
newLinkId: '',
newTrustBoundaryId: '',
clickNodeId: '',
clickTrustBoundaryId: '',
clickLinkId: '',
modelOption: 'addNode',
alertMessageInfo: '',
Expand Down Expand Up @@ -247,6 +255,47 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
}
};

onTrustBoundaryClick: IOnTrustBoundaryClick = ({ trustBoundaryId }) => {
console.log('trustBoundaryId', trustBoundaryId);
let selectedTB = this.state.trustBoundaries[trustBoundaryId];
let tbProperties = !!selectedTB.properties ? selectedTB.properties : this.emptyProperties;
if (!selectedTB.properties) {
this.state.trustBoundaries[trustBoundaryId].properties = this.emptyProperties;
tbProperties = this.emptyProperties;
}
this.setState({
selected: {
type: 'trustBoundary',
id: trustBoundaryId,
},
linkLabel: '',
nodeName: '',
nodeId: '',
nodeDescription: '',
nodeOutOfScope: false,
nodeOutOfScopeReason: '',
clickTrustBoundaryId: trustBoundaryId,
clickNodeId: '',
clickLinkId: '',
});
if (this.filterStatementsCallbaack) {
this.filterStatementsCallbaack(
'', //filterSTRIDE
trustBoundaryId,
'trustBoundary', //selectedNode.type
tbProperties.name,
tbProperties.description,
tbProperties.outOfScope,
tbProperties.outOfScopeReason,
tbProperties.tags,
[], //tbProperties.dataFeatures
[], //tbProperties.techFeatures
[], //tbProperties.securityFeatures
[], //tbProperties.threats
);
};
};

onNodeDoubleClick: IOnNodeDoubleClick = ({ nodeId }) => {
let clickNodeProperties = this.state.nodes[nodeId].properties;
clickNodeProperties = !!clickNodeProperties ? clickNodeProperties : {};
Expand Down Expand Up @@ -332,6 +381,10 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
});
};

onDragTrustBoundary: IOnDragTrustBoundary = ({ config }) => {
console.log('onDragTrustBoundary', config);
};

private stateActions = mapValues({
onDragNode,
onDragCanvas,
Expand All @@ -350,6 +403,8 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
onCanvasDrop,
onNodeDoubleClick: this.onNodeDoubleClick,
onLabelDoubleClick: this.onLabelDoubleClick,
onTrustBoundaryClick: this.onTrustBoundaryClick,
onDragTrustBoundary: this.onDragTrustBoundary,
}, (func: any) => (...args: any) => this.setState(func(...args)));

hideModel = () => {
Expand Down Expand Up @@ -411,6 +466,12 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
});
};

handleTrustBoundaryNameInput = (e: any) => {
this.setState({
trustBoundaryName: e.currentTarget.value,
});
};

setNodeInfo = (): boolean => {
let nodeKey = '';
for (var key of Object.keys(this.state.nodes)) {
Expand Down Expand Up @@ -458,6 +519,33 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
});
};

setTrustBoundaryInfo = (): boolean => {
let trustBoundaryKey = '';
for (var key of Object.keys(this.state.trustBoundaries)) {
if (trustBoundaryKey !== '' && this.state.trustBoundaries[key].position === this.state.trustBoundaries[trustBoundaryKey].position) {
delete this.state.trustBoundaries[key];
}
trustBoundaryKey = key;
}

if (this.state.trustBoundaryName.trim() === '') {
this.warningMessage('Please input the trust boundary name!');
return false;
}
let _trustBoundaries = this.state.trustBoundaries;
let _trustBoundaryId = this.state.modelOption === 'addTrustBoundary' ? this.state.newTrustBoundaryId : this.state.clickTrustBoundaryId;
_trustBoundaries[_trustBoundaryId].properties = {
name: !!this.state.trustBoundaryName ? this.state.trustBoundaryName : '',
Id: this.state.trustBoundaryId,
description: this.state.trustBoundaryDescription,
};
this.setState({
trustBoundaries: _trustBoundaries,
isModelShow: false,
});
return true;
};

handleNodeRoleChange = (value: string): void => {
this.setState({
nodeRoleOption: value,
Expand Down Expand Up @@ -519,6 +607,25 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
);
};

renderAddNewTrustBoundaryModel = () => {
return (
<ModelBox className={this.state.isModelShow ? '' : 'hide'}>
<ModelContent>
<div className="InputBox">
<InputBox>
<label>Name:</label>
<Input onChange={this.handleTrustBoundaryNameInput} value={this.state.trustBoundaryName} type="text" />
</InputBox>
</div>
<ButtonBox>
<Button onClick={this.setTrustBoundaryInfo} type="primary">Confirm</Button>
<Button onClick={this.hideModel} type="cancel">Cancel</Button>
</ButtonBox>
</ModelContent>
</ModelBox>
);
};

warningMessage = (content: string): void => {
this.setState(() => ({
alertMessageInfo: content,
Expand Down Expand Up @@ -548,6 +655,12 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
delete node.position.node;
}
}
for (var key of Object.keys(flowData.trustBoundaries)) {
let trustBoundary = flowData.trustBoundaries[key];
if (trustBoundary.position && trustBoundary.position.node) {
delete trustBoundary.position.node;
}
}
if (!!this.props.getWorkFlowChartValue) {
this.props.getWorkFlowChartValue(flowData);
}
Expand Down Expand Up @@ -598,6 +711,26 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
preNodes: Object.keys(preState.nodes),
}));
}

if (Object.keys(this.state.trustBoundaries).length > this.state.preTrustBoundaries.length) {
let preTrustBoundaries = this.state.preTrustBoundaries;
let currentTrustBoundaries = Object.keys(this.state.trustBoundaries);
let newTrustBoundary = currentTrustBoundaries.filter(trustBoundary => !preTrustBoundaries.includes(trustBoundary));

this.setState({
isModelShow: true,
showModelName: 'newTrustBoundaryModel',
modelOption: 'addTrustBoundary',
newTrustBoundaryId: newTrustBoundary[0],
trustBoundaryName: '',
trustBoundaryId: '',
});
}
if (Object.keys(this.state.trustBoundaries).length != this.state.preTrustBoundaries.length) {
this.setState((preState) => ({
preTrustBoundaries: Object.keys(preState.trustBoundaries),
}));
}
}

public render () {
Expand All @@ -607,6 +740,7 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
<React.Fragment>
{ this.state.showModelName === 'newNodeModel' ? this.renderAddNewNodeModel() : ''}
{ this.state.showModelName === 'newLinkModel' ? this.renderAddNewLinkModel() : ''}
{ this.state.showModelName === 'newTrustBoundaryModel' ? this.renderAddNewTrustBoundaryModel() : ''}
{ this.renderAlertMessage() }
<FlowChart
chart={this.state}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { INode, IPosition } from '../../../';

export const getLinkPosition = (node: INode, portId: string): IPosition => {
const port = node.ports[portId];
console.log('port ', port);
let nodeWidth = (!!node && !!node.size) ? node.size.width : 0;
let nodeHeight = (!!node && !!node.size) ? node.size.height : 0;
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ export const NodeWrapper = ({
const onClick = React.useCallback(
(e: React.MouseEvent) => {
if (!config.readonly || config.selectable) {
e.stopPropagation();
onNodeClick({ config, nodeId: node.id });
e.stopPropagation();
}
},
[config, node.id, onNodeClick],
Expand Down
Loading

0 comments on commit 7da8b11

Please sign in to comment.