commit
60a68c4a54
|
@ -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">
|
||||
|
|
11
package.json
11
package.json
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
|
@ -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>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
|
@ -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
|
||||
}
|
||||
};
|
|
@ -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>
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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
193
src/js/utils.js
193
src/js/utils.js
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue