From 5a897231e1b0d26a0c00dfbd4c9c5fcd524dfef5 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Fri, 6 Jan 2017 01:26:26 -0500 Subject: [PATCH 01/19] Rewrite of login screen --- index.html | 1 - src/components/Float/Login.jsx | 142 ++++++++++++++++++-------- src/components/Modals/LogoutModal.jsx | 3 +- src/css/styles.css | 4 + src/index.js | 7 ++ 5 files changed, 113 insertions(+), 44 deletions(-) diff --git a/index.html b/index.html index e8d0ef4e5..c35f12433 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,6 @@ - diff --git a/src/components/Float/Login.jsx b/src/components/Float/Login.jsx index 7c5120ead..00340c3ea 100644 --- a/src/components/Float/Login.jsx +++ b/src/components/Float/Login.jsx @@ -7,8 +7,6 @@ export default class Login extends Component { url: "", icon: null, loginEnabled: false, - dbHelpVisible: false, - loginHelpVisible: false, user: "", password: "", loginInProgress: false @@ -18,6 +16,7 @@ export default class Login extends Component { checkDBPresence(){ var url = this.state.url; var icon = this.state.icon; + var jicon = jQuery(icon) if (url === ""){ return; @@ -38,28 +37,32 @@ export default class Login extends Component { icon.removeClass(); icon.addClass("fa fa-spinner fa-spin form-control-feedback"); icon.toggle(true); - var driver = neo4j.v1.driver(url) - var session = driver.session() - - session.run('MATCH (n) RETURN (n) LIMIT 1') - .subscribe({ - onNext: function(next){ - }, - onError: function(error){ - if (error.code){ - this.setState({dbHelpVisible: true}) - icon.removeClass(); - icon.addClass("fa fa-times-circle red-icon-color form-control-feedback"); - }else{ - icon.removeClass(); - icon.addClass("fa fa-check-circle green-icon-color form-control-feedback"); - this.setState({loginEnabled: true, url: url}) - } - }.bind(this), - onComplete: function(){ - session.close() - } - }) + var driver = neo4j.driver(url) + driver.onCompleted = function(){ + driver.close() + } + driver.onError = function(error){ + if (error.message && error.message.includes("encryption certificate has changed")){ + var path = error.message.match("`(.*?)`")[1] + icon.removeClass(); + icon.addClass("fa fa-times-circle red-icon-color form-control-feedback"); + icon.attr('data-original-title', 'Certificate error - delete localhost line in {}'.format(path)) + .tooltip('fixTitle') + .tooltip('show') + }else if (error.fields && error.fields[0].code === "Neo.ClientError.Security.Unauthorized"){ + 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') + } + driver.close() + }.bind(this) + driver.session(); } checkDBCreds(){ @@ -68,24 +71,64 @@ export default class Login extends Component { } this.setState({ loginInProgress: true, - loginHelpVisible: false, loginEnabled: false }) var btn = jQuery(this.refs.loginButton) + var pwf = jQuery(this.refs.password) - var driver = neo4j.v1.driver(this.state.url, neo4j.v1.auth.basic(this.state.user, this.state.password),{knownHosts: 'known_hosts'}) - var session = driver.session() + var driver = neo4j.driver(this.state.url, neo4j.auth.basic(this.state.user, this.state.password)) + driver.onError = function(error){ + if (error.fields && error.fields[0].code === "Neo.ClientError.Security.Unauthorized"){ + 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.fields && error.fields[0].code === "Neo.ClientError.Security.AuthenticationRateLimit"){ + 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.message && error.message.includes("encryption certificate has changed")){ + var path = error.message.match("`(.*?)`")[1] + var icon = this.state.icon + icon.toggle('true') + icon.removeClass(); + icon.addClass("fa fa-times-circle red-icon-color form-control-feedback"); + jQuery(icon).tooltip({ + placement : 'right', + title: 'Certificate error - delete localhost line in ' + path, + container: 'body', + delay: {show: 200, hide: 0}, + template: '' + }) + jQuery(icon).tooltip('show') + } + driver.close() + }.bind(this) + var session = driver.session(); session.run('MATCH (n) RETURN (n) LIMIT 1') .subscribe({ onError: function(error){ - btn.toggleClass('activate'); - this.setState({ - loginHelpVisible: true, - loginInProgress: false, - loginEnabled: true - }) - + 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(){ @@ -105,12 +148,14 @@ export default class Login extends Component { }) global.driver = driver appStore.databaseInfo = conf.get('databaseInfo'); + 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) - session.close() + driver.close() }.bind(this) }) @@ -130,8 +175,23 @@ export default class Login extends Component { } componentDidMount() { - jQuery(this.refs.urlspinner).toggle(false) + jQuery(this.refs.password).tooltip({ + placement : 'right', + title: '', + container: 'body', + trigger: 'manual', + template: '' + }) this.setState({icon: jQuery(this.refs.urlspinner)}) + var icon = jQuery(this.refs.urlspinner) + icon.tooltip({ + placement : 'right', + title: '', + container: 'body', + delay: {show: 200, hide: 0}, + template: '' + }) + icon.toggle(false) if (this.state.password !== ""){ this.checkDBCreds(); } @@ -142,11 +202,13 @@ export default class Login extends Component { } _userChanged(event){ - this.setState({user: event.target.value}) + this.setState({user: event.target.value}) + jQuery(this.refs.password).tooltip('hide') } _passChanged(event){ - this.setState({password: event.target.value}) + this.setState({password: event.target.value}) + jQuery(this.refs.password).tooltip('hide') } _triggerLogin(e){ @@ -171,10 +233,9 @@ export default class Login extends Component { Database URL - + - {this.state.dbHelpVisible ?

No Neo4j Database Found

: null}
DB Username @@ -183,7 +244,6 @@ export default class Login extends Component { DB Password
- {this.state.loginHelpVisible ?

Wrong username or password

: null} + + + ); + } +} \ No newline at end of file diff --git a/src/css/styles.css b/src/css/styles.css index 2bd3dd921..ed48b0439 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -44,6 +44,13 @@ div.tooltip-inner-custom { max-width: 250px; } +.aboutscroll{ + width: 100%; + overflow: auto; + height: 150px; + white-space: pre-wrap; +} + .queryInput { position: relative; left: 50%; From 2f73b9ba69574d313a3015a87173ea2ed3e232ea Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Thu, 12 Jan 2017 23:23:49 -0500 Subject: [PATCH 13/19] Fix ACL ingestion --- src/components/Menu/MenuContainer.jsx | 2 +- src/index.js | 211 ++++++++--------- src/js/utils.js | 328 +++++++++++++------------- 3 files changed, 271 insertions(+), 270 deletions(-) diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index 2a4e85a7f..e80fbc3c6 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -134,7 +134,7 @@ export default class MenuContainer extends Component { .then(function(){ sess.close() }) - }, 250) + }, 2000) console.time('IngestTime') Papa.parse(fileobject,{ diff --git a/src/index.js b/src/index.js index babbf0182..0ccf57ebb 100644 --- a/src/index.js +++ b/src/index.js @@ -20,20 +20,21 @@ global.neo4j = require('neo4j-driver').v1; global.Mustache = require('mustache') -String.prototype.format = function () { - var i = 0, args = arguments; - return this.replace(/{}/g, function () { - return typeof args[i] != 'undefined' ? args[i++] : ''; - }); +String.prototype.format = function() { + var i = 0, + args = arguments; + return this.replace(/{}/g, function() { + return typeof args[i] != 'undefined' ? args[i++] : ''; + }); }; -String.prototype.formatAll = function () { - var args = arguments; - return this.replace(/{}/g, args[0]); +String.prototype.formatAll = function() { + var args = arguments; + return this.replace(/{}/g, args[0]); }; String.prototype.toTitleCase = function() { - return this.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); + return this.replace(/\w\S*/g, function(txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }); }; Array.prototype.allEdgesSameType = function() { @@ -46,8 +47,8 @@ Array.prototype.allEdgesSameType = function() { return true; }; -if (!Array.prototype.last){ - Array.prototype.last = function(){ +if (!Array.prototype.last) { + Array.prototype.last = function() { return this[this.length - 1]; }; }; @@ -63,16 +64,16 @@ sigma.classes.graph.addMethod('inboundNodes', function(id) { }); global.appStore = { - dagre: false, - startNode: null, - endNode: null, - highlightedEdges: [], - spotlightData: {}, - queryStack: [], - currentTooltip: null, - highResPalette: { - iconScheme: { - 'User': { + dagre: false, + startNode: null, + endNode: null, + highlightedEdges: [], + spotlightData: {}, + queryStack: [], + currentTooltip: null, + highResPalette: { + iconScheme: { + 'User': { font: 'FontAwesome', content: '\uF007', scale: 1.5, @@ -98,103 +99,103 @@ global.appStore = { } }, edgeScheme: { - 'AdminTo': 'tapered', - 'MemberOf': 'tapered', - 'HasSession': 'tapered', - 'TrustedBy' : 'curvedArrow' + 'AdminTo': 'tapered', + 'MemberOf': 'tapered', + 'HasSession': 'tapered', + 'TrustedBy': 'curvedArrow' } - }, - lowResPalette: { - colorScheme: { - 'User' : '#17E625', - 'Computer' : '#E67873', - 'Group' : '#DBE617', - 'Domain' : '#17E6B9' - }, + }, + lowResPalette: { + colorScheme: { + 'User': '#17E625', + 'Computer': '#E67873', + 'Group': '#DBE617', + 'Domain': '#17E6B9' + }, edgeScheme: { - 'AdminTo': 'line', - 'MemberOf': 'line', - 'HasSession': 'line', - 'TrustedBy' : 'curvedArrow' + 'AdminTo': 'line', + 'MemberOf': 'line', + 'HasSession': 'line', + 'TrustedBy': 'curvedArrow' + } + }, + highResStyle: { + nodes: { + label: { + by: 'label' + }, + size: { + by: 'degree', + bins: 5, + min: 10, + max: 20 + }, + icon: { + by: 'type', + scheme: 'iconScheme' + } + }, + edges: { + type: { + by: 'type', + scheme: 'edgeScheme' + } } - }, - highResStyle: { - nodes: { - label: { - by: 'label' - }, - size: { - by: 'degree', - bins: 5, - min: 10, - max: 20 - }, - icon: { - by: 'type', - scheme: 'iconScheme' - } - }, - edges: { - type : { - by : 'type', - scheme: 'edgeScheme' - } - } - }, - lowResStyle: { - nodes: { - label: { - by: 'label' - }, - size: { - by: 'degree', - bins: 10, - min: 10, - max: 20 - }, - color: { - by: 'type', - scheme: 'colorScheme' - } - }, - edges: { - type : { - by : 'type', - scheme: 'edgeScheme' - } - } - } + }, + lowResStyle: { + nodes: { + label: { + by: 'label' + }, + size: { + by: 'degree', + bins: 10, + min: 10, + max: 20 + }, + color: { + by: 'type', + scheme: 'colorScheme' + } + }, + edges: { + type: { + by: 'type', + scheme: 'edgeScheme' + } + } + } } -if (typeof conf.get('performance') === 'undefined'){ - conf.set('performance', { - edge: 5, - sibling: 10, - lowGraphics: false, - nodeLabels: 1 - }) +if (typeof conf.get('performance') === 'undefined') { + conf.set('performance', { + edge: 5, + sibling: 10, + lowGraphics: false, + nodeLabels: 1 + }) } -var custompath = path.join(app.getPath('userData'),'customqueries.json') +var custompath = path.join(app.getPath('userData'), 'customqueries.json') -fs.stat(custompath, function(err, stats){ - if (err){ - fs.writeFile(custompath,"") - } +fs.stat(custompath, function(err, stats) { + if (err) { + fs.writeFile(custompath, "[]") + } }) appStore.performance = conf.get('performance') -renderEmit.on('login', function(){ - emitter.removeAllListeners() - ReactDOM.unmountComponentAtNode(document.getElementById('root')) - ReactDOM.render(, document.getElementById('root')) +renderEmit.on('login', function() { + emitter.removeAllListeners() + ReactDOM.unmountComponentAtNode(document.getElementById('root')) + ReactDOM.render( < AppContainer / > , document.getElementById('root')) }) -renderEmit.on('logout', function(){ - emitter.removeAllListeners() - ReactDOM.unmountComponentAtNode(document.getElementById('root')) - ReactDOM.render(, document.getElementById('root')) +renderEmit.on('logout', function() { + emitter.removeAllListeners() + ReactDOM.unmountComponentAtNode(document.getElementById('root')) + ReactDOM.render( < Login / > , document.getElementById('root')) }) -ReactDOM.render(, document.getElementById('root')) \ No newline at end of file +ReactDOM.render( < Login / > , document.getElementById('root')) \ No newline at end of file diff --git a/src/js/utils.js b/src/js/utils.js index 42f5fe2a9..5d371ca46 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -1,184 +1,184 @@ -export function generateUniqueId(sigmaInstance, isNode){ - var i = Math.floor(Math.random() * (100000 - 10 + 1)) + 10; - if (isNode){ - while (typeof sigmaInstance.graph.nodes(i) !== 'undefined'){ - i = Math.floor(Math.random() * (100000 - 10 + 1)) + 10; - } - }else{ - while (typeof sigmaInstance.graph.edges(i) !== 'undefined'){ - i = Math.floor(Math.random() * (100000 - 10 + 1)) + 10; - } - } - - return i +export function generateUniqueId(sigmaInstance, isNode) { + var i = Math.floor(Math.random() * (100000 - 10 + 1)) + 10; + if (isNode) { + while (typeof sigmaInstance.graph.nodes(i) !== 'undefined') { + i = Math.floor(Math.random() * (100000 - 10 + 1)) + 10; + } + } else { + while (typeof sigmaInstance.graph.edges(i) !== 'undefined') { + i = Math.floor(Math.random() * (100000 - 10 + 1)) + 10; + } + } + + return i } //Recursive function to highlight paths to start/end nodes -export function findGraphPath(sigmaInstance, reverse, nodeid){ - 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) - //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 - //If an edge is pointing in the right direction, set its color - //Push the edge into our store and then - node = parseInt(node) - if (check === node){ - edge.color = reverse ? 'blue' : 'red'; - appStore.highlightedEdges.push(edge); - findGraphPath(sigmaInstance, reverse, node); - } - }) - }) - }else{ - return - } +export function findGraphPath(sigmaInstance, reverse, nodeid) { + 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) + //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 + //If an edge is pointing in the right direction, set its color + //Push the edge into our store and then + node = parseInt(node) + if (check === node) { + edge.color = reverse ? 'blue' : 'red'; + appStore.highlightedEdges.push(edge); + findGraphPath(sigmaInstance, reverse, node); + } + }) + }) + } else { + return + } } -export function clearDatabase(){ - emitter.emit('openClearingModal'); - deleteEdges() +export function clearDatabase() { + emitter.emit('openClearingModal'); + deleteEdges() } -function deleteEdges(){ - var session = driver.session() - session.run("MATCH ()-[r]-() WITH r LIMIT 100000 DELETE r RETURN count(r)") - .then(function(results){ - session.close() - var count = results.records[0]._fields[0].low - if (count === 0){ - deleteNodes() - }else{ - deleteEdges() - } - }) +function deleteEdges() { + var session = driver.session() + session.run("MATCH ()-[r]-() WITH r LIMIT 100000 DELETE r RETURN count(r)") + .then(function(results) { + session.close() + var count = results.records[0]._fields[0].low + if (count === 0) { + deleteNodes() + } else { + deleteEdges() + } + }) } -function deleteNodes(){ - var session = driver.session() - session.run("MATCH (n) WITH n LIMIT 100000 DELETE n RETURN count(n)") - .then(function(results){ - session.close() - var count = results.records[0]._fields[0].low - if (count === 0){ - emitter.emit('hideDBClearModal') - }else{ - deleteNodes() - } - }) +function deleteNodes() { + var session = driver.session() + session.run("MATCH (n) WITH n LIMIT 100000 DELETE n RETURN count(n)") + .then(function(results) { + session.close() + var count = results.records[0]._fields[0].low + if (count === 0) { + emitter.emit('hideDBClearModal') + } else { + deleteNodes() + } + }) } -export function buildGroupMembershipProps(rows){ - 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 - case 'computer': - computers.push({account:row.AccountName.toUpperCase(), group: row.GroupName.toUpperCase()}) - break - case 'group': - groups.push({account:row.AccountName.toUpperCase(), group: row.GroupName.toUpperCase()}) - break - } - }) - - return {users: users, groups: groups, computers: computers} +export function buildGroupMembershipProps(rows) { + 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 + case 'computer': + computers.push({ account: row.AccountName.toUpperCase(), group: row.GroupName.toUpperCase() }) + break + case 'group': + groups.push({ account: row.AccountName.toUpperCase(), group: row.GroupName.toUpperCase() }) + break + } + }) + + return { users: users, groups: groups, computers: computers } } -export function buildLocalAdminProps(rows){ - var users = [] - var groups = [] - var computers = [] - $.each(rows, function(index, row){ - if (row.AccountName.startsWith('@')){ - return - } - switch(row.AccountType){ - case 'user': - users.push({account:row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase()}) - break; - case 'group': - groups.push({account:row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase()}) - break; - case 'computer': - computers.push({account:row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase()}) - break - } - }) - return {users: users, groups: groups, computers: computers} +export function buildLocalAdminProps(rows) { + var users = [] + var groups = [] + var computers = [] + $.each(rows, function(index, row) { + if (row.AccountName.startsWith('@')) { + return + } + switch (row.AccountType) { + case 'user': + users.push({ account: row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase() }) + break; + case 'group': + groups.push({ account: row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase() }) + break; + case 'computer': + computers.push({ account: row.AccountName.toUpperCase(), computer: row.ComputerName.toUpperCase() }) + break + } + }) + return { users: users, groups: groups, computers: computers } } -export function buildSessionProps(rows){ - var sessions = [] - $.each(rows, function(index, row){ - if (row.UserName === 'ANONYMOUS LOGON@UNKNOWN' || row.UserName === ''){ - return - } - sessions.push({account: row.UserName.toUpperCase(), computer: row.ComputerName.toUpperCase(), weight: row.Weight}) - }) +export function buildSessionProps(rows) { + var sessions = [] + $.each(rows, function(index, row) { + if (row.UserName === 'ANONYMOUS LOGON@UNKNOWN' || row.UserName === '') { + return + } + sessions.push({ account: row.UserName.toUpperCase(), computer: row.ComputerName.toUpperCase(), weight: row.Weight }) + }) - return sessions + return sessions } -export function buildDomainProps(rows){ - 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}) - break; - case 'Outbound': - 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 - } - }) - - return domains +export function buildDomainProps(rows) { + 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 }) + break; + case 'Outbound': + 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 + } + }) + + return domains } -export function buildACLProps(rows){ - var datadict = {} - - $.each(rows, function(index, row){ - var a = row.ObjectName.toUpperCase() - var b = row.PrincipalName.toUpperCase() - var atype = row.ObjectType.toTitleCase() - var btype = row.PrincipalType.toTitleCase() - var rel = row.ActiveDirectoryRights - - var hash = (atype + rel + btype).toUpperCase() - if (btype === 'Computer'){ - return - } - - if (rel.includes(' ')){ - rel = 'WriteDACL' - } - - 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:{}]->(b)'.format(atype,btype,rel), - props: [{account: a, principal: b}] - } - } - }) - - return datadict -} +export function buildACLProps(rows) { + 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 hash = (atype + rel + btype).toUpperCase() + if (btype === 'Computer') { + return + } + + if (rel.includes(' ')) { + rel = 'WriteDACL' + } + + 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:{}]->(b)'.format(atype, btype, rel), + props: [{ account: a, principal: b }] + } + } + }) + + return datadict +} \ No newline at end of file From 5754bb74803aafe41ec5331418146995a804d1f9 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Fri, 20 Jan 2017 11:05:43 -0500 Subject: [PATCH 14/19] Fix button disabled state --- src/components/Float/Login.jsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/Float/Login.jsx b/src/components/Float/Login.jsx index cc1692bb6..05f6e1d5e 100644 --- a/src/components/Float/Login.jsx +++ b/src/components/Float/Login.jsx @@ -17,6 +17,7 @@ export default class Login extends Component { var url = this.state.url; var icon = this.state.icon; var jicon = jQuery(icon) + var btn = jQuery(this.refs.loginButton) if (url === ""){ return; @@ -42,6 +43,7 @@ export default class Login extends Component { driver.close() } driver.onError = function(error){ + console.log(error) if (error.message && error.message.includes("encryption certificate has changed")){ var path = error.message.match("`(.*?)`")[1] icon.removeClass(); @@ -49,9 +51,12 @@ export default class Login extends Component { icon.attr('data-original-title', 'Certificate error - delete localhost line in {}'.format(path)) .tooltip('fixTitle') .tooltip('show') + this.setState({ + loginInProgress: false, + loginEnabled: false + }) }else if (error.fields && error.fields[0].code === "Neo.ClientError.Security.Unauthorized"){ icon.removeClass(); - btn.addClass('activate'); icon.addClass("fa fa-check-circle green-icon-color form-control-feedback"); this.setState({loginEnabled: true, url: url}) }else{ @@ -60,11 +65,11 @@ export default class Login extends Component { icon.attr('data-original-title', 'No database found') .tooltip('fixTitle') .tooltip('show') + this.setState({ + loginInProgress: false, + loginEnabled: false + }) } - this.setState({ - loginInProgress: false, - loginEnabled: false - }) driver.close() }.bind(this) driver.session(); From f26addc3bdb9e8d20ce181c6b0f534b0a8d96efe Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Fri, 20 Jan 2017 11:07:16 -0500 Subject: [PATCH 15/19] Update packages --- package.json | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index b7da64568..6c1e00d21 100644 --- a/package.json +++ b/package.json @@ -34,16 +34,16 @@ ] }, "devDependencies": { - "babel-cli": "^6.11.4", - "babel-core": "^6.11.4", - "babel-loader": "^6.2.4", - "babel-polyfill": "^6.9.1", - "babel-preset-es2015": "^6.9.0", - "babel-preset-react": "^6.11.1", - "babel-preset-stage-0": "^6.5.0", - "concurrently": "^2.2.0", - "cross-env": "^2.0.0", - "electron": "^1.4.3", + "babel-cli": "^6.22.2", + "babel-core": "^6.22.1", + "babel-loader": "*", + "babel-polyfill": "^6.22.0", + "babel-preset-es2015": "^6.22.0", + "babel-preset-react": "^6.22.0", + "babel-preset-stage-0": "^6.22.0", + "concurrently": "^3.1.0", + "cross-env": "^3.1.4", + "electron": "^1.4.15", "express": "^4.14.0", "webpack": "^1.13.1", "webpack-dev-middleware": "^1.6.1", @@ -51,19 +51,20 @@ "webpack-target-electron-renderer": "^0.4.0" }, "dependencies": { + "async": "^2.1.4", "bootstrap": "^3.3.6", "bootstrap-3-typeahead": "^4.0.1", - "configstore": "^2.0.0", + "configstore": "^2.1.0", "dagre": "^0.7.4", - "eventemitter2": "^2.0.0", + "eventemitter2": "^2.2.2", "jquery": "^2.2.4", "linkurious": "^1.5.1", "mustache": "^2.2.1", - "neo4j-driver": "^1.0.4", - "react": "^15.3.1", - "react-addons-css-transition-group": "^15.3.1", + "neo4j-driver": "^1.1.0", + "react": "^15.4.2", + "react-addons-css-transition-group": "^15.4.2", "react-bootstrap": "^0.30.3", - "react-dom": "^15.3.1", + "react-dom": "^15.4.2", "react-if": "^2.1.0" } } From 6b7b5538a1251830918a22f112b8795c9544d830 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Fri, 20 Jan 2017 11:09:04 -0500 Subject: [PATCH 16/19] Lower workaround timeout --- src/components/Menu/MenuContainer.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index e80fbc3c6..391707f52 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -134,7 +134,7 @@ export default class MenuContainer extends Component { .then(function(){ sess.close() }) - }, 2000) + }, 1000) console.time('IngestTime') Papa.parse(fileobject,{ From 1ef258ab2b63e4dc3bc4b8e1dd6d37f292311f44 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Fri, 20 Jan 2017 11:12:30 -0500 Subject: [PATCH 17/19] Upgrade DB --- .../neostore.counts.db.a | Bin 1248 -> 1248 bytes .../neostore.transaction.db.0 | Bin 16451474 -> 16451492 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/BloodHoundExampleDB.graphdb/neostore.counts.db.a b/BloodHoundExampleDB.graphdb/neostore.counts.db.a index 98be43307f80e5059b8559c4efa1d120ee9824fc..cda798b2573d09c1d1c82f21f0cc280edfb2ddb3 100644 GIT binary patch delta 12 TcmaFB`G9kRJtNCThXpJE9~%Tb delta 12 TcmaFB`G9kRJtNabhXpJE9}xsP diff --git a/BloodHoundExampleDB.graphdb/neostore.transaction.db.0 b/BloodHoundExampleDB.graphdb/neostore.transaction.db.0 index 0fc9007e257dec180b15d383e56f7fe411338758..c76ae782fd5381bb31dc85dfd8f79b9c5e4a077f 100644 GIT binary patch delta 532 zcmYkrWta{I003b9Ve&AW?%tU0m_FS#HQn9KYr0N%oo-HdP0XfEcRL*?{_xZLJa5RP z@IdIdLq!%vRMA8iLqJTi#P)+Y;)*A}1QH68NMcF+D5;b=6Z}0}VCuhsK&{s+s2g)Iv+GwAMyj?X=fHN1b%mMOWQ)*F#Ue^wvjT z{q#4$K!Xf6#8ATwHv%L5Wt7pz7;BvICYWfF$)*S~)il%1Fw-ov%`w+J^DVH@B8x4t zRH$W^TVbVDR$F7Ob=KQpqfIv3BFt9XY`4QsyX>~dUi<8Kz(I!`cEnMCJLb3(PWs0w z|N75qXPkA;c^6!C$^S09;;L(|yWyr=ZoA{Id+vMSp+_Ej;;CoCJ@>*(ue|ogTkpL0 WL4=P!`5g8oBoGu4i1fWLf#9zQHw5JX delta 513 zcmWN=Ly!;v006*=mu=g&ZQHhOW3w+?mtB`_+qP|eYG*cR%DaHT|Az@7q)X_qBIO&wr&N%Cw^DemPlFP2R>YD3rxapSL y?zroo`yP1ck;k5R>Y3+WcYML=`01D5{sjLG4EP7Z(E^D8 From 81d6ef21c4a9f0381142ef0661983bf6e13e7794 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Fri, 20 Jan 2017 11:37:49 -0500 Subject: [PATCH 18/19] Add session clear button --- src/AppContainer.jsx | 2 + src/components/Modals/ClearingModal.jsx | 2 +- src/components/Modals/SessionClearModal.jsx | 54 +++++++++++++++++++ .../Tabs/DatabaseDataDisplay.jsx | 7 ++- src/js/utils.js | 19 +++++++ 5 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 src/components/Modals/SessionClearModal.jsx diff --git a/src/AppContainer.jsx b/src/AppContainer.jsx index bd4604e89..17fd0c73f 100644 --- a/src/AppContainer.jsx +++ b/src/AppContainer.jsx @@ -15,6 +15,7 @@ import ExportContainer from './components/Float/ExportContainer'; import Settings from './components/Float/Settings' import ZoomContainer from './components/Zoom/ZoomContainer' import QueryNodeSelect from './components/Float/QueryNodeSelect' +import SessionClearModal from './components/Modals/SessionClearModal' import ReactCSSTransitionGroup from 'react-addons-css-transition-group' import About from './components/Modals/About.jsx' @@ -42,6 +43,7 @@ export default class AppContainer extends Component { + diff --git a/src/components/Modals/ClearingModal.jsx b/src/components/Modals/ClearingModal.jsx index 8911097fd..d2e2941ad 100644 --- a/src/components/Modals/ClearingModal.jsx +++ b/src/components/Modals/ClearingModal.jsx @@ -30,7 +30,7 @@ export default class ClearingModal extends Component { aria-labelledby="ClearingModalHeader"> - Clearing Database + Clearing Data ); diff --git a/src/components/Modals/SessionClearModal.jsx b/src/components/Modals/SessionClearModal.jsx new file mode 100644 index 000000000..dcaf8f9fc --- /dev/null +++ b/src/components/Modals/SessionClearModal.jsx @@ -0,0 +1,54 @@ +import React, { Component } from 'react'; +import { clearSessions } from 'utils' + +var Modal = require('react-bootstrap').Modal; + +export default class SessionClearModal extends Component { + constructor(){ + super(); + + this.state = { + open: false + } + } + + closeModal(){ + this.setState({open: false}) + } + + openModal(){ + this.setState({open: true}) + } + + closeAndClear(){ + this.setState({ open: false }) + clearSessions(); + } + + componentDidMount(){ + emitter.on('openSessionClearModal', this.openModal.bind(this)) + } + + render() { + return ( + + + + Clear Sessions + + + +

Are you sure you want to clear sessions?

+
+ + + + + +
+ ); + } +} diff --git a/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx b/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx index f350e4f94..3da5a5a7d 100644 --- a/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx +++ b/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx @@ -43,6 +43,10 @@ export default class DatabaseDataDisplay extends Component { toggleDBWarnModal(){ emitter.emit('openDBWarnModal') } + + toggleSessionClearModal(){ + emitter.emit('openSessionClearModal') + } render() { @@ -67,8 +71,9 @@ export default class DatabaseDataDisplay extends Component {
-
+
+
diff --git a/src/js/utils.js b/src/js/utils.js index 5d371ca46..56b38e3c6 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -39,6 +39,25 @@ export function findGraphPath(sigmaInstance, reverse, nodeid) { } } +export function clearSessions(){ + emitter.emit('openClearingModal'); + deleteSessions(); +} + +function deleteSessions(){ + var session = driver.session() + session.run("MATCH ()-[r:HasSession]-() WITH r LIMIT 100000 DELETE r RETURN count(r)") + .then(function(results) { + session.close() + var count = results.records[0]._fields[0].low + if (count === 0) { + emitter.emit('hideDBClearModal') + } else { + deleteSessions(); + } + }) +} + export function clearDatabase() { emitter.emit('openClearingModal'); deleteEdges() From fa1438fd912e7bf2f628b7185b460a4dc995f69d Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Fri, 20 Jan 2017 11:47:12 -0500 Subject: [PATCH 19/19] Refresh db stats on clear loops --- src/js/utils.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/js/utils.js b/src/js/utils.js index 56b38e3c6..53252ebbc 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -49,6 +49,7 @@ function deleteSessions(){ 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 if (count === 0) { emitter.emit('hideDBClearModal') @@ -67,6 +68,7 @@ function deleteEdges() { 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 if (count === 0) { @@ -81,6 +83,7 @@ function deleteNodes() { 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 if (count === 0) {