Skip to content

Commit 7da8b11

Browse files
authored
Feat/dfd trust boundary (#25)
* DFD: added a draggable & resizable trust boundary node type (yet stateless) * DFD: intruduces trust boundaries (coded the foundation)
1 parent 21ccad8 commit 7da8b11

File tree

17 files changed

+512
-41
lines changed

17 files changed

+512
-41
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"react-draggable": "^4.4.6",
7272
"react-markdown": "*",
7373
"react-resize-observer": "^1.1.1",
74+
"react-rnd": "^10.4.1",
7475
"react-router-dom": "*",
7576
"react-scripts": "5.0.1",
7677
"react-simply-carousel": "*",

src/components/generic/DiagramCanvas/flowDiagram/components/Canvas/Canvas.wrapper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ export class CanvasWrapper extends React.Component<ICanvasWrapperProps, IState>
105105
} = this.state;
106106

107107
function handleDrag(event, data: DraggableData) {
108+
console.log('onDragCanvas', config);
108109
onDragCanvas({ config, event, data });
109110
};
110111

@@ -133,7 +134,6 @@ export class CanvasWrapper extends React.Component<ICanvasWrapperProps, IState>
133134
onDrop={ (e) => {
134135
const data = JSON.parse(e.dataTransfer.getData(REACT_FLOW_CHART));
135136
if (data) {
136-
console.log('dropped data: ', data);
137137
onCanvasDrop({
138138
data,
139139
position: {

src/components/generic/DiagramCanvas/flowDiagram/components/FlowChart/FlowChart.tsx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import {
2121
IOnDragNode, IOnLinkCancel, IOnLinkClick, IOnLinkComplete, IOnLinkMouseEnter,
2222
IOnLinkMouseLeave, IOnLinkMove, IOnLinkStart, IOnNodeClick, IOnNodeDoubleClick, IOnNodeSizeChange, IOnLabelDoubleClick,
2323
IOnPortPositionChange, IPortDefaultProps,
24-
IPortsDefaultProps, ISelectedOrHovered, LinkDefault, LinkWrapper, NodeDefault, NodeInnerDefault, NodeWrapper, PortDefault, PortsDefault,
24+
IPortsDefaultProps, ISelectedOrHovered, LinkDefault, LinkWrapper,
25+
NodeDefault, NodeInnerDefault, NodeWrapper, PortDefault, PortsDefault, IOnTrustBoundaryClick, IOnDragTrustBoundary,
2526
} from '../../';
27+
import { ITrustBoundaryDefaultProps, TrustBoundaryDefault, TrustBoundaryWrapper } from '../TrustBoundary';
2628

2729
export interface IFlowChartCallbacks {
2830
onDragNode: IOnDragNode;
@@ -42,6 +44,8 @@ export interface IFlowChartCallbacks {
4244
onNodeDoubleClick: IOnNodeDoubleClick;
4345
onNodeSizeChange: IOnNodeSizeChange;
4446
onLabelDoubleClick: IOnLabelDoubleClick;
47+
onTrustBoundaryClick: IOnTrustBoundaryClick;
48+
onDragTrustBoundary: IOnDragTrustBoundary;
4549
};
4650

4751
export interface IFlowChartComponents {
@@ -52,6 +56,7 @@ export interface IFlowChartComponents {
5256
Port?: React.FunctionComponent<IPortDefaultProps>;
5357
Node?: React.FunctionComponent<INodeDefaultProps>;
5458
Link?: React.FunctionComponent<ILinkDefaultProps>;
59+
TrustBoundary?: React.FunctionComponent<ITrustBoundaryDefaultProps>;
5560
};
5661

5762
export interface IFlowChartProps {
@@ -101,6 +106,8 @@ export const FlowChart = (props: IFlowChartProps) => {
101106
onNodeDoubleClick,
102107
onNodeSizeChange,
103108
onLabelDoubleClick,
109+
onTrustBoundaryClick,
110+
onDragTrustBoundary,
104111
},
105112
Components: {
106113
CanvasOuter = CanvasOuterDefault,
@@ -110,13 +117,15 @@ export const FlowChart = (props: IFlowChartProps) => {
110117
Port = PortDefault,
111118
Node = NodeDefault,
112119
Link = LinkDefault,
120+
TrustBoundary = TrustBoundaryDefault,
113121
} = {},
114122
config = {},
115123
} = props;
116-
const { links, nodes, selected, hovered, offset } = chart;
124+
const { links, nodes, trustBoundaries, selected, hovered, offset } = chart;
117125
const canvasCallbacks = { onDragCanvas, onCanvasClick, onDeleteKey, onCanvasDrop };
118126
const linkCallbacks = { onLinkMouseEnter, onLinkMouseLeave, onLinkClick, onLabelDoubleClick };
119127
const nodeCallbacks = { onDragNode, onNodeClick, onNodeSizeChange, onNodeDoubleClick };
128+
const trustBoundaryCallbacks = { onTrustBoundaryClick, onDragTrustBoundary };
120129
const portCallbacks = { onPortPositionChange, onLinkStart, onLinkMove, onLinkComplete, onLinkCancel };
121130

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

153+
const trustBoundariesInView = Object.keys(trustBoundaries).filter((trustBoundaryId) => {
154+
const defaultTrustBoundarySize = { width: 100, height: 100 };
155+
const { x, y } = trustBoundaries[trustBoundaryId].position;
156+
const size = trustBoundaries[trustBoundaryId].size || defaultTrustBoundarySize;
157+
158+
return x + offset.x + size.width > 0 && x + offset.x < canvasSize.width &&
159+
y + offset.y + size.height > 0 && y + offset.y < canvasSize.height;
160+
});
161+
144162
return (
145163
<CanvasWrapper
146164
config={config}
@@ -200,6 +218,21 @@ export const FlowChart = (props: IFlowChartProps) => {
200218
);
201219
})
202220
}
221+
{ trustBoundariesInView.map((trustBoundaryId) => {
222+
const isSelected = selected.type === 'trustBoundary' && selected.id === trustBoundaryId;
223+
return (
224+
<TrustBoundaryWrapper
225+
config={config}
226+
key={trustBoundaryId}
227+
Component={TrustBoundary}
228+
trustBoundary={trustBoundaries[trustBoundaryId]}
229+
offset={chart.offset}
230+
isSelected={isSelected}
231+
{...trustBoundaryCallbacks}
232+
/>
233+
);
234+
})
235+
}
203236
</CanvasWrapper>
204237
);
205238
};

src/components/generic/DiagramCanvas/flowDiagram/components/FlowChart/FlowChartWithState.tsx

Lines changed: 136 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import * as React from 'react';
1717
import styled from 'styled-components';
1818
import mapValues from '../../container/utils/mapValues';
19-
import { FlowChart, IChart, IConfig, IFlowChartComponents, IOnNodeClick, IOnNodeDoubleClick, IOnLabelDoubleClick, IOnLinkClick } from '../..';
19+
import { FlowChart, IChart, IConfig, IFlowChartComponents, IOnNodeClick, IOnNodeDoubleClick, IOnLabelDoubleClick, IOnLinkClick, IOnTrustBoundaryClick, IOnDragTrustBoundary } from '../..';
2020
import {
2121
onDragNode, onDragCanvas, onLinkStart, onLinkMove, onLinkComplete,
2222
onLinkCancel, onLinkMouseEnter, onLinkMouseLeave,
@@ -133,6 +133,7 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
133133
...props.initialValue,
134134
preNodes: Object.keys(props.initialValue.nodes),
135135
preLinks: Object.keys(props.initialValue.links),
136+
preTrustBoundaries: Object.keys(props.initialValue.trustBoundaries),
136137
isModelShow: false,
137138
showModelName: '',
138139
nodeName: '',
@@ -146,10 +147,17 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
146147
securityFeatures: [],
147148
threats: [],
148149
nodeRoleOption: '',
150+
trustBoundaryName: '',
151+
trustBoundaryId: '',
152+
trustBoundaryDescription: '',
153+
trustBoundaryOutOfScope: false,
154+
trustBoundaryOutOfScopeReason: '',
149155
linkLabel: '',
150156
newNodeId: '',
151-
clickNodeId: '',
152157
newLinkId: '',
158+
newTrustBoundaryId: '',
159+
clickNodeId: '',
160+
clickTrustBoundaryId: '',
153161
clickLinkId: '',
154162
modelOption: 'addNode',
155163
alertMessageInfo: '',
@@ -247,6 +255,47 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
247255
}
248256
};
249257

258+
onTrustBoundaryClick: IOnTrustBoundaryClick = ({ trustBoundaryId }) => {
259+
console.log('trustBoundaryId', trustBoundaryId);
260+
let selectedTB = this.state.trustBoundaries[trustBoundaryId];
261+
let tbProperties = !!selectedTB.properties ? selectedTB.properties : this.emptyProperties;
262+
if (!selectedTB.properties) {
263+
this.state.trustBoundaries[trustBoundaryId].properties = this.emptyProperties;
264+
tbProperties = this.emptyProperties;
265+
}
266+
this.setState({
267+
selected: {
268+
type: 'trustBoundary',
269+
id: trustBoundaryId,
270+
},
271+
linkLabel: '',
272+
nodeName: '',
273+
nodeId: '',
274+
nodeDescription: '',
275+
nodeOutOfScope: false,
276+
nodeOutOfScopeReason: '',
277+
clickTrustBoundaryId: trustBoundaryId,
278+
clickNodeId: '',
279+
clickLinkId: '',
280+
});
281+
if (this.filterStatementsCallbaack) {
282+
this.filterStatementsCallbaack(
283+
'', //filterSTRIDE
284+
trustBoundaryId,
285+
'trustBoundary', //selectedNode.type
286+
tbProperties.name,
287+
tbProperties.description,
288+
tbProperties.outOfScope,
289+
tbProperties.outOfScopeReason,
290+
tbProperties.tags,
291+
[], //tbProperties.dataFeatures
292+
[], //tbProperties.techFeatures
293+
[], //tbProperties.securityFeatures
294+
[], //tbProperties.threats
295+
);
296+
};
297+
};
298+
250299
onNodeDoubleClick: IOnNodeDoubleClick = ({ nodeId }) => {
251300
let clickNodeProperties = this.state.nodes[nodeId].properties;
252301
clickNodeProperties = !!clickNodeProperties ? clickNodeProperties : {};
@@ -332,6 +381,10 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
332381
});
333382
};
334383

384+
onDragTrustBoundary: IOnDragTrustBoundary = ({ config }) => {
385+
console.log('onDragTrustBoundary', config);
386+
};
387+
335388
private stateActions = mapValues({
336389
onDragNode,
337390
onDragCanvas,
@@ -350,6 +403,8 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
350403
onCanvasDrop,
351404
onNodeDoubleClick: this.onNodeDoubleClick,
352405
onLabelDoubleClick: this.onLabelDoubleClick,
406+
onTrustBoundaryClick: this.onTrustBoundaryClick,
407+
onDragTrustBoundary: this.onDragTrustBoundary,
353408
}, (func: any) => (...args: any) => this.setState(func(...args)));
354409

355410
hideModel = () => {
@@ -411,6 +466,12 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
411466
});
412467
};
413468

469+
handleTrustBoundaryNameInput = (e: any) => {
470+
this.setState({
471+
trustBoundaryName: e.currentTarget.value,
472+
});
473+
};
474+
414475
setNodeInfo = (): boolean => {
415476
let nodeKey = '';
416477
for (var key of Object.keys(this.state.nodes)) {
@@ -458,6 +519,33 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
458519
});
459520
};
460521

522+
setTrustBoundaryInfo = (): boolean => {
523+
let trustBoundaryKey = '';
524+
for (var key of Object.keys(this.state.trustBoundaries)) {
525+
if (trustBoundaryKey !== '' && this.state.trustBoundaries[key].position === this.state.trustBoundaries[trustBoundaryKey].position) {
526+
delete this.state.trustBoundaries[key];
527+
}
528+
trustBoundaryKey = key;
529+
}
530+
531+
if (this.state.trustBoundaryName.trim() === '') {
532+
this.warningMessage('Please input the trust boundary name!');
533+
return false;
534+
}
535+
let _trustBoundaries = this.state.trustBoundaries;
536+
let _trustBoundaryId = this.state.modelOption === 'addTrustBoundary' ? this.state.newTrustBoundaryId : this.state.clickTrustBoundaryId;
537+
_trustBoundaries[_trustBoundaryId].properties = {
538+
name: !!this.state.trustBoundaryName ? this.state.trustBoundaryName : '',
539+
Id: this.state.trustBoundaryId,
540+
description: this.state.trustBoundaryDescription,
541+
};
542+
this.setState({
543+
trustBoundaries: _trustBoundaries,
544+
isModelShow: false,
545+
});
546+
return true;
547+
};
548+
461549
handleNodeRoleChange = (value: string): void => {
462550
this.setState({
463551
nodeRoleOption: value,
@@ -519,6 +607,25 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
519607
);
520608
};
521609

610+
renderAddNewTrustBoundaryModel = () => {
611+
return (
612+
<ModelBox className={this.state.isModelShow ? '' : 'hide'}>
613+
<ModelContent>
614+
<div className="InputBox">
615+
<InputBox>
616+
<label>Name:</label>
617+
<Input onChange={this.handleTrustBoundaryNameInput} value={this.state.trustBoundaryName} type="text" />
618+
</InputBox>
619+
</div>
620+
<ButtonBox>
621+
<Button onClick={this.setTrustBoundaryInfo} type="primary">Confirm</Button>
622+
<Button onClick={this.hideModel} type="cancel">Cancel</Button>
623+
</ButtonBox>
624+
</ModelContent>
625+
</ModelBox>
626+
);
627+
};
628+
522629
warningMessage = (content: string): void => {
523630
this.setState(() => ({
524631
alertMessageInfo: content,
@@ -548,6 +655,12 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
548655
delete node.position.node;
549656
}
550657
}
658+
for (var key of Object.keys(flowData.trustBoundaries)) {
659+
let trustBoundary = flowData.trustBoundaries[key];
660+
if (trustBoundary.position && trustBoundary.position.node) {
661+
delete trustBoundary.position.node;
662+
}
663+
}
551664
if (!!this.props.getWorkFlowChartValue) {
552665
this.props.getWorkFlowChartValue(flowData);
553666
}
@@ -598,6 +711,26 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
598711
preNodes: Object.keys(preState.nodes),
599712
}));
600713
}
714+
715+
if (Object.keys(this.state.trustBoundaries).length > this.state.preTrustBoundaries.length) {
716+
let preTrustBoundaries = this.state.preTrustBoundaries;
717+
let currentTrustBoundaries = Object.keys(this.state.trustBoundaries);
718+
let newTrustBoundary = currentTrustBoundaries.filter(trustBoundary => !preTrustBoundaries.includes(trustBoundary));
719+
720+
this.setState({
721+
isModelShow: true,
722+
showModelName: 'newTrustBoundaryModel',
723+
modelOption: 'addTrustBoundary',
724+
newTrustBoundaryId: newTrustBoundary[0],
725+
trustBoundaryName: '',
726+
trustBoundaryId: '',
727+
});
728+
}
729+
if (Object.keys(this.state.trustBoundaries).length != this.state.preTrustBoundaries.length) {
730+
this.setState((preState) => ({
731+
preTrustBoundaries: Object.keys(preState.trustBoundaries),
732+
}));
733+
}
601734
}
602735

603736
public render () {
@@ -607,6 +740,7 @@ class FlowChartWithState extends React.Component<IFlowChartWithStateProps, IChar
607740
<React.Fragment>
608741
{ this.state.showModelName === 'newNodeModel' ? this.renderAddNewNodeModel() : ''}
609742
{ this.state.showModelName === 'newLinkModel' ? this.renderAddNewLinkModel() : ''}
743+
{ this.state.showModelName === 'newTrustBoundaryModel' ? this.renderAddNewTrustBoundaryModel() : ''}
610744
{ this.renderAlertMessage() }
611745
<FlowChart
612746
chart={this.state}

src/components/generic/DiagramCanvas/flowDiagram/components/Link/utils/getLinkPosition.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { INode, IPosition } from '../../../';
1717

1818
export const getLinkPosition = (node: INode, portId: string): IPosition => {
1919
const port = node.ports[portId];
20-
console.log('port ', port);
2120
let nodeWidth = (!!node && !!node.size) ? node.size.width : 0;
2221
let nodeHeight = (!!node && !!node.size) ? node.size.height : 0;
2322
return {

src/components/generic/DiagramCanvas/flowDiagram/components/Node/Node.wrapper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ export const NodeWrapper = ({
7878
const onClick = React.useCallback(
7979
(e: React.MouseEvent) => {
8080
if (!config.readonly || config.selectable) {
81-
e.stopPropagation();
8281
onNodeClick({ config, nodeId: node.id });
82+
e.stopPropagation();
8383
}
8484
},
8585
[config, node.id, onNodeClick],

0 commit comments

Comments
 (0)