Merge pull request #132 from BloodHoundAD/UI-Update

Object Properties Update
master
Rohan Vazarkar 2017-10-10 03:54:19 -04:00 committed by GitHub
commit 60a68c4a54
20 changed files with 2862 additions and 2605 deletions

View File

@ -16,7 +16,6 @@
<script src="node_modules/linkurious/dist/plugins.js" type="text/javascript"></script>
<script src="node_modules/dagre/dist/dagre.min.js" type="text/javascript"></script>
<script src="node_modules/bootstrap-3-typeahead/bootstrap3-typeahead.min.js" type="text/javascript"></script>
<script src="src/js/papaparse.min.js" type="text/javascript"></script>
<script src="src/js/simple-slider.min.js" type="text/javascript"></script>
<link type="text/css" rel="stylesheet" href="src/css/simple-slider.css">
<link type="text/css" rel="stylesheet" href="src/css/simple-slider-volume.css">

View File

@ -28,7 +28,7 @@
},
"babel": {
"presets": [
"es2015",
"env",
"stage-0",
"react"
]
@ -38,7 +38,7 @@
"babel-core": "^6.22.1",
"babel-loader": "^7.0.0",
"babel-polyfill": "^6.22.0",
"babel-preset-es2015": "^6.22.0",
"babel-preset-env": "^1.6.0",
"babel-preset-react": "^6.22.0",
"babel-preset-stage-0": "^6.22.0",
"concurrently": "^3.1.0",
@ -56,15 +56,16 @@
"configstore": "^3.1.0",
"dagre": "^0.7.4",
"eventemitter2": "^4.1.0",
"fast-csv": "^2.4.1",
"jquery": "^3.2.1",
"linkurious": "^1.5.1",
"mustache": "^2.2.1",
"neo4j-driver": "^1.3.0",
"prop-types": "^15.5.10",
"neo4j-driver": "^1.4.1",
"react": "^15.4.2",
"react-bootstrap": "^0.31.0",
"react-dom": "^15.4.2",
"react-if": "^2.1.0",
"react-transition-group": "^1.1.3"
"react-transition-group": "^1.1.3",
"unzipper": "^0.8.9"
}
}

View File

@ -3,15 +3,15 @@ import { Alert } from 'react-bootstrap';
export default class GenericAlert extends Component {
constructor(){
super()
super();
this.state = {
visible: false,
text: "No data returned from query",
timeout: null
}
};
emitter.on('showAlert', this._show.bind(this))
emitter.on('hideAlert', this._dismiss.bind(this))
emitter.on('showAlert', this._show.bind(this));
emitter.on('hideAlert', this._dismiss.bind(this));
}
_dismiss(){
@ -19,16 +19,16 @@ export default class GenericAlert extends Component {
}
_show(val){
clearTimeout(this.state.timeout)
clearTimeout(this.state.timeout);
var t = setTimeout(function(){
this._dismiss()
}.bind(this), 2500)
this._dismiss();
}.bind(this), 2500);
this.setState({
visible: true,
text: val,
timeout: t
})
});
}
@ -38,9 +38,9 @@ export default class GenericAlert extends Component {
<Alert className="alertdiv" bsStyle="danger" onDismiss={this._dismiss.bind(this)}>
{this.state.text}
</Alert>
)
);
}else{
return null
return null;
}
}

View File

@ -11,176 +11,18 @@ export default class Login extends Component {
password: "",
loginInProgress: false,
save: false
}
}
checkDBPresence(){
var url = this.state.url;
var icon = this.state.icon;
var jicon = jQuery(icon)
var btn = jQuery(this.refs.loginButton)
if (url === ""){
return;
}
jQuery(this.refs.urlspinner).toggle(true)
url = url.replace(/\/$/, "");
if (!url.includes(':')){
url = url + ':7687'
}
if (!url.startsWith('bolt://')){
url = 'bolt://' + url
}
icon.removeClass();
icon.addClass("fa fa-spinner fa-spin form-control-feedback");
icon.toggle(true);
var driver = neo4j.driver(url, neo4j.auth.basic("",""), {encrypted:'ENCRYPTION_ON'})
var session = driver.session();
driver.onCompleted = function(){
session.close()
driver.close()
}
driver.onError = function(error){
if (error.message.includes("authentication failure")){
icon.removeClass();
icon.addClass("fa fa-check-circle green-icon-color form-control-feedback");
this.setState({loginEnabled: true, url: url})
}else{
icon.removeClass();
icon.addClass("fa fa-times-circle red-icon-color form-control-feedback");
icon.attr('data-original-title', 'No database found')
.tooltip('fixTitle')
.tooltip('show')
this.setState({
loginInProgress: false,
loginEnabled: false
})
}
session.close()
driver.close()
}.bind(this)
session.run("return 1")
}
checkDBCreds(){
if (this.state.loginInProgress){
return;
}
this.setState({
loginInProgress: true,
loginEnabled: false
})
var btn = jQuery(this.refs.loginButton)
var pwf = jQuery(this.refs.password)
var driver = neo4j.driver(this.state.url, neo4j.auth.basic(this.state.user, this.state.password))
driver.onError = function(error){
console.log(error)
if (error.message.includes("authentication failure")){
btn.removeClass('activate');
this.setState({
loginInProgress: false,
loginEnabled: true
})
pwf.attr('data-original-title', 'Invalid username or password')
.tooltip('fixTitle')
.tooltip('show')
}else if (error.message.includes("too many times in a row")){
btn.removeClass('activate');
this.setState({
loginInProgress: false,
loginEnabled: true
})
pwf.attr('data-original-title', 'Too many authentication attempts, please wait')
.tooltip('fixTitle')
.tooltip('show')
}else if (error.toString().includes('ECONNREFUSED')){
var icon = this.state.icon
icon.toggle('true')
icon.removeClass();
icon.addClass("fa fa-times-circle red-icon-color form-control-feedback");
icon.attr('data-original-title', 'No database found')
.tooltip('fixTitle')
.tooltip('show')
this.setState({
loginInProgress: false,
loginEnabled: false
})
}
driver.close()
}.bind(this)
var session = driver.session();
session.run('MATCH (n) RETURN (n) LIMIT 1')
.subscribe({
onError: function(error){
btn.removeClass('activate');
var url = this.state.url.replace('bolt://','http://').replace('7687','7474')
if (error.fields && error.fields[0].code === "Neo.ClientError.Security.CredentialsExpired"){
pwf.attr('data-original-title', 'Credentials need to be changed from the neo4j browser first. Go to {} and change them.'.format(url))
.tooltip('fixTitle')
.tooltip('show')
this.setState({
loginInProgress: false,
loginEnabled: true
})
}
}.bind(this),
onNext: function(){
},
onCompleted: function(){
btn.toggleClass('activate');
btn.removeClass('btn-default')
btn.addClass('btn-success')
btn.html('Success!')
this.setState({
loginInProgress: false
})
var dbinfo = {
url: this.state.url,
user: this.state.user,
password: this.state.password
}
if (this.state.save){
conf.set('databaseInfo',dbinfo)
}
appStore.databaseInfo = dbinfo;
jQuery(this.refs.password).tooltip('hide')
jQuery(this.refs.urlspinner).tooltip('hide')
setTimeout(function(){
jQuery(this.refs.outer).fadeOut(400, function(){
renderEmit.emit('login');
});
}.bind(this), 1500)
driver.close()
global.driver = neo4j.driver(this.state.url, neo4j.auth.basic(this.state.user, this.state.password))
}.bind(this)
})
btn.toggleClass('activate');
};
}
componentWillMount() {
var c = conf.get('databaseInfo')
var c = conf.get('databaseInfo');
if (typeof c !== 'undefined'){
this.setState({
url: c.url,
user: c.user,
password: c.password,
save: true
})
});
}
}
@ -191,20 +33,183 @@ export default class Login extends Component {
container: 'body',
trigger: 'manual',
template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner tooltip-inner-custom"></div></div>'
})
this.setState({icon: jQuery(this.refs.urlspinner)})
var icon = jQuery(this.refs.urlspinner)
});
this.setIcon();
if (this.state.password !== ""){
this.checkDBCreds();
}
}
setIcon(){
var icon = jQuery(this.refs.urlspinner);
icon.tooltip({
placement : 'right',
title: '',
container: 'body',
delay: {show: 200, hide: 0},
template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner tooltip-inner-custom"></div></div>'
})
icon.toggle(false)
if (this.state.password !== ""){
this.checkDBCreds();
});
icon.toggle(false);
this.setState({icon: jQuery(this.refs.urlspinner)});
}
checkDBPresence(){
var url = this.state.url;
var icon = this.state.icon;
var jicon = jQuery(icon);
var btn = jQuery(this.refs.loginButton);
if (url === ""){
return;
}
jQuery(this.refs.urlspinner).toggle(true);
url = url.replace(/\/$/, "");
if (!url.includes(':')){
url = url + ':7687';
}
if (!url.startsWith('bolt://')){
url = 'bolt://' + url;
}
icon.removeClass();
icon.addClass("fa fa-spinner fa-spin form-control-feedback");
icon.toggle(true);
var driver = neo4j.driver(url, neo4j.auth.basic("",""), {encrypted:'ENCRYPTION_ON'});
var session = driver.session();
driver.onCompleted = function(){
session.close();
driver.close();
};
driver.onError = function(error){
if (error.message.includes("authentication failure")){
icon.removeClass();
icon.addClass("fa fa-check-circle green-icon-color form-control-feedback");
this.setState({loginEnabled: true, url: url});
}else{
icon.removeClass();
icon.addClass("fa fa-times-circle red-icon-color form-control-feedback");
icon.attr('data-original-title', 'No database found')
.tooltip('fixTitle')
.tooltip('show');
this.setState({
loginInProgress: false,
loginEnabled: false
});
}
session.close();
driver.close();
}.bind(this);
session.run("return 1");
}
checkDBCreds(){
if (this.state.loginInProgress){
return;
}
this.setState({
loginInProgress: true,
loginEnabled: false
});
var btn = jQuery(this.refs.loginButton);
var pwf = jQuery(this.refs.password);
var driver = neo4j.driver(this.state.url, neo4j.auth.basic(this.state.user, this.state.password));
driver.onError = function(error){
console.log(error);
if (error.message.includes("authentication failure")){
btn.removeClass('activate');
this.setState({
loginInProgress: false,
loginEnabled: true
});
pwf.attr('data-original-title', 'Invalid username or password')
.tooltip('fixTitle')
.tooltip('show');
}else if (error.message.includes("too many times in a row")){
btn.removeClass('activate');
this.setState({
loginInProgress: false,
loginEnabled: true
});
pwf.attr('data-original-title', 'Too many authentication attempts, please wait')
.tooltip('fixTitle')
.tooltip('show');
}else if (error.toString().includes('ECONNREFUSED')){
var icon = this.state.icon;
icon.toggle('true');
icon.removeClass();
icon.addClass("fa fa-times-circle red-icon-color form-control-feedback");
icon.attr('data-original-title', 'No database found')
.tooltip('fixTitle')
.tooltip('show');
this.setState({
loginInProgress: false,
loginEnabled: false
});
}
driver.close();
}.bind(this);
var session = driver.session();
session.run('MATCH (n) RETURN (n) LIMIT 1')
.subscribe({
onError: function(error){
btn.removeClass('activate');
var url = this.state.url.replace('bolt://','http://').replace('7687','7474');
if (error.fields && error.fields[0].code === "Neo.ClientError.Security.CredentialsExpired"){
pwf.attr('data-original-title', 'Credentials need to be changed from the neo4j browser first. Go to {} and change them.'.format(url))
.tooltip('fixTitle')
.tooltip('show');
this.setState({
loginInProgress: false,
loginEnabled: true
});
}
}.bind(this),
onNext: function(){
},
onCompleted: function(){
btn.toggleClass('activate');
btn.removeClass('btn-default');
btn.addClass('btn-success');
btn.html('Success!');
this.setState({
loginInProgress: false
});
var dbinfo = {
url: this.state.url,
user: this.state.user,
password: this.state.password
};
if (this.state.save){
conf.set('databaseInfo',dbinfo);
}
appStore.databaseInfo = dbinfo;
jQuery(this.refs.password).tooltip('hide');
jQuery(this.refs.urlspinner).tooltip('hide');
setTimeout(function(){
jQuery(this.refs.outer).fadeOut(400, function(){
renderEmit.emit('login');
});
}.bind(this), 1500);
driver.close();
global.driver = neo4j.driver(this.state.url, neo4j.auth.basic(this.state.user, this.state.password));
}.bind(this)
});
btn.toggleClass('activate');
}
_saveChange(event) {
@ -212,24 +217,24 @@ export default class Login extends Component {
}
_urlChanged(event){
this.setState({url: event.target.value})
this.setState({url: event.target.value});
}
_userChanged(event){
this.setState({user: event.target.value})
jQuery(this.refs.password).tooltip('hide')
this.setState({user: event.target.value});
jQuery(this.refs.password).tooltip('hide');
}
_passChanged(event){
this.setState({password: event.target.value})
jQuery(this.refs.password).tooltip('hide')
this.setState({password: event.target.value});
jQuery(this.refs.password).tooltip('hide');
}
_triggerLogin(e){
var key = e.keyCode ? e.keyCode : e.which
var key = e.keyCode ? e.keyCode : e.which;
if (key === 13){
this.checkDBCreds()
this.checkDBCreds();
}
}
@ -260,7 +265,7 @@ export default class Login extends Component {
</div>
<div className="savecontainer">
<div className="checkbox logincheck">
<label><input value={this.state.save} onChange={this._saveChange.bind(this)} ref="save" type="checkbox" />Save Password</label>
<label><input checked={this.state.save} onChange={this._saveChange.bind(this)} ref="save" type="checkbox" />Save Password</label>
</div>
<div className="buttoncontainer">
<button ref="loginButton" disabled={!this.state.loginEnabled} type="button" onClick={this.checkDBCreds.bind(this)} className="btn btn-primary loginbutton has-spinner">

View File

@ -112,6 +112,7 @@ export default class Settings extends Component {
<input type="number" min="0" max="20" className="sliderinput" ref="edgeinput" />
</span>
</div>
<br />
<div className="checkbox-inline">
<label>
<input ref="debug" type="checkbox" onChange={this.onDebugChange.bind(this)}/> Query Debug Mode

View File

@ -1,15 +1,15 @@
import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import ReactDOM from 'react-dom';
import { findGraphPath } from 'utils';
var fs = require('fs');
var child_process = require('child_process')
var child_process = require('child_process');
var child;
var path = require('path')
const { dialog } = require('electron').remote
var path = require('path');
const { dialog } = require('electron').remote;
export default class GraphContainer extends Component {
constructor(props){
super(props)
super(props);
child = child_process.fork(path.join(__dirname,'src','js','worker.js'), {silent:true});
@ -20,14 +20,15 @@ export default class GraphContainer extends Component {
firstDraw: true,
template: null,
session: driver.session()
}
};
$.ajax({
url: 'src/components/tooltip.html',
type: 'GET',
success: function(response){
this.setState({template: response})
this.setState({template: response});
}.bind(this)
})
});
child.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
@ -38,64 +39,93 @@ export default class GraphContainer extends Component {
});
child.on('message', function(m) {
this.loadFromChildProcess(m)
this.loadFromChildProcess(m);
}.bind(this));
var s1 = driver.session()
var s2 = driver.session()
var s3 = driver.session()
var s4 = driver.session()
var s1 = driver.session();
var s2 = driver.session();
var s3 = driver.session();
var s4 = driver.session();
s1.run("CREATE CONSTRAINT ON (c:User) ASSERT c.name IS UNIQUE")
.then(function(){
s1.close()
s1.close();
s2.run("CREATE CONSTRAINT ON (c:Computer) ASSERT c.name IS UNIQUE")
.then(function(){
s2.close()
s2.close();
s3.run("CREATE CONSTRAINT ON (c:Group) ASSERT c.name IS UNIQUE")
.then(function(){
s3.close()
s3.close();
s4.run("CREATE CONSTRAINT ON (c:Domain) ASSERT c.name IS UNIQUE")
.then(function(){
s4.close()
s4.close();
})
.catch(function(){
s4.close()
})
s4.close();
});
})
.catch(function(){
s3.close()
})
s3.close();
});
})
.catch(function(){
s2.close()
})
s2.close();
});
})
.catch(function(){
s1.close()
})
s1.close();
});
emitter.on('doLogout', function(){
this.state.sigmaInstance.graph.clear();
this.state.sigmaInstance.refresh();
sigma.layouts.killForceLink();
this.setState({sigmaInstance: null})
this.setState({sigmaInstance: null});
child.kill();
}.bind(this))
}.bind(this));
}
componentWillMount() {
emitter.on('searchQuery', this.doSearchQuery.bind(this));
emitter.on('pathQuery', this.doPathQuery.bind(this));
emitter.on('graphBack', this.goBack.bind(this));
emitter.on('query', this.doGenericQuery.bind(this));
emitter.on('spotlightClick', this.spotlightClickHandler.bind(this));
emitter.on('graphRefresh', this.relayout.bind(this));
emitter.on('export', this.export.bind(this));
emitter.on('import', this.import.bind(this));
emitter.on('clearDB', this.clearGraph.bind(this));
emitter.on('changeGraphicsMode', this.setGraphicsMode.bind(this));
emitter.on('ungroupNode', this.ungroupNode.bind(this));
emitter.on('unfoldNode', this.unfoldEdgeNode.bind(this));
emitter.on('collapseNode', this.foldEdgeNode.bind(this));
emitter.on('resetZoom', this.resetZoom.bind(this));
emitter.on('zoomIn', this.zoomIn.bind(this));
emitter.on('zoomOut', this.zoomOut.bind(this));
}
componentDidMount() {
this.initializeSigma();
this.doQueryNative({
statement: 'MATCH (n:Group) WHERE n.name =~ "(?i).*DOMAIN ADMINS.*" WITH n MATCH (n)<-[r:MemberOf*1..]-(m) RETURN n,r,m',
allowCollapse: false,
props: {}
});
}
relayout(){
sigma.layouts.stopForceLink()
sigma.layouts.stopForceLink();
if (appStore.dagre){
sigma.layouts.dagre.start(this.state.sigmaInstance);
}else{
sigma.layouts.startForceLink()
sigma.layouts.startForceLink();
}
}
export(payload){
if (payload === 'image'){
var size = $('#graph').outerWidth()
var size = $('#graph').outerWidth();
sigma.plugins.image(this.state.sigmaInstance,
this.state.sigmaInstance.renderers[0],
{
@ -106,27 +136,27 @@ export default class GraphContainer extends Component {
});
}else{
var json = this.state.sigmaInstance.toJSON({
pretty: true,
})
pretty: true
});
json = JSON.parse(json)
json.spotlight = appStore.spotlightData
json = JSON.parse(json);
json.spotlight = appStore.spotlightData;
dialog.showSaveDialog({
defaultPath: 'graph.json'
}, function(loc){
fs.writeFile(loc, JSON.stringify(json, null, 2))
})
fs.writeFile(loc, JSON.stringify(json, null, 2));
});
}
}
loadFromChildProcess(graph){
if (graph.nodes.length === 0){
emitter.emit('showAlert', "No data returned from query")
emitter.emit('updateLoadingText', "Done!")
emitter.emit('showAlert', "No data returned from query");
emitter.emit('updateLoadingText', "Done!");
setTimeout(function(){
emitter.emit('showLoadingIndicator', false);
}, 1500)
}, 1500);
}else{
if (!this.state.firstDraw){
appStore.queryStack.push({
@ -135,23 +165,23 @@ export default class GraphContainer extends Component {
spotlight: appStore.spotlightData,
startNode: appStore.startNode,
endNode: appStore.endNode
})
});
}
$.each(graph.nodes, function(i, node){
if (node.start){
appStore.startNode = node
appStore.startNode = node;
}
if (node.end){
appStore.endNode = node
appStore.endNode = node;
}
node.glyphs = $.map(node.glyphs, function(value, index) {
return [value];
});
})
});
this.setState({firstDraw: false})
this.setState({firstDraw: false});
sigma.misc.animation.camera(this.state.sigmaInstance.camera, { x: 0, y: 0, ratio: 1.075 });
appStore.spotlightData = graph.spotlight;
@ -164,7 +194,7 @@ export default class GraphContainer extends Component {
if (appStore.dagre){
sigma.layouts.dagre.start(this.state.sigmaInstance);
}else{
sigma.layouts.startForceLink()
sigma.layouts.startForceLink();
}
emitter.emit('spotlightUpdate');
}
@ -177,44 +207,44 @@ export default class GraphContainer extends Component {
graph = JSON.parse(data);
}catch (err){
emitter.emit('showAlert', 'Bad JSON File');
return
return;
}
if (graph.nodes.length === 0){
emitter.emit('showAlert', "No data returned from query")
emitter.emit('showAlert', "No data returned from query");
}else{
$.each(graph.nodes, function(i, node){
node.glyphs = $.map(node.glyphs, function(value, index) {
return [value];
});
})
});
appStore.queryStack.push({
nodes: this.state.sigmaInstance.graph.nodes(),
edges: this.state.sigmaInstance.graph.edges(),
spotlight: appStore.spotlightData,
startNode: appStore.startNode,
endNode: appStore.endNode
})
});
appStore.spotlightData = graph.spotlight;
this.state.sigmaInstance.graph.clear();
this.state.sigmaInstance.graph.read(graph);
this.state.sigmaInstance.refresh()
this.state.sigmaInstance.refresh();
emitter.emit('spotlightUpdate');
}
}.bind(this))
}.bind(this));
}
clearGraph(){
this.state.sigmaInstance.graph.clear()
this.state.sigmaInstance.refresh()
this.state.sigmaInstance.graph.clear();
this.state.sigmaInstance.refresh();
}
setGraphicsMode(){
var lowgfx = appStore.performance.lowGraphics
var sigmaInstance = this.state.sigmaInstance
this.state.design.clear()
var lowgfx = appStore.performance.lowGraphics;
var sigmaInstance = this.state.sigmaInstance;
this.state.design.clear();
if (lowgfx){
sigmaInstance.settings('defaultEdgeType', 'line');
sigmaInstance.settings('defaultEdgeColor', 'black');
@ -226,28 +256,9 @@ export default class GraphContainer extends Component {
this.state.design.setPalette(appStore.highResPalette);
this.state.design.setStyles(appStore.highResStyle);
}
this.state.design.deprecate()
sigmaInstance.refresh()
this.state.design.apply()
}
componentWillMount() {
emitter.on('searchQuery', this.doSearchQuery.bind(this));
emitter.on('pathQuery', this.doPathQuery.bind(this));
emitter.on('graphBack', this.goBack.bind(this));
emitter.on('query', this.doGenericQuery.bind(this));
emitter.on('spotlightClick', this.spotlightClickHandler.bind(this))
emitter.on('graphRefresh', this.relayout.bind(this))
emitter.on('export', this.export.bind(this))
emitter.on('import', this.import.bind(this))
emitter.on('clearDB', this.clearGraph.bind(this))
emitter.on('changeGraphicsMode', this.setGraphicsMode.bind(this))
emitter.on('ungroupNode', this.ungroupNode.bind(this))
emitter.on('unfoldNode', this.unfoldEdgeNode.bind(this))
emitter.on('collapseNode', this.foldEdgeNode.bind(this))
emitter.on('resetZoom', this.resetZoom.bind(this))
emitter.on('zoomIn', this.zoomIn.bind(this))
emitter.on('zoomOut', this.zoomOut.bind(this))
this.state.design.deprecate();
sigmaInstance.refresh();
this.state.design.apply();
}
resetZoom(){
@ -258,7 +269,7 @@ export default class GraphContainer extends Component {
}
zoomOut(){
var sigmaInstance = this.state.sigmaInstance
var sigmaInstance = this.state.sigmaInstance;
var cam = sigmaInstance.camera;
sigma.misc.animation.camera(cam, {
@ -269,7 +280,7 @@ export default class GraphContainer extends Component {
}
zoomIn(){
var sigmaInstance = this.state.sigmaInstance
var sigmaInstance = this.state.sigmaInstance;
var cam = sigmaInstance.camera;
sigma.misc.animation.camera(cam,
@ -281,16 +292,6 @@ export default class GraphContainer extends Component {
});
}
componentDidMount() {
this.initializeSigma();
this.doQueryNative({
statement: 'MATCH (n:Group) WHERE n.name =~ "(?i).*DOMAIN ADMINS.*" WITH n MATCH (n)<-[r:MemberOf*1..]-(m) RETURN n,r,m',
allowCollapse: false,
props: {}
})
}
render() {
return (
<div className="graph">
@ -321,25 +322,25 @@ export default class GraphContainer extends Component {
spotlightClickHandler(nodeId, parentId){
var sigmaInstance = this.state.sigmaInstance;
var parent = sigmaInstance.graph.nodes(nodeId)
var parent = sigmaInstance.graph.nodes(nodeId);
var label, child;
if (typeof parent === 'undefined'){
child = sigmaInstance.graph.nodes(parentId).folded.nodes.filter(function(val){
return val.id == nodeId;
})[0]
return val.id === nodeId;
})[0];
parent = sigmaInstance.graph.nodes(parentId);
}else{
child = parent;
}
label = child.label;
if (child.type_user){
emitter.emit('userNodeClicked', label)
emitter.emit('userNodeClicked', label);
}else if (child.type_group){
emitter.emit('groupNodeClicked', label)
emitter.emit('groupNodeClicked', label);
}else if (child.type_computer){
emitter.emit('computerNodeClicked', label)
emitter.emit('computerNodeClicked', label);
}
parent.color = "#2DC486"
parent.color = "#2DC486";
sigma.misc.animation.camera(
sigmaInstance.camera, {
x: parent[sigmaInstance.camera.readPrefix + 'x'],
@ -351,119 +352,120 @@ export default class GraphContainer extends Component {
setTimeout(function(){
parent.color = "black";
sigmaInstance.refresh({skipIndexation: true});
}, 2000)
}, 2000);
}
doQueryNative(params){
if (appStore.performance.debug){
emitter.emit('setRawQuery',params.statement);
}
var sigmaInstance = this.state.sigmaInstance
var nodes = {}
var edges = {}
var session = driver.session()
var sigmaInstance = this.state.sigmaInstance;
var nodes = {};
var edges = {};
var session = driver.session();
if (typeof params.props === 'undefined'){
params.props = {}
params.props = {};
}
emitter.emit('showLoadingIndicator', true);
emitter.emit('updateLoadingText', "Querying Database")
emitter.emit('resetSpotlight')
emitter.emit('updateLoadingText', "Querying Database");
emitter.emit('resetSpotlight');
session.run(params.statement, params.props)
.subscribe({
onNext: function(result){
$.each(result._fields, function(index, field){
if (field != null){
if (field !== null){
if (field.hasOwnProperty('segments')){
$.each(field.segments,function(index, segment){
var end = this.createNodeFromRow(segment.end, params)
var start = this.createNodeFromRow(segment.start, params)
var edge = this.createEdgeFromRow(segment.relationship)
var end = this.createNodeFromRow(segment.end, params);
var start = this.createNodeFromRow(segment.start, params);
var edge = this.createEdgeFromRow(segment.relationship);
if (!edges[edge.id]){
edges[edge.id] = edge
edges[edge.id] = edge;
}
if (!nodes[end.id]){
nodes[end.id] = end
nodes[end.id] = end;
}
if (!nodes[start.id]){
nodes[start.id] = start
nodes[start.id] = start;
}
}.bind(this))
}.bind(this));
}else{
if ($.isArray(field)){
$.each(field, function(index, value){
if (value != null){
var id = value.identity.low
if (value !== null){
var id = value.identity.low;
if (value.end && !edges.id){
edges[id] = this.createEdgeFromRow(value)
edges[id] = this.createEdgeFromRow(value);
}else if (!nodes.id){
nodes[id] = this.createNodeFromRow(value, params)
nodes[id] = this.createNodeFromRow(value, params);
}
}
}.bind(this))
}.bind(this));
}else{
var id = field.identity.low
var id = field.identity.low;
if (field.end && !edges.id){
edges[id] = this.createEdgeFromRow(field)
edges[id] = this.createEdgeFromRow(field);
}else if (!nodes.id){
nodes[id] = this.createNodeFromRow(field, params)
nodes[id] = this.createNodeFromRow(field, params);
}
}
}
}
}.bind(this))
}.bind(this));
}.bind(this),
onError: function(error){
console.log(error)
console.log(error);
},
onCompleted: function(){
var graph = {nodes:[],edges:[]}
var graph = {nodes:[],edges:[]};
$.each(nodes, function(node){
graph.nodes.push(nodes[node])
})
graph.nodes.push(nodes[node]);
});
$.each(edges, function(edge){
graph.edges.push(edges[edge])
})
emitter.emit('updateLoadingText', "Processing Data")
graph.edges.push(edges[edge]);
});
emitter.emit('updateLoadingText', "Processing Data");
child.send(JSON.stringify({graph: graph,
edge: params.allowCollapse ? appStore.performance.edge : 0 ,
sibling: params.allowCollapse ? appStore.performance.sibling : 0,
start: params.start,
end: params.end
}))
session.close()
}));
session.close();
}.bind(this)
})
});
}
createEdgeFromRow(data){
var id = data.identity.low
var type = data.type
var source = data.start.low
var target = data.end.low
var id = data.identity.low;
var type = data.type;
var source = data.start.low;
var target = data.end.low;
var edge = {
id: id,
type: type,
source: source,
target:target,
label: type
}
};
return edge
return edge;
}
createNodeFromRow(data, params){
var id = data.identity.low
var type = data.labels[0]
var label = data.properties.name
var id = data.identity.low;
var type = data.labels[0];
var label = data.properties.name;
var node = {
id: id,
type: type,
label: label,
Enabled: data.properties.Enabled,
glyphs: [],
folded: {
nodes: [],
@ -471,28 +473,28 @@ export default class GraphContainer extends Component {
},
x: Math.random(),
y: Math.random()
}
};
if (label === params.start){
node.start = true
node.start = true;
node.glyphs.push({
'position': 'bottom-right',
'font': 'FontAwesome',
'content': '\uF21D',
'fillColor': '#3399FF',
'fontScale': 1.5
})
});
}
if (label === params.end){
node.end = true
node.end = true;
node.glyphs.push({
'position': 'bottom-right',
'font': 'FontAwesome',
'fillColor': '#990000',
'content': '\uF05B',
'fontScale': 1.5
})
});
}
switch (type) {
@ -510,60 +512,60 @@ export default class GraphContainer extends Component {
break;
}
return node
return node;
}
unfoldEdgeNode(id){
var sigmaInstance = this.state.sigmaInstance
sigmaInstance.graph.read(sigmaInstance.graph.nodes(id).folded)
this.state.design.deprecate()
var sigmaInstance = this.state.sigmaInstance;
sigmaInstance.graph.read(sigmaInstance.graph.nodes(id).folded);
this.state.design.deprecate();
this.state.design.apply();
this.relayout()
this.relayout();
}
foldEdgeNode(id){
var sigmaInstance = this.state.sigmaInstance
var sigmaInstance = this.state.sigmaInstance;
$.each(sigmaInstance.graph.nodes(id).folded.nodes, function(index, node){
sigmaInstance.graph.dropNode(node.id)
})
sigmaInstance.refresh()
sigmaInstance.graph.dropNode(node.id);
});
sigmaInstance.refresh();
this.state.design.deprecate();
this.state.design.apply();
this.relayout();
}
ungroupNode(id){
var sigmaInstance = this.state.sigmaInstance
var node = sigmaInstance.graph.nodes(id)
var sigmaInstance = this.state.sigmaInstance;
var node = sigmaInstance.graph.nodes(id);
sigmaInstance.graph.dropNode(id);
sigmaInstance.graph.read(node.folded)
this.state.design.deprecate()
sigmaInstance.refresh()
this.state.design.apply()
sigmaInstance.graph.read(node.folded);
this.state.design.deprecate();
sigmaInstance.refresh();
this.state.design.apply();
this.relayout();
}
doSearchQuery(payload, props){
if (typeof props === 'undefined'){
props = {}
props = {};
}
this.doQueryNative({
statement: payload,
allowCollapse: true,
props: props
})
});
}
doPathQuery(start, end){
var statement = "MATCH (n {name:{start}}), (m {name:{end}}), p=allShortestPaths((n)-[*]->(m)) RETURN p"
var props = {start: start, end: end}
var statement = "MATCH (n {name:{start}}), (m {name:{end}}), p=allShortestPaths((n)-[*]->(m)) RETURN p";
var props = {start: start, end: end};
this.doQueryNative({
statement: statement,
allowCollapse: true,
props: props,
start: start,
end: end
})
});
}
doGenericQuery(statement, props, start, end, allowCollapse=true){
@ -572,7 +574,7 @@ export default class GraphContainer extends Component {
}
if (typeof props === 'undefined'){
props = {}
props = {};
}
this.doQueryNative({
statement: statement,
@ -580,26 +582,26 @@ export default class GraphContainer extends Component {
start: start,
end: end,
props: props
})
});
}
_nodeDragged(){
this.setState({dragged:true})
this.setState({dragged:true});
}
_nodeClicked(n){
if (!this.state.dragged){
if (n.data.node.type_user){
emitter.emit('userNodeClicked', n.data.node.label)
emitter.emit('userNodeClicked', n.data.node.label);
}else if (n.data.node.type_group){
emitter.emit('groupNodeClicked', n.data.node.label)
emitter.emit('groupNodeClicked', n.data.node.label);
}else if (n.data.node.type_computer && (n.data.node.label !== 'Grouped Computers')){
emitter.emit('computerNodeClicked', n.data.node.label)
emitter.emit('computerNodeClicked', n.data.node.label);
}else if (n.data.node.type_domain){
emitter.emit('domainNodeClicked', n.data.node.label)
emitter.emit('domainNodeClicked', n.data.node.label);
}
}else{
this.setState({dragged: false})
this.setState({dragged: false});
}
}
@ -610,7 +612,7 @@ export default class GraphContainer extends Component {
{
container: 'graph'
}
)
);
sigmaInstance.settings(
{
@ -629,7 +631,7 @@ export default class GraphContainer extends Component {
zoomingRatio: 1.4,
scalingMode: 'inside'
}
)
);
//Bind sigma events
sigmaInstance.renderers[0].bind('render', function(e) {
@ -642,21 +644,21 @@ export default class GraphContainer extends Component {
}else{
sigmaInstance.settings('drawEdgeLabels', true);
}
})
});
sigmaInstance.bind('clickNode', this._nodeClicked.bind(this))
sigmaInstance.bind('clickNode', this._nodeClicked.bind(this));
sigmaInstance.bind('hovers', function(e){
if (e.data.enter.nodes.length > 0) {
if (appStore.endNode !== null) {
findGraphPath(this.state.sigmaInstance, false, e.data.enter.nodes[0].id)
findGraphPath(this.state.sigmaInstance, false, e.data.enter.nodes[0].id);
}
if (appStore.startNode !== null) {
findGraphPath(this.state.sigmaInstance, true, e.data.enter.nodes[0].id)
findGraphPath(this.state.sigmaInstance, true, e.data.enter.nodes[0].id);
}
sigmaInstance.refresh({'skipIndexation': true})
sigmaInstance.refresh({'skipIndexation': true});
}
if (e.data.leave.nodes.length > 0) {
@ -668,13 +670,13 @@ export default class GraphContainer extends Component {
sigmaInstance.refresh({ 'skipIndexation': true });
}
}
}.bind(this))
}.bind(this));
//Some key binds
$(window).on('keyup', function(e){
var key = e.keyCode ? e.keyCode : e.which
var mode = appStore.performance.nodeLabels
var sigmaInstance = this.state.sigmaInstance
var key = e.keyCode ? e.keyCode : e.which;
var mode = appStore.performance.nodeLabels;
var sigmaInstance = this.state.sigmaInstance;
if (document.activeElement === document.body && key === 17){
mode = mode + 1;
@ -682,28 +684,28 @@ export default class GraphContainer extends Component {
mode = 0;
}
appStore.performance.nodeLabels = mode;
conf.set('performance', appStore.performance)
conf.set('performance', appStore.performance);
if (mode === 0){
sigmaInstance.settings('labelThreshold', 500);
emitter.emit('showAlert', 'Hiding Node Labels')
emitter.emit('showAlert', 'Hiding Node Labels');
}else if (mode === 1){
sigmaInstance.settings('labelThreshold', 15);
emitter.emit('showAlert', 'Default Node Label Threshold')
emitter.emit('showAlert', 'Default Node Label Threshold');
}else{
sigmaInstance.settings('labelThreshold', 1);
emitter.emit('showAlert', 'Always Showing Node Labels')
emitter.emit('showAlert', 'Always Showing Node Labels');
}
sigmaInstance.refresh({'skipIndexation' : true})
sigmaInstance.refresh({'skipIndexation' : true});
}
}.bind(this))
}.bind(this));
//Plugin Configuration
var dragListener = sigma.plugins.dragNodes(sigmaInstance,
sigmaInstance.renderers[0])
sigmaInstance.renderers[0]);
dragListener.bind('drag', this._nodeDragged.bind(this))
dragListener.bind('drag', this._nodeDragged.bind(this));
var tooltips = sigma.plugins.tooltips(
sigmaInstance,
@ -718,7 +720,7 @@ export default class GraphContainer extends Component {
node.expand = false;
node.collapse = false;
if (node.folded.nodes.length > 0 && !node.groupedNode) {
if (typeof this.state.sigmaInstance.graph.nodes(node.folded.nodes[0].id) == 'undefined') {
if (typeof this.state.sigmaInstance.graph.nodes(node.folded.nodes[0].id) === 'undefined') {
node.expand = true;
} else {
node.collapse = true;
@ -753,12 +755,12 @@ export default class GraphContainer extends Component {
forcelinkListener.bind('stop', function(event) {
emitter.emit('updateLoadingText', "Fixing Overlap");
sigmaInstance.startNoverlap();
})
});
forcelinkListener.bind('start', function(event){
emitter.emit('updateLoadingText', 'Initial Layout')
emitter.emit('showLoadingIndicator', true)
})
emitter.emit('updateLoadingText', 'Initial Layout');
emitter.emit('showLoadingIndicator', true);
});
var dagreListener = sigma.layouts.dagre.configure(sigmaInstance, {
easing: 'cubicInOut',
@ -774,22 +776,22 @@ export default class GraphContainer extends Component {
emitter.emit('updateLoadingText', "Fixing Overlap");
sigmaInstance.startNoverlap();
needsfix = true;
return
return;
}
}, this);
if (!needsfix){
emitter.emit('updateLoadingText', 'Done!');
sigma.canvas.edges.autoCurve(sigmaInstance)
sigma.canvas.edges.autoCurve(sigmaInstance);
setTimeout(function(){
emitter.emit('showLoadingIndicator', false);
}, 1500)
}, 1500);
}
})
});
dagreListener.bind('start', function(event){
emitter.emit('updateLoadingText', 'Initial Layout')
emitter.emit('showLoadingIndicator', true)
})
emitter.emit('updateLoadingText', 'Initial Layout');
emitter.emit('showLoadingIndicator', true);
});
// var noverlapListener = sigmaInstance.configNoverlap({
// nodeMargin: 5.0,
@ -799,19 +801,19 @@ export default class GraphContainer extends Component {
// });
//
var noverlapListener = sigmaInstance.configNoverlap({})
var noverlapListener = sigmaInstance.configNoverlap({});
noverlapListener.bind('stop', function(event) {
emitter.emit('updateLoadingText', 'Done!');
sigma.canvas.edges.autoCurve(sigmaInstance)
sigma.canvas.edges.autoCurve(sigmaInstance);
setTimeout(function(){
emitter.emit('showLoadingIndicator', false);
}, 1500)
}, 1500);
});
var lowgfx = appStore.performance.lowGraphics
var lowgfx = appStore.performance.lowGraphics;
design = sigma.plugins.design(sigmaInstance);
if (lowgfx){
@ -826,7 +828,7 @@ export default class GraphContainer extends Component {
design.setStyles(appStore.highResStyle);
}
var mode = appStore.performance.nodeLabels
var mode = appStore.performance.nodeLabels;
if (mode === 0){
sigmaInstance.settings('labelThreshold', 500);

View File

@ -1,31 +1,34 @@
import React, { Component } from 'react';
import MenuButton from './MenuButton';
import ProgressBarMenuButton from './ProgressBarMenuButton';
import { buildDomainProps, buildSessionProps, buildLocalAdminProps, buildGroupMembershipProps, buildACLProps } from 'utils';
import { buildDomainProps, buildSessionProps, buildLocalAdminProps, buildGroupMembershipProps, buildACLProps, findObjectType} from 'utils';
import { If, Then, Else } from 'react-if';
const { dialog, clipboard } = require('electron').remote
var fs = require('fs')
var async = require('async')
const { dialog, clipboard, app } = require('electron').remote;
var fs = require('fs');
var async = require('async');
var unzip = require('unzipper');
var fpath = require('path');
var csv = require('fast-csv');
export default class MenuContainer extends Component {
constructor(){
super()
super();
this.state = {
refreshHover: false,
uploading: false,
progress: 0,
parser: null
}
cancelled: false
};
emitter.on('cancelUpload', this.cancelUpload.bind(this))
}
cancelUpload(){
this.state.parser.abort()
this.setState({cancelled: true});
setTimeout(function(){
this.setState({uploading: false})
}.bind(this), 1000)
}.bind(this), 1000);
}
_refreshClick(){
@ -46,9 +49,9 @@ export default class MenuContainer extends Component {
_importClick(){
var fname = dialog.showOpenDialog({
properties: ['openFile']
})
});
if (typeof fname !== 'undefined'){
emitter.emit('import',fname[0])
emitter.emit('import',fname[0]);
}
}
@ -61,190 +64,213 @@ export default class MenuContainer extends Component {
}
_uploadClick(){
var input = jQuery(this.refs.fileInput)
var files = $.makeArray(input[0].files)
var input = jQuery(this.refs.fileInput);
var fileNames = [];
async.eachSeries(files, function(file, callback){
$.each(input[0].files, function(index, file){
fileNames.push({path:file.path, name:file.name});
});
this.unzipNecessary(fileNames).then(function(results){
async.eachSeries(results, function(file, callback){
emitter.emit('showAlert', 'Processing file {}'.format(file.name));
this.processFile(file.path, file, callback)
this.processFile(file.path, callback);
}.bind(this),
function done(){
setTimeout(function(){
this.setState({uploading: false})
}.bind(this), 3000)
}.bind(this))
this.setState({uploading: false});
}.bind(this), 3000);
$.each(results, function(index, file){
if (file.delete){
fs.unlinkSync(file.path);
}
});
}.bind(this));
input.val('')
input.val('');
}.bind(this));
}
async unzipNecessary(files){
var index = 0;
var processed = [];
var tempPath = app.getPath('temp');
while (index < files.length){
var path = files[index].path;
var name = files[index].name;
if (path.endsWith(".zip")){
await fs.createReadStream(path)
.pipe(unzip.Parse())
.on('entry', function(entry){
var output = fpath.join(tempPath, entry.path);
entry.pipe(fs.createWriteStream(output));
processed.push({path:output, name:entry.path, delete: true});
}).promise();
}else{
processed.push({path:path,name:name, delete: false});
}
index++;
}
return processed;
}
_aboutClick(){
emitter.emit('showAbout')
emitter.emit('showAbout');
}
processFile(filename, fileobject, callback){
var sent = 0
var i;
processFile(file, callback){
console.log(file);
var count = 0;
var header = ""
var procHeader = true;
fs.createReadStream(filename)
.on('data', function(chunk) {
for (i=0; i < chunk.length; ++i){
if (procHeader){
header = header + String.fromCharCode(chunk[i])
}
if (chunk[i] == 10){
if (procHeader){
procHeader = false;
}
count++
};
}
var header = "";
var processHeader = true;
var fileType;
//Lets calculate the number of lines in the file and get the header
var input = fs.createReadStream(file);
input.on('data', function (chunk){
for (var i=0; i < chunk.length; ++i){
if (processHeader){
header = header + String.fromCharCode(chunk[i]);
}
if (chunk[i] === 10){
//At the first newline, we look at the header to figure out
if (processHeader){
processHeader = false;
fileType = findObjectType(header);
if (fileType === 'unknown'){
emitter.emit('showAlert', 'Unrecognized CSV Type');
input.close();
}
}
count++;
}
}
})
.on('end', function(){
count = count - 1
var filetype;
if (header.includes('UserName') && header.includes('ComputerName') && header.includes('Weight')){
filetype = 'sessions'
}else if (header.includes('AccountName') && header.includes('AccountType') && header.includes('GroupName')){
filetype = 'groupmembership'
}else if (header.includes('AccountName') && header.includes('AccountType') && header.includes('ComputerName')){
filetype = 'localadmin'
}else if (header.includes('SourceDomain') && header.includes('TargetDomain') && header.includes('TrustDirection') && header.includes('TrustType') && header.includes('Transitive')){
filetype = 'domain'
}else if (header.includes('ActiveDirectoryRights') && header.includes('ObjectType') && header.includes('PrincipalType')){
filetype = 'acl'
}
//We've got our line count for progress
var chunk = [];
var localCount = 0;
var sent = 0;
if (typeof filetype === 'undefined'){
emitter.emit('showAlert', 'Unrecognized CSV Type');
return;
}
//Subtract one line to account for the header
count--;
//Change the UI to display our uploading state
this.setState({
uploading: true,
progress: 0
});
//Start a timer
console.time('IngestTime');
//Start parsing the file
var parser = csv.fromStream(fs.createReadStream(file),
{
headers: true,
ignoreEmpty: true
})
//I have no idea why this workaround is needed. Apparently all my sessions freeze unless I make a random query
setTimeout(function(){
var sess = driver.session()
sess.run('MATCH (n) RETURN (n) LIMIT 1')
.then(function(){
sess.close()
})
}, 1000)
.on('data', function(data){
//On each row, push it into an array and increment a counter
chunk.push(data);
localCount++;
console.time('IngestTime')
Papa.parse(fileobject,{
header: true,
dynamicTyping: true,
skipEmptyLines: true,
chunkSize: 5242880,
//chunkSize: 500000,
chunk: function(rows, parser){
this.setState({parser: parser})
if (rows.data.length === 0){
console.timeEnd('IngestTime')
parser.abort()
this.setState({progress:100})
emitter.emit('refreshDBData')
callback()
return
}
parser.pause()
sent += rows.data.length
if (filetype === 'sessions'){
var query = 'UNWIND {props} AS prop MERGE (user:User {name:prop.account}) WITH user,prop MERGE (computer:Computer {name: prop.computer}) WITH user,computer,prop MERGE (computer)-[:HasSession {Weight : prop.weight}]-(user)'
var props = buildSessionProps(rows.data)
var session = driver.session()
session.run(query, {props: props})
//If we've collected 10k rows, push it all to the DB.
if (localCount % 10000 === 0){
//Pause the parser until upload is complete
parser.pause();
this.uploadData(chunk, fileType, count)
.then(function(){
this.setState({progress: Math.floor((sent / count) * 100)})
session.close()
parser.resume()
}.bind(this))
}else if (filetype === 'groupmembership'){
var props = buildGroupMembershipProps(rows.data)
var userQuery = 'UNWIND {props} AS prop MERGE (user:User {name:prop.account}) WITH user,prop MERGE (group:Group {name:prop.group}) WITH user,group MERGE (user)-[:MemberOf]->(group)'
var computerQuery = 'UNWIND {props} AS prop MERGE (computer:Computer {name:prop.account}) WITH computer,prop MERGE (group:Group {name:prop.group}) WITH computer,group MERGE (computer)-[:MemberOf]->(group)'
var groupQuery = 'UNWIND {props} AS prop MERGE (group1:Group {name:prop.account}) WITH group1,prop MERGE (group2:Group {name:prop.group}) WITH group1,group2 MERGE (group1)-[:MemberOf]->(group2)'
//Update the sent number, and resume the parser
sent += chunk.length;
this.setState({progress: Math.floor(sent / count * 100)});
var session = driver.session()
var tx = session.beginTransaction()
var promises = []
promises.push(tx.run(userQuery, {props: props.users}))
promises.push(tx.run(computerQuery, {props: props.computers}))
promises.push(tx.run(groupQuery, {props: props.groups}))
Promise.all(promises)
.then(function(){
tx.commit()
.then(function(){
session.close()
this.setState({progress: Math.floor((sent / count) * 100)})
parser.resume()
}.bind(this))
}.bind(this))
}else if (filetype === 'localadmin'){
var props = buildLocalAdminProps(rows.data)
var userQuery = 'UNWIND {props} AS prop MERGE (user:User {name: prop.account}) WITH user,prop MERGE (computer:Computer {name: prop.computer}) WITH user,computer MERGE (user)-[:AdminTo]->(computer)'
var groupQuery = 'UNWIND {props} AS prop MERGE (group:Group {name: prop.account}) WITH group,prop MERGE (computer:Computer {name: prop.computer}) WITH group,computer MERGE (group)-[:AdminTo]->(computer)'
var computerQuery = 'UNWIND {props} AS prop MERGE (computer1:Computer {name: prop.account}) WITH computer1,prop MERGE (computer2:Computer {name: prop.computer}) WITH computer1,computer2 MERGE (computer1)-[:AdminTo]->(computer2)'
var session = driver.session()
var tx = session.beginTransaction()
var promises = []
promises.push(tx.run(userQuery, {props: props.users}))
promises.push(tx.run(computerQuery, {props: props.computers}))
promises.push(tx.run(groupQuery, {props: props.groups}))
Promise.all(promises)
.then(function(){
tx.commit()
.then(function(){
session.close()
this.setState({progress: Math.floor((sent / count) * 100)})
parser.resume()
}.bind(this))
}.bind(this))
}else if (filetype === 'domain'){
var props = buildDomainProps(rows.data)
var query = "UNWIND {props} AS prop MERGE (domain1:Domain {name: prop.domain1}) WITH domain1,prop MERGE (domain2:Domain {name: prop.domain2}) WITH domain1,domain2,prop MERGE (domain1)-[:TrustedBy {TrustType : prop.trusttype, Transitive: prop.transitive}]->(domain2)"
var session = driver.session()
session.run(query, {props: props})
.then(function(){
this.setState({progress: Math.floor((sent / count) * 100)})
session.close()
parser.resume()
}.bind(this))
}else if (filetype === 'acl'){
var data = buildACLProps(rows.data)
var promises = []
var session = driver.session()
var tx = session.beginTransaction()
for (var key in data){
var promise = tx.run(data[key].statement, {props: data[key].props})
promises.push(promise)
}
Promise.all(promises)
.then(function(){
tx.commit()
.then(function(){
this.setState({progress: Math.floor((sent / count) * 100)})
session.close()
parser.resume()
}.bind(this))
}.bind(this))
}
}.bind(this)
})
chunk = [];
parser.resume();
}.bind(this));
}
}.bind(this))
.on('end', function(){
//Upload any remaining data
this.uploadData(chunk, fileType, count)
.then(function(){
//Set the uploading state to 100%, refresh the db display, and move on to the next file if there is one
this.setState({progress:100});
emitter.emit('refreshDBData');
console.timeEnd('IngestTime');
callback();
}.bind(this));
}.bind(this));
}.bind(this));
}
async uploadData(currentChunk, filetype, total){
var index = 0;
var processed;
var sent = 0;
var session = driver.session();
if (filetype === 'groupmembership'){
var userQuery = 'UNWIND {props} AS prop MERGE (user:User {name:prop.account}) WITH user,prop MERGE (group:Group {name:prop.group}) WITH user,group MERGE (user)-[:MemberOf {isACL:false}]->(group)';
var computerQuery = 'UNWIND {props} AS prop MERGE (computer:Computer {name:prop.account}) WITH computer,prop MERGE (group:Group {name:prop.group}) WITH computer,group MERGE (computer)-[:MemberOf {isACL:false}]->(group)';
var groupQuery = 'UNWIND {props} AS prop MERGE (group1:Group {name:prop.account}) WITH group1,prop MERGE (group2:Group {name:prop.group}) WITH group1,group2 MERGE (group1)-[:MemberOf {isACL:false}]->(group2)';
processed = buildGroupMembershipProps(currentChunk);
await session.run(userQuery, {props: processed.users});
await session.run(computerQuery, {props: processed.computers});
await session.run(groupQuery, {props: processed.groups});
}else if (filetype === 'localadmin'){
userQuery = 'UNWIND {props} AS prop MERGE (user:User {name: prop.account}) WITH user,prop MERGE (computer:Computer {name: prop.computer}) WITH user,computer MERGE (user)-[:AdminTo {isACL:false}]->(computer)';
groupQuery = 'UNWIND {props} AS prop MERGE (group:Group {name: prop.account}) WITH group,prop MERGE (computer:Computer {name: prop.computer}) WITH group,computer MERGE (group)-[:AdminTo {isACL:false}]->(computer)';
computerQuery = 'UNWIND {props} AS prop MERGE (computer1:Computer {name: prop.account}) WITH computer1,prop MERGE (computer2:Computer {name: prop.computer}) WITH computer1,computer2 MERGE (computer1)-[:AdminTo {isACL:false}]->(computer2)';
processed = buildLocalAdminProps(currentChunk);
await session.run(userQuery, {props: processed.users});
await session.run(computerQuery, {props: processed.computers});
await session.run(groupQuery, {props: processed.groups});
}else if (filetype === 'sessions'){
var query = 'UNWIND {props} AS prop MERGE (user:User {name:prop.account}) WITH user,prop MERGE (computer:Computer {name: prop.computer}) WITH user,computer,prop MERGE (computer)-[:HasSession {Weight : prop.weight, isACL: false}]-(user)';
processed = buildSessionProps(currentChunk);
await session.run(query, {props: processed});
}else if (filetype === 'domain'){
query = "UNWIND {props} AS prop MERGE (domain1:Domain {name: prop.domain1}) WITH domain1,prop MERGE (domain2:Domain {name: prop.domain2}) WITH domain1,domain2,prop MERGE (domain1)-[:TrustedBy {TrustType : prop.trusttype, Transitive: toBoolean(prop.transitive), isACL:false}]->(domain2)";
processed = buildDomainProps(currentChunk);
await session.run(query, {props: processed});
}else if (filetype === 'acl'){
processed = buildACLProps(currentChunk);
for (var key in processed){
await session.run(processed[key].statement, {props: processed[key].props});
}
}else if (filetype === 'userprops'){
$.each(currentChunk, function(index, obj){
var spn = obj.ServicePrincipalNames;
var sh = obj.SidHistory;
if (spn === ""){
obj.ServicePrincipalNames = [];
}else{
obj.ServicePrincipalNames = spn.split('|');
}
});
query = 'UNWIND {props} AS prop MERGE (user:User {name: upper(prop.AccountName)}) SET user.Enabled = toBoolean(prop.Enabled),user.PwdLastSet = toInt(prop.PwdLastSet),user.LastLogon = toInt(prop.LastLogon),user.Sid = prop.Sid,user.SidHistory = prop.SidHistory,user.HasSPN = toBoolean(prop.HasSPN),user.DisplayName=prop.DisplayName,user.ServicePrincipalNames = prop.ServicePrincipalNames,user.Email=prop.Email';
await session.run(query, {props:currentChunk});
}else if (filetype === 'compprops'){
query = 'UNWIND {props} AS prop MERGE (comp:Computer {name: upper(prop.AccountName)}) SET comp.Enabled=toBoolean(prop.Enabled),comp.PwdLastSet=toInt(prop.PwdLastSet),comp.LastLogon=toInt(prop.LastLogon),comp.OperatingSystem=prop.OperatingSystem,comp.Sid=prop.Sid,comp.UnconstrainedDelegation=toBoolean(prop.UnconstrainedDelegation)';
await session.run(query, {props:currentChunk});
}
}
render() {
return (

View File

@ -1,43 +1,19 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types'
import PropTypes from 'prop-types';
export default class ProgressBarMenuButton extends Component {
constructor(){
super()
super();
this.state = {
expanded: false
}
};
}
_leave(e){
this.setState({expanded: false})
var target = $(e.target)
var oldWidth = target.width()
target.html('{}%'.format(this.props.progress))
target.animate({
width: '41px'
}, 100)
}
_enter(e){
this.setState({expanded: true})
var target = $(e.target)
var oldWidth = target.width()
var template = `
<div class="progress" style="margin-bottom:0px">
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-value-now={} aria-value-max="100" style="width:{}%">
</div>
<span>
{}%
</span>
</div>
`.formatAll(this.props.progress)
target.html(template)
target.animate({
width: '150px'
}, 100)
componentDidMount(){
$(this.refs.btn).html('{}%'.format(this.props.progress));
$(this.refs.btn).css('padding','6px 0px 6px 0px');
$(this.refs.btn).css('width','41px');
}
componentWillReceiveProps(nextProps){
@ -48,23 +24,47 @@ export default class ProgressBarMenuButton extends Component {
<span>
{}%
</span>
</div>`.formatAll(nextProps.progress)
$(this.refs.btn).html(template)
</div>`.formatAll(nextProps.progress);
$(this.refs.btn).html(template);
}else{
$(this.refs.btn).html('{}%'.format(nextProps.progress))
$(this.refs.btn).html('{}%'.format(nextProps.progress));
}
this.forceUpdate()
this.forceUpdate();
}
shouldComponentUpdate(nextProps, nextState){
return true
return true;
}
componentDidMount(){
$(this.refs.btn).html('{}%'.format(this.props.progress))
$(this.refs.btn).css('padding','6px 0px 6px 0px')
$(this.refs.btn).css('width','41px')
_leave(e){
this.setState({expanded: false});
var target = $(e.target);
var oldWidth = target.width();
target.html('{}%'.format(this.props.progress));
target.animate({
width: '41px'
}, 100);
}
_enter(e){
this.setState({expanded: true});
var target = $(e.target);
var oldWidth = target.width();
var template = `
<div class="progress" style="margin-bottom:0px">
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-value-now={} aria-value-max="100" style="width:{}%">
</div>
<span>
{}%
</span>
</div>
`.formatAll(this.props.progress);
target.html(template);
target.animate({
width: '150px'
}, 100);
}
render() {
@ -77,4 +77,4 @@ export default class ProgressBarMenuButton extends Component {
ProgressBarMenuButton.propTypes = {
progress : React.PropTypes.number.isRequired,
click : React.PropTypes.func.isRequired
}
};

View File

@ -1,86 +1,86 @@
import React, { Component } from 'react';
import GlyphiconSpan from '../GlyphiconSpan'
import Icon from '../Icon'
import TabContainer from './TabContainer'
import GlyphiconSpan from '../GlyphiconSpan';
import Icon from '../Icon';
import TabContainer from './TabContainer';
export default class SearchContainer extends Component {
constructor(props){
super(props)
super(props);
this.state = {
mainPlaceholder:"Start typing to search for a node...",
pathfindingIsOpen: false,
mainValue: "",
pathfindValue: ""
}
};
}
_onPathfindClick(){
jQuery(this.refs.pathfinding).slideToggle()
var p = !this.state.pathfindingIsOpen
var t = this.state.pathfindingIsOpen ? "Start typing to search for a node..." : "Start Node"
jQuery(this.refs.pathfinding).slideToggle();
var p = !this.state.pathfindingIsOpen;
var t = this.state.pathfindingIsOpen ? "Start typing to search for a node..." : "Start Node";
this.setState({
pathfindingIsOpen: p,
mainPlaceholder: t
})
});
}
_onPlayClick(){
var start = jQuery(this.refs.searchbar).val()
var end = jQuery(this.refs.pathbar).val()
var start = jQuery(this.refs.searchbar).val();
var end = jQuery(this.refs.pathbar).val();
if (start !== "" && end !== ""){
emitter.emit('pathQuery', start, end)
emitter.emit('pathQuery', start, end);
}
}
_onExpandClick(){
jQuery(this.refs.tabs).slideToggle()
jQuery(this.refs.tabs).slideToggle();
}
openNodeTab(){
var e = jQuery(this.refs.tabs)
var e = jQuery(this.refs.tabs);
if (!(e.is(":visible"))){
e.slideToggle()
e.slideToggle();
}
}
componentDidMount() {
jQuery(this.refs.pathfinding).slideToggle(0);
jQuery(this.refs.tabs).slideToggle(0);
emitter.on('userNodeClicked', this.openNodeTab.bind(this))
emitter.on('groupNodeClicked', this.openNodeTab.bind(this))
emitter.on('computerNodeClicked', this.openNodeTab.bind(this))
emitter.on('domainNodeClicked', this.openNodeTab.bind(this))
emitter.on('userNodeClicked', this.openNodeTab.bind(this));
emitter.on('groupNodeClicked', this.openNodeTab.bind(this));
emitter.on('computerNodeClicked', this.openNodeTab.bind(this));
emitter.on('domainNodeClicked', this.openNodeTab.bind(this));
emitter.on('setStart', function(payload){
jQuery(this.refs.searchbar).val(payload);
}.bind(this))
}.bind(this));
emitter.on('setEnd', function(payload){
jQuery(this.refs.pathbar).val(payload);
var e = jQuery(this.refs.pathfinding)
var e = jQuery(this.refs.pathfinding);
if (!(e.is(":visible"))){
e.slideToggle()
e.slideToggle();
}
}.bind(this))
}.bind(this));
jQuery(this.refs.searchbar).typeahead({
source: function(query, process) {
var session = driver.session()
var t = '(?i).*' + query + '.*'
var data = []
var session = driver.session();
var t = '(?i).*' + query + '.*';
var data = [];
session.run("MATCH (n) WHERE n.name =~ {name} RETURN n LIMIT 10", {name:t})
.then(function(results){
$.each(results.records, function(index, record){
data.push(record._fields[0].properties.name + "#" + record._fields[0].labels[0])
})
session.close()
return process(data)
})
data.push(record._fields[0].properties.name + "#" + record._fields[0].labels[0]);
});
session.close();
return process(data);
});
},
afterSelect: function(selected) {
if (!this.state.pathfindingIsOpen) {
var statement = "MATCH (n) WHERE n.name = {name} RETURN n"
emitter.emit('searchQuery', statement, {name: selected.split("#")[0]})
var statement = "MATCH (n) WHERE n.name = {name} RETURN n";
emitter.emit('searchQuery', statement, {name: selected.split("#")[0]});
} else {
var start = jQuery(this.refs.searchbar).val();
var end = jQuery(this.refs.pathbar).val();
@ -91,57 +91,57 @@ export default class SearchContainer extends Component {
}.bind(this),
autoSelect: false,
updater: function(item){
return item.split("#")[0]
return item.split("#")[0];
},
highlighter: function(item) {
var parts = item.split("#")
var parts = item.split("#");
var query = this.query;
var icon = "";
var html = ""
var html = "";
switch (parts[1]){
case "Group":
icon = "<i style=\"float:right\" class=\"fa fa-users\"></i>"
icon = "<i style=\"float:right\" class=\"fa fa-users\"></i>";
break;
case "User":
icon = "<i style=\"float:right\" class=\"fa fa-user\"></i>"
icon = "<i style=\"float:right\" class=\"fa fa-user\"></i>";
break;
case "Computer":
icon = "<i style=\"float:right\" class=\"fa fa-desktop\"></i>"
icon = "<i style=\"float:right\" class=\"fa fa-desktop\"></i>";
break;
case "Domain":
icon = "<i style=\"float:right\" class=\"fa fa-globe\"></i>"
break
icon = "<i style=\"float:right\" class=\"fa fa-globe\"></i>";
break;
}
html = '<div>' + parts[0] + ' ' + icon + '</div>'
html = '<div>' + parts[0] + ' ' + icon + '</div>';
var reEscQuery = query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
var reQuery = new RegExp('(' + reEscQuery + ')', "gi");
var jElem = $(html)
var jElem = $(html);
var textNodes = $(jElem.find('*')).add(jElem).contents().filter(function () { return this.nodeType === 3; });
textNodes.replaceWith(function() {
return $(this).text().replace(reQuery, '<strong>$1</strong>')
return $(this).text().replace(reQuery, '<strong>$1</strong>');
});
return jElem.html();
}
}
)
);
jQuery(this.refs.pathbar).typeahead({
source: function(query, process) {
var session = driver.session()
var t = '(?i).*' + query + '.*'
var data = []
var session = driver.session();
var t = '(?i).*' + query + '.*';
var data = [];
session.run("MATCH (n) WHERE n.name =~ {name} RETURN n LIMIT 10", {name:t})
.then(function(results){
$.each(results.records, function(index, record){
data.push(record._fields[0].properties.name + "#" + record._fields[0].labels[0])
})
session.close()
return process(data)
})
data.push(record._fields[0].properties.name + "#" + record._fields[0].labels[0]);
});
session.close();
return process(data);
});
},
afterSelect: function(selected) {
var start = jQuery(this.refs.searchbar).val();
@ -152,47 +152,47 @@ export default class SearchContainer extends Component {
}.bind(this),
autoSelect: false,
updater: function(item){
return item.split("#")[0]
return item.split("#")[0];
},
highlighter: function(item) {
var parts = item.split("#")
var parts = item.split("#");
var query = this.query;
var icon = "";
var html = ""
var html = "";
switch (parts[1]){
case "Group":
icon = "<i style=\"float:right\" class=\"fa fa-users\"></i>"
icon = "<i class=\"fa fa-users\"></i>";
break;
case "User":
icon = "<i style=\"float:right\" class=\"fa fa-user\"></i>"
icon = "<i class=\"fa fa-user\"></i>";
break;
case "Computer":
icon = "<i style=\"float:right\" class=\"fa fa-desktop\"></i>"
icon = "<i class=\"fa fa-desktop\"></i>";
break;
case "Domain":
icon = "<i style=\"float:right\" class=\"fa fa-globe\"></i>"
break
icon = "<i class=\"fa fa-globe\"></i>";
break;
}
html = '<div>' + parts[0] + ' ' + icon + '</div>'
html = '<div>' + parts[0] + ' ' + icon + '</div>';
var reEscQuery = query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
var reQuery = new RegExp('(' + reEscQuery + ')', "gi");
var jElem = $(html)
var jElem = $(html);
var textNodes = $(jElem.find('*')).add(jElem).contents().filter(function () { return this.nodeType === 3; });
textNodes.replaceWith(function() {
return $(this).text().replace(reQuery, '<strong>$1</strong>')
return $(this).text().replace(reQuery, '<strong>$1</strong>');
});
return jElem.html();
}
}
)
);
}
_inputKeyPress(e){
var key = e.keyCode ? e.keyCode : e.which
var key = e.keyCode ? e.keyCode : e.which;
var start = jQuery(this.refs.searchbar).val();
var end = jQuery(this.refs.pathbar).val();
var stop = false;
@ -201,17 +201,17 @@ export default class SearchContainer extends Component {
if (!$('.searchSelectorS > ul').is(':hidden')){
$('.searchSelectorS > ul li').each(function(i){
if($(this).hasClass('active')){
stop = true
stop = true;
}
})
});
}
if (!$('.searchSelectorP > ul').is(':hidden')){
$('.searchSelectorP > ul li').each(function(i){
if($(this).hasClass('active')){
stop = true
stop = true;
}
})
});
}
if (stop){
return;
@ -219,8 +219,8 @@ export default class SearchContainer extends Component {
if (!this.state.pathfindingIsOpen) {
if (start !== ""){
var statement = "MATCH (n) WHERE n.name =~ {regex} RETURN n";
var regex = '(?i).*' + start + '.*'
emitter.emit('searchQuery', statement, {regex:regex})
var regex = '(?i).*' + start + '.*';
emitter.emit('searchQuery', statement, {regex:regex});
}
} else {
var start = jQuery(this.refs.searchbar).val();
@ -257,7 +257,7 @@ export default class SearchContainer extends Component {
tooltipTitle="Back"
classes="input-group-addon spanfix"
click={function(){
emitter.emit('graphBack')
emitter.emit('graphBack');
}}>
<Icon glyph="step-backward" extraClass="menuglyph" />
</GlyphiconSpan>
@ -289,6 +289,6 @@ export default class SearchContainer extends Component {
<TabContainer />
</div>
</div>
)
);
}
}

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import NodeALink from './NodeALink'
import PropTypes from 'prop-types'
import NodeALink from './NodeALink';
import PropTypes from 'prop-types';
export default class ComputerNodeData extends Component {
constructor(){
@ -8,8 +8,6 @@ export default class ComputerNodeData extends Component {
this.state = {
label: "",
os: "None",
unconstrained: "None",
explicitAdmins: -1,
unrolledAdmins: -1,
firstDegreeGroupMembership: -1,
@ -22,8 +20,9 @@ export default class ComputerNodeData extends Component {
groupDelegatedControl: -1,
transitiveControl: -1,
derivativeLocalAdmins: -1,
driversessions: []
}
driversessions: [],
propertyMap: {}
};
emitter.on('computerNodeClicked', this.getNodeData.bind(this));
}
@ -31,11 +30,9 @@ export default class ComputerNodeData extends Component {
getNodeData(payload){
$.each(this.state.driversessions, function(index, record){
record.close();
})
});
this.setState({
label: payload,
os: "None",
unconstrained: "None",
explicitAdmins: -1,
unrolledAdmins: -1,
firstDegreeGroupMembership: -1,
@ -47,102 +44,132 @@ export default class ComputerNodeData extends Component {
firstdegreeControl: -1,
groupDelegatedControl: -1,
transitiveControl: -1,
derivativeLocalAdmins: -1
})
derivativeLocalAdmins: -1,
propertyMap: {}
});
var s1 = driver.session()
var s2 = driver.session()
var s3 = driver.session()
var s4 = driver.session()
var s5 = driver.session()
var s6 = driver.session()
var s7 = driver.session()
var s8 = driver.session()
var s9 = driver.session()
var s10 = driver.session()
var s11 = driver.session()
var s12 = driver.session()
var s13 = driver.session()
var s1 = driver.session();
var s2 = driver.session();
var s3 = driver.session();
var s4 = driver.session();
var s5 = driver.session();
var s6 = driver.session();
var s7 = driver.session();
var s8 = driver.session();
var s9 = driver.session();
var s10 = driver.session();
var s11 = driver.session();
var s12 = driver.session();
var s13 = driver.session();
var propCollection = driver.session();
propCollection.run("MATCH (c:Computer {name:{name}}) RETURN c", {name:payload})
.then(function(result){
var properties = result.records[0]._fields[0].properties;
this.setState({propertyMap: properties});
propCollection.close();
}.bind(this));
s1.run("MATCH (a)-[b:AdminTo]->(c:Computer {name:{name}}) RETURN count(a)", {name:payload})
.then(function(result){
this.setState({'explicitAdmins':result.records[0]._fields[0].low})
s1.close()
}.bind(this))
this.setState({'explicitAdmins':result.records[0]._fields[0].low});
s1.close();
}.bind(this));
s2.run("MATCH p=(n:User)-[r:MemberOf|AdminTo*1..]->(m:Computer {name:{name}}) RETURN count(distinct(n))", {name:payload})
.then(function(result){
this.setState({'unrolledAdmins':result.records[0]._fields[0].low})
s2.close()
}.bind(this))
this.setState({'unrolledAdmins':result.records[0]._fields[0].low});
s2.close();
}.bind(this));
s3.run("MATCH (m:Computer {name:{name}}), (n:Computer), (m)-[r:AdminTo]->(n) RETURN count(distinct(m))", {name:payload})
.then(function(result){
this.setState({'firstDegreeLocalAdmin':result.records[0]._fields[0].low})
s3.close()
}.bind(this))
this.setState({'firstDegreeLocalAdmin':result.records[0]._fields[0].low});
s3.close();
}.bind(this));
s4.run("MATCH p=(n:Computer {name:{name}})-[r1:MemberOf*1..]->(g:Group)-[r2:AdminTo]->(c2:Computer) RETURN count(c2)", {name:payload})
.then(function(result){
this.setState({'groupDelegatedLocalAdmin':result.records[0]._fields[0].low})
s4.close()
}.bind(this))
this.setState({'groupDelegatedLocalAdmin':result.records[0]._fields[0].low});
s4.close();
}.bind(this));
s5.run("MATCH (n:Computer {name:{name}}), (m:Computer), p=shortestPath((n)-[r:AdminTo|MemberOf*1..]->(m)) RETURN count(distinct(m))", {name:payload})
s5.run("MATCH (n:Computer {name:{name}}), (m:Computer) WHERE NOT m.name={name} MATCH p=shortestPath((n)-[r:AdminTo|MemberOf*1..]->(m)) RETURN count(distinct(m))", {name:payload})
.then(function(result){
this.setState({'derivativeLocalAdmin':result.records[0]._fields[0].low})
s5.close()
}.bind(this))
this.setState({'derivativeLocalAdmin':result.records[0]._fields[0].low});
s5.close();
}.bind(this));
s6.run("MATCH (n:Computer {name:{name}}),(target:Group), (n)-[r:MemberOf]->(target) RETURN count(target)", {name:payload})
.then(function(result){
this.setState({'firstDegreeGroupMembership':result.records[0]._fields[0].low})
s6.close()
}.bind(this))
this.setState({'firstDegreeGroupMembership':result.records[0]._fields[0].low});
s6.close();
}.bind(this));
s7.run("MATCH p = (c:Computer {name:{name}})-[r:MemberOf*1..]->(g:Group) RETURN COUNT(DISTINCT(g))", {name:payload})
.then(function(result){
this.setState({'unrolledGroupMembership':result.records[0]._fields[0].low})
s7.close()
}.bind(this))
this.setState({'unrolledGroupMembership':result.records[0]._fields[0].low});
s7.close();
}.bind(this));
s8.run("MATCH (m:Computer {name:{name}})-[r:HasSession]->(n:User) WITH n,r,m WHERE NOT n.name ENDS WITH '$' RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'sessions':result.records[0]._fields[0].low})
s8.close()
}.bind(this))
this.setState({'sessions':result.records[0]._fields[0].low});
s8.close();
}.bind(this));
s9.run("MATCH p = shortestPath((n)-[r:AdminTo|MemberOf|HasSession*1..]->(m:Computer {name:{name}})) RETURN COUNT(DISTINCT(n))", {name:payload})
s9.run("MATCH (n) WHERE NOT n.name={name} WITH n MATCH p = shortestPath((n)-[r:AdminTo|MemberOf|HasSession*1..]->(m:Computer {name:{name}})) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'derivativeLocalAdmins':result.records[0]._fields[0].low})
s9.close()
}.bind(this))
this.setState({'derivativeLocalAdmins':result.records[0]._fields[0].low});
s9.close();
}.bind(this));
s10.run("MATCH p = (c:Computer {name:{name}})-[r:MemberOf*1..]->(g:Group) WHERE NOT g.domain = c.domain RETURN COUNT(DISTINCT(g))", {name:payload})
.then(function(result){
this.setState({'foreignGroupMembership':result.records[0]._fields[0].low})
s10.close()
}.bind(this))
this.setState({'foreignGroupMembership':result.records[0]._fields[0].low});
s10.close();
}.bind(this));
s11.run("MATCH p = (c:Computer {name:{name}})-[r:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'firstdegreeControl':result.records[0]._fields[0].low})
s11.close()
}.bind(this))
this.setState({'firstdegreeControl':result.records[0]._fields[0].low});
s11.close();
}.bind(this));
s12.run("MATCH p = (c:Computer {name:{name}})-[r1:MemberOf*1..]->(g:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'groupDelegatedControl':result.records[0]._fields[0].low})
s12.close()
}.bind(this))
this.setState({'groupDelegatedControl':result.records[0]._fields[0].low});
s12.close();
}.bind(this));
s13.run("MATCH p = shortestPath((c:Computer {name:{name}})-[r:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN COUNT(DISTINCT(n))", {name:payload})
s13.run("MATCH (n) WHERE NOT n.name={name} WITH n MATCH p = shortestPath((c:Computer {name:{name}})-[r:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'transitiveControl':result.records[0]._fields[0].low})
s13.close()
}.bind(this))
this.setState({'transitiveControl':result.records[0]._fields[0].low});
s13.close();
}.bind(this));
this.setState({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13]})
this.setState({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,propCollection]});
}
convertToDisplayProp(propName){
var obj = this.state.propertyMap[propName];
var type = typeof obj;
if (type === 'undefined'){
return "No Data";
}else if (obj.hasOwnProperty('low')){
var t = obj.low;
if (t === 0){
return "Never";
}else{
return new Date(obj.low * 1000).toUTCString();
}
}else if (type === 'boolean'){
return obj.toString().toTitleCase();
}else if (obj === ""){
return "None";
}else{
return obj;
}
}
render() {
@ -160,13 +187,19 @@ export default class ComputerNodeData extends Component {
OS
</dt>
<dd>
{this.state.os}
{this.convertToDisplayProp("OperatingSystem")}
</dd>
<dt>
Enabled
</dt>
<dd>
{this.convertToDisplayProp("Enabled")}
</dd>
<dt>
Allows Unconstrained Delegation
</dt>
<dd>
{this.state.unconstrained}
{this.convertToDisplayProp("UnconstrainedDelegation")}
</dd>
<dt>
Sessions
@ -177,10 +210,10 @@ export default class ComputerNodeData extends Component {
value={this.state.sessions}
click={function(){
emitter.emit('query',
"MATCH (m:Computer {name:{name}})-[r:HasSession]->(n:User) WITH n,r,m WHERE NOT n.name ENDS WITH '$' RETURN n,r,m", {name: this.state.label})
}.bind(this)} />
"MATCH (m:Computer {name:{name}})-[r:HasSession]->(n:User) WITH n,r,m WHERE NOT n.name ENDS WITH '$' RETURN n,r,m", {name: this.state.label});
}.bind(this)}
/>
</dd>
<br />
<h4>Local Admins</h4>
<dt>
Explicit Admins
@ -191,8 +224,9 @@ export default class ComputerNodeData extends Component {
value={this.state.explicitAdmins}
click={function(){
emitter.emit('query',
"MATCH (n)-[r:AdminTo]->(m:Computer {name:{name}}) RETURN n,r,m",{name: this.state.label})
}.bind(this)} />
"MATCH (n)-[r:AdminTo]->(m:Computer {name:{name}}) RETURN n,r,m",{name: this.state.label});
}.bind(this)}
/>
</dd>
<dt>
Unrolled Admins
@ -205,8 +239,9 @@ export default class ComputerNodeData extends Component {
emitter.emit('query',
"MATCH p = (n:User)-[r:MemberOf*1..]->(g:Group)-[r2:AdminTo]->(c:Computer {name:{name}}) RETURN p",
{name: this.state.label},
this.state.label)
}.bind(this)} />
this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Derivative Local Admins
@ -217,10 +252,10 @@ export default class ComputerNodeData extends Component {
value={this.state.derivativeLocalAdmins}
click={function(){
emitter.emit('query',
"MATCH p = shortestPath((n)-[r:AdminTo|MemberOf|HasSession*1..]->(m:Computer {name:{name}})) RETURN p",{name: this.state.label}, this.state.label)
}.bind(this)} />
"MATCH (n) WHERE NOT n.name={name} WITH n MATCH p = shortestPath((n)-[r:AdminTo|MemberOf|HasSession*1..]->(m:Computer {name:{name}})) RETURN p",{name: this.state.label}, this.state.label);
}.bind(this)}
/>
</dd>
<br />
<h4>Group Memberships</h4>
<dt>
First Degree Group Membership
@ -231,8 +266,9 @@ export default class ComputerNodeData extends Component {
value={this.state.firstDegreeGroupMembership}
click={function(){
emitter.emit('query',
"MATCH (n:Computer {name:{name}}),(m:Group), (n)-[r:MemberOf]->(m) RETURN n,r,m",{name: this.state.label}, this.state.label)
}.bind(this)} />
"MATCH (n:Computer {name:{name}}),(m:Group), (n)-[r:MemberOf]->(m) RETURN n,r,m",{name: this.state.label}, this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Unrolled Group Membership
@ -243,8 +279,9 @@ export default class ComputerNodeData extends Component {
value={this.state.unrolledGroupMembership}
click={function(){
emitter.emit('query',
"MATCH p = (n:Computer {name:{name}})-[r:MemberOf*1..]->(m:Group) RETURN p",{name: this.state.label}, this.state.label)
}.bind(this)} />
"MATCH p = (n:Computer {name:{name}})-[r:MemberOf*1..]->(m:Group) RETURN p",{name: this.state.label}, this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Foreign Group Membership
@ -255,10 +292,10 @@ export default class ComputerNodeData extends Component {
value={this.state.foreignGroupMembership}
click={function(){
emitter.emit('query',
"MATCH p = (c:Computer {name:{name}})-[r:MemberOf*1..]->(g:Group) WHERE NOT g.domain = c.domain RETURN p",{name: this.state.label}, this.state.label)
}.bind(this)} />
"MATCH p = (c:Computer {name:{name}})-[r:MemberOf*1..]->(g:Group) WHERE NOT g.domain = c.domain RETURN p",{name: this.state.label}, this.state.label);
}.bind(this)}
/>
</dd>
<br />
<h4>Local Admin Rights</h4>
<dt>
First Degree Local Admin
@ -269,8 +306,9 @@ export default class ComputerNodeData extends Component {
value={this.state.firstDegreeLocalAdmin}
click={function(){
emitter.emit('query',
"MATCH (n:Computer {name:{name}}), (m:Computer), p=(n)-[r:AdminTo]->(m) RETURN p",{name: this.state.label}, this.state.label)
}.bind(this)} />
"MATCH (n:Computer {name:{name}}), (m:Computer), p=(n)-[r:AdminTo]->(m) RETURN p",{name: this.state.label}, this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Group Delegated Local Admin
@ -281,8 +319,9 @@ export default class ComputerNodeData extends Component {
value={this.state.groupDelegatedLocalAdmin}
click={function(){
emitter.emit('query',
"MATCH p=(n:Computer {name:{name}})-[r1:MemberOf*1..]->(g:Group)-[r2:AdminTo]->(m:Computer) RETURN p",{name: this.state.label}, this.state.label)
}.bind(this)} />
"MATCH p=(n:Computer {name:{name}})-[r1:MemberOf*1..]->(g:Group)-[r2:AdminTo]->(m:Computer) RETURN p",{name: this.state.label}, this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Derivative Local Admin
@ -293,10 +332,10 @@ export default class ComputerNodeData extends Component {
value={this.state.derivativeLocalAdmin}
click={function(){
emitter.emit('query',
"MATCH p = shortestPath((c1:Computer {name:{name}})-[r:AdminTo|MemberOf|HasSession*1..]->(c:Computer)) RETURN p",{name: this.state.label}, this.state.label)
}.bind(this)} />
"MATCH (c:Computer) WHERE NOT c.name={name} WITH c MATCH p = shortestPath((c1:Computer {name:{name}})-[r:AdminTo|MemberOf|HasSession*1..]->(c)) RETURN p",{name: this.state.label}, this.state.label);
}.bind(this)}
/>
</dd>
<br />
<h4>Outbound Object Control</h4>
<dt>
First Degree Object Control
@ -306,8 +345,9 @@ export default class ComputerNodeData extends Component {
ready={this.state.firstdegreeControl !== -1}
value={this.state.firstdegreeControl}
click={function(){
emitter.emit('query', "MATCH p = (c:Computer {name:{name}})-[r1:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH p = (c:Computer {name:{name}})-[r1:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label});
}.bind(this)}
/>
</dd>
<dt>
Group Delegated Object Control
@ -318,8 +358,9 @@ export default class ComputerNodeData extends Component {
value={this.state.groupDelegatedControl}
click={function(){
emitter.emit('query', "MATCH p = (c:Computer {name:{name}})-[r1:MemberOf*1..]->(g:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
,this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Transitive Object Control
@ -329,9 +370,10 @@ export default class ComputerNodeData extends Component {
ready={this.state.transitiveControl !== -1}
value={this.state.transitiveControl}
click={function(){
emitter.emit('query', "MATCH p = shortestPath((c:Computer {name:{name}})-[r1:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
emitter.emit('query', "MATCH (n) WHERE NOT n.name={name} WITH n MATCH p = shortestPath((c:Computer {name:{name}})-[r1:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN p", {name:this.state.label}
,this.state.label);
}.bind(this)}
/>
</dd>
</dl>
</div>
@ -341,4 +383,4 @@ export default class ComputerNodeData extends Component {
ComputerNodeData.propTypes= {
visible : React.PropTypes.bool.isRequired
}
};

View File

@ -3,7 +3,7 @@ import LogoutModal from 'modals/LogoutModal';
export default class DatabaseDataDisplay extends Component {
constructor(){
super()
super();
this.state = {
url: appStore.databaseInfo.url,
user: appStore.databaseInfo.user,
@ -12,42 +12,91 @@ export default class DatabaseDataDisplay extends Component {
num_groups: 'Refreshing',
num_relationships: 'Refreshing',
num_sessions: 'Refreshing',
num_acls: 'Refreshing',
interval: null
}
};
}
componentDidMount() {
this.refreshDBData()
var x = setInterval(function(){
this.refreshDBData()
}.bind(this), 60000);
this.setState({
interval: x
})
emitter.on('hideDBClearModal', this.refreshDBData.bind(this))
emitter.on('refreshDBData', this.refreshDBData.bind(this))
this.refreshDBData();
emitter.on('hideDBClearModal', this.refreshDBData.bind(this));
emitter.on('refreshDBData', this.refreshDBData.bind(this));
this.createInterval();
}
componentWillUnmount() {
clearInterval(this.state.interval)
clearInterval(this.state.interval);
this.setState({
interval: null,
session: null
})
});
}
createInterval(){
var x = setInterval(function(){
this.refreshDBData();
}.bind(this), 60000);
this.setState({
interval: x
});
};
toggleLogoutModal(){
emitter.emit('showLogout');
}
toggleDBWarnModal(){
emitter.emit('openDBWarnModal')
emitter.emit('openDBWarnModal');
}
toggleSessionClearModal(){
emitter.emit('openSessionClearModal')
emitter.emit('openSessionClearModal');
}
refreshDBData(){
var s1 = driver.session();
var s2 = driver.session();
var s3 = driver.session();
var s4 = driver.session();
var s5 = driver.session();
var s6 = driver.session();
s1.run("MATCH (n:User) WHERE NOT n.name ENDS WITH '$' RETURN count(n)")
.then(function(result){
this.setState({'num_users':result.records[0]._fields[0].low});
s1.close();
}.bind(this));
s2.run("MATCH (n:Group) RETURN count(n)")
.then(function(result){
this.setState({'num_groups':result.records[0]._fields[0].low});
s2.close();
}.bind(this));
s3.run("MATCH (n:Computer) RETURN count(n)")
.then(function(result){
this.setState({'num_computers':result.records[0]._fields[0].low});
s3.close();
}.bind(this));
s4.run("MATCH ()-[r:HasSession]->() RETURN count(r)")
.then(function(result){
this.setState({'num_sessions':result.records[0]._fields[0].low});
s4.close();
}.bind(this));
s6.run("MATCH ()-[r {isACL: true}]->() RETURN count(r)")
.then(function(result){
this.setState({'num_acls':result.records[0]._fields[0].low});
s6.close();
}.bind(this));
s5.run("MATCH ()-[r]->() RETURN count(r)")
.then(function(result){
this.setState({'num_relationships':result.records[0]._fields[0].low});
s5.close();
}.bind(this));
}
render() {
return (
@ -66,13 +115,15 @@ export default class DatabaseDataDisplay extends Component {
<dd>{this.state.num_groups}</dd>
<dt>Sessions</dt>
<dd>{this.state.num_sessions}</dd>
<dt>ACLs</dt>
<dd>{this.state.num_acls}</dd>
<dt>Relationships</dt>
<dd>{this.state.num_relationships}</dd>
</dl>
<div className="text-center">
<div className="btn-group btn-group-sm dbbuttons">
<button type="button" className="btn btn-success" onClick={function(){this.refreshDBData()}.bind(this)}>Refresh DB Stats</button>
<button type="button" className="btn btn-success" onClick={function(){this.refreshDBData();}.bind(this)}>Refresh DB Stats</button>
<button type="button" className="btn btn-info" onClick={this.toggleSessionClearModal}>Clear Sessions</button>
<button type="button" className="btn btn-warning" onClick={this.toggleLogoutModal}>Log Out/Switch DB</button>
<button type="button" className="btn btn-danger" onClick={this.toggleDBWarnModal}>Clear Database</button>
@ -81,42 +132,4 @@ export default class DatabaseDataDisplay extends Component {
</div>
);
}
refreshDBData(){
var s1 = driver.session()
var s2 = driver.session()
var s3 = driver.session()
var s4 = driver.session()
var s5 = driver.session()
s1.run("MATCH (n:User) WHERE NOT n.name ENDS WITH '$' RETURN count(n)")
.then(function(result){
this.setState({'num_users':result.records[0]._fields[0].low})
s1.close()
}.bind(this))
s2.run("MATCH (n:Group) RETURN count(n)")
.then(function(result){
this.setState({'num_groups':result.records[0]._fields[0].low})
s2.close()
}.bind(this))
s3.run("MATCH (n:Computer) RETURN count(n)")
.then(function(result){
this.setState({'num_computers':result.records[0]._fields[0].low})
s3.close()
}.bind(this))
s4.run("MATCH ()-[r:HasSession]->() RETURN count(r)")
.then(function(result){
this.setState({'num_sessions':result.records[0]._fields[0].low})
s4.close()
}.bind(this))
s5.run("MATCH ()-[r]->() RETURN count(r)")
.then(function(result){
this.setState({'num_relationships':result.records[0]._fields[0].low})
s5.close()
}.bind(this))
}
}

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import NodeALink from './NodeALink.jsx'
import LoadLabel from './LoadLabel.jsx'
import PropTypes from 'prop-types'
import NodeALink from './NodeALink.jsx';
import LoadLabel from './LoadLabel.jsx';
import PropTypes from 'prop-types';
export default class DomainNodeData extends Component {
constructor(){
@ -19,7 +19,7 @@ export default class DomainNodeData extends Component {
firstDegreeInboundTrusts: -1,
effectiveInboundTrusts: -1,
driversessions: []
}
};
emitter.on('domainNodeClicked', this.getNodeData.bind(this));
}
@ -27,7 +27,7 @@ export default class DomainNodeData extends Component {
getNodeData(payload){
$.each(this.state.driversessions, function(index, record){
record.close();
})
});
this.setState({
label: payload,
users: -1,
@ -39,73 +39,73 @@ export default class DomainNodeData extends Component {
effectiveOutboundTrusts: -1,
firstDegreeInboundTrusts: -1,
effectiveInboundTrusts: -1
})
});
var s1 = driver.session()
var s2 = driver.session()
var s3 = driver.session()
var s4 = driver.session()
var s5 = driver.session()
var s6 = driver.session()
var s7 = driver.session()
var s8 = driver.session()
var s9 = driver.session()
var s1 = driver.session();
var s2 = driver.session();
var s3 = driver.session();
var s4 = driver.session();
var s5 = driver.session();
var s6 = driver.session();
var s7 = driver.session();
var s8 = driver.session();
var s9 = driver.session();
s1.run("MATCH (a:User) WHERE a.name ENDS WITH ('@' + {name}) RETURN COUNT(a)", {name:payload})
.then(function(result){
this.setState({'users':result.records[0]._fields[0].low})
s1.close()
}.bind(this))
this.setState({'users':result.records[0]._fields[0].low});
s1.close();
}.bind(this));
s2.run("MATCH (a:Group) WHERE a.name ENDS WITH ('@' + {name}) RETURN COUNT(a)", {name:payload})
.then(function(result){
this.setState({'groups':result.records[0]._fields[0].low})
s2.close()
}.bind(this))
this.setState({'groups':result.records[0]._fields[0].low});
s2.close();
}.bind(this));
s3.run("MATCH (n:Computer) WHERE n.name ENDS WITH {name} WITH n WHERE size(split(n.name,'.')) - size(split({name},'.')) = 1 RETURN count(n)", {name:payload})
.then(function(result){
this.setState({'computers':result.records[0]._fields[0].low})
s3.close()
}.bind(this))
this.setState({'computers':result.records[0]._fields[0].low});
s3.close();
}.bind(this));
s4.run("MATCH (a:Group) WHERE NOT a.name ENDS WITH ('@' + {name}) WITH a MATCH (b:Group) WHERE b.name ENDS WITH ('@' + {name}) WITH a,b MATCH (a)-[r:MemberOf]->(b) RETURN count(a)", {name:payload})
.then(function(result){
this.setState({'foreignGroups':result.records[0]._fields[0].low})
s4.close()
}.bind(this))
this.setState({'foreignGroups':result.records[0]._fields[0].low});
s4.close();
}.bind(this));
s5.run("MATCH (a:User) WHERE NOT a.name ENDS WITH ('@' + {name}) WITH a MATCH (b:Group) WHERE b.name ENDS WITH ('@' + {name}) WITH a,b MATCH (a)-[r:MemberOf]->(b) RETURN count(a)", {name:payload})
.then(function(result){
this.setState({'foreignUsers':result.records[0]._fields[0].low})
s5.close()
}.bind(this))
this.setState({'foreignUsers':result.records[0]._fields[0].low});
s5.close();
}.bind(this));
s6.run("MATCH (a:Domain {name:{name}})<-[r:TrustedBy]-(b:Domain) RETURN count(b)", {name:payload})
.then(function(result){
this.setState({'firstDegreeInboundTrusts':result.records[0]._fields[0].low})
s6.close()
}.bind(this))
this.setState({'firstDegreeInboundTrusts':result.records[0]._fields[0].low});
s6.close();
}.bind(this));
s7.run("MATCH (a:Domain {name:{name}})-[r:TrustedBy]->(b:Domain) RETURN count(b)", {name:payload})
.then(function(result){
this.setState({'firstDegreeOutboundTrusts':result.records[0]._fields[0].low})
s7.close()
}.bind(this))
this.setState({'firstDegreeOutboundTrusts':result.records[0]._fields[0].low});
s7.close();
}.bind(this));
s8.run("MATCH p=shortestPath((a:Domain {name:{name}})<-[r:TrustedBy*1..]-(b:Domain)) RETURN count(b)", {name:payload})
s8.run("MATCH (b:Domain) WHERE NOT b.name={name} WITH b MATCH p=shortestPath((a:Domain {name:{name}})<-[r:TrustedBy*1..]-(b)) RETURN count(b)", {name:payload})
.then(function(result){
this.setState({'effectiveInboundTrusts':result.records[0]._fields[0].low})
s8.close()
}.bind(this))
this.setState({'effectiveInboundTrusts':result.records[0]._fields[0].low});
s8.close();
}.bind(this));
s9.run("MATCH p=shortestPath((a:Domain {name:{name}})-[r:TrustedBy*1..]->(b:Domain)) RETURN count(b)", {name:payload})
s9.run("MATCH (b:Domain) WHERE NOT b.name={name} MATCH p=shortestPath((a:Domain {name:{name}})-[r:TrustedBy*1..]->(b)) RETURN count(b)", {name:payload})
.then(function(result){
this.setState({'effectiveOutboundTrusts':result.records[0]._fields[0].low})
s9.close()
}.bind(this))
this.setState({'effectiveOutboundTrusts':result.records[0]._fields[0].low});
s9.close();
}.bind(this));
this.setState({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9]})
this.setState({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9]});
}
render() {
@ -125,7 +125,8 @@ export default class DomainNodeData extends Component {
<dd>
<LoadLabel
ready={this.state.users !== -1}
value={this.state.users} />
value={this.state.users}
/>
</dd>
<dt>
Groups
@ -133,7 +134,8 @@ export default class DomainNodeData extends Component {
<dd>
<LoadLabel
ready={this.state.groups !== -1}
value={this.state.groups} />
value={this.state.groups}
/>
</dd>
<dt>
Computers
@ -141,7 +143,8 @@ export default class DomainNodeData extends Component {
<dd>
<LoadLabel
ready={this.state.computers !== -1}
value={this.state.computers} />
value={this.state.computers}
/>
</dd>
<br />
<dt>
@ -152,8 +155,9 @@ export default class DomainNodeData extends Component {
ready={this.state.foreignUsers !== -1}
value={this.state.foreignUsers}
click={function(){
emitter.emit('query', "MATCH (a:User) WHERE NOT a.name ENDS WITH ('@' + {domain}) WITH a MATCH (b:Group) WHERE b.name ENDS WITH ('@' + {domain}) WITH a,b MATCH (a)-[r:MemberOf]-(b) RETURN a,r,b", {domain: this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH (a:User) WHERE NOT a.name ENDS WITH ('@' + {domain}) WITH a MATCH (b:Group) WHERE b.name ENDS WITH ('@' + {domain}) WITH a,b MATCH (a)-[r:MemberOf]-(b) RETURN a,r,b", {domain: this.state.label});
}.bind(this)}
/>
</dd>
<dt>
Foreign Groups
@ -163,8 +167,9 @@ export default class DomainNodeData extends Component {
ready={this.state.foreignGroups !== -1}
value={this.state.foreignGroups}
click={function(){
emitter.emit('query', "MATCH (a:Group) WHERE NOT a.name ENDS WITH ('@' + {domain}) WITH a MATCH (b:Group) WHERE b.name ENDS WITH ('@' + {domain}) WITH a,b MATCH (a)-[r:MemberOf]-(b) RETURN a,r,b", {domain: this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH (a:Group) WHERE NOT a.name ENDS WITH ('@' + {domain}) WITH a MATCH (b:Group) WHERE b.name ENDS WITH ('@' + {domain}) WITH a,b MATCH (a)-[r:MemberOf]-(b) RETURN a,r,b", {domain: this.state.label});
}.bind(this)}
/>
</dd>
<dt>
Foreign Admins
@ -174,8 +179,9 @@ export default class DomainNodeData extends Component {
ready={this.state.foreignAdmins !== -1}
value={this.state.foreignAdmins}
click={function(){
emitter.emit('query', "MATCH (a:Group) WHERE NOT a.name ENDS WITH ('@' + {domain}) WITH a MATCH (b:Group) WHERE b.name ENDS WITH ('@' + {domain}) WITH a,b MATCH (a)-[r:MemberOf]-(b) RETURN a,r,b", {domain: this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH (a:Group) WHERE NOT a.name ENDS WITH ('@' + {domain}) WITH a MATCH (b:Group) WHERE b.name ENDS WITH ('@' + {domain}) WITH a,b MATCH (a)-[r:MemberOf]-(b) RETURN a,r,b", {domain: this.state.label});
}.bind(this)}
/>
</dd>
<br />
<dt>
@ -186,8 +192,9 @@ export default class DomainNodeData extends Component {
ready={this.state.firstDegreeInboundTrusts !== -1}
value={this.state.firstDegreeInboundTrusts}
click={function(){
emitter.emit('query', "MATCH (a:Domain {name:{domain}})<-[r:TrustedBy]-(b:Domain) RETURN a,r,b", {domain: this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH (a:Domain {name:{domain}})<-[r:TrustedBy]-(b:Domain) RETURN a,r,b", {domain: this.state.label});
}.bind(this)}
/>
</dd>
<dt>
Effective Inbound Trusts
@ -197,8 +204,9 @@ export default class DomainNodeData extends Component {
ready={this.state.effectiveInboundTrusts !== -1}
value={this.state.effectiveInboundTrusts}
click={function(){
emitter.emit('query', "MATCH p=shortestPath((a:Domain {name:{domain}})<-[r:TrustedBy*1..]-(b:Domain)) RETURN p", {domain: this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH (b:Domain) WHERE NOT b.name={domain} WITH b MATCH p=shortestPath((a:Domain {name:{domain}})<-[r:TrustedBy*1..]-(b)) RETURN p", {domain: this.state.label});
}.bind(this)}
/>
</dd>
<dt>
Outbound Trusts
@ -208,8 +216,9 @@ export default class DomainNodeData extends Component {
ready={this.state.firstDegreeOutboundTrusts !== -1}
value={this.state.firstDegreeOutboundTrusts}
click={function(){
emitter.emit('query', "MATCH (a:Domain {name:{domain}})-[r:TrustedBy]->(b:Domain) RETURN a,r,b", {domain: this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH (a:Domain {name:{domain}})-[r:TrustedBy]->(b:Domain) RETURN a,r,b", {domain: this.state.label});
}.bind(this)}
/>
</dd>
<dt>
Effective Outbound Trusts
@ -219,8 +228,9 @@ export default class DomainNodeData extends Component {
ready={this.state.effectiveOutboundTrusts !== -1}
value={this.state.effectiveOutboundTrusts}
click={function(){
emitter.emit('query', "MATCH p=shortestPath((a:Domain {name:{domain}})-[r:TrustedBy*1..]->(b:Domain)) RETURN p", {domain: this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH (b:Domain) WHERE NOT b.name={domain} WITH b MATCH p=shortestPath((a:Domain {name:{domain}})-[r:TrustedBy*1..]->(b:Domain)) RETURN p", {domain: this.state.label});
}.bind(this)}
/>
</dd>
</dl>
</div>
@ -230,4 +240,4 @@ export default class DomainNodeData extends Component {
DomainNodeData.propTypes = {
visible : React.PropTypes.bool.isRequired
}
};

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import NodeALink from './NodeALink'
import PropTypes from 'prop-types'
import NodeALink from './NodeALink';
import PropTypes from 'prop-types';
export default class GroupNodeData extends Component {
constructor(){
@ -25,7 +25,7 @@ export default class GroupNodeData extends Component {
unrolledControllers: -1,
transitiveControllers: -1,
driversessions: []
}
};
emitter.on('groupNodeClicked', this.getNodeData.bind(this));
}
@ -33,7 +33,7 @@ export default class GroupNodeData extends Component {
getNodeData(payload){
$.each(this.state.driversessions, function(index, record){
record.close();
})
});
this.setState({
label: payload,
@ -53,127 +53,127 @@ export default class GroupNodeData extends Component {
firstDegreeControllers: -1,
unrolledControllers: -1,
transitiveControllers: -1
})
});
var domain = '@' + payload.split('@').last()
var s1 = driver.session()
var s2 = driver.session()
var s3 = driver.session()
var s4 = driver.session()
var s5 = driver.session()
var s6 = driver.session()
var s7 = driver.session()
var s8 = driver.session()
var s9 = driver.session()
var s10 = driver.session()
var s11 = driver.session()
var s12 = driver.session()
var s13 = driver.session()
var s14 = driver.session()
var s15 = driver.session()
var s16 = driver.session()
var domain = '@' + payload.split('@').last();
var s1 = driver.session();
var s2 = driver.session();
var s3 = driver.session();
var s4 = driver.session();
var s5 = driver.session();
var s6 = driver.session();
var s7 = driver.session();
var s8 = driver.session();
var s9 = driver.session();
var s10 = driver.session();
var s11 = driver.session();
var s12 = driver.session();
var s13 = driver.session();
var s14 = driver.session();
var s15 = driver.session();
var s16 = driver.session();
s1.run("MATCH (a)-[b:MemberOf]->(c:Group {name:{name}}) RETURN count(a)", {name:payload})
.then(function(result){
this.setState({'directMembers':result.records[0]._fields[0].low})
s1.close()
}.bind(this))
this.setState({'directMembers':result.records[0]._fields[0].low});
s1.close();
}.bind(this));
s2.run("MATCH p = (n)-[r:MemberOf*1..]->(g:Group {name:{name}}) RETURN COUNT(n)", {name:payload})
.then(function(result){
this.setState({'unrolledMembers':result.records[0]._fields[0].low})
s2.close()
}.bind(this))
this.setState({'unrolledMembers':result.records[0]._fields[0].low});
s2.close();
}.bind(this));
s3.run("MATCH (n:Group {name:{name}})-[r:AdminTo]->(m:Computer) RETURN count(distinct(m))", {name:payload})
.then(function(result){
this.setState({'directAdminTo':result.records[0]._fields[0].low})
s3.close()
}.bind(this))
this.setState({'directAdminTo':result.records[0]._fields[0].low});
s3.close();
}.bind(this));
s4.run("MATCH p = shortestPath((g:Group {name:{name}})-[r:MemberOf|AdminTo|HasSession*1..]->(c:Computer)) RETURN COUNT(DISTINCT(c))", {name:payload})
s4.run("MATCH (c:Computer) WHERE NOT c.name={name} WITH c MATCH p = shortestPath((g:Group {name:{name}})-[r:MemberOf|AdminTo|HasSession*1..]->(c)) RETURN COUNT(DISTINCT(c))", {name:payload})
.then(function(result){
this.setState({'derivativeAdminTo':result.records[0]._fields[0].low})
s4.close()
}.bind(this))
this.setState({'derivativeAdminTo':result.records[0]._fields[0].low});
s4.close();
}.bind(this));
s5.run("MATCH p = (g1:Group {name:{name}})-[r:MemberOf*1..]->(g2:Group) RETURN COUNT(DISTINCT(g2))", {name:payload})
.then(function(result){
this.setState({'unrolledMemberOf':result.records[0]._fields[0].low})
s5.close()
}.bind(this))
this.setState({'unrolledMemberOf':result.records[0]._fields[0].low});
s5.close();
}.bind(this));
s6.run("MATCH p = (c:Computer)-[r1:HasSession]->(u:User)-[r2:MemberOf*1..]->(g:Group {name: {name}}) RETURN COUNT(r1)", {name:payload})
.then(function(result){
this.setState({'sessions':result.records[0]._fields[0].low})
s6.close()
}.bind(this))
this.setState({'sessions':result.records[0]._fields[0].low});
s6.close();
}.bind(this));
s7.run("MATCH (n:Group) WHERE NOT n.name ENDS WITH {domain} WITH n MATCH (m:Group {name:{name}}) MATCH (m)-[r:MemberOf]->(n) RETURN count(n)", {name:payload, domain:domain})
.then(function(result){
this.setState({'foreignGroupMembership':result.records[0]._fields[0].low})
s7.close()
}.bind(this))
this.setState({'foreignGroupMembership':result.records[0]._fields[0].low});
s7.close();
}.bind(this));
s8.run("MATCH p = (n)-[r:MemberOf*1..]->(g:Group {name:{name}}) WHERE NOT g.domain = n.domain RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'foreignGroupMembers':result.records[0]._fields[0].low})
s8.close()
}.bind(this))
this.setState({'foreignGroupMembers':result.records[0]._fields[0].low});
s8.close();
}.bind(this));
s9.run("MATCH p = (g1:Group {name:{name}})-[r:MemberOf]->(g2:Group) RETURN COUNT(DISTINCT(g2))", {name:payload})
.then(function(result){
this.setState({'firstDegreeGroupMembership':result.records[0]._fields[0].low})
s9.close()
}.bind(this))
this.setState({'firstDegreeGroupMembership':result.records[0]._fields[0].low});
s9.close();
}.bind(this));
s10.run("MATCH p = (g1:Group {name:{name}})-[r1:MemberOf*1..]->(g2:Group)-[r2:AdminTo]->(c:Computer) RETURN COUNT(DISTINCT(c))", {name:payload})
.then(function(result){
this.setState({'groupDelegatedAdmin':result.records[0]._fields[0].low})
s10.close()
}.bind(this))
this.setState({'groupDelegatedAdmin':result.records[0]._fields[0].low});
s10.close();
}.bind(this));
s11.run("MATCH p = (g:Group {name:{name}})-[r:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'firstdegreeControl':result.records[0]._fields[0].low})
s11.close()
}.bind(this))
this.setState({'firstdegreeControl':result.records[0]._fields[0].low});
s11.close();
}.bind(this));
s12.run("MATCH p = (g1:Group {name:{name}})-[r1:MemberOf*1..]->(g2:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'groupDelegatedControl':result.records[0]._fields[0].low})
s12.close()
}.bind(this))
this.setState({'groupDelegatedControl':result.records[0]._fields[0].low});
s12.close();
}.bind(this));
s13.run("MATCH p = shortestPath((g:Group {name:{name}})-[r:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN COUNT(DISTINCT(n))", {name:payload})
s13.run("MATCH (n) WHERE NOT n.name={name} WITH n MATCH p = shortestPath((g:Group {name:{name}})-[r:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'transitiveControl':result.records[0]._fields[0].low})
s13.close()
}.bind(this))
this.setState({'transitiveControl':result.records[0]._fields[0].low});
s13.close();
}.bind(this));
s14.run("MATCH p = (n)-[r:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(g:Group {name:{name}}) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'firstDegreeControllers':result.records[0]._fields[0].low})
s14.close()
}.bind(this))
this.setState({'firstDegreeControllers':result.records[0]._fields[0].low});
s14.close();
}.bind(this));
s15.run("MATCH p = (n1)-[r:MemberOf*1..]->(g1:Group)-[r1:AddMembers|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(g2:Group {name: {name}}) WITH LENGTH(p) as pathLength, p, n1 WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = g2.name) AND NOT n1.name = g2.name RETURN COUNT(DISTINCT(n1))", {name:payload})
.then(function(result){
this.setState({'unrolledControllers':result.records[0]._fields[0].low})
s15.close()
}.bind(this))
this.setState({'unrolledControllers':result.records[0]._fields[0].low});
s15.close();
}.bind(this));
s16.run("MATCH p = shortestPath((n)-[r:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(g:Group {name:{name}})) RETURN COUNT(DISTINCT(n))", {name:payload})
s16.run("MATCH (n) WHERE NOT n.name={name} WITH n MATCH p = shortestPath((n)-[r:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(g:Group {name:{name}})) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'transitiveControllers':result.records[0]._fields[0].low})
s16.close()
}.bind(this))
this.setState({'transitiveControllers':result.records[0]._fields[0].low});
s16.close();
}.bind(this));
this.setState({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15,s16]})
this.setState({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15,s16]});
}
render() {
var domain = '@' + this.state.label.split('@')
var domain = '@' + this.state.label.split('@');
return (
<div className={this.props.visible ? "" : "displaynone"}>
<dl className='dl-horizontal'>
@ -193,10 +193,10 @@ export default class GroupNodeData extends Component {
value={this.state.sessions}
click={function(){
emitter.emit('query', "MATCH p = (c:Computer)-[r1:HasSession]->(u:User)-[r2:MemberOf*1..]->(g:Group {name: {name}}) RETURN p", {name: this.state.label},
"",this.state.label)
}.bind(this)} />
"",this.state.label);
}.bind(this)}
/>
</dd>
<br />
<h4>Group Members</h4>
<dt>
Direct Members
@ -206,8 +206,9 @@ export default class GroupNodeData extends Component {
ready={this.state.directMembers !== -1}
value={this.state.directMembers}
click={function(){
emitter.emit('query', "MATCH (n)-[r:MemberOf]->(m:Group {name:{name}}) RETURN n,r,m", {name: this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH (n)-[r:MemberOf]->(m:Group {name:{name}}) RETURN n,r,m", {name: this.state.label});
}.bind(this)}
/>
</dd>
<dt>
Unrolled Members
@ -218,8 +219,9 @@ export default class GroupNodeData extends Component {
value={this.state.unrolledMembers}
click={function(){
emitter.emit('query', "MATCH p = (n)-[r:MemberOf*1..]->(g:Group {name:{name}}) RETURN p", {name: this.state.label},
this.state.label)
}.bind(this)} />
this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Foreign Members
@ -230,10 +232,10 @@ export default class GroupNodeData extends Component {
value={this.state.foreignGroupMembers}
click={function(){
emitter.emit('query', "MATCH p = (n)-[r:MemberOf*1..]->(g:Group {name:{name}}) WHERE NOT g.domain = n.domain RETURN p", {name: this.state.label},
this.state.label)
}.bind(this)} />
this.state.label);
}.bind(this)}
/>
</dd>
<br />
<h4>Group Membership</h4>
<dt>
First Degree Group Membership
@ -244,8 +246,9 @@ export default class GroupNodeData extends Component {
value={this.state.firstDegreeGroupMembership}
click={function(){
emitter.emit('query', "MATCH p = (g1:Group {name:{name}})-[r:MemberOf]->(g2:Group) RETURN p", {name: this.state.label},
this.state.label)
}.bind(this)} />
this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Unrolled Member Of
@ -256,8 +259,9 @@ export default class GroupNodeData extends Component {
value={this.state.unrolledMemberOf}
click={function(){
emitter.emit('query', "MATCH p = (g1:Group {name:{name}})-[r:MemberOf*1..]->(g2:Group) RETURN p", {name: this.state.label},
this.state.label)
}.bind(this)} />
this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Foreign Group Membership
@ -267,10 +271,10 @@ export default class GroupNodeData extends Component {
ready={this.state.foreignGroupMembership !== -1}
value={this.state.foreignGroupMembership}
click={function(){
emitter.emit('query', "MATCH (n:Group) WHERE NOT n.name ENDS WITH {domain} WITH n MATCH (m:Group {name:{name}}) MATCH (m)-[r:MemberOf]->(n) RETURN m,r,n", {name: this.state.label, domain: domain})
}.bind(this)} />
emitter.emit('query', "MATCH (n:Group) WHERE NOT n.name ENDS WITH {domain} WITH n MATCH (m:Group {name:{name}}) MATCH (m)-[r:MemberOf]->(n) RETURN m,r,n", {name: this.state.label, domain: domain});
}.bind(this)}
/>
</dd>
<br />
<h4>Local Admin Rights</h4>
<dt>
First Degree Local Admin
@ -281,8 +285,9 @@ export default class GroupNodeData extends Component {
value={this.state.directAdminTo}
click={function(){
emitter.emit('query', "MATCH p=(g:Group {name:{name}})-[r:AdminTo]->(c:Computer) RETURN p", {name: this.state.label},
this.state.label)
}.bind(this)} />
this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Group Delegated Local Admin Rights
@ -293,8 +298,9 @@ export default class GroupNodeData extends Component {
value={this.state.groupDelegatedAdmin}
click={function(){
emitter.emit('query', "MATCH p = (g1:Group {name:{name}})-[r1:MemberOf*1..]->(g2:Group)-[r2:AdminTo]->(c:Computer) RETURN p", {name: this.state.label},
this.state.label)
}.bind(this)} />
this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Derivative Local Admin Rights
@ -304,11 +310,11 @@ export default class GroupNodeData extends Component {
ready={this.state.derivativeAdminTo !== -1}
value={this.state.derivativeAdminTo}
click={function(){
emitter.emit('query', "MATCH p = shortestPath((g:Group {name:{name}})-[r:MemberOf|AdminTo|HasSession*1..]->(c:Computer)) RETURN p", {name: this.state.label},
this.state.label)
}.bind(this)} />
emitter.emit('query', "MATCH (c:Computer) WHERE NOT c.name={name} WITH c MATCH p = shortestPath((g:Group {name:{name}})-[r:MemberOf|AdminTo|HasSession*1..]->(c)) RETURN p", {name: this.state.label},
this.state.label);
}.bind(this)}
/>
</dd>
<br />
<h4>Outbound Object Control</h4>
<dt>
First Degree Object Control
@ -318,8 +324,9 @@ export default class GroupNodeData extends Component {
ready={this.state.firstdegreeControl !== -1}
value={this.state.firstdegreeControl}
click={function(){
emitter.emit('query', "MATCH p = (g:Group {name:{name}})-[r1:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH p = (g:Group {name:{name}})-[r1:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label});
}.bind(this)}
/>
</dd>
<dt>
Group Delegated Object Control
@ -330,8 +337,9 @@ export default class GroupNodeData extends Component {
value={this.state.groupDelegatedControl}
click={function(){
emitter.emit('query', "MATCH p = (g1:Group {name:{name}})-[r1:MemberOf*1..]->(g2:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
,this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Transitive Object Control
@ -341,11 +349,11 @@ export default class GroupNodeData extends Component {
ready={this.state.transitiveControl !== -1}
value={this.state.transitiveControl}
click={function(){
emitter.emit('query', "MATCH p = shortestPath((g:Group {name:{name}})-[r1:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
emitter.emit('query', "MATCH (n) WHERE NOT n.name={name} WITH n MATCH p = shortestPath((g:Group {name:{name}})-[r1:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN p", {name:this.state.label}
,this.state.label);
}.bind(this)}
/>
</dd>
<br />
<h4>Inbound Object Control</h4>
<dt>
Explicit Object Controllers
@ -356,8 +364,9 @@ export default class GroupNodeData extends Component {
value={this.state.firstDegreeControllers}
click={function(){
emitter.emit('query', "MATCH p = (n)-[r:AddMembers|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(g:Group {name: {name}}) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
,this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Unrolled Object Controllers
@ -368,8 +377,9 @@ export default class GroupNodeData extends Component {
value={this.state.unrolledControllers}
click={function(){
emitter.emit('query', "MATCH p = (n1)-[r:MemberOf*1..]->(g1:Group)-[r1:AddMembers|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(g2:Group {name: {name}}) WITH LENGTH(p) as pathLength, p, n1 WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = g2.name) AND NOT n1.name = g2.name RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
,this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Transitive Object Controllers
@ -379,9 +389,10 @@ export default class GroupNodeData extends Component {
ready={this.state.transitiveControllers !== -1}
value={this.state.transitiveControllers}
click={function(){
emitter.emit('query', "MATCH p = shortestPath((n)-[r:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(g:Group {name: {name}})) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
emitter.emit('query', "MATCH (n) WHERE NOT n.name={name} WITH n MATCH p = shortestPath((n)-[r:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(g:Group {name: {name}})) RETURN p", {name:this.state.label}
,this.state.label);
}.bind(this)}
/>
</dd>
</dl>
</div>
@ -391,4 +402,4 @@ export default class GroupNodeData extends Component {
GroupNodeData.propTypes = {
visible : React.PropTypes.bool.isRequired
}
};

View File

@ -0,0 +1,51 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class NodePropItem extends Component{
constructor(props){
super(props);
}
isArray(object){
return object && typeof object === 'object' && object.constructor === Array;
}
render() {
var val;
var obj = this.props.keyValue;
if (obj.hasOwnProperty('low')){
return [
<dt>{this.props.keyName}</dt>,
<dd>{obj.low}</dd>
];
}else if (this.isArray(obj)){
console.log(obj);
if (obj.length === 0){
return [
<dt>{this.props.keyName}</dt>,
<dd>None</dd>
];
}else{
var elements = [];
$.each(obj, function(index, prop){
elements.push(<dt></dt>);
elements.push(<dd>{prop}</dd>);
});
elements[0] = <dt>Service Principal Names</dt>;
}
return elements;
}else if (typeof obj === 'boolean'){
return [
<dt>{this.props.keyName}</dt>,
<dd>{this.props.keyValue.toString().toTitleCase()}</dd>
];
}else{
return [
<dt>{this.props.keyName}</dt>,
<dd>{this.props.keyValue.toString()}</dd>
];
}
}
}

View File

@ -1,6 +1,9 @@
import React, { Component } from 'react';
import NodeALink from './NodeALink'
import PropTypes from 'prop-types'
import NodeALink from './NodeALink';
import NodePropItem from './NodePropItem';
import PropTypes from 'prop-types';
import { If, Then, Else } from 'react-if';
export default class UserNodeData extends Component {
constructor(){
@ -8,9 +11,6 @@ export default class UserNodeData extends Component {
this.state = {
label: "",
samAccountName: "None",
displayName: "None",
pwdLastChanged: "None",
firstDegreeGroupMembership: -1,
unrolledGroupMembership: -1,
foreignGroupMembership: -1,
@ -24,8 +24,9 @@ export default class UserNodeData extends Component {
firstdegreeControl: -1,
unrolledControl: -1,
transitiveControl: -1,
driversessions : []
}
driversessions : [],
propertyMap: {ServicePrincipalNames: []}
};
emitter.on('userNodeClicked', this.getNodeData.bind(this));
}
@ -33,13 +34,10 @@ export default class UserNodeData extends Component {
getNodeData(payload){
$.each(this.state.driversessions,function(index, record){
record.close();
})
});
this.setState({
label: payload,
samAccountName: "None",
displayName: "None",
pwdLastChanged: "None",
firstDegreeGroupMembership: -1,
unrolledGroupMembership: -1,
foreignGroupMembership: -1,
@ -52,113 +50,147 @@ export default class UserNodeData extends Component {
transitiveControllers: -1,
firstdegreeControl: -1,
unrolledControl: -1,
transitiveControl: -1
})
transitiveControl: -1,
propertyMap: {ServicePrincipalNames: []}
});
var domain = '@' + payload.split('@').last()
var domain = '@' + payload.split('@').last();
var s1 = driver.session()
var s2 = driver.session()
var s3 = driver.session()
var s4 = driver.session()
var s5 = driver.session()
var s6 = driver.session()
var s7 = driver.session()
var s8 = driver.session()
var s9 = driver.session()
var s10 = driver.session()
var s11 = driver.session()
var s12 = driver.session()
var s13 = driver.session()
var s1 = driver.session();
var s2 = driver.session();
var s3 = driver.session();
var s4 = driver.session();
var s5 = driver.session();
var s6 = driver.session();
var s7 = driver.session();
var s8 = driver.session();
var s9 = driver.session();
var s10 = driver.session();
var s11 = driver.session();
var s12 = driver.session();
var s13 = driver.session();
var props = driver.session();
props.run("MATCH (n:User {name:{name}}) RETURN n", {name: payload})
.then(function(result){
var properties = result.records[0]._fields[0].properties;
this.setState({propertyMap: properties});
props.close();
}.bind(this));
s1.run("MATCH (n:Group) WHERE NOT n.name ENDS WITH {domain} WITH n MATCH (m:User {name:{name}}) MATCH (m)-[r:MemberOf*1..]->(n) RETURN count(n)", {name:payload, domain: domain})
.then(function(result){
this.setState({'foreignGroupMembership':result.records[0]._fields[0].low})
s1.close()
}.bind(this))
this.setState({'foreignGroupMembership':result.records[0]._fields[0].low});
s1.close();
}.bind(this));
s2.run("MATCH (n:User {name:{name}}), (m:Group), p=(n)-[:MemberOf]->(m) RETURN count(m)", {name:payload})
.then(function(result){
this.setState({'firstDegreeGroupMembership':result.records[0]._fields[0].low})
s2.close()
}.bind(this))
this.setState({'firstDegreeGroupMembership':result.records[0]._fields[0].low});
s2.close();
}.bind(this));
s3.run("MATCH p = (n:User {name:{name}})-[r:MemberOf*1..]->(g:Group) RETURN COUNT(DISTINCT(g))", {name:payload})
.then(function(result){
this.setState({'unrolledGroupMembership':result.records[0]._fields[0].low})
s3.close()
}.bind(this))
this.setState({'unrolledGroupMembership':result.records[0]._fields[0].low});
s3.close();
}.bind(this));
s4.run("MATCH p = (n:User {name:{name}})-[r:AdminTo]->(c:Computer) RETURN COUNT(DISTINCT(c))", {name:payload})
.then(function(result){
this.setState({'firstDegreeLocalAdmin':result.records[0]._fields[0].low})
s4.close()
}.bind(this))
this.setState({'firstDegreeLocalAdmin':result.records[0]._fields[0].low});
s4.close();
}.bind(this));
s5.run("MATCH p=(n:User {name:{name}})-[r1:MemberOf*1..]->(g:Group)-[r2:AdminTo]->(c:Computer) RETURN count(distinct(c))", {name:payload})
.then(function(result){
this.setState({'groupDelegatedLocalAdmin':result.records[0]._fields[0].low})
s5.close()
}.bind(this))
this.setState({'groupDelegatedLocalAdmin':result.records[0]._fields[0].low});
s5.close();
}.bind(this));
s6.run("MATCH p = shortestPath((n:User {name:{name}})-[r:HasSession|AdminTo|MemberOf*1..]->(c:Computer)) RETURN COUNT(c)", {name:payload})
s6.run("MATCH (c:Computer) WHERE NOT c.name={name} WITH c MATCH p = shortestPath((n:User {name:{name}})-[r:HasSession|AdminTo|MemberOf*1..]->(c)) RETURN COUNT(c)", {name:payload})
.then(function(result){
this.setState({'derivativeLocalAdmin':result.records[0]._fields[0].low})
s6.close()
}.bind(this))
this.setState({'derivativeLocalAdmin':result.records[0]._fields[0].low});
s6.close();
}.bind(this));
s7.run("MATCH p = (n:Computer)-[r:HasSession]->(m:User {name:{name}}) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'sessions':result.records[0]._fields[0].low})
s7.close()
}.bind(this))
this.setState({'sessions':result.records[0]._fields[0].low});
s7.close();
}.bind(this));
s8.run("MATCH p = (n)-[r:AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(u1:User {name: {name}}) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'firstdegreeControllers':result.records[0]._fields[0].low})
s8.close()
}.bind(this))
this.setState({'firstdegreeControllers':result.records[0]._fields[0].low});
s8.close();
}.bind(this));
s9.run("MATCH p = (n1)-[r:MemberOf*1..]->(g:Group)-[r1:AddMembers|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(u:User {name: {name}}) WITH LENGTH(p) as pathLength, p, n1 WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = u.name) AND NOT n1.name = u.name RETURN COUNT(DISTINCT(n1))", {name:payload})
.then(function(result){
this.setState({'unrolledControllers':result.records[0]._fields[0].low})
s9.close()
}.bind(this))
this.setState({'unrolledControllers':result.records[0]._fields[0].low});
s9.close();
}.bind(this));
s10.run("MATCH p = shortestPath((n1)-[r1:MemberOf|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(u1:User {name: {name}})) RETURN COUNT(DISTINCT(n1))", {name:payload})
s10.run("MATCH (n1) WHERE NOT n1.name={name} WITH n1 MATCH p = shortestPath((n1)-[r1:MemberOf|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(u1:User {name: {name}})) RETURN COUNT(DISTINCT(n1))", {name:payload})
.then(function(result){
this.setState({'transitiveControllers':result.records[0]._fields[0].low})
s10.close()
}.bind(this))
this.setState({'transitiveControllers':result.records[0]._fields[0].low});
s10.close();
}.bind(this));
s11.run("MATCH p = (u:User {name:{name}})-[r1:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'firstdegreeControl':result.records[0]._fields[0].low})
s11.close()
}.bind(this))
this.setState({'firstdegreeControl':result.records[0]._fields[0].low});
s11.close();
}.bind(this));
s12.run("MATCH p = (u:User {name:{name}})-[r1:MemberOf*1..]->(g:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'unrolledControl':result.records[0]._fields[0].low})
s12.close()
}.bind(this))
this.setState({'unrolledControl':result.records[0]._fields[0].low});
s12.close();
}.bind(this));
s13.run("MATCH p = shortestPath((u:User {name:{name}})-[r1:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN COUNT(DISTINCT(n))", {name:payload})
s13.run("MATCH (n) WHERE NOT n.name={name} WITH n MATCH p = shortestPath((u:User {name:{name}})-[r1:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN COUNT(DISTINCT(n))", {name:payload})
.then(function(result){
this.setState({'transitiveControl':result.records[0]._fields[0].low})
s13.close()
}.bind(this))
this.setState({'transitiveControl':result.records[0]._fields[0].low});
s13.close();
}.bind(this));
this.setState({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13]})
this.setState({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,props]});
}
isArray(object){
return object && typeof object === 'object' && object.constructor === Array;
}
convertToDisplayProp(propName){
var obj = this.state.propertyMap[propName];
var type = typeof obj;
if (type === 'undefined'){
return "No Data";
}else if (obj.hasOwnProperty('low')){
var t = obj.low;
if (t === 0){
return "Never";
}else{
return new Date(obj.low * 1000).toUTCString();
}
}else if (type === 'boolean'){
return obj.toString().toTitleCase();
}else if (obj === ""){
return "None";
}else{
return obj;
}
}
render() {
var domain = '@' + this.state.label.split('@').last()
var domain = '@' + this.state.label.split('@').last();
return (
<div className={this.props.visible ? "" : "displaynone"}>
<dl className='dl-horizontal'>
<h4>
Node Info
User Info
</h4>
<dt>
Name
@ -166,24 +198,48 @@ export default class UserNodeData extends Component {
<dd>
{this.state.label}
</dd>
<dt>
SAMAccountName
</dt>
<dd>
{this.state.samAccountName}
</dd>
<dt>
Display Name
</dt>
<dd>
{this.state.displayName}
{this.convertToDisplayProp("DisplayName")}
</dd>
<dt>
Password Last Changed
</dt>
<dd>
{this.state.pwdLastChanged}
{this.convertToDisplayProp("PwdLastSet")}
</dd>
<dt>
Last Logon
</dt>
<dd>
{this.convertToDisplayProp("LastLogon")}
</dd>
<dt>
Enabled
</dt>
<dd>
{this.convertToDisplayProp("Enabled")}
</dd>
<dt>
Email
</dt>
<dd>
{this.convertToDisplayProp("Email")}
</dd>
<dt>
Service Principal Names
</dt>
{(() => {
if (this.state.propertyMap.ServicePrincipalNames.length === 0){
return <dd>None</dd>;
}
})()}
{Object.keys(this.state.propertyMap.ServicePrincipalNames).map(function(key){
var x = <dd key={key}>{this.state.propertyMap.ServicePrincipalNames[key]}</dd>;
return x;
}.bind(this))}
<dt>
Sessions
</dt>
@ -193,10 +249,11 @@ export default class UserNodeData extends Component {
value={this.state.sessions}
click={function(){
emitter.emit('query', "MATCH (n:Computer)-[r:HasSession]->(m:User {name:{name}}) RETURN n,r,m", {name:this.state.label}
,this.state.label)
}.bind(this)} />
,this.state.label);
}.bind(this)}
/>
</dd>
<br />
<h4>Group Membership</h4>
<dt>
First Degree Group Memberships
@ -209,8 +266,9 @@ export default class UserNodeData extends Component {
emitter.emit(
'query',
"MATCH p = (n:User {name:{name}})-[r:MemberOf]->(g:Group) RETURN p", {name:this.state.label}
)
}.bind(this)} />
);
}.bind(this)}
/>
</dd>
<dt>
Unrolled Group Memberships
@ -221,8 +279,9 @@ export default class UserNodeData extends Component {
value={this.state.unrolledGroupMembership}
click={function(){
emitter.emit('query', "MATCH p = (n:User {name:{name}})-[r:MemberOf*1..]->(g:Group) RETURN p", {name:this.state.label},
this.state.label)
}.bind(this)} />
this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Foreign Group Membership
@ -233,10 +292,11 @@ export default class UserNodeData extends Component {
value={this.state.foreignGroupMembership}
click={function(){
emitter.emit('query',
"MATCH (n:Group) WHERE NOT n.name ENDS WITH {domain} WITH n MATCH (m:User {name:{name}}) WITH n,m MATCH p = (m)-[r:MemberOf*1..]->(n) RETURN p", {name: this.state.label, domain: domain})
}.bind(this)} />
"MATCH (n:Group) WHERE NOT n.name ENDS WITH {domain} WITH n MATCH (m:User {name:{name}}) WITH n,m MATCH p = (m)-[r:MemberOf*1..]->(n) RETURN p", {name: this.state.label, domain: domain});
}.bind(this)}
/>
</dd>
<br />
<h4>
Local Admin Rights
</h4>
@ -248,8 +308,9 @@ export default class UserNodeData extends Component {
ready={this.state.firstDegreeLocalAdmin !== -1}
value={this.state.firstDegreeLocalAdmin}
click={function(){
emitter.emit('query', "MATCH p = (n:User {name:{name}})-[r:AdminTo]->(c:Computer) RETURN p", {name:this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH p = (n:User {name:{name}})-[r:AdminTo]->(c:Computer) RETURN p", {name:this.state.label});
}.bind(this)}
/>
</dd>
<dt>
Group Delegated Local Admin Rights
@ -260,8 +321,9 @@ export default class UserNodeData extends Component {
value={this.state.groupDelegatedLocalAdmin}
click={function(){
emitter.emit('query', "MATCH p=(n:User {name:{name}})-[r1:MemberOf*1..]->(g:Group)-[r2:AdminTo]->(c:Computer) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
,this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Derivative Local Admin Rights
@ -271,11 +333,12 @@ export default class UserNodeData extends Component {
ready={this.state.derivativeLocalAdmin !== -1}
value={this.state.derivativeLocalAdmin}
click={function(){
emitter.emit('query', "MATCH p = shortestPath((n:User {name:{name}})-[r:HasSession|AdminTo|MemberOf*1..]->(c:Computer)) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
emitter.emit('query', "MATCH (c:Computer) WHERE NOT c.name={name} WITH c MATCH p = shortestPath((n:User {name:{name}})-[r:HasSession|AdminTo|MemberOf*1..]->(c)) RETURN p", {name:this.state.label}
,this.state.label);
}.bind(this)}
/>
</dd>
<br />
<h4>
Outbound Object Control
</h4>
@ -287,8 +350,9 @@ export default class UserNodeData extends Component {
ready={this.state.firstdegreeControl !== -1}
value={this.state.firstdegreeControl}
click={function(){
emitter.emit('query', "MATCH p = (u:User {name:{name}})-[r1:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label})
}.bind(this)} />
emitter.emit('query', "MATCH p = (u:User {name:{name}})-[r1:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label});
}.bind(this)}
/>
</dd>
<dt>
Group Delegated Object Control
@ -299,8 +363,9 @@ export default class UserNodeData extends Component {
value={this.state.unrolledControl}
click={function(){
emitter.emit('query', "MATCH p = (u:User {name:{name}})-[r1:MemberOf*1..]->(g:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
,this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Transitive Object Control
@ -310,11 +375,12 @@ export default class UserNodeData extends Component {
ready={this.state.transitiveControl !== -1}
value={this.state.transitiveControl}
click={function(){
emitter.emit('query', "MATCH p = shortestPath((u:User {name:{name}})-[r1:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
emitter.emit('query', "MATCH (n) WHERE NOT n.name={name} WITH n MATCH p = shortestPath((u:User {name:{name}})-[r1:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) RETURN p", {name:this.state.label}
,this.state.label);
}.bind(this)}
/>
</dd>
<br />
<h4>Inbound Object Control</h4>
<dt>
Explicit Object Controllers
@ -325,8 +391,9 @@ export default class UserNodeData extends Component {
value={this.state.firstdegreeControllers}
click={function(){
emitter.emit('query', "MATCH p = (n)-[r:AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(u1:User {name: {name}}) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
,this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Unrolled Object Controllers
@ -337,8 +404,9 @@ export default class UserNodeData extends Component {
value={this.state.unrolledControllers}
click={function(){
emitter.emit('query', "MATCH p = (n1)-[r:MemberOf*1..]->(g:Group)-[r1:AddMembers|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(u:User {name: {name}}) WITH LENGTH(p) as pathLength, p, n1 WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = u.name) AND NOT n1.name = u.name RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
,this.state.label);
}.bind(this)}
/>
</dd>
<dt>
Transitive Object Controllers
@ -348,9 +416,10 @@ export default class UserNodeData extends Component {
ready={this.state.transitiveControllers !== -1}
value={this.state.transitiveControllers}
click={function(){
emitter.emit('query', "MATCH p = shortestPath((n1)-[r1:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(u1:User {name: {name}})) RETURN p", {name:this.state.label}
,this.state.label)
}.bind(this)} />
emitter.emit('query', "MATCH (n1) WHERE NOT n1.name={name} WITH n1 MATCH p = shortestPath((n1)-[r1:MemberOf|AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(u1:User {name: {name}})) RETURN p", {name:this.state.label}
,this.state.label);
}.bind(this)}
/>
</dd>
</dl>
</div>
@ -359,5 +428,5 @@ export default class UserNodeData extends Component {
}
UserNodeData.propTypes = {
visible : React.PropTypes.bool.isRequired
}
visible : PropTypes.bool.isRequired
};

View File

@ -4,6 +4,7 @@
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
.max{
@ -94,6 +95,13 @@ div.tooltip-inner-custom {
width: 100%;
height: 100%;
background-color: lightgray;
overflow: hidden;
}
.dropdown-item > i{
float:right;
margin-top:3px;
margin-left: 2px;
}
.graph:focus {
@ -187,6 +195,12 @@ div.tooltip-inner-custom {
margin-bottom: 1em;
}
.dl-horizontal > h4 {
margin-top: 10px;
margin-bottom: 5px;
color: blue;
}
.dl-horizontal{
margin-right: 10px;
}
@ -763,7 +777,6 @@ div.tooltip-inner-custom {
}
.tab-content > div:nth-last-child(2) > div{
height: 600px;
max-height: 600px;
overflow-y: auto;
resize: vertical;
}

View File

@ -106,10 +106,11 @@ global.appStore = {
'ForceChangePassword': 'tapered',
'GenericAll': 'tapered',
'GenericWrite': 'tapered',
'WriteDACL': 'tapered',
'WriteDacl': 'tapered',
'WriteOwner': 'tapered',
'AddMembers': 'tapered',
'TrustedBy': 'curvedArrow'
'TrustedBy': 'curvedArrow',
'DCSync' : 'tapered'
}
},
lowResPalette: {

File diff suppressed because one or more lines are too long

View File

@ -10,32 +10,32 @@ export function generateUniqueId(sigmaInstance, isNode) {
}
}
return i
return i;
}
//Recursive function to highlight paths to start/end nodes
export function findGraphPath(sigmaInstance, reverse, nodeid) {
var target = reverse ? appStore.startNode : appStore.endNode
var target = reverse ? appStore.startNode : appStore.endNode;
//This is our stop condition for recursing
if (nodeid !== target.id) {
var edges = sigmaInstance.graph.adjacentEdges(nodeid)
var nodes = reverse ? sigmaInstance.graph.inboundNodes(nodeid) : sigmaInstance.graph.outboundNodes(nodeid)
var edges = sigmaInstance.graph.adjacentEdges(nodeid);
var nodes = reverse ? sigmaInstance.graph.inboundNodes(nodeid) : sigmaInstance.graph.outboundNodes(nodeid);
//Loop over the nodes near us and the edges connecting to those nodes
$.each(nodes, function(index, node) {
$.each(edges, function(index, edge) {
var check = reverse ? edge.source : edge.target
var check = reverse ? edge.source : edge.target;
//If an edge is pointing in the right direction, set its color
//Push the edge into our store and then
node = parseInt(node)
node = parseInt(node);
if (check === node) {
edge.color = reverse ? 'blue' : 'red';
appStore.highlightedEdges.push(edge);
findGraphPath(sigmaInstance, reverse, node);
}
})
})
});
});
} else {
return
return;
}
}
@ -45,192 +45,211 @@ export function clearSessions(){
}
function deleteSessions(){
var session = driver.session()
var session = driver.session();
session.run("MATCH ()-[r:HasSession]-() WITH r LIMIT 100000 DELETE r RETURN count(r)")
.then(function(results) {
session.close()
emitter.emit("refreshDBData")
var count = results.records[0]._fields[0].low
session.close();
emitter.emit("refreshDBData");
var count = results.records[0]._fields[0].low;
if (count === 0) {
emitter.emit('hideDBClearModal')
emitter.emit('hideDBClearModal');
} else {
deleteSessions();
}
})
});
}
export function clearDatabase() {
emitter.emit('openClearingModal');
deleteEdges()
deleteEdges();
}
function deleteEdges() {
var session = driver.session()
var session = driver.session();
session.run("MATCH ()-[r]-() WITH r LIMIT 100000 DELETE r RETURN count(r)")
.then(function(results) {
emitter.emit("refreshDBData");
session.close()
var count = results.records[0]._fields[0].low
session.close();
var count = results.records[0]._fields[0].low;
if (count === 0) {
deleteNodes()
deleteNodes();
} else {
deleteEdges()
deleteEdges();
}
})
});
}
function deleteNodes() {
var session = driver.session()
var session = driver.session();
session.run("MATCH (n) WITH n LIMIT 100000 DELETE n RETURN count(n)")
.then(function(results) {
emitter.emit("refreshDBData")
session.close()
var count = results.records[0]._fields[0].low
emitter.emit("refreshDBData");
session.close();
var count = results.records[0]._fields[0].low;
if (count === 0) {
emitter.emit('hideDBClearModal')
emitter.emit('hideDBClearModal');
} else {
deleteNodes()
deleteNodes();
}
});
}
export function findObjectType(header){
if (header.includes('UserName') && header.includes('ComputerName') && header.includes('Weight')){
return 'sessions';
}else if (header.includes('AccountName') && header.includes('AccountType') && header.includes('GroupName')){
return 'groupmembership';
}else if (header.includes('AccountName') && header.includes('AccountType') && header.includes('ComputerName')){
return 'localadmin';
}else if (header.includes('SourceDomain') && header.includes('TargetDomain') && header.includes('TrustDirection') && header.includes('TrustType') && header.includes('Transitive')){
return 'domain';
}else if (header.includes('ActiveDirectoryRights') && header.includes('ObjectType') && header.includes('PrincipalType') && header.includes('PrincipalName') && header.includes('ObjectName') && header.includes('ACEType') && header.includes('AccessControlType') && header.includes('IsInherited')){
return 'acl';
}else if (header.includes('AccountName') && header.includes('Enabled') && header.includes('PwdLastSet') && header.includes('LastLogon') && header.includes('Sid') && header.includes('SidHistory') && header.includes('HasSPN') && header.includes('ServicePrincipalNames')){
return 'userprops';
}else if (header.includes('AccountName') && header.includes('Enabled') && header.includes('PwdLastSet') && header.includes('LastLogon') && header.includes('OperatingSystem') && header.includes('Sid')){
return 'compprops';
}else{
return 'unknown';
}
})
}
export function buildGroupMembershipProps(rows) {
var users = []
var groups = []
var computers = []
var users = [];
var groups = [];
var computers = [];
$.each(rows, function(index, row) {
switch (row.AccountType) {
case 'user':
users.push({ account: row.AccountName.toUpperCase(), group: row.GroupName.toUpperCase() })
break
users.push({ account: row.AccountName.toUpperCase(), group: row.GroupName.toUpperCase() });
break;
case 'computer':
computers.push({ account: row.AccountName.toUpperCase(), group: row.GroupName.toUpperCase() })
break
computers.push({ account: row.AccountName.toUpperCase(), group: row.GroupName.toUpperCase() });
break;
case 'group':
groups.push({ account: row.AccountName.toUpperCase(), group: row.GroupName.toUpperCase() })
break
groups.push({ account: row.AccountName.toUpperCase(), group: row.GroupName.toUpperCase() });
break;
}
})
});
return { users: users, groups: groups, computers: computers }
return { users: users, groups: groups, computers: computers };
}
export function buildLocalAdminProps(rows) {
var users = []
var groups = []
var computers = []
var users = [];
var groups = [];
var computers = [];
$.each(rows, function(index, row) {
if (row.AccountName.startsWith('@')) {
return
return;
}
switch (row.AccountType) {
case 'user':
users.push({ account: row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase() })
users.push({ account: row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase() });
break;
case 'group':
groups.push({ account: row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase() })
groups.push({ account: row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase() });
break;
case 'computer':
computers.push({ account: row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase() })
break
computers.push({ account: row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase() });
break;
}
})
return { users: users, groups: groups, computers: computers }
});
return { users: users, groups: groups, computers: computers };
}
export function buildSessionProps(rows) {
var sessions = []
var sessions = [];
$.each(rows, function(index, row) {
if (row.UserName === 'ANONYMOUS LOGON@UNKNOWN' || row.UserName === '') {
return
return;
}
sessions.push({ account: row.UserName.toUpperCase(), computer: row.ComputerName.toUpperCase(), weight: row.Weight })
})
sessions.push({ account: row.UserName.toUpperCase(), computer: row.ComputerName.toUpperCase(), weight: row.Weight });
});
return sessions
return sessions;
}
export function buildDomainProps(rows) {
var domains = []
var domains = [];
$.each(rows, function(index, row) {
switch (row.TrustDirection) {
case 'Inbound':
domains.push({ domain1: row.TargetDomain.toUpperCase(), domain2: row.SourceDomain.toUpperCase(), trusttype: row.TrustType, transitive: row.Transitive })
domains.push({ domain1: row.TargetDomain.toUpperCase(), domain2: row.SourceDomain.toUpperCase(), trusttype: row.TrustType, transitive: row.Transitive });
break;
case 'Outbound':
domains.push({ domain1: row.SourceDomain.toUpperCase(), domain2: row.TargetDomain.toUpperCase(), trusttype: row.TrustType, transitive: row.Transitive })
domains.push({ domain1: row.SourceDomain.toUpperCase(), domain2: row.TargetDomain.toUpperCase(), trusttype: row.TrustType, transitive: row.Transitive });
break;
case 'Bidirectional':
domains.push({ domain1: row.TargetDomain.toUpperCase(), domain2: row.SourceDomain.toUpperCase(), trusttype: row.TrustType, transitive: row.Transitive })
domains.push({ domain1: row.SourceDomain.toUpperCase(), domain2: row.TargetDomain.toUpperCase(), trusttype: row.TrustType, transitive: row.Transitive })
break
domains.push({ domain1: row.TargetDomain.toUpperCase(), domain2: row.SourceDomain.toUpperCase(), trusttype: row.TrustType, transitive: row.Transitive });
domains.push({ domain1: row.SourceDomain.toUpperCase(), domain2: row.TargetDomain.toUpperCase(), trusttype: row.TrustType, transitive: row.Transitive });
break;
}
})
});
return domains
return domains;
}
export function buildACLProps(rows) {
var datadict = {}
var datadict = {};
$.each(rows, function(index, row) {
var b = row.ObjectName.toUpperCase()
var a = row.PrincipalName.toUpperCase()
var btype = row.ObjectType.toTitleCase()
var atype = row.PrincipalType.toTitleCase()
var rel = row.ActiveDirectoryRights
var extright = row.ACEType
var b = row.ObjectName.toUpperCase();
var a = row.PrincipalName.toUpperCase();
var btype = row.ObjectType.toTitleCase();
var atype = row.PrincipalType.toTitleCase();
var rel = row.ActiveDirectoryRights;
var extright = row.ACEType;
var rights = []
var rights = [];
if (extright === 'All'){
rights.push("AllExtendedRights")
rights.push("AllExtendedRights");
}else if (extright === 'User-Force-Change-Password'){
rights.push("ForceChangePassword")
rights.push("ForceChangePassword");
}else if (rel === "ExtendedRight"){
rights.push(extright)
rights.push(extright);
}
if (rel.includes("GenericAll")){
rights.push("GenericAll")
rights.push("GenericAll");
}
if (rel.includes("WriteDacl")){
rights.push("WriteDacl")
rights.push("WriteDacl");
}
if (rel.includes("WriteOwner")){
rights.push("WriteOwner")
rights.push("WriteOwner");
}
if (rel.includes("GenericWrite")){
rights.push("GenericWrite")
rights.push("GenericWrite");
}
if (rel.includes("WriteProperty") && extright === "Member"){
rights.push("AddMember")
rights.push("AddMember");
}
$.each(rights, function(index, record){
var hash = (atype + record + btype).toUpperCase()
var hash = (atype + record + btype).toUpperCase();
if (btype === 'Computer') {
return
return;
}
if (datadict[hash]) {
datadict[hash].props.push({
account: a,
principal: b
})
});
} else {
datadict[hash] = {
statement: 'UNWIND {props} AS prop MERGE (a:{} {name:prop.account}) WITH a,prop MERGE (b:{} {name: prop.principal}) WITH a,b,prop MERGE (a)-[r:{} {isACL:true}]->(b)'.format(atype, btype, record),
props: [{ account: a, principal: b }]
};
}
}
})
});
});
})
return datadict
return datadict;
}

View File

@ -1,5 +1,5 @@
global.sigma = require('linkurious')
require('./sigma.helpers.graph.min.js')
global.sigma = require('linkurious');
require('./sigma.helpers.graph.min.js');
Array.prototype.allEdgesSameType = function() {
for (var i = 1; i < this.length; i++) {
@ -18,56 +18,56 @@ sigma.classes.graph.addMethod('inboundNodes', function(id) {
return this.inNeighborsIndex.get(id).keyList();
});
var sigmaInstance = new sigma()
var sigmaInstance = new sigma();
process.on('message', function(m){
var data = JSON.parse(m)
params = {edge: data.edge,sibling: data.sibling, start: data.start, end: data.end}
var spotlightData = {}
sigmaInstance.graph.clear()
sigmaInstance.graph.read(data.graph)
var data = JSON.parse(m);
params = {edge: data.edge,sibling: data.sibling, start: data.start, end: data.end};
var spotlightData = {};
sigmaInstance.graph.clear();
sigmaInstance.graph.read(data.graph);
sigmaInstance.graph.nodes().forEach(function(node){
node.degree = sigmaInstance.graph.degree(node.id)
})
var result = collapseEdgeNodes(sigmaInstance, params, spotlightData)
sigmaInstance = result[0]
spotlightData = result[1]
result = collapseSiblingNodes(sigmaInstance, params, spotlightData)
sigmaInstance = result[0]
spotlightData = result[1]
node.degree = sigmaInstance.graph.degree(node.id);
});
var result = collapseEdgeNodes(sigmaInstance, params, spotlightData);
sigmaInstance = result[0];
spotlightData = result[1];
result = collapseSiblingNodes(sigmaInstance, params, spotlightData);
sigmaInstance = result[0];
spotlightData = result[1];
sigmaInstance.graph.nodes().forEach(function(node) {
if (!spotlightData.hasOwnProperty(node.id)) {
spotlightData[node.id] = [node.label, 0, "", node.type, ""];
}
});
var toSend = {nodes: sigmaInstance.graph.nodes(), edges: sigmaInstance.graph.edges(), spotlight: spotlightData}
process.send(toSend)
})
var toSend = {nodes: sigmaInstance.graph.nodes(), edges: sigmaInstance.graph.edges(), spotlight: spotlightData};
process.send(toSend);
});
function collapseEdgeNodes(sigmaInstance, params, spotlightData){
var threshold = params.edge;
if (threshold == 0){
return [sigmaInstance, spotlightData]
return [sigmaInstance, spotlightData];
}
sigmaInstance.graph.nodes().forEach(function(node){
if (node.degree < threshold){
return
return;
}
sigmaInstance.graph.adjacentNodes(node.id).forEach(function(anode){
if (params.end !== null && anode.label === params.end){
return
return;
}
if (params.start !== null && anode.label === params.start){
return
return;
}
var edges = sigmaInstance.graph.adjacentEdges(anode.id);
if ((edges.length > 1 || edges.length === 0) || (anode.folded.nodes.length > 0)){
return
return;
}
var edge = edges[0];
@ -76,9 +76,9 @@ function collapseEdgeNodes(sigmaInstance, params, spotlightData){
|| (anode.type_computer)
|| (anode.type_group && edge.label === 'AdminTo')){
node.isGrouped = true
node.folded.nodes.push(anode)
node.folded.edges.push(edge)
node.isGrouped = true;
node.folded.nodes.push(anode);
node.folded.edges.push(edge);
spotlightData[anode.id] = [anode.label, node.id, node.label, anode.type, node.type];
sigmaInstance.graph.dropNode(anode.id);
}
@ -87,42 +87,42 @@ function collapseEdgeNodes(sigmaInstance, params, spotlightData){
node.glyphs.push({
'position': 'bottom-left',
'content': node.folded.nodes.length
})
});
}
})
});
return [sigmaInstance, spotlightData]
return [sigmaInstance, spotlightData];
}
function collapseSiblingNodes(sigmaInstance, params, spotlightData){
var threshold = params.sibling
var threshold = params.sibling;
if (threshold === 0){
return [sigmaInstance, spotlightData]
return [sigmaInstance, spotlightData];
}
sigmaInstance.graph.nodes().forEach(function(node){
//Dont apply this logic to anything thats folded or isn't a computer
if (!node.type_computer || node.folded.nodes.length > 0){
return
return;
}
//Start by getting all the edges attached to this node
var adjacent = sigmaInstance.graph.adjacentEdges(node.id)
var siblings = []
var adjacent = sigmaInstance.graph.adjacentEdges(node.id);
var siblings = [];
//Check to see if all the edges are the same type (i.e. AdminTo)
if (adjacent.length > 1 && adjacent.allEdgesSameType()){
//Get the "parents" by mapping the source from every edge
var parents = adjacent.map(
function(e){
return e.source
return e.source;
}
)
);
//Generate our string to compare other nodes to
//by sorting the parents and turning it into a string
var checkString = parents.sort().join(',')
var checkString = parents.sort().join(',');
var testString;
//Loop back over nodes in the graph and look for any nodes
@ -132,7 +132,7 @@ function collapseSiblingNodes(sigmaInstance, params, spotlightData){
function(e){
return e.source;
}
).sort().join(',')
).sort().join(',');
if (testString === checkString){
siblings.push(node2);
}
@ -172,25 +172,25 @@ function collapseSiblingNodes(sigmaInstance, params, spotlightData){
label: 'AdminTo',
neo4j_type: 'AdminTo',
size: 1
})
})
});
});
var n = sigmaInstance.graph.nodes(nodeId);
//Loop over all the siblings, and push the edges into our new parent node
//Push the nodes in as well so we can unfold them
siblings.forEach(function(sibling){
sigmaInstance.graph.adjacentEdges(sibling.id).forEach(function(edge){
n.folded.edges.push(edge)
})
n.folded.edges.push(edge);
});
n.folded.nodes.push(sibling)
n.folded.nodes.push(sibling);
spotlightData[sibling.id] = [sibling.label, nodeId, n.label, sibling.type, n.type];
sigmaInstance.graph.dropNode(sibling.id)
})
sigmaInstance.graph.dropNode(sibling.id);
});
}
}
})
return [sigmaInstance, spotlightData]
});
return [sigmaInstance, spotlightData];
}
function generateUniqueId(sigmaInstance, isNode){
@ -205,5 +205,5 @@ function generateUniqueId(sigmaInstance, isNode){
}
}
return i
return i;
}