diff --git a/src/main/webapp/app/modeller/components/Modeller.js b/src/main/webapp/app/modeller/components/Modeller.js index a7d738a2..d984214b 100644 --- a/src/main/webapp/app/modeller/components/Modeller.js +++ b/src/main/webapp/app/modeller/components/Modeller.js @@ -60,6 +60,8 @@ class Modeller extends React.Component { this.getAssetTypeById = this.getAssetTypeById.bind(this); this.isAssetDisplayed = this.isAssetDisplayed.bind(this); this.isRelationDisplayed = this.isRelationDisplayed.bind(this); + this.getValidStartpoints = this.getValidStartpoints.bind(this); + this.getValidEndpoints = this.getValidEndpoints.bind(this); this.getLink = this.getLink.bind(this); this.getMisbehaviour = this.getMisbehaviour.bind(this); this.getTwasForMisbehaviourSet = this.getTwasForMisbehaviourSet.bind(this); @@ -307,6 +309,8 @@ class Modeller extends React.Component { loading={this.props.loading} isAssetDisplayed={this.isAssetDisplayed} isRelationDisplayed={this.isRelationDisplayed} + linkFromTypes={this.getValidStartpoints} + linkToTypes={this.getValidEndpoints} selectedLayers={this.props.selectedLayers} selectedAsset={this.props.selectedAsset} selectedThreat={this.props.selectedThreat} @@ -340,6 +344,8 @@ class Modeller extends React.Component { expanded={this.props.expanded} filters={this.props.filters} loading={this.props.loading} + linkFromTypes={this.getValidStartpoints} + linkToTypes={this.getValidEndpoints} getAssetType={this.getAssetType} getAssetsForType={this.getAssetsForType} getLink={this.getLink} @@ -912,6 +918,82 @@ class Modeller extends React.Component { return false;*/ } + /** + * Get all assets from which a connection could be made + * + * @param {type} assetType the type of the asset for which to check for incoming connections + * @returns {unresolved} the valid startpoints in the asset model on the canvas + */ + getValidStartpoints(assetType) { + let self = this; + let linkTypes = this.props.model["palette"]["links"][assetType]; + let validStartpoints = {}; + + if (linkTypes === undefined || linkTypes["linksTo"] === undefined) { + return validStartpoints; + } + + //iterate over all allowed links + for (let conn of linkTypes["linksTo"]) { + + //this is a new relationship type + if (validStartpoints[conn["type"]] === undefined) { + validStartpoints[conn["type"]] = {label: conn["label"], comment: conn["comment"], + assets: []}; + + //check all existing assets to see if they're of the allowed type + for (let asset of self.props.model["assets"]) { + //if they are add them to the list of allowed endpoints + if (conn["options"].indexOf(asset["type"]) >= 0) { + validStartpoints[conn["type"]]["assets"].push(asset["id"]); + } + } + } else { + console.warn("duplicate entry for for connection type " + conn["type"] + ", ignoring"); + } + } + + return validStartpoints; + } + + /** + * Get all assets to which a connection could be made + * + * @param {type} assetType the type of the asset for which to check for outgoing connections + * @returns {unresolved} the valid endpoints in the asset model on the canvas + */ + getValidEndpoints(assetType) { + let self = this; + let linkTypes = this.props.model["palette"]["links"][assetType]; + let validEndpoints = {}; + + if (linkTypes === undefined || linkTypes["linksFrom"] === undefined) { + return validEndpoints; + } + + //iterate over all allowed links + for (let conn of linkTypes["linksFrom"]) { + + //this is a new relationship type + if (validEndpoints[conn["type"]] === undefined) { + validEndpoints[conn["type"]] = {label: conn["label"], comment: conn["comment"], + assets: []}; + + //check all existing assets to see if they're of the allowed type + for (let asset of self.props.model["assets"]) { + //if they are add them to the list of allowed endpoints + if (conn["options"].indexOf(asset["type"]) >= 0) { + validEndpoints[conn["type"]]["assets"].push(asset["id"]); + } + } + } else { + console.warn("duplicate entry for for connection type " + conn["type"] + ", ignoring"); + } + } + + return validEndpoints; + } + getLink(assetType, relType, direction) { let link = undefined; //console.log("getLink for ", assetType, relType, direction); diff --git a/src/main/webapp/app/modeller/components/canvas/Canvas.js b/src/main/webapp/app/modeller/components/canvas/Canvas.js index 80e5b4e8..2222cd50 100644 --- a/src/main/webapp/app/modeller/components/canvas/Canvas.js +++ b/src/main/webapp/app/modeller/components/canvas/Canvas.js @@ -110,8 +110,6 @@ class Canvas extends React.Component { this.handleAssetMouseOver = this.handleAssetMouseOver.bind(this); this.handleAssetMouseOut = this.handleAssetMouseOut.bind(this); this.handleRelationClick = this.handleRelationClick.bind(this); - this.getValidStartpoints = this.getValidStartpoints.bind(this); - this.getValidEndpoints = this.getValidEndpoints.bind(this); this.getPaletteLink = this.getPaletteLink.bind(this); this.getPalette = this.getPalette.bind(this); this.isSelectedAsset = this.isSelectedAsset.bind(this); @@ -885,8 +883,8 @@ class Canvas extends React.Component { assetType={assetType} getPalette={this.getPalette} canvasZoom={this.props.canvas.zoom} - linkFromTypes={self.getValidStartpoints} - linkToTypes={self.getValidEndpoints} + linkFromTypes={this.props.linkFromTypes} + linkToTypes={this.props.linkToTypes} loading={this.props.loading["asset"]} handleAssetDrag={ self.handleAssetDrag } handleAssetMouseDown={ self.handleAssetMouseDown } @@ -2189,95 +2187,11 @@ class Canvas extends React.Component { this.canvasContextTriggerVar = c; } - /** - * Get all assets from which a connection could be made - * - * @param {type} assetType the type of the asset for which to check for incoming connections - * @returns {unresolved} the valid startpoints in the asset model on the canvas - */ - getValidStartpoints(assetType) { - var self = this; - //console.log("Finding valid startpoints for asset type " + assetType); - - var linkTypes = this.props.model["palette"]["links"][assetType]; - //console.log(linkTypes["linksTo"]); - - var validStartpoints = {}; - if (linkTypes === undefined || linkTypes["linksTo"] === undefined) { - return validStartpoints; - } - - //iterate over all allowed links - for (var conn of linkTypes["linksTo"]) { - - //this is a new relationship type - if (validStartpoints[conn["type"]] === undefined) { - validStartpoints[conn["type"]] = {label: conn["label"], comment: conn["comment"], - assets: []}; - - //check all existing assets to see if they're of the allowed type - for (var asset of self.props.model["assets"]) { - //if they are add them to the list of allowed endpoints - if (conn["options"].indexOf(asset["type"]) >= 0) { - validStartpoints[conn["type"]]["assets"].push(asset["id"]); - } - } - } else { - console.warn("duplicate entry for for connection type " + conn["type"] + ", ignoring"); - } - } - //console.log("valid startpoints:"); - //console.log(validStartpoints); - return validStartpoints; - } - - /** - * Get all assets to which a connection could be made - * - * @param {type} assetType the type of the asset for which to check for outgoing connections - * @returns {unresolved} the valid endpoints in the asset model on the canvas - */ - getValidEndpoints(assetType) { - var self = this; - //console.log("Finding valid endpoints for asset type " + assetType); - - var linkTypes = this.props.model["palette"]["links"][assetType]; - //console.log(linkTypes["linksFrom"]); - - var validEndpoints = {}; - if (linkTypes === undefined || linkTypes["linksFrom"] === undefined) { - return validEndpoints; - } - - //iterate over all allowed links - for (var conn of linkTypes["linksFrom"]) { - - //this is a new relationship type - if (validEndpoints[conn["type"]] === undefined) { - validEndpoints[conn["type"]] = {label: conn["label"], comment: conn["comment"], - assets: []}; - - //check all existing assets to see if they're of the allowed type - for (var asset of self.props.model["assets"]) { - //if they are add them to the list of allowed endpoints - if (conn["options"].indexOf(asset["type"]) >= 0) { - validEndpoints[conn["type"]]["assets"].push(asset["id"]); - } - } - } else { - console.warn("duplicate entry for for connection type " + conn["type"] + ", ignoring"); - } - } - //console.log("valid endpoints:"); - //console.log(validEndpoints); - return validEndpoints; - } - getPaletteLink(fromAssetType, linkType) { //console.log("getPaletteLink from " + fromAssetType + " linkType: " + linkType); let fromPaletteAssetType = this.props.getAssetType(fromAssetType); //console.log("fromPaletteAssetType:", fromPaletteAssetType); - let validEndpoints = this.getValidEndpoints(fromPaletteAssetType["id"]); + let validEndpoints = this.props.linkToTypes(fromPaletteAssetType["id"]); let link = validEndpoints[linkType]; //console.log("link:", link); return link; @@ -2373,6 +2287,8 @@ Canvas.propTypes = { loading: PropTypes.object, isAssetDisplayed: PropTypes.func, isRelationDisplayed: PropTypes.func, + linkFromTypes: PropTypes.func, + linkToTypes: PropTypes.func, selectedLayers: PropTypes.array, selectedAsset: PropTypes.object, selectedThreat: PropTypes.object, diff --git a/src/main/webapp/app/modeller/components/canvas/popups/RelationSelectionMenu.js b/src/main/webapp/app/modeller/components/canvas/popups/RelationSelectionMenu.js index 55897996..83f1efdf 100644 --- a/src/main/webapp/app/modeller/components/canvas/popups/RelationSelectionMenu.js +++ b/src/main/webapp/app/modeller/components/canvas/popups/RelationSelectionMenu.js @@ -86,9 +86,7 @@ class RelationSelectionMenu extends React.Component { } renderOption(option, index) { - //let label = option["label"] + " (" + option["direction"] + ")"; //show label with direction let label = option["from"] + " " + option["label"] + " " + option["to"]; //show label with asset names (labels) - //console.log(index, option); let value = index; if (option["comment"].length && option["comment"].length > 0) { let props = { diff --git a/src/main/webapp/app/modeller/components/panes/details/DetailPane.js b/src/main/webapp/app/modeller/components/panes/details/DetailPane.js index dae023c8..f0f3bd3b 100644 --- a/src/main/webapp/app/modeller/components/panes/details/DetailPane.js +++ b/src/main/webapp/app/modeller/components/panes/details/DetailPane.js @@ -284,6 +284,8 @@ class DetailPane extends React.Component { assets={this.props.model.assets} host={this.state.addRelationModal.host} links={this.state.addRelationModal.links} + linkFromTypes={this.props.linkFromTypes} + linkToTypes={this.props.linkToTypes} isIncoming={this.state.addRelationModal.isIncoming} isRelationExists={this.isRelationExists} submit={this.submitAddRelationModal} @@ -367,8 +369,8 @@ class DetailPane extends React.Component { } handleAdd(asset, isIncoming) { - var linkTypes = this.props.model.palette["links"]; - var assetType = this.props.getAssetType(asset["type"]); + let linkTypes = this.props.model.palette["links"]; + let assetType = this.props.getAssetType(asset["type"]); this.openAddRelationModal(asset, isIncoming ? linkTypes[assetType["id"]]["linksTo"] : linkTypes[assetType["id"]]["linksFrom"], isIncoming); } @@ -481,6 +483,8 @@ DetailPane.propTypes = { getAssetType: PropTypes.func, getAssetsForType: PropTypes.func, getLink: PropTypes.func, + linkFromTypes: PropTypes.func, + linkToTypes: PropTypes.func, model: PropTypes.object, threats: PropTypes.array, complianceSetsData: PropTypes.object, diff --git a/src/main/webapp/app/modeller/components/panes/details/popups/AddRelationModal.js b/src/main/webapp/app/modeller/components/panes/details/popups/AddRelationModal.js index dd683f9a..c87fec8b 100644 --- a/src/main/webapp/app/modeller/components/panes/details/popups/AddRelationModal.js +++ b/src/main/webapp/app/modeller/components/panes/details/popups/AddRelationModal.js @@ -1,12 +1,17 @@ import React, {Fragment} from "react"; import PropTypes from 'prop-types'; -import {Modal, Button, FormControl, FormGroup, ControlLabel} from "react-bootstrap"; +import {Modal, Button, FormControl, FormGroup, ControlLabel, OverlayTrigger, Tooltip} from "react-bootstrap"; +import * as Constants from "../../../../../common/constants.js"; class AddRelationModal extends React.Component { constructor(props) { super(props); + this.getLinkTypesMap = this.getLinkTypesMap.bind(this); + this.getConnectableAssets = this.getConnectableAssets.bind(this); + this.getAvaialableLinksForAsset = this.getAvaialableLinksForAsset.bind(this); + this.renderRelationOption = this.renderRelationOption.bind(this); this.handleSubmit = this.handleSubmit.bind(this); this.handleRelUpdate = this.handleRelUpdate.bind(this); this.handleAssetUpdate = this.handleAssetUpdate.bind(this); @@ -14,24 +19,89 @@ class AddRelationModal extends React.Component { componentWillMount() { this.setState({ - selectedRel: "", selectedAsset: "", - selectableAssets: [] + selectedRel: "", + selectableAssets: [], + selectableLinks: [], + linkTypesMap: {} }) } componentWillReceiveProps(nextProps) { - if(nextProps.show && !this.props.show){ + if (nextProps.show && !this.props.show) { + let linkTypesMap = this.getLinkTypesMap(nextProps); + let assetIds = this.getConnectableAssets(nextProps, linkTypesMap); + let selectableAssets = this.props.assets.filter((asset) => { + return assetIds.has(asset['id']); + }); + this.setState({ + selectedAsset: "", selectedRel: "", + selectableAssets: selectableAssets, + selectableLinks: [], + linkTypesMap: linkTypesMap + }) + } + else if (!nextProps.show && this.props.show) { + this.setState({ selectedAsset: "", - selectableAssets: [] + selectedRel: "", + selectableAssets: [], + selectableLinks: [], + linkTypesMap: [] }) } } + getLinkTypesMap(props) { + let linkTypesMap = props.isIncoming ? props.linkFromTypes(props.host['type']) : props.linkToTypes(props.host['type']); + return linkTypesMap; + } + + getConnectableAssets(props, linkTypesMap) { + let linkTypes = Object.values(linkTypesMap); + + let assetsSet = new Set(); + + linkTypes.forEach((linkData, i) => { + let assets = linkData.assets; + assets.forEach((assetid, i) => { + if (assetid !== props.host.id) { + assetsSet.add(assetid); + } + }); + }); + + return assetsSet; + } + + getAvaialableLinksForAsset(assetId) { + let linkTypesMap = this.state.linkTypesMap; + let linksSet = new Set(); + + Object.keys(linkTypesMap).forEach((linkTypeUri, i) => { + let linkType = linkTypesMap[linkTypeUri]; + let assets = linkType.assets; + if (assets.includes(assetId)) { + linksSet.add(linkTypeUri); + } + }); + + let selectableLinkUris = Array.from(linksSet); + + let selectableLinks = selectableLinkUris.map((uri, i) => { + let linkType = linkTypesMap[uri]; + linkType.uri = uri; + return linkType; + }); + + return selectableLinks; + } + render() { - var self = this; + let self = this; + return (