Skip to content
This repository has been archived by the owner on Oct 20, 2023. It is now read-only.

Commit

Permalink
GRAPH 2: Server Node Rendering (#117)
Browse files Browse the repository at this point in the history
* get the graph dev mode running again

* remove Text console log again

* .gitignore packages/graph/testData

* update graph TODOs

* graph dev mode will start without test data

* fix linting errors

* update forceClampToRadius

* better circle area calculations

* type forceClampToRadius

* split HierarchicalGraphRenderer constructor into initializeForces and initializeSelection

* refactor {method}CallAllChildren into callChildrenRecursively('method')

* GraphHandler useForces

* graph severs are a single node!

* server node is now a polygon

* more detailed textOcclusionSort

* apply selection.classed() the correct way

* update server styles in graph package

* add previewedParent and selectedParent classes

* lots of graph z-indexing sort attempts and a successful one using d3.selection.sort()

* graph layer sorting cleanup

* style serverNode like softwareNode, hide most subNodeNameLabels

* need isDragging Class

* adding icons to node rows

* update comments

* Update gt.redeye file.

* Update gt.redeye file

* Add waits to commands to help with failing tests.

* Update multi-command-comment test to address failure.

* Update timeline test to address failure.

---------

Co-authored-by: James Bradford <[email protected]>
Co-authored-by: Courtney Carpenter <[email protected]>
  • Loading branch information
3 people authored Apr 10, 2023
1 parent 7e433c4 commit 404c9e5
Show file tree
Hide file tree
Showing 12 changed files with 183 additions and 95 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Classes } from '@blueprintjs/core';
import { ViewOff16 } from '@carbon/icons-react';
import { dateShortFormat, semanticIcons } from '@redeye/client/components';
import { CarbonIcon, dateShortFormat, semanticIcons } from '@redeye/client/components';
import type { BeaconModel } from '@redeye/client/store';
import { useStore } from '@redeye/client/store';
import { InfoType } from '@redeye/client/types';
Expand Down Expand Up @@ -56,6 +56,7 @@ export const BeaconRow = observer<BeaconProps>(({ beacon, ...props }) => {
{store.settings.momentTz(beacon.minTime)?.format(dateShortFormat)}&mdash;
{store.settings.momentTz(beacon.maxTime)?.format(dateShortFormat)}
</RowTime>
<CarbonIcon icon={semanticIcons.beacon} />
<RowTitle cy-test="beacon-display-name" className={skeletonClass}>
{beacon?.displayName || `${beacon.server?.displayName}`}
</RowTitle>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ export const HostRow = observer<HostRowProps>(({ host, ...props }) => {
{host.maxTime ? store.settings.momentTz(host.maxTime)?.format(dateShortFormat) : dateShortPlaceholder}
</RowTime>
<RowTitle>
{host.cobaltStrikeServer && (
{host.cobaltStrikeServer ? (
<>
<CarbonIcon icon={semanticIcons.teamServer} css={{ verticalAlign: 'sub' }} />
<Txt muted> Server: </Txt>
<CarbonIcon icon={semanticIcons.teamServer} css={{ verticalAlign: 'sub' }} /> <Txt muted>Server:</Txt>
</>
)}
) : (
<CarbonIcon icon={semanticIcons.host} css={{ verticalAlign: 'sub' }} />
)}{' '}
<Txt cy-test="hostName" bold={!!host.cobaltStrikeServer}>
{host.displayName}
</Txt>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ export const GraphControls = observer<GraphControlsProps>(({ zoomIn, zoomOut, zo
cx={svgStyle.center}
cy={svgStyle.center}
css={[legendNodeStyle]}
className={[GCN.subNode, ...legendItem[1]].join(' ')}
className={[GCN.subNode, GCN.softwareNode, ...legendItem[1]].join(' ')}
/>
<circle
r={svgStyle.center}
cx={svgStyle.width - svgStyle.center}
cy={svgStyle.center}
css={[legendNodeStyle]}
className={[GCN.subNode, ...(legendItem[3] ?? legendItem[1])].join(' ')}
className={[GCN.subNode, GCN.softwareNode, ...(legendItem[3] ?? legendItem[1])].join(' ')}
/>
</svg>
<Txt css={[legendLabelStyle]}>{legendItem[0]}</Txt>
Expand Down
11 changes: 8 additions & 3 deletions applications/client/src/views/Campaign/Graph/graph-styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ export const graphStyles = css`
}
.${GCN.superNode}, .${GCN.subNode} {
cursor: pointer;
/* &:active { cursor: grabbing; } */
}
.${GCN.groupNode} {
/* pointer-events: none; */ // set in code
}
&:not(.${GCN.isZooming}) .${GCN.superNode} {
transition: r 0.2s cubic-bezier(0, 1, 0, 1);
}
.${GCN.occludedLabel} {
.${GCN.occludedLabel}, .${GCN.subNodeNameLabel}:not(.${GCN.selectedFocus}):not(.${GCN.previewedFocus}) {
display: none;
}
.${GCN.parentLinkNode} {
Expand Down Expand Up @@ -88,7 +89,7 @@ export const graphStyles = css`
stroke: ${GraphTokens.GroupNodeStrokeColor};
}
.${GCN.superNode} {
.${GCN.computerNode} {
fill: ${GraphTokens.PresentBgColor};
stroke: ${GraphTokens.PresentFgColor};
Expand All @@ -101,6 +102,10 @@ export const graphStyles = css`
stroke: ${GraphTokens.FutureFgColor};
}
&.${GCN.selectedParent} {
stroke: ${GraphTokens.PreviewFgColor};
}
&.${GCN.previewed} {
stroke-width: ${GraphTokens.PreviewThickness};
}
Expand Down Expand Up @@ -131,7 +136,7 @@ export const graphStyles = css`
stroke: ${GraphTokens.PresentBgColor};
}
.${GCN.subNode} {
.${GCN.softwareNode}, .${GCN.serverNode} {
fill: ${GraphTokens.PresentFgColor};
stroke: ${GraphTokens.PresentBgColor};
r: 4px;
Expand Down
26 changes: 23 additions & 3 deletions packages/graph/src/GraphData/HierarchicalGraphData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,15 +257,35 @@ export class HierarchicalGraphData {
}
function addNode(node: HierarchicalGraphNode) {
if (node == null || node.parent == null) return; // rootLink or rootNode
node[interactionProp] = true;
interactionSet.add(node);
addAncestors(node);
if (node.data.isServer) {
// select severNodes in unison, as if a tree is a single subNode
const serverParent = node.ancestors().find((d) => d.type === 'keyNode' && d.depth === 1) || node;
const serverChild = serverParent.leaves().find((d) => d.type === 'keyNode') || node;
[serverParent, serverChild].forEach((_node) => {
_node[interactionProp] = true;
interactionSet.add(_node);
});
} else {
// select node and ancestors
node[interactionProp] = true;
interactionSet.add(node);
addAncestors(node);
}
}
function addLink(link: HierarchicalGraphLink) {
link[interactionProp] = true;
interactionSet.add(link);
}

// select severNodes in unison, as if a tree is a single subNode
if (node.data.isServer) {
const serverParent = node.ancestors().find((d) => d.type === 'keyNode' && d.depth === 1) || node;
const serverChild = serverParent.leaves().find((d) => d.type === 'keyNode') || node;
serverParent[interactionPropFocus] = true;
addNode(serverParent);
node = serverChild;
}

// set focus node
node[interactionPropFocus] = true;
addNode(node);
Expand Down
19 changes: 12 additions & 7 deletions packages/graph/src/GraphRenderers/GroupGraphRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
isInteractionFocus,
circleArea,
circleRadius,
interactionSort,
} from './layout-utils';
import { HierarchicalGraphLink, HierarchicalGraphNode } from '../GraphData/types';
import { defNum } from '../utils';
Expand All @@ -24,7 +25,8 @@ import { defNum } from '../utils';
export class GroupGraphRenderer extends HierarchicalGraphRenderer {
constructor(props: GraphHierarchicalConstructorProps) {
super(props);
super.initialize(SubGraphRenderer, true);
this.initialize(true);
this.initializeChildGraphs(SubGraphRenderer);
}

initializeForces() {
Expand All @@ -34,8 +36,8 @@ export class GroupGraphRenderer extends HierarchicalGraphRenderer {
return d.type === 'keyNode' ? (d.graphLinks.length || 1) * -2 - 1 : 0;
});
const forceLink = d3ForceLink(this.links)
// .strength(d=>d.children.length > 1 ? 0. : 0)
// .strength(d => d.group ? 0.2 : 0.05)
// .strength((d) => (d.children.length > 1 ? 0 : 0))
// .strength((d) => (d.group ? 0.2 : 0.05))
.strength((d) => d.source.graphLinks.length / 100)
.distance(
this.keyNodes.length < 2
Expand Down Expand Up @@ -71,14 +73,13 @@ export class GroupGraphRenderer extends HierarchicalGraphRenderer {
.attr('transform-origin', 'center');

this.linkSelection = this.rootGroupSelection
.append('g')
.selectAll('line')
.data(this.links)
.join('line')
.attr('class', (d) => (d.type === 'siblingLink' ? classNames.siblingLink : classNames.parentLink));
.classed(classNames.siblingLink, (d) => d.type === 'siblingLink')
.classed(classNames.parentLink, (d) => d.type === 'parentLink');

this.childGraphRootSelection = this.rootGroupSelection
.append('g')
.selectAll('g')
.data(this.nodes)
.join('g')
Expand All @@ -89,7 +90,8 @@ export class GroupGraphRenderer extends HierarchicalGraphRenderer {
.append('circle')
.attr('r', (d) => d.r || 0)
.style('pointer-events', 'none') // not interactive, layout only
.attr('class', (d) => (d.type === 'parentLinkNode' ? classNames.parentLinkNode : classNames.keyNode))
.classed(classNames.parentLinkNode, (d) => d.type === 'parentLinkNode')
.classed(classNames.keyNode, (d) => d.type === 'keyNode')
.classed(classNames.groupNode, true);

super.initializeSelection();
Expand Down Expand Up @@ -124,6 +126,9 @@ export class GroupGraphRenderer extends HierarchicalGraphRenderer {
if (isInteractionFocus(this.rootNode!)) {
this.showLayout();
super.drawInteraction();
this.rootGroupSelection
.selectChildren<any, HierarchicalGraphNode | HierarchicalGraphLink>()
.sort(interactionSort);
} else {
this.hideLayout();
}
Expand Down
10 changes: 4 additions & 6 deletions packages/graph/src/GraphRenderers/HierarchicalGraphRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import { select as d3Select, forceSimulation as d3ForceSimulation } from 'd3';
import { GraphHandler } from '../GraphHandler';
import { classNames, isInteractionRelated } from './layout-utils';
import { classNames } from './layout-utils';
import {
HierarchyLinkSelection,
HierarchyNodeSelection,
Expand Down Expand Up @@ -51,12 +51,11 @@ export class HierarchicalGraphRenderer {
this.rootSelection = rootSelection;
}

initialize(ChildGraphClass?: typeof HierarchicalGraphRenderer, startHidden = false) {
initialize(startHidden = false) {
this.initializeForces();
this.initializeSelection();
if (startHidden) this.hideLayout();
this.initializeSimulationLayout();
this.initializeChildGraphs(ChildGraphClass);
}
initializeForces() {
this.simulation = d3ForceSimulation(this.nodes).on('tick', this.drawLayout.bind(this));
Expand Down Expand Up @@ -130,7 +129,6 @@ export class HierarchicalGraphRenderer {
drawLayout() {}

drawInteraction() {
// could use selection.merge()... instead of array?
[
this.rootSelection,
this.rootGroupSelection,
Expand All @@ -141,10 +139,10 @@ export class HierarchicalGraphRenderer {
selection
?.classed(classNames.selected, (d) => !!d.selected)
.classed(classNames.selectedFocus, (d) => !!d.selectedFocus)
.classed(classNames.selectedParent, (d) => !!d.selectedParent)
.classed(classNames.previewed, (d) => !!d.previewed)
.classed(classNames.previewedFocus, (d) => !!d.previewedFocus)
.filter(isInteractionRelated)
.raise();
.classed(classNames.previewedParent, (d) => !!d.previewedParent);
});
}

Expand Down
22 changes: 12 additions & 10 deletions packages/graph/src/GraphRenderers/SubGraphRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { forceLink as d3ForceLink, forceManyBody as d3ForceManyBody, forceX as d3ForceX, forceY as d3ForceY } from 'd3';
import { HierarchicalGraphNode } from '../GraphData/types';
import { HierarchicalGraphLink, HierarchicalGraphNode } from '../GraphData/types';
import { defNum } from '../utils';
import { HierarchicalGraphRenderer, GraphHierarchicalConstructorProps } from './HierarchicalGraphRenderer';
import {
Expand All @@ -9,13 +9,14 @@ import {
translateCenter,
isInteractionFocus,
isInteractionRelated,
interactionSort,
} from './layout-utils';

/** a graph that handles sub nodes (that have all been grouped because the same 'signature') */
export class SubGraphRenderer extends HierarchicalGraphRenderer {
constructor(props: GraphHierarchicalConstructorProps) {
super(props);
super.initialize(undefined, true);
super.initialize(true);
}

initializeForces() {
Expand Down Expand Up @@ -46,39 +47,37 @@ export class SubGraphRenderer extends HierarchicalGraphRenderer {
this.rootGroupSelection = this.rootSelection
.data([this.rootNode])
.append('g')
.attr('class', classNames.subGraph)
.attr(classNames.subGraph, true)
.attr('transform-origin', 'center')
.attr('cy-test', 'subGSelection');

this.linkSelection = this.rootGroupSelection
.append('g')
.selectAll('line')
.data(this.links)
.join('line')
.attr('class', (d) => (d.type === 'siblingLink' ? classNames.siblingLink : classNames.parentLink))
.classed(classNames.siblingLink, (d) => d.type === 'siblingLink')
.classed(classNames.parentLink, (d) => d.type === 'parentLink')
.attr('cy-test', 'subLinkSelection');

this.nodeSelection = this.rootGroupSelection
.append('g')
.selectAll('circle')
.data(this.nodes)
.join('circle')
.attr('r', (d) => d.r || 0)
.attr('cy-test', 'beaconsGraph')
.attr('class', (d) => (d.type === 'parentLinkNode' ? classNames.parentLinkNode : classNames.keyNode))
.classed(classNames.parentLinkNode, (d) => d.type === 'parentLinkNode')
.classed(classNames.keyNode, (d) => d.type === 'keyNode')
.classed(classNames.subNode, true)
.classed(classNames.softwareNode, true)
.on('click', this.graphHandler.clickNode.bind(this.graphHandler))
.on('mouseover', this.graphHandler.mouseOverNode.bind(this.graphHandler));

// TODO: this will currently z-index under (some) super and group nodes
// ... to fix this, these will need to render inside the SuperGraph.rootSelection
this.labelSelection = this.rootGroupSelection
.append('g')
.attr('cy-test', 'selectedLabel')
.selectAll('text')
.data(this.nodes)
.join('text')
// .attr('text-anchor', 'end')
.classed(classNames.subNodeNameLabel, true)
.text(createLabel);

Expand Down Expand Up @@ -106,6 +105,9 @@ export class SubGraphRenderer extends HierarchicalGraphRenderer {
if (isInteractionFocus(this.parentNode!)) {
this.showLayout();
super.drawInteraction();
this.rootGroupSelection
.selectChildren<any, HierarchicalGraphNode | HierarchicalGraphLink>()
.sort(interactionSort);
} else {
this.hideLayout();
}
Expand Down
Loading

0 comments on commit 404c9e5

Please sign in to comment.