From a2fccc39a0ac0553cf0c7f82663c23bda382a48f Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Wed, 27 Sep 2017 19:05:55 -0400 Subject: [PATCH 01/15] Updating UI and CSV ingestion --- index.html | 1 - package.json | 4 +- src/components/Graph.jsx | 79 ++-- src/components/Menu/MenuContainer.jsx | 658 +++++++++++++++----------- src/css/styles.css | 4 +- src/index.js | 3 +- src/js/utils.js | 202 ++++---- src/js/worker.js | 350 +++++++------- 8 files changed, 719 insertions(+), 582 deletions(-) diff --git a/index.html b/index.html index c35f12433..1608292d7 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,6 @@ - diff --git a/package.json b/package.json index 093165009..cdb26cf8f 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ }, "dependencies": { "async": "^2.1.4", + "await-stream-ready": "^1.0.1", "bootstrap": "^3.3.6", "bootstrap-3-typeahead": "^4.0.1", "configstore": "^3.1.0", @@ -59,7 +60,8 @@ "jquery": "^3.2.1", "linkurious": "^1.5.1", "mustache": "^2.2.1", - "neo4j-driver": "^1.3.0", + "neo4j-driver": "^1.4.1", + "papaparse": "^4.3.6", "prop-types": "^15.5.10", "react": "^15.4.2", "react-bootstrap": "^0.31.0", diff --git a/src/components/Graph.jsx b/src/components/Graph.jsx index 3729d27e4..33e181632 100644 --- a/src/components/Graph.jsx +++ b/src/components/Graph.jsx @@ -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}) }.bind(this) - }) + }); child.stdout.on('data', (data) => { console.log(`stdout: ${data}`); @@ -41,7 +42,7 @@ export default class GraphContainer extends Component { this.loadFromChildProcess(m) }.bind(this)); - var s1 = driver.session() + var s1 = driver.session(); var s2 = driver.session() var s3 = driver.session() var s4 = driver.session() @@ -84,12 +85,41 @@ export default class GraphContainer extends Component { }.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() if (appStore.dagre){ sigma.layouts.dagre.start(this.state.sigmaInstance); }else{ - sigma.layouts.startForceLink() + sigma.layouts.startForceLink(); } } @@ -106,17 +136,17 @@ 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)); + }); } } @@ -231,25 +261,6 @@ export default class GraphContainer extends Component { 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)) - } - resetZoom(){ sigma.misc.animation.camera( this.state.sigmaInstance.camera, @@ -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 (
diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index 391707f52..91e2bda1b 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -1,284 +1,388 @@ 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 } = require('electron').remote; +var fs = require('fs'); +var async = require('async'); +var Papa = require('papaparse'); +var awaitReadStream = require('await-stream-ready').read; export default class MenuContainer extends Component { - constructor(){ - super() - - this.state = { - refreshHover: false, - uploading: false, - progress: 0, - parser: null - } - - emitter.on('cancelUpload', this.cancelUpload.bind(this)) - } - - cancelUpload(){ - this.state.parser.abort() - setTimeout(function(){ - this.setState({uploading: false}) - }.bind(this), 1000) - } - - _refreshClick(){ - emitter.emit('graphRefresh') - } - - _changeLayoutClick(){ - appStore.dagre = !appStore.dagre - emitter.emit('graphRefresh') - var type = appStore.dagre ? 'Hierarchical' : 'Directed' - emitter.emit('showAlert', 'Changed Layout to ' + type) - } - - _exportClick(){ - emitter.emit('showExport'); - } - - _importClick(){ - var fname = dialog.showOpenDialog({ - properties: ['openFile'] - }) - if (typeof fname !== 'undefined'){ - emitter.emit('import',fname[0]) - } - } - - _settingsClick(){ - emitter.emit('openSettings') - } - - _cancelUploadClick(){ - emitter.emit('showCancelUpload') - } - - _uploadClick(){ - var input = jQuery(this.refs.fileInput) - var files = $.makeArray(input[0].files) - - async.eachSeries(files, function(file, callback){ - emitter.emit('showAlert', 'Processing file {}'.format(file.name)); - this.processFile(file.path, file, callback) - }.bind(this), - function done(){ - setTimeout(function(){ - this.setState({uploading: false}) - }.bind(this), 3000) - }.bind(this)) - - input.val('') - } - - _aboutClick(){ - emitter.emit('showAbout') - } - - processFile(filename, fileobject, callback){ - var sent = 0 - - var i; - 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++ - }; - } - - }) - .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' - } - - if (typeof filetype === 'undefined'){ - emitter.emit('showAlert', 'Unrecognized CSV Type'); - return; - } - - this.setState({ - uploading: true, - progress: 0 - }) - //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) - - 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}) - .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)' - - 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) - }) - }.bind(this)); - } - - render() { - return ( -
-
- -
-
- -
-
- -
-
- - - - - { () => - - } - -
-
- -
-
- -
-
- -
- -
- ); - } + constructor(){ + super() + + this.state = { + refreshHover: false, + uploading: false, + progress: 0, + parser: null + } + + emitter.on('cancelUpload', this.cancelUpload.bind(this)) + } + + cancelUpload(){ + this.state.parser.abort() + setTimeout(function(){ + this.setState({uploading: false}) + }.bind(this), 1000) + } + + _refreshClick(){ + emitter.emit('graphRefresh') + } + + _changeLayoutClick(){ + appStore.dagre = !appStore.dagre + emitter.emit('graphRefresh') + var type = appStore.dagre ? 'Hierarchical' : 'Directed' + emitter.emit('showAlert', 'Changed Layout to ' + type) + } + + _exportClick(){ + emitter.emit('showExport'); + } + + _importClick(){ + var fname = dialog.showOpenDialog({ + properties: ['openFile'] + }) + if (typeof fname !== 'undefined'){ + emitter.emit('import',fname[0]) + } + } + + _settingsClick(){ + emitter.emit('openSettings') + } + + _cancelUploadClick(){ + emitter.emit('showCancelUpload') + } + + _uploadClick(){ + var input = jQuery(this.refs.fileInput) + var files = $.makeArray(input[0].files) + + async.eachSeries(files, function(file, callback){ + emitter.emit('showAlert', 'Processing file {}'.format(file.name)); + this.processFile(file.path, file, callback) + }.bind(this), + function done(){ + setTimeout(function(){ + this.setState({uploading: false}) + }.bind(this), 3000) + }.bind(this)) + + input.val('') + } + + _aboutClick(){ + emitter.emit('showAbout') + } + + processFile(filename, fileobject, callback){ + var count = 0; + var dataset = []; + + var filetype; + var abort = false; + + this.setState({ + uploading: true, + progress: 0 + }); + console.time('IngestTime'); + Papa.parse(fs.createReadStream(filename), { + header: true, + skipEmptyLines: true, + beforeFirstChunk: function(chunk){ + filetype = findObjectType(chunk.split('\n')[0]); + if (filetype === 'unknown'){ + abort = true; + } + }, + chunk: function(results, parser){ + if (abort){ + parser.abort(); + emitter.emit('showAlert', 'Unrecognized CSV Type'); + this.setState({ + uploading: false, + progress: 0 + }); + return; + } + dataset = dataset.concat(results.data); + count += results.data.length; + }.bind(this), + complete: function(results){ + var chunks = []; + var temparray,i,j,chunk = 100000; + for (i=0, j=dataset.length; i < j; i+=chunk){ + temparray = dataset.slice(i,i+chunk); + chunks.push(temparray); + } + this.uploadData(chunks, filetype, count, callback); + }.bind(this) + }); + } + + async uploadData(chunks, filetype, total, callback){ + var index = 0; + var processed; + var sent = 0; + var session = driver.session(); + + while (index < chunks.length){ + var currentChunk = chunks[index]; + + 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: props}); + }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'){ + query = 'UNWIND {props} AS prop MERGE (user:User {name: upper(prop.AccountName)}) SET user.Enabled = toBoolean(prop.Enabled),user.PwdLastSet = prop.PwdLastSet,user.LastLogon = prop.LastLogon,user.Sid = prop.Sid,user.SidHistory = prop.SidHistory,user.HasSPN = toBoolean(prop.HasSPN),user.ServicePrincipalNames = split(prop.ServicePrincipalNames, "|")'; + + 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=prop.PwdLastSet,comp.LastLogon=prop.LastLogon,comp.OperatingSystem=prop.OperatingSystem,comp.Sid=prop.Sid'; + + await session.run(query, {props:currentChunk}); + } + + sent += currentChunk.length; + this.setState({progress: Math.floor(sent / total * 100)}); + index++; + } + this.setState({progress:100}); + emitter.emit('refreshDBData'); + console.timeEnd('IngestTime'); + callback(); + } + + processFileOld(filename, fileobject, callback){ + var sent = 0; + + var i; + 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++; + } + } + + }) + .on('end', function() { + count = count - 1; + var filetype = findObjectType(header); + + if (filetype === 'unknown'){ + emitter.emit('showAlert', 'Unrecognized CSV Type'); + return; + } + + this.setState({ + uploading: true, + progress: 0 + }) + //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) */ + + 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}) + .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)' + + 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) + }) + }.bind(this)); + } + + render() { + return ( +
+
+ +
+
+ +
+
+ +
+
+ + + + + { () => + + } + +
+
+ +
+
+ +
+
+ +
+ +
+ ); + } } diff --git a/src/css/styles.css b/src/css/styles.css index 72c0a53d2..f190a593a 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -3,7 +3,8 @@ #root { position: absolute; width: 100%; - height: 100%; + height: 100%; + overflow: hidden; } .max{ @@ -94,6 +95,7 @@ div.tooltip-inner-custom { width: 100%; height: 100%; background-color: lightgray; + overflow: hidden; } .graph:focus { diff --git a/src/index.js b/src/index.js index 3f7101cd2..7864d8158 100644 --- a/src/index.js +++ b/src/index.js @@ -109,7 +109,8 @@ global.appStore = { 'WriteDACL': 'tapered', 'WriteOwner': 'tapered', 'AddMembers': 'tapered', - 'TrustedBy': 'curvedArrow' + 'TrustedBy': 'curvedArrow', + 'DCSync' : 'tapered' } }, lowResPalette: { diff --git a/src/js/utils.js b/src/js/utils.js index e058f0acd..decbe8664 100644 --- a/src/js/utils.js +++ b/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,220 @@ 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.include('Enabled') && header.include('PwdLastSet') && header.include('LastLogon') && header.include('OperatingSystem') && header.include('Sid')){ + return 'compprops'; + }else{ + return 'unknown'; + } +} + +export function buildUserObjectProps(rows){ + var data = []; + $.each(rows, function(index, row){ + data.push({ account:row.AccountName.toUpperCase(), enabled: row.Enabled, pwdlastset:row.PwdLastSet, lastlogon: row.LastLogon, sid: row.Sid, sidhistory: row.SidHistory, hasspn: row.HasSPN, spn: row.ServicePrincipalNames}); + }); + + return data; } 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: parseInt(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; } \ No newline at end of file diff --git a/src/js/worker.js b/src/js/worker.js index fcb5ec814..b91d8acd3 100644 --- a/src/js/worker.js +++ b/src/js/worker.js @@ -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,192 +18,192 @@ 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) - 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] - sigmaInstance.graph.nodes().forEach(function(node) { + 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]; + 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] - } - sigmaInstance.graph.nodes().forEach(function(node){ - if (node.degree < threshold){ - return - } - - sigmaInstance.graph.adjacentNodes(node.id).forEach(function(anode){ - if (params.end !== null && anode.label === params.end){ - return - } - - if (params.start !== null && anode.label === params.start){ - return - } - - var edges = sigmaInstance.graph.adjacentEdges(anode.id); - if ((edges.length > 1 || edges.length === 0) || (anode.folded.nodes.length > 0)){ - return - } - - var edge = edges[0]; - - if ((anode.type_user) - || (anode.type_computer) - || (anode.type_group && edge.label === 'AdminTo')){ - - 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); - } - }); - if (node.folded.nodes.length > 0){ - node.glyphs.push({ - 'position': 'bottom-left', - 'content': node.folded.nodes.length - }) - } - }) - - return [sigmaInstance, spotlightData] + var threshold = params.edge; + + if (threshold == 0){ + return [sigmaInstance, spotlightData]; + } + sigmaInstance.graph.nodes().forEach(function(node){ + if (node.degree < threshold){ + return; + } + + sigmaInstance.graph.adjacentNodes(node.id).forEach(function(anode){ + if (params.end !== null && anode.label === params.end){ + return; + } + + if (params.start !== null && anode.label === params.start){ + return; + } + + var edges = sigmaInstance.graph.adjacentEdges(anode.id); + if ((edges.length > 1 || edges.length === 0) || (anode.folded.nodes.length > 0)){ + return; + } + + var edge = edges[0]; + + if ((anode.type_user) + || (anode.type_computer) + || (anode.type_group && edge.label === 'AdminTo')){ + + 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); + } + }); + if (node.folded.nodes.length > 0){ + node.glyphs.push({ + 'position': 'bottom-left', + 'content': node.folded.nodes.length + }); + } + }); + + return [sigmaInstance, spotlightData]; } function collapseSiblingNodes(sigmaInstance, params, spotlightData){ - var threshold = params.sibling - - if (threshold === 0){ - 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 - } - - //Start by getting all the edges attached to this node - 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 - } - ) - - //Generate our string to compare other nodes to - //by sorting the parents and turning it into a string - var checkString = parents.sort().join(',') - var testString; - - //Loop back over nodes in the graph and look for any nodes - //with identical parents - sigmaInstance.graph.nodes().forEach(function(node2){ - testString = sigmaInstance.graph.adjacentEdges(node2.id).map( - function(e){ - return e.source; - } - ).sort().join(',') - if (testString === checkString){ - siblings.push(node2); - } - }); - - if (siblings.length >= threshold){ - //Generate a new ID for our grouped node - var nodeId = generateUniqueId(sigmaInstance, true); - - sigmaInstance.graph.addNode({ - id: nodeId, - x: node.x, - y: node.y, - degree: siblings.length, - label: "Grouped Computers", - type: 'Computer', - type_computer: true, - groupedNode: true, - glyphs: [{ - position: 'bottom-left', - content: siblings.length - }], - folded: { - nodes: [], - edges: [] - } - }); - - //Generate new edges for each parent going to our new node - parents.forEach(function(parent){ - var id = generateUniqueId(sigmaInstance, false); - - sigmaInstance.graph.addEdge({ - id: id, - source: parent, - target: nodeId, - 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.nodes.push(sibling) - spotlightData[sibling.id] = [sibling.label, nodeId, n.label, sibling.type, n.type]; - sigmaInstance.graph.dropNode(sibling.id) - }) - } - } - }) - return [sigmaInstance, spotlightData] + var threshold = params.sibling; + + if (threshold === 0){ + 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; + } + + //Start by getting all the edges attached to this node + 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; + } + ); + + //Generate our string to compare other nodes to + //by sorting the parents and turning it into a string + var checkString = parents.sort().join(','); + var testString; + + //Loop back over nodes in the graph and look for any nodes + //with identical parents + sigmaInstance.graph.nodes().forEach(function(node2){ + testString = sigmaInstance.graph.adjacentEdges(node2.id).map( + function(e){ + return e.source; + } + ).sort().join(','); + if (testString === checkString){ + siblings.push(node2); + } + }); + + if (siblings.length >= threshold){ + //Generate a new ID for our grouped node + var nodeId = generateUniqueId(sigmaInstance, true); + + sigmaInstance.graph.addNode({ + id: nodeId, + x: node.x, + y: node.y, + degree: siblings.length, + label: "Grouped Computers", + type: 'Computer', + type_computer: true, + groupedNode: true, + glyphs: [{ + position: 'bottom-left', + content: siblings.length + }], + folded: { + nodes: [], + edges: [] + } + }); + + //Generate new edges for each parent going to our new node + parents.forEach(function(parent){ + var id = generateUniqueId(sigmaInstance, false); + + sigmaInstance.graph.addEdge({ + id: id, + source: parent, + target: nodeId, + 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.nodes.push(sibling); + spotlightData[sibling.id] = [sibling.label, nodeId, n.label, sibling.type, n.type]; + sigmaInstance.graph.dropNode(sibling.id); + }); + } + } + }); + return [sigmaInstance, spotlightData]; } 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 + 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; } \ No newline at end of file From 9968132410763e7af2383c5bfd1ddc31563c6ea3 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Thu, 28 Sep 2017 00:06:47 -0400 Subject: [PATCH 02/15] Add zip ingestion --- package.json | 3 +- src/components/Menu/MenuContainer.jsx | 80 ++++++++++++++++++++------- 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index cdb26cf8f..3fdd70215 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "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" } } diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index 91e2bda1b..ba581f687 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -3,22 +3,24 @@ import MenuButton from './MenuButton'; import ProgressBarMenuButton from './ProgressBarMenuButton'; import { buildDomainProps, buildSessionProps, buildLocalAdminProps, buildGroupMembershipProps, buildACLProps, findObjectType} from 'utils'; import { If, Then, Else } from 'react-if'; -const { dialog, clipboard } = require('electron').remote; +const { dialog, clipboard, app } = require('electron').remote; var fs = require('fs'); var async = require('async'); var Papa = require('papaparse'); var awaitReadStream = require('await-stream-ready').read; +var unzip = require('unzipper'); +var fpath = require('path'); export default class MenuContainer extends Component { constructor(){ - super() + super(); this.state = { refreshHover: false, uploading: false, progress: 0, parser: null - } + }; emitter.on('cancelUpload', this.cancelUpload.bind(this)) } @@ -48,7 +50,7 @@ export default class MenuContainer extends Component { _importClick(){ var fname = dialog.showOpenDialog({ properties: ['openFile'] - }) + }); if (typeof fname !== 'undefined'){ emitter.emit('import',fname[0]) } @@ -63,27 +65,63 @@ export default class MenuContainer extends Component { } _uploadClick(){ - var input = jQuery(this.refs.fileInput) - var files = $.makeArray(input[0].files) - - async.eachSeries(files, function(file, callback){ - emitter.emit('showAlert', 'Processing file {}'.format(file.name)); - this.processFile(file.path, file, callback) - }.bind(this), - function done(){ - setTimeout(function(){ - this.setState({uploading: false}) - }.bind(this), 3000) - }.bind(this)) - - input.val('') + var input = jQuery(this.refs.fileInput); + var fileNames = []; + var files = []; + + $.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, callback); + if (file.delete){ + fs.unlink(file.path); + } + }.bind(this), + function done(){ + setTimeout(function(){ + this.setState({uploading: false}) + }.bind(this), 3000) + }.bind(this)) + + 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') } - processFile(filename, fileobject, callback){ + processFile(file, callback){ var count = 0; var dataset = []; @@ -95,7 +133,7 @@ export default class MenuContainer extends Component { progress: 0 }); console.time('IngestTime'); - Papa.parse(fs.createReadStream(filename), { + Papa.parse(fs.createReadStream(file), { header: true, skipEmptyLines: true, beforeFirstChunk: function(chunk){ @@ -169,7 +207,7 @@ export default class MenuContainer extends Component { processed = buildDomainProps(currentChunk); - await session.run(query, {props: props}); + await session.run(query, {props: processed}); }else if (filetype === 'acl'){ processed = buildACLProps(currentChunk); From 3690a5ad86c83d965abcc60177d03144666b1eb6 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Thu, 28 Sep 2017 00:27:13 -0400 Subject: [PATCH 03/15] Convert ints properly --- src/components/Menu/MenuContainer.jsx | 11 ++++++----- src/js/utils.js | 11 +---------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index ba581f687..239ab12b0 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -52,7 +52,7 @@ export default class MenuContainer extends Component { properties: ['openFile'] }); if (typeof fname !== 'undefined'){ - emitter.emit('import',fname[0]) + emitter.emit('import',fname[0]); } } @@ -67,7 +67,6 @@ export default class MenuContainer extends Component { _uploadClick(){ var input = jQuery(this.refs.fileInput); var fileNames = []; - var files = []; $.each(input[0].files, function(index, file){ fileNames.push({path:file.path, name:file.name}); @@ -127,12 +126,13 @@ export default class MenuContainer extends Component { var filetype; var abort = false; - + this.setState({ uploading: true, progress: 0 }); console.time('IngestTime'); + Papa.parse(fs.createReadStream(file), { header: true, skipEmptyLines: true, @@ -141,6 +141,7 @@ export default class MenuContainer extends Component { if (filetype === 'unknown'){ abort = true; } + }, chunk: function(results, parser){ if (abort){ @@ -215,11 +216,11 @@ export default class MenuContainer extends Component { await session.run(processed[key].statement, {props: processed[key].props}); } }else if (filetype === 'userprops'){ - query = 'UNWIND {props} AS prop MERGE (user:User {name: upper(prop.AccountName)}) SET user.Enabled = toBoolean(prop.Enabled),user.PwdLastSet = prop.PwdLastSet,user.LastLogon = prop.LastLogon,user.Sid = prop.Sid,user.SidHistory = prop.SidHistory,user.HasSPN = toBoolean(prop.HasSPN),user.ServicePrincipalNames = split(prop.ServicePrincipalNames, "|")'; + 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.ServicePrincipalNames = split(prop.ServicePrincipalNames, "|")'; 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=prop.PwdLastSet,comp.LastLogon=prop.LastLogon,comp.OperatingSystem=prop.OperatingSystem,comp.Sid=prop.Sid'; + 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'; await session.run(query, {props:currentChunk}); } diff --git a/src/js/utils.js b/src/js/utils.js index decbe8664..009533f76 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -107,22 +107,13 @@ export function findObjectType(header){ 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.include('Enabled') && header.include('PwdLastSet') && header.include('LastLogon') && header.include('OperatingSystem') && header.include('Sid')){ + }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 buildUserObjectProps(rows){ - var data = []; - $.each(rows, function(index, row){ - data.push({ account:row.AccountName.toUpperCase(), enabled: row.Enabled, pwdlastset:row.PwdLastSet, lastlogon: row.LastLogon, sid: row.Sid, sidhistory: row.SidHistory, hasspn: row.HasSPN, spn: row.ServicePrincipalNames}); - }); - - return data; -} - export function buildGroupMembershipProps(rows) { var users = []; var groups = []; From c29d3c168a31beb067b467ca16614a7d185a1fdf Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Thu, 28 Sep 2017 12:40:26 -0400 Subject: [PATCH 04/15] Fix queries with start/end shortestpath --- .../SearchContainer/Tabs/ComputerNodeData.jsx | 647 +++++++-------- .../SearchContainer/Tabs/DomainNodeData.jsx | 464 +++++------ .../SearchContainer/Tabs/GroupNodeData.jsx | 748 +++++++++--------- .../SearchContainer/Tabs/UserNodeData.jsx | 683 ++++++++-------- 4 files changed, 1297 insertions(+), 1245 deletions(-) diff --git a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx index d58b5d3a6..505a17257 100644 --- a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx +++ b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx @@ -3,342 +3,355 @@ import NodeALink from './NodeALink' import PropTypes from 'prop-types' export default class ComputerNodeData extends Component { - constructor(){ - super(); + constructor(){ + super(); - this.state = { - label: "", - os: "None", - unconstrained: "None", - explicitAdmins: -1, - unrolledAdmins: -1, - firstDegreeGroupMembership: -1, - unrolledGroupMembership: -1, - firstDegreeLocalAdmin: -1, - groupDelegatedLocalAdmin: -1, - derivativeLocalAdmin: -1, - sessions: -1, - firstdegreeControl: -1, - groupDelegatedControl: -1, - transitiveControl: -1, - derivativeLocalAdmins: -1, - driversessions: [] - } + this.state = { + label: "", + os: "None", + unconstrained: "None", + explicitAdmins: -1, + unrolledAdmins: -1, + firstDegreeGroupMembership: -1, + unrolledGroupMembership: -1, + firstDegreeLocalAdmin: -1, + groupDelegatedLocalAdmin: -1, + derivativeLocalAdmin: -1, + sessions: -1, + firstdegreeControl: -1, + groupDelegatedControl: -1, + transitiveControl: -1, + derivativeLocalAdmins: -1, + driversessions: [] + } - emitter.on('computerNodeClicked', this.getNodeData.bind(this)); - } + emitter.on('computerNodeClicked', this.getNodeData.bind(this)); + } - 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, - unrolledGroupMembership: -1, - sessions: -1, - firstDegreeLocalAdmin: -1, - groupDelegatedLocalAdmin: -1, - derivativeLocalAdmin: -1, - firstdegreeControl: -1, - groupDelegatedControl: -1, - transitiveControl: -1, - derivativeLocalAdmins: -1 - }) + 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, + unrolledGroupMembership: -1, + sessions: -1, + firstDegreeLocalAdmin: -1, + groupDelegatedLocalAdmin: -1, + derivativeLocalAdmin: -1, + firstdegreeControl: -1, + groupDelegatedControl: -1, + transitiveControl: -1, + derivativeLocalAdmins: -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 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() - 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)) + 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)) - 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)) + 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)) - 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)) + 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)) - 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)) + 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)) - s5.run("MATCH (n:Computer {name:{name}}), (m:Computer), 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)) + 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)) - 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)) + 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)) - 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)) + 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)) - 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)) + 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)) - s9.run("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)) + 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)) - 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)) + 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)) - 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)) + 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)) - 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)) + 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)) - 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}) - .then(function(result){ - 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]}) - } + 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({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13]}) + } - render() { - return ( -
-
-

Node Info

-
- Name -
-
- {this.state.label} -
-
- OS -
-
- {this.state.os} -
-
- Allows Unconstrained Delegation -
-
- {this.state.unconstrained} -
-
- Sessions -
-
- (n:User) WITH n,r,m WHERE NOT n.name ENDS WITH '$' RETURN n,r,m", {name: this.state.label}) - }.bind(this)} /> -
-
-

Local Admins

-
- Explicit Admins -
-
- (m:Computer {name:{name}}) RETURN n,r,m",{name: this.state.label}) - }.bind(this)} /> -
-
- Unrolled Admins -
-
- (g:Group)-[r2:AdminTo]->(c:Computer {name:{name}}) RETURN p", - {name: this.state.label}, - this.state.label) - }.bind(this)} /> -
-
- Derivative Local Admins -
-
- (m:Computer {name:{name}})) RETURN p",{name: this.state.label}, this.state.label) - }.bind(this)} /> -
-
-

Group Memberships

-
- First Degree Group Membership -
-
- (m) RETURN n,r,m",{name: this.state.label}, this.state.label) - }.bind(this)} /> -
-
- Unrolled Group Membership -
-
- (m:Group) RETURN p",{name: this.state.label}, this.state.label) - }.bind(this)} /> -
-
- Foreign Group Membership -
-
- (g:Group) WHERE NOT g.domain = c.domain RETURN p",{name: this.state.label}, this.state.label) - }.bind(this)} /> -
-
-

Local Admin Rights

-
- First Degree Local Admin -
-
- (m) RETURN p",{name: this.state.label}, this.state.label) - }.bind(this)} /> -
-
- Group Delegated Local Admin -
-
- (g:Group)-[r2:AdminTo]->(m:Computer) RETURN p",{name: this.state.label}, this.state.label) - }.bind(this)} /> -
-
- Derivative Local Admin -
-
- (c:Computer)) RETURN p",{name: this.state.label}, this.state.label) - }.bind(this)} /> -
-
-

Outbound Object Control

-
- First Degree Object Control -
-
- (n) RETURN p", {name:this.state.label}) - }.bind(this)} /> -
-
- Group Delegated Object Control -
-
- (g:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
- Transitive Object Control -
-
- (n)) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
-
- ); - } + render() { + return ( +
+
+

Node Info

+
+ Name +
+
+ {this.state.label} +
+
+ OS +
+
+ {this.state.os} +
+
+ Allows Unconstrained Delegation +
+
+ {this.state.unconstrained} +
+
+ Sessions +
+
+ (n:User) WITH n,r,m WHERE NOT n.name ENDS WITH '$' RETURN n,r,m", {name: this.state.label}) + }.bind(this)} + /> +
+
+

Local Admins

+
+ Explicit Admins +
+
+ (m:Computer {name:{name}}) RETURN n,r,m",{name: this.state.label}) + }.bind(this)} + /> +
+
+ Unrolled Admins +
+
+ (g:Group)-[r2:AdminTo]->(c:Computer {name:{name}}) RETURN p", + {name: this.state.label}, + this.state.label) + }.bind(this)} + /> +
+
+ Derivative Local Admins +
+
+ (m:Computer {name:{name}})) RETURN p",{name: this.state.label}, this.state.label) + }.bind(this)} + /> +
+
+

Group Memberships

+
+ First Degree Group Membership +
+
+ (m) RETURN n,r,m",{name: this.state.label}, this.state.label) + }.bind(this)} + /> +
+
+ Unrolled Group Membership +
+
+ (m:Group) RETURN p",{name: this.state.label}, this.state.label) + }.bind(this)} + /> +
+
+ Foreign Group Membership +
+
+ (g:Group) WHERE NOT g.domain = c.domain RETURN p",{name: this.state.label}, this.state.label) + }.bind(this)} + /> +
+
+

Local Admin Rights

+
+ First Degree Local Admin +
+
+ (m) RETURN p",{name: this.state.label}, this.state.label) + }.bind(this)} + /> +
+
+ Group Delegated Local Admin +
+
+ (g:Group)-[r2:AdminTo]->(m:Computer) RETURN p",{name: this.state.label}, this.state.label) + }.bind(this)} + /> +
+
+ Derivative Local Admin +
+
+ (c)) RETURN p",{name: this.state.label}, this.state.label); + }.bind(this)} + /> +
+
+

Outbound Object Control

+
+ First Degree Object Control +
+
+ (n) RETURN p", {name:this.state.label}) + }.bind(this)} + /> +
+
+ Group Delegated Object Control +
+
+ (g:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label} + ,this.state.label) + }.bind(this)} + /> +
+
+ Transitive Object Control +
+
+ (n)) RETURN p", {name:this.state.label} + ,this.state.label) + }.bind(this)} + /> +
+
+
+ ); + } } ComputerNodeData.propTypes= { - visible : React.PropTypes.bool.isRequired + visible : React.PropTypes.bool.isRequired } diff --git a/src/components/SearchContainer/Tabs/DomainNodeData.jsx b/src/components/SearchContainer/Tabs/DomainNodeData.jsx index 8c28cb91a..4a9c386e4 100644 --- a/src/components/SearchContainer/Tabs/DomainNodeData.jsx +++ b/src/components/SearchContainer/Tabs/DomainNodeData.jsx @@ -1,233 +1,243 @@ 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(){ - super(); - - this.state = { - label: "", - users: -1, - groups: -1, - computers: -1, - foreignGroups: -1, - foreignUsers: -1, - firstDegreeOutboundTrusts: -1, - effectiveOutboundTrusts: -1, - firstDegreeInboundTrusts: -1, - effectiveInboundTrusts: -1, - driversessions: [] - } - - emitter.on('domainNodeClicked', this.getNodeData.bind(this)); - } - - getNodeData(payload){ - $.each(this.state.driversessions, function(index, record){ - record.close(); - }) - this.setState({ - label: payload, - users: -1, - groups: -1, - computers: -1, - foreignGroups: -1, - foreignUsers: -1, - firstDegreeOutboundTrusts: -1, - 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() - - 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)) - - 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)) - - 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)) - - 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)) - - 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)) - - 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)) - - 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)) - - s8.run("MATCH p=shortestPath((a:Domain {name:{name}})<-[r:TrustedBy*1..]-(b:Domain)) RETURN count(b)", {name:payload}) - .then(function(result){ - 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}) - .then(function(result){ - 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]}) - } - - render() { - return ( -
-
-
- Node -
-
- {this.state.label} -
-
-
- Users -
-
- -
-
- Groups -
-
- -
-
- Computers -
-
- -
-
-
- Foreign Users -
-
- -
-
- Foreign Groups -
-
- -
-
- Foreign Admins -
-
- -
-
-
- Inbound Trusts -
-
- -
-
- Effective Inbound Trusts -
-
- -
-
- Outbound Trusts -
-
- (b:Domain) RETURN a,r,b", {domain: this.state.label}) - }.bind(this)} /> -
-
- Effective Outbound Trusts -
-
- (b:Domain)) RETURN p", {domain: this.state.label}) - }.bind(this)} /> -
-
-
- ); - } + constructor(){ + super(); + + this.state = { + label: "", + users: -1, + groups: -1, + computers: -1, + foreignGroups: -1, + foreignUsers: -1, + firstDegreeOutboundTrusts: -1, + effectiveOutboundTrusts: -1, + firstDegreeInboundTrusts: -1, + effectiveInboundTrusts: -1, + driversessions: [] + }; + + emitter.on('domainNodeClicked', this.getNodeData.bind(this)); + } + + getNodeData(payload){ + $.each(this.state.driversessions, function(index, record){ + record.close(); + }); + this.setState({ + label: payload, + users: -1, + groups: -1, + computers: -1, + foreignGroups: -1, + foreignUsers: -1, + firstDegreeOutboundTrusts: -1, + 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(); + + 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)); + + 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)); + + 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)); + + 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)); + + 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)); + + 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)); + + 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)); + + 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)); + + 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({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9]}); + } + + render() { + return ( +
+
+
+ Node +
+
+ {this.state.label} +
+
+
+ Users +
+
+ +
+
+ Groups +
+
+ +
+
+ Computers +
+
+ +
+
+
+ Foreign Users +
+
+ +
+
+ Foreign Groups +
+
+ +
+
+ Foreign Admins +
+
+ +
+
+
+ Inbound Trusts +
+
+ +
+
+ Effective Inbound Trusts +
+
+ +
+
+ Outbound Trusts +
+
+ (b:Domain) RETURN a,r,b", {domain: this.state.label}); + }.bind(this)} + /> +
+
+ Effective Outbound Trusts +
+
+ (b:Domain)) RETURN p", {domain: this.state.label}); + }.bind(this)} + /> +
+
+
+ ); + } } DomainNodeData.propTypes = { - visible : React.PropTypes.bool.isRequired -} \ No newline at end of file + visible : React.PropTypes.bool.isRequired +}; \ No newline at end of file diff --git a/src/components/SearchContainer/Tabs/GroupNodeData.jsx b/src/components/SearchContainer/Tabs/GroupNodeData.jsx index d3d2c72c7..96168c493 100644 --- a/src/components/SearchContainer/Tabs/GroupNodeData.jsx +++ b/src/components/SearchContainer/Tabs/GroupNodeData.jsx @@ -1,394 +1,410 @@ 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(){ - super(); + constructor(){ + super(); - this.state = { - label: "", - directMembers: -1, - unrolledMembers: -1, - directAdminTo: -1, - derivativeAdminTo: -1, - unrolledMemberOf: -1, - sessions: -1, - foreignGroupMembership: -1, - foreignGroupMembers: -1, - firstDegreeGroupMembership: -1, - groupDelegatedAdmin: -1, - firstdegreeControl: -1, - groupDelegatedControl: -1, - transitiveControl: -1, - firstDegreeControllers: -1, - unrolledControllers: -1, - transitiveControllers: -1, - driversessions: [] - } + this.state = { + label: "", + directMembers: -1, + unrolledMembers: -1, + directAdminTo: -1, + derivativeAdminTo: -1, + unrolledMemberOf: -1, + sessions: -1, + foreignGroupMembership: -1, + foreignGroupMembers: -1, + firstDegreeGroupMembership: -1, + groupDelegatedAdmin: -1, + firstdegreeControl: -1, + groupDelegatedControl: -1, + transitiveControl: -1, + firstDegreeControllers: -1, + unrolledControllers: -1, + transitiveControllers: -1, + driversessions: [] + }; - emitter.on('groupNodeClicked', this.getNodeData.bind(this)); - } + emitter.on('groupNodeClicked', this.getNodeData.bind(this)); + } - getNodeData(payload){ - $.each(this.state.driversessions, function(index, record){ - record.close(); - }) + getNodeData(payload){ + $.each(this.state.driversessions, function(index, record){ + record.close(); + }); - this.setState({ - label: payload, - directMembers: -1, - unrolledMembers: -1, - directAdminTo: -1, - derivativeAdminTo: -1, - unrolledMemberOf: -1, - sessions: -1, - foreignGroupMembership: -1, - foreignGroupMembers: -1, - firstDegreeGroupMembership: -1, - groupDelegatedAdmin: -1, - firstdegreeControl: -1, - groupDelegatedControl: -1, - transitiveControl: -1, - firstDegreeControllers: -1, - unrolledControllers: -1, - transitiveControllers: -1 - }) + this.setState({ + label: payload, + directMembers: -1, + unrolledMembers: -1, + directAdminTo: -1, + derivativeAdminTo: -1, + unrolledMemberOf: -1, + sessions: -1, + foreignGroupMembership: -1, + foreignGroupMembers: -1, + firstDegreeGroupMembership: -1, + groupDelegatedAdmin: -1, + firstdegreeControl: -1, + groupDelegatedControl: -1, + transitiveControl: -1, + 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)) + 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)); - 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)) + 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)); - 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)) + 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)); - s4.run("MATCH p = shortestPath((g:Group {name:{name}})-[r:MemberOf|AdminTo|HasSession*1..]->(c:Computer)) RETURN COUNT(DISTINCT(c))", {name:payload}) - .then(function(result){ - this.setState({'derivativeAdminTo':result.records[0]._fields[0].low}) - s4.close() - }.bind(this)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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}) - .then(function(result){ - this.setState({'transitiveControl':result.records[0]._fields[0].low}) - s13.close() - }.bind(this)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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}) - .then(function(result){ - 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]}) - } + 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({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15,s16]}); + } - render() { - var domain = '@' + this.state.label.split('@') - return ( -
-
-

Node Info

-
- Name -
-
- {this.state.label} -
-
- Sessions -
-
- (u:User)-[r2:MemberOf*1..]->(g:Group {name: {name}}) RETURN p", {name: this.state.label}, - "",this.state.label) - }.bind(this)} /> -
-
-

Group Members

-
- Direct Members -
-
- (m:Group {name:{name}}) RETURN n,r,m", {name: this.state.label}) - }.bind(this)} /> -
-
- Unrolled Members -
-
- (g:Group {name:{name}}) RETURN p", {name: this.state.label}, - this.state.label) - }.bind(this)} /> -
-
- Foreign Members -
-
- (g:Group {name:{name}}) WHERE NOT g.domain = n.domain RETURN p", {name: this.state.label}, - this.state.label) - }.bind(this)} /> -
-
-

Group Membership

-
- First Degree Group Membership -
-
- (g2:Group) RETURN p", {name: this.state.label}, - this.state.label) - }.bind(this)} /> -
-
- Unrolled Member Of -
-
- (g2:Group) RETURN p", {name: this.state.label}, - this.state.label) - }.bind(this)} /> -
-
- Foreign Group Membership -
-
- (n) RETURN m,r,n", {name: this.state.label, domain: domain}) - }.bind(this)} /> -
-
-

Local Admin Rights

-
- First Degree Local Admin -
-
- (c:Computer) RETURN p", {name: this.state.label}, - this.state.label) - }.bind(this)} /> -
-
- Group Delegated Local Admin Rights -
-
- (g2:Group)-[r2:AdminTo]->(c:Computer) RETURN p", {name: this.state.label}, - this.state.label) - }.bind(this)} /> -
-
- Derivative Local Admin Rights -
-
- (c:Computer)) RETURN p", {name: this.state.label}, - this.state.label) - }.bind(this)} /> -
-
-

Outbound Object Control

-
- First Degree Object Control -
-
- (n) RETURN p", {name:this.state.label}) - }.bind(this)} /> -
-
- Group Delegated Object Control -
-
- (g2:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
- Transitive Object Control -
-
- (n)) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
-

Inbound Object Control

-
- Explicit Object Controllers -
-
- (g:Group {name: {name}}) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
- Unrolled Object Controllers -
-
- (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)} /> -
-
- Transitive Object Controllers -
-
- (g:Group {name: {name}})) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
-
- ); - } + render() { + var domain = '@' + this.state.label.split('@'); + return ( +
+
+

Node Info

+
+ Name +
+
+ {this.state.label} +
+
+ Sessions +
+
+ (u:User)-[r2:MemberOf*1..]->(g:Group {name: {name}}) RETURN p", {name: this.state.label}, + "",this.state.label); + }.bind(this)} + /> +
+
+

Group Members

+
+ Direct Members +
+
+ (m:Group {name:{name}}) RETURN n,r,m", {name: this.state.label}); + }.bind(this)} + /> +
+
+ Unrolled Members +
+
+ (g:Group {name:{name}}) RETURN p", {name: this.state.label}, + this.state.label); + }.bind(this)} + /> +
+
+ Foreign Members +
+
+ (g:Group {name:{name}}) WHERE NOT g.domain = n.domain RETURN p", {name: this.state.label}, + this.state.label); + }.bind(this)} + /> +
+
+

Group Membership

+
+ First Degree Group Membership +
+
+ (g2:Group) RETURN p", {name: this.state.label}, + this.state.label); + }.bind(this)} + /> +
+
+ Unrolled Member Of +
+
+ (g2:Group) RETURN p", {name: this.state.label}, + this.state.label); + }.bind(this)} + /> +
+
+ Foreign Group Membership +
+
+ (n) RETURN m,r,n", {name: this.state.label, domain: domain}); + }.bind(this)} + /> +
+
+

Local Admin Rights

+
+ First Degree Local Admin +
+
+ (c:Computer) RETURN p", {name: this.state.label}, + this.state.label); + }.bind(this)} + /> +
+
+ Group Delegated Local Admin Rights +
+
+ (g2:Group)-[r2:AdminTo]->(c:Computer) RETURN p", {name: this.state.label}, + this.state.label); + }.bind(this)} + /> +
+
+ Derivative Local Admin Rights +
+
+ (c)) RETURN p", {name: this.state.label}, + this.state.label); + }.bind(this)} + /> +
+
+

Outbound Object Control

+
+ First Degree Object Control +
+
+ (n) RETURN p", {name:this.state.label}); + }.bind(this)} + /> +
+
+ Group Delegated Object Control +
+
+ (g2:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label} + ,this.state.label); + }.bind(this)} + /> +
+
+ Transitive Object Control +
+
+ (n)) RETURN p", {name:this.state.label} + ,this.state.label); + }.bind(this)} + /> +
+
+

Inbound Object Control

+
+ Explicit Object Controllers +
+
+ (g:Group {name: {name}}) RETURN p", {name:this.state.label} + ,this.state.label); + }.bind(this)} + /> +
+
+ Unrolled Object Controllers +
+
+ (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)} + /> +
+
+ Transitive Object Controllers +
+
+ (g:Group {name: {name}})) RETURN p", {name:this.state.label} + ,this.state.label); + }.bind(this)} + /> +
+
+
+ ); + } } GroupNodeData.propTypes = { - visible : React.PropTypes.bool.isRequired -} \ No newline at end of file + visible : React.PropTypes.bool.isRequired +}; \ No newline at end of file diff --git a/src/components/SearchContainer/Tabs/UserNodeData.jsx b/src/components/SearchContainer/Tabs/UserNodeData.jsx index e4e6e0b52..087ec273a 100644 --- a/src/components/SearchContainer/Tabs/UserNodeData.jsx +++ b/src/components/SearchContainer/Tabs/UserNodeData.jsx @@ -1,363 +1,376 @@ 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 UserNodeData extends Component { - constructor(){ - super(); + constructor(){ + super(); - this.state = { - label: "", - samAccountName: "None", - displayName: "None", - pwdLastChanged: "None", - firstDegreeGroupMembership: -1, - unrolledGroupMembership: -1, - foreignGroupMembership: -1, - firstDegreeLocalAdmin: -1, - groupDelegatedLocalAdmin: -1, - derivativeLocalAdmin: -1, - sessions: -1, - firstdegreeControllers: -1, - unrolledControllers: -1, - transitiveControllers: -1, - firstdegreeControl: -1, - unrolledControl: -1, - transitiveControl: -1, - driversessions : [] - } + this.state = { + label: "", + samAccountName: "None", + displayName: "None", + pwdLastChanged: "None", + firstDegreeGroupMembership: -1, + unrolledGroupMembership: -1, + foreignGroupMembership: -1, + firstDegreeLocalAdmin: -1, + groupDelegatedLocalAdmin: -1, + derivativeLocalAdmin: -1, + sessions: -1, + firstdegreeControllers: -1, + unrolledControllers: -1, + transitiveControllers: -1, + firstdegreeControl: -1, + unrolledControl: -1, + transitiveControl: -1, + driversessions : [] + }; - emitter.on('userNodeClicked', this.getNodeData.bind(this)); - } + emitter.on('userNodeClicked', this.getNodeData.bind(this)); + } - getNodeData(payload){ - $.each(this.state.driversessions,function(index, record){ - record.close(); - }) + 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, - firstDegreeLocalAdmin: -1, - groupDelegatedLocalAdmin: -1, - derivativeLocalAdmin: -1, - sessions: -1, - firstdegreeControllers: -1, - unrolledControllers: -1, - transitiveControllers: -1, - firstdegreeControl: -1, - unrolledControl: -1, - transitiveControl: -1 - }) + this.setState({ + label: payload, + samAccountName: "None", + displayName: "None", + pwdLastChanged: "None", + firstDegreeGroupMembership: -1, + unrolledGroupMembership: -1, + foreignGroupMembership: -1, + firstDegreeLocalAdmin: -1, + groupDelegatedLocalAdmin: -1, + derivativeLocalAdmin: -1, + sessions: -1, + firstdegreeControllers: -1, + unrolledControllers: -1, + transitiveControllers: -1, + firstdegreeControl: -1, + unrolledControl: -1, + transitiveControl: -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 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(); - 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)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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)) + 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)); - s6.run("MATCH p = shortestPath((n:User {name:{name}})-[r:HasSession|AdminTo|MemberOf*1..]->(c:Computer)) RETURN COUNT(c)", {name:payload}) - .then(function(result){ - this.setState({'derivativeLocalAdmin':result.records[0]._fields[0].low}) - s6.close() - }.bind(this)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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}) - .then(function(result){ - this.setState({'transitiveControllers':result.records[0]._fields[0].low}) - s10.close() - }.bind(this)) + 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)); - 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)) + 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)); - 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)) + 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)); - 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}) - .then(function(result){ - 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]}) - } + 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({'driversessions': [s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13]}); + } - render() { - var domain = '@' + this.state.label.split('@').last() - return ( -
-
+ render() { + var domain = '@' + this.state.label.split('@').last(); + return ( +
+

Node Info

-
- Name -
-
- {this.state.label} -
-
- SAMAccountName -
-
- {this.state.samAccountName} -
-
- Display Name -
-
- {this.state.displayName} -
-
- Password Last Changed -
-
- {this.state.pwdLastChanged} -
-
- Sessions -
-
- (m:User {name:{name}}) RETURN n,r,m", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
-

Group Membership

-
- First Degree Group Memberships -
-
- (g:Group) RETURN p", {name:this.state.label} - ) - }.bind(this)} /> -
-
- Unrolled Group Memberships -
-
- (g:Group) RETURN p", {name:this.state.label}, - this.state.label) - }.bind(this)} /> -
-
- Foreign Group Membership -
-
- (n) RETURN p", {name: this.state.label, domain: domain}) - }.bind(this)} /> -
-
-

- Local Admin Rights -

-
- First Degree Local Admin -
-
- (c:Computer) RETURN p", {name:this.state.label}) - }.bind(this)} /> -
-
- Group Delegated Local Admin Rights -
-
- (g:Group)-[r2:AdminTo]->(c:Computer) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
- Derivative Local Admin Rights -
-
- (c:Computer)) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
-

- Outbound Object Control -

-
- First Degree Object Control -
-
- (n) RETURN p", {name:this.state.label}) - }.bind(this)} /> -
-
- Group Delegated Object Control -
-
- (g:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
- Transitive Object Control -
-
- (n)) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
-

Inbound Object Control

-
- Explicit Object Controllers -
-
- (u1:User {name: {name}}) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
- Unrolled Object Controllers -
-
- (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)} /> -
-
- Transitive Object Controllers -
-
- (u1:User {name: {name}})) RETURN p", {name:this.state.label} - ,this.state.label) - }.bind(this)} /> -
-
-
- ); - } +
+ Name +
+
+ {this.state.label} +
+
+ SAMAccountName +
+
+ {this.state.samAccountName} +
+
+ Display Name +
+
+ {this.state.displayName} +
+
+ Password Last Changed +
+
+ {this.state.pwdLastChanged} +
+
+ Sessions +
+
+ (m:User {name:{name}}) RETURN n,r,m", {name:this.state.label} + ,this.state.label); + }.bind(this)} + /> +
+
+

Group Membership

+
+ First Degree Group Memberships +
+
+ (g:Group) RETURN p", {name:this.state.label} + ); + }.bind(this)} + /> +
+
+ Unrolled Group Memberships +
+
+ (g:Group) RETURN p", {name:this.state.label}, + this.state.label); + }.bind(this)} + /> +
+
+ Foreign Group Membership +
+
+ (n) RETURN p", {name: this.state.label, domain: domain}); + }.bind(this)} + /> +
+
+

+ Local Admin Rights +

+
+ First Degree Local Admin +
+
+ (c:Computer) RETURN p", {name:this.state.label}); + }.bind(this)} + /> +
+
+ Group Delegated Local Admin Rights +
+
+ (g:Group)-[r2:AdminTo]->(c:Computer) RETURN p", {name:this.state.label} + ,this.state.label); + }.bind(this)} + /> +
+
+ Derivative Local Admin Rights +
+
+ (c)) RETURN p", {name:this.state.label} + ,this.state.label); + }.bind(this)} + /> +
+
+

+ Outbound Object Control +

+
+ First Degree Object Control +
+
+ (n) RETURN p", {name:this.state.label}); + }.bind(this)} + /> +
+
+ Group Delegated Object Control +
+
+ (g:Group)-[r2:AddMembers|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner]->(n) RETURN p", {name:this.state.label} + ,this.state.label); + }.bind(this)} + /> +
+
+ Transitive Object Control +
+
+ (n)) RETURN p", {name:this.state.label} + ,this.state.label); + }.bind(this)} + /> +
+
+

Inbound Object Control

+
+ Explicit Object Controllers +
+
+ (u1:User {name: {name}}) RETURN p", {name:this.state.label} + ,this.state.label); + }.bind(this)} + /> +
+
+ Unrolled Object Controllers +
+
+ (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)} + /> +
+
+ Transitive Object Controllers +
+
+ (u1:User {name: {name}})) RETURN p", {name:this.state.label} + ,this.state.label); + }.bind(this)} + /> +
+
+
+ ); + } } UserNodeData.propTypes = { - visible : React.PropTypes.bool.isRequired -} \ No newline at end of file + visible : React.PropTypes.bool.isRequired +}; \ No newline at end of file From 66e86f82c16d75653df5bfd37f3e42526898c00a Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Thu, 28 Sep 2017 18:38:54 -0400 Subject: [PATCH 05/15] Add more node properties --- src/components/Float/Alert.jsx | 74 ++-- src/components/Graph.jsx | 366 +++++++++--------- src/components/Menu/MenuContainer.jsx | 4 +- .../SearchContainer/Tabs/ComputerNodeData.jsx | 151 ++++---- .../SearchContainer/Tabs/UserNodeData.jsx | 28 +- 5 files changed, 323 insertions(+), 300 deletions(-) diff --git a/src/components/Float/Alert.jsx b/src/components/Float/Alert.jsx index 69daeef88..a2856ff41 100644 --- a/src/components/Float/Alert.jsx +++ b/src/components/Float/Alert.jsx @@ -2,46 +2,46 @@ import React, { Component } from 'react'; import { Alert } from 'react-bootstrap'; export default class GenericAlert extends Component { - constructor(){ - super() - this.state = { - visible: false, - text: "No data returned from query", - timeout: null - } + constructor(){ + 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(){ - this.setState({visible: false}); - } + _dismiss(){ + this.setState({visible: false}); + } - _show(val){ - clearTimeout(this.state.timeout) - var t = setTimeout(function(){ - this._dismiss() - }.bind(this), 2500) - - this.setState({ - visible: true, - text: val, - timeout: t - }) + _show(val){ + clearTimeout(this.state.timeout); + var t = setTimeout(function(){ + this._dismiss(); + }.bind(this), 2500); - } + this.setState({ + visible: true, + text: val, + timeout: t + }); + + } - render() { - if (this.state.visible){ - return ( - - {this.state.text} - - ) - }else{ - return null - } - - } + render() { + if (this.state.visible){ + return ( + + {this.state.text} + + ); + }else{ + return null; + } + + } } diff --git a/src/components/Graph.jsx b/src/components/Graph.jsx index 33e181632..60c9ba9f6 100644 --- a/src/components/Graph.jsx +++ b/src/components/Graph.jsx @@ -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}); @@ -26,7 +26,7 @@ export default class GraphContainer extends Component { url: 'src/components/tooltip.html', type: 'GET', success: function(response){ - this.setState({template: response}) + this.setState({template: response}); }.bind(this) }); @@ -39,50 +39,50 @@ 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 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() { @@ -90,18 +90,18 @@ export default class GraphContainer extends Component { 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)) + 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() { @@ -115,7 +115,7 @@ export default class GraphContainer extends Component { } relayout(){ - sigma.layouts.stopForceLink() + sigma.layouts.stopForceLink(); if (appStore.dagre){ sigma.layouts.dagre.start(this.state.sigmaInstance); }else{ @@ -125,7 +125,7 @@ export default class GraphContainer extends Component { export(payload){ if (payload === 'image'){ - var size = $('#graph').outerWidth() + var size = $('#graph').outerWidth(); sigma.plugins.image(this.state.sigmaInstance, this.state.sigmaInstance.renderers[0], { @@ -152,11 +152,11 @@ export default class GraphContainer extends Component { 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({ @@ -165,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; @@ -194,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'); } @@ -207,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'); @@ -256,9 +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() + this.state.design.deprecate(); + sigmaInstance.refresh(); + this.state.design.apply(); } resetZoom(){ @@ -269,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, { @@ -280,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, @@ -322,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'], @@ -352,115 +352,115 @@ 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, @@ -472,28 +472,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) { @@ -511,60 +511,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){ @@ -573,7 +573,7 @@ export default class GraphContainer extends Component { } if (typeof props === 'undefined'){ - props = {} + props = {}; } this.doQueryNative({ statement: statement, @@ -581,26 +581,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}); } } @@ -611,7 +611,7 @@ export default class GraphContainer extends Component { { container: 'graph' } - ) + ); sigmaInstance.settings( { @@ -630,7 +630,7 @@ export default class GraphContainer extends Component { zoomingRatio: 1.4, scalingMode: 'inside' } - ) + ); //Bind sigma events sigmaInstance.renderers[0].bind('render', function(e) { @@ -643,21 +643,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) { @@ -669,13 +669,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; @@ -683,28 +683,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, @@ -719,7 +719,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; @@ -754,12 +754,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', @@ -775,22 +775,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, @@ -800,19 +800,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){ @@ -827,7 +827,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); diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index 239ab12b0..6e8398686 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -216,11 +216,11 @@ export default class MenuContainer extends Component { await session.run(processed[key].statement, {props: processed[key].props}); } }else if (filetype === 'userprops'){ - 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.ServicePrincipalNames = split(prop.ServicePrincipalNames, "|")'; + 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 = split(prop.ServicePrincipalNames, "|")'; 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'; + 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}); } diff --git a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx index 505a17257..23100d330 100644 --- a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx +++ b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx @@ -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(){ @@ -23,7 +23,7 @@ export default class ComputerNodeData extends Component { transitiveControl: -1, derivativeLocalAdmins: -1, driversessions: [] - } + }; emitter.on('computerNodeClicked', this.getNodeData.bind(this)); } @@ -31,7 +31,7 @@ export default class ComputerNodeData extends Component { getNodeData(payload){ $.each(this.state.driversessions, function(index, record){ record.close(); - }) + }); this.setState({ label: payload, os: "None", @@ -48,101 +48,114 @@ export default class ComputerNodeData extends Component { groupDelegatedControl: -1, transitiveControl: -1, derivativeLocalAdmins: -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 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(results){ + var props = results.records[0]._fields[0].properties; + if (props.hasOwnProperty('UnconstrainedDelegation')){ + this.setState({'unconstrained': props.UnconstrainedDelegation.toString().toTitleCase()}); + } + if (props.hasOwnProperty('OperatingSystem')){ + this.setState({'os': props.OperatingSystem}); + } + 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)) + }.bind(this)); 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 (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 (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]}); } render() { @@ -177,7 +190,7 @@ 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}) + "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)} /> @@ -192,7 +205,7 @@ 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}) + "MATCH (n)-[r:AdminTo]->(m:Computer {name:{name}}) RETURN n,r,m",{name: this.state.label}); }.bind(this)} /> @@ -207,7 +220,7 @@ 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) + this.state.label); }.bind(this)} /> @@ -220,7 +233,7 @@ export default class ComputerNodeData extends Component { value={this.state.derivativeLocalAdmins} click={function(){ emitter.emit('query', - "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) + "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)} /> @@ -235,7 +248,7 @@ 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) + "MATCH (n:Computer {name:{name}}),(m:Group), (n)-[r:MemberOf]->(m) RETURN n,r,m",{name: this.state.label}, this.state.label); }.bind(this)} /> @@ -248,7 +261,7 @@ 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) + "MATCH p = (n:Computer {name:{name}})-[r:MemberOf*1..]->(m:Group) RETURN p",{name: this.state.label}, this.state.label); }.bind(this)} /> @@ -261,7 +274,7 @@ 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) + "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)} /> @@ -276,7 +289,7 @@ 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) + "MATCH (n:Computer {name:{name}}), (m:Computer), p=(n)-[r:AdminTo]->(m) RETURN p",{name: this.state.label}, this.state.label); }.bind(this)} /> @@ -289,7 +302,7 @@ 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) + "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)} /> @@ -316,7 +329,7 @@ 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}) + 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)} /> @@ -329,7 +342,7 @@ 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) + ,this.state.label); }.bind(this)} /> @@ -342,7 +355,7 @@ export default class ComputerNodeData extends Component { value={this.state.transitiveControl} click={function(){ 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) + ,this.state.label); }.bind(this)} /> @@ -354,4 +367,4 @@ export default class ComputerNodeData extends Component { ComputerNodeData.propTypes= { visible : React.PropTypes.bool.isRequired -} +}; diff --git a/src/components/SearchContainer/Tabs/UserNodeData.jsx b/src/components/SearchContainer/Tabs/UserNodeData.jsx index 087ec273a..5f73527ac 100644 --- a/src/components/SearchContainer/Tabs/UserNodeData.jsx +++ b/src/components/SearchContainer/Tabs/UserNodeData.jsx @@ -8,7 +8,6 @@ export default class UserNodeData extends Component { this.state = { label: "", - samAccountName: "None", displayName: "None", pwdLastChanged: "None", firstDegreeGroupMembership: -1, @@ -37,7 +36,6 @@ export default class UserNodeData extends Component { this.setState({ label: payload, - samAccountName: "None", displayName: "None", pwdLastChanged: "None", firstDegreeGroupMembership: -1, @@ -71,6 +69,24 @@ export default class UserNodeData extends Component { 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; + if (properties.hasOwnProperty('PwdLastSet')){ + this.setState({'pwdLastChanged': new Date(properties.PwdLastSet.low * 1000).toUTCString() }); + } + + if (properties.hasOwnProperty("DisplayName")){ + var dname = properties.DisplayName; + if (dname === ""){ + dname = "None"; + } + this.setState({'displayName': dname }); + } + 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}); @@ -149,7 +165,7 @@ export default class UserNodeData extends Component { 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]}); } render() { @@ -166,12 +182,6 @@ export default class UserNodeData extends Component {
{this.state.label}
-
- SAMAccountName -
-
- {this.state.samAccountName} -
Display Name
From c5a1a0bd43bd173c8ce7c2f5685aea2c3d92c204 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Thu, 28 Sep 2017 19:20:12 -0400 Subject: [PATCH 06/15] Fix search container glyphs --- .../SearchContainer/SearchContainer.jsx | 148 +++++++++--------- src/css/styles.css | 6 + 2 files changed, 80 insertions(+), 74 deletions(-) diff --git a/src/components/SearchContainer/SearchContainer.jsx b/src/components/SearchContainer/SearchContainer.jsx index fcdb1d15a..0d969478a 100644 --- a/src/components/SearchContainer/SearchContainer.jsx +++ b/src/components/SearchContainer/SearchContainer.jsx @@ -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 = "" + icon = ""; break; case "User": - icon = "" + icon = ""; break; case "Computer": - icon = "" + icon = ""; break; case "Domain": - icon = "" - break + icon = ""; + break; } - html = '
' + parts[0] + ' ' + icon + '
' + html = '
' + parts[0] + ' ' + icon + '
'; 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, '$1') + return $(this).text().replace(reQuery, '$1'); }); 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 = "" + icon = ""; break; case "User": - icon = "" + icon = ""; break; case "Computer": - icon = "" + icon = ""; break; case "Domain": - icon = "" - break + icon = ""; + break; } - html = '
' + parts[0] + ' ' + icon + '
' + html = '
' + parts[0] + ' ' + icon + '
'; 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, '$1') + return $(this).text().replace(reQuery, '$1'); }); 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'); }}> @@ -289,6 +289,6 @@ export default class SearchContainer extends Component {
- ) + ); } } \ No newline at end of file diff --git a/src/css/styles.css b/src/css/styles.css index f190a593a..13de9a266 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -98,6 +98,12 @@ div.tooltip-inner-custom { overflow: hidden; } +.dropdown-item > i{ + float:right; + margin-top:3px; + margin-left: 2px; +} + .graph:focus { outline: 0; } From 16a50591c3d27c6ba583ff21184625005780ed74 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Fri, 29 Sep 2017 01:41:15 -0400 Subject: [PATCH 07/15] Fix cancel upload, add Enabled property to nodes --- src/components/Graph.jsx | 1 + src/components/Menu/MenuContainer.jsx | 20 ++- src/components/Menu/ProgressBarMenuButton.jsx | 132 +++++++++--------- 3 files changed, 83 insertions(+), 70 deletions(-) diff --git a/src/components/Graph.jsx b/src/components/Graph.jsx index 60c9ba9f6..85488556c 100644 --- a/src/components/Graph.jsx +++ b/src/components/Graph.jsx @@ -465,6 +465,7 @@ export default class GraphContainer extends Component { id: id, type: type, label: label, + Enabled: data.properties.Enabled, glyphs: [], folded: { nodes: [], diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index 6e8398686..ca8fabd2b 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -19,17 +19,17 @@ export default class MenuContainer extends Component { 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(){ @@ -153,6 +153,11 @@ export default class MenuContainer extends Component { }); return; } + + if (this.state.cancelled){ + parser.abort(); + } + dataset = dataset.concat(results.data); count += results.data.length; }.bind(this), @@ -175,6 +180,10 @@ export default class MenuContainer extends Component { var session = driver.session(); while (index < chunks.length){ + if (this.state.cancelled){ + this.setState({cancelled:false}); + break; + } var currentChunk = chunks[index]; if (filetype === 'groupmembership'){ @@ -229,7 +238,10 @@ export default class MenuContainer extends Component { this.setState({progress: Math.floor(sent / total * 100)}); index++; } - this.setState({progress:100}); + if (!this.state.cancelled){ + this.setState({progress:100}); + } + emitter.emit('refreshDBData'); console.timeEnd('IngestTime'); callback(); diff --git a/src/components/Menu/ProgressBarMenuButton.jsx b/src/components/Menu/ProgressBarMenuButton.jsx index 089f7093f..5ca05539e 100644 --- a/src/components/Menu/ProgressBarMenuButton.jsx +++ b/src/components/Menu/ProgressBarMenuButton.jsx @@ -1,80 +1,80 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types' +import PropTypes from 'prop-types'; export default class ProgressBarMenuButton extends Component { - constructor(){ - super() + constructor(){ + super(); - this.state = { - expanded: false - } - } + 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) - } + componentDidMount(){ + $(this.refs.btn).html('{}%'.format(this.props.progress)); + $(this.refs.btn).css('padding','6px 0px 6px 0px'); + $(this.refs.btn).css('width','41px'); + } - _enter(e){ - this.setState({expanded: true}) - var target = $(e.target) - var oldWidth = target.width() - var template = ` -
-
-
- - {}% - -
- `.formatAll(this.props.progress) - - target.html(template) - target.animate({ - width: '150px' - }, 100) - } + componentWillReceiveProps(nextProps){ + if (this.state.expanded){ + var template = `
+
+
+ + {}% + +
`.formatAll(nextProps.progress); + $(this.refs.btn).html(template); + }else{ + $(this.refs.btn).html('{}%'.format(nextProps.progress)); + } - componentWillReceiveProps(nextProps){ - if (this.state.expanded){ - var template = `
-
-
- - {}% - -
`.formatAll(nextProps.progress) - $(this.refs.btn).html(template) - }else{ - $(this.refs.btn).html('{}%'.format(nextProps.progress)) - } + this.forceUpdate(); + } - this.forceUpdate() - } + shouldComponentUpdate(nextProps, nextState){ + return true; + } - shouldComponentUpdate(nextProps, nextState){ - return true - } + _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); + } - componentDidMount(){ - $(this.refs.btn).html('{}%'.format(this.props.progress)) - $(this.refs.btn).css('padding','6px 0px 6px 0px') - $(this.refs.btn).css('width','41px') - } + _enter(e){ + this.setState({expanded: true}); + var target = $(e.target); + var oldWidth = target.width(); + var template = ` +
+
+
+ + {}% + +
+ `.formatAll(this.props.progress); + + target.html(template); + target.animate({ + width: '150px' + }, 100); + } - render() { - return ( - - - - - - - - ); - } + 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(); - 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)); - 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)); - 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)); + } + + render() { + return ( +
+

Database Info

+
+
DB Address
+
{this.state.url}
+
DB User
+
{this.state.user}
+
Users
+
{this.state.num_users}
+
Computers
+
{this.state.num_computers}
+
Groups
+
{this.state.num_groups}
+
Sessions
+
{this.state.num_sessions}
+
ACLs
+
{this.state.num_acls}
+
Relationships
+
{this.state.num_relationships}
+
- s5.run("MATCH ()-[r]->() RETURN count(r)") - .then(function(result){ - this.setState({'num_relationships':result.records[0]._fields[0].low}) - s5.close() - }.bind(this)) - } +
+
+ + + + +
+
+
+ ); + } } diff --git a/src/js/papaparse.min.js b/src/js/papaparse.min.js deleted file mode 100644 index e0524a328..000000000 --- a/src/js/papaparse.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! - Papa Parse - v4.1.2 - https://github.com/mholt/PapaParse -*/ -!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof module&&module.exports?module.exports=b():a.Papa=b()}(this,function(){"use strict";function a(a,b){if(b=b||{},b.worker&&y.WORKERS_SUPPORTED){var c=j();return c.userStep=b.step,c.userChunk=b.chunk,c.userComplete=b.complete,c.userError=b.error,b.step=q(b.step),b.chunk=q(b.chunk),b.complete=q(b.complete),b.error=q(b.error),delete b.worker,void c.postMessage({input:a,config:b,workerId:c.id})}var g=null;return"string"==typeof a?g=b.download?new d(b):new f(b):(s.File&&a instanceof File||a instanceof Object)&&(g=new e(b)),g.stream(a)}function b(a,b){function c(){"object"==typeof b&&("string"==typeof b.delimiter&&1===b.delimiter.length&&-1===y.BAD_DELIMITERS.indexOf(b.delimiter)&&(i=b.delimiter),("boolean"==typeof b.quotes||b.quotes instanceof Array)&&(h=b.quotes),"string"==typeof b.newline&&(j=b.newline))}function d(a){if("object"!=typeof a)return[];var b=[];for(var c in a)b.push(c);return b}function e(a,b){var c="";"string"==typeof a&&(a=JSON.parse(a)),"string"==typeof b&&(b=JSON.parse(b));var d=a instanceof Array&&a.length>0,e=!(b[0]instanceof Array);if(d){for(var g=0;g0&&(c+=i),c+=f(a[g],g);b.length>0&&(c+=j)}for(var h=0;hl;l++){l>0&&(c+=i);var m=d&&e?a[l]:l;c+=f(b[h][m],l)}h-1||" "===a.charAt(0)||" "===a.charAt(a.length-1);return c?'"'+a+'"':a}function g(a,b){for(var c=0;c-1)return!0;return!1}var h=!1,i=",",j="\r\n";if(c(),"string"==typeof a&&(a=JSON.parse(a)),a instanceof Array){if(!a.length||a[0]instanceof Array)return e(null,a);if("object"==typeof a[0])return e(d(a[0]),a)}else if("object"==typeof a)return"string"==typeof a.data&&(a.data=JSON.parse(a.data)),a.data instanceof Array&&(a.fields||(a.fields=a.meta&&a.meta.fields),a.fields||(a.fields=a.data[0]instanceof Array?a.fields:d(a.data[0])),a.data[0]instanceof Array||"object"==typeof a.data[0]||(a.data=[a.data])),e(a.fields||[],a.data||[]);throw"exception: Unable to serialize unrecognized input"}function c(a){function b(a){var b=o(a);b.chunkSize=parseInt(b.chunkSize),a.step||a.chunk||(b.chunkSize=null),this._handle=new g(b),this._handle.streamer=this,this._config=b}this._handle=null,this._paused=!1,this._finished=!1,this._input=null,this._baseIndex=0,this._partialLine="",this._rowCount=0,this._start=0,this._nextChunk=null,this.isFirstChunk=!0,this._completeResults={data:[],errors:[],meta:{}},b.call(this,a),this.parseChunk=function(a){if(this.isFirstChunk&&q(this._config.beforeFirstChunk)){var b=this._config.beforeFirstChunk(a);void 0!==b&&(a=b)}this.isFirstChunk=!1;var c=this._partialLine+a;this._partialLine="";var d=this._handle.parse(c,this._baseIndex,!this._finished);if(!this._handle.paused()&&!this._handle.aborted()){var e=d.meta.cursor;this._finished||(this._partialLine=c.substring(e-this._baseIndex),this._baseIndex=e),d&&d.data&&(this._rowCount+=d.data.length);var f=this._finished||this._config.preview&&this._rowCount>=this._config.preview;if(u)s.postMessage({results:d,workerId:y.WORKER_ID,finished:f});else if(q(this._config.chunk)){if(this._config.chunk(d,this._handle),this._paused)return;d=void 0,this._completeResults=void 0}return this._config.step||this._config.chunk||(this._completeResults.data=this._completeResults.data.concat(d.data),this._completeResults.errors=this._completeResults.errors.concat(d.errors),this._completeResults.meta=d.meta),!f||!q(this._config.complete)||d&&d.meta.aborted||this._config.complete(this._completeResults,this._input),f||d&&d.meta.paused||this._nextChunk(),d}},this._sendError=function(a){q(this._config.error)?this._config.error(a):u&&this._config.error&&s.postMessage({workerId:y.WORKER_ID,error:a,finished:!1})}}function d(a){function b(a){var b=a.getResponseHeader("Content-Range");return parseInt(b.substr(b.lastIndexOf("/")+1))}a=a||{},a.chunkSize||(a.chunkSize=y.RemoteChunkSize),c.call(this,a);var d;t?this._nextChunk=function(){this._readChunk(),this._chunkLoaded()}:this._nextChunk=function(){this._readChunk()},this.stream=function(a){this._input=a,this._nextChunk()},this._readChunk=function(){if(this._finished)return void this._chunkLoaded();if(d=new XMLHttpRequest,this._config.withCredentials&&(d.withCredentials=this._config.withCredentials),t||(d.onload=p(this._chunkLoaded,this),d.onerror=p(this._chunkError,this)),d.open("GET",this._input,!t),this._config.chunkSize){var a=this._start+this._config.chunkSize-1;d.setRequestHeader("Range","bytes="+this._start+"-"+a),d.setRequestHeader("If-None-Match","webkit-no-cache")}try{d.send()}catch(b){this._chunkError(b.message)}t&&0===d.status?this._chunkError():this._start+=this._config.chunkSize},this._chunkLoaded=function(){if(4==d.readyState){if(d.status<200||d.status>=400)return void this._chunkError();this._finished=!this._config.chunkSize||this._start>b(d),this.parseChunk(d.responseText)}},this._chunkError=function(a){var b=d.statusText||a;this._sendError(b)}}function e(a){a=a||{},a.chunkSize||(a.chunkSize=y.LocalChunkSize),c.call(this,a);var b,d,e="undefined"!=typeof FileReader;this.stream=function(a){this._input=a,d=a.slice||a.webkitSlice||a.mozSlice,e?(b=new FileReader,b.onload=p(this._chunkLoaded,this),b.onerror=p(this._chunkError,this)):b=new FileReaderSync,this._nextChunk()},this._nextChunk=function(){this._finished||this._config.preview&&!(this._rowCount=this._input.size,this.parseChunk(a.target.result)},this._chunkError=function(){this._sendError(b.error)}}function f(a){a=a||{},c.call(this,a);var b,d;this.stream=function(a){return b=a,d=a,this._nextChunk()},this._nextChunk=function(){if(!this._finished){var a=this._config.chunkSize,b=a?d.substr(0,a):d;return d=a?d.substr(a):"",this._finished=!d,this.parseChunk(b)}}}function g(a){function b(){if(v&&m&&(j("Delimiter","UndetectableDelimiter","Unable to auto-detect delimiting character; defaulted to '"+y.DefaultDelimiter+"'"),m=!1),a.skipEmptyLines)for(var b=0;b=u.length?(c.__parsed_extra||(c.__parsed_extra=[]),c.__parsed_extra.push(v.data[b][d])):c[u[d]]=v.data[b][d])}a.header&&(v.data[b]=c,d>u.length?j("FieldMismatch","TooManyFields","Too many fields: expected "+u.length+" fields but parsed "+d,b):d1&&(k+=Math.abs(o-f),f=o):f=o}m.data.length>0&&(l/=m.data.length),("undefined"==typeof e||e>k)&&l>1.99&&(e=k,d=j)}return a.delimiter=d,{successful:!!d,bestDelimiter:d}}function g(a){a=a.substr(0,1048576);var b=a.split("\r"),c=a.split("\n"),d=c.length>1&&c[0].length=b.length/2?"\r\n":"\r"}function i(a){var b=n.test(a);return b?parseFloat(a):a}function j(a,b,c,d){v.errors.push({type:a,code:b,message:c,row:d})}var k,l,m,n=/^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i,p=this,r=0,s=!1,t=!1,u=[],v={data:[],errors:[],meta:{}};if(q(a.step)){var w=a.step;a.step=function(d){if(v=d,c())b();else{if(b(),0===v.data.length)return;r+=d.data.length,a.preview&&r>a.preview?l.abort():w(v,p)}}}this.parse=function(c,d,e){if(a.newline||(a.newline=g(c)),m=!1,!a.delimiter){var i=f(c,a.newline);i.successful?a.delimiter=i.bestDelimiter:(m=!0,a.delimiter=y.DefaultDelimiter),v.meta.delimiter=a.delimiter}var j=o(a);return a.preview&&a.header&&j.preview++,k=c,l=new h(j),v=l.parse(k,d,e),b(),s?{meta:{paused:!0}}:v||{meta:{paused:!1}}},this.paused=function(){return s},this.pause=function(){s=!0,l.abort(),k=k.substr(l.getCharIndex())},this.resume=function(){s=!1,p.streamer.parseChunk(k)},this.aborted=function(){return t},this.abort=function(){t=!0,l.abort(),v.meta.aborted=!0,q(a.complete)&&a.complete(v),k=""}}function h(a){a=a||{};var b=a.delimiter,c=a.newline,d=a.comments,e=a.step,f=a.preview,g=a.fastMode;if(("string"!=typeof b||y.BAD_DELIMITERS.indexOf(b)>-1)&&(b=","),d===b)throw"Comment character same as delimiter";d===!0?d="#":("string"!=typeof d||y.BAD_DELIMITERS.indexOf(d)>-1)&&(d=!1),"\n"!=c&&"\r"!=c&&"\r\n"!=c&&(c="\n");var h=0,i=!1;this.parse=function(a,j,k){function l(a){v.push(a),y=h}function m(b){return k?o():("undefined"==typeof b&&(b=a.substr(h)),x.push(b),h=q,l(x),u&&p(),o())}function n(b){h=b,l(x),x=[],C=a.indexOf(c,h)}function o(a){return{data:v,errors:w,meta:{delimiter:b,linebreak:c,aborted:i,truncated:!!a,cursor:y+(j||0)}}}function p(){e(o()),v=[],w=[]}if("string"!=typeof a)throw"Input must be a string";var q=a.length,r=b.length,s=c.length,t=d.length,u="function"==typeof e;h=0;var v=[],w=[],x=[],y=0;if(!a)return o();if(g||g!==!1&&-1===a.indexOf('"')){for(var z=a.split(c),A=0;A=f)return v=v.slice(0,f),o(!0)}}return o()}for(var B=a.indexOf(b,h),C=a.indexOf(c,h);;)if('"'!==a[h])if(d&&0===x.length&&a.substr(h,t)===d){if(-1===C)return o();h=C+s,C=a.indexOf(c,h),B=a.indexOf(b,h)}else if(-1!==B&&(C>B||-1===C))x.push(a.substring(h,B)),h=B+r,B=a.indexOf(b,h);else{if(-1===C)break;if(x.push(a.substring(h,C)),n(C+s),u&&(p(),i))return o();if(f&&v.length>=f)return o(!0)}else{var D=h;for(h++;;){var D=a.indexOf('"',D+1);if(-1===D)return k||w.push({type:"Quotes",code:"MissingQuotes",message:"Quoted field unterminated",row:v.length,index:h}),m();if(D===q-1){var E=a.substring(h,D).replace(/""/g,'"');return m(E)}if('"'!==a[D+1]){if(a[D+1]===b){x.push(a.substring(h,D).replace(/""/g,'"')),h=D+1+r,B=a.indexOf(b,h),C=a.indexOf(c,h);break}if(a.substr(D+1,s)===c){if(x.push(a.substring(h,D).replace(/""/g,'"')),n(D+1+s),B=a.indexOf(b,h),u&&(p(),i))return o();if(f&&v.length>=f)return o(!0);break}}else D++}}return m()},this.abort=function(){i=!0},this.getCharIndex=function(){return h}}function i(){var a=document.getElementsByTagName("script");return a.length?a[a.length-1].src:""}function j(){if(!y.WORKERS_SUPPORTED)return!1;if(!v&&null===y.SCRIPT_PATH)throw new Error("Script path cannot be determined automatically when Papa Parse is loaded asynchronously. You need to set Papa.SCRIPT_PATH manually.");var a=y.SCRIPT_PATH||r;a+=(-1!==a.indexOf("?")?"&":"?")+"papaworker";var b=new s.Worker(a);return b.onmessage=k,b.id=x++,w[b.id]=b,b}function k(a){var b=a.data,c=w[b.workerId],d=!1;if(b.error)c.userError(b.error,b.file);else if(b.results&&b.results.data){var e=function(){d=!0,l(b.workerId,{data:[],errors:[],meta:{aborted:!0}})},f={abort:e,pause:m,resume:m};if(q(c.userStep)){for(var g=0;g Date: Wed, 4 Oct 2017 20:41:25 -0400 Subject: [PATCH 10/15] Fix WriteDacl style key --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 7864d8158..2588a2da8 100644 --- a/src/index.js +++ b/src/index.js @@ -106,7 +106,7 @@ global.appStore = { 'ForceChangePassword': 'tapered', 'GenericAll': 'tapered', 'GenericWrite': 'tapered', - 'WriteDACL': 'tapered', + 'WriteDacl': 'tapered', 'WriteOwner': 'tapered', 'AddMembers': 'tapered', 'TrustedBy': 'curvedArrow', From 5f366ac9f602c24f1dc9963253f692f2ffcc54a8 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Thu, 5 Oct 2017 04:35:04 -0400 Subject: [PATCH 11/15] Add new properties to user/computer --- package.json | 4 +- .../SearchContainer/Tabs/ComputerNodeData.jsx | 51 ++++++----- .../SearchContainer/Tabs/NodePropItem.jsx | 51 +++++++++++ .../SearchContainer/Tabs/UserNodeData.jsx | 88 +++++++++++++------ src/css/styles.css | 9 +- src/js/utils.js | 2 +- 6 files changed, 153 insertions(+), 52 deletions(-) create mode 100644 src/components/SearchContainer/Tabs/NodePropItem.jsx diff --git a/package.json b/package.json index 48d99210f..74cd2fc32 100644 --- a/package.json +++ b/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", diff --git a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx index 23100d330..20d634f8d 100644 --- a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx +++ b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx @@ -8,8 +8,6 @@ export default class ComputerNodeData extends Component { this.state = { label: "", - os: "None", - unconstrained: "None", explicitAdmins: -1, unrolledAdmins: -1, firstDegreeGroupMembership: -1, @@ -22,7 +20,8 @@ export default class ComputerNodeData extends Component { groupDelegatedControl: -1, transitiveControl: -1, derivativeLocalAdmins: -1, - driversessions: [] + driversessions: [], + propertyMap: {} }; emitter.on('computerNodeClicked', this.getNodeData.bind(this)); @@ -34,8 +33,6 @@ export default class ComputerNodeData extends Component { }); this.setState({ label: payload, - os: "None", - unconstrained: "None", explicitAdmins: -1, unrolledAdmins: -1, firstDegreeGroupMembership: -1, @@ -47,7 +44,8 @@ export default class ComputerNodeData extends Component { firstdegreeControl: -1, groupDelegatedControl: -1, transitiveControl: -1, - derivativeLocalAdmins: -1 + derivativeLocalAdmins: -1, + propertyMap: {} }); var s1 = driver.session(); @@ -66,14 +64,9 @@ export default class ComputerNodeData extends Component { var propCollection = driver.session(); propCollection.run("MATCH (c:Computer {name:{name}}) RETURN c", {name:payload}) - .then(function(results){ - var props = results.records[0]._fields[0].properties; - if (props.hasOwnProperty('UnconstrainedDelegation')){ - this.setState({'unconstrained': props.UnconstrainedDelegation.toString().toTitleCase()}); - } - if (props.hasOwnProperty('OperatingSystem')){ - this.setState({'os': props.OperatingSystem}); - } + .then(function(result){ + var properties = result.records[0]._fields[0].properties; + this.setState({propertyMap: properties}); propCollection.close(); }.bind(this)); @@ -158,6 +151,22 @@ export default class ComputerNodeData extends Component { 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')){ + return new Date(obj.low * 1000).toUTCString(); + }else if (type === 'boolean'){ + return obj.toString().toTitleCase(); + }else if (obj === ""){ + return "None"; + }else{ + return obj; + } + } + render() { return (
@@ -173,13 +182,19 @@ export default class ComputerNodeData extends Component { OS
- {this.state.os} + {this.convertToDisplayProp("OperatingSystem")} +
+
+ Enabled +
+
+ {this.convertToDisplayProp("Enabled")}
Allows Unconstrained Delegation
- {this.state.unconstrained} + {this.convertToDisplayProp("UnconstrainedDelegation")}
Sessions @@ -194,7 +209,6 @@ export default class ComputerNodeData extends Component { }.bind(this)} /> -

Local Admins

Explicit Admins @@ -237,7 +251,6 @@ export default class ComputerNodeData extends Component { }.bind(this)} /> -

Group Memberships

First Degree Group Membership @@ -278,7 +291,6 @@ export default class ComputerNodeData extends Component { }.bind(this)} /> -

Local Admin Rights

First Degree Local Admin @@ -319,7 +331,6 @@ export default class ComputerNodeData extends Component { }.bind(this)} /> -

Outbound Object Control

First Degree Object Control diff --git a/src/components/SearchContainer/Tabs/NodePropItem.jsx b/src/components/SearchContainer/Tabs/NodePropItem.jsx new file mode 100644 index 000000000..031f0f6c7 --- /dev/null +++ b/src/components/SearchContainer/Tabs/NodePropItem.jsx @@ -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 [ +
{this.props.keyName}
, +
{obj.low}
+ ]; + }else if (this.isArray(obj)){ + console.log(obj); + if (obj.length === 0){ + return [ +
{this.props.keyName}
, +
None
+ ]; + }else{ + var elements = []; + $.each(obj, function(index, prop){ + elements.push(
); + elements.push(
{prop}
); + }); + elements[0] =
Service Principal Names
; + } + + return elements; + }else if (typeof obj === 'boolean'){ + return [ +
{this.props.keyName}
, +
{this.props.keyValue.toString().toTitleCase()}
+ ]; + }else{ + return [ +
{this.props.keyName}
, +
{this.props.keyValue.toString()}
+ ]; + } + + } +} \ No newline at end of file diff --git a/src/components/SearchContainer/Tabs/UserNodeData.jsx b/src/components/SearchContainer/Tabs/UserNodeData.jsx index 5f73527ac..73f471840 100644 --- a/src/components/SearchContainer/Tabs/UserNodeData.jsx +++ b/src/components/SearchContainer/Tabs/UserNodeData.jsx @@ -1,15 +1,16 @@ import React, { Component } from 'react'; 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(){ super(); this.state = { label: "", - displayName: "None", - pwdLastChanged: "None", firstDegreeGroupMembership: -1, unrolledGroupMembership: -1, foreignGroupMembership: -1, @@ -23,7 +24,8 @@ export default class UserNodeData extends Component { firstdegreeControl: -1, unrolledControl: -1, transitiveControl: -1, - driversessions : [] + driversessions : [], + propertyMap: {ServicePrincipalNames: []} }; emitter.on('userNodeClicked', this.getNodeData.bind(this)); @@ -36,8 +38,6 @@ export default class UserNodeData extends Component { this.setState({ label: payload, - displayName: "None", - pwdLastChanged: "None", firstDegreeGroupMembership: -1, unrolledGroupMembership: -1, foreignGroupMembership: -1, @@ -50,7 +50,8 @@ export default class UserNodeData extends Component { transitiveControllers: -1, firstdegreeControl: -1, unrolledControl: -1, - transitiveControl: -1 + transitiveControl: -1, + propertyMap: {ServicePrincipalNames: []} }); var domain = '@' + payload.split('@').last(); @@ -71,19 +72,8 @@ export default class UserNodeData extends Component { 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; - if (properties.hasOwnProperty('PwdLastSet')){ - this.setState({'pwdLastChanged': new Date(properties.PwdLastSet.low * 1000).toUTCString() }); - } - - if (properties.hasOwnProperty("DisplayName")){ - var dname = properties.DisplayName; - if (dname === ""){ - dname = "None"; - } - this.setState({'displayName': dname }); - } + .then(function(result){ + this.setState({propertyMap: properties}); props.close(); }.bind(this)); @@ -168,13 +158,33 @@ export default class UserNodeData extends Component { 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')){ + 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(); return (

- Node Info + User Info

Name @@ -186,14 +196,38 @@ export default class UserNodeData extends Component { Display Name
- {this.state.displayName} + {this.convertToDisplayProp("DisplayName")}
Password Last Changed
- {this.state.pwdLastChanged} + {this.convertToDisplayProp("PwdLastSet")} +
+
+ Last Logon +
+
+ {this.convertToDisplayProp("LastLogon")}
+
+ Enabled +
+
+ {this.convertToDisplayProp("Enabled")} +
+
+ Service Principal Names +
+ {(() => { + if (this.state.propertyMap.ServicePrincipalNames.length === 0){ + return
None
; + } + })()} + {Object.keys(this.state.propertyMap.ServicePrincipalNames).map(function(key){ + var x =
{this.state.propertyMap.ServicePrincipalNames[key]}
; + return x; + }.bind(this))}
Sessions
@@ -207,7 +241,7 @@ export default class UserNodeData extends Component { }.bind(this)} /> -
+

Group Membership

First Degree Group Memberships @@ -250,7 +284,7 @@ export default class UserNodeData extends Component { }.bind(this)} /> -
+

Local Admin Rights

@@ -292,7 +326,7 @@ export default class UserNodeData extends Component { }.bind(this)} /> -
+

Outbound Object Control

@@ -334,7 +368,7 @@ export default class UserNodeData extends Component { }.bind(this)} /> -
+

Inbound Object Control

Explicit Object Controllers @@ -382,5 +416,5 @@ export default class UserNodeData extends Component { } UserNodeData.propTypes = { - visible : React.PropTypes.bool.isRequired + visible : PropTypes.bool.isRequired }; \ No newline at end of file diff --git a/src/css/styles.css b/src/css/styles.css index 13de9a266..3cbe9cdc6 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -195,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; } @@ -771,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; } \ No newline at end of file diff --git a/src/js/utils.js b/src/js/utils.js index 009533f76..4950e0c6e 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -164,7 +164,7 @@ export function buildSessionProps(rows) { if (row.UserName === 'ANONYMOUS LOGON@UNKNOWN' || row.UserName === '') { return; } - sessions.push({ account: row.UserName.toUpperCase(), computer: row.ComputerName.toUpperCase(), weight: parseInt(row.Weight) }); + sessions.push({ account: row.UserName.toUpperCase(), computer: row.ComputerName.toUpperCase(), weight: row.Weight }); }); return sessions; From 85f1491f276e6b88c62a7918eb86ebf7da5bebb9 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Thu, 5 Oct 2017 05:00:12 -0400 Subject: [PATCH 12/15] Fix Query Debug checkbox so it falls under properly --- src/components/Float/Settings.jsx | 269 +++++++++++++++--------------- 1 file changed, 135 insertions(+), 134 deletions(-) diff --git a/src/components/Float/Settings.jsx b/src/components/Float/Settings.jsx index dfb4501ec..51b99e633 100644 --- a/src/components/Float/Settings.jsx +++ b/src/components/Float/Settings.jsx @@ -1,138 +1,139 @@ import React, { Component } from 'react'; export default class Settings extends Component { - constructor(){ - super(); - } - - componentDidMount() { - emitter.on('openSettings', function(){ - this.openSettings() - }.bind(this)) - - $(this.refs.edge).simpleSlider({ - range: [0,20], - step: 1, - theme: 'volume slideinline' - }) - - $(this.refs.sibling).simpleSlider({ - range: [0,20], - step: 1, - theme: 'volume slideinline' - }) - - $(this.refs.edge).bind('slider:changed', this.edgeChange.bind(this)) - $(this.refs.sibling).bind('slider:changed', this.siblingChange.bind(this)) - - $(this.refs.edge).simpleSlider('setValue', appStore.performance.edge) - $(this.refs.sibling).simpleSlider('setValue', appStore.performance.sibling) - - $(this.refs.check).prop('checked', appStore.performance.lowGraphics) - $(this.refs.debug).prop('checked', appStore.performance.debug) - - $(this.refs.outer).fadeToggle(0) - $(this.refs.outer).draggable() - } - - edgeChange(event, data){ - appStore.performance.edge = data.value; - $(this.refs.edgeinput).val(data.value) - conf.set('performance', appStore.performance) - } - - siblingChange(event, data){ - appStore.performance.sibling = data.value; - $(this.refs.siblinginput).val(data.value) - conf.set('performance', appStore.performance) - } - - onGfxChange(event){ - $(this.refs.check).prop('checked', event.target.checked) - appStore.performance.lowGraphics = event.target.checked - conf.set('performance', appStore.performance) - emitter.emit('changeGraphicsMode') - } - - onDebugChange(event){ - $(this.refs.debug).prop('checked', event.target.checked) - appStore.performance.debug = event.target.checked - conf.set('performance', appStore.performance) - } - - closeSettings(){ - $(this.refs.outer).fadeToggle(false) - } - - openSettings(){ - $(this.refs.outer).fadeToggle(false) - } - - updateSibling(event){ - $(this.refs.sibling).simpleSlider('setValue', event.target.value) - } - - updateEdge(event){ - $(this.refs.edge).simpleSlider('setValue', event.target.value) - } - - render() { - return ( -
-
- Settings - -
- -
-
- Sibling Collapse Threshold - -
- - - - -
- -
- Node Collapse Threshold - -
- - - - -
-
- -
- -
-
- -
- -
-
- ); - } + constructor(){ + super(); + } + + componentDidMount() { + emitter.on('openSettings', function(){ + this.openSettings() + }.bind(this)) + + $(this.refs.edge).simpleSlider({ + range: [0,20], + step: 1, + theme: 'volume slideinline' + }) + + $(this.refs.sibling).simpleSlider({ + range: [0,20], + step: 1, + theme: 'volume slideinline' + }) + + $(this.refs.edge).bind('slider:changed', this.edgeChange.bind(this)) + $(this.refs.sibling).bind('slider:changed', this.siblingChange.bind(this)) + + $(this.refs.edge).simpleSlider('setValue', appStore.performance.edge) + $(this.refs.sibling).simpleSlider('setValue', appStore.performance.sibling) + + $(this.refs.check).prop('checked', appStore.performance.lowGraphics) + $(this.refs.debug).prop('checked', appStore.performance.debug) + + $(this.refs.outer).fadeToggle(0) + $(this.refs.outer).draggable() + } + + edgeChange(event, data){ + appStore.performance.edge = data.value; + $(this.refs.edgeinput).val(data.value) + conf.set('performance', appStore.performance) + } + + siblingChange(event, data){ + appStore.performance.sibling = data.value; + $(this.refs.siblinginput).val(data.value) + conf.set('performance', appStore.performance) + } + + onGfxChange(event){ + $(this.refs.check).prop('checked', event.target.checked) + appStore.performance.lowGraphics = event.target.checked + conf.set('performance', appStore.performance) + emitter.emit('changeGraphicsMode') + } + + onDebugChange(event){ + $(this.refs.debug).prop('checked', event.target.checked) + appStore.performance.debug = event.target.checked + conf.set('performance', appStore.performance) + } + + closeSettings(){ + $(this.refs.outer).fadeToggle(false) + } + + openSettings(){ + $(this.refs.outer).fadeToggle(false) + } + + updateSibling(event){ + $(this.refs.sibling).simpleSlider('setValue', event.target.value) + } + + updateEdge(event){ + $(this.refs.edge).simpleSlider('setValue', event.target.value) + } + + render() { + return ( +
+
+ Settings + +
+ +
+
+ Sibling Collapse Threshold + +
+ + + + +
+ +
+ Node Collapse Threshold + +
+ + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ ); + } } From 4a304d663bf112c2f67abc6d3b51b8de77f5eb14 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Thu, 5 Oct 2017 05:04:06 -0400 Subject: [PATCH 13/15] Fix checkbox on login to properly load --- src/components/Float/Login.jsx | 507 +++++++++++++++++---------------- 1 file changed, 256 insertions(+), 251 deletions(-) diff --git a/src/components/Float/Login.jsx b/src/components/Float/Login.jsx index e1cdb5278..82ecbe056 100644 --- a/src/components/Float/Login.jsx +++ b/src/components/Float/Login.jsx @@ -1,280 +1,285 @@ import React, { Component } from 'react'; export default class Login extends Component { - constructor(){ - super(); - this.state = { - url: "", - icon: null, - loginEnabled: false, - user: "", - password: "", - loginInProgress: false, - save: false - } - } + constructor(){ + super(); + this.state = { + url: "", + icon: null, + loginEnabled: false, + user: "", + 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) + componentWillMount() { + var c = conf.get('databaseInfo'); + if (typeof c !== 'undefined'){ + this.setState({ + url: c.url, + user: c.user, + password: c.password, + save: true + }); + } + } - if (url === ""){ - return; - } + componentDidMount() { + jQuery(this.refs.password).tooltip({ + placement : 'right', + title: '', + container: 'body', + trigger: 'manual', + template: '' + }); + this.setIcon(); + + if (this.state.password !== ""){ + this.checkDBCreds(); + } + } - jQuery(this.refs.urlspinner).toggle(true) + setIcon(){ + var icon = jQuery(this.refs.urlspinner); + icon.tooltip({ + placement : 'right', + title: '', + container: 'body', + delay: {show: 200, hide: 0}, + template: '' + }); + icon.toggle(false); + this.setState({icon: jQuery(this.refs.urlspinner)}); + } - url = url.replace(/\/$/, ""); + checkDBPresence(){ + var url = this.state.url; + var icon = this.state.icon; + var jicon = jQuery(icon); + var btn = jQuery(this.refs.loginButton); - if (!url.includes(':')){ - url = url + ':7687' - } + if (url === ""){ + return; + } - if (!url.startsWith('bolt://')){ - url = 'bolt://' + url - } + jQuery(this.refs.urlspinner).toggle(true); - 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(); + url = url.replace(/\/$/, ""); - 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") - } + if (!url.includes(':')){ + url = url + ':7687'; + } - checkDBCreds(){ - if (this.state.loginInProgress){ - return; - } - this.setState({ - loginInProgress: true, - loginEnabled: false - }) + if (!url.startsWith('bolt://')){ + url = 'bolt://' + url; + } - var btn = jQuery(this.refs.loginButton) - var pwf = jQuery(this.refs.password) + 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(); - 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(){ + 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"); + } - }, - onCompleted: function(){ - btn.toggleClass('activate'); - btn.removeClass('btn-default') - btn.addClass('btn-success') - btn.html('Success!') - this.setState({ - loginInProgress: false - }) + checkDBCreds(){ + if (this.state.loginInProgress){ + return; + } + this.setState({ + loginInProgress: true, + loginEnabled: false + }); - var dbinfo = { - url: this.state.url, - user: this.state.user, - password: this.state.password - } - - if (this.state.save){ - conf.set('databaseInfo',dbinfo) - } + var btn = jQuery(this.refs.loginButton); + var pwf = jQuery(this.refs.password); - 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) - }) + 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(){ - btn.toggleClass('activate'); + }, + 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); + } - componentWillMount() { - var c = conf.get('databaseInfo') - if (typeof c !== 'undefined'){ - this.setState({ - url: c.url, - user: c.user, - password: c.password, - save: true - }) - } - } + 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) + }); - componentDidMount() { - 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(); - } - } + btn.toggleClass('activate'); - _saveChange(event) { - this.setState({save: event.target.checked}); - } + } - _urlChanged(event){ - this.setState({url: event.target.value}) - } + _saveChange(event) { + this.setState({save: event.target.checked}); + } - _userChanged(event){ - this.setState({user: event.target.value}) - jQuery(this.refs.password).tooltip('hide') - } + _urlChanged(event){ + this.setState({url: event.target.value}); + } - _passChanged(event){ - this.setState({password: event.target.value}) - jQuery(this.refs.password).tooltip('hide') - } + _userChanged(event){ + this.setState({user: event.target.value}); + jQuery(this.refs.password).tooltip('hide'); + } - _triggerLogin(e){ - var key = e.keyCode ? e.keyCode : e.which + _passChanged(event){ + this.setState({password: event.target.value}); + jQuery(this.refs.password).tooltip('hide'); + } - if (key === 13){ - this.checkDBCreds() - } - } + _triggerLogin(e){ + var key = e.keyCode ? e.keyCode : e.which; - render() { - return ( -
-
- -
- Log in to Neo4j Database -
-
-
-
- - Database URL - - - -
-
- DB Username - -
-
- DB Password - -
-
-
- -
-
- -
-
-
- -
-
- ); - } + if (key === 13){ + this.checkDBCreds(); + } + } + + render() { + return ( +
+
+ +
+ Log in to Neo4j Database +
+
+
+
+ + Database URL + + + +
+
+ DB Username + +
+
+ DB Password + +
+
+
+ +
+
+ +
+
+
+ +
+
+ ); + } } From e61b2023944317ed5a5473aeceb356463b13f26a Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Thu, 5 Oct 2017 15:41:56 -0400 Subject: [PATCH 14/15] Add mail --- 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 f05a0b2ba..800efe19f 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -262,7 +262,7 @@ export default class MenuContainer extends Component { 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'; + 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'){ From 29e3e046be954f9d43badc4ab6ea19fa51a17367 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Tue, 10 Oct 2017 03:53:27 -0400 Subject: [PATCH 15/15] Add more props, fix more stuff --- package.json | 1 - .../SearchContainer/Tabs/ComputerNodeData.jsx | 7 ++++++- .../SearchContainer/Tabs/GroupNodeData.jsx | 5 ----- .../SearchContainer/Tabs/UserNodeData.jsx | 16 ++++++++++++++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 74cd2fc32..915490e2d 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ }, "dependencies": { "async": "^2.1.4", - "await-stream-ready": "^1.0.1", "bootstrap": "^3.3.6", "bootstrap-3-typeahead": "^4.0.1", "configstore": "^3.1.0", diff --git a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx index 20d634f8d..6d4f569c0 100644 --- a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx +++ b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx @@ -157,7 +157,12 @@ export default class ComputerNodeData extends Component { if (type === 'undefined'){ return "No Data"; }else if (obj.hasOwnProperty('low')){ - return new Date(obj.low * 1000).toUTCString(); + 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 === ""){ diff --git a/src/components/SearchContainer/Tabs/GroupNodeData.jsx b/src/components/SearchContainer/Tabs/GroupNodeData.jsx index 96168c493..46278264c 100644 --- a/src/components/SearchContainer/Tabs/GroupNodeData.jsx +++ b/src/components/SearchContainer/Tabs/GroupNodeData.jsx @@ -197,7 +197,6 @@ export default class GroupNodeData extends Component { }.bind(this)} /> -

Group Members

Direct Members @@ -237,7 +236,6 @@ export default class GroupNodeData extends Component { }.bind(this)} /> -

Group Membership

First Degree Group Membership @@ -277,7 +275,6 @@ export default class GroupNodeData extends Component { }.bind(this)} /> -

Local Admin Rights

First Degree Local Admin @@ -318,7 +315,6 @@ export default class GroupNodeData extends Component { }.bind(this)} /> -

Outbound Object Control

First Degree Object Control @@ -358,7 +354,6 @@ export default class GroupNodeData extends Component { }.bind(this)} /> -

Inbound Object Control

Explicit Object Controllers diff --git a/src/components/SearchContainer/Tabs/UserNodeData.jsx b/src/components/SearchContainer/Tabs/UserNodeData.jsx index 73f471840..27248c2c7 100644 --- a/src/components/SearchContainer/Tabs/UserNodeData.jsx +++ b/src/components/SearchContainer/Tabs/UserNodeData.jsx @@ -72,7 +72,8 @@ export default class UserNodeData extends Component { var props = driver.session(); props.run("MATCH (n:User {name:{name}}) RETURN n", {name: payload}) - .then(function(result){ + .then(function(result){ + var properties = result.records[0]._fields[0].properties; this.setState({propertyMap: properties}); props.close(); }.bind(this)); @@ -168,7 +169,12 @@ export default class UserNodeData extends Component { if (type === 'undefined'){ return "No Data"; }else if (obj.hasOwnProperty('low')){ - return new Date(obj.low * 1000).toUTCString(); + 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 === ""){ @@ -216,6 +222,12 @@ export default class UserNodeData extends Component {
{this.convertToDisplayProp("Enabled")}
+
+ Email +
+
+ {this.convertToDisplayProp("Email")} +
Service Principal Names