From c7006528632b409f7aa9394ffbf8dd62dbea4fc2 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 10:29:53 -0400 Subject: [PATCH 01/67] Fix login for new neo4j --- src/components/Float/Login.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Float/Login.jsx b/src/components/Float/Login.jsx index 6016ec4..fe8e839 100644 --- a/src/components/Float/Login.jsx +++ b/src/components/Float/Login.jsx @@ -119,7 +119,7 @@ export default class Login extends Component { var btn = jQuery(this.refs.loginButton); var pwf = jQuery(this.refs.password); - var driver = neo4j.driver(this.state.url, neo4j.auth.basic(this.state.user, this.state.password)); + var driver = neo4j.driver(this.state.url, neo4j.auth.basic(this.state.user, this.state.password), {encrypted:'ENCRYPTION_ON'}); driver.onError = function(error){ console.log(error); if (error.message.includes("authentication failure")){ @@ -161,7 +161,7 @@ export default class Login extends Component { 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"){ + if (error.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'); From 634b3fb51281bdca7098260178494804c56cb5ee Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 10:30:16 -0400 Subject: [PATCH 02/67] JSON ingestion updates --- LICENSE-3RD-PARTY.md | 39 ++- package.json | 3 +- src/components/Graph.jsx | 6 + src/components/Menu/MenuContainer.jsx | 100 ++++++- src/js/utils.js | 370 ++++++++++++++++++++++++++ 5 files changed, 512 insertions(+), 6 deletions(-) diff --git a/LICENSE-3RD-PARTY.md b/LICENSE-3RD-PARTY.md index c7670e8..a2c8e6e 100644 --- a/LICENSE-3RD-PARTY.md +++ b/LICENSE-3RD-PARTY.md @@ -85,4 +85,41 @@ jQuery-UI All files located in the node_modules and external directories are externally maintained libraries used by this software which have their own licenses; we recommend you read them, as their terms may differ from - the terms above. \ No newline at end of file + the terms above. + + + stream-json + This library is available under the terms of the modified BSD license. No external contributions +are allowed under licenses which are fundamentally incompatible with the BSD license that this library is distributed under. + +The text of the BSD license is reproduced below. + +------------------------------------------------------------------------------- +The "New" BSD License: +********************** + +Copyright (c) 2005-2018, Eugene Lazutkin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Eugene Lazutkin nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/package.json b/package.json index 43fe771..2e4e887 100644 --- a/package.json +++ b/package.json @@ -60,12 +60,13 @@ "jquery": "^3.2.1", "linkurious": "^1.5.1", "mustache": "^2.3.0", - "neo4j-driver": "^1.5.2", + "neo4j-driver": "^1.6.2", "react": "^16.2.0", "react-bootstrap": "^0.32.0", "react-dom": "^16.2.0", "react-if": "^2.1.0", "react-transition-group": "^2.2.1", + "stream-json": "^1.1.0", "unzipper": "^0.8.9" } } diff --git a/src/components/Graph.jsx b/src/components/Graph.jsx index 1405d92..397988c 100644 --- a/src/components/Graph.jsx +++ b/src/components/Graph.jsx @@ -540,6 +540,12 @@ export default class GraphContainer extends Component { var id = data.identity.low; var type = data.labels[0]; var label = data.properties.name; + var guid = data.properties.guid; + + if (label == null){ + label = guid; + } + var node = { id: id, type: type, diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index 96435de..98d41ca 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -1,15 +1,20 @@ import React, { Component } from 'react'; import MenuButton from './MenuButton'; import ProgressBarMenuButton from './ProgressBarMenuButton'; -import { buildDomainProps, buildSessionProps, buildLocalAdminProps, buildGroupMembershipProps, buildACLProps, findObjectType, buildStructureProps, buildGplinkProps} from 'utils'; +import { buildGpoAdminJson, buildSessionJson, buildUserJson,buildComputerJson, buildDomainJson, buildGpoJson, buildGroupJson, buildOuJson, buildDomainProps, buildSessionProps, buildLocalAdminProps, buildGroupMembershipProps, buildACLProps, findObjectType, buildStructureProps, buildGplinkProps} from 'utils'; import { If, Then, Else } from 'react-if'; -const { dialog, clipboard, app } = require('electron').remote; +const { dialog, app } = require('electron').remote; var fs = require('fs'); var async = require('async'); var unzip = require('unzipper'); var fpath = require('path'); var csv = require('fast-csv'); +const Pick = require('stream-json/filters/Pick'); +const {streamArray} = require('stream-json/streamers/StreamArray'); +const {chain} = require('stream-chain'); +const Asm = require('stream-json/Assembler'); + export default class MenuContainer extends Component { constructor(){ super(); @@ -74,7 +79,7 @@ export default class MenuContainer extends Component { 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); + this.getFileMeta(file.path, callback); }.bind(this), function done(){ setTimeout(function(){ @@ -120,8 +125,93 @@ export default class MenuContainer extends Component { _aboutClick(){ emitter.emit('showAbout'); } + + getFileMeta(file, callback){ + let acceptableTypes = ["sessions","ous","groups","gpoadmins","gpos","computers","users","domains"]; + let count; + let type; + + console.log(file) + + let pipeline = chain([ + fs.createReadStream(file, {encoding: 'utf8'}), + Pick.withParser({filter:'meta'}) + ]); + + let asm = Asm.connectTo(pipeline); + asm.on('done', function(asm){ + let data = asm.current + count = data.count + type = data.type + + if (!acceptableTypes.includes(type)){ + emitter.emit('showAlert', 'Unrecognized JSON Type'); + callback(); + } + + this.processJson(file, callback, count, type) + }.bind(this)) + + } + + processJson(file, callback, count, type){ + let pipeline = chain([ + fs.createReadStream(file, {encoding: 'utf8'}), + Pick.withParser({filter:type}), + streamArray() + ]) + + let localcount = 0; + let sent = 0; + let chunk = [] + //Start a timer for fun + console.time('IngestTime') + + pipeline.on('data', async function(data){ + chunk.push(data.value) + localcount++; + + if (localcount % 100 === 0){ + pipeline.pause(); + await this.uploadDataNew(chunk, type) + sent += chunk.length; + this.setState({ + progress: Math.floor(sent / count * 100) + }); + chunk = [] + pipeline.resume(); + } + + }.bind(this)).on('end', async function(){ + await this.uploadDataNew(chunk, type) + this.setState({progress:100}); + emitter.emit('refreshDBData'); + console.timeEnd('IngestTime'); + callback() + }.bind(this)) + } + + //DO NOT USE THIS FUNCTION FOR ANYTHING, ITS ONLY FOR TESTING + sleep_test(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + async uploadDataNew(chunk, type){ + let session = driver.session(); + let funcMap = {'computers' : buildComputerJson, 'domains': buildDomainJson, 'gpos': buildGpoJson, 'users': buildUserJson, + 'groups': buildGroupJson, 'ous': buildOuJson, 'sessions': buildSessionJson, 'gpoadmins':buildGpoAdminJson} + let data = funcMap[type](chunk) + + for (let key in data){ + await session.run(data[key].statement, {props: data[key].props}).catch(function(error){ + console.log(error) + }) + } + + session.close(); + } - processFile(file, callback){ + processFileOld(file, callback){ console.log(file); var count = 0; var header = ""; @@ -206,6 +296,8 @@ export default class MenuContainer extends Component { }.bind(this)); }.bind(this)); } + + async uploadData(currentChunk, filetype, total){ var index = 0; diff --git a/src/js/utils.js b/src/js/utils.js index 84e97ee..4788a9e 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -584,6 +584,376 @@ export function buildGplinkProps(rows){ return datadict; } +function processAceArray(array, objname, objtype, output){ + let baseAceQuery = 'UNWIND {props} AS prop MERGE (a:{} {name:prop.principal}) MERGE (b:{} {name: prop.obj}) MERGE (a)-[r:{} {isacl:true}]->(b)' + + $.each(array, function(_, ace){ + let principal = ace.PrincipalName; + let principaltype = ace.PrincipalType; + let right = ace.RightName; + let acetype = ace.AceType; + + if (objname === principal){ + return; + } + + let rights = [] + + //Process the right/type to figure out the ACEs we need to add + if (acetype === 'All'){ + rights.push('AllExtendedRights'); + }else if (acetype === 'User-Force-Change-Password'){ + rights.push('ForceChangePassword'); + }else if (acetype === 'Member'){ + rights.push('AddMember'); + }else if (right === 'ExtendedRight'){ + rights.push(acetype); + } + + if (right.includes('GenericAll')){ + rights.push('GenericAll'); + } + + if (right.includes('WriteDacl')){ + rights.push('WriteDacl'); + } + + if (right.includes('WriteOwner')){ + rights.push('WriteOwner'); + } + + if (right.includes('GenericWrite')){ + rights.push('GenericWrite'); + } + + if (right === 'Owner'){ + rights.push('Owns'); + } + + $.each(rights, function(_, right){ + let hash = right + principaltype; + let formatted = baseAceQuery.format(principaltype.toTitleCase(), objtype, right); + + insert(output, hash, formatted, {principal:principal,obj:objname}); + }) + }) +} + +export function buildDomainJson(chunk){ + let queries = {} + queries.properties = { + statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.name}) SET n += prop.map", + props: [] + }; + + queries.links = { + statement: 'UNWIND {props} as prop MERGE (n:Domain {name:prop.domain}) MERGE (m:GPO {name:prop.gpo}) MERGE (m)-[r:GpLink {enforced:prop.enforced, isacl:false}]->(n)', + props: [] + } + + queries.trusts = { + statement: 'UNWIND {props} AS prop MERGE (n:Domain {name: prop.a}) MERGE (m:Domain {name: prop.b}) MERGE (n)-[:TrustedBy {trusttype : prop.trusttype, transitive: prop.transitive, isacl:false}]->(m)', + props: [] + } + + queries.childous = { + statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:OU {guid:prop.guid}) MERGE (n)-[r:Contains]->(m)", + props : [] + } + + queries.computers = { + statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:Contains]->(m)", + props:[] + } + + queries.users = { + statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:User {name:prop.user}) MERGE (n)-[r:Contains]->(m)", + props:[] + } + + $.each(chunk, function(_, domain){ + let name = domain.Name; + let properties = domain.Properties; + + queries.properties.props.push({map:properties, name:name}); + + let links = domain.Links; + $.each(links, function(_, link){ + let enforced = link.IsEnforced; + let target = link.Name; + + queries.links.props.push({domain:name, gpo:target,enforced:enforced}); + }); + + let trusts = domain.Trusts; + $.each(trusts, function(_, trust){ + let target = trust.TargetName; + let transitive = trust.IsTransitive; + let direction = trust.TrustDirection; + let type = trust.TrustType; + + switch (direction){ + case 0: + queries.trusts.props.push({a: target, b: name, transitive: transitive, trusttype: type}); + break; + case 1: + queries.trusts.props.push({a: name, b: target, transitive: transitive, trusttype: type}); + break; + case 2: + queries.trusts.props.push({a: name, b: target, transitive: transitive, trusttype: type}); + queries.trusts.props.push({a: target, b: name, transitive: transitive, trusttype: type}); + break; + } + }); + + let aces = domain.Aces; + processAceArray(aces, name, "Domain", queries); + + let childous = domain.ChildOus; + + $.each(childous, function(_, ou){ + queries.childous.props.push({domain:name, guid:ou}) + }) + + let comps = domain.Computers; + $.each(comps, function(_, computer){ + queries.computers.props.push({domain:name, comp:computer}) + }) + + let users = domain.Users + $.each(users, function(_, user){ + queries.users.props.push({domain: name, user:user}); + }); + }); + + return queries; +} + +export function buildGpoJson(chunk){ + let queries = {} + queries.properties = { + statement: "UNWIND {props} AS prop MERGE (n:GPO {name:prop.name}) SET n.guid=prop.guid", + props: [] + } + + $.each(chunk, function(_, gpo){ + let name = gpo.Name; + let guid = gpo.Guid; + queries.properties.props.push({name:name, guid:guid}); + + let aces = gpo.Aces; + processAceArray(aces, name, "GPO", queries); + }); + + return queries; +} + +export function buildGroupJson(chunk){ + let queries = {} + queries.properties = { + statement: "UNWIND {props} AS prop MERGE (n:Group {name:prop.name}) SET n += prop.map", + props: [] + } + + let baseStatement = "UNWIND {props} AS prop MERGE (n:Group {name: prop.name}) MERGE (m:{} {name:prop.member}) MERGE (m)-[r:MemberOf]->(n)"; + + $.each(chunk, function(_, group){ + let name = group.Name; + let properties = group.Properties; + + queries.properties.props.push({map:properties, name:name}); + + let aces = group.Aces; + processAceArray(aces, name, "Group", queries); + + let members = group.Members; + $.each(members, function(_, member){ + let mname = member.MemberName; + let mtype = member.MemberType; + + let statement = baseStatement.format(mtype.toTitleCase()) + insert(queries, mtype, statement, {name: name, member: mname}) + }); + }); + + return queries +} + +export function buildOuJson(chunk){ + let queries = {}; + + queries.properties = { + statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.guid}) SET n += prop.map", + props: [] + } + + queries.childous = { + statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.parent}) MERGE (m:OU {guid:prop.child}) MERGE (n)-[r:Contains]->(m)", + props : [] + } + + queries.computers = { + statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.ou}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:Contains]->(m)", + props:[] + } + + queries.users = { + statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.ou}) MERGE (m:User {name:prop.user}) MERGE (n)-[r:Contains]->(m)", + props:[] + } + + $.each(chunk, function(_, ou){ + let guid = ou.Guid; + let properties = ou.Properties; + + queries.properties.props.push({guid:guid, map: properties}); + + let childous = ou.ChildOus; + $.each(childous, function(_, cou){ + queries.childous.props.push({parent: guid, child: cou}); + }) + + let computers = ou.Computers; + $.each(computers, function(_, computer){ + queries.computers.props.push({ou:guid, comp:computer}) + }) + + let users = ou.Users + $.each(users, function(_, user){ + queries.users.props.push({ou: guid, user:user}); + }); + }) + + return queries; +} + +export function buildSessionJson(chunk){ + let queries = {} + queries.sessions = { + statement:"UNWIND {props} AS prop MERGE (n:User {name:prop.user}) MERGE (m:Computer {name:prop.comp}) MERGE (m)-[r:HasSession {weight: prop.weight, isacl:false}]->(n)", + props: [] + } + + $.each(chunk, function(_, session){ + let name = session.UserName; + let comp = session.ComputerName; + let weight = session.Weight; + + queries.sessions.props.push({user: name, comp: comp, weight: weight}) + }) + return queries; +} + +export function buildGpoAdminJson(chunk){ + let queries = {} + + let baseQuery = "UNWIND {props} AS prop MERGE (n:{} {name:prop.admin}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:AdminTo {isacl:false}]->(m)" + $.each(chunk, function(_, gpoadmin){ + let comp = gpoadmin.Computer; + let admin = gpoadmin.Name; + let type = gpoadmin.Type; + + let query = baseQuery.format(type.toTitleCase()); + insert(queries, type, query, {admin: admin, comp:comp}) + }); + + return queries; +} + +export function buildUserJson(chunk){ + let queries = {} + + $.each(chunk, function(_, user){ + let name = user.Name; + let properties = user.Properties; + let primarygroup = user.PrimaryGroup; + + if (!queries.properties){ + if (primarygroup === null){ + queries.properties = { + statement:"UNWIND {props} AS prop MERGE (n:User {name:prop.name}) SET n += prop.map", + props:[] + } + }else{ + queries.properties = { + statement:"UNWIND {props} AS prop MERGE (n:User {name:prop.name}) MERGE (m:Group {name:prop.pg}) MERGE (n)-[r:MemberOf {isacl:false}]->(m) SET n += prop.map", + props:[] + } + } + } + + queries.properties.props.push({map:properties, name:name, pg: primarygroup}); + + let aces = user.Aces; + processAceArray(aces, name, "User", queries); + }); + return queries +} + +export function buildComputerJson(chunk){ + let queries = {} + let baseQuery = "UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) MERGE (m:{} {name:prop.target}) MERGE (m)-[r:{}]->(n)" + + $.each(chunk, function(_, comp){ + let name = comp.Name; + let properties = comp.Properties; + let localadmins = comp.LocalAdmins; + let rdpers = comp.RemoteDesktopUsers; + let primarygroup = comp.PrimaryGroup; + + if (!queries.properties){ + if (primarygroup === null){ + queries.properties = { + statement:"UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) SET n += prop.map", + props:[] + } + }else{ + queries.properties = { + statement:"UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) MERGE (m:Group {name:prop.pg}) MERGE (n)-[r:MemberOf]->(m) SET n += prop.map", + props:[] + } + } + } + + queries.properties.props.push({map:properties, name:name, pg: primarygroup}); + $.each(localadmins, function(_, admin){ + let aType = admin.Type; + let aName = admin.Name; + let rel = "AdminTo"; + + let hash = rel+aType; + + let statement = baseQuery.format(aType, rel); + let p = {name: name, target: aName}; + insert(queries,hash,statement,p); + }) + + $.each(rdpers, function(_, rdp){ + let aType = rdp.Type; + let aName = rdp.Name; + let rel = "CanRDP"; + + let hash = rel+aType; + + let statement = baseQuery.format(aType, rel); + let p = {name: name, target: aName}; + insert(queries,hash,statement,p); + }) + }); + return queries +} + +function insert(obj, hash, statement, prop){ + if (obj[hash]){ + obj[hash].props.push(prop) + }else{ + obj[hash] = {} + obj[hash].statement = statement; + obj[hash].props = [] + obj[hash].props.push(prop) + } +} + export function buildACLProps(rows) { var datadict = {}; From aec2a6f7489a20287b5268eabd6d13654fee5bd7 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 10:53:18 -0400 Subject: [PATCH 03/67] Login fix for old neo4j --- src/components/Float/Login.jsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/Float/Login.jsx b/src/components/Float/Login.jsx index fe8e839..2f9ff3b 100644 --- a/src/components/Float/Login.jsx +++ b/src/components/Float/Login.jsx @@ -161,7 +161,18 @@ export default class Login extends Component { onError: function(error){ btn.removeClass('activate'); var url = this.state.url.replace('bolt://','http://').replace('7687','7474'); - if (error.code === "Neo.ClientError.Security.CredentialsExpired"){ + //This block will trip for neo4j < 3.4 + 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 + }); + } + + if (error.code && error.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'); From 9e837087e4216177326abbf22b2d320493d4c84d Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 10:53:29 -0400 Subject: [PATCH 04/67] Remove old code --- package.json | 4 + src/components/Menu/MenuContainer.jsx | 8 +- .../SearchContainer/Tabs/ComputerNodeData.jsx | 4 +- .../Tabs/DatabaseDataDisplay.jsx | 2 +- .../SearchContainer/Tabs/DomainNodeData.jsx | 6 +- .../SearchContainer/Tabs/GpoNodeData.jsx | 2 +- .../SearchContainer/Tabs/GroupNodeData.jsx | 6 +- .../SearchContainer/Tabs/UserNodeData.jsx | 55 +- src/js/utils.js | 692 +++--------------- 9 files changed, 169 insertions(+), 610 deletions(-) diff --git a/package.json b/package.json index 2e4e887..c7e573e 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "linkurious": "^1.5.1", "mustache": "^2.3.0", "neo4j-driver": "^1.6.2", + "prop-types": "^15.6.2", "react": "^16.2.0", "react-bootstrap": "^0.32.0", "react-dom": "^16.2.0", @@ -68,5 +69,8 @@ "react-transition-group": "^2.2.1", "stream-json": "^1.1.0", "unzipper": "^0.8.9" + }, + "jshintConfig": { + "esversion": "6" } } diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index 98d41ca..dcdd117 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import MenuButton from './MenuButton'; import ProgressBarMenuButton from './ProgressBarMenuButton'; -import { buildGpoAdminJson, buildSessionJson, buildUserJson,buildComputerJson, buildDomainJson, buildGpoJson, buildGroupJson, buildOuJson, buildDomainProps, buildSessionProps, buildLocalAdminProps, buildGroupMembershipProps, buildACLProps, findObjectType, buildStructureProps, buildGplinkProps} from 'utils'; +import { buildGpoAdminJson, buildSessionJson, buildUserJson,buildComputerJson, buildDomainJson, buildGpoJson, buildGroupJson, buildOuJson} from 'utils'; import { If, Then, Else } from 'react-if'; const { dialog, app } = require('electron').remote; var fs = require('fs'); @@ -165,6 +165,12 @@ export default class MenuContainer extends Component { let sent = 0; let chunk = [] //Start a timer for fun + + this.setState({ + uploading: true, + progress: 0 + }); + console.time('IngestTime') pipeline.on('data', async function(data){ diff --git a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx index a1cb61d..04e37e7 100644 --- a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx +++ b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx @@ -86,9 +86,9 @@ export default class ComputerNodeData extends Component { (n))"} start={this.state.label} distinct />

Outbound Object Control

- (n) WHERE r.isACL=true"} start={this.state.label} distinct /> + (n) WHERE r.isacl=true"} start={this.state.label} distinct /> - (g:Group)-[r2]->(n) WHERE r2.isACL=true"} start={this.state.label} distinct /> + (g:Group)-[r2]->(n) WHERE r2.isacl=true"} start={this.state.label} distinct /> (n))"} start={this.state.label} distinct /> diff --git a/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx b/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx index 105d327..a6afda2 100644 --- a/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx +++ b/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx @@ -85,7 +85,7 @@ export default class DatabaseDataDisplay extends Component { s4.close(); }.bind(this)); - s6.run("MATCH ()-[r {isACL: true}]->() RETURN count(r)") + s6.run("MATCH ()-[r {isacl: true}]->() RETURN count(r)") .then(function(result){ this.setState({'num_acls':result.records[0]._fields[0].low}); s6.close(); diff --git a/src/components/SearchContainer/Tabs/DomainNodeData.jsx b/src/components/SearchContainer/Tabs/DomainNodeData.jsx index d6282cb..40c78f3 100644 --- a/src/components/SearchContainer/Tabs/DomainNodeData.jsx +++ b/src/components/SearchContainer/Tabs/DomainNodeData.jsx @@ -140,7 +140,7 @@ export default class DomainNodeData extends Component { (b))"} /> - (b) WHERE r.isACL=true"} /> + (b) WHERE r.isacl=true"} />

Inbound Trusts

@@ -154,9 +154,9 @@ export default class DomainNodeData extends Component {

Inbound Controllers

- (u:Domain {name: {name}}) WHERE r.isACL=true"} distinct /> + (u:Domain {name: {name}}) WHERE r.isacl=true"} distinct /> - (g:Group)-[r1]->(u:Domain {name: {name}}) WHERE r1.isACL=true"} distinct /> + (g:Group)-[r1]->(u:Domain {name: {name}}) WHERE r1.isacl=true"} distinct /> (u:Domain {name: {name}})) WHERE NOT n.name={name}"} distinct /> diff --git a/src/components/SearchContainer/Tabs/GpoNodeData.jsx b/src/components/SearchContainer/Tabs/GpoNodeData.jsx index 7e9321b..2bee998 100644 --- a/src/components/SearchContainer/Tabs/GpoNodeData.jsx +++ b/src/components/SearchContainer/Tabs/GpoNodeData.jsx @@ -53,7 +53,7 @@ export default class GpoNodeData extends Component { (g:GPO {name:{name}})"} end={this.state.label} distinct /> - (g1:Group)-[r1]->(g2:GPO {name: {name}}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = g2.name) AND NOT n.name = g2.name AND r1.isACL=true"} end={this.state.label} distinct /> + (g1:Group)-[r1]->(g2:GPO {name: {name}}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = g2.name) AND NOT n.name = g2.name AND r1.isacl=true"} end={this.state.label} distinct /> (g:GPO {name:{name}}))"} end={this.state.label} distinct /> diff --git a/src/components/SearchContainer/Tabs/GroupNodeData.jsx b/src/components/SearchContainer/Tabs/GroupNodeData.jsx index 2968433..b78ab7b 100644 --- a/src/components/SearchContainer/Tabs/GroupNodeData.jsx +++ b/src/components/SearchContainer/Tabs/GroupNodeData.jsx @@ -66,9 +66,9 @@ export default class GroupNodeData extends Component {

Outbound Object Control

- (n) WHERE r.isACL=true"} start={this.state.label} distinct /> + (n) WHERE r.isacl=true"} start={this.state.label} distinct /> - (g2:Group)-[r2]->(n) WHERE r2.isACL=true"} start={this.state.label} distinct /> + (g2:Group)-[r2]->(n) WHERE r2.isacl=true"} start={this.state.label} distinct /> (n))"} start={this.state.label} distinct /> @@ -76,7 +76,7 @@ export default class GroupNodeData extends Component { (g:Group {name:{name}})"} end={this.state.label} distinct /> - (g1:Group)-[r1]->(g2:Group {name: {name}}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = g2.name) AND NOT n.name = g2.name AND r1.isACL=true"} end={this.state.label} distinct /> + (g1:Group)-[r1]->(g2:Group {name: {name}}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = g2.name) AND NOT n.name = g2.name AND r1.isacl=true"} end={this.state.label} distinct /> (g:Group {name:{name}}))"} end={this.state.label} distinct /> diff --git a/src/components/SearchContainer/Tabs/UserNodeData.jsx b/src/components/SearchContainer/Tabs/UserNodeData.jsx index 1cf31eb..0d6104c 100644 --- a/src/components/SearchContainer/Tabs/UserNodeData.jsx +++ b/src/components/SearchContainer/Tabs/UserNodeData.jsx @@ -1,30 +1,27 @@ import React, { Component } from 'react'; -import NodePropItem from './NodePropItem'; import PropTypes from 'prop-types'; import NodeProps from './NodeProps'; import NodeCypherLink from './NodeCypherLink'; import NodeCypherNoNumberLink from './NodeCypherNoNumberLink'; import NodeCypherLinkComplex from './NodeCypherLinkComplex'; -import { If, Then, Else } from 'react-if'; - export default class UserNodeData extends Component { - constructor(){ + constructor() { super(); this.state = { label: "", - driversessions : [], + driversessions: [], propertyMap: {}, ServicePrincipalNames: [], - displayMap: {"DisplayName":"Display Name", "PwdLastSet":"Password Last Changed", "LastLogon": "Last Logon", "Enabled":"Enabled","Email":"Email", "Title":"Title", "HomeDir":"Home Directory"} + displayMap: { "displayname": "Display Name", "pwdlastset": "Password Last Changed", "lastlogon": "Last Logon", "enabled": "Enabled", "email": "Email", "title": "Title", "homedirectory": "Home Directory", "description": "Description", "userpassword": "User Password", "admincount": "AdminCount" } }; emitter.on('userNodeClicked', this.getNodeData.bind(this)); } - getNodeData(payload){ - $.each(this.state.driversessions,function(index, record){ + getNodeData(payload) { + $.each(this.state.driversessions, function (index, record) { record.close(); }); @@ -36,18 +33,18 @@ export default class UserNodeData extends Component { }); var props = driver.session(); - props.run("MATCH (n:User {name:{name}}) RETURN n", {name: payload}) - .then(function(result){ + props.run("MATCH (n:User {name:{name}}) RETURN n", { name: payload }) + .then(function (result) { var properties = result.records[0]._fields[0].properties; - if (typeof properties.ServicePrincipalNames === 'undefined'){ - this.setState({ServicePrincipalNames: []}); - }else{ + if (typeof properties.ServicePrincipalNames === 'undefined') { + this.setState({ ServicePrincipalNames: [] }); + } else { this.setState({ ServicePrincipalNames: properties.ServicePrincipalNames }); } - this.setState({propertyMap: properties}); + this.setState({ propertyMap: properties }); props.close(); }.bind(this)); - this.setState({driversessions:[props]}); + this.setState({ driversessions: [props] }); } render() { @@ -65,7 +62,7 @@ export default class UserNodeData extends Component { {this.state.label} - + (n:User {name:{name}})"} end={this.state.label} /> @@ -74,15 +71,15 @@ export default class UserNodeData extends Component { (container1)-[r2:Contains*1..]->(c) OPTIONAL MATCH p2 = (g2:GPO)-[r3:GpLink {enforced:false}]->(container2)-[r4:Contains*1..]->(c) WHERE NONE (x in NODES(p2) WHERE x.blocksInheritance = true AND x:OU AND NOT (g2)-->(x)) RETURN count(g1)+count(g2)"} graphQuery={"MATCH (c:User {name:{name}}) OPTIONAL MATCH p1 = (g1:GPO)-[r1:GpLink {enforced:true}]->(container1)-[r2:Contains*1..]->(c) OPTIONAL MATCH p2 = (g2:GPO)-[r3:GpLink {enforced:false}]->(container2)-[r4:Contains*1..]->(c) WHERE NONE (x in NODES(p2) WHERE x.blocksInheritance = true AND x:OU AND NOT (g2)-->(x)) RETURN p1,p2"} /> - +

Group Membership

- + (n)"} start={this.state.label} /> - + (n:Group)"} start={this.state.label} distinct /> - + (n)"} start={this.state.label} /> - +

Local Admin Rights

@@ -92,23 +89,23 @@ export default class UserNodeData extends Component { (g:Group)-[r2:AdminTo]->(n:Computer)"} start={this.state.label} distinct /> (n:Computer))"} start={this.state.label} distinct /> - +

Outbound Object Control

- (n) WHERE r1.isACL=true"} end={this.state.label} distinct /> + (n) WHERE r1.isacl=true"} end={this.state.label} distinct /> + + (g:Group)-[r2]->(n) WHERE r2.isacl=true"} start={this.state.label} distinct /> - (g:Group)-[r2]->(n) WHERE r2.isACL=true"} start={this.state.label} distinct /> - (n))"} start={this.state.label} distinct /> - +

Inbound Object Control

- (u1:User {name: {name}}) WHERE r.isACL=true"} end={this.state.label} distinct /> + (u1:User {name: {name}}) WHERE r.isacl=true"} end={this.state.label} distinct /> (g:Group)-[r1:AddMember|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(u:User {name: {name}}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = u.name) AND NOT n.name = u.name"} end={this.state.label} distinct /> - + (u1:User {name: {name}}))"} end={this.state.label} distinct /> @@ -117,5 +114,5 @@ export default class UserNodeData extends Component { } UserNodeData.propTypes = { - visible : PropTypes.bool.isRequired + visible: PropTypes.bool.isRequired }; diff --git a/src/js/utils.js b/src/js/utils.js index 4788a9e..caa1579 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -22,8 +22,8 @@ export function findGraphPath(sigmaInstance, reverse, nodeid, traversed) { 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) { + $.each(nodes, function (index, node) { + $.each(edges, function (index, edge) { var check = reverse ? edge.source : edge.target; //If an edge is pointing in the right direction, set its color //Push the edge into our store and then @@ -40,15 +40,15 @@ export function findGraphPath(sigmaInstance, reverse, nodeid, traversed) { } } -export function clearSessions(){ +export function clearSessions() { emitter.emit('openClearingModal'); deleteSessions(); } -function deleteSessions(){ +function deleteSessions() { var session = driver.session(); session.run("MATCH ()-[r:HasSession]-() WITH r LIMIT 100000 DELETE r RETURN count(r)") - .then(function(results) { + .then(function (results) { session.close(); emitter.emit("refreshDBData"); var count = results.records[0]._fields[0].low; @@ -68,7 +68,7 @@ export function clearDatabase() { function deleteEdges() { var session = driver.session(); session.run("MATCH ()-[r]-() WITH r LIMIT 100000 DELETE r RETURN count(r)") - .then(function(results) { + .then(function (results) { emitter.emit("refreshDBData"); session.close(); var count = results.records[0]._fields[0].low; @@ -83,7 +83,7 @@ function deleteEdges() { function deleteNodes() { var session = driver.session(); session.run("MATCH (n) WITH n LIMIT 100000 DELETE n RETURN count(n)") - .then(function(results) { + .then(function (results) { emitter.emit("refreshDBData"); session.close(); var count = results.records[0]._fields[0].low; @@ -95,12 +95,12 @@ function deleteNodes() { }); } -function grabConstraints(){ +function grabConstraints() { var session = driver.session(); let constraints = []; session.run("CALL db.constraints") - .then(function(results){ - $.each(results.records, function(index, container){ + .then(function (results) { + $.each(results.records, function (index, container) { let constraint = container._fields[0]; let query = "DROP " + constraint; constraints.push(query); @@ -112,21 +112,21 @@ function grabConstraints(){ }); } -function dropConstraints(constraints){ - if (constraints.length > 0){ +function dropConstraints(constraints) { + if (constraints.length > 0) { let constraint = constraints.shift(); let session = driver.session(); session.run(constraint) - .then(function(){ + .then(function () { dropConstraints(constraints); session.close(); }); - }else{ + } else { grabIndexes(); } } -function grabIndexes(){ +function grabIndexes() { var session = driver.session(); let constraints = []; @@ -144,7 +144,7 @@ function grabIndexes(){ }); } -function dropIndexes(indexes){ +function dropIndexes(indexes) { if (indexes.length > 0) { let constraint = indexes.shift(); let session = driver.session(); @@ -158,7 +158,7 @@ function dropIndexes(indexes){ } } -function addConstraints(){ +function addConstraints() { var s1 = driver.session(); var s2 = driver.session(); var s3 = driver.session(); @@ -212,434 +212,62 @@ function addConstraints(){ emitter.emit('hideDBClearModal'); } -export function findObjectType(header){ - if (header.includes('UserName') && header.includes('ComputerName') && header.includes('Weight')){ - return 'sessions'; - }else if (header.includes('AccountName') && header.includes('AccountType') && header.includes('GroupName')){ - return 'groupmembership'; - }else if (header.includes('AccountName') && header.includes('AccountType') && header.includes('ComputerName')){ - return 'localadmin'; - }else if (header.includes('SourceDomain') && header.includes('TargetDomain') && header.includes('TrustDirection') && header.includes('TrustType') && header.includes('Transitive')){ - return 'domain'; - }else if (header.includes('ActiveDirectoryRights') && header.includes('ObjectType') && header.includes('PrincipalType') && header.includes('PrincipalName') && header.includes('ObjectName') && header.includes('ACEType') && header.includes('AccessControlType') && header.includes('IsInherited')){ - return 'acl'; - }else if (header.includes('AccountName') && header.includes('Enabled') && header.includes('PwdLastSet') && header.includes('LastLogon') && header.includes('Sid') && header.includes('SidHistory') && header.includes('HasSPN') && header.includes('ServicePrincipalNames')){ - return 'userprops'; - }else if (header.includes('AccountName') && header.includes('Enabled') && header.includes('PwdLastSet') && header.includes('LastLogon') && header.includes('OperatingSystem') && header.includes('Sid')){ - return 'compprops'; - } else if (header.includes('ContainerType') && header.includes('ContainerName') && header.includes('ContainerGUID') && header.includes('ContainerBlocksInheritance') && header.includes('ObjectType') && header.includes('ObjectName') && header.includes('ObjectId')){ - return 'structure'; - } else if (header.includes('ObjectType') && header.includes('ObjectName') && header.includes('ObjectGUID') && header.includes('GPODisplayName') && header.includes('GPOGuid') && header.includes('IsEnforced')){ - return 'gplink'; - }else{ - return 'unknown'; - } -} - -function getDomainFromLabel(label, type){ - if (type === 'Domain'){ - return label; - } - if (label.includes('@')){ - return label.split('@').pop(); - }else{ - let d = label.split('.'); - d.shift(); - return d.join('.'); - } -} - -export function buildGroupMembershipProps(rows) { - var datadict = {}; - - $.each(rows, function (index, row) { - let type = row.AccountType.toTitleCase(); - let account = row.AccountName.toUpperCase(); - let group = row.GroupName.toUpperCase(); - - if (datadict[type]){ - datadict[type].props.push({ - accountName: account, - accountDomain: getDomainFromLabel(account, type), - groupName: group, - groupDomain: getDomainFromLabel(group, "group") - }); - }else{ - datadict[type] = { - statement: `UNWIND {props} AS prop MERGE (a:{} {name:prop.accountName}) WITH a,prop MERGE (b:Group {name:prop.groupName}) WITH a,b,prop MERGE (a)-[r:MemberOf {isACL:false}]->(b) SET a.domain=prop.accountDomain,b.domain=prop.groupDomain`.format(type), - props:[{ - accountName: account, - accountDomain: getDomainFromLabel(account, type), - groupName: group, - groupDomain: getDomainFromLabel(group, "group") - }] - }; - } - }); - - return datadict; -} - -export function buildLocalAdminProps(rows) { - var datadict = {}; - - $.each(rows, function (index, row) { - let type = row.AccountType.toTitleCase(); - let account = row.AccountName.toUpperCase(); - let computer = row.ComputerName.toUpperCase(); - - if (datadict[type]) { - datadict[type].props.push({ - accountName: account, - accountDomain: getDomainFromLabel(account, type), - computerName: computer, - computerDomain: getDomainFromLabel(computer, "computer") - }); - } else { - datadict[type] = { - statement: `UNWIND {props} AS prop MERGE (a:{} {name:prop.accountName}) WITH a,prop MERGE (b:Computer {name:prop.computerName}) WITH a,b,prop MERGE (a)-[r:AdminTo {isACL: false}]->(b) SET a.domain=prop.accountDomain, b.domain=prop.computerDomain`.format(type), - props: [{ - accountName: account, - accountDomain: getDomainFromLabel(account, type), - computerName: computer, - computerDomain: getDomainFromLabel(computer, "computer") - }] - }; - } - }); - - return datadict; -} - -export function buildSessionProps(rows) { - var datadict = {}; - - $.each(rows, function (index, row) { - let account = row.UserName.toUpperCase(); - let computer = row.ComputerName.toUpperCase(); - - if (datadict['user']) { - datadict['user'].props.push({ - accountName: account, - accountDomain: getDomainFromLabel(account, "user"), - computerName: computer, - computerDomain: getDomainFromLabel(computer, "computer"), - weight: row.Weight - }); - } else { - datadict['user'] = { - statement: `UNWIND {props} AS prop MERGE (a:User {name:prop.accountName}) WITH a,prop MERGE (b:Computer {name: prop.computerName}) WITH a,b,prop MERGE (b)-[:HasSession {Weight : prop.weight, isACL:false}]-(a) SET a.domain=prop.accountDomain,b.domain=prop.computerDomain`, - props: [{ - accountName: account, - accountDomain: getDomainFromLabel(account, "user"), - computerName: computer, - computerDomain: getDomainFromLabel(computer, "computer"), - weight: row.Weight - }] - }; - } - }); - - return datadict; -} - -export function buildDomainProps(rows) { - var datadict = {}; - - datadict['domain'] = { - statement: '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) SET domain1.domain=prop.domain1,domain2.domain=prop.domain2', - props: [] - }; - - $.each(rows, function (index, row) { - let type = row.TrustDirection; - let domaina = row.TargetDomain.toUpperCase(); - let domainb = row.SourceDomain.toUpperCase(); - - switch (type){ - case 'Inbound': - datadict['domain'].props.push({ - domain1: domaina, - domain2: domainb, - trusttype: row.TrustType, - transitive: row.Transitive - }); - break; - case 'Outbound': - datadict['domain'].props.push({ - domain1: domainb, - domain2: domaina, - trusttype: row.TrustType, - transitive: row.Transitive - }); - break; - case 'Bidirectional': - datadict['domain'].props.push({ - domain1: domaina, - domain2: domainb, - trusttype: row.TrustType, - transitive: row.Transitive - }); - datadict['domain'].props.push({ - domain1: domainb, - domain2: domaina, - trusttype: row.TrustType, - transitive: row.Transitive - }); - break; - } - }); - - return datadict; -} - -export function buildStructureProps(rows){ - let datadict = {}; - - $.each(rows, function(index, row){ - let hash = (row.ContainerType + row.ObjectType).toUpperCase(); - let atype = row.ContainerType; - let btype = row.ObjectType; - let aguid = row.ContainerGUID; - let bguid = row.ObjectId; - - if (atype === 'ou'){ - atype = 'OU'; - }else{ - atype = atype.toTitleCase(); - } - - if (btype === 'ou') { - btype = 'OU'; - } else { - btype = btype.toTitleCase(); - } - - let container = row.ContainerName.toUpperCase(); - let object = row.ObjectName.toUpperCase(); - - if (atype === 'OU' && btype === 'OU') { - if (datadict[hash]){ - datadict[hash].props.push({ - container: container, - object: object, - containerDomain: getDomainFromLabel(container, atype), - objectDomain: getDomainFromLabel(object, btype), - blocksInheritance: row.ContainerBlocksInheritance, - containerGuid: aguid, - objectGuid: bguid - }); - }else{ - datadict[hash] = { - statement: 'UNWIND {props} AS prop MERGE (a:OU {guid:prop.containerGuid}) WITH a,prop MERGE (b:OU {guid:prop.objectGuid}) WITH a,b,prop MERGE (a)-[r:Contains {isACL: false}]->(b) SET a.blocksInheritance=toBoolean(prop.blocksInheritance), a.domain=prop.containerDomain, b.domain=prop.objectDomain, a.name=prop.container, b.name=prop.object', - props: [{ - container: container, - object: object, - containerDomain: getDomainFromLabel(container, atype), - objectDomain: getDomainFromLabel(object, btype), - blocksInheritance: row.ContainerBlocksInheritance, - containerGuid: aguid, - objectGuid: bguid - }] - }; - } - }else if (atype === 'OU'){ - if (datadict[hash]) { - datadict[hash].props.push({ - container: container, - object: object, - containerDomain: getDomainFromLabel(container, "OU"), - objectDomain: getDomainFromLabel(object, btype), - blocksInheritance: row.ContainerBlocksInheritance, - containerGuid: row.ContainerGUID - }); - } else { - datadict[hash] = { - statement: 'UNWIND {props} AS prop MERGE (a:OU {guid:prop.containerGuid}) WITH a,prop MERGE (b:{} {name:prop.object}) WITH a,b,prop MERGE (a)-[r:Contains {isACL: false}]->(b) SET a.blocksInheritance=toBoolean(prop.blocksInheritance), a.domain=prop.containerDomain, b.domain=prop.objectDomain, a.name=prop.container'.format(btype), - props: [{ - container: container, - object: object, - containerDomain: getDomainFromLabel(container, "OU"), - objectDomain: getDomainFromLabel(object, btype), - blocksInheritance: row.ContainerBlocksInheritance, - containerGuid: row.ContainerGUID - }] - }; - } - }else if (btype === 'OU'){ - if (datadict[hash]) { - datadict[hash].props.push({ - container: container, - object: object, - containerDomain: getDomainFromLabel(container, atype), - objectDomain: getDomainFromLabel(object, "OU"), - blocksInheritance: row.ContainerBlocksInheritance, - objectGuid: row.ObjectId - }); - } else { - datadict[hash] = { - statement: 'UNWIND {props} AS prop MERGE (a:{} {name:prop.container}) WITH a,prop MERGE (b:OU {guid:prop.objectGuid}) WITH a,b,prop MERGE (a)-[r:Contains {isACL: false}]->(b) SET a.blocksInheritance=toBoolean(prop.blocksInheritance), a.domain=prop.containerDomain, b.domain=prop.objectDomain, b.name=prop.object'.format(atype), - props: [{ - container: container, - object: object, - containerDomain: getDomainFromLabel(container, atype), - objectDomain: getDomainFromLabel(object, "OU"), - blocksInheritance: row.ContainerBlocksInheritance, - objectGuid: row.ObjectId - }] - }; - } - }else{ - if (datadict[hash]) { - datadict[hash].props.push({ - container: container, - object: object, - containerDomain: getDomainFromLabel(container, atype), - objectDomain: getDomainFromLabel(object, btype), - blocksInheritance: row.ContainerBlocksInheritance - }); - } else { - datadict[hash] = { - statement: 'UNWIND {props} AS prop MERGE (a:{} {name:prop.container}) WITH a,prop MERGE (b:{} {name: prop.object}) WITH a,b,prop MERGE (a)-[r:Contains {isACL: false}]->(b) SET a.blocksInheritance=toBoolean(prop.blocksInheritance), a.domain=prop.containerDomain, b.domain=prop.objectDomain'.format(atype, btype), - props: [{ - container: container, - object: object, - containerDomain: getDomainFromLabel(container, atype), - objectDomain: getDomainFromLabel(object, btype), - blocksInheritance: row.ContainerBlocksInheritance - }] - }; - } - } - }); - - return datadict; -} - -export function buildGplinkProps(rows){ - let datadict = {}; - - $.each(rows, function (index, row) { - let type = row.ObjectType; - let gpoName = row.GPODisplayName.toUpperCase(); - let objectName = row.ObjectName.toUpperCase(); - - if (type === 'ou') { - type = 'OU'; - } else { - type = type.toTitleCase(); - } - - if (type === 'OU'){ - if (datadict[type]) { - datadict[type].props.push({ - gponame: gpoName, - objectname: objectName, - enforced: row.IsEnforced, - gpoDomain: getDomainFromLabel(gpoName, "GPO"), - objectDomain: getDomainFromLabel(objectName, "OU"), - objectGuid: row.ObjectGUID, - gpoGuid: row.GPOGuid - }); - } else { - datadict[type] = { - statement: 'UNWIND {props} as prop MERGE (a:GPO {name: prop.gponame}) WITH a,prop MERGE (b:OU {guid: prop.objectGuid}) WITH a,b,prop MERGE (a)-[r:GpLink {enforced: toBoolean(prop.enforced), isACL: false}]->(b) SET a.guid=prop.gpoGuid,b.name=prop.objectName,a.domain=prop.gpoDomain,b.domain=prop.objectDomain', - props: [{ - gponame: gpoName, - objectname: objectName, - enforced: row.IsEnforced, - gpoDomain: getDomainFromLabel(gpoName, "GPO"), - objectDomain: getDomainFromLabel(objectName, "OU"), - objectGuid: row.ObjectGUID, - gpoGuid: row.GPOGuid - }] - }; - } - }else{ - if (datadict[type]) { - datadict[type].props.push({ - gponame: gpoName, - objectname: objectName, - enforced: row.IsEnforced, - gpoDomain: getDomainFromLabel(gpoName, "GPO"), - objectDomain: getDomainFromLabel(objectName, type), - gpoGuid: row.GPOGuid - }); - } else { - datadict[type] = { - statement: 'UNWIND {props} as prop MERGE (a:GPO {name: prop.gponame}) WITH a,prop MERGE (b:{} {name: prop.objectname}) WITH a,b,prop MERGE (a)-[r:GpLink {enforced: toBoolean(prop.enforced), isACL: false}]->(b) SET a.guid=prop.gpoGuid,a.domain=prop.gpoDomain,b.domain=prop.objectDomain'.format(type), - props: [{ - gponame: gpoName, - objectname: objectName, - enforced: row.IsEnforced, - gpoDomain: getDomainFromLabel(gpoName, "GPO"), - objectDomain: getDomainFromLabel(objectName, type), - gpoGuid: row.GPOGuid - }] - }; - } - } - - }); - return datadict; -} - -function processAceArray(array, objname, objtype, output){ +function processAceArray(array, objname, objtype, output) { let baseAceQuery = 'UNWIND {props} AS prop MERGE (a:{} {name:prop.principal}) MERGE (b:{} {name: prop.obj}) MERGE (a)-[r:{} {isacl:true}]->(b)' - $.each(array, function(_, ace){ + $.each(array, function (_, ace) { let principal = ace.PrincipalName; let principaltype = ace.PrincipalType; let right = ace.RightName; let acetype = ace.AceType; - if (objname === principal){ + if (objname === principal) { return; } - + let rights = [] //Process the right/type to figure out the ACEs we need to add - if (acetype === 'All'){ + if (acetype === 'All') { rights.push('AllExtendedRights'); - }else if (acetype === 'User-Force-Change-Password'){ + } else if (acetype === 'User-Force-Change-Password') { rights.push('ForceChangePassword'); - }else if (acetype === 'Member'){ + } else if (acetype === 'Member') { rights.push('AddMember'); - }else if (right === 'ExtendedRight'){ + } else if (right === 'ExtendedRight') { rights.push(acetype); } - if (right.includes('GenericAll')){ + if (right.includes('GenericAll')) { rights.push('GenericAll'); } - if (right.includes('WriteDacl')){ + if (right.includes('WriteDacl')) { rights.push('WriteDacl'); } - if (right.includes('WriteOwner')){ + if (right.includes('WriteOwner')) { rights.push('WriteOwner'); } - if (right.includes('GenericWrite')){ + if (right.includes('GenericWrite')) { rights.push('GenericWrite'); } - if (right === 'Owner'){ + if (right === 'Owner') { rights.push('Owns'); } - $.each(rights, function(_, right){ + $.each(rights, function (_, right) { let hash = right + principaltype; let formatted = baseAceQuery.format(principaltype.toTitleCase(), objtype, right); - insert(output, hash, formatted, {principal:principal,obj:objname}); + insert(output, hash, formatted, { principal: principal, obj: objname }); }) }) } -export function buildDomainJson(chunk){ +export function buildDomainJson(chunk) { let queries = {} queries.properties = { statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.name}) SET n += prop.map", @@ -657,89 +285,89 @@ export function buildDomainJson(chunk){ } queries.childous = { - statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:OU {guid:prop.guid}) MERGE (n)-[r:Contains]->(m)", - props : [] + statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:OU {guid:prop.guid}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + props: [] } queries.computers = { - statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:Contains]->(m)", - props:[] + statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + props: [] } queries.users = { - statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:User {name:prop.user}) MERGE (n)-[r:Contains]->(m)", - props:[] + statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:User {name:prop.user}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + props: [] } - $.each(chunk, function(_, domain){ + $.each(chunk, function (_, domain) { let name = domain.Name; let properties = domain.Properties; - queries.properties.props.push({map:properties, name:name}); - + queries.properties.props.push({ map: properties, name: name }); + let links = domain.Links; - $.each(links, function(_, link){ + $.each(links, function (_, link) { let enforced = link.IsEnforced; let target = link.Name; - queries.links.props.push({domain:name, gpo:target,enforced:enforced}); + queries.links.props.push({ domain: name, gpo: target, enforced: enforced }); }); let trusts = domain.Trusts; - $.each(trusts, function(_, trust){ + $.each(trusts, function (_, trust) { let target = trust.TargetName; let transitive = trust.IsTransitive; let direction = trust.TrustDirection; let type = trust.TrustType; - switch (direction){ + switch (direction) { case 0: - queries.trusts.props.push({a: target, b: name, transitive: transitive, trusttype: type}); + queries.trusts.props.push({ a: target, b: name, transitive: transitive, trusttype: type }); break; case 1: - queries.trusts.props.push({a: name, b: target, transitive: transitive, trusttype: type}); + queries.trusts.props.push({ a: name, b: target, transitive: transitive, trusttype: type }); break; case 2: - queries.trusts.props.push({a: name, b: target, transitive: transitive, trusttype: type}); - queries.trusts.props.push({a: target, b: name, transitive: transitive, trusttype: type}); + queries.trusts.props.push({ a: name, b: target, transitive: transitive, trusttype: type }); + queries.trusts.props.push({ a: target, b: name, transitive: transitive, trusttype: type }); break; } }); let aces = domain.Aces; - processAceArray(aces, name, "Domain", queries); + processAceArray(aces, name, "Domain", queries); let childous = domain.ChildOus; - $.each(childous, function(_, ou){ - queries.childous.props.push({domain:name, guid:ou}) + $.each(childous, function (_, ou) { + queries.childous.props.push({ domain: name, guid: ou }) }) let comps = domain.Computers; - $.each(comps, function(_, computer){ - queries.computers.props.push({domain:name, comp:computer}) + $.each(comps, function (_, computer) { + queries.computers.props.push({ domain: name, comp: computer }) }) let users = domain.Users - $.each(users, function(_, user){ - queries.users.props.push({domain: name, user:user}); + $.each(users, function (_, user) { + queries.users.props.push({ domain: name, user: user }); }); }); - + return queries; } -export function buildGpoJson(chunk){ +export function buildGpoJson(chunk) { let queries = {} queries.properties = { statement: "UNWIND {props} AS prop MERGE (n:GPO {name:prop.name}) SET n.guid=prop.guid", props: [] } - $.each(chunk, function(_, gpo){ + $.each(chunk, function (_, gpo) { let name = gpo.Name; let guid = gpo.Guid; - queries.properties.props.push({name:name, guid:guid}); + queries.properties.props.push({ name: name, guid: guid }); let aces = gpo.Aces; processAceArray(aces, name, "GPO", queries); @@ -748,38 +376,38 @@ export function buildGpoJson(chunk){ return queries; } -export function buildGroupJson(chunk){ +export function buildGroupJson(chunk) { let queries = {} queries.properties = { statement: "UNWIND {props} AS prop MERGE (n:Group {name:prop.name}) SET n += prop.map", props: [] } - let baseStatement = "UNWIND {props} AS prop MERGE (n:Group {name: prop.name}) MERGE (m:{} {name:prop.member}) MERGE (m)-[r:MemberOf]->(n)"; + let baseStatement = "UNWIND {props} AS prop MERGE (n:Group {name: prop.name}) MERGE (m:{} {name:prop.member}) MERGE (m)-[r:MemberOf {isacl:false}]->(n)"; - $.each(chunk, function(_, group){ + $.each(chunk, function (_, group) { let name = group.Name; let properties = group.Properties; - queries.properties.props.push({map:properties, name:name}); + queries.properties.props.push({ map: properties, name: name }); let aces = group.Aces; processAceArray(aces, name, "Group", queries); let members = group.Members; - $.each(members, function(_, member){ + $.each(members, function (_, member) { let mname = member.MemberName; let mtype = member.MemberType; let statement = baseStatement.format(mtype.toTitleCase()) - insert(queries, mtype, statement, {name: name, member: mname}) + insert(queries, mtype, statement, { name: name, member: mname }) }); }); return queries } -export function buildOuJson(chunk){ +export function buildOuJson(chunk) { let queries = {}; queries.properties = { @@ -788,101 +416,101 @@ export function buildOuJson(chunk){ } queries.childous = { - statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.parent}) MERGE (m:OU {guid:prop.child}) MERGE (n)-[r:Contains]->(m)", - props : [] + statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.parent}) MERGE (m:OU {guid:prop.child}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + props: [] } queries.computers = { - statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.ou}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:Contains]->(m)", - props:[] + statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.ou}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + props: [] } queries.users = { - statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.ou}) MERGE (m:User {name:prop.user}) MERGE (n)-[r:Contains]->(m)", - props:[] + statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.ou}) MERGE (m:User {name:prop.user}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + props: [] } - $.each(chunk, function(_, ou){ + $.each(chunk, function (_, ou) { let guid = ou.Guid; let properties = ou.Properties; - queries.properties.props.push({guid:guid, map: properties}); + queries.properties.props.push({ guid: guid, map: properties }); let childous = ou.ChildOus; - $.each(childous, function(_, cou){ - queries.childous.props.push({parent: guid, child: cou}); + $.each(childous, function (_, cou) { + queries.childous.props.push({ parent: guid, child: cou }); }) let computers = ou.Computers; - $.each(computers, function(_, computer){ - queries.computers.props.push({ou:guid, comp:computer}) + $.each(computers, function (_, computer) { + queries.computers.props.push({ ou: guid, comp: computer }) }) let users = ou.Users - $.each(users, function(_, user){ - queries.users.props.push({ou: guid, user:user}); + $.each(users, function (_, user) { + queries.users.props.push({ ou: guid, user: user }); }); }) return queries; } -export function buildSessionJson(chunk){ +export function buildSessionJson(chunk) { let queries = {} queries.sessions = { - statement:"UNWIND {props} AS prop MERGE (n:User {name:prop.user}) MERGE (m:Computer {name:prop.comp}) MERGE (m)-[r:HasSession {weight: prop.weight, isacl:false}]->(n)", + statement: "UNWIND {props} AS prop MERGE (n:User {name:prop.user}) MERGE (m:Computer {name:prop.comp}) MERGE (m)-[r:HasSession {weight: prop.weight, isacl:false}]->(n)", props: [] } - $.each(chunk, function(_, session){ + $.each(chunk, function (_, session) { let name = session.UserName; let comp = session.ComputerName; let weight = session.Weight; - queries.sessions.props.push({user: name, comp: comp, weight: weight}) + queries.sessions.props.push({ user: name, comp: comp, weight: weight }) }) return queries; } -export function buildGpoAdminJson(chunk){ +export function buildGpoAdminJson(chunk) { let queries = {} - + let baseQuery = "UNWIND {props} AS prop MERGE (n:{} {name:prop.admin}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:AdminTo {isacl:false}]->(m)" - $.each(chunk, function(_, gpoadmin){ + $.each(chunk, function (_, gpoadmin) { let comp = gpoadmin.Computer; let admin = gpoadmin.Name; let type = gpoadmin.Type; let query = baseQuery.format(type.toTitleCase()); - insert(queries, type, query, {admin: admin, comp:comp}) + insert(queries, type, query, { admin: admin, comp: comp }) }); return queries; } -export function buildUserJson(chunk){ +export function buildUserJson(chunk) { let queries = {} - $.each(chunk, function(_, user){ + $.each(chunk, function (_, user) { let name = user.Name; let properties = user.Properties; let primarygroup = user.PrimaryGroup; - if (!queries.properties){ - if (primarygroup === null){ + if (!queries.properties) { + if (primarygroup === null) { queries.properties = { - statement:"UNWIND {props} AS prop MERGE (n:User {name:prop.name}) SET n += prop.map", - props:[] + statement: "UNWIND {props} AS prop MERGE (n:User {name:prop.name}) SET n += prop.map", + props: [] } - }else{ + } else { queries.properties = { - statement:"UNWIND {props} AS prop MERGE (n:User {name:prop.name}) MERGE (m:Group {name:prop.pg}) MERGE (n)-[r:MemberOf {isacl:false}]->(m) SET n += prop.map", - props:[] + statement: "UNWIND {props} AS prop MERGE (n:User {name:prop.name}) MERGE (m:Group {name:prop.pg}) MERGE (n)-[r:MemberOf {isacl:false}]->(m) SET n += prop.map", + props: [] } } } - queries.properties.props.push({map:properties, name:name, pg: primarygroup}); + queries.properties.props.push({ map: properties, name: name, pg: primarygroup }); let aces = user.Aces; processAceArray(aces, name, "User", queries); @@ -890,63 +518,63 @@ export function buildUserJson(chunk){ return queries } -export function buildComputerJson(chunk){ +export function buildComputerJson(chunk) { let queries = {} - let baseQuery = "UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) MERGE (m:{} {name:prop.target}) MERGE (m)-[r:{}]->(n)" + let baseQuery = "UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) MERGE (m:{} {name:prop.target}) MERGE (m)-[r:{} {isacl: false}]->(n)" - $.each(chunk, function(_, comp){ + $.each(chunk, function (_, comp) { let name = comp.Name; let properties = comp.Properties; let localadmins = comp.LocalAdmins; let rdpers = comp.RemoteDesktopUsers; let primarygroup = comp.PrimaryGroup; - if (!queries.properties){ - if (primarygroup === null){ + if (!queries.properties) { + if (primarygroup === null) { queries.properties = { - statement:"UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) SET n += prop.map", - props:[] + statement: "UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) SET n += prop.map", + props: [] } - }else{ + } else { queries.properties = { - statement:"UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) MERGE (m:Group {name:prop.pg}) MERGE (n)-[r:MemberOf]->(m) SET n += prop.map", - props:[] + statement: "UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) MERGE (m:Group {name:prop.pg}) MERGE (n)-[r:MemberOf {isacl:false}]->(m) SET n += prop.map", + props: [] } } } - queries.properties.props.push({map:properties, name:name, pg: primarygroup}); - $.each(localadmins, function(_, admin){ + queries.properties.props.push({ map: properties, name: name, pg: primarygroup }); + $.each(localadmins, function (_, admin) { let aType = admin.Type; let aName = admin.Name; let rel = "AdminTo"; - let hash = rel+aType; + let hash = rel + aType; let statement = baseQuery.format(aType, rel); - let p = {name: name, target: aName}; - insert(queries,hash,statement,p); + let p = { name: name, target: aName }; + insert(queries, hash, statement, p); }) - $.each(rdpers, function(_, rdp){ + $.each(rdpers, function (_, rdp) { let aType = rdp.Type; let aName = rdp.Name; let rel = "CanRDP"; - let hash = rel+aType; + let hash = rel + aType; let statement = baseQuery.format(aType, rel); - let p = {name: name, target: aName}; - insert(queries,hash,statement,p); + let p = { name: name, target: aName }; + insert(queries, hash, statement, p); }) }); return queries } -function insert(obj, hash, statement, prop){ - if (obj[hash]){ +function insert(obj, hash, statement, prop) { + if (obj[hash]) { obj[hash].props.push(prop) - }else{ + } else { obj[hash] = {} obj[hash].statement = statement; obj[hash].props = [] @@ -954,82 +582,6 @@ function insert(obj, hash, statement, prop){ } } -export function buildACLProps(rows) { - var datadict = {}; - - $.each(rows, function(index, row) { - var b = row.ObjectName.toUpperCase(); - var a = row.PrincipalName.toUpperCase(); - if (a === b){ - return; - } - var btype = row.ObjectType.toTitleCase(); - if (btype === 'Gpo'){ - btype = 'GPO'; - } - var atype = row.PrincipalType.toTitleCase(); - var rel = row.ActiveDirectoryRights; - var extright = row.ACEType; - - var rights = []; - - if (extright === 'All'){ - rights.push("AllExtendedRights"); - }else if (extright === 'User-Force-Change-Password'){ - rights.push("ForceChangePassword"); - }else if (rel === "ExtendedRight"){ - rights.push(extright); - } - - if (rel.includes("GenericAll")){ - rights.push("GenericAll"); - } - - if (rel.includes("WriteDacl")){ - rights.push("WriteDacl"); - } - - if (rel.includes("WriteOwner")){ - rights.push("WriteOwner"); - } - - if (rel.includes("GenericWrite")){ - rights.push("GenericWrite"); - } - - if (rel.includes("WriteProperty") && extright === "Member"){ - rights.push("AddMember"); - } - - if (rel === "Owner"){ - rights.push("Owns"); - } - - $.each(rights, function(index, record){ - var hash = (atype + record + btype).toUpperCase(); - if (btype === 'Computer') { - 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; -} - export function escapeRegExp(str) { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); } From 827d1d362de5cedc6644f12051fa271df36d85b6 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 11:47:35 -0400 Subject: [PATCH 05/67] Linting + Formatting --- package.json | 6 +- src/AppContainer.jsx | 50 +- src/components/Float/Alert.jsx | 39 +- src/components/Float/ExportContainer.jsx | 150 ++- src/components/Float/LoadingContainer.jsx | 62 +- src/components/Float/Login.jsx | 365 ++++-- src/components/Float/QueryNodeSelect.jsx | 121 +- src/components/Float/QueryNodeSelectItem.jsx | 77 +- src/components/Float/Settings.jsx | 194 ++- src/components/GlyphiconSpan.jsx | 69 +- src/components/Graph.jsx | 1128 +++++++++------- src/components/Icon.jsx | 35 +- src/components/Menu/MenuButton.jsx | 99 +- src/components/Menu/MenuContainer.jsx | 523 ++++---- src/components/Menu/ProgressBarMenuButton.jsx | 70 +- src/components/Modals/About.jsx | 110 +- src/components/Modals/CancelUploadModal.jsx | 101 +- src/components/Modals/ClearConfirmModal.jsx | 102 +- src/components/Modals/ClearWarnModal.jsx | 100 +- src/components/Modals/ClearingModal.jsx | 65 +- src/components/Modals/LogoutModal.jsx | 109 +- src/components/Modals/SessionClearModal.jsx | 96 +- src/components/RawQuery.jsx | 127 +- .../SearchContainer/SearchContainer.jsx | 1159 ++++++++++------- .../SearchContainer/TabContainer.jsx | 92 +- .../SearchContainer/Tabs/ComputerNodeData.jsx | 226 +++- .../Tabs/DatabaseDataDisplay.jsx | 156 ++- .../SearchContainer/Tabs/DomainNodeData.jsx | 229 ++-- .../SearchContainer/Tabs/GpoNodeData.jsx | 99 +- .../SearchContainer/Tabs/GroupNodeData.jsx | 209 ++- .../SearchContainer/Tabs/LoadLabel.jsx | 34 +- .../SearchContainer/Tabs/NoNodeData.jsx | 29 +- .../SearchContainer/Tabs/NodeALink.jsx | 38 +- .../SearchContainer/Tabs/NodeCypherLink.jsx | 67 +- .../Tabs/NodeCypherLinkComplex.jsx | 34 +- .../Tabs/NodeCypherNoNumberLink.jsx | 25 +- .../SearchContainer/Tabs/NodePropItem.jsx | 44 +- .../SearchContainer/Tabs/NodeProps.jsx | 34 +- .../SearchContainer/Tabs/OuNodeData.jsx | 105 +- .../SearchContainer/Tabs/PrebuiltQueries.json | 59 +- .../Tabs/PrebuiltQueriesDisplay.jsx | 120 +- .../Tabs/PrebuiltQueryNode.jsx | 18 +- .../SearchContainer/Tabs/UserNodeData.jsx | 245 +++- .../Spotlight/SpotlightContainer.jsx | 107 +- src/components/Spotlight/SpotlightRow.jsx | 41 +- src/components/Zoom/ZoomContainer.jsx | 65 +- src/components/tooltip.html | 158 ++- src/css/styles.css | 219 ++-- src/css/tooltip.css | 2 +- src/index.js | 206 +-- src/js/utils.js | 437 ++++--- src/js/worker.js | 167 +-- 52 files changed, 4844 insertions(+), 3378 deletions(-) diff --git a/package.json b/package.json index c7e573e..f8e1c03 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bloodhound", - "version": "1.5.2", + "version": "2.0", "description": "Graph Theory for Active Directory", "keywords": [ "Graph", @@ -56,7 +56,6 @@ "dagre": "^0.7.4", "electron-store": "^1.3.0", "eventemitter2": "^4.1.0", - "fast-csv": "^2.4.1", "jquery": "^3.2.1", "linkurious": "^1.5.1", "mustache": "^2.3.0", @@ -69,8 +68,5 @@ "react-transition-group": "^2.2.1", "stream-json": "^1.1.0", "unzipper": "^0.8.9" - }, - "jshintConfig": { - "esversion": "6" } } diff --git a/src/AppContainer.jsx b/src/AppContainer.jsx index 2dff409..b586312 100644 --- a/src/AppContainer.jsx +++ b/src/AppContainer.jsx @@ -1,31 +1,29 @@ -import React, { Component } from 'react'; -import GraphContainer from './components/Graph'; -import SearchContainer from './components/SearchContainer/SearchContainer'; -import SpotlightContainer from './components/Spotlight/SpotlightContainer'; -import LogoutModal from './components/Modals/LogoutModal'; -import CancelUploadModal from './components/Modals/CancelUploadModal'; -import ClearWarnModal from './components/Modals/ClearWarnModal' -import ClearConfirmModal from './components/Modals/ClearConfirmModal' -import ClearingModal from './components/Modals/ClearingModal' -import LoadingContainer from './components/Float/LoadingContainer'; -import GenericAlert from './components/Float/Alert'; -import RawQuery from './components/RawQuery'; -import MenuContainer from './components/Menu/MenuContainer'; -import ExportContainer from './components/Float/ExportContainer'; -import Settings from './components/Float/Settings' -import ZoomContainer from './components/Zoom/ZoomContainer' -import QueryNodeSelect from './components/Float/QueryNodeSelect' -import SessionClearModal from './components/Modals/SessionClearModal' -import About from './components/Modals/About.jsx' -import { CSSTransition, TransitionGroup } from 'react-transition-group'; +import React, { Component } from "react"; +import GraphContainer from "./components/Graph"; +import SearchContainer from "./components/SearchContainer/SearchContainer"; +import SpotlightContainer from "./components/Spotlight/SpotlightContainer"; +import LogoutModal from "./components/Modals/LogoutModal"; +import CancelUploadModal from "./components/Modals/CancelUploadModal"; +import ClearWarnModal from "./components/Modals/ClearWarnModal"; +import ClearConfirmModal from "./components/Modals/ClearConfirmModal"; +import ClearingModal from "./components/Modals/ClearingModal"; +import LoadingContainer from "./components/Float/LoadingContainer"; +import GenericAlert from "./components/Float/Alert"; +import RawQuery from "./components/RawQuery"; +import MenuContainer from "./components/Menu/MenuContainer"; +import ExportContainer from "./components/Float/ExportContainer"; +import Settings from "./components/Float/Settings"; +import ZoomContainer from "./components/Zoom/ZoomContainer"; +import QueryNodeSelect from "./components/Float/QueryNodeSelect"; +import SessionClearModal from "./components/Modals/SessionClearModal"; +import About from "./components/Modals/About.jsx"; +import { CSSTransition, TransitionGroup } from "react-transition-group"; export default class AppContainer extends Component { render() { return ( - - + +
@@ -49,5 +47,5 @@ export default class AppContainer extends Component { ); - }; -} \ No newline at end of file + } +} diff --git a/src/components/Float/Alert.jsx b/src/components/Float/Alert.jsx index a2856ff..92c6f33 100644 --- a/src/components/Float/Alert.jsx +++ b/src/components/Float/Alert.jsx @@ -1,8 +1,8 @@ -import React, { Component } from 'react'; -import { Alert } from 'react-bootstrap'; +import React, { Component } from "react"; +import { Alert } from "react-bootstrap"; export default class GenericAlert extends Component { - constructor(){ + constructor() { super(); this.state = { visible: false, @@ -10,38 +10,43 @@ export default class GenericAlert extends Component { 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){ + _show(val) { clearTimeout(this.state.timeout); - var t = setTimeout(function(){ - this._dismiss(); - }.bind(this), 2500); - + var t = setTimeout( + function() { + this._dismiss(); + }.bind(this), + 2500 + ); + this.setState({ visible: true, text: val, timeout: t }); - } render() { - if (this.state.visible){ + if (this.state.visible) { return ( - + {this.state.text} ); - }else{ + } else { return null; } - } } diff --git a/src/components/Float/ExportContainer.jsx b/src/components/Float/ExportContainer.jsx index 68c666d..2083190 100644 --- a/src/components/Float/ExportContainer.jsx +++ b/src/components/Float/ExportContainer.jsx @@ -1,72 +1,104 @@ -import React, { Component } from 'react'; +import React, { Component } from "react"; export default class ExportContainer extends Component { - constructor(){ - super(); + constructor() { + super(); - this.state = { - jsonActive: true, - imageActive: false - } - } - _dismiss(){ - $(this.refs.outer).fadeToggle(false) - } + this.state = { + jsonActive: true, + imageActive: false + }; + } + _dismiss() { + $(this.refs.outer).fadeToggle(false); + } - _show(){ - $(this.refs.outer).fadeToggle(true) - } + _show() { + $(this.refs.outer).fadeToggle(true); + } - componentDidMount() { - $(this.refs.outer).fadeToggle(0) - $(this.refs.outer).draggable() - emitter.on('showExport', this._show.bind(this)) - } + componentDidMount() { + $(this.refs.outer).fadeToggle(0); + $(this.refs.outer).draggable(); + emitter.on("showExport", this._show.bind(this)); + } - _jsonClick(){ - this.setState({jsonActive: true, imageActive: false}) - } + _jsonClick() { + this.setState({ jsonActive: true, imageActive: false }); + } - _imageClick(){ - this.setState({jsonActive: false, imageActive: true}) - } + _imageClick() { + this.setState({ jsonActive: false, imageActive: true }); + } - _buttonClick(){ - emitter.emit('export', this.state.jsonActive ? 'json' : 'image'); - } + _buttonClick() { + emitter.emit("export", this.state.jsonActive ? "json" : "image"); + } - render() { - return ( - + ); + } } diff --git a/src/components/Float/LoadingContainer.jsx b/src/components/Float/LoadingContainer.jsx index 0711068..0b1ca83 100644 --- a/src/components/Float/LoadingContainer.jsx +++ b/src/components/Float/LoadingContainer.jsx @@ -1,36 +1,42 @@ -import React, { Component } from 'react'; +import React, { Component } from "react"; export default class LoadingContainer extends Component { - constructor(){ - super(); + constructor() { + super(); - this.state = { - text: "Loading" - } + this.state = { + text: "Loading" + }; - emitter.on('updateLoadingText', function(payload){ - this.setState({text: payload}) - }.bind(this)) + emitter.on( + "updateLoadingText", + function(payload) { + this.setState({ text: payload }); + }.bind(this) + ); - emitter.on('showLoadingIndicator', function(payload){ - if (payload){ - jQuery(this.refs.load).fadeIn() - }else{ - jQuery(this.refs.load).fadeOut() - } - }.bind(this)) - } + emitter.on( + "showLoadingIndicator", + function(payload) { + if (payload) { + jQuery(this.refs.load).fadeIn(); + } else { + jQuery(this.refs.load).fadeOut(); + } + }.bind(this) + ); + } - componentDidMount() { - jQuery(this.refs.load).fadeToggle(0) - } + componentDidMount() { + jQuery(this.refs.load).fadeToggle(0); + } - render() { - return ( -
-
{this.state.text}
- -
- ); - } + render() { + return ( +
+
{this.state.text}
+ +
+ ); + } } diff --git a/src/components/Float/Login.jsx b/src/components/Float/Login.jsx index 2f9ff3b..5ba0c46 100644 --- a/src/components/Float/Login.jsx +++ b/src/components/Float/Login.jsx @@ -1,7 +1,7 @@ -import React, { Component } from 'react'; +import React, { Component } from "react"; export default class Login extends Component { - constructor(){ + constructor() { super(); this.state = { url: "", @@ -15,8 +15,8 @@ export default class Login extends Component { } componentWillMount() { - var c = conf.get('databaseInfo'); - if (typeof c !== 'undefined'){ + var c = conf.get("databaseInfo"); + if (typeof c !== "undefined") { this.setState({ url: c.url, user: c.user, @@ -28,39 +28,41 @@ export default class Login extends Component { componentDidMount() { jQuery(this.refs.password).tooltip({ - placement : 'right', - title: '', - container: 'body', - trigger: 'manual', - template: '' + placement: "right", + title: "", + container: "body", + trigger: "manual", + template: + '' }); this.setIcon(); - - if (this.state.password !== ""){ + + if (this.state.password !== "") { this.checkDBCreds(); } } - setIcon(){ + setIcon() { var icon = jQuery(this.refs.urlspinner); icon.tooltip({ - placement : 'right', - title: '', - container: 'body', - delay: {show: 200, hide: 0}, - template: '' + placement: "right", + title: "", + container: "body", + delay: { show: 200, hide: 0 }, + template: + '' }); icon.toggle(false); - this.setState({icon: jQuery(this.refs.urlspinner)}); + this.setState({ icon: jQuery(this.refs.urlspinner) }); } - checkDBPresence(){ + checkDBPresence() { var url = this.state.url; var icon = this.state.icon; var jicon = jQuery(icon); var btn = jQuery(this.refs.loginButton); - if (url === ""){ + if (url === "") { return; } @@ -68,35 +70,41 @@ export default class Login extends Component { url = url.replace(/\/$/, ""); - if (!url.includes(':')){ - url = url + ':7687'; + if (!url.includes(":")) { + url = url + ":7687"; } - if (!url.startsWith('bolt://')){ - url = 'bolt://' + url; + if (!url.startsWith("bolt://")) { + url = "bolt://" + url; } icon.removeClass(); icon.addClass("fa fa-spinner fa-spin form-control-feedback"); icon.toggle(true); - var driver = neo4j.driver(url, neo4j.auth.basic("",""), {encrypted:'ENCRYPTION_ON'}); + var driver = neo4j.driver(url, neo4j.auth.basic("", ""), { + encrypted: "ENCRYPTION_ON" + }); var session = driver.session(); - driver.onCompleted = function(){ + driver.onCompleted = function() { session.close(); driver.close(); }; - driver.onError = function(error){ - if (error.code.includes("Unauthorized")){ + driver.onError = function(error) { + if (error.code.includes("Unauthorized")) { icon.removeClass(); - icon.addClass("fa fa-check-circle green-icon-color form-control-feedback"); - this.setState({loginEnabled: true, url: url}); - }else{ + 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'); + 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 @@ -107,8 +115,8 @@ export default class Login extends Component { }.bind(this); } - checkDBCreds(){ - if (this.state.loginInProgress){ + checkDBCreds() { + if (this.state.loginInProgress) { return; } this.setState({ @@ -119,35 +127,44 @@ export default class Login extends Component { var btn = jQuery(this.refs.loginButton); var pwf = jQuery(this.refs.password); - var driver = neo4j.driver(this.state.url, neo4j.auth.basic(this.state.user, this.state.password), {encrypted:'ENCRYPTION_ON'}); - driver.onError = function(error){ + var driver = neo4j.driver( + this.state.url, + neo4j.auth.basic(this.state.user, this.state.password), + { encrypted: "ENCRYPTION_ON" } + ); + driver.onError = function(error) { console.log(error); - if (error.message.includes("authentication failure")){ - btn.removeClass('activate'); + 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'); + 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')){ + 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.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'); + 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 @@ -156,94 +173,115 @@ export default class Login extends Component { 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'); - //This block will trip for neo4j < 3.4 - 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 - }); - } - - if (error.code && error.code === "Neo.ClientError.Security.CredentialsExpired"){ - pwf.attr('data-original-title', 'Credentials need to be changed from the neo4j browser first. Go to {} and change them.'.format(url)) - .tooltip('fixTitle') - .tooltip('show'); - this.setState({ - loginInProgress: false, - loginEnabled: true - }); - } - }.bind(this), - onNext: function(){ - - }, - onCompleted: function(){ - btn.toggleClass('activate'); - btn.removeClass('btn-default'); - btn.addClass('btn-success'); - btn.html('Success!'); + 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"); + //This block will trip for neo4j < 3.4 + 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 + loginInProgress: false, + loginEnabled: true }); + } - var dbinfo = { - url: this.state.url, - user: this.state.user, - password: this.state.password - }; - - if (this.state.save){ - conf.set('databaseInfo',dbinfo); - } + if ( + error.code && + error.code === "Neo.ClientError.Security.CredentialsExpired" + ) { + pwf.attr( + "data-original-title", + "Credentials need to be changed from the neo4j browser first. Go to {} and change them.".format( + url + ) + ) + .tooltip("fixTitle") + .tooltip("show"); + this.setState({ + loginInProgress: false, + loginEnabled: true + }); + } + }.bind(this), + onNext: function() {}, + onCompleted: function() { + btn.toggleClass("activate"); + btn.removeClass("btn-default"); + btn.addClass("btn-success"); + btn.html("Success!"); + this.setState({ + loginInProgress: false + }); - 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'); + var dbinfo = { + url: this.state.url, + user: this.state.user, + password: this.state.password + }; + + if (this.state.save) { + conf.set("databaseInfo", dbinfo); + } + + appStore.databaseInfo = dbinfo; + + jQuery(this.refs.password).tooltip("hide"); + jQuery(this.refs.urlspinner).tooltip("hide"); + setTimeout( + function() { + jQuery(this.refs.outer).fadeOut(400, function() { + renderEmit.emit("login"); }); - }.bind(this), 1500); - driver.close(); - global.driver = neo4j.driver(this.state.url, neo4j.auth.basic(this.state.user, this.state.password)); - }.bind(this) - }); - - btn.toggleClass('activate'); + }.bind(this), + 1500 + ); + driver.close(); + global.driver = neo4j.driver( + this.state.url, + neo4j.auth.basic(this.state.user, this.state.password) + ); + }.bind(this) + }); + btn.toggleClass("activate"); } _saveChange(event) { - this.setState({save: event.target.checked}); + this.setState({ save: event.target.checked }); } - _urlChanged(event){ - this.setState({url: event.target.value}); + _urlChanged(event) { + this.setState({ url: event.target.value }); } - _userChanged(event){ - this.setState({user: event.target.value}); - jQuery(this.refs.password).tooltip('hide'); + _userChanged(event) { + this.setState({ user: event.target.value }); + jQuery(this.refs.password).tooltip("hide"); } - _passChanged(event){ - this.setState({password: event.target.value}); - jQuery(this.refs.password).tooltip('hide'); + _passChanged(event) { + this.setState({ password: event.target.value }); + jQuery(this.refs.password).tooltip("hide"); } - _triggerLogin(e){ + _triggerLogin(e) { var key = e.keyCode ? e.keyCode : e.which; - if (key === 13){ + if (key === 13) { this.checkDBCreds(); } } @@ -259,26 +297,87 @@ export default class Login extends Component {
- + Database URL - - + +
- DB Username - + + DB Username + +
- DB Password - + + DB Password + +
- +
-
- Sibling Collapse Threshold - Sibling Collapse Threshold + -
+
- +
Node Collapse Threshold -
- +

@@ -145,12 +174,17 @@ export default class Settings extends Component { Edge Label Display - - + @@ -158,12 +192,18 @@ export default class Settings extends Component { Node Label Display - - + @@ -173,24 +213,36 @@ export default class Settings extends Component {
-
-
diff --git a/src/components/GlyphiconSpan.jsx b/src/components/GlyphiconSpan.jsx index dc5563b..f138e12 100644 --- a/src/components/GlyphiconSpan.jsx +++ b/src/components/GlyphiconSpan.jsx @@ -1,34 +1,45 @@ -import React, { Component } from 'react'; -import { If, Then, Else } from 'react-if'; -import PropTypes from 'prop-types' +import React, { Component } from "react"; +import { If, Then, Else } from "react-if"; +import PropTypes from "prop-types"; -export default class GlyphiconSpan extends Component { - constructor(props){ - super(props); - } +export default class GlyphiconSpan extends Component { + constructor(props) { + super(props); + } - render() { - return ( - - - - {this.props.children} - - - {() => - - {this.props.children} - - } - - ); - } + render() { + return ( + + + + {this.props.children} + + + + {() => ( + + {this.props.children} + + )} + + + ); + } } GlyphiconSpan.propTypes = { - classes : PropTypes.string, - tooltipDir : PropTypes.string, - tooltipTitle : PropTypes.string, - tooltip : PropTypes.bool.isRequired, - click: PropTypes.func -} + classes: PropTypes.string, + tooltipDir: PropTypes.string, + tooltipTitle: PropTypes.string, + tooltip: PropTypes.bool.isRequired, + click: PropTypes.func +}; diff --git a/src/components/Graph.jsx b/src/components/Graph.jsx index 397988c..4971df3 100644 --- a/src/components/Graph.jsx +++ b/src/components/Graph.jsx @@ -1,20 +1,23 @@ -import React, { Component } from 'react'; -import ReactDOM from 'react-dom'; -import { findGraphPath } from 'utils'; -var fs = require('fs'); -var child_process = require('child_process'); +import React, { Component } from "react"; +import { findGraphPath } from "utils"; +import { writeFile, readFile } from "fs"; +import { fork } from "child_process"; var child; -var path = require('path'); -const { dialog } = require('electron').remote; +import { join } from "path"; +import { remote } from "electron"; +const { dialog } = remote; export default class GraphContainer extends Component { - constructor(props){ + constructor(props) { super(props); - child = child_process.fork(path.join(__dirname,'src','js','worker.js'), {silent:true}); + child = fork( + join(__dirname, "src", "js", "worker.js"), + { silent: true } + ); this.state = { - sigmaInstance : null, + sigmaInstance: null, design: null, dragged: false, firstDraw: true, @@ -23,110 +26,127 @@ export default class GraphContainer extends Component { }; $.ajax({ - url: 'src/components/tooltip.html', - type: 'GET', - success: function(response){ - this.setState({template: response}); + 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}`); + child.stdout.on("data", data => { + console.log(`stdout: ${data}`); }); - child.stderr.on('data', (data) => { + child.stderr.on("data", data => { console.log(`error: ${data}`); }); - child.on('message', function(m) { - this.loadFromChildProcess(m); - }.bind(this)); + child.on( + "message", + function(m) { + this.loadFromChildProcess(m); + }.bind(this) + ); - var s1 = driver.session(); - var s2 = driver.session(); - var s3 = driver.session(); - var s4 = driver.session(); - var s5 = driver.session(); - var s6 = driver.session(); + let s1 = driver.session(); + let s2 = driver.session(); + let s3 = driver.session(); + let s4 = driver.session(); + let s5 = driver.session(); + let s6 = driver.session(); s1.run("CREATE CONSTRAINT ON (c:User) ASSERT c.name IS UNIQUE") - .then(function(){ + .then(function() { s1.close(); - s2.run("CREATE CONSTRAINT ON (c:Computer) ASSERT c.name IS UNIQUE") - .then(function(){ + s2.run( + "CREATE CONSTRAINT ON (c:Computer) ASSERT c.name IS UNIQUE" + ) + .then(function() { s2.close(); - s3.run("CREATE CONSTRAINT ON (c:Group) ASSERT c.name IS UNIQUE") - .then(function(){ + s3.run( + "CREATE CONSTRAINT ON (c:Group) ASSERT c.name IS UNIQUE" + ) + .then(function() { s3.close(); - s4.run("CREATE CONSTRAINT ON (c:Domain) ASSERT c.name IS UNIQUE") - .then(function(){ + s4.run( + "CREATE CONSTRAINT ON (c:Domain) ASSERT c.name IS UNIQUE" + ) + .then(function() { s4.close(); - s5.run("CREATE CONSTRAINT on (c:OU) ASSERT c.guid IS UNIQUE") + s5.run( + "CREATE CONSTRAINT on (c:OU) ASSERT c.guid IS UNIQUE" + ) .then(function() { s5.close(); - s6.run("CREATE CONSTRAINT on (c:GPO) ASSERT c.name is UNIQUE") - .then(function(){ + s6.run( + "CREATE CONSTRAINT on (c:GPO) ASSERT c.name is UNIQUE" + ) + .then(function() { s6.close(); }) - .catch(function(){ + .catch(function() { s6.close(); }); }) - .catch(function(){ - s5.close(); + .catch(function() { + s5.close(); }); }) - .catch(function(){ + .catch(function() { s4.close(); }); }) - .catch(function(){ + .catch(function() { s3.close(); }); }) - .catch(function(){ + .catch(function() { s2.close(); }); }) - .catch(function(){ + .catch(function() { s1.close(); }); - - emitter.on('doLogout', function(){ - this.state.sigmaInstance.graph.clear(); - this.state.sigmaInstance.refresh(); - sigma.layouts.killForceLink(); - this.setState({sigmaInstance: null}); - child.kill(); - }.bind(this)); + + emitter.on( + "doLogout", + function() { + this.state.sigmaInstance.graph.clear(); + this.state.sigmaInstance.refresh(); + sigma.layouts.killForceLink(); + this.setState({ sigmaInstance: null }); + child.kill(); + }.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)); - emitter.on('changeNodeLabels', this.changeNodeLabelMode.bind(this)); - emitter.on('changeEdgeLabels', this.changeEdgeLabelMode.bind(this)); + 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)); + emitter.on("changeNodeLabels", this.changeNodeLabelMode.bind(this)); + emitter.on("changeEdgeLabels", this.changeEdgeLabelMode.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', + statement: + 'MATCH (n:Group) WHERE n.name =~ "(?i).*DOMAIN ADMINS.*" WITH n MATCH (n)<-[r:MemberOf*1..]-(m) RETURN n,r,m', //statement: 'MATCH (n)-[r]->(m) RETURN n,r,m', //statement: 'MATCH p=(n:Domain)-[r]-(m:Domain) RETURN p', allowCollapse: false, @@ -134,80 +154,85 @@ export default class GraphContainer extends Component { }); } - relayout(){ + relayout() { sigma.layouts.stopForceLink(); - if (appStore.dagre){ + if (appStore.dagre) { sigma.layouts.dagre.start(this.state.sigmaInstance); - }else{ + } else { sigma.layouts.startForceLink(); } } - export(payload){ - if (payload === 'image'){ - var size = $('#graph').outerWidth(); - sigma.plugins.image(this.state.sigmaInstance, + export(payload) { + if (payload === "image") { + let size = $("#graph").outerWidth(); + sigma.plugins.image( + this.state.sigmaInstance, this.state.sigmaInstance.renderers[0], { download: true, size: size, - background: 'lightgray', + background: "lightgray", clip: true - }); - }else{ - var json = this.state.sigmaInstance.toJSON({ + } + ); + } else { + let json = this.state.sigmaInstance.toJSON({ pretty: true }); json = JSON.parse(json); json.spotlight = appStore.spotlightData; - dialog.showSaveDialog({ - defaultPath: 'graph.json' - }, function(loc){ - fs.writeFile(loc, JSON.stringify(json, null, 2)); - }); + dialog.showSaveDialog( + { + defaultPath: "graph.json" + }, + function(loc) { + writeFile(loc, JSON.stringify(json, null, 2)); + } + ); } } - changeNodeLabelMode(){ + changeNodeLabelMode() { let mode = appStore.performance.nodeLabels; let instance = this.state.sigmaInstance; if (mode === 0) { - instance.settings('labelThreshold', 15); + instance.settings("labelThreshold", 15); } else if (mode === 1) { - instance.settings('labelThreshold', 1); + instance.settings("labelThreshold", 1); } else { - instance.settings('labelThreshold', 500); + instance.settings("labelThreshold", 500); } - instance.refresh({ 'skipIndexation': true }); + instance.refresh({ skipIndexation: true }); this.setState({ sigmaInstance: instance }); } - changeEdgeLabelMode(){ + changeEdgeLabelMode() { let instance = this.state.sigmaInstance; let x = instance.camera.x; let y = instance.camera.y; let ratio = instance.camera.ratio; let angle = instance.camera.angle; - instance.camera.goTo({x:x,y:y,ratio:ratio,angle:angle}); - instance.refresh({ 'skipIndexation': true }); + instance.camera.goTo({ x: x, y: y, ratio: ratio, angle: angle }); + instance.refresh({ skipIndexation: true }); this.setState({ sigmaInstance: instance }); } - loadFromChildProcess(graph){ - if (graph.nodes.length === 0){ - emitter.emit('showAlert', "No data returned from query"); - emitter.emit('updateLoadingText', "Done!"); - setTimeout(function(){ - emitter.emit('showLoadingIndicator', false); - }, 1500); - }else{ - if (!this.state.firstDraw){ + loadFromChildProcess(graph) { + if (graph.nodes.length === 0) { + emitter.emit("showAlert", "No data returned from query"); + emitter.emit("updateLoadingText", "Done!"); + setTimeout(function() { + emitter.emit("showLoadingIndicator", false); + }, 1500); + } else { + if (!this.state.firstDraw) { appStore.queryStack.push({ nodes: this.state.sigmaInstance.graph.nodes(), edges: this.state.sigmaInstance.graph.edges(), @@ -216,12 +241,12 @@ export default class GraphContainer extends Component { endNode: appStore.endNode }); } - $.each(graph.nodes, function(i, node){ - if (node.start){ + $.each(graph.nodes, function(i, node) { + if (node.start) { appStore.startNode = node; } - if (node.end){ + if (node.end) { appStore.endNode = node; } @@ -230,322 +255,398 @@ export default class GraphContainer extends Component { }); }); - this.setState({firstDraw: false}); - sigma.misc.animation.camera(this.state.sigmaInstance.camera, { x: 0, y: 0, ratio: 1.075 }); + this.setState({ firstDraw: false }); + sigma.misc.animation.camera(this.state.sigmaInstance.camera, { + x: 0, + y: 0, + ratio: 1.075 + }); appStore.spotlightData = graph.spotlight; this.state.sigmaInstance.graph.clear(); this.state.sigmaInstance.graph.read(graph); this.applyDesign(); - if (appStore.dagre){ + if (appStore.dagre) { sigma.layouts.dagre.start(this.state.sigmaInstance); - }else{ + } else { sigma.layouts.startForceLink(); } - emitter.emit('spotlightUpdate'); - } + emitter.emit("spotlightUpdate"); + } } - import(payload){ - fs.readFile(payload, 'utf8', function(err, data){ - var graph; - try{ - graph = JSON.parse(data); - }catch (err){ - emitter.emit('showAlert', 'Bad JSON File'); - return; - } + import(payload) { + readFile( + payload, + "utf8", + function(err, data) { + var graph; + try { + graph = JSON.parse(data); + } catch (err) { + emitter.emit("showAlert", "Bad JSON File"); + return; + } - if (graph.nodes.length === 0){ - 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]; + if (graph.nodes.length === 0) { + 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.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(); - emitter.emit('spotlightUpdate'); - } - - }.bind(this)); + appStore.spotlightData = graph.spotlight; + this.state.sigmaInstance.graph.clear(); + this.state.sigmaInstance.graph.read(graph); + this.state.sigmaInstance.refresh(); + emitter.emit("spotlightUpdate"); + } + }.bind(this) + ); } - clearGraph(){ + clearGraph() { this.state.sigmaInstance.graph.clear(); this.state.sigmaInstance.refresh(); } - applyDesign(){ + applyDesign() { this.state.design.deprecate(); this.state.sigmaInstance.refresh(); this.state.design.apply(); - $.each(this.state.sigmaInstance.graph.edges(), function(index, edge){ - if (edge.hasOwnProperty('enforced')){ - if (edge.enforced === false){ - edge.type = 'dashed'; + $.each(this.state.sigmaInstance.graph.edges(), function(index, edge) { + if (edge.hasOwnProperty("enforced")) { + if (edge.enforced === false) { + edge.type = "dashed"; } } }); - $.each(this.state.sigmaInstance.graph.nodes(), function(index, node){ - if (node.hasOwnProperty('blocksInheritance')){ - if (node.blocksInheritance === true){ - let targets = []; - $.each(this.state.sigmaInstance.graph.outNeighbors(node.id),function(index, nodeid){ - targets.push(parseInt(nodeid)); - }.bind(this)); + $.each( + this.state.sigmaInstance.graph.nodes(), + function(_, node) { + if (node.hasOwnProperty("blocksInheritance")) { + if (node.blocksInheritance === true) { + let targets = []; + $.each( + this.state.sigmaInstance.graph.outNeighbors( + node.id + ), + function(_, nodeid) { + targets.push(parseInt(nodeid)); + }.bind(this) + ); - $.each(this.state.sigmaInstance.graph.adjacentEdges(node.id), function(index, edge){ - if (targets.includes(edge.target)){ - edge.type = 'dotted'; - } - }); + $.each( + this.state.sigmaInstance.graph.adjacentEdges( + node.id + ), + function(_, edge) { + if (targets.includes(edge.target)) { + edge.type = "dotted"; + } + } + ); + } } - } - }.bind(this)); + }.bind(this) + ); } - setGraphicsMode(){ + setGraphicsMode() { var lowgfx = appStore.performance.lowGraphics; var sigmaInstance = this.state.sigmaInstance; this.state.design.clear(); - if (lowgfx){ - sigmaInstance.settings('defaultEdgeType', 'line'); - sigmaInstance.settings('defaultEdgeColor', 'black'); + if (lowgfx) { + sigmaInstance.settings("defaultEdgeType", "line"); + sigmaInstance.settings("defaultEdgeColor", "black"); this.state.design.setPalette(appStore.lowResPalette); this.state.design.setStyles(appStore.lowResStyle); - }else{ - sigmaInstance.settings('defaultEdgeType', 'tapered'); - sigmaInstance.settings('defaultEdgeColor', '#356'); + } else { + sigmaInstance.settings("defaultEdgeType", "tapered"); + sigmaInstance.settings("defaultEdgeColor", "#356"); this.state.design.setPalette(appStore.highResPalette); this.state.design.setStyles(appStore.highResStyle); } this.applyDesign(); } - resetZoom(){ + resetZoom() { + sigma.misc.animation.camera(this.state.sigmaInstance.camera, { + x: 0, + y: 0, + ratio: 1.075 + }); + } + + zoomOut() { + var sigmaInstance = this.state.sigmaInstance; + var cam = sigmaInstance.camera; + sigma.misc.animation.camera( - this.state.sigmaInstance.camera, - { x: 0, y: 0, ratio: 1.075 }) - ; + cam, + { + ratio: cam.ratio * cam.settings("zoomingRatio") + }, + { + duration: sigmaInstance.settings("animationsTime") + } + ); } - zoomOut(){ + zoomIn() { var sigmaInstance = this.state.sigmaInstance; var cam = sigmaInstance.camera; - sigma.misc.animation.camera(cam, { - ratio: cam.ratio * cam.settings('zoomingRatio') - }, { - duration: sigmaInstance.settings('animationsTime') - }); - } - - zoomIn(){ - var sigmaInstance = this.state.sigmaInstance; - var cam = sigmaInstance.camera; - - sigma.misc.animation.camera(cam, - { - ratio: cam.ratio / cam.settings('zoomingRatio') - }, - { - duration: sigmaInstance.settings('animationsTime') - }); + sigma.misc.animation.camera( + cam, + { + ratio: cam.ratio / cam.settings("zoomingRatio") + }, + { + duration: sigmaInstance.settings("animationsTime") + } + ); } render() { return (
-
+
); } - goBack(){ + goBack() { if (appStore.queryStack.length > 0) { if (appStore.currentTooltip !== null) { appStore.currentTooltip.close(); } sigma.layouts.stopForceLink(); - var query = appStore.queryStack.pop(); + let query = appStore.queryStack.pop(); this.state.sigmaInstance.graph.clear(); - this.state.sigmaInstance.graph.read({ nodes: query.nodes, edges: query.edges }); - this.applyDesign() + this.state.sigmaInstance.graph.read({ + nodes: query.nodes, + edges: query.edges + }); + this.applyDesign(); appStore.spotlightData = query.spotlight; - appStore.startNode = query.startNode, - appStore.endNode = query.endNode; - emitter.emit('spotlightUpdate'); + (appStore.startNode = query.startNode), + (appStore.endNode = query.endNode); + emitter.emit("spotlightUpdate"); } } - spotlightClickHandler(nodeId, parentId){ - var sigmaInstance = this.state.sigmaInstance; - 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]; + spotlightClickHandler(nodeId, parentId) { + let sigmaInstance = this.state.sigmaInstance; + let parent = sigmaInstance.graph.nodes(nodeId); + let label, child; + if (typeof parent === "undefined") { + child = sigmaInstance.graph + .nodes(parentId) + .folded.nodes.filter(function(val) { + return val.id === nodeId; + })[0]; parent = sigmaInstance.graph.nodes(parentId); - }else{ + } else { child = parent; } label = child.label; - if (child.type_user){ - emitter.emit('userNodeClicked', label); - }else if (child.type_group){ - emitter.emit('groupNodeClicked', label); - }else if (child.type_computer){ - emitter.emit('computerNodeClicked', label); + if (child.type_user) { + emitter.emit("userNodeClicked", label); + } else if (child.type_group) { + emitter.emit("groupNodeClicked", label); + } else if (child.type_computer) { + emitter.emit("computerNodeClicked", label); } parent.color = "#2DC486"; sigma.misc.animation.camera( - sigmaInstance.camera, { - x: parent[sigmaInstance.camera.readPrefix + 'x'], - y: parent[sigmaInstance.camera.readPrefix + 'y'], + sigmaInstance.camera, + { + x: parent[sigmaInstance.camera.readPrefix + "x"], + y: parent[sigmaInstance.camera.readPrefix + "y"], ratio: 0.5 - }, { duration: sigmaInstance.settings('animationsTime') } + }, + { duration: sigmaInstance.settings("animationsTime") } ); - setTimeout(function(){ + setTimeout(function() { parent.color = "black"; - sigmaInstance.refresh({skipIndexation: true}); + sigmaInstance.refresh({ skipIndexation: true }); }, 2000); } - doQueryNative(params){ - if (appStore.performance.debug){ - emitter.emit('setRawQuery',params.statement); + doQueryNative(params) { + if (appStore.performance.debug) { + emitter.emit("setRawQuery", params.statement); } - var sigmaInstance = this.state.sigmaInstance; var nodes = {}; var edges = {}; var session = driver.session(); - if (typeof params.props === 'undefined'){ + if (typeof params.props === "undefined") { params.props = {}; } - emitter.emit('showLoadingIndicator', true); - 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.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); + emitter.emit("showLoadingIndicator", true); + emitter.emit("updateLoadingText", "Querying Database"); + emitter.emit("resetSpotlight"); + session.run(params.statement, params.props).subscribe({ + onNext: function(result) { + $.each( + result._fields, + function(_, field) { + if (field !== null) { + if (field.hasOwnProperty("segments")) { + $.each( + field.segments, + function(_, segment) { + 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; - } - - if (!nodes[end.id]){ - nodes[end.id] = end; - } - - if (!nodes[start.id]){ - nodes[start.id] = start; - } - }.bind(this)); - }else{ - if ($.isArray(field)){ - $.each(field, function(index, value){ - if (value !== null){ - var id = value.identity.low; - if (value.end && !edges.id){ - edges[id] = this.createEdgeFromRow(value); - }else if (!nodes.id){ - nodes[id] = this.createNodeFromRow(value, params); - } + if (!edges[edge.id]) { + edges[edge.id] = edge; } - }.bind(this)); - }else{ + + if (!nodes[end.id]) { + nodes[end.id] = end; + } + + if (!nodes[start.id]) { + nodes[start.id] = start; + } + }.bind(this) + ); + } else { + if ($.isArray(field)) { + $.each( + field, + function(_, value) { + if (value !== null) { + var id = value.identity.low; + if (value.end && !edges.id) { + edges[ + id + ] = this.createEdgeFromRow( + value + ); + } else if (!nodes.id) { + nodes[ + id + ] = this.createNodeFromRow( + value, + params + ); + } + } + }.bind(this) + ); + } else { var id = field.identity.low; - if (field.end && !edges.id){ - edges[id] = this.createEdgeFromRow(field); - }else if (!nodes.id){ - nodes[id] = this.createNodeFromRow(field, params); + if (field.end && !edges.id) { + edges[id] = this.createEdgeFromRow( + field + ); + } else if (!nodes.id) { + nodes[id] = this.createNodeFromRow( + field, + params + ); } } } } - }.bind(this)); - }.bind(this), - onError: function(error){ - console.log(error); - }, - onCompleted: function(){ - var graph = {nodes:[],edges:[]}; - $.each(nodes, function(node){ - graph.nodes.push(nodes[node]); - }); + }.bind(this) + ); + }.bind(this), + onError: function(error) { + console.log(error); + }, + onCompleted: function() { + var graph = { nodes: [], edges: [] }; + $.each(nodes, function(node) { + graph.nodes.push(nodes[node]); + }); - $.each(edges, function(edge){ - graph.edges.push(edges[edge]); - }); - emitter.emit('updateLoadingText', "Processing Data"); + $.each(edges, function(edge) { + 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(); - }.bind(this) - }); + 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(); + }.bind(this) + }); } - createEdgeFromRow(data){ + createEdgeFromRow(data) { 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, + target: target, label: type }; - if (data.hasOwnProperty('properties') && data.properties.hasOwnProperty('enforced')) { + if ( + data.hasOwnProperty("properties") && + data.properties.hasOwnProperty("enforced") + ) { edge.enforced = data.properties.enforced; } return edge; } - createNodeFromRow(data, params){ + createNodeFromRow(data, params) { var id = data.identity.low; var type = data.labels[0]; var label = data.properties.name; var guid = data.properties.guid; - if (label == null){ + if (label == null) { label = guid; } - + var node = { id: id, type: type, @@ -560,36 +661,35 @@ export default class GraphContainer extends Component { y: Math.random() }; - if (data.hasOwnProperty('properties')){ - if (data.properties.hasOwnProperty('blocksInheritance')) { + if (data.hasOwnProperty("properties")) { + if (data.properties.hasOwnProperty("blocksInheritance")) { node.blocksInheritance = data.properties.blocksInheritance; } - if (data.properties.hasOwnProperty('guid')){ + if (data.properties.hasOwnProperty("guid")) { node.guid = data.properties.guid; } } - - if (label === params.start){ + if (label === params.start) { node.start = true; node.glyphs.push({ - 'position': 'bottom-right', - 'font': 'FontAwesome', - 'content': '\uF21D', - 'fillColor': '#3399FF', - 'fontScale': 1.5 + position: "bottom-right", + font: "FontAwesome", + content: "\uF21D", + fillColor: "#3399FF", + fontScale: 1.5 }); } - if (label === params.end){ + if (label === params.end) { node.end = true; node.glyphs.push({ - 'position': 'bottom-right', - 'font': 'FontAwesome', - 'fillColor': '#990000', - 'content': '\uF05B', - 'fontScale': 1.5 + position: "bottom-right", + font: "FontAwesome", + fillColor: "#990000", + content: "\uF05B", + fontScale: 1.5 }); } @@ -617,7 +717,7 @@ export default class GraphContainer extends Component { return node; } - unfoldEdgeNode(id){ + unfoldEdgeNode(id) { var sigmaInstance = this.state.sigmaInstance; sigmaInstance.graph.read(sigmaInstance.graph.nodes(id).folded); this.state.design.deprecate(); @@ -625,9 +725,12 @@ export default class GraphContainer extends Component { this.relayout(); } - foldEdgeNode(id){ + foldEdgeNode(id) { var sigmaInstance = this.state.sigmaInstance; - $.each(sigmaInstance.graph.nodes(id).folded.nodes, function(index, node){ + $.each(sigmaInstance.graph.nodes(id).folded.nodes, function( + _, + node + ) { sigmaInstance.graph.dropNode(node.id); }); sigmaInstance.refresh(); @@ -636,7 +739,7 @@ export default class GraphContainer extends Component { this.relayout(); } - ungroupNode(id){ + ungroupNode(id) { var sigmaInstance = this.state.sigmaInstance; var node = sigmaInstance.graph.nodes(id); sigmaInstance.graph.dropNode(id); @@ -647,8 +750,8 @@ export default class GraphContainer extends Component { this.relayout(); } - doSearchQuery(payload, props){ - if (typeof props === 'undefined'){ + doSearchQuery(payload, props) { + if (typeof props === "undefined") { props = {}; } this.doQueryNative({ @@ -658,9 +761,10 @@ export default class GraphContainer extends Component { }); } - doPathQuery(start, end){ - var statement = "MATCH (n {name:{start}}), (m {name:{end}}), p=allShortestPaths((n)-[*]->(m)) RETURN p"; - var props = {start: start, end: end}; + doPathQuery(start, 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, @@ -670,12 +774,12 @@ export default class GraphContainer extends Component { }); } - doGenericQuery(statement, props, start, end, allowCollapse=true){ + doGenericQuery(statement, props, start, end, allowCollapse = true) { if (appStore.currentTooltip !== null) { appStore.currentTooltip.close(); } - if (typeof props === 'undefined'){ + if (typeof props === "undefined") { props = {}; } this.doQueryNative({ @@ -687,267 +791,311 @@ export default class GraphContainer extends Component { }); } - _nodeDragged(){ - this.setState({dragged:true}); + _nodeDragged() { + this.setState({ dragged: true }); } - _nodeClicked(n){ - if (!this.state.dragged){ - if (n.data.node.type_user){ - emitter.emit('userNodeClicked', n.data.node.label); - }else if (n.data.node.type_group){ - 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); - }else if (n.data.node.type_domain){ - emitter.emit('domainNodeClicked', n.data.node.label); - }else if (n.data.node.type_gpo){ - emitter.emit('gpoNodeClicked', n.data.node.label, n.data.node.guid); - }else if (n.data.node.type_ou){ - emitter.emit('ouNodeClicked', n.data.node.label, n.data.node.guid, n.data.node.blocksInheritance); + _nodeClicked(n) { + if (!this.state.dragged) { + if (n.data.node.type_user) { + emitter.emit("userNodeClicked", n.data.node.label); + } else if (n.data.node.type_group) { + 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); + } else if (n.data.node.type_domain) { + emitter.emit("domainNodeClicked", n.data.node.label); + } else if (n.data.node.type_gpo) { + emitter.emit( + "gpoNodeClicked", + n.data.node.label, + n.data.node.guid + ); + } else if (n.data.node.type_ou) { + emitter.emit( + "ouNodeClicked", + n.data.node.label, + n.data.node.guid, + n.data.node.blocksInheritance + ); } - }else{ - this.setState({dragged: false}); + } else { + this.setState({ dragged: false }); } } - initializeSigma(){ - var sigmaInstance, design; + initializeSigma() { + let sigmaInstance, design; - sigmaInstance = new sigma( - { - container: 'graph' - } - ); + sigmaInstance = new sigma({ + container: "graph" + }); - sigmaInstance.settings( - { - edgeColor: 'default', - nodeColor: 'default', - minEdgeSize: 1, - maxEdgeSize: 2.5, - iconThreshold: 4, - labelThreshold: 15, - labelAlignment: 'bottom', - labelColor: 'default', - font: 'Roboto', - glyphFillColor: 'black', - glyphTextColor: 'white', - glyphTextThreshold: 1, - zoomingRatio: 1.4, - scalingMode: 'inside' - } - ); + sigmaInstance.settings({ + edgeColor: "default", + nodeColor: "default", + minEdgeSize: 1, + maxEdgeSize: 2.5, + iconThreshold: 4, + labelThreshold: 15, + labelAlignment: "bottom", + labelColor: "default", + font: "Roboto", + glyphFillColor: "black", + glyphTextColor: "white", + glyphTextThreshold: 1, + zoomingRatio: 1.4, + scalingMode: "inside" + }); //Bind sigma events - sigmaInstance.renderers[0].bind('render', function(e) { + sigmaInstance.renderers[0].bind("render", function(e) { sigmaInstance.renderers[0].glyphs(); }); - sigmaInstance.camera.bind('coordinatesUpdated', function(e){ - if (appStore.performance.edgeLabels === 0){ + sigmaInstance.camera.bind("coordinatesUpdated", function(e) { + if (appStore.performance.edgeLabels === 0) { if (e.target.ratio > 1.25) { - sigmaInstance.settings('drawEdgeLabels', false); + sigmaInstance.settings("drawEdgeLabels", false); } else { - sigmaInstance.settings('drawEdgeLabels', true); + sigmaInstance.settings("drawEdgeLabels", true); } - }else if (appStore.performance.edgeLabels === 1){ - sigmaInstance.settings('drawEdgeLabels', true); - }else{ - sigmaInstance.settings('drawEdgeLabels', false); + } else if (appStore.performance.edgeLabels === 1) { + sigmaInstance.settings("drawEdgeLabels", true); + } else { + sigmaInstance.settings("drawEdgeLabels", false); } }); - 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, []); + 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, + [] + ); + } + + if (appStore.startNode !== null) { + findGraphPath( + this.state.sigmaInstance, + true, + e.data.enter.nodes[0].id, + [] + ); + } + + sigmaInstance.refresh({ skipIndexation: true }); } - if (appStore.startNode !== null) { - findGraphPath(this.state.sigmaInstance, true, e.data.enter.nodes[0].id, []); + if (e.data.leave.nodes.length > 0) { + if (appStore.highlightedEdges.length > 0) { + $.each(appStore.highlightedEdges, function( + index, + edge + ) { + edge.color = + sigmaInstance.settings.defaultEdgeColor; + }); + appStore.highlightedEdges = []; + sigmaInstance.refresh({ skipIndexation: true }); + } } - - sigmaInstance.refresh({'skipIndexation': true}); - } - - if (e.data.leave.nodes.length > 0) { - if (appStore.highlightedEdges.length > 0) { - $.each(appStore.highlightedEdges, function(index, edge) { - edge.color = sigmaInstance.settings.defaultEdgeColor; - }); - appStore.highlightedEdges = []; - 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; + $(window).on( + "keyup", + function(e) { + let key = e.keyCode ? e.keyCode : e.which; + let mode = appStore.performance.nodeLabels; + let sigmaInstance = this.state.sigmaInstance; - if (document.activeElement === document.body && key === 17){ - mode = mode + 1; - if (mode > 2){ - mode = 0; + if (document.activeElement === document.body && key === 17) { + mode = mode + 1; + if (mode > 2) { + mode = 0; + } + appStore.performance.nodeLabels = mode; + conf.set("performance", appStore.performance); + + if (mode === 2) { + sigmaInstance.settings("labelThreshold", 500); + emitter.emit("showAlert", "Hiding Node Labels"); + } else if (mode === 0) { + sigmaInstance.settings("labelThreshold", 15); + emitter.emit( + "showAlert", + "Default Node Label Threshold" + ); + } else { + sigmaInstance.settings("labelThreshold", 1); + emitter.emit("showAlert", "Always Showing Node Labels"); + } + + sigmaInstance.refresh({ skipIndexation: true }); } - appStore.performance.nodeLabels = mode; - conf.set('performance', appStore.performance); - - if (mode === 2){ - sigmaInstance.settings('labelThreshold', 500); - emitter.emit('showAlert', 'Hiding Node Labels'); - }else if (mode === 0){ - sigmaInstance.settings('labelThreshold', 15); - emitter.emit('showAlert', 'Default Node Label Threshold'); - }else{ - sigmaInstance.settings('labelThreshold', 1); - emitter.emit('showAlert', 'Always Showing Node Labels'); - } - - sigmaInstance.refresh({'skipIndexation' : true}); - } - }.bind(this)); + }.bind(this) + ); //Plugin Configuration - var dragListener = sigma.plugins.dragNodes(sigmaInstance, - sigmaInstance.renderers[0]); + var dragListener = sigma.plugins.dragNodes( + sigmaInstance, + sigmaInstance.renderers[0] + ); - dragListener.bind('drag', this._nodeDragged.bind(this)); + dragListener.bind("drag", this._nodeDragged.bind(this)); var tooltips = sigma.plugins.tooltips( - sigmaInstance, - sigmaInstance.renderers[0], + sigmaInstance, + sigmaInstance.renderers[0], { - node: [{ - show: 'rightClickNode', - cssClass: 'new-tooltip', - autoadjust: true, - renderer: function(node) { - var template = this.state.template; - 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') { - node.expand = true; - } else { - node.collapse = true; + node: [ + { + show: "rightClickNode", + cssClass: "new-tooltip", + autoadjust: true, + renderer: function(node) { + var template = this.state.template; + 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" + ) { + node.expand = true; + } else { + node.collapse = true; + } } - } - return Mustache.render(template, node); - }.bind(this) - }] + return Mustache.render(template, node); + }.bind(this) + } + ] } ); - tooltips.bind('shown', function(event) { + tooltips.bind("shown", function(event) { appStore.currentTooltip = event.target; }); - tooltips.bind('hidden', function(event) { + tooltips.bind("hidden", function(event) { appStore.currentTooltip = null; }); - //Layout Plugins var forcelinkListener = sigma.layouts.configForceLink(sigmaInstance, { worker: true, background: true, - easing: 'cubicInOut', + easing: "cubicInOut", autoStop: true, alignNodeSiblings: true, barnesHutOptimize: true, - randomize: 'globally' + randomize: "globally" }); - forcelinkListener.bind('stop', function(event) { - emitter.emit('updateLoadingText', "Fixing Overlap"); + 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); + forcelinkListener.bind("start", function(event) { + emitter.emit("updateLoadingText", "Initial Layout"); + emitter.emit("showLoadingIndicator", true); }); var dagreListener = sigma.layouts.dagre.configure(sigmaInstance, { - easing: 'cubicInOut', - boundingBox: {minX: 0, minY: 0, maxX:$('#graph').outerWidth(), maxY:$('#graph').outerHeight() }, + easing: "cubicInOut", + boundingBox: { + minX: 0, + minY: 0, + maxX: $("#graph").outerWidth(), + maxY: $("#graph").outerHeight() + }, background: true, - rankDir: 'LR' + rankDir: "LR" }); - dagreListener.bind('stop', function(event){ + dagreListener.bind("stop", function(event) { var needsfix = false; sigmaInstance.graph.nodes().forEach(function(node) { - if (isNaN(node.x) || isNaN(node.y)){ - emitter.emit('updateLoadingText', "Fixing Overlap"); + if (isNaN(node.x) || isNaN(node.y)) { + emitter.emit("updateLoadingText", "Fixing Overlap"); sigmaInstance.startNoverlap(); needsfix = true; return; } }, this); - if (!needsfix){ - emitter.emit('updateLoadingText', 'Done!'); + if (!needsfix) { + emitter.emit("updateLoadingText", "Done!"); sigma.canvas.edges.autoCurve(sigmaInstance); - setTimeout(function(){ - emitter.emit('showLoadingIndicator', false); + setTimeout(function() { + emitter.emit("showLoadingIndicator", false); }, 1500); } }); - dagreListener.bind('start', function(event){ - emitter.emit('updateLoadingText', 'Initial Layout'); - emitter.emit('showLoadingIndicator', true); + dagreListener.bind("start", function(event) { + emitter.emit("updateLoadingText", "Initial Layout"); + emitter.emit("showLoadingIndicator", true); }); // var noverlapListener = sigmaInstance.configNoverlap({ // nodeMargin: 5.0, // easing: 'cubicInOut', // gridSize: 20, - // permittedExpansion: 1.3 + // permittedExpansion: 1.3 // }); - // - + // + var noverlapListener = sigmaInstance.configNoverlap({}); - noverlapListener.bind('stop', function(event) { - emitter.emit('updateLoadingText', 'Done!'); + noverlapListener.bind("stop", function(event) { + emitter.emit("updateLoadingText", "Done!"); sigma.canvas.edges.autoCurve(sigmaInstance); - setTimeout(function(){ - emitter.emit('showLoadingIndicator', false); + setTimeout(function() { + emitter.emit("showLoadingIndicator", false); }, 1500); - }); - var lowgfx = appStore.performance.lowGraphics; design = sigma.plugins.design(sigmaInstance); - if (lowgfx){ - sigmaInstance.settings('defaultEdgeType', 'line'); - sigmaInstance.settings('defaultEdgeColor', 'black'); + if (lowgfx) { + sigmaInstance.settings("defaultEdgeType", "line"); + sigmaInstance.settings("defaultEdgeColor", "black"); design.setPalette(appStore.lowResPalette); design.setStyles(appStore.lowResStyle); - }else{ - sigmaInstance.settings('defaultEdgeType', 'tapered'); - sigmaInstance.settings('defaultEdgeColor', '#356'); + } else { + sigmaInstance.settings("defaultEdgeType", "tapered"); + sigmaInstance.settings("defaultEdgeColor", "#356"); design.setPalette(appStore.highResPalette); design.setStyles(appStore.highResStyle); } var mode = appStore.performance.nodeLabels; - if (mode === 2){ - sigmaInstance.settings('labelThreshold', 500); - }else if (mode === 0){ - sigmaInstance.settings('labelThreshold', 15); - }else{ - sigmaInstance.settings('labelThreshold', 1); + if (mode === 2) { + sigmaInstance.settings("labelThreshold", 500); + } else if (mode === 0) { + sigmaInstance.settings("labelThreshold", 15); + } else { + sigmaInstance.settings("labelThreshold", 1); } this.setState({ diff --git a/src/components/Icon.jsx b/src/components/Icon.jsx index 519f6de..b4c8588 100644 --- a/src/components/Icon.jsx +++ b/src/components/Icon.jsx @@ -1,19 +1,26 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types' +import React, { Component } from "react"; +import PropTypes from "prop-types"; export default class Icon extends Component { - constructor(props){ - super(props); - } + constructor(props) { + super(props); + } - render() { - return ( - - ); - } + render() { + return ( + + ); + } } -Icon.propTypes = { - glyph : PropTypes.string.isRequired, - extraClass : PropTypes.string -} \ No newline at end of file +Icon.propTypes = { + glyph: PropTypes.string.isRequired, + extraClass: PropTypes.string +}; diff --git a/src/components/Menu/MenuButton.jsx b/src/components/Menu/MenuButton.jsx index 697e4f0..46abda1 100644 --- a/src/components/Menu/MenuButton.jsx +++ b/src/components/Menu/MenuButton.jsx @@ -1,47 +1,66 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types' +import React, { Component } from "react"; +import PropTypes from "prop-types"; export default class MenuButton extends Component { - _leave(e){ - var target = $(e.target) - target.css('width', 'auto') - var oldWidth = target.width(); - target.html(''.format(this.props.glyphicon)) - var newWidth = target.outerWidth(); - target.width(oldWidth); - target.animate({ - width: newWidth + 'px' - }, 100) - } + _leave(e) { + var target = $(e.target); + target.css("width", "auto"); + var oldWidth = target.width(); + target.html(''.format(this.props.glyphicon)); + var newWidth = target.outerWidth(); + target.width(oldWidth); + target.animate( + { + width: newWidth + "px" + }, + 100 + ); + } - _enter(e){ - var target = $(e.target) - target.css('width', 'auto') - var oldWidth = target.width(); - target.html('{} '.format(this.props.hoverVal, this.props.glyphicon)) - var newWidth = target.outerWidth(); - target.width(oldWidth); - target.animate({ - width: newWidth + 'px' - }, 100) - } + _enter(e) { + var target = $(e.target); + target.css("width", "auto"); + var oldWidth = target.width(); + target.html( + '{} '.format( + this.props.hoverVal, + this.props.glyphicon + ) + ); + var newWidth = target.outerWidth(); + target.width(oldWidth); + target.animate( + { + width: newWidth + "px" + }, + 100 + ); + } - componentDidMount(){ - $(this.refs.btn).html(''.format(this.props.glyphicon)) - } + componentDidMount() { + $(this.refs.btn).html( + ''.format(this.props.glyphicon) + ); + } - render() { - var c = "glyphicon glyphicon-" + this.props.glyphicon - return ( - - ); - } + render() { + var c = "glyphicon glyphicon-" + this.props.glyphicon; + return ( + + ); + } } -MenuButton.propTypes = { - hoverVal : PropTypes.string.isRequired, - glyphicon : PropTypes.string.isRequired, - click : PropTypes.func.isRequired -} \ No newline at end of file +MenuButton.propTypes = { + hoverVal: PropTypes.string.isRequired, + glyphicon: PropTypes.string.isRequired, + click: PropTypes.func.isRequired +}; diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index dcdd117..aae4737 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -1,22 +1,30 @@ -import React, { Component } from 'react'; -import MenuButton from './MenuButton'; -import ProgressBarMenuButton from './ProgressBarMenuButton'; -import { buildGpoAdminJson, buildSessionJson, buildUserJson,buildComputerJson, buildDomainJson, buildGpoJson, buildGroupJson, buildOuJson} from 'utils'; -import { If, Then, Else } from 'react-if'; -const { dialog, app } = require('electron').remote; -var fs = require('fs'); -var async = require('async'); -var unzip = require('unzipper'); -var fpath = require('path'); -var csv = require('fast-csv'); +import React, { Component } from "react"; +import MenuButton from "./MenuButton"; +import ProgressBarMenuButton from "./ProgressBarMenuButton"; +import { + buildGpoAdminJson, + buildSessionJson, + buildUserJson, + buildComputerJson, + buildDomainJson, + buildGpoJson, + buildGroupJson, + buildOuJson +} from "utils"; +import { If, Then, Else } from "react-if"; +const { dialog, app } = require("electron").remote; +var fs = require("fs"); +var async = require("async"); +var unzip = require("unzipper"); +var fpath = require("path"); -const Pick = require('stream-json/filters/Pick'); -const {streamArray} = require('stream-json/streamers/StreamArray'); -const {chain} = require('stream-chain'); -const Asm = require('stream-json/Assembler'); +const Pick = require("stream-json/filters/Pick"); +const { streamArray } = require("stream-json/streamers/StreamArray"); +const { chain } = require("stream-chain"); +const Asm = require("stream-json/Assembler"); export default class MenuContainer extends Component { - constructor(){ + constructor() { super(); this.state = { @@ -26,95 +34,114 @@ export default class MenuContainer extends Component { cancelled: false }; - emitter.on('cancelUpload', this.cancelUpload.bind(this)) + emitter.on("cancelUpload", this.cancelUpload.bind(this)); } - cancelUpload(){ - this.setState({cancelled: true}); - setTimeout(function(){ - this.setState({uploading: false}) - }.bind(this), 1000); + cancelUpload() { + this.setState({ cancelled: true }); + setTimeout( + function() { + this.setState({ uploading: false }); + }.bind(this), + 1000 + ); } - _refreshClick(){ - emitter.emit('graphRefresh') + _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) + _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'); + _exportClick() { + emitter.emit("showExport"); } - _importClick(){ + _importClick() { var fname = dialog.showOpenDialog({ - properties: ['openFile'] + properties: ["openFile"] }); - if (typeof fname !== 'undefined'){ - emitter.emit('import',fname[0]); + if (typeof fname !== "undefined") { + emitter.emit("import", fname[0]); } } - _settingsClick(){ - emitter.emit('openSettings') + _settingsClick() { + emitter.emit("openSettings"); } - _cancelUploadClick(){ - emitter.emit('showCancelUpload') + _cancelUploadClick() { + emitter.emit("showCancelUpload"); } - _uploadClick(){ + _uploadClick() { var input = jQuery(this.refs.fileInput); var fileNames = []; - $.each(input[0].files, function(index, file){ - fileNames.push({path:file.path, name:file.name}); + $.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.getFileMeta(file.path, callback); - }.bind(this), - function done(){ - setTimeout(function(){ - this.setState({uploading: false}); - }.bind(this), 3000); - $.each(results, function(index, file){ - if (file.delete){ - fs.unlinkSync(file.path); - } - }); - }.bind(this)); - - input.val(''); - }.bind(this)); - + this.unzipNecessary(fileNames).then( + function(results) { + async.eachSeries( + results, + function(file, callback) { + emitter.emit( + "showAlert", + "Processing file {}".format(file.name) + ); + this.getFileMeta(file.path, callback); + }.bind(this), + function done() { + setTimeout( + function() { + this.setState({ uploading: false }); + }.bind(this), + 3000 + ); + $.each(results, function(index, file) { + if (file.delete) { + fs.unlinkSync(file.path); + } + }); + }.bind(this) + ); + + input.val(""); + }.bind(this) + ); } - async unzipNecessary(files){ + async unzipNecessary(files) { var index = 0; var processed = []; - var tempPath = app.getPath('temp'); - while (index < files.length){ + 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) + if (path.endsWith(".zip")) { + await fs + .createReadStream(path) .pipe(unzip.Parse()) - .on('entry', function(entry){ + .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}); + processed.push({ + path: output, + name: entry.path, + delete: true + }); + }) + .promise(); + } else { + processed.push({ path: path, name: name, delete: false }); } index++; } @@ -122,48 +149,59 @@ export default class MenuContainer extends Component { return processed; } - _aboutClick(){ - emitter.emit('showAbout'); + _aboutClick() { + emitter.emit("showAbout"); } - getFileMeta(file, callback){ - let acceptableTypes = ["sessions","ous","groups","gpoadmins","gpos","computers","users","domains"]; + getFileMeta(file, callback) { + let acceptableTypes = [ + "sessions", + "ous", + "groups", + "gpoadmins", + "gpos", + "computers", + "users", + "domains" + ]; let count; let type; - console.log(file) + console.log(file); let pipeline = chain([ - fs.createReadStream(file, {encoding: 'utf8'}), - Pick.withParser({filter:'meta'}) + fs.createReadStream(file, { encoding: "utf8" }), + Pick.withParser({ filter: "meta" }) ]); let asm = Asm.connectTo(pipeline); - asm.on('done', function(asm){ - let data = asm.current - count = data.count - type = data.type + asm.on( + "done", + function(asm) { + let data = asm.current; + count = data.count; + type = data.type; - if (!acceptableTypes.includes(type)){ - emitter.emit('showAlert', 'Unrecognized JSON Type'); - callback(); - } - - this.processJson(file, callback, count, type) - }.bind(this)) + if (!acceptableTypes.includes(type)) { + emitter.emit("showAlert", "Unrecognized JSON Type"); + callback(); + } + this.processJson(file, callback, count, type); + }.bind(this) + ); } - processJson(file, callback, count, type){ + processJson(file, callback, count, type) { let pipeline = chain([ - fs.createReadStream(file, {encoding: 'utf8'}), - Pick.withParser({filter:type}), + fs.createReadStream(file, { encoding: "utf8" }), + Pick.withParser({ filter: type }), streamArray() - ]) + ]); let localcount = 0; let sent = 0; - let chunk = [] + let chunk = []; //Start a timer for fun this.setState({ @@ -171,30 +209,37 @@ export default class MenuContainer extends Component { progress: 0 }); - console.time('IngestTime') + console.time("IngestTime"); - pipeline.on('data', async function(data){ - chunk.push(data.value) - localcount++; + pipeline + .on( + "data", + async function(data) { + chunk.push(data.value); + localcount++; - if (localcount % 100 === 0){ - pipeline.pause(); - await this.uploadDataNew(chunk, type) - sent += chunk.length; - this.setState({ - progress: Math.floor(sent / count * 100) - }); - chunk = [] - pipeline.resume(); - } - - }.bind(this)).on('end', async function(){ - await this.uploadDataNew(chunk, type) - this.setState({progress:100}); - emitter.emit('refreshDBData'); - console.timeEnd('IngestTime'); - callback() - }.bind(this)) + if (localcount % 100 === 0) { + pipeline.pause(); + await this.uploadData(chunk, type); + sent += chunk.length; + this.setState({ + progress: Math.floor((sent / count) * 100) + }); + chunk = []; + pipeline.resume(); + } + }.bind(this) + ) + .on( + "end", + async function() { + await this.uploadData(chunk, type); + this.setState({ progress: 100 }); + emitter.emit("refreshDBData"); + console.timeEnd("IngestTime"); + callback(); + }.bind(this) + ); } //DO NOT USE THIS FUNCTION FOR ANYTHING, ITS ONLY FOR TESTING @@ -202,205 +247,105 @@ export default class MenuContainer extends Component { return new Promise(resolve => setTimeout(resolve, ms)); } - async uploadDataNew(chunk, type){ + async uploadData(chunk, type) { let session = driver.session(); - let funcMap = {'computers' : buildComputerJson, 'domains': buildDomainJson, 'gpos': buildGpoJson, 'users': buildUserJson, - 'groups': buildGroupJson, 'ous': buildOuJson, 'sessions': buildSessionJson, 'gpoadmins':buildGpoAdminJson} - let data = funcMap[type](chunk) + let funcMap = { + computers: buildComputerJson, + domains: buildDomainJson, + gpos: buildGpoJson, + users: buildUserJson, + groups: buildGroupJson, + ous: buildOuJson, + sessions: buildSessionJson, + gpoadmins: buildGpoAdminJson + }; + let data = funcMap[type](chunk); - for (let key in data){ - await session.run(data[key].statement, {props: data[key].props}).catch(function(error){ - console.log(error) - }) + for (let key in data) { + await session + .run(data[key].statement, { props: data[key].props }) + .catch(function(error) { + console.log(error); + }); } - + session.close(); } - - processFileOld(file, callback){ - console.log(file); - var count = 0; - var header = ""; - var processHeader = true; - var fileType; - - //Lets calculate the number of lines in the file and get the header - var input = fs.createReadStream(file); - input.on('data', function (chunk){ - for (var i=0; i < chunk.length; ++i){ - if (processHeader){ - header = header + String.fromCharCode(chunk[i]); - } - if (chunk[i] === 10){ - //At the first newline, we look at the header to figure out - if (processHeader){ - processHeader = false; - fileType = findObjectType(header); - if (fileType === 'unknown'){ - emitter.emit('showAlert', 'Unrecognized CSV Type'); - input.close(); - } - } - count++; - } - } - }) - .on('end', function(){ - //We've got our line count for progress - var chunk = []; - var localCount = 0; - var sent = 0; - - //Subtract one line to account for the header - count--; - - //Change the UI to display our uploading state - this.setState({ - uploading: true, - progress: 0 - }); - - //Start a timer - console.time('IngestTime'); - - //Start parsing the file - var parser = csv.fromStream(fs.createReadStream(file), - { - headers: true, - ignoreEmpty: true - }) - .on('data', function(data){ - //On each row, push it into an array and increment a counter - chunk.push(data); - localCount++; - - //If we've collected 10k rows, push it all to the DB. - if (localCount % 10000 === 0){ - //Pause the parser until upload is complete - parser.pause(); - this.uploadData(chunk, fileType, count) - .then(function(){ - //Update the sent number, and resume the parser - sent += chunk.length; - this.setState({progress: Math.floor(sent / count * 100)}); - - chunk = []; - parser.resume(); - }.bind(this)); - } - }.bind(this)) - .on('end', function(){ - //Upload any remaining data - this.uploadData(chunk, fileType, count) - .then(function(){ - //Set the uploading state to 100%, refresh the db display, and move on to the next file if there is one - this.setState({progress:100}); - emitter.emit('refreshDBData'); - console.timeEnd('IngestTime'); - callback(); - }.bind(this)); - }.bind(this)); - }.bind(this)); - } - - - - async uploadData(currentChunk, filetype, total){ - var index = 0; - var processed; - var sent = 0; - var session = driver.session(); - - if (filetype === 'groupmembership'){ - processed = buildGroupMembershipProps(currentChunk); - for (let skey in processed) { - await session.run(processed[skey].statement, { props: processed[skey].props }); - } - }else if (filetype === 'localadmin'){ - processed = buildLocalAdminProps(currentChunk); - for (let skey in processed) { - await session.run(processed[skey].statement, { props: processed[skey].props }); - } - }else if (filetype === 'sessions'){ - processed = buildSessionProps(currentChunk); - - for (let skey in processed) { - await session.run(processed[skey].statement, { props: processed[skey].props }); - } - }else if (filetype === 'domain'){ - processed = buildDomainProps(currentChunk); - - for (let skey in processed) { - await session.run(processed[skey].statement, { props: processed[skey].props }); - } - }else if (filetype === 'acl'){ - processed = buildACLProps(currentChunk); - - for (let key in processed){ - await session.run(processed[key].statement, {props: processed[key].props}); - } - }else if (filetype === 'userprops'){ - $.each(currentChunk, function(index, obj){ - var spn = obj.ServicePrincipalNames; - var sh = obj.SidHistory; - - if (spn === ""){ - obj.ServicePrincipalNames = []; - }else{ - obj.ServicePrincipalNames = spn.split('|'); - } - }); - let 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,user.domain=prop.Domain,user.Title=prop.Title,user.HomeDir=prop.HomeDirectory'; - - await session.run(query, {props:currentChunk}); - }else if (filetype === 'compprops'){ - let 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),comp.domain=prop.Domain'; - await session.run(query, {props:currentChunk}); - }else if (filetype === 'structure'){ - processed = buildStructureProps(currentChunk); - for (let skey in processed){ - await session.run(processed[skey].statement, { props: processed[skey].props }); - } - }else if (filetype === 'gplink'){ - processed = buildGplinkProps(currentChunk); - for (let gkey in processed) { - await session.run(processed[gkey].statement, { props: processed[gkey].props }); - } - } - } render() { return (
- +
- +
- +
- + - { () => - - } - + + {() => ( + + )} + +
- +
- +
- +
- +
); } diff --git a/src/components/Menu/ProgressBarMenuButton.jsx b/src/components/Menu/ProgressBarMenuButton.jsx index 517d3b8..70c5de3 100644 --- a/src/components/Menu/ProgressBarMenuButton.jsx +++ b/src/components/Menu/ProgressBarMenuButton.jsx @@ -1,8 +1,8 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; export default class ProgressBarMenuButton extends Component { - constructor(){ + constructor() { super(); this.state = { @@ -10,14 +10,14 @@ export default class ProgressBarMenuButton extends Component { }; } - componentDidMount(){ - $(this.refs.btn).html('{}%'.format(this.props.progress)); - $(this.refs.btn).css('padding','6px 0px 6px 0px'); - $(this.refs.btn).css('width','41px'); + componentDidMount() { + $(this.refs.btn).html("{}%".format(this.props.progress)); + $(this.refs.btn).css("padding", "6px 0px 6px 0px"); + $(this.refs.btn).css("width", "41px"); } - componentWillReceiveProps(nextProps){ - if (this.state.expanded){ + componentWillReceiveProps(nextProps) { + if (this.state.expanded) { var template = `
@@ -26,31 +26,32 @@ export default class ProgressBarMenuButton extends Component {
`.formatAll(nextProps.progress); $(this.refs.btn).html(template); - }else{ - $(this.refs.btn).html('{}%'.format(nextProps.progress)); + } else { + $(this.refs.btn).html("{}%".format(nextProps.progress)); } this.forceUpdate(); } - shouldComponentUpdate(nextProps, nextState){ + shouldComponentUpdate(_nextProps, _nextState) { return true; } - _leave(e){ - this.setState({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); + target.html("{}%".format(this.props.progress)); + target.animate( + { + width: "41px" + }, + 100 + ); } - _enter(e){ - this.setState({expanded: true}); + _enter(e) { + this.setState({ expanded: true }); var target = $(e.target); - var oldWidth = target.width(); var template = `
@@ -60,21 +61,30 @@ export default class ProgressBarMenuButton extends Component {
`.formatAll(this.props.progress); - + target.html(template); - target.animate({ - width: '150px' - }, 100); + target.animate( + { + width: "150px" + }, + 100 + ); } render() { return ( - diff --git a/src/components/Modals/CancelUploadModal.jsx b/src/components/Modals/CancelUploadModal.jsx index 1535ed3..1d8fb36 100644 --- a/src/components/Modals/CancelUploadModal.jsx +++ b/src/components/Modals/CancelUploadModal.jsx @@ -1,57 +1,66 @@ -import React, { Component } from 'react'; +import React, { Component } from "react"; -var Modal = require('react-bootstrap').Modal; +import { Modal } from "react-bootstrap"; export default class CancelUploadModal extends Component { + constructor() { + super(); + this.state = { + open: false + }; + } - constructor(){ - super(); - this.state = { - open: false - } - } + closeModal() { + this.setState({ open: false }); + } - closeModal(){ - this.setState({ open: false }) - } + closeAndCancel() { + this.setState({ open: false }); + emitter.emit("cancelUpload"); + } - closeAndCancel(){ - this.setState({ open: false }) - emitter.emit('cancelUpload'); - } + openModal() { + this.setState({ open: true }); + } - openModal(){ - this.setState({open: true}) - } + componentDidMount() { + emitter.on("showCancelUpload", this.openModal.bind(this)); + } - componentDidMount() { - emitter.on('showCancelUpload', this.openModal.bind(this)) - } + render() { + return ( + + + + Cancel Upload + + - render() { - return ( - + +

Are you sure you want to cancel the upload?

+
- - Cancel Upload - - - -

Are you sure you want to cancel the upload?

-
- - - - - -
- ); - } + + + + +
+ ); + } } diff --git a/src/components/Modals/ClearConfirmModal.jsx b/src/components/Modals/ClearConfirmModal.jsx index 4523404..d6897c8 100644 --- a/src/components/Modals/ClearConfirmModal.jsx +++ b/src/components/Modals/ClearConfirmModal.jsx @@ -1,54 +1,70 @@ -import React, { Component } from 'react'; -import { clearDatabase } from 'utils' +import React, { Component } from "react"; +import { clearDatabase } from "utils"; -var Modal = require('react-bootstrap').Modal; +import { Modal } from "react-bootstrap"; export default class ClearConfirmModal extends Component { - constructor(){ - super(); - this.state = { - open: false - } - } + constructor() { + super(); + this.state = { + open: false + }; + } - openModal(){ - this.setState({open: true}) - } + openModal() { + this.setState({ open: true }); + } - closeModal(){ - this.setState({ open: false }) - } + closeModal() { + this.setState({ open: false }); + } - closeModalAndClearDB(){ - this.setState({open: false}) - emitter.emit('clearDB') - clearDatabase() - } + closeModalAndClearDB() { + this.setState({ open: false }); + emitter.emit("clearDB"); + clearDatabase(); + } - componentDidMount() { - emitter.on('openDBConfirm', this.openModal.bind(this)) - } + componentDidMount() { + emitter.on("openDBConfirm", this.openModal.bind(this)); + } - render() { - return ( - + render() { + return ( + + + + Clear Database + + - - Clear Database - + +

+ Are you ABSOLUTELY sure you want to clear the database? +

+
- -

Are you ABSOLUTELY sure you want to clear the database?

-
- - - - - -
- ); - } + + + + +
+ ); + } } diff --git a/src/components/Modals/ClearWarnModal.jsx b/src/components/Modals/ClearWarnModal.jsx index ea71d5e..da711b5 100644 --- a/src/components/Modals/ClearWarnModal.jsx +++ b/src/components/Modals/ClearWarnModal.jsx @@ -1,53 +1,69 @@ -import React, { Component } from 'react'; - -var Modal = require('react-bootstrap').Modal; +import React, { Component } from "react"; +import { Modal } from "react-bootstrap"; export default class ClearWarnModal extends Component { - constructor(){ - super(); + constructor() { + super(); - this.state = { - open: false - } - } + this.state = { + open: false + }; + } - closeModal(){ - this.setState({open: false}) - } + closeModal() { + this.setState({ open: false }); + } - openModal(){ - this.setState({open: true}) - } + openModal() { + this.setState({ open: true }); + } - closeAndOpenStep(){ - this.setState({ open: false }) - emitter.emit('openDBConfirm') - } + closeAndOpenStep() { + this.setState({ open: false }); + emitter.emit("openDBConfirm"); + } - componentDidMount(){ - emitter.on('openDBWarnModal', this.openModal.bind(this)) - } + componentDidMount() { + emitter.on("openDBWarnModal", this.openModal.bind(this)); + } - render() { - return ( - + render() { + return ( + + + + Clear Database + + - - Clear Database - + +

+ Are you sure you want to clear the database? This is + irreversible and may take some time! +

+
- -

Are you sure you want to clear the database? This is irreversible and may take some time!

-
- - - - - -
- ); - } + + + + +
+ ); + } } diff --git a/src/components/Modals/ClearingModal.jsx b/src/components/Modals/ClearingModal.jsx index d2e2941..f426e9d 100644 --- a/src/components/Modals/ClearingModal.jsx +++ b/src/components/Modals/ClearingModal.jsx @@ -1,38 +1,39 @@ -import React, { Component } from 'react'; - -var Modal = require('react-bootstrap').Modal; +import React, { Component } from "react"; +import { Modal } from "react-bootstrap"; export default class ClearingModal extends Component { - constructor(){ - super(); - this.state = { - open: false - } - } + constructor() { + super(); + this.state = { + open: false + }; + } - openModal(){ - this.setState({open: true}) - } + openModal() { + this.setState({ open: true }); + } - closeModal(){ - this.setState({ open: false }) - } + closeModal() { + this.setState({ open: false }); + } - componentDidMount() { - emitter.on('openClearingModal', this.openModal.bind(this)) - emitter.on('hideDBClearModal', this.closeModal.bind(this)) - } - render() { - return ( - - - - Clearing Data - - - ); - } + componentDidMount() { + emitter.on("openClearingModal", this.openModal.bind(this)); + emitter.on("hideDBClearModal", this.closeModal.bind(this)); + } + render() { + return ( + + + + Clearing Data + + + + ); + } } diff --git a/src/components/Modals/LogoutModal.jsx b/src/components/Modals/LogoutModal.jsx index 563f0c4..ff9aa96 100644 --- a/src/components/Modals/LogoutModal.jsx +++ b/src/components/Modals/LogoutModal.jsx @@ -1,63 +1,68 @@ -import React, { Component } from 'react'; -import ReactDOM from 'react-dom'; -import Login from '../Float/Login' - -var Modal = require('react-bootstrap').Modal; +import React, { Component } from "react"; +import { Modal } from "react-bootstrap"; export default class LogoutModal extends Component { - constructor(){ - super(); - this.state = { - open: false - } - } + constructor() { + super(); + this.state = { + open: false + }; + } - closeModal(){ - this.setState({ open: false }) - } + closeModal() { + this.setState({ open: false }); + } - closeAndLogout(){ - conf.delete('databaseInfo') - appStore.databaseInfo = null; - this.setState({ open: false }) - emitter.emit('doLogout'); - driver.close() - renderEmit.emit('logout') - } + closeAndLogout() { + conf.delete("databaseInfo"); + appStore.databaseInfo = null; + this.setState({ open: false }); + emitter.emit("doLogout"); + driver.close(); + renderEmit.emit("logout"); + } - openModal(){ - this.setState({open: true}) - } + openModal() { + this.setState({ open: true }); + } - componentDidMount() { - emitter.on('showLogout', this.openModal.bind(this)) - } + componentDidMount() { + emitter.on("showLogout", this.openModal.bind(this)); + } - render() { - return ( - + render() { + return ( + + + Logout + - - Logout - + +

Are you sure you want to logout?

+
- -

Are you sure you want to logout?

-
- - - - - -
- ); - } + + + + +
+ ); + } } diff --git a/src/components/Modals/SessionClearModal.jsx b/src/components/Modals/SessionClearModal.jsx index dcaf8f9..7717683 100644 --- a/src/components/Modals/SessionClearModal.jsx +++ b/src/components/Modals/SessionClearModal.jsx @@ -1,54 +1,68 @@ -import React, { Component } from 'react'; -import { clearSessions } from 'utils' +import React, { Component } from "react"; +import { clearSessions } from "utils"; -var Modal = require('react-bootstrap').Modal; +import { Modal } from "react-bootstrap"; export default class SessionClearModal extends Component { - constructor(){ - super(); + constructor() { + super(); - this.state = { - open: false - } - } + this.state = { + open: false + }; + } - closeModal(){ - this.setState({open: false}) - } + closeModal() { + this.setState({ open: false }); + } - openModal(){ - this.setState({open: true}) - } + openModal() { + this.setState({ open: true }); + } - closeAndClear(){ - this.setState({ open: false }) + closeAndClear() { + this.setState({ open: false }); clearSessions(); - } + } - componentDidMount(){ - emitter.on('openSessionClearModal', this.openModal.bind(this)) - } + componentDidMount() { + emitter.on("openSessionClearModal", this.openModal.bind(this)); + } - render() { - return ( - + render() { + return ( + + + + Clear Sessions + + - - Clear Sessions - + +

Are you sure you want to clear sessions?

+
- -

Are you sure you want to clear sessions?

-
- - - - - -
- ); - } + + + + +
+ ); + } } diff --git a/src/components/RawQuery.jsx b/src/components/RawQuery.jsx index e238788..19cd066 100644 --- a/src/components/RawQuery.jsx +++ b/src/components/RawQuery.jsx @@ -1,66 +1,81 @@ -import React, { Component } from 'react'; +import React, { Component } from "react"; export default class RawQuery extends Component { - constructor(){ - super(); - this.state = { - val : "", - open: false - } - } + constructor() { + super(); + this.state = { + val: "", + open: false + }; + } - _onChange(event){ - this.setState({ - val:event.target.value - }) - } + _onChange(event) { + this.setState({ + val: event.target.value + }); + } - _onKeyUp(e){ - var key = e.keyCode ? e.keyCode : e.which + _onKeyUp(e) { + var key = e.keyCode ? e.keyCode : e.which; - if (key === 13){ - emitter.emit('query', this.state.val) - } - } + if (key === 13) { + emitter.emit("query", this.state.val); + } + } - - componentWillMount() { - emitter.on('setRawQuery', this._setQueryFromEvent.bind(this)) - } - - componentDidMount() { - $(this.refs.input).slideToggle(0) - } + componentWillMount() { + emitter.on("setRawQuery", this._setQueryFromEvent.bind(this)); + } - _toggle(){ - $(this.refs.input).slideToggle() - this.setState({ - open: !this.state.open - }) - } + componentDidMount() { + $(this.refs.input).slideToggle(0); + } - _setQueryFromEvent(query){ - this.setState({val:query}); - } + _toggle() { + $(this.refs.input).slideToggle(); + this.setState({ + open: !this.state.open + }); + } - render() { - return ( -
- - -
- ); - } + _setQueryFromEvent(query) { + this.setState({ val: query }); + } + + render() { + return ( +
+ + +
+ ); + } } diff --git a/src/components/SearchContainer/SearchContainer.jsx b/src/components/SearchContainer/SearchContainer.jsx index f7f5e79..5a8b22e 100644 --- a/src/components/SearchContainer/SearchContainer.jsx +++ b/src/components/SearchContainer/SearchContainer.jsx @@ -1,15 +1,15 @@ -import React, { Component } from 'react'; -import GlyphiconSpan from '../GlyphiconSpan'; -import Icon from '../Icon'; -import TabContainer from './TabContainer'; -import {escapeRegExp} from 'utils'; +import React, { Component } from "react"; +import GlyphiconSpan from "../GlyphiconSpan"; +import Icon from "../Icon"; +import TabContainer from "./TabContainer"; +import { escapeRegExp } from "utils"; export default class SearchContainer extends Component { - constructor(props){ + constructor(props) { super(props); this.state = { - mainPlaceholder:"Start typing to search for a node...", + mainPlaceholder: "Start typing to search for a node...", pathfindingIsOpen: false, mainValue: "", pathfindValue: "" @@ -19,104 +19,140 @@ export default class SearchContainer extends Component { 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('gpoNodeClicked', this.openNodeTab.bind(this)); - emitter.on('ouNodeClicked', this.openNodeTab.bind(this)); - emitter.on('setStart', function (payload) { - jQuery(this.refs.searchbar).val(payload); - }.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("gpoNodeClicked", this.openNodeTab.bind(this)); + emitter.on("ouNodeClicked", this.openNodeTab.bind(this)); + emitter.on( + "setStart", + function(payload) { + jQuery(this.refs.searchbar).val(payload); + }.bind(this) + ); - emitter.on('setEnd', function (payload) { - jQuery(this.refs.pathbar).val(payload); - var e = jQuery(this.refs.pathfinding); - if (! e.is(":visible")) { - this.setState({pathfindingIsOpen: true}); - e.slideToggle(); - } - }.bind(this)); + emitter.on( + "setEnd", + function(payload) { + jQuery(this.refs.pathbar).val(payload); + var e = jQuery(this.refs.pathfinding); + if (!e.is(":visible")) { + this.setState({ pathfindingIsOpen: true }); + e.slideToggle(); + } + }.bind(this) + ); jQuery(this.refs.searchbar).typeahead({ - source: function (query, process) { + source: function(query, process) { let session = driver.session(); - if (query.includes(':')){ - let sp = query.split(':'); + if (query.includes(":")) { + let sp = query.split(":"); let type = sp[0]; let term = sp[1]; term = escapeRegExp(term); - let t = '(?i).*' + term + '.*'; - - let labels = ["OU","GPO","User","Computer","Group","Domain"]; - $.each(labels, function(_, l){ - if (l.toLowerCase() === type.toLowerCase()){ + let t = "(?i).*" + term + ".*"; + + let labels = [ + "OU", + "GPO", + "User", + "Computer", + "Group", + "Domain" + ]; + $.each(labels, function(_, l) { + if (l.toLowerCase() === type.toLowerCase()) { type = l; } }); let data = []; - session.run("MATCH (n:{}) WHERE n.name =~ {name} OR n.guid =~ {name} RETURN n LIMIT 10".format(type), { name: t }) - .then(function (results) { - let data = []; - let map = {}; - let index = 0; + session + .run( + "MATCH (n:{}) WHERE n.name =~ {name} OR n.guid =~ {name} RETURN n LIMIT 10".format( + type + ), + { name: t } + ) + .then( + function(results) { + let data = []; + let map = {}; - $.each(results.records, function (index, record) { - let props = {}; - props = record._fields[0].properties; - Object.assign(props, { - type:record._fields[0].labels[0] + $.each(results.records, function( + index, + record + ) { + let props = {}; + props = record._fields[0].properties; + Object.assign(props, { + type: record._fields[0].labels[0] + }); + map[index] = props; + data.push( + "{}#{}".format(props.name, index) + ); + index++; }); - map[index] = props; - data.push("{}#{}".format(props.name, index)); - index++; - }); - this.map = map; - session.close(); - return process(data); - }.bind(this)); - - }else{ + this.map = map; + session.close(); + return process(data); + }.bind(this) + ); + } else { let q = escapeRegExp(query); - let t = '(?i).*' + q + '.*'; - let data = []; - session.run("MATCH (n) WHERE n.name =~ {name} OR n.guid =~ {name} RETURN n LIMIT 10", { name: t }) - .then(function (results) { - let data = []; - let map = {}; - let index = 0; + let t = "(?i).*" + q + ".*"; + session + .run( + "MATCH (n) WHERE n.name =~ {name} OR n.guid =~ {name} RETURN n LIMIT 10", + { name: t } + ) + .then( + function(results) { + let data = []; + let map = {}; - $.each(results.records, function (index, record) { - let props = {}; - props = record._fields[0].properties; - Object.assign(props, { - type: record._fields[0].labels[0] + $.each(results.records, function( + index, + record + ) { + let props = {}; + props = record._fields[0].properties; + Object.assign(props, { + type: record._fields[0].labels[0] + }); + map[index] = props; + data.push( + "{}#{}".format(props.name, index) + ); + index++; }); - map[index] = props; - data.push("{}#{}".format(props.name, index)); - index++; - }); - this.map = map; - session.close(); - return process(data); - }.bind(this)); + this.map = map; + session.close(); + return process(data); + }.bind(this) + ); } - }, - afterSelect: function (selected) { + afterSelect: function(selected) { if (!this.state.pathfindingIsOpen) { let props = {}; let statement = ""; - if (selected.type === 'OU'){ - statement = "MATCH (n:{}) WHERE n.guid = {guid} RETURN n".format(selected.type); - props = {guid: selected.guid}; - }else{ - statement = "MATCH (n:{}) WHERE n.name = {name} RETURN n".format(selected.type); + if (selected.type === "OU") { + statement = "MATCH (n:{}) WHERE n.guid = {guid} RETURN n".format( + selected.type + ); + props = { guid: selected.guid }; + } else { + statement = "MATCH (n:{}) WHERE n.name = {name} RETURN n".format( + selected.type + ); props = { name: selected.name }; } - - emitter.emit('searchQuery', statement, props); + + emitter.emit("searchQuery", statement, props); } else { let start = jQuery(this.refs.searchbar).val(); let end = jQuery(this.refs.pathbar).val(); @@ -131,23 +167,34 @@ export default class SearchContainer extends Component { let query = ""; - let labels = ["OU", "GPO", "User", "Computer", "Group", "Domain"]; + let labels = [ + "OU", + "GPO", + "User", + "Computer", + "Group", + "Domain" + ]; if (start.includes(":")) { - let spl = start.split(':'); + let spl = start.split(":"); let type = spl[0]; let search = spl[1]; start = search; - $.each(labels, function (_, l) { + $.each(labels, function(_, l) { if (l.toLowerCase() === type.toLowerCase()) { type = l; } }); - if (type === 'OU' || type === 'GPO') { - query += "MATCH (n:{}) WHERE n.name =~ {aprop} OR n.guid =~ {aprop}".format(type); + if (type === "OU" || type === "GPO") { + query += "MATCH (n:{}) WHERE n.name =~ {aprop} OR n.guid =~ {aprop}".format( + type + ); } else { - query += "MATCH (n:{}) WHERE n.name =~ {aprop}".format(type); + query += "MATCH (n:{}) WHERE n.name =~ {aprop}".format( + type + ); } } else { query += "MATCH (n) WHERE n.name =~ {aprop}"; @@ -156,58 +203,44 @@ export default class SearchContainer extends Component { query += " WITH n "; if (end.includes(":")) { - let spl = end.split(':'); + let spl = end.split(":"); let type = spl[0]; let search = spl[1]; end = search; - $.each(labels, function (_, l) { + $.each(labels, function(_, l) { if (l.toLowerCase() === type.toLowerCase()) { type = l; } }); - if (type === 'OU' || type === 'GPO') { - query += "MATCH (m:{}) WHERE m.name =~ {bprop} OR m.guid =~ {bprop}".format(type); + if (type === "OU" || type === "GPO") { + query += "MATCH (m:{}) WHERE m.name =~ {bprop} OR m.guid =~ {bprop}".format( + type + ); } else { - query += "MATCH (m:{}) WHERE m.name =~ {bprop}".format(type); + query += "MATCH (m:{}) WHERE m.name =~ {bprop}".format( + type + ); } } else { query += "MATCH (m) WHERE m.name =~ {bprop}"; } - query += " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; + query += + " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; - emitter.emit('query', query, { aprop: start, bprop: end }); + emitter.emit("query", query, { aprop: start, bprop: end }); } }.bind(this), autoSelect: false, - updater: function (item) { + updater: function(item) { let spl = item.split("#"); - let name = spl[0]; let index = spl[1]; let obj = this.map[index]; return obj; }, - matcher: function(item){ - let spl = item.split("#"); - let name = spl[0]; - let index = spl[1]; - let obj = this.map[index]; - - let searchTerm = this.query; - if (this.query.includes(":")){ - searchTerm = searchTerm.split(":")[1]; - } - if (name.toLowerCase().indexOf(searchTerm.toLowerCase()) !==-1){ - return true; - }else if (obj.hasOwnProperty("guid") && obj['guid'].toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1){ - return true; - }else{ - return false; - } - }, - highlighter: function (item) { + matcher: function(item) { let spl = item.split("#"); let name = spl[0]; let index = spl[1]; @@ -217,200 +250,22 @@ export default class SearchContainer extends Component { if (this.query.includes(":")) { searchTerm = searchTerm.split(":")[1]; } - - let type = obj.type; - let icon = ""; - - switch (type){ - case "Group": - icon = ""; - break; - case "User": - icon = ""; - break; - case "Computer": - icon = ""; - break; - case "Domain": - icon = ""; - break; - case "GPO": - icon = ""; - break; - case "OU": - icon = ""; - break; - } - - let html = '
{}'.format(name); - - if (searchTerm !== ""){ - let reQuery = new RegExp('(' + searchTerm + ')', "gi"); - - html = html.replace(reQuery, "$1"); - } - html += icon + "
"; - let jElem = $(html); - - return jElem.html(); - } - }); - - jQuery(this.refs.pathbar).typeahead({ - source: function (query, process) { - let session = driver.session(); - if (query.includes(':')) { - let sp = query.split(':'); - let type = sp[0]; - let term = sp[1]; - term = escapeRegExp(term); - let t = '(?i).*' + term + '.*'; - - let labels = ["OU", "GPO", "User", "Computer", "Group", "Domain"]; - $.each(labels, function (_, l) { - if (l.toLowerCase() === type.toLowerCase()) { - type = l; - } - }); - - let data = []; - session.run("MATCH (n:{}) WHERE n.name =~ {name} OR n.guid =~ {name} RETURN n LIMIT 10".format(type), { name: t }) - .then(function (results) { - let data = []; - let map = {}; - let index = 0; - - $.each(results.records, function (index, record) { - let props = {}; - props = record._fields[0].properties; - Object.assign(props, { - type: record._fields[0].labels[0] - }); - map[index] = props; - data.push("{}#{}".format(props.name, index)); - index++; - }); - this.map = map; - session.close(); - return process(data); - }.bind(this)); - - } else { - let q = escapeRegExp(query); - let t = '(?i).*' + q + '.*'; - let data = []; - session.run("MATCH (n) WHERE n.name =~ {name} OR n.guid =~ {name} RETURN n LIMIT 10", { name: t }) - .then(function (results) { - let data = []; - let map = {}; - let index = 0; - - $.each(results.records, function (index, record) { - let props = {}; - props = record._fields[0].properties; - Object.assign(props, { - type: record._fields[0].labels[0] - }); - map[index] = props; - data.push("{}#{}".format(props.name, index)); - index++; - }); - this.map = map; - session.close(); - return process(data); - }.bind(this)); - } - - }, - afterSelect: function (selected) { - let start = jQuery(this.refs.searchbar).val(); - let end = jQuery(this.refs.pathbar).val(); - - if (start === "" || end === "") { - return; - } - - if (start === end) { - return; - } - - let query = ""; - - let labels = ["OU", "GPO", "User", "Computer", "Group", "Domain"]; - if (start.includes(":")) { - let spl = start.split(':'); - let type = spl[0]; - let search = spl[1]; - start = search; - - $.each(labels, function (_, l) { - if (l.toLowerCase() === type.toLowerCase()) { - type = l; - } - }); - - if (type === 'OU' || type === 'GPO') { - query += "MATCH (n:{}) WHERE n.name =~ {aprop} OR n.guid =~ {aprop}".format(type); - } else { - query += "MATCH (n:{}) WHERE n.name =~ {aprop}".format(type); - } - } else { - query += "MATCH (n) WHERE n.name =~ {aprop}"; - } - - query += " WITH n "; - - if (end.includes(":")) { - let spl = end.split(':'); - let type = spl[0]; - let search = spl[1]; - end = search; - - $.each(labels, function (_, l) { - if (l.toLowerCase() === type.toLowerCase()) { - type = l; - } - }); - - if (type === 'OU' || type === 'GPO') { - query += "MATCH (m:{}) WHERE m.name =~ {bprop} OR m.guid =~ {bprop}".format(type); - } else { - query += "MATCH (m:{}) WHERE m.name =~ {bprop}".format(type); - } - } else { - query += "MATCH (m) WHERE m.name =~ {bprop}"; - } - - query += " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; - - emitter.emit('query', query, { aprop: start, bprop: end }); - }.bind(this), - autoSelect: false, - updater: function (item) { - let spl = item.split("#"); - let index = spl[1]; - let obj = this.map[index]; - return obj; - }, - matcher: function (item) { - let spl = item.split("#"); - let name = spl[0]; - let index = spl[1]; - let obj = this.map[index]; - - let searchTerm = this.query; - if (this.query.includes(":")) { - searchTerm = searchTerm.split(":")[1]; - } - if (name.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1) { + if ( + name.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1 + ) { return true; - } else if (obj.hasOwnProperty("guid") && obj['guid'].toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1) { + } else if ( + obj.hasOwnProperty("guid") && + obj["guid"] + .toLowerCase() + .indexOf(searchTerm.toLowerCase()) !== -1 + ) { return true; } else { return false; } }, - highlighter: function (item) { + highlighter: function(item) { let spl = item.split("#"); let name = spl[0]; let index = spl[1]; @@ -426,29 +281,33 @@ export default class SearchContainer extends Component { switch (type) { case "Group": - icon = ""; + icon = + ''; break; case "User": - icon = ""; + icon = ''; break; case "Computer": - icon = ""; + icon = + ''; break; case "Domain": - icon = ""; + icon = + ''; break; case "GPO": - icon = ""; + icon = ''; break; case "OU": - icon = ""; + icon = + ''; break; } - let html = '
{}'.format(name); + let html = "
{}".format(name); if (searchTerm !== "") { - let reQuery = new RegExp('(' + searchTerm + ')', "gi"); + let reQuery = new RegExp("(" + searchTerm + ")", "gi"); html = html.replace(reQuery, "$1"); } @@ -458,152 +317,99 @@ export default class SearchContainer extends Component { return jElem.html(); } }); - } - + jQuery(this.refs.pathbar).typeahead({ + source: function(query, process) { + let session = driver.session(); + if (query.includes(":")) { + let sp = query.split(":"); + let type = sp[0]; + let term = sp[1]; + term = escapeRegExp(term); + let t = "(?i).*" + term + ".*"; - _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"; - this.setState({ - pathfindingIsOpen: p, - mainPlaceholder: t - }); - } - - _onPlayClick(){ - let start = jQuery(this.refs.searchbar).val(); - let end = jQuery(this.refs.pathbar).val(); - - if (start === "" || end === "") { - return; - } - - if (start === end){ - return; - } - - let query = ""; - - let labels = ["OU", "GPO", "User", "Computer", "Group", "Domain"]; - if (start.includes(":")) { - let spl = start.split(':'); - let type = spl[0]; - let search = spl[1]; - start = search; - - $.each(labels, function (_, l) { - if (l.toLowerCase() === type.toLowerCase()) { - type = l; - } - }); - - if (type === 'OU' || type === 'GPO') { - query += "MATCH (n:{}) WHERE n.name =~ {aprop} OR n.guid =~ {aprop}".format(type); - } else { - query += "MATCH (n:{}) WHERE n.name =~ {aprop}".format(type); - } - } else { - query += "MATCH (n) WHERE n.name =~ {aprop}"; - } - - query += " WITH n "; - - if (end.includes(":")) { - let spl = end.split(':'); - let type = spl[0]; - let search = spl[1]; - end = search; - - $.each(labels, function (_, l) { - if (l.toLowerCase() === type.toLowerCase()) { - type = l; - } - }); - - if (type === 'OU' || type === 'GPO') { - query += "MATCH (m:{}) WHERE m.name =~ {bprop} OR m.guid =~ {bprop}".format(type); - } else { - query += "MATCH (m:{}) WHERE m.name =~ {bprop}".format(type); - } - } else { - query += "MATCH (m) WHERE m.name =~ {bprop}"; - } - - query += " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; - - emitter.emit('query', query, { aprop: start, bprop: end }); - } - - _onExpandClick(){ - jQuery(this.refs.tabs).slideToggle(); - } - - openNodeTab(){ - var e = jQuery(this.refs.tabs); - if (!(e.is(":visible"))){ - e.slideToggle(); - } - } - - _inputKeyPress(e){ - let key = e.keyCode ? e.keyCode : e.which; - let start = jQuery(this.refs.searchbar).val(); - let end = jQuery(this.refs.pathbar).val(); - let stop = false; - - if (key === 13){ - if (!$('.searchSelectorS > ul').is(':hidden')){ - $('.searchSelectorS > ul li').each(function(i){ - if($(this).hasClass('active')){ - stop = true; - } - }); - } - - if (!$('.searchSelectorP > ul').is(':hidden')){ - $('.searchSelectorP > ul li').each(function(i){ - if($(this).hasClass('active')){ - stop = true; - } - }); - } - if (stop){ - return; - } - if (!this.state.pathfindingIsOpen) { - if (start !== ""){ - if (start.includes(':')){ - let spl = start.split(':'); - let type = spl[0]; - let search = spl[1]; - let statement = ""; - let regex = ""; - - let labels = ["OU", "GPO", "User", "Computer", "Group", "Domain"]; - $.each(labels, function (_, l) { - if (l.toLowerCase() === type.toLowerCase()) { - type = l; - } - }); - if (type === 'OU'){ - statement = "MATCH (n:{}) WHERE n.name =~ {search} OR n.guid =~ {search} RETURN n".format(type); - regex = '(?i).*' + search + '.*'; - }else{ - statement = "MATCH (n:{}) WHERE n.name =~ {search} RETURN n".format(type); - regex = '(?i).*' + search + '.*'; + let labels = [ + "OU", + "GPO", + "User", + "Computer", + "Group", + "Domain" + ]; + $.each(labels, function(_, l) { + if (l.toLowerCase() === type.toLowerCase()) { + type = l; } + }); - emitter.emit('searchQuery', statement, { search: regex }); - - }else{ - var statement = "MATCH (n) WHERE n.name =~ {regex} RETURN n"; - var regex = '(?i).*' + start + '.*'; - emitter.emit('searchQuery', statement, { regex: regex }); - } + session + .run( + "MATCH (n:{}) WHERE n.name =~ {name} OR n.guid =~ {name} RETURN n LIMIT 10".format( + type + ), + { name: t } + ) + .then( + function(results) { + let data = []; + let map = {}; + + $.each(results.records, function( + index, + record + ) { + let props = {}; + props = record._fields[0].properties; + Object.assign(props, { + type: record._fields[0].labels[0] + }); + map[index] = props; + data.push( + "{}#{}".format(props.name, index) + ); + index++; + }); + this.map = map; + session.close(); + return process(data); + }.bind(this) + ); + } else { + let q = escapeRegExp(query); + let t = "(?i).*" + q + ".*"; + session + .run( + "MATCH (n) WHERE n.name =~ {name} OR n.guid =~ {name} RETURN n LIMIT 10", + { name: t } + ) + .then( + function(results) { + let data = []; + let map = {}; + + $.each(results.records, function( + index, + record + ) { + let props = {}; + props = record._fields[0].properties; + Object.assign(props, { + type: record._fields[0].labels[0] + }); + map[index] = props; + data.push( + "{}#{}".format(props.name, index) + ); + index++; + }); + this.map = map; + session.close(); + return process(data); + }.bind(this) + ); } - } else { + }, + afterSelect: function(_) { let start = jQuery(this.refs.searchbar).val(); let end = jQuery(this.refs.pathbar).val(); @@ -617,23 +423,34 @@ export default class SearchContainer extends Component { let query = ""; - let labels = ["OU", "GPO", "User", "Computer", "Group", "Domain"]; + let labels = [ + "OU", + "GPO", + "User", + "Computer", + "Group", + "Domain" + ]; if (start.includes(":")) { - let spl = start.split(':'); + let spl = start.split(":"); let type = spl[0]; let search = spl[1]; start = search; - $.each(labels, function (_, l) { + $.each(labels, function(_, l) { if (l.toLowerCase() === type.toLowerCase()) { type = l; } }); - if (type === 'OU' || type === 'GPO') { - query += "MATCH (n:{}) WHERE n.name =~ {aprop} OR n.guid =~ {aprop}".format(type); + if (type === "OU" || type === "GPO") { + query += "MATCH (n:{}) WHERE n.name =~ {aprop} OR n.guid =~ {aprop}".format( + type + ); } else { - query += "MATCH (n:{}) WHERE n.name =~ {aprop}".format(type); + query += "MATCH (n:{}) WHERE n.name =~ {aprop}".format( + type + ); } } else { query += "MATCH (n) WHERE n.name =~ {aprop}"; @@ -642,81 +459,437 @@ export default class SearchContainer extends Component { query += " WITH n "; if (end.includes(":")) { - let spl = end.split(':'); + let spl = end.split(":"); let type = spl[0]; let search = spl[1]; end = search; - $.each(labels, function (_, l) { + $.each(labels, function(_, l) { if (l.toLowerCase() === type.toLowerCase()) { type = l; } }); - if (type === 'OU' || type === 'GPO') { - query += "MATCH (m:{}) WHERE m.name =~ {bprop} OR m.guid =~ {bprop}".format(type); + if (type === "OU" || type === "GPO") { + query += "MATCH (m:{}) WHERE m.name =~ {bprop} OR m.guid =~ {bprop}".format( + type + ); } else { - query += "MATCH (m:{}) WHERE m.name =~ {bprop}".format(type); + query += "MATCH (m:{}) WHERE m.name =~ {bprop}".format( + type + ); } } else { query += "MATCH (m) WHERE m.name =~ {bprop}"; } - query += " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; + query += + " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; - emitter.emit('query', query, { aprop: start, bprop: end }); + emitter.emit("query", query, { aprop: start, bprop: end }); + }.bind(this), + autoSelect: false, + updater: function(item) { + let spl = item.split("#"); + let index = spl[1]; + let obj = this.map[index]; + return obj; + }, + matcher: function(item) { + let spl = item.split("#"); + let name = spl[0]; + let index = spl[1]; + let obj = this.map[index]; + + let searchTerm = this.query; + if (this.query.includes(":")) { + searchTerm = searchTerm.split(":")[1]; + } + if ( + name.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1 + ) { + return true; + } else if ( + obj.hasOwnProperty("guid") && + obj["guid"] + .toLowerCase() + .indexOf(searchTerm.toLowerCase()) !== -1 + ) { + return true; + } else { + return false; + } + }, + highlighter: function(item) { + let spl = item.split("#"); + let name = spl[0]; + let index = spl[1]; + let obj = this.map[index]; + + let searchTerm = this.query; + if (this.query.includes(":")) { + searchTerm = searchTerm.split(":")[1]; + } + + let type = obj.type; + let icon = ""; + + switch (type) { + case "Group": + icon = + ''; + break; + case "User": + icon = ''; + break; + case "Computer": + icon = + ''; + break; + case "Domain": + icon = + ''; + break; + case "GPO": + icon = ''; + break; + case "OU": + icon = + ''; + break; + } + + let html = "
{}".format(name); + + if (searchTerm !== "") { + let reQuery = new RegExp("(" + searchTerm + ")", "gi"); + + html = html.replace(reQuery, "$1"); + } + html += icon + "
"; + let jElem = $(html); + + return jElem.html(); + } + }); + } + + _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"; + this.setState({ + pathfindingIsOpen: p, + mainPlaceholder: t + }); + } + + _onPlayClick() { + let start = jQuery(this.refs.searchbar).val(); + let end = jQuery(this.refs.pathbar).val(); + + if (start === "" || end === "") { + return; + } + + if (start === end) { + return; + } + + let query = ""; + + let labels = ["OU", "GPO", "User", "Computer", "Group", "Domain"]; + if (start.includes(":")) { + let spl = start.split(":"); + let type = spl[0]; + let search = spl[1]; + start = search; + + $.each(labels, function(_, l) { + if (l.toLowerCase() === type.toLowerCase()) { + type = l; + } + }); + + if (type === "OU" || type === "GPO") { + query += "MATCH (n:{}) WHERE n.name =~ {aprop} OR n.guid =~ {aprop}".format( + type + ); + } else { + query += "MATCH (n:{}) WHERE n.name =~ {aprop}".format(type); + } + } else { + query += "MATCH (n) WHERE n.name =~ {aprop}"; + } + + query += " WITH n "; + + if (end.includes(":")) { + let spl = end.split(":"); + let type = spl[0]; + let search = spl[1]; + end = search; + + $.each(labels, function(_, l) { + if (l.toLowerCase() === type.toLowerCase()) { + type = l; + } + }); + + if (type === "OU" || type === "GPO") { + query += "MATCH (m:{}) WHERE m.name =~ {bprop} OR m.guid =~ {bprop}".format( + type + ); + } else { + query += "MATCH (m:{}) WHERE m.name =~ {bprop}".format(type); + } + } else { + query += "MATCH (m) WHERE m.name =~ {bprop}"; + } + + query += + " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; + + emitter.emit("query", query, { aprop: start, bprop: end }); + } + + _onExpandClick() { + jQuery(this.refs.tabs).slideToggle(); + } + + openNodeTab() { + var e = jQuery(this.refs.tabs); + if (!e.is(":visible")) { + e.slideToggle(); + } + } + + _inputKeyPress(e) { + let key = e.keyCode ? e.keyCode : e.which; + let start = jQuery(this.refs.searchbar).val(); + let end = jQuery(this.refs.pathbar).val(); + let stop = false; + + if (key === 13) { + if (!$(".searchSelectorS > ul").is(":hidden")) { + $(".searchSelectorS > ul li").each(function(i) { + if ($(this).hasClass("active")) { + stop = true; + } + }); + } + + if (!$(".searchSelectorP > ul").is(":hidden")) { + $(".searchSelectorP > ul li").each(function(i) { + if ($(this).hasClass("active")) { + stop = true; + } + }); + } + if (stop) { + return; + } + if (!this.state.pathfindingIsOpen) { + if (start !== "") { + if (start.includes(":")) { + let spl = start.split(":"); + let type = spl[0]; + let search = spl[1]; + let statement = ""; + let regex = ""; + + let labels = [ + "OU", + "GPO", + "User", + "Computer", + "Group", + "Domain" + ]; + $.each(labels, function(_, l) { + if (l.toLowerCase() === type.toLowerCase()) { + type = l; + } + }); + if (type === "OU") { + statement = "MATCH (n:{}) WHERE n.name =~ {search} OR n.guid =~ {search} RETURN n".format( + type + ); + regex = "(?i).*" + search + ".*"; + } else { + statement = "MATCH (n:{}) WHERE n.name =~ {search} RETURN n".format( + type + ); + regex = "(?i).*" + search + ".*"; + } + + emitter.emit("searchQuery", statement, { + search: regex + }); + } else { + var statement = + "MATCH (n) WHERE n.name =~ {regex} RETURN n"; + var regex = "(?i).*" + start + ".*"; + emitter.emit("searchQuery", statement, { + regex: regex + }); + } + } + } else { + if (start === "" || end === "") { + return; + } + + if (start === end) { + return; + } + + let query = ""; + + let labels = [ + "OU", + "GPO", + "User", + "Computer", + "Group", + "Domain" + ]; + if (start.includes(":")) { + let spl = start.split(":"); + let type = spl[0]; + let search = spl[1]; + start = search; + + $.each(labels, function(_, l) { + if (l.toLowerCase() === type.toLowerCase()) { + type = l; + } + }); + + if (type === "OU" || type === "GPO") { + query += "MATCH (n:{}) WHERE n.name =~ {aprop} OR n.guid =~ {aprop}".format( + type + ); + } else { + query += "MATCH (n:{}) WHERE n.name =~ {aprop}".format( + type + ); + } + } else { + query += "MATCH (n) WHERE n.name =~ {aprop}"; + } + + query += " WITH n "; + + if (end.includes(":")) { + let spl = end.split(":"); + let type = spl[0]; + let search = spl[1]; + end = search; + + $.each(labels, function(_, l) { + if (l.toLowerCase() === type.toLowerCase()) { + type = l; + } + }); + + if (type === "OU" || type === "GPO") { + query += "MATCH (m:{}) WHERE m.name =~ {bprop} OR m.guid =~ {bprop}".format( + type + ); + } else { + query += "MATCH (m:{}) WHERE m.name =~ {bprop}".format( + type + ); + } + } else { + query += "MATCH (m) WHERE m.name =~ {bprop}"; + } + + query += + " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; + + emitter.emit("query", query, { aprop: start, bprop: end }); } } } - render(){ + render() { return (
- + click={this._onExpandClick.bind(this)} + > - - + + click={this._onPathfindClick.bind(this)} + > - + click={function() { + emitter.emit("graphBack"); + }} + >
- - + classes="input-group-addon spanfix invisible" + > + - - + + - +
@@ -728,4 +901,4 @@ export default class SearchContainer extends Component {
); } -} \ No newline at end of file +} diff --git a/src/components/SearchContainer/TabContainer.jsx b/src/components/SearchContainer/TabContainer.jsx index 6924213..d87074e 100644 --- a/src/components/SearchContainer/TabContainer.jsx +++ b/src/components/SearchContainer/TabContainer.jsx @@ -1,17 +1,17 @@ -import React, { Component } from 'react'; -import DatabaseDataDisplay from './Tabs/DatabaseDataDisplay'; -import PrebuiltQueriesDisplay from './Tabs/PrebuiltQueriesDisplay'; -import NoNodeData from './Tabs/NoNodeData'; -import UserNodeData from './Tabs/UserNodeData'; -import GroupNodeData from './Tabs/GroupNodeData'; -import ComputerNodeData from './Tabs/ComputerNodeData'; -import DomainNodeData from './Tabs/DomainNodeData'; -import GpoNodeData from './Tabs/GpoNodeData'; -import OuNodeData from './Tabs/OuNodeData'; -import { Tabs, Tab } from 'react-bootstrap'; +import React, { Component } from "react"; +import DatabaseDataDisplay from "./Tabs/DatabaseDataDisplay"; +import PrebuiltQueriesDisplay from "./Tabs/PrebuiltQueriesDisplay"; +import NoNodeData from "./Tabs/NoNodeData"; +import UserNodeData from "./Tabs/UserNodeData"; +import GroupNodeData from "./Tabs/GroupNodeData"; +import ComputerNodeData from "./Tabs/ComputerNodeData"; +import DomainNodeData from "./Tabs/DomainNodeData"; +import GpoNodeData from "./Tabs/GpoNodeData"; +import OuNodeData from "./Tabs/OuNodeData"; +import { Tabs, Tab } from "react-bootstrap"; export default class TabContainer extends Component { - constructor(props){ + constructor(props) { super(props); this.state = { @@ -24,17 +24,17 @@ export default class TabContainer extends Component { selected: 1 }; } - + componentDidMount() { - emitter.on('userNodeClicked', this._userNodeClicked.bind(this)); - emitter.on('groupNodeClicked', this._groupNodeClicked.bind(this)); - emitter.on('computerNodeClicked', this._computerNodeClicked.bind(this)); - emitter.on('domainNodeClicked', this._domainNodeClicked.bind(this)); - emitter.on('gpoNodeClicked', this._gpoNodeClicked.bind(this)); - emitter.on('ouNodeClicked', this._ouNodeClicked.bind(this)); + emitter.on("userNodeClicked", this._userNodeClicked.bind(this)); + emitter.on("groupNodeClicked", this._groupNodeClicked.bind(this)); + emitter.on("computerNodeClicked", this._computerNodeClicked.bind(this)); + emitter.on("domainNodeClicked", this._domainNodeClicked.bind(this)); + emitter.on("gpoNodeClicked", this._gpoNodeClicked.bind(this)); + emitter.on("ouNodeClicked", this._ouNodeClicked.bind(this)); } - _userNodeClicked(){ + _userNodeClicked() { this.setState({ userVisible: true, computerVisible: false, @@ -43,10 +43,10 @@ export default class TabContainer extends Component { gpoVisible: false, ouVisible: false }); - this.setState({selected: 2}); + this.setState({ selected: 2 }); } - _groupNodeClicked(){ + _groupNodeClicked() { this.setState({ userVisible: false, computerVisible: false, @@ -55,10 +55,10 @@ export default class TabContainer extends Component { gpoVisible: false, ouVisible: false }); - this.setState({selected: 2}); + this.setState({ selected: 2 }); } - _computerNodeClicked(){ + _computerNodeClicked() { this.setState({ userVisible: false, computerVisible: true, @@ -67,10 +67,10 @@ export default class TabContainer extends Component { gpoVisible: false, ouVisible: false }); - this.setState({selected: 2}); + this.setState({ selected: 2 }); } - _domainNodeClicked(){ + _domainNodeClicked() { this.setState({ userVisible: false, computerVisible: false, @@ -79,10 +79,10 @@ export default class TabContainer extends Component { gpoVisible: false, ouVisible: false }); - this.setState({selected: 2}); + this.setState({ selected: 2 }); } - _gpoNodeClicked(){ + _gpoNodeClicked() { this.setState({ userVisible: false, computerVisible: false, @@ -105,26 +105,42 @@ export default class TabContainer extends Component { }); this.setState({ selected: 2 }); } - - _handleSelect(index, last){ - this.setState({selected: index}); + + _handleSelect(index, last) { + this.setState({ selected: index }); } render() { return (
- + - - - - - - + + + + + + diff --git a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx index 04e37e7..91a6768 100644 --- a/src/components/SearchContainer/Tabs/ComputerNodeData.jsx +++ b/src/components/SearchContainer/Tabs/ComputerNodeData.jsx @@ -1,12 +1,12 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import NodeProps from './NodeProps'; -import NodeCypherLink from './NodeCypherLink'; -import NodeCypherNoNumberLink from './NodeCypherNoNumberLink'; -import NodeCypherLinkComplex from './NodeCypherLinkComplex'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import NodeProps from "./NodeProps"; +import NodeCypherLink from "./NodeCypherLink"; +import NodeCypherNoNumberLink from "./NodeCypherNoNumberLink"; +import NodeCypherLinkComplex from "./NodeCypherLinkComplex"; export default class ComputerNodeData extends Component { - constructor(){ + constructor() { super(); this.state = { @@ -14,14 +14,18 @@ export default class ComputerNodeData extends Component { driversessions: [], propertyMap: {}, ServicePrincipalNames: [], - displayMap: { "OperatingSystem": "OS", "Enabled": "Enabled", "UnconstrainedDelegation": "Allows Unconstrained Delegation"} + displayMap: { + operatingsystem: "OS", + enabled: "Enabled", + unconstraineddelegation: "Allows Unconstrained Delegation" + } }; - emitter.on('computerNodeClicked', this.getNodeData.bind(this)); + emitter.on("computerNodeClicked", this.getNodeData.bind(this)); } - getNodeData(payload){ - $.each(this.state.driversessions, function(index, record){ + getNodeData(payload) { + $.each(this.state.driversessions, function(_, record) { record.close(); }); this.setState({ @@ -31,72 +35,202 @@ export default class ComputerNodeData extends Component { }); var propCollection = driver.session(); - propCollection.run("MATCH (c:Computer {name:{name}}) RETURN c", {name:payload}) - .then(function(result){ - var properties = result.records[0]._fields[0].properties; - this.setState({propertyMap: properties}); - propCollection.close(); - }.bind(this)); - - this.setState({'driversessions': [propCollection]}); + propCollection + .run("MATCH (c:Computer {name:{name}}) RETURN c", { name: payload }) + .then( + function(result) { + var properties = result.records[0]._fields[0].properties; + if (typeof properties.serviceprincipalnames === 'undefined') { + this.setState({ ServicePrincipalNames: [] }); + } else { + this.setState({ ServicePrincipalNames: properties.serviceprincipalnames }); + } + this.setState({ propertyMap: properties }); + propCollection.close(); + }.bind(this) + ); + + this.setState({ driversessions: [propCollection] }); } render() { return (
-
+

Node Info

-
- Name -
-
- {this.state.label} -
- +
Name
+
{this.state.label}
+ - (n:User) WHERE NOT n.name ENDS WITH '$'"} start={this.state.label} distinct /> + (n:User) WHERE NOT n.name ENDS WITH '$'" + } + start={this.state.label} + distinct + /> - (o2:Computer {name:{name}}) WITH o1 OPTIONAL MATCH p1=(d)-[r2:Contains*1..]->(o1) OPTIONAL MATCH p2=(o1)-[r3:Contains]->(n) WHERE n:User OR n:Computer RETURN count(distinct(n))"} graphQuery={"MATCH (o1)-[r1:Contains]->(o2:Computer {name:{name}}) WITH o1 OPTIONAL MATCH p1=(d)-[r2:Contains*1..]->(o1) OPTIONAL MATCH p2=(o1)-[r3:Contains]->(n) WHERE n:User OR n:Computer RETURN p1,p2"} /> + (o2:Computer {name:{name}}) WITH o1 OPTIONAL MATCH p1=(d)-[r2:Contains*1..]->(o1) OPTIONAL MATCH p2=(o1)-[r3:Contains]->(n) WHERE n:User OR n:Computer RETURN count(distinct(n))" + } + graphQuery={ + "MATCH (o1)-[r1:Contains]->(o2:Computer {name:{name}}) WITH o1 OPTIONAL MATCH p1=(d)-[r2:Contains*1..]->(o1) OPTIONAL MATCH p2=(o1)-[r3:Contains]->(n) WHERE n:User OR n:Computer RETURN p1,p2" + } + /> - (container1)-[r2:Contains*1..]->(c) OPTIONAL MATCH p2 = (g2:GPO)-[r3:GpLink {enforced:false}]->(container2)-[r4:Contains*1..]->(c) WHERE NONE (x in NODES(p2) WHERE x.blocksInheritance = true AND x:OU AND NOT (g2)-->(x)) RETURN count(g1)+count(g2)"} graphQuery={"MATCH (c:Computer {name:{name}}) OPTIONAL MATCH p1 = (g1:GPO)-[r1:GpLink {enforced:true}]->(container1)-[r2:Contains*1..]->(c) OPTIONAL MATCH p2 = (g2:GPO)-[r3:GpLink {enforced:false}]->(container2)-[r4:Contains*1..]->(c) WHERE NONE (x in NODES(p2) WHERE x.blocksInheritance = true AND x:OU AND NOT (g2)-->(x)) RETURN p1,p2"} /> + (container1)-[r2:Contains*1..]->(c) OPTIONAL MATCH p2 = (g2:GPO)-[r3:GpLink {enforced:false}]->(container2)-[r4:Contains*1..]->(c) WHERE NONE (x in NODES(p2) WHERE x.blocksInheritance = true AND x:OU AND NOT (g2)-->(x)) RETURN count(g1)+count(g2)" + } + graphQuery={ + "MATCH (c:Computer {name:{name}}) OPTIONAL MATCH p1 = (g1:GPO)-[r1:GpLink {enforced:true}]->(container1)-[r2:Contains*1..]->(c) OPTIONAL MATCH p2 = (g2:GPO)-[r3:GpLink {enforced:false}]->(container2)-[r4:Contains*1..]->(c) WHERE NONE (x in NODES(p2) WHERE x.blocksInheritance = true AND x:OU AND NOT (g2)-->(x)) RETURN p1,p2" + } + /> - +

Local Admins

- (c:Computer {name:{name}})"} end={this.state.label} /> + (c:Computer {name:{name}})" + } + end={this.state.label} + /> - (m:Computer {name:{name}}) WHERE NOT n:Group"} end={this.state.label} distinct /> + (m:Computer {name:{name}}) WHERE NOT n:Group" + } + end={this.state.label} + distinct + /> - (m:Computer {name:{name}}))"} end={this.state.label} distinct /> + (m:Computer {name:{name}}))" + } + end={this.state.label} + distinct + />

Group Memberships

- - (n)"} start={this.state.label} /> - (n:Group)"} start={this.state.label} /> + (n)" + } + start={this.state.label} + /> - (n:Group) WHERE NOT n.domain = c.domain"} start={this.state.label} /> + (n:Group)" + } + start={this.state.label} + /> + + (n:Group) WHERE NOT n.domain = c.domain" + } + start={this.state.label} + />

Local Admin Rights

- (n)"} start={this.state.label} distinct /> + (n)" + } + start={this.state.label} + distinct + /> - (g:Group)-[r2:AdminTo]->(n:Computer) WHERE NOT n.name={name}"} start={this.state.label} distinct /> + (g:Group)-[r2:AdminTo]->(n:Computer) WHERE NOT n.name={name}" + } + start={this.state.label} + distinct + /> - (n))"} start={this.state.label} distinct /> + (n))" + } + start={this.state.label} + distinct + />

Outbound Object Control

- (n) WHERE r.isacl=true"} start={this.state.label} distinct /> + (n) WHERE r.isacl=true" + } + start={this.state.label} + distinct + /> - (g:Group)-[r2]->(n) WHERE r2.isacl=true"} start={this.state.label} distinct /> + (g:Group)-[r2]->(n) WHERE r2.isacl=true" + } + start={this.state.label} + distinct + /> - (n))"} start={this.state.label} distinct /> + (n))" + } + start={this.state.label} + distinct + />
); } } -ComputerNodeData.propTypes= { - visible : PropTypes.bool.isRequired +ComputerNodeData.propTypes = { + visible: PropTypes.bool.isRequired }; diff --git a/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx b/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx index a6afda2..b156375 100644 --- a/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx +++ b/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx @@ -1,26 +1,25 @@ -import React, { Component } from 'react'; -import LogoutModal from 'modals/LogoutModal'; +import React, { Component } from "react"; export default class DatabaseDataDisplay extends Component { - constructor(){ + constructor() { super(); this.state = { url: appStore.databaseInfo.url, user: appStore.databaseInfo.user, - num_users: 'Refreshing', - num_computers: 'Refreshing', - num_groups: 'Refreshing', - num_relationships: 'Refreshing', - num_sessions: 'Refreshing', - num_acls: 'Refreshing', + num_users: "Refreshing", + num_computers: "Refreshing", + num_groups: "Refreshing", + num_relationships: "Refreshing", + num_sessions: "Refreshing", + num_acls: "Refreshing", interval: null }; } componentDidMount() { this.refreshDBData(); - emitter.on('hideDBClearModal', this.refreshDBData.bind(this)); - emitter.on('refreshDBData', this.refreshDBData.bind(this)); + emitter.on("hideDBClearModal", this.refreshDBData.bind(this)); + emitter.on("refreshDBData", this.refreshDBData.bind(this)); this.createInterval(); } @@ -32,28 +31,31 @@ export default class DatabaseDataDisplay extends Component { }); } - createInterval(){ - var x = setInterval(function(){ - this.refreshDBData(); - }.bind(this), 60000); + createInterval() { + var x = setInterval( + function() { + this.refreshDBData(); + }.bind(this), + 60000 + ); this.setState({ interval: x }); - }; - - toggleLogoutModal(){ - emitter.emit('showLogout'); } - toggleDBWarnModal(){ - emitter.emit('openDBWarnModal'); + toggleLogoutModal() { + emitter.emit("showLogout"); } - toggleSessionClearModal(){ - emitter.emit('openSessionClearModal'); + toggleDBWarnModal() { + emitter.emit("openDBWarnModal"); } - refreshDBData(){ + toggleSessionClearModal() { + emitter.emit("openSessionClearModal"); + } + + refreshDBData() { var s1 = driver.session(); var s2 = driver.session(); var s3 = driver.session(); @@ -61,43 +63,57 @@ export default class DatabaseDataDisplay extends Component { var s5 = driver.session(); var s6 = driver.session(); - s1.run("MATCH (n:User) WHERE NOT n.name ENDS WITH '$' RETURN count(n)") - .then(function(result){ - this.setState({'num_users':result.records[0]._fields[0].low}); + s1.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}); + }.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}); + }.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)); + }.bind(this) + ); - s4.run("MATCH ()-[r:HasSession]->() RETURN count(r)") - .then(function(result){ - this.setState({'num_sessions':result.records[0]._fields[0].low}); + 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)); + }.bind(this) + ); - s5.run("MATCH ()-[r]->() RETURN count(r)") - .then(function(result){ - this.setState({'num_relationships':result.records[0]._fields[0].low}); + s6.run("MATCH ()-[r {isacl: true}]->() RETURN count(r)").then( + function(result) { + this.setState({ num_acls: result.records[0]._fields[0].low }); + s6.close(); + }.bind(this) + ); + + s5.run("MATCH ()-[r]->() RETURN count(r)").then( + function(result) { + this.setState({ + num_relationships: result.records[0]._fields[0].low + }); s5.close(); - }.bind(this)); + }.bind(this) + ); } - + render() { return (
@@ -123,10 +139,36 @@ export default class DatabaseDataDisplay extends Component {
- - - - + + + +
diff --git a/src/components/SearchContainer/Tabs/DomainNodeData.jsx b/src/components/SearchContainer/Tabs/DomainNodeData.jsx index 40c78f3..daf5e49 100644 --- a/src/components/SearchContainer/Tabs/DomainNodeData.jsx +++ b/src/components/SearchContainer/Tabs/DomainNodeData.jsx @@ -1,12 +1,12 @@ -import React, { Component } from 'react'; -import LoadLabel from './LoadLabel.jsx'; -import PropTypes from 'prop-types'; -import NodeCypherLink from './NodeCypherLink.jsx'; -import NodeCypherNoNumberLink from './NodeCypherNoNumberLink'; -import NodeCypherLinkComplex from './NodeCypherLinkComplex'; +import React, { Component } from "react"; +import LoadLabel from "./LoadLabel.jsx"; +import PropTypes from "prop-types"; +import NodeCypherLink from "./NodeCypherLink.jsx"; +import NodeCypherNoNumberLink from "./NodeCypherNoNumberLink"; +import NodeCypherLinkComplex from "./NodeCypherLinkComplex"; export default class DomainNodeData extends Component { - constructor(){ + constructor() { super(); this.state = { @@ -19,11 +19,11 @@ export default class DomainNodeData extends Component { driversessions: [] }; - emitter.on('domainNodeClicked', this.getNodeData.bind(this)); + emitter.on("domainNodeClicked", this.getNodeData.bind(this)); } - getNodeData(payload){ - $.each(this.state.driversessions, function(index, record){ + getNodeData(payload) { + $.each(this.state.driversessions, function(index, record) { record.close(); }); this.setState({ @@ -41,126 +41,209 @@ export default class DomainNodeData extends Component { let s4 = driver.session(); let s5 = driver.session(); - s1.run("MATCH (a:User) WHERE a.domain={name} RETURN COUNT(a)", {name:payload}) - .then(function(result){ - this.setState({'users':result.records[0]._fields[0].low}); + s1.run("MATCH (a:User) WHERE a.domain={name} RETURN COUNT(a)", { + name: payload + }).then( + function(result) { + this.setState({ users: result.records[0]._fields[0].low }); s1.close(); - }.bind(this)); + }.bind(this) + ); - s2.run("MATCH (a:Group) WHERE a.domain={name} RETURN COUNT(a)", {name:payload}) - .then(function(result){ - this.setState({'groups':result.records[0]._fields[0].low}); + s2.run("MATCH (a:Group) WHERE a.domain={name} RETURN COUNT(a)", { + name: payload + }).then( + function(result) { + this.setState({ groups: result.records[0]._fields[0].low }); s2.close(); - }.bind(this)); + }.bind(this) + ); - s3.run("MATCH (n:Computer) WHERE n.domain={name} RETURN count(n)", {name:payload}) - .then(function(result){ - this.setState({'computers':result.records[0]._fields[0].low}); + s3.run("MATCH (n:Computer) WHERE n.domain={name} RETURN count(n)", { + name: payload + }).then( + function(result) { + this.setState({ computers: result.records[0]._fields[0].low }); s3.close(); - }.bind(this)); - - s4.run("MATCH (n:OU {domain:{name}}) RETURN COUNT(n)", { name: payload }) - .then(function (result) { - this.setState({ 'ous': result.records[0]._fields[0].low }); + }.bind(this) + ); + + s4.run("MATCH (n:OU {domain:{name}}) RETURN COUNT(n)", { + name: payload + }).then( + function(result) { + this.setState({ ous: result.records[0]._fields[0].low }); s4.close(); - }.bind(this)); - - s5.run("MATCH (n:GPO {domain:{name}}) RETURN COUNT(n)", { name: payload }) - .then(function (result) { - this.setState({ 'gpos': result.records[0]._fields[0].low }); + }.bind(this) + ); + + s5.run("MATCH (n:GPO {domain:{name}}) RETURN COUNT(n)", { + name: payload + }).then( + function(result) { + this.setState({ gpos: result.records[0]._fields[0].low }); s5.close(); - }.bind(this)); - - this.setState({'driversessions': [s1,s2,s3]}); + }.bind(this) + ); + + this.setState({ driversessions: [s1, s2, s3] }); } render() { return (
-
-
- Domain -
-
- {this.state.label} -
+
+
Domain
+
{this.state.label}

-
- Users -
+
Users
-
- Groups -
+
Groups
-
- Computers -
+
Computers
-
- OUs -
+
OUs
-
- GPOs -
+
GPOs
- +

Foreign Members

- (b)"} /> - - (b)"} /> + (b)" + } + /> - (b))"} /> + (b)" + } + /> - (b) WHERE r.isacl=true"} /> + (b))" + } + /> + + (b) WHERE r.isacl=true" + } + />

Inbound Trusts

- - - + + +

Outbound Trusts

- (n:Domain)"} /> + (n:Domain)" + } + /> - (n))"} /> + (n))" + } + />

Inbound Controllers

- (u:Domain {name: {name}}) WHERE r.isacl=true"} distinct /> + (u:Domain {name: {name}}) WHERE r.isacl=true" + } + distinct + /> - (g:Group)-[r1]->(u:Domain {name: {name}}) WHERE r1.isacl=true"} distinct /> + (g:Group)-[r1]->(u:Domain {name: {name}}) WHERE r1.isacl=true" + } + distinct + /> - (u:Domain {name: {name}})) WHERE NOT n.name={name}"} distinct /> + (u:Domain {name: {name}})) WHERE NOT n.name={name}" + } + distinct + /> - (u:Domain {name: {name}}) WITH n1 MATCH (n1)-[:MemberOf|GetChangesAll*1..]->(u:Domain {name: {name}}) WITH n1 MATCH (n2)-[:MemberOf|DCSync*1..]->(u:Domain {name: {name}}) WITH collect(distinct(n1))+collect(distinct(n2)) as results UNWIND results as x WITH x WHERE x:User OR x:Computer RETURN count(distinct(x))"} graphQuery={"MATCH p=(n1)-[:MemberOf|GetChanges*1..]->(u:Domain {name: {name}}) WITH p,n1 MATCH p2=(n1)-[:MemberOf|GetChangesAll*1..]->(u:Domain {name: {name}}) WITH p,p2 MATCH p3=(n2)-[:MemberOf|DCSync*1..]->(u:Domain {name: {name}}) RETURN p,p2,p3"} /> + (u:Domain {name: {name}}) WITH n1 MATCH (n1)-[:MemberOf|GetChangesAll*1..]->(u:Domain {name: {name}}) WITH n1 MATCH (n2)-[:MemberOf|DCSync*1..]->(u:Domain {name: {name}}) WITH collect(distinct(n1))+collect(distinct(n2)) as results UNWIND results as x WITH x WHERE x:User OR x:Computer RETURN count(distinct(x))" + } + graphQuery={ + "MATCH p=(n1)-[:MemberOf|GetChanges*1..]->(u:Domain {name: {name}}) WITH p,n1 MATCH p2=(n1)-[:MemberOf|GetChangesAll*1..]->(u:Domain {name: {name}}) WITH p,p2 MATCH p3=(n2)-[:MemberOf|DCSync*1..]->(u:Domain {name: {name}}) RETURN p,p2,p3" + } + />
); @@ -168,5 +251,5 @@ export default class DomainNodeData extends Component { } DomainNodeData.propTypes = { - visible : PropTypes.bool.isRequired -}; \ No newline at end of file + visible: PropTypes.bool.isRequired +}; diff --git a/src/components/SearchContainer/Tabs/GpoNodeData.jsx b/src/components/SearchContainer/Tabs/GpoNodeData.jsx index 2bee998..d7eac65 100644 --- a/src/components/SearchContainer/Tabs/GpoNodeData.jsx +++ b/src/components/SearchContainer/Tabs/GpoNodeData.jsx @@ -1,8 +1,7 @@ -import React, { Component } from 'react'; -import LoadLabel from './LoadLabel.jsx'; -import PropTypes from 'prop-types'; -import NodeCypherLink from './NodeCypherLink.jsx'; -import NodeCypherLinkComplex from './NodeCypherLinkComplex.jsx'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import NodeCypherLink from "./NodeCypherLink.jsx"; +import NodeCypherLinkComplex from "./NodeCypherLinkComplex.jsx"; export default class GpoNodeData extends Component { constructor() { @@ -13,7 +12,7 @@ export default class GpoNodeData extends Component { guid: "" }; - emitter.on('gpoNodeClicked', this.getNodeData.bind(this)); + emitter.on("gpoNodeClicked", this.getNodeData.bind(this)); } getNodeData(payload, guid) { @@ -26,36 +25,84 @@ export default class GpoNodeData extends Component { render() { return (
-
-
- Domain -
-
- {this.state.label} -
-
- GUID -
-
- {this.state.guid} -
+
+
Domain
+
{this.state.label}
+
GUID
+
{this.state.guid}

Affected Objects

- (n)"} start={this.state.label}/> + (n)" + } + start={this.state.label} + /> - (n) WHERE n:OU OR n:Domain"} start={this.state.label}/> + (n) WHERE n:OU OR n:Domain" + } + start={this.state.label} + /> - (container1) WITH g,container1 OPTIONAL MATCH (g)-[r2:GpLink {enforced:true}]->(container2) WITH g,container1,container2 OPTIONAL MATCH p1 = (g)-[r1:GpLink]->(container1)-[r2:Contains*1..]->(n1:Computer) WHERE NONE(x in NODES(p1) WHERE x.blocksInheritance = true AND LABELS(x) = 'OU') WITH g,p1,container2,n1 OPTIONAL MATCH p2 = (g)-[r1:GpLink]->(container2)-[r2:Contains*1..]->(n2:Computer) RETURN count(n1) + count(n2)"} graphQuery={"MATCH (g:GPO {name:{name}}) OPTIONAL MATCH (g)-[r1:GpLink {enforced:false}]->(container1) WITH g,container1 OPTIONAL MATCH (g)-[r2:GpLink {enforced:true}]->(container2) WITH g,container1,container2 OPTIONAL MATCH p1 = (g)-[r1:GpLink]->(container1)-[r2:Contains*1..]->(n1:Computer) WHERE NONE(x in NODES(p1) WHERE x.blocksInheritance = true AND LABELS(x) = 'OU') WITH g,p1,container2,n1 OPTIONAL MATCH p2 = (g)-[r1:GpLink]->(container2)-[r2:Contains*1..]->(n2:Computer) RETURN p1,p2"}/> + (container1) WITH g,container1 OPTIONAL MATCH (g)-[r2:GpLink {enforced:true}]->(container2) WITH g,container1,container2 OPTIONAL MATCH p1 = (g)-[r1:GpLink]->(container1)-[r2:Contains*1..]->(n1:Computer) WHERE NONE(x in NODES(p1) WHERE x.blocksInheritance = true AND LABELS(x) = 'OU') WITH g,p1,container2,n1 OPTIONAL MATCH p2 = (g)-[r1:GpLink]->(container2)-[r2:Contains*1..]->(n2:Computer) RETURN count(n1) + count(n2)" + } + graphQuery={ + "MATCH (g:GPO {name:{name}}) OPTIONAL MATCH (g)-[r1:GpLink {enforced:false}]->(container1) WITH g,container1 OPTIONAL MATCH (g)-[r2:GpLink {enforced:true}]->(container2) WITH g,container1,container2 OPTIONAL MATCH p1 = (g)-[r1:GpLink]->(container1)-[r2:Contains*1..]->(n1:Computer) WHERE NONE(x in NODES(p1) WHERE x.blocksInheritance = true AND LABELS(x) = 'OU') WITH g,p1,container2,n1 OPTIONAL MATCH p2 = (g)-[r1:GpLink]->(container2)-[r2:Contains*1..]->(n2:Computer) RETURN p1,p2" + } + /> - (container1) WITH g,container1 OPTIONAL MATCH (g)-[r2:GpLink {enforced:true}]->(container2) WITH g,container1,container2 OPTIONAL MATCH p1 = (g)-[r1:GpLink]->(container1)-[r2:Contains*1..]->(n1:User) WHERE NONE(x in NODES(p1) WHERE x.blocksInheritance = true AND LABELS(x) = 'OU') WITH g,p1,container2,n1 OPTIONAL MATCH p2 = (g)-[r1:GpLink]->(container2)-[r2:Contains*1..]->(n2:User) RETURN count(n1) + count(n2)"} graphQuery={"MATCH (g:GPO {name:{name}}) OPTIONAL MATCH (g)-[r1:GpLink {enforced:false}]->(container1) WITH g,container1 OPTIONAL MATCH (g)-[r2:GpLink {enforced:true}]->(container2) WITH g,container1,container2 OPTIONAL MATCH p1 = (g)-[r1:GpLink]->(container1)-[r2:Contains*1..]->(n1:User) WHERE NONE(x in NODES(p1) WHERE x.blocksInheritance = true AND LABELS(x) = 'OU') WITH g,p1,container2,n1 OPTIONAL MATCH p2 = (g)-[r1:GpLink]->(container2)-[r2:Contains*1..]->(n2:User) RETURN p1,p2"} /> + (container1) WITH g,container1 OPTIONAL MATCH (g)-[r2:GpLink {enforced:true}]->(container2) WITH g,container1,container2 OPTIONAL MATCH p1 = (g)-[r1:GpLink]->(container1)-[r2:Contains*1..]->(n1:User) WHERE NONE(x in NODES(p1) WHERE x.blocksInheritance = true AND LABELS(x) = 'OU') WITH g,p1,container2,n1 OPTIONAL MATCH p2 = (g)-[r1:GpLink]->(container2)-[r2:Contains*1..]->(n2:User) RETURN count(n1) + count(n2)" + } + graphQuery={ + "MATCH (g:GPO {name:{name}}) OPTIONAL MATCH (g)-[r1:GpLink {enforced:false}]->(container1) WITH g,container1 OPTIONAL MATCH (g)-[r2:GpLink {enforced:true}]->(container2) WITH g,container1,container2 OPTIONAL MATCH p1 = (g)-[r1:GpLink]->(container1)-[r2:Contains*1..]->(n1:User) WHERE NONE(x in NODES(p1) WHERE x.blocksInheritance = true AND LABELS(x) = 'OU') WITH g,p1,container2,n1 OPTIONAL MATCH p2 = (g)-[r1:GpLink]->(container2)-[r2:Contains*1..]->(n2:User) RETURN p1,p2" + } + />

Inbound Object Control

- (g:GPO {name:{name}})"} end={this.state.label} distinct /> + (g:GPO {name:{name}})" + } + end={this.state.label} + distinct + /> - (g1:Group)-[r1]->(g2:GPO {name: {name}}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = g2.name) AND NOT n.name = g2.name AND r1.isacl=true"} end={this.state.label} distinct /> + (g1:Group)-[r1]->(g2:GPO {name: {name}}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = g2.name) AND NOT n.name = g2.name AND r1.isacl=true" + } + end={this.state.label} + distinct + /> - (g:GPO {name:{name}}))"} end={this.state.label} distinct /> + (g:GPO {name:{name}}))" + } + end={this.state.label} + distinct + />
); diff --git a/src/components/SearchContainer/Tabs/GroupNodeData.jsx b/src/components/SearchContainer/Tabs/GroupNodeData.jsx index b78ab7b..5eef2e5 100644 --- a/src/components/SearchContainer/Tabs/GroupNodeData.jsx +++ b/src/components/SearchContainer/Tabs/GroupNodeData.jsx @@ -1,11 +1,9 @@ -import React, { Component } from 'react'; -import NodeALink from './NodeALink'; -import PropTypes from 'prop-types'; -import NodeCypherLink from './NodeCypherLink'; -import NodeCypherNoNumberLink from './NodeCypherNoNumberLink'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import NodeCypherLink from "./NodeCypherLink"; export default class GroupNodeData extends Component { - constructor(){ + constructor() { super(); this.state = { @@ -13,11 +11,11 @@ export default class GroupNodeData extends Component { driversessions: [] }; - emitter.on('groupNodeClicked', this.getNodeData.bind(this)); + emitter.on("groupNodeClicked", this.getNodeData.bind(this)); } - getNodeData(payload){ - $.each(this.state.driversessions, function(index, record){ + getNodeData(payload) { + $.each(this.state.driversessions, function(index, record) { record.close(); }); @@ -27,58 +25,177 @@ export default class GroupNodeData extends Component { } render() { - var domain = '@' + this.state.label.split('@'); return (
-
+

Node Info

-
- Name -
-
- {this.state.label} -
- (u:User)-[r2:MemberOf*1..]->(g:Group {name: {name}})"} end={this.state.label} /> +
Name
+
{this.state.label}
+ (u:User)-[r2:MemberOf*1..]->(g:Group {name: {name}})" + } + end={this.state.label} + /> {/* (g1:Group {name:{name}}) WITH o1 MATCH p= (d: Domain)-[r2:Contains*1..]->(o1)-[r3:Contains]->(n)"} /> */} - +

Group Members

- (c:Group {name: {name}})"} end={this.state.label} /> - - (g:Group {name:{name}})"} end={this.state.label} /> + (c:Group {name: {name}})" + } + end={this.state.label} + /> + + (g:Group {name:{name}})" + } + end={this.state.label} + /> + + (g:Group {name:{name}}) WHERE NOT g.domain = n.domain" + } + end={this.state.label} + distinct + /> - (g:Group {name:{name}}) WHERE NOT g.domain = n.domain"} end={this.state.label} distinct /> -

Group Membership

- (n:Group)"} start={this.state.label} distinct /> - - (n:Group)"} start={this.state.label} distinct /> + (n:Group)" + } + start={this.state.label} + distinct + /> + + (n:Group)" + } + start={this.state.label} + distinct + /> + + (n) WHERE NOT m.domain=n.domain" + } + start={this.state.label} + /> - (n) WHERE NOT m.domain=n.domain"} start={this.state.label} /> -

Local Admin Rights

- (n:Computer)"} start={this.state.label} distinct /> - - (g2:Group)-[r2:AdminTo]->(n:Computer)"} start={this.state.label} distinct /> - - (n:Computer))"} start={this.state.label} distinct /> - + (n:Computer)" + } + start={this.state.label} + distinct + /> + + (g2:Group)-[r2:AdminTo]->(n:Computer)" + } + start={this.state.label} + distinct + /> + + (n:Computer))" + } + start={this.state.label} + distinct + /> +

Outbound Object Control

- (n) WHERE r.isacl=true"} start={this.state.label} distinct /> - - (g2:Group)-[r2]->(n) WHERE r2.isacl=true"} start={this.state.label} distinct /> + (n) WHERE r.isacl=true" + } + start={this.state.label} + distinct + /> - (n))"} start={this.state.label} distinct /> + (g2:Group)-[r2]->(n) WHERE r2.isacl=true" + } + start={this.state.label} + distinct + /> + + (n))" + } + start={this.state.label} + distinct + />

Inbound Object Control

- (g:Group {name:{name}})"} end={this.state.label} distinct /> - - (g1:Group)-[r1]->(g2:Group {name: {name}}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = g2.name) AND NOT n.name = g2.name AND r1.isacl=true"} end={this.state.label} distinct /> - - (g:Group {name:{name}}))"} end={this.state.label} distinct /> + (g:Group {name:{name}})" + } + end={this.state.label} + distinct + /> + + (g1:Group)-[r1]->(g2:Group {name: {name}}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = g2.name) AND NOT n.name = g2.name AND r1.isacl=true" + } + end={this.state.label} + distinct + /> + + (g:Group {name:{name}}))" + } + end={this.state.label} + distinct + />
); @@ -86,5 +203,5 @@ export default class GroupNodeData extends Component { } GroupNodeData.propTypes = { - visible : PropTypes.bool.isRequired -}; \ No newline at end of file + visible: PropTypes.bool.isRequired +}; diff --git a/src/components/SearchContainer/Tabs/LoadLabel.jsx b/src/components/SearchContainer/Tabs/LoadLabel.jsx index 1014e0c..09cc16c 100644 --- a/src/components/SearchContainer/Tabs/LoadLabel.jsx +++ b/src/components/SearchContainer/Tabs/LoadLabel.jsx @@ -1,28 +1,32 @@ -import React, { Component } from 'react'; -import { If, Then, Else } from 'react-if'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import { If, Then, Else } from "react-if"; +import PropTypes from "prop-types"; export default class LoadLabel extends Component { - constructor(props){ + constructor(props) { super(props); } render() { return ( -
{this.props.value}
- {() => -
-
-
-
-
- } + +
{this.props.value}
+
+ + {() => ( +
+
+
+
+
+ )} + ); } } LoadLabel.propTypes = { - ready : PropTypes.bool.isRequired, - value : PropTypes.number -} \ No newline at end of file + ready: PropTypes.bool.isRequired, + value: PropTypes.number +}; diff --git a/src/components/SearchContainer/Tabs/NoNodeData.jsx b/src/components/SearchContainer/Tabs/NoNodeData.jsx index f8a42be..1d33602 100644 --- a/src/components/SearchContainer/Tabs/NoNodeData.jsx +++ b/src/components/SearchContainer/Tabs/NoNodeData.jsx @@ -1,22 +1,17 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types' +import React, { Component } from "react"; +import PropTypes from "prop-types"; export default class NoNodeData extends Component { - render() { - return ( -
-

- Node Properties -

-

- Select a node for more information -

-
- ); - } + render() { + return ( +
+

Node Properties

+

Select a node for more information

+
+ ); + } } - NoNodeData.propTypes = { - visible : PropTypes.bool.isRequired -} \ No newline at end of file + visible: PropTypes.bool.isRequired +}; diff --git a/src/components/SearchContainer/Tabs/NodeALink.jsx b/src/components/SearchContainer/Tabs/NodeALink.jsx index f4e7165..1460605 100644 --- a/src/components/SearchContainer/Tabs/NodeALink.jsx +++ b/src/components/SearchContainer/Tabs/NodeALink.jsx @@ -1,29 +1,35 @@ -import React, { Component } from 'react'; -import { If, Then, Else } from 'react-if'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import { If, Then, Else } from "react-if"; +import PropTypes from "prop-types"; export default class NodeALink extends Component { - constructor(props){ + constructor(props) { super(props); } render() { return ( - {this.props.value} - {() => -
-
-
-
-
- } + + + {this.props.value} + + + + {() => ( +
+
+
+
+
+ )} + ); } } NodeALink.propTypes = { - ready : PropTypes.bool.isRequired, - click : PropTypes.func, - value : PropTypes.number -} \ No newline at end of file + ready: PropTypes.bool.isRequired, + click: PropTypes.func, + value: PropTypes.number +}; diff --git a/src/components/SearchContainer/Tabs/NodeCypherLink.jsx b/src/components/SearchContainer/Tabs/NodeCypherLink.jsx index 8c10636..bf7612e 100644 --- a/src/components/SearchContainer/Tabs/NodeCypherLink.jsx +++ b/src/components/SearchContainer/Tabs/NodeCypherLink.jsx @@ -1,72 +1,73 @@ -import React, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; -import NodeALink from './NodeALink'; +import React, { Component, Fragment } from "react"; +import PropTypes from "prop-types"; +import NodeALink from "./NodeALink"; export default class NodeCypherLink extends Component { - constructor(props){ + constructor(props) { super(props); } - componentWillMount() { this.setState({ ready: false, value: 0 }); } - - componentWillReceiveProps(newProps){ - if (this.props.target !== newProps.target){ + componentWillReceiveProps(newProps) { + if (this.props.target !== newProps.target) { var session = driver.session(); - if (typeof this.state.session !== 'undefined'){ + if (typeof this.state.session !== "undefined") { this.state.session.close(); } - + this.setState({ session: session, - ready:false + ready: false }); let query = this.props.baseQuery; - if (this.props.distinct){ - query += ' RETURN COUNT(DISTINCT(n))'; - }else{ - query += ' RETURN COUNT(n)'; + if (this.props.distinct) { + query += " RETURN COUNT(DISTINCT(n))"; + } else { + query += " RETURN COUNT(n)"; } - let domain = '@' + newProps.target.split('@').last(); - this.setState({domain:domain}); - session.run(query, {name: newProps.target, domain: domain}) - .then(function(result){ + let domain = "@" + newProps.target.split("@").last(); + this.setState({ domain: domain }); + session.run(query, { name: newProps.target, domain: domain }).then( + function(result) { this.setState({ - value:result.records[0]._fields[0].low, - ready:true + value: result.records[0]._fields[0].low, + ready: true }); - }.bind(this)); + }.bind(this) + ); } } - render(){ + render() { return ( -
- {this.props.property - }
+
{this.props.property}
); - } } @@ -77,4 +78,4 @@ NodeCypherLink.propTypes = { distinct: PropTypes.bool, start: PropTypes.string, end: PropTypes.string -}; \ No newline at end of file +}; diff --git a/src/components/SearchContainer/Tabs/NodeCypherLinkComplex.jsx b/src/components/SearchContainer/Tabs/NodeCypherLinkComplex.jsx index cdedd71..9831323 100644 --- a/src/components/SearchContainer/Tabs/NodeCypherLinkComplex.jsx +++ b/src/components/SearchContainer/Tabs/NodeCypherLinkComplex.jsx @@ -1,13 +1,12 @@ -import React, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; -import NodeALink from './NodeALink'; +import React, { Component, Fragment } from "react"; +import PropTypes from "prop-types"; +import NodeALink from "./NodeALink"; export default class NodeCypherLinkComplex extends Component { constructor(props) { super(props); } - componentWillMount() { this.setState({ ready: false, @@ -15,11 +14,10 @@ export default class NodeCypherLinkComplex extends Component { }); } - componentWillReceiveProps(newProps) { if (this.props.target !== newProps.target) { var session = driver.session(); - if (typeof this.state.session !== 'undefined') { + if (typeof this.state.session !== "undefined") { this.state.session.close(); } @@ -28,39 +26,39 @@ export default class NodeCypherLinkComplex extends Component { ready: false }); let query = this.props.countQuery; - let domain = '@' + newProps.target.split('@').last(); - session.run(query, { name: newProps.target, domain: domain }) - .then(function (result) { + let domain = "@" + newProps.target.split("@").last(); + session.run(query, { name: newProps.target, domain: domain }).then( + function(result) { this.setState({ value: result.records[0]._fields[0].low, ready: true }); - }.bind(this)); + }.bind(this) + ); } } render() { return ( -
- {this.props.property - }
+
{this.props.property}
); - } } @@ -72,4 +70,4 @@ NodeCypherLinkComplex.propTypes = { distinct: PropTypes.bool, start: PropTypes.string, end: PropTypes.string -}; \ No newline at end of file +}; diff --git a/src/components/SearchContainer/Tabs/NodeCypherNoNumberLink.jsx b/src/components/SearchContainer/Tabs/NodeCypherNoNumberLink.jsx index bb94bf6..9966bd7 100644 --- a/src/components/SearchContainer/Tabs/NodeCypherNoNumberLink.jsx +++ b/src/components/SearchContainer/Tabs/NodeCypherNoNumberLink.jsx @@ -1,29 +1,32 @@ -import React, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component, Fragment } from "react"; +import PropTypes from "prop-types"; export default class NodeCypherNoNumberLink extends Component { - constructor(props){ + constructor(props) { super(props); } - render(){ - let c = function(){ + render() { + let c = function() { emitter.emit( - 'query', + "query", this.props.query, - { name: this.props.target }, this.props.start, this.props.end + { name: this.props.target }, + this.props.start, + this.props.end ); }.bind(this); - + return (
- {this.props.property} + + {this.props.property} +
); - } } @@ -33,4 +36,4 @@ NodeCypherNoNumberLink.propTypes = { query: PropTypes.string.isRequired, start: PropTypes.string, end: PropTypes.string -}; \ No newline at end of file +}; diff --git a/src/components/SearchContainer/Tabs/NodePropItem.jsx b/src/components/SearchContainer/Tabs/NodePropItem.jsx index 031f0f6..82c6043 100644 --- a/src/components/SearchContainer/Tabs/NodePropItem.jsx +++ b/src/components/SearchContainer/Tabs/NodePropItem.jsx @@ -1,51 +1,45 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; -export default class NodePropItem extends Component{ - constructor(props){ +export default class NodePropItem extends Component { + constructor(props) { super(props); } - isArray(object){ - return object && typeof object === 'object' && object.constructor === Array; + 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)){ + 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{ + if (obj.length === 0) { + return [
{this.props.keyName}
,
None
]; + } else { var elements = []; - $.each(obj, function(index, prop){ - elements.push(
); + $.each(obj, function(_, prop) { + elements.push(
); elements.push(
{prop}
); }); elements[0] =
Service Principal Names
; } - + return elements; - }else if (typeof obj === 'boolean'){ + } else if (typeof obj === "boolean") { return [
{this.props.keyName}
,
{this.props.keyValue.toString().toTitleCase()}
]; - }else{ + } else { return [
{this.props.keyName}
,
{this.props.keyValue.toString()}
]; } - } -} \ No newline at end of file +} diff --git a/src/components/SearchContainer/Tabs/NodeProps.jsx b/src/components/SearchContainer/Tabs/NodeProps.jsx index ca8e912..5409aed 100644 --- a/src/components/SearchContainer/Tabs/NodeProps.jsx +++ b/src/components/SearchContainer/Tabs/NodeProps.jsx @@ -1,24 +1,23 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; export default class componentName extends Component { - constructor(props){ + constructor(props) { super(props); } convertToDisplayProp(propName) { var obj = this.props.properties[propName]; var type = typeof obj; - if (type === 'undefined') { + if (type === "undefined") { return null; - } else if (obj.hasOwnProperty('low')) { + } else if (obj.hasOwnProperty("low")) { var t = obj.low; if (t === 0 || t === -1) { return "Never"; } else { return new Date(obj.low * 1000).toUTCString(); } - } else if (type === 'boolean') { + } else if (type === "boolean") { return obj.toString().toTitleCase(); } else if (obj === "") { return null; @@ -30,22 +29,25 @@ export default class componentName extends Component { render() { let l = []; - $.each(this.props.displayMap, function(key, value){ - let val = this.convertToDisplayProp(key); + $.each( + this.props.displayMap, + function(key, value) { + let val = this.convertToDisplayProp(key); - if (val !== null){ - l.push(
{value}
); - l.push(
{val}
); - } - }.bind(this)); + if (val !== null) { + l.push(
{value}
); + l.push(
{val}
); + } + }.bind(this) + ); - if (this.props.ServicePrincipalNames.length > 0){ + if (this.props.ServicePrincipalNames.length > 0) { l.push(
Service Principal Names
); - $.each(this.props.ServicePrincipalNames, function(index, value){ + $.each(this.props.ServicePrincipalNames, function(index, value) { l.push(
{value}
); }); } return l; } -} \ No newline at end of file +} diff --git a/src/components/SearchContainer/Tabs/OuNodeData.jsx b/src/components/SearchContainer/Tabs/OuNodeData.jsx index ca0566e..1be52ed 100644 --- a/src/components/SearchContainer/Tabs/OuNodeData.jsx +++ b/src/components/SearchContainer/Tabs/OuNodeData.jsx @@ -1,8 +1,7 @@ -import React, { Component } from 'react'; -import LoadLabel from './LoadLabel.jsx'; -import PropTypes from 'prop-types'; -import NodeCypherLink from './NodeCypherLink.jsx'; -import NodeCypherNoNumberLink from './NodeCypherNoNumberLink'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import NodeCypherLink from "./NodeCypherLink.jsx"; +import NodeCypherNoNumberLink from "./NodeCypherNoNumberLink"; export default class OuNodeData extends Component { constructor() { @@ -14,12 +13,12 @@ export default class OuNodeData extends Component { blocks: "" }; - emitter.on('ouNodeClicked', this.getNodeData.bind(this)); + emitter.on("ouNodeClicked", this.getNodeData.bind(this)); } getNodeData(payload, guid, blocksInheritance) { - let bi = ''+blocksInheritance; - bi = bi.toTitleCase() + let bi = "" + blocksInheritance; + bi = bi.toTitleCase(); this.setState({ label: payload, @@ -31,41 +30,73 @@ export default class OuNodeData extends Component { render() { return (
-
-
- Ou -
-
- {this.state.label} -
-
- GUID -
-
- {this.state.guid} -
-
- Blocks Inheritance -
-
- {this.state.blocks} -
- - +
+
Ou
+
{this.state.label}
+
GUID
+
{this.state.guid}
+
Blocks Inheritance
+
{this.state.blocks}
+ +

Affecting GPOs

- (o:OU {guid:{name}})"} /> - (o:OU {guid:{name}})"} /> + (o:OU {guid:{name}})" + } + /> + (o:OU {guid:{name}})" + } + />

Descendant Objects

- (n:User)"} distinct /> + (n:User)" + } + distinct + /> - (n:Group)"} distinct /> + (n:Group)" + } + distinct + /> - (n:Computer)"} distinct /> - - (o2:OU {guid:{name}}) WITH o1 MATCH p=(d)-[r2:Contains*1..]->(o1)-[r3:Contains]->(n)"} distinct /> + (n:Computer)" + } + distinct + /> + + (o2:OU {guid:{name}}) WITH o1 MATCH p=(d)-[r2:Contains*1..]->(o1)-[r3:Contains]->(n)" + } + distinct + />
); @@ -74,4 +105,4 @@ export default class OuNodeData extends Component { OuNodeData.propTypes = { visible: PropTypes.bool.isRequired -}; \ No newline at end of file +}; diff --git a/src/components/SearchContainer/Tabs/PrebuiltQueries.json b/src/components/SearchContainer/Tabs/PrebuiltQueries.json index fff637f..6fdc133 100644 --- a/src/components/SearchContainer/Tabs/PrebuiltQueries.json +++ b/src/components/SearchContainer/Tabs/PrebuiltQueries.json @@ -6,7 +6,8 @@ { "final": true, "title": "Select a domain...", - "query": "MATCH (n:Group) WHERE n.name =~ {name} WITH n MATCH p=(n)<-[r:MemberOf*1..]-(m) RETURN p", + "query": + "MATCH (n:Group) WHERE n.name =~ {name} WITH n MATCH p=(n)<-[r:MemberOf*1..]-(m) RETURN p", "props": { "name": "(?i).*DOMAIN ADMINS.*" }, @@ -20,16 +21,18 @@ { "final": false, "title": "Select a Domain Admin group...", - "query": "MATCH (n:Group) WHERE n.name =~ {name} RETURN n.name ORDER BY n.name DESC", + "query": + "MATCH (n:Group) WHERE n.name =~ {name} RETURN n.name ORDER BY n.name DESC", "props": { "name": "(?i).*DOMAIN ADMINS.*" } }, { "final": true, - "query": "MATCH (n:User),(m:Group {name:{result}}),p=shortestPath((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p", + "query": + "MATCH (n:User),(m:Group {name:{result}}),p=shortestPath((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p", "allowCollapse": true, - "endNode":"{}" + "endNode": "{}" } ] }, @@ -38,7 +41,8 @@ "queryList": [ { "final": true, - "query": "MATCH p=(a:Computer)-[r:HasSession]->(b:User) WITH a,b,r MATCH p=shortestPath((b)-[:AdminTo|MemberOf*1..]->(a)) RETURN p", + "query": + "MATCH p=(a:Computer)-[r:HasSession]->(b:User) WITH a,b,r MATCH p=shortestPath((b)-[:AdminTo|MemberOf*1..]->(a)) RETURN p", "allowCollapse": true } ] @@ -48,7 +52,8 @@ "queryList": [ { "final": true, - "query": "MATCH (n:User),(m:Computer), (n)<-[r:HasSession]-(m) WHERE NOT n.name STARTS WITH 'ANONYMOUS LOGON' AND NOT n.name='' WITH n, count(r) as rel_count order by rel_count desc LIMIT 10 MATCH p=(m)-[r:HasSession]->(n) RETURN p", + "query": + "MATCH (n:User),(m:Computer), (n)<-[r:HasSession]-(m) WHERE NOT n.name STARTS WITH 'ANONYMOUS LOGON' AND NOT n.name='' WITH n, count(r) as rel_count order by rel_count desc LIMIT 10 MATCH p=(m)-[r:HasSession]->(n) RETURN p", "allowCollapse": true } ] @@ -58,7 +63,8 @@ "queryList": [ { "final": true, - "query": "MATCH (n:User),(m:Computer), (n)<-[r:HasSession]-(m) WHERE NOT n.name STARTS WITH 'ANONYMOUS LOGON' AND NOT n.name='' WITH m, count(r) as rel_count order by rel_count desc LIMIT 10 MATCH p=(m)-[r:HasSession]->(n) RETURN n,r,m", + "query": + "MATCH (n:User),(m:Computer), (n)<-[r:HasSession]-(m) WHERE NOT n.name STARTS WITH 'ANONYMOUS LOGON' AND NOT n.name='' WITH m, count(r) as rel_count order by rel_count desc LIMIT 10 MATCH p=(m)-[r:HasSession]->(n) RETURN n,r,m", "allowCollapse": true } ] @@ -68,7 +74,8 @@ "queryList": [ { "final": true, - "query": "MATCH (n:User),(m:Computer), (n)-[r:AdminTo]->(m) WHERE NOT n.name STARTS WITH 'ANONYMOUS LOGON' AND NOT n.name='' WITH n, count(r) as rel_count order by rel_count desc LIMIT 10 MATCH p=(m)<-[r:AdminTo]-(n) RETURN p", + "query": + "MATCH (n:User),(m:Computer), (n)-[r:AdminTo]->(m) WHERE NOT n.name STARTS WITH 'ANONYMOUS LOGON' AND NOT n.name='' WITH n, count(r) as rel_count order by rel_count desc LIMIT 10 MATCH p=(m)<-[r:AdminTo]-(n) RETURN p", "allowCollapse": true } ] @@ -78,7 +85,8 @@ "queryList": [ { "final": true, - "query": "MATCH (n:User),(m:Computer), (n)-[r:AdminTo]->(m) WHERE NOT n.name STARTS WITH 'ANONYMOUS LOGON' AND NOT n.name='' WITH m, count(r) as rel_count order by rel_count desc LIMIT 10 MATCH p=(m)<-[r:AdminTo]-(n) RETURN p", + "query": + "MATCH (n:User),(m:Computer), (n)-[r:AdminTo]->(m) WHERE NOT n.name STARTS WITH 'ANONYMOUS LOGON' AND NOT n.name='' WITH m, count(r) as rel_count order by rel_count desc LIMIT 10 MATCH p=(m)<-[r:AdminTo]-(n) RETURN p", "allowCollapse": true } ] @@ -89,11 +97,13 @@ { "final": false, "title": "Select source domain...", - "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" + "query": + "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" }, { "final": true, - "query": "MATCH (n:User) WITH n MATCH (m:Group) WITH n,m MATCH p=(n)-[r:MemberOf]->(m) WHERE n.domain={result} AND NOT m.domain=n.domain RETURN p", + "query": + "MATCH (n:User) WITH n MATCH (m:Group) WITH n,m MATCH p=(n)-[r:MemberOf]->(m) WHERE n.domain={result} AND NOT m.domain=n.domain RETURN p", "startNode": "{}", "allowCollapse": false } @@ -105,11 +115,13 @@ { "final": false, "title": "Select source domain...", - "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" + "query": + "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" }, { "final": true, - "query": "MATCH (n:Group) WITH n MATCH (m:Group) WITH n,m MATCH p=(n)-[r:MemberOf]->(m) WHERE n.domain={result} AND NOT m.domain=n.domain AND NOT n.name=m.name RETURN p", + "query": + "MATCH (n:Group) WITH n MATCH (m:Group) WITH n,m MATCH p=(n)-[r:MemberOf]->(m) WHERE n.domain={result} AND NOT m.domain=n.domain AND NOT n.name=m.name RETURN p", "startNode": "{}", "allowCollapse": false } @@ -127,21 +139,24 @@ }, { "name": "Shortest Path from SPN User", - "queryList":[ + "queryList": [ { "final": false, - "title":"Select a domain...", - "query":"MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" + "title": "Select a domain...", + "query": + "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" }, { "final": false, "title": "Select a user", - "query": "MATCH (n:User) WHERE n.domain={result} AND n.HasSPN=true RETURN n.name, n.PwdLastSet ORDER BY n.PwdLastSet ASC" + "query": + "MATCH (n:User) WHERE n.domain={result} AND n.HasSPN=true RETURN n.name, n.PwdLastSet ORDER BY n.PwdLastSet ASC" }, { "final": true, - "query": "MATCH n=shortestPath((a:User {name:{result}})-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(b:Computer)) RETURN n", - "startNode":"{}", + "query": + "MATCH n=shortestPath((a:User {name:{result}})-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(b:Computer)) RETURN n", + "startNode": "{}", "allowCollapse": true } ] @@ -152,14 +167,16 @@ { "final": false, "title": "Select a Domain Admin group...", - "query": "MATCH (n:Group) WHERE n.name =~ {name} RETURN n.name ORDER BY n.name DESC", + "query": + "MATCH (n:Group) WHERE n.name =~ {name} RETURN n.name ORDER BY n.name DESC", "props": { "name": "(?i).*DOMAIN ADMINS.*" } }, { "final": true, - "query": "MATCH (n:User),(m:Group {name:{result}}),p=shortestPath((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) WHERE n.HasSPN=true RETURN p", + "query": + "MATCH (n:User),(m:Group {name:{result}}),p=shortestPath((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) WHERE n.HasSPN=true RETURN p", "allowCollapse": true, "endNode": "{}" } diff --git a/src/components/SearchContainer/Tabs/PrebuiltQueriesDisplay.jsx b/src/components/SearchContainer/Tabs/PrebuiltQueriesDisplay.jsx index dc39a52..50d2ea1 100644 --- a/src/components/SearchContainer/Tabs/PrebuiltQueriesDisplay.jsx +++ b/src/components/SearchContainer/Tabs/PrebuiltQueriesDisplay.jsx @@ -1,14 +1,14 @@ -import React, { Component } from 'react'; -import PrebuiltQueryNode from './PrebuiltQueryNode'; -import { If, Then, Else } from 'react-if'; -const { app } = require('electron').remote; -var fs = require('fs'); -var path = require('path'); -var process = require('process'); -var exec = require('child_process').exec; +import React, { Component } from "react"; +import PrebuiltQueryNode from "./PrebuiltQueryNode"; +import { If, Then, Else } from "react-if"; +import { remote } from "electron"; +const { app } = remote; +import { join } from "path"; +import { platform } from "process"; +import { exec } from "child_process"; export default class PrebuiltQueriesDisplay extends Component { - constructor(){ + constructor() { super(); this.state = { @@ -19,13 +19,13 @@ export default class PrebuiltQueriesDisplay extends Component { componentWillMount() { $.ajax({ - url: path.join(app.getPath('userData'), '/customqueries.json'), - type: 'GET', - success: function (response) { + url: join(app.getPath("userData"), "/customqueries.json"), + type: "GET", + success: function(response) { var x = JSON.parse(response); var y = []; - $.each(x.queries, function (index, el) { + $.each(x.queries, function(_, el) { y.push(el); }); @@ -34,13 +34,13 @@ export default class PrebuiltQueriesDisplay extends Component { }); $.ajax({ - url: 'src/components/SearchContainer/Tabs/PrebuiltQueries.json', - type: 'GET', - success: function (response) { + url: "src/components/SearchContainer/Tabs/PrebuiltQueries.json", + type: "GET", + success: function(response) { var x = JSON.parse(response); var y = []; - $.each(x.queries, function (index, el) { + $.each(x.queries, function(_, el) { y.push(el); }); @@ -50,64 +50,88 @@ export default class PrebuiltQueriesDisplay extends Component { } getCommandLine() { - switch (process.platform) { - case 'darwin' : return 'open'; - case 'win32' : return ''; - case 'win64' : return ''; - default : return 'xdg-open'; + switch (platform) { + case "darwin": + return "open"; + case "win32": + return ""; + case "win64": + return ""; + default: + return "xdg-open"; } } - editCustom(){ - exec(this.getCommandLine() + ' "' + path.join(app.getPath('userData'),'/customqueries.json') + '"'); + editCustom() { + exec( + this.getCommandLine() + + ' "' + + join(app.getPath("userData"), "/customqueries.json") + + '"' + ); } - refreshCustom(){ + refreshCustom() { $.ajax({ - url: path.join(app.getPath('userData'),'/customqueries.json'), - type: 'GET', - success: function(response){ + url: join(app.getPath("userData"), "/customqueries.json"), + type: "GET", + success: function(response) { var x = JSON.parse(response); var y = []; $.each(x.queries, function(index, el) { y.push(el); }); - - this.setState({custom: y}); + + this.setState({ custom: y }); }.bind(this) }); } - - render() { return (
-

- Pre-Built Analytics Queries -

+

Pre-Built Analytics Queries

- {this.state.queries.map(function(a){ - return + {this.state.queries.map(function(a) { + return ; })}

Custom Queries - - + +

-
No user defined queries.
- {() => -
- {this.state.custom.map(function(a){ - return - })} -
- } -
+ +
No user defined queries.
+
+ + {() => ( +
+ {this.state.custom.map(function(a) { + return ( + + ); + })} +
+ )} +
diff --git a/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx b/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx index b94b407..982cfc0 100644 --- a/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx +++ b/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx @@ -1,19 +1,23 @@ -import React, { Component } from 'react'; +import React, { Component } from "react"; export default class PrebuiltQueryNode extends Component { render() { var c; - - c = function(){ - if (appStore.prebuiltQuery.length === 0){ - appStore.prebuiltQuery = JSON.parse(JSON.stringify(this.props.info.queryList)); - emitter.emit('prebuiltQueryStart'); + + c = function() { + if (appStore.prebuiltQuery.length === 0) { + appStore.prebuiltQuery = JSON.parse( + JSON.stringify(this.props.info.queryList) + ); + emitter.emit("prebuiltQueryStart"); } }.bind(this); return ( ); diff --git a/src/components/SearchContainer/Tabs/UserNodeData.jsx b/src/components/SearchContainer/Tabs/UserNodeData.jsx index 0d6104c..9547032 100644 --- a/src/components/SearchContainer/Tabs/UserNodeData.jsx +++ b/src/components/SearchContainer/Tabs/UserNodeData.jsx @@ -1,9 +1,9 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import NodeProps from './NodeProps'; -import NodeCypherLink from './NodeCypherLink'; -import NodeCypherNoNumberLink from './NodeCypherNoNumberLink'; -import NodeCypherLinkComplex from './NodeCypherLinkComplex'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import NodeProps from "./NodeProps"; +import NodeCypherLink from "./NodeCypherLink"; +import NodeCypherNoNumberLink from "./NodeCypherNoNumberLink"; +import NodeCypherLinkComplex from "./NodeCypherLinkComplex"; export default class UserNodeData extends Component { constructor() { @@ -14,14 +14,25 @@ export default class UserNodeData extends Component { driversessions: [], propertyMap: {}, ServicePrincipalNames: [], - displayMap: { "displayname": "Display Name", "pwdlastset": "Password Last Changed", "lastlogon": "Last Logon", "enabled": "Enabled", "email": "Email", "title": "Title", "homedirectory": "Home Directory", "description": "Description", "userpassword": "User Password", "admincount": "AdminCount" } + displayMap: { + displayname: "Display Name", + pwdlastset: "Password Last Changed", + lastlogon: "Last Logon", + enabled: "Enabled", + email: "Email", + title: "Title", + homedirectory: "Home Directory", + description: "Description", + userpassword: "User Password", + admincount: "AdminCount" + } }; - emitter.on('userNodeClicked', this.getNodeData.bind(this)); + emitter.on("userNodeClicked", this.getNodeData.bind(this)); } getNodeData(payload) { - $.each(this.state.driversessions, function (index, record) { + $.each(this.state.driversessions, function(index, record) { record.close(); }); @@ -33,80 +44,204 @@ 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 (typeof properties.ServicePrincipalNames === 'undefined') { - this.setState({ ServicePrincipalNames: [] }); - } else { - this.setState({ ServicePrincipalNames: properties.ServicePrincipalNames }); - } - this.setState({ propertyMap: properties }); - props.close(); - }.bind(this)); + props + .run("MATCH (n:User {name:{name}}) RETURN n", { name: payload }) + .then( + function(result) { + var properties = result.records[0]._fields[0].properties; + if ( + typeof properties.serviceprincipalnames === "undefined" + ) { + this.setState({ ServicePrincipalNames: [] }); + } else { + this.setState({ + ServicePrincipalNames: + properties.serviceprincipalnames + }); + } + this.setState({ propertyMap: properties }); + props.close(); + }.bind(this) + ); this.setState({ driversessions: [props] }); } render() { - var domain = '@' + this.state.label.split('@').last(); + var domain = "@" + this.state.label.split("@").last(); return (
-
-

- User Info -

-
- Name -
-
- {this.state.label} -
- +
+

User Info

+
Name
+
{this.state.label}
+ + (n:User {name:{name}})" + } + end={this.state.label} + /> - (n:User {name:{name}})"} end={this.state.label} /> + (o2:User {name:{name}}) WITH o1 OPTIONAL MATCH p1=(d)-[r2:Contains*1..]->(o1) OPTIONAL MATCH p2=(o1)-[r3:Contains]->(n) WHERE n:User OR n:Computer RETURN count(distinct(n))" + } + graphQuery={ + "MATCH (o1)-[r1:Contains]->(o2:User {name:{name}}) WITH o1 OPTIONAL MATCH p1=(d)-[r2:Contains*1..]->(o1) OPTIONAL MATCH p2=(o1)-[r3:Contains]->(n) WHERE n:User OR n:Computer RETURN p1,p2" + } + /> - (o2:User {name:{name}}) WITH o1 OPTIONAL MATCH p1=(d)-[r2:Contains*1..]->(o1) OPTIONAL MATCH p2=(o1)-[r3:Contains]->(n) WHERE n:User OR n:Computer RETURN count(distinct(n))"} graphQuery={"MATCH (o1)-[r1:Contains]->(o2:User {name:{name}}) WITH o1 OPTIONAL MATCH p1=(d)-[r2:Contains*1..]->(o1) OPTIONAL MATCH p2=(o1)-[r3:Contains]->(n) WHERE n:User OR n:Computer RETURN p1,p2"} /> + (container1)-[r2:Contains*1..]->(c) OPTIONAL MATCH p2 = (g2:GPO)-[r3:GpLink {enforced:false}]->(container2)-[r4:Contains*1..]->(c) WHERE NONE (x in NODES(p2) WHERE x.blocksInheritance = true AND x:OU AND NOT (g2)-->(x)) RETURN count(g1)+count(g2)" + } + graphQuery={ + "MATCH (c:User {name:{name}}) OPTIONAL MATCH p1 = (g1:GPO)-[r1:GpLink {enforced:true}]->(container1)-[r2:Contains*1..]->(c) OPTIONAL MATCH p2 = (g2:GPO)-[r3:GpLink {enforced:false}]->(container2)-[r4:Contains*1..]->(c) WHERE NONE (x in NODES(p2) WHERE x.blocksInheritance = true AND x:OU AND NOT (g2)-->(x)) RETURN p1,p2" + } + /> - (container1)-[r2:Contains*1..]->(c) OPTIONAL MATCH p2 = (g2:GPO)-[r3:GpLink {enforced:false}]->(container2)-[r4:Contains*1..]->(c) WHERE NONE (x in NODES(p2) WHERE x.blocksInheritance = true AND x:OU AND NOT (g2)-->(x)) RETURN count(g1)+count(g2)"} graphQuery={"MATCH (c:User {name:{name}}) OPTIONAL MATCH p1 = (g1:GPO)-[r1:GpLink {enforced:true}]->(container1)-[r2:Contains*1..]->(c) OPTIONAL MATCH p2 = (g2:GPO)-[r3:GpLink {enforced:false}]->(container2)-[r4:Contains*1..]->(c) WHERE NONE (x in NODES(p2) WHERE x.blocksInheritance = true AND x:OU AND NOT (g2)-->(x)) RETURN p1,p2"} /> - - +

Group Membership

- (n)"} start={this.state.label} /> + (n)" + } + start={this.state.label} + /> - (n:Group)"} start={this.state.label} distinct /> + (n:Group)" + } + start={this.state.label} + distinct + /> - (n)"} start={this.state.label} /> + (n)" + } + start={this.state.label} + /> -

- Local Admin Rights -

+

Local Admin Rights

- (n:Computer)"} start={this.state.label} distinct /> + (n:Computer)" + } + start={this.state.label} + distinct + /> - (g:Group)-[r2:AdminTo]->(n:Computer)"} start={this.state.label} distinct /> + (g:Group)-[r2:AdminTo]->(n:Computer)" + } + start={this.state.label} + distinct + /> - (n:Computer))"} start={this.state.label} distinct /> + (n:Computer))" + } + start={this.state.label} + distinct + /> -

- Outbound Object Control -

+

Outbound Object Control

- (n) WHERE r1.isacl=true"} end={this.state.label} distinct /> + (n) WHERE r1.isacl=true" + } + end={this.state.label} + distinct + /> - (g:Group)-[r2]->(n) WHERE r2.isacl=true"} start={this.state.label} distinct /> + (g:Group)-[r2]->(n) WHERE r2.isacl=true" + } + start={this.state.label} + distinct + /> - (n))"} start={this.state.label} distinct /> + (n))" + } + start={this.state.label} + distinct + />

Inbound Object Control

- (u1:User {name: {name}}) WHERE r.isacl=true"} end={this.state.label} distinct /> + (u1:User {name: {name}}) WHERE r.isacl=true" + } + end={this.state.label} + distinct + /> - (g:Group)-[r1:AddMember|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(u:User {name: {name}}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = u.name) AND NOT n.name = u.name"} end={this.state.label} distinct /> + (g:Group)-[r1:AddMember|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(u:User {name: {name}}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.name = u.name) AND NOT n.name = u.name" + } + end={this.state.label} + distinct + /> - (u1:User {name: {name}}))"} end={this.state.label} distinct /> + (u1:User {name: {name}}))" + } + end={this.state.label} + distinct + />
); diff --git a/src/components/Spotlight/SpotlightContainer.jsx b/src/components/Spotlight/SpotlightContainer.jsx index f8b6898..2c87a10 100644 --- a/src/components/Spotlight/SpotlightContainer.jsx +++ b/src/components/Spotlight/SpotlightContainer.jsx @@ -1,51 +1,69 @@ -import React, { Component } from 'react'; -import GlyphiconSpan from '../GlyphiconSpan'; -import Icon from '../Icon'; -import SpotlightRow from './SpotlightRow'; +import React, { Component } from "react"; +import GlyphiconSpan from "../GlyphiconSpan"; +import Icon from "../Icon"; +import SpotlightRow from "./SpotlightRow"; export default class SpotlightContainer extends Component { - constructor(props){ + constructor(props) { super(props); - + this.state = { data: appStore.spotlightData, searchVal: "", - rex: new RegExp("", 'i') + rex: new RegExp("", "i") }; - emitter.on('spotlightUpdate', function(){ - this.setState({data: appStore.spotlightData}); - }.bind(this)); + emitter.on( + "spotlightUpdate", + function() { + this.setState({ data: appStore.spotlightData }); + }.bind(this) + ); - emitter.on('spotlightClick', function(){ - $(this.refs.spotlight).fadeToggle(false); - }.bind(this)); + emitter.on( + "spotlightClick", + function() { + $(this.refs.spotlight).fadeToggle(false); + }.bind(this) + ); - emitter.on('resetSpotlight', function(){ - this.setState({ - searchVal: "", - rex: new RegExp("", 'i') - }); - - }.bind(this)); + emitter.on( + "resetSpotlight", + function() { + this.setState({ + searchVal: "", + rex: new RegExp("", "i") + }); + }.bind(this) + ); } - _searchChanged(event){ + _searchChanged(event) { this.setState({ searchVal: event.target.value, - rex: new RegExp(event.target.value, 'i') + rex: new RegExp(event.target.value, "i") }); } render() { - return (
- + - +
@@ -57,12 +75,24 @@ export default class SpotlightContainer extends Component { - {Object.keys(this.state.data).map(function(key){ - var d = this.state.data[key]; - var nid = parseInt(key); - var x = this.state.rex.test(d[0]) ? : null; - return x; - }.bind(this))} + {Object.keys(this.state.data).map( + function(key) { + var d = this.state.data[key]; + var nid = parseInt(key); + var x = this.state.rex.test(d[0]) ? ( + + ) : null; + return x; + }.bind(this) + )}
@@ -73,12 +103,15 @@ export default class SpotlightContainer extends Component { componentDidMount() { jQuery(this.refs.spotlight).fadeToggle(0); - $(window).on('keyup', function(e){ - var key = e.keyCode ? e.keyCode : e.which; + $(window).on( + "keyup", + function(e) { + var key = e.keyCode ? e.keyCode : e.which; - if (document.activeElement === document.body && key === 32){ - $(this.refs.spotlight).fadeToggle(); - } - }.bind(this)); + if (document.activeElement === document.body && key === 32) { + $(this.refs.spotlight).fadeToggle(); + } + }.bind(this) + ); } } diff --git a/src/components/Spotlight/SpotlightRow.jsx b/src/components/Spotlight/SpotlightRow.jsx index 3d37b49..181cdb3 100644 --- a/src/components/Spotlight/SpotlightRow.jsx +++ b/src/components/Spotlight/SpotlightRow.jsx @@ -1,15 +1,19 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; export default class SpotlightRow extends Component { - _handleClick(){ - emitter.emit('spotlightClick', this.props.nodeId, this.props.parentNodeId); + _handleClick() { + emitter.emit( + "spotlightClick", + this.props.nodeId, + this.props.parentNodeId + ); } render() { var nodeIcon = ""; var parentIcon = ""; - switch (this.props.nodeType){ + switch (this.props.nodeType) { case "Group": nodeIcon = "fa fa-users"; break; @@ -33,7 +37,7 @@ export default class SpotlightRow extends Component { break; } - switch (this.props.parentNodeType){ + switch (this.props.parentNodeType) { case "Group": parentIcon = "fa fa-users"; break; @@ -57,19 +61,28 @@ export default class SpotlightRow extends Component { break; } return ( - - {this.props.nodeLabel} - {this.props.parentNodeLabel} + + + {this.props.nodeLabel} + + + {this.props.parentNodeLabel} + ); } } SpotlightRow.propTypes = { - nodeId : PropTypes.number.isRequired, - parentNodeId : PropTypes.number.isRequired, - nodeLabel : PropTypes.string.isRequired, - parentNodeLabel : PropTypes.string.isRequired, + nodeId: PropTypes.number.isRequired, + parentNodeId: PropTypes.number.isRequired, + nodeLabel: PropTypes.string.isRequired, + parentNodeLabel: PropTypes.string.isRequired, nodeType: PropTypes.string.isRequired, parentNodeType: PropTypes.string.isRequired -}; \ No newline at end of file +}; diff --git a/src/components/Zoom/ZoomContainer.jsx b/src/components/Zoom/ZoomContainer.jsx index 660278f..d7f8882 100644 --- a/src/components/Zoom/ZoomContainer.jsx +++ b/src/components/Zoom/ZoomContainer.jsx @@ -1,31 +1,40 @@ -import React, { Component } from 'react'; +import React, { Component } from "react"; export default class ZoomContainer extends Component { - render() { - return ( -
-
- -
-
- -
-
- -
-
- ); - } + render() { + return ( +
+
+ +
+
+ +
+
+ +
+
+ ); + } } diff --git a/src/components/tooltip.html b/src/components/tooltip.html index c5ea37c..93dd01d 100644 --- a/src/components/tooltip.html +++ b/src/components/tooltip.html @@ -1,86 +1,78 @@
- {{label}} + {{label}}
    - {{#type_user}} -
  • - Set as Starting Node -
  • -
  • - Set as Ending Node -
  • -
  • - Shortest Paths to Here -
  • - {{/type_user}} - {{#type_computer}} -
  • - Set as Starting Node -
  • -
  • - Set as Ending Node -
  • -
  • - Shortest Paths to Here -
  • - {{/type_computer}} - {{#type_group}} -
  • - Set as Starting Node -
  • -
  • - Set as Ending Node -
  • -
  • - Shortest Paths to Here -
  • - {{/type_group}} - {{#type_gpo}} -
  • - Set as Starting Node -
  • -
  • - Set as Ending Node -
  • -
  • - Shortest Paths to Here -
  • - {{/type_gpo}} - {{#type_ou}} -
  • - Set as Starting Node -
  • -
  • - Set as Ending Node -
  • -
  • - Shortest Paths to Here -
  • - {{/type_ou}} - {{#type_domain}} -
  • - Set as Starting Node -
  • -
  • - Set as Ending Node -
  • -
  • - Shortest Paths to Here -
  • - {{/type_domain}} - {{#expand}} -
  • - Expand -
  • - {{/expand}} - {{#collapse}} -
  • - Collapse -
  • - {{/collapse}} - {{#groupedNode}} -
  • - Expand -
  • - {{/groupedNode}} -
+ {{#type_user}} +
  • + Set as Starting Node +
  • +
  • + Set as Ending Node +
  • +
  • + Shortest Paths to Here +
  • + {{/type_user}} {{#type_computer}} +
  • + Set as Starting Node +
  • +
  • + Set as Ending Node +
  • +
  • + Shortest Paths to Here +
  • + {{/type_computer}} {{#type_group}} +
  • + Set as Starting Node +
  • +
  • + Set as Ending Node +
  • +
  • + Shortest Paths to Here +
  • + {{/type_group}} {{#type_gpo}} +
  • + Set as Starting Node +
  • +
  • + Set as Ending Node +
  • +
  • + Shortest Paths to Here +
  • + {{/type_gpo}} {{#type_ou}} +
  • + Set as Starting Node +
  • +
  • + Set as Ending Node +
  • +
  • + Shortest Paths to Here +
  • + {{/type_ou}} {{#type_domain}} +
  • + Set as Starting Node +
  • +
  • + Set as Ending Node +
  • +
  • + Shortest Paths to Here +
  • + {{/type_domain}} {{#expand}} +
  • + Expand +
  • + {{/expand}} {{#collapse}} +
  • + Collapse +
  • + {{/collapse}} {{#groupedNode}} +
  • + Expand +
  • + {{/groupedNode}} + \ No newline at end of file diff --git a/src/css/styles.css b/src/css/styles.css index 2a1b5ec..1b8c098 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -7,35 +7,41 @@ overflow: hidden; } -.max{ +.max { width: 100%; height: 100%; } @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-weight: 400; font-style: normal; - src: url('../fonts/roboto-v15-latin-regular.eot'); + src: url("../fonts/roboto-v15-latin-regular.eot"); /* IE9 Compat Modes */ - src: local('Roboto'), local('Roboto-Regular'), url('../fonts/roboto-v15-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url('../fonts/roboto-v15-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */ - url('../fonts/roboto-v15-latin-regular.woff') format('woff'), /* Modern Browsers */ - url('../fonts/roboto-v15-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */ - url('../fonts/roboto-v15-latin-regular.svg#Roboto') format('svg'); + src: local("Roboto"), local("Roboto-Regular"), + url("../fonts/roboto-v15-latin-regular.eot?#iefix") + format("embedded-opentype"), + /* IE6-IE8 */ url("../fonts/roboto-v15-latin-regular.woff2") + format("woff2"), + /* Super Modern Browsers */ + url("../fonts/roboto-v15-latin-regular.woff") format("woff"), + /* Modern Browsers */ url("../fonts/roboto-v15-latin-regular.ttf") + format("truetype"), + /* Safari, Android, iOS */ + url("../fonts/roboto-v15-latin-regular.svg#Roboto") format("svg"); /* Legacy iOS */ } html, body { - font-family: 'Roboto', sans-serif; + font-family: "Roboto", sans-serif; height: 100%; overflow: hidden; background-color: lightgray; overflow: hidden; } -.customQueryGlyph{ +.customQueryGlyph { font-size: small; vertical-align: middle; top: -1px; @@ -47,7 +53,7 @@ div.tooltip-inner-custom { max-width: 250px; } -.aboutscroll{ +.aboutscroll { width: 100%; overflow: auto; height: 150px; @@ -87,8 +93,8 @@ div.tooltip-inner-custom { outline: 0; } -.query-box{ - padding: 0 .5em .5em .5em +.query-box { + padding: 0 0.5em 0.5em 0.5em; } .graph { @@ -98,9 +104,9 @@ div.tooltip-inner-custom { overflow: hidden; } -.dropdown-item > i{ - float:right; - margin-top:3px; +.dropdown-item > i { + float: right; + margin-top: 3px; margin-left: 2px; } @@ -108,24 +114,24 @@ div.tooltip-inner-custom { outline: 0; } -.typeaheadfix{ +.typeaheadfix { border: 0; } -.typeaheadfix .Select-menu-outer{ +.typeaheadfix .Select-menu-outer { z-index: 99999; } -.typeaheadfix .Select-control{ +.typeaheadfix .Select-control { border: 0; box-shadow: none !important; } -.typeaheadfix .Select-control:focus{ +.typeaheadfix .Select-control:focus { border: 0; } -.typeaheadfix .Select-arrow-zone{ +.typeaheadfix .Select-arrow-zone { display: none; } @@ -181,14 +187,13 @@ div.tooltip-inner-custom { } } - .mainfade-appear { - opacity: 0.01; + opacity: 0.01; } .mainfade-appear.mainfade-appear-active { - opacity: 1; - transition: opacity 1s ease-in; + opacity: 1; + transition: opacity 1s ease-in; } .dl-horizontal-fix { @@ -201,7 +206,7 @@ div.tooltip-inner-custom { color: blue; } -.dl-horizontal{ +.dl-horizontal { margin-right: 10px; } @@ -223,7 +228,7 @@ div.tooltip-inner-custom { .nodedatabox { overflow: auto; - border-top: .5px solid lightgray; + border-top: 0.5px solid lightgray; background-color: white; } @@ -270,7 +275,6 @@ div.tooltip-inner-custom { padding-bottom: 10px; } - .hideme { position: absolute; top: 0; @@ -296,7 +300,7 @@ div.tooltip-inner-custom { .loginwindow { width: 100%; height: 100%; - background-color: rgba(0, 0, 0, .7); + background-color: rgba(0, 0, 0, 0.7); } @media all and (min-width: 600px) { @@ -307,10 +311,10 @@ div.tooltip-inner-custom { overflow: auto; max-width: 70%; transform: translate(-50%, -50%); - border: 5px solid rgba(0, 0, 0, .5); + border: 5px solid rgba(0, 0, 0, 0.5); border-radius: 10px; - background-color: rgba(255, 255, 255, .9); - box-shadow: 0 12px 15px 0 rgba(0, 0, 0, .2); + background-color: rgba(255, 255, 255, 0.9); + box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.2); } } @@ -322,10 +326,10 @@ div.tooltip-inner-custom { overflow: auto; max-width: 40%; transform: translate(-50%, -50%); - border: 5px solid rgba(0, 0, 0, .5); + border: 5px solid rgba(0, 0, 0, 0.5); border-radius: 10px; - background-color: rgba(255, 255, 255, .9); - box-shadow: 0 12px 15px 0 rgba(0, 0, 0, .2); + background-color: rgba(255, 255, 255, 0.9); + box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.2); } } @@ -337,10 +341,10 @@ div.tooltip-inner-custom { overflow: auto; max-width: 25%; transform: translate(-50%, -50%); - border: 5px solid rgba(0, 0, 0, .5); + border: 5px solid rgba(0, 0, 0, 0.5); border-radius: 10px; - background-color: rgba(255, 255, 255, .9); - box-shadow: 0 12px 15px 0 rgba(0, 0, 0, .2); + background-color: rgba(255, 255, 255, 0.9); + box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.2); } } @@ -356,8 +360,8 @@ div.tooltip-inner-custom { } .loginwindow .input-group-addon { - min-width:115px; - text-align:left; + min-width: 115px; + text-align: left; } .loginwindow > #checkconnectionpanel { @@ -367,10 +371,10 @@ div.tooltip-inner-custom { overflow: auto; max-width: 30%; transform: translate(-50%, -50%); - border: 5px solid rgba(0, 0, 0, .5); + border: 5px solid rgba(0, 0, 0, 0.5); border-radius: 10px; - background-color: rgba(255, 255, 255, .9); - box-shadow: 0 12px 15px 0 rgba(0, 0, 0, .2); + background-color: rgba(255, 255, 255, 0.9); + box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.2); } .loginwindow > #checkconnectionpanel span { @@ -386,10 +390,10 @@ div.tooltip-inner-custom { overflow: auto; max-width: 30%; transform: translate(-50%, -50%); - border: 5px solid rgba(0, 0, 0, .5); + border: 5px solid rgba(0, 0, 0, 0.5); border-radius: 10px; - background-color: rgba(255, 255, 255, .9); - box-shadow: 0 12px 15px 0 rgba(0, 0, 0, .2); + background-color: rgba(255, 255, 255, 0.9); + box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.2); } .loginwindow > #logoutpanel span { @@ -399,7 +403,7 @@ div.tooltip-inner-custom { } .loginwindow > #logoutpanel div { - margin: .5em 1em .5em 1em; + margin: 0.5em 1em 0.5em 1em; } .green-icon-color { @@ -418,19 +422,19 @@ div.tooltip-inner-custom { margin-top: 5px; } -.savecontainer{ - margin-top:10px; +.savecontainer { + margin-top: 10px; } .buttoncontainer { width: 50%; - display:inline-block; + display: inline-block; text-align: right; } .logincheck { width: 50%; - display:inline-block; + display: inline-block; } .has-spinner.activate .button-spinner { @@ -441,51 +445,58 @@ div.tooltip-inner-custom { .button-spinner { display: inline-block; width: 0; - -webkit-transition: opacity .25s, width .25s; - -moz-transition: opacity .25s, width .25s; - -o-transition: opacity .25s, width .25s; - transition: opacity .25s, width .25s; + -webkit-transition: opacity 0.25s, width 0.25s; + -moz-transition: opacity 0.25s, width 0.25s; + -o-transition: opacity 0.25s, width 0.25s; + transition: opacity 0.25s, width 0.25s; opacity: 0; } .spinner > div { - width: 12px; - height: 12px; - background-color: #333; + width: 12px; + height: 12px; + background-color: #333; - border-radius: 100%; - display: inline-block; - -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both; - animation: sk-bouncedelay 1.4s infinite ease-in-out both; + border-radius: 100%; + display: inline-block; + -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both; + animation: sk-bouncedelay 1.4s infinite ease-in-out both; } .spinner .bounce1 { - -webkit-animation-delay: -0.32s; - animation-delay: -0.32s; + -webkit-animation-delay: -0.32s; + animation-delay: -0.32s; } .spinner .bounce2 { - -webkit-animation-delay: -0.16s; - animation-delay: -0.16s; + -webkit-animation-delay: -0.16s; + animation-delay: -0.16s; } @-webkit-keyframes sk-bouncedelay { - 0%, 80%, 100% { -webkit-transform: scale(0) } - 40% { -webkit-transform: scale(1.0) } + 0%, + 80%, + 100% { + -webkit-transform: scale(0); + } + 40% { + -webkit-transform: scale(1); + } } @keyframes sk-bouncedelay { - 0%, 80%, 100% { - -webkit-transform: scale(0); - transform: scale(0); - } 40% { - -webkit-transform: scale(1.0); - transform: scale(1.0); - } + 0%, + 80%, + 100% { + -webkit-transform: scale(0); + transform: scale(0); + } + 40% { + -webkit-transform: scale(1); + transform: scale(1); + } } - - .modal { text-align: center; } @@ -494,7 +505,7 @@ div.tooltip-inner-custom { .modal:before { display: inline-block; height: 100%; - content: ' '; + content: " "; vertical-align: middle; } } @@ -586,17 +597,17 @@ div.tooltip-inner-custom { .loadingIndicator { position: absolute; z-index: 25; - bottom: .5em; + bottom: 0.5em; left: 1em; width: 80px; } .loadingIndicator div:first-child { text-align: center; - font-weight: bold + font-weight: bold; } -.loadingIndicator img{ +.loadingIndicator img { width: 80px; height: auto; } @@ -608,8 +619,8 @@ div.tooltip-inner-custom { right: 10em; overflow: auto; min-width: 15%; - background-color: rgba(255, 255, 255, .9); - box-shadow: 0 12px 15px 0 rgba(0, 0, 0, .2); + background-color: rgba(255, 255, 255, 0.9); + box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.2); } .sliderinput { @@ -633,15 +644,15 @@ div.tooltip-inner-custom { margin-left: 5px; } -.center-align{ +.center-align { text-align: center; } -#tab-style{ - border-top: .5px solid lightgray; +#tab-style { + border-top: 0.5px solid lightgray; } -#tab-style > ul{ +#tab-style > ul { display: table; width: 100%; border-bottom: 1px solid lightgray; @@ -655,7 +666,7 @@ div.tooltip-inner-custom { display: table-cell; width: 33%; text-align: center; - color: #337AB7; + color: #337ab7; } #tab-style > ul > li > a:focus { @@ -673,17 +684,17 @@ div.tooltip-inner-custom { color: black; } -#tab-style > ul > li > a[aria-selected='true']{ +#tab-style > ul > li > a[aria-selected="true"] { border-right: 1px solid lightgray; border-left: 1px solid lightgray; color: black; } -#tab-style > div{ +#tab-style > div { padding: 0 10px 0 10px; } -#tab-style h3{ +#tab-style h3 { text-align: center; } @@ -695,11 +706,11 @@ div.tooltip-inner-custom { display: none; } -.menubutton{ +.menubutton { border-radius: 4px; background-color: #f4511e; border: none; - color: #FFFFFF; + color: #ffffff; text-align: center; font-size: 28px; padding: 20px; @@ -717,7 +728,7 @@ div.tooltip-inner-custom { overflow: visible; } -.menudiv > div > button{ +.menudiv > div > button { overflow: hidden; white-space: nowrap; background-color: #fff; @@ -726,14 +737,14 @@ div.tooltip-inner-custom { clear: right; } -.menudiv > div > button > span{ +.menudiv > div > button > span { vertical-align: middle; top: -1px; width: 15px; height: 15px; } -.fake-hidden{ +.fake-hidden { width: 0; height: 0; overflow: hidden; @@ -745,8 +756,8 @@ div.tooltip-inner-custom { top: 1em; right: 10em; overflow: auto; - background-color: rgba(255, 255, 255, .9); - box-shadow: 0 12px 15px 0 rgba(0, 0, 0, .2); + background-color: rgba(255, 255, 255, 0.9); + box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.2); } .queryNodeSelect { @@ -764,23 +775,23 @@ div.tooltip-inner-custom { max-height: 16em; } -.queryNodeSelect img{ +.queryNodeSelect img { width: auto; height: 100px; display: block; - margin:auto; + margin: auto; } -.queryNodeItemPreWrap > p{ +.queryNodeItemPreWrap > p { white-space: pre-wrap; } -.tab-content > div:last-child > div{ +.tab-content > div:last-child > div { max-height: 200px; overflow-y: auto; } -.tab-content > div:nth-last-child(2) > div{ +.tab-content > div:nth-last-child(2) > div { max-height: 600px; overflow-y: auto; -} \ No newline at end of file +} diff --git a/src/css/tooltip.css b/src/css/tooltip.css index 599e87a..0c663d8 100644 --- a/src/css/tooltip.css +++ b/src/css/tooltip.css @@ -90,7 +90,7 @@ z-index: 29; max-height: 400px; display: inline-block; - background-color: rgba(255, 255, 255, .95); + background-color: rgba(255, 255, 255, 0.95); border-radius: 12px; position: absolute; top: 100px; diff --git a/src/index.js b/src/index.js index 238b454..2001912 100644 --- a/src/index.js +++ b/src/index.js @@ -1,30 +1,30 @@ -import 'babel-polyfill'; // generators -import React from 'react'; -import ReactDOM from 'react-dom'; +import "babel-polyfill"; // generators +import React from "react"; +import ReactDOM from "react-dom"; -import AppContainer from './AppContainer'; -import Login from './components/Float/Login'; -import { getStorageData, storageHasKey, storageSetKey } from './js/utils.js'; +import AppContainer from "./AppContainer"; +import Login from "./components/Float/Login"; -const { app } = require('electron').remote; -var fs = require('fs'); -const path = require('path'); +import { remote } from "electron"; +const { app } = remote; +import { stat, writeFile } from "fs"; +import { join } from "path"; -const ConfigStore = require('electron-store'); +import ConfigStore from "electron-store"; global.conf = new ConfigStore(); -var e = require('eventemitter2').EventEmitter2; +import { EventEmitter2 as e } from "eventemitter2"; global.emitter = new e({}); global.renderEmit = new e({}); -global.neo4j = require('neo4j-driver').v1; +global.neo4j = require("neo4j-driver").v1; -global.Mustache = require('mustache'); +global.Mustache = require("mustache"); String.prototype.format = function() { var i = 0, args = arguments; return this.replace(/{}/g, function() { - return typeof args[i] !== 'undefined' ? args[i++] : ''; + return typeof args[i] !== "undefined" ? args[i++] : ""; }); }; @@ -34,14 +34,14 @@ String.prototype.formatAll = function() { }; String.prototype.toTitleCase = function() { - return this.replace(/\w\S*/g, function(txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }); + return this.replace(/\w\S*/g, function(txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); }; Array.prototype.allEdgesSameType = function() { - for (var i = 1; i < this.length; i++) { - if (this[i].neo4j_type !== this[0].neo4j_type) - return false; + if (this[i].neo4j_type !== this[0].neo4j_type) return false; } return true; @@ -51,19 +51,19 @@ if (!Array.prototype.last) { Array.prototype.last = function() { return this[this.length - 1]; }; -}; +} sigma.renderers.def = sigma.renderers.canvas; -sigma.classes.graph.addMethod('outboundNodes', function(id) { +sigma.classes.graph.addMethod("outboundNodes", function(id) { return this.outNeighborsIndex.get(id).keyList(); }); -sigma.classes.graph.addMethod('inboundNodes', function(id) { +sigma.classes.graph.addMethod("inboundNodes", function(id) { return this.inNeighborsIndex.get(id).keyList(); }); -sigma.classes.graph.addMethod('outNeighbors', function (id) { +sigma.classes.graph.addMethod("outNeighbors", function(id) { return this.outNeighborsIndex.get(id).keyList(); }); @@ -78,132 +78,132 @@ global.appStore = { currentTooltip: null, highResPalette: { iconScheme: { - 'User': { - font: 'FontAwesome', - content: '\uF007', + User: { + font: "FontAwesome", + content: "\uF007", scale: 1.5, - color: '#17E625' + color: "#17E625" }, - 'Computer': { - font: 'FontAwesome', - content: '\uF108', + Computer: { + font: "FontAwesome", + content: "\uF108", scale: 1.2, - color: '#E67873' + color: "#E67873" }, - 'Group': { - font: 'FontAwesome', - content: '\uF0C0', + Group: { + font: "FontAwesome", + content: "\uF0C0", scale: 1.5, - color: '#DBE617' + color: "#DBE617" }, - 'Domain': { - font: 'FontAwesome', - content: '\uF0AC', + Domain: { + font: "FontAwesome", + content: "\uF0AC", scale: 1.5, - color: '#17E6B9' + color: "#17E6B9" }, - 'OU': { - font: 'FontAwesome', - content: '\uF0E8', + OU: { + font: "FontAwesome", + content: "\uF0E8", scale: 1.25, - color: '#FFAA00' + color: "#FFAA00" }, - 'GPO': { - font: 'FontAwesome', - content: '\uF03A', + GPO: { + font: "FontAwesome", + content: "\uF03A", scale: 1.25, - color: '#7F72FD' + color: "#7F72FD" } }, edgeScheme: { - 'AdminTo': 'tapered', - 'MemberOf': 'tapered', - 'HasSession': 'tapered', - 'AllExtendedRights': 'tapered', - 'ForceChangePassword': 'tapered', - 'GenericAll': 'tapered', - 'GenericWrite': 'tapered', - 'WriteDacl': 'tapered', - 'WriteOwner': 'tapered', - 'AddMember': 'tapered', - 'TrustedBy': 'curvedArrow', - 'DCSync' : 'tapered', - 'Contains': 'tapered', - 'GpLink':'tapered', - 'Owns':'tapered' + AdminTo: "tapered", + MemberOf: "tapered", + HasSession: "tapered", + AllExtendedRights: "tapered", + ForceChangePassword: "tapered", + GenericAll: "tapered", + GenericWrite: "tapered", + WriteDacl: "tapered", + WriteOwner: "tapered", + AddMember: "tapered", + TrustedBy: "curvedArrow", + DCSync: "tapered", + Contains: "tapered", + GpLink: "tapered", + Owns: "tapered" } }, lowResPalette: { colorScheme: { - 'User': '#17E625', - 'Computer': '#E67873', - 'Group': '#DBE617', - 'Domain': '#17E6B9' + User: "#17E625", + Computer: "#E67873", + Group: "#DBE617", + Domain: "#17E6B9" }, edgeScheme: { - 'AdminTo': 'line', - 'MemberOf': 'line', - 'HasSession': 'line', - 'AllExtendedRights': 'line', - 'ForceChangePassword': 'line', - 'GenericAll': 'line', - 'GenericWrite': 'line', - 'WriteDACL': 'line', - 'WriteOwner': 'line', - 'AddMember': 'line', - 'TrustedBy': 'curvedArrow' + AdminTo: "line", + MemberOf: "line", + HasSession: "line", + AllExtendedRights: "line", + ForceChangePassword: "line", + GenericAll: "line", + GenericWrite: "line", + WriteDACL: "line", + WriteOwner: "line", + AddMember: "line", + TrustedBy: "curvedArrow" } }, highResStyle: { nodes: { label: { - by: 'label' + by: "label" }, size: { - by: 'degree', + by: "degree", bins: 5, min: 10, max: 20 }, icon: { - by: 'type', - scheme: 'iconScheme' + by: "type", + scheme: "iconScheme" } }, edges: { type: { - by: 'type', - scheme: 'edgeScheme' + by: "type", + scheme: "edgeScheme" } } }, lowResStyle: { nodes: { label: { - by: 'label' + by: "label" }, size: { - by: 'degree', + by: "degree", bins: 10, min: 10, max: 20 }, color: { - by: 'type', - scheme: 'colorScheme' + by: "type", + scheme: "colorScheme" } }, edges: { type: { - by: 'type', - scheme: 'edgeScheme' + by: "type", + scheme: "edgeScheme" } } } }; -if (typeof conf.get('performance') === 'undefined') { - conf.set('performance', { +if (typeof conf.get("performance") === "undefined") { + conf.set("performance", { edge: 5, sibling: 10, lowGraphics: false, @@ -212,31 +212,31 @@ if (typeof conf.get('performance') === 'undefined') { }); } -var custompath = path.join(app.getPath('userData'), 'customqueries.json'); +var custompath = join(app.getPath("userData"), "customqueries.json"); -fs.stat(custompath, function(err, stats) { +stat(custompath, function(err, stats) { if (err) { - fs.writeFile(custompath, "[]"); + writeFile(custompath, "[]"); } }); -appStore.performance = conf.get('performance'); +appStore.performance = conf.get("performance"); -if (typeof appStore.performance.edgeLabels === 'undefined'){ +if (typeof appStore.performance.edgeLabels === "undefined") { appStore.performance.edgeLabels = 0; - conf.set('performance', appStore.performance); + conf.set("performance", appStore.performance); } -renderEmit.on('login', function() { +renderEmit.on("login", function() { emitter.removeAllListeners(); - ReactDOM.unmountComponentAtNode(document.getElementById('root')); - ReactDOM.render( < AppContainer / > , document.getElementById('root')); + ReactDOM.unmountComponentAtNode(document.getElementById("root")); + ReactDOM.render(, document.getElementById("root")); }); -renderEmit.on('logout', function() { +renderEmit.on("logout", function() { emitter.removeAllListeners(); - ReactDOM.unmountComponentAtNode(document.getElementById('root')); - ReactDOM.render( < Login / > , document.getElementById('root')); + ReactDOM.unmountComponentAtNode(document.getElementById("root")); + ReactDOM.render(, document.getElementById("root")); }); -ReactDOM.render( < Login / > , document.getElementById('root')); \ No newline at end of file +ReactDOM.render(, document.getElementById("root")); diff --git a/src/js/utils.js b/src/js/utils.js index caa1579..5e98cd2 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -1,11 +1,11 @@ export function generateUniqueId(sigmaInstance, isNode) { var i = Math.floor(Math.random() * (100000 - 10 + 1)) + 10; if (isNode) { - while (typeof sigmaInstance.graph.nodes(i) !== 'undefined') { + while (typeof sigmaInstance.graph.nodes(i) !== "undefined") { i = Math.floor(Math.random() * (100000 - 10 + 1)) + 10; } } else { - while (typeof sigmaInstance.graph.edges(i) !== 'undefined') { + while (typeof sigmaInstance.graph.edges(i) !== "undefined") { i = Math.floor(Math.random() * (100000 - 10 + 1)) + 10; } } @@ -20,16 +20,18 @@ export function findGraphPath(sigmaInstance, reverse, nodeid, traversed) { //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 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) { + $.each(nodes, function(index, node) { + $.each(edges, function(index, edge) { var check = reverse ? edge.source : edge.target; //If an edge is pointing in the right direction, set its color - //Push the edge into our store and then + //Push the edge into our store and then node = parseInt(node); if (check === node && !traversed.includes(node)) { - edge.color = reverse ? 'blue' : 'red'; + edge.color = reverse ? "blue" : "red"; appStore.highlightedEdges.push(edge); findGraphPath(sigmaInstance, reverse, node, traversed); } @@ -41,19 +43,22 @@ export function findGraphPath(sigmaInstance, reverse, nodeid, traversed) { } export function clearSessions() { - emitter.emit('openClearingModal'); + emitter.emit("openClearingModal"); deleteSessions(); } function deleteSessions() { var session = driver.session(); - session.run("MATCH ()-[r:HasSession]-() WITH r LIMIT 100000 DELETE r RETURN count(r)") - .then(function (results) { + session + .run( + "MATCH ()-[r:HasSession]-() WITH r LIMIT 100000 DELETE r RETURN count(r)" + ) + .then(function(results) { session.close(); emitter.emit("refreshDBData"); var count = results.records[0]._fields[0].low; if (count === 0) { - emitter.emit('hideDBClearModal'); + emitter.emit("hideDBClearModal"); } else { deleteSessions(); } @@ -61,14 +66,15 @@ function deleteSessions() { } export function clearDatabase() { - emitter.emit('openClearingModal'); + emitter.emit("openClearingModal"); deleteEdges(); } function deleteEdges() { var session = driver.session(); - session.run("MATCH ()-[r]-() WITH r LIMIT 100000 DELETE r RETURN count(r)") - .then(function (results) { + session + .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; @@ -82,8 +88,9 @@ function deleteEdges() { function deleteNodes() { var session = driver.session(); - session.run("MATCH (n) WITH n LIMIT 100000 DELETE n RETURN count(n)") - .then(function (results) { + 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; @@ -98,29 +105,27 @@ function deleteNodes() { function grabConstraints() { var session = driver.session(); let constraints = []; - session.run("CALL db.constraints") - .then(function (results) { - $.each(results.records, function (index, container) { - let constraint = container._fields[0]; - let query = "DROP " + constraint; - constraints.push(query); - }); - - session.close(); - - dropConstraints(constraints); + session.run("CALL db.constraints").then(function(results) { + $.each(results.records, function(index, container) { + let constraint = container._fields[0]; + let query = "DROP " + constraint; + constraints.push(query); }); + + session.close(); + + dropConstraints(constraints); + }); } function dropConstraints(constraints) { if (constraints.length > 0) { let constraint = constraints.shift(); let session = driver.session(); - session.run(constraint) - .then(function () { - dropConstraints(constraints); - session.close(); - }); + session.run(constraint).then(function() { + dropConstraints(constraints); + session.close(); + }); } else { grabIndexes(); } @@ -130,29 +135,27 @@ function grabIndexes() { var session = driver.session(); let constraints = []; - session.run("CALL db.indexes") - .then(function (results) { - $.each(results.records, function (index, container) { - let constraint = container._fields[0]; - let query = "DROP " + constraint; - constraints.push(query); - }); - - session.close(); - - dropIndexes(constraints); + session.run("CALL db.indexes").then(function(results) { + $.each(results.records, function(index, container) { + let constraint = container._fields[0]; + let query = "DROP " + constraint; + constraints.push(query); }); + + session.close(); + + dropIndexes(constraints); + }); } function dropIndexes(indexes) { if (indexes.length > 0) { let constraint = indexes.shift(); let session = driver.session(); - session.run(constraint) - .then(function () { - dropConstraints(indexes); - session.close(); - }); + session.run(constraint).then(function() { + dropConstraints(indexes); + session.close(); + }); } else { addConstraints(); } @@ -167,55 +170,64 @@ function addConstraints() { var s6 = driver.session(); s1.run("CREATE CONSTRAINT ON (c:User) ASSERT c.name IS UNIQUE") - .then(function () { + .then(function() { s1.close(); s2.run("CREATE CONSTRAINT ON (c:Computer) ASSERT c.name IS UNIQUE") - .then(function () { + .then(function() { s2.close(); - s3.run("CREATE CONSTRAINT ON (c:Group) ASSERT c.name IS UNIQUE") - .then(function () { + s3.run( + "CREATE CONSTRAINT ON (c:Group) ASSERT c.name IS UNIQUE" + ) + .then(function() { s3.close(); - s4.run("CREATE CONSTRAINT ON (c:Domain) ASSERT c.name IS UNIQUE") - .then(function () { + s4.run( + "CREATE CONSTRAINT ON (c:Domain) ASSERT c.name IS UNIQUE" + ) + .then(function() { s4.close(); - s5.run("CREATE CONSTRAINT on (c:OU) ASSERT c.guid IS UNIQUE") - .then(function () { + s5.run( + "CREATE CONSTRAINT on (c:OU) ASSERT c.guid IS UNIQUE" + ) + .then(function() { s5.close(); - s6.run("CREATE CONSTRAINT on (c:GPO) ASSERT c.name is UNIQUE") - .then(function () { + s6.run( + "CREATE CONSTRAINT on (c:GPO) ASSERT c.name is UNIQUE" + ) + .then(function() { s6.close(); }) - .catch(function () { + .catch(function() { s6.close(); }); }) - .catch(function () { + .catch(function() { s5.close(); }); }) - .catch(function () { + .catch(function() { s4.close(); }); }) - .catch(function () { + .catch(function() { s3.close(); }); }) - .catch(function () { + .catch(function() { s2.close(); }); }) - .catch(function () { + .catch(function() { s1.close(); }); - emitter.emit('hideDBClearModal'); + emitter.emit("hideDBClearModal"); } function processAceArray(array, objname, objtype, output) { - let baseAceQuery = 'UNWIND {props} AS prop MERGE (a:{} {name:prop.principal}) MERGE (b:{} {name: prop.obj}) MERGE (a)-[r:{} {isacl:true}]->(b)' + let baseAceQuery = + "UNWIND {props} AS prop MERGE (a:{} {name:prop.principal}) MERGE (b:{} {name: prop.obj}) MERGE (a)-[r:{} {isacl:true}]->(b)"; - $.each(array, function (_, ace) { + $.each(array, function(_, ace) { let principal = ace.PrincipalName; let principaltype = ace.PrincipalType; let right = ace.RightName; @@ -225,96 +237,113 @@ function processAceArray(array, objname, objtype, output) { return; } - let rights = [] + let rights = []; //Process the right/type to figure out the ACEs we need to add - if (acetype === 'All') { - rights.push('AllExtendedRights'); - } else if (acetype === 'User-Force-Change-Password') { - rights.push('ForceChangePassword'); - } else if (acetype === 'Member') { - rights.push('AddMember'); - } else if (right === 'ExtendedRight') { + if (acetype === "All") { + rights.push("AllExtendedRights"); + } else if (acetype === "User-Force-Change-Password") { + rights.push("ForceChangePassword"); + } else if (acetype === "Member") { + rights.push("AddMember"); + } else if (right === "ExtendedRight") { rights.push(acetype); } - if (right.includes('GenericAll')) { - rights.push('GenericAll'); + if (right.includes("GenericAll")) { + rights.push("GenericAll"); } - if (right.includes('WriteDacl')) { - rights.push('WriteDacl'); + if (right.includes("WriteDacl")) { + rights.push("WriteDacl"); } - if (right.includes('WriteOwner')) { - rights.push('WriteOwner'); + if (right.includes("WriteOwner")) { + rights.push("WriteOwner"); } - if (right.includes('GenericWrite')) { - rights.push('GenericWrite'); + if (right.includes("GenericWrite")) { + rights.push("GenericWrite"); } - if (right === 'Owner') { - rights.push('Owns'); + if (right === "Owner") { + rights.push("Owns"); } - $.each(rights, function (_, right) { + $.each(rights, function(_, right) { let hash = right + principaltype; - let formatted = baseAceQuery.format(principaltype.toTitleCase(), objtype, right); + let formatted = baseAceQuery.format( + principaltype.toTitleCase(), + objtype, + right + ); - insert(output, hash, formatted, { principal: principal, obj: objname }); - }) - }) + insert(output, hash, formatted, { + principal: principal, + obj: objname + }); + }); + }); } export function buildDomainJson(chunk) { - let queries = {} + let queries = {}; queries.properties = { - statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.name}) SET n += prop.map", + statement: + "UNWIND {props} AS prop MERGE (n:Domain {name:prop.name}) SET n += prop.map", props: [] }; queries.links = { - statement: 'UNWIND {props} as prop MERGE (n:Domain {name:prop.domain}) MERGE (m:GPO {name:prop.gpo}) MERGE (m)-[r:GpLink {enforced:prop.enforced, isacl:false}]->(n)', + statement: + "UNWIND {props} as prop MERGE (n:Domain {name:prop.domain}) MERGE (m:GPO {name:prop.gpo}) MERGE (m)-[r:GpLink {enforced:prop.enforced, isacl:false}]->(n)", props: [] - } + }; queries.trusts = { - statement: 'UNWIND {props} AS prop MERGE (n:Domain {name: prop.a}) MERGE (m:Domain {name: prop.b}) MERGE (n)-[:TrustedBy {trusttype : prop.trusttype, transitive: prop.transitive, isacl:false}]->(m)', + statement: + "UNWIND {props} AS prop MERGE (n:Domain {name: prop.a}) MERGE (m:Domain {name: prop.b}) MERGE (n)-[:TrustedBy {trusttype : prop.trusttype, transitive: prop.transitive, isacl:false}]->(m)", props: [] - } + }; queries.childous = { - statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:OU {guid:prop.guid}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + statement: + "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:OU {guid:prop.guid}) MERGE (n)-[r:Contains {isacl:false}]->(m)", props: [] - } + }; queries.computers = { - statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + statement: + "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:Contains {isacl:false}]->(m)", props: [] - } + }; queries.users = { - statement: "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:User {name:prop.user}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + statement: + "UNWIND {props} AS prop MERGE (n:Domain {name:prop.domain}) MERGE (m:User {name:prop.user}) MERGE (n)-[r:Contains {isacl:false}]->(m)", props: [] - } + }; - $.each(chunk, function (_, domain) { + $.each(chunk, function(_, domain) { let name = domain.Name; let properties = domain.Properties; queries.properties.props.push({ map: properties, name: name }); let links = domain.Links; - $.each(links, function (_, link) { + $.each(links, function(_, link) { let enforced = link.IsEnforced; let target = link.Name; - queries.links.props.push({ domain: name, gpo: target, enforced: enforced }); + queries.links.props.push({ + domain: name, + gpo: target, + enforced: enforced + }); }); let trusts = domain.Trusts; - $.each(trusts, function (_, trust) { + $.each(trusts, function(_, trust) { let target = trust.TargetName; let transitive = trust.IsTransitive; let direction = trust.TrustDirection; @@ -322,14 +351,34 @@ export function buildDomainJson(chunk) { switch (direction) { case 0: - queries.trusts.props.push({ a: target, b: name, transitive: transitive, trusttype: type }); + queries.trusts.props.push({ + a: target, + b: name, + transitive: transitive, + trusttype: type + }); break; case 1: - queries.trusts.props.push({ a: name, b: target, transitive: transitive, trusttype: type }); + queries.trusts.props.push({ + a: name, + b: target, + transitive: transitive, + trusttype: type + }); break; case 2: - queries.trusts.props.push({ a: name, b: target, transitive: transitive, trusttype: type }); - queries.trusts.props.push({ a: target, b: name, transitive: transitive, trusttype: type }); + queries.trusts.props.push({ + a: name, + b: target, + transitive: transitive, + trusttype: type + }); + queries.trusts.props.push({ + a: target, + b: name, + transitive: transitive, + trusttype: type + }); break; } }); @@ -339,17 +388,17 @@ export function buildDomainJson(chunk) { let childous = domain.ChildOus; - $.each(childous, function (_, ou) { - queries.childous.props.push({ domain: name, guid: ou }) - }) + $.each(childous, function(_, ou) { + queries.childous.props.push({ domain: name, guid: ou }); + }); let comps = domain.Computers; - $.each(comps, function (_, computer) { - queries.computers.props.push({ domain: name, comp: computer }) - }) + $.each(comps, function(_, computer) { + queries.computers.props.push({ domain: name, comp: computer }); + }); - let users = domain.Users - $.each(users, function (_, user) { + let users = domain.Users; + $.each(users, function(_, user) { queries.users.props.push({ domain: name, user: user }); }); }); @@ -358,13 +407,14 @@ export function buildDomainJson(chunk) { } export function buildGpoJson(chunk) { - let queries = {} + let queries = {}; queries.properties = { - statement: "UNWIND {props} AS prop MERGE (n:GPO {name:prop.name}) SET n.guid=prop.guid", + statement: + "UNWIND {props} AS prop MERGE (n:GPO {name:prop.name}) SET n.guid=prop.guid", props: [] - } + }; - $.each(chunk, function (_, gpo) { + $.each(chunk, function(_, gpo) { let name = gpo.Name; let guid = gpo.Guid; queries.properties.props.push({ name: name, guid: guid }); @@ -377,15 +427,17 @@ export function buildGpoJson(chunk) { } export function buildGroupJson(chunk) { - let queries = {} + let queries = {}; queries.properties = { - statement: "UNWIND {props} AS prop MERGE (n:Group {name:prop.name}) SET n += prop.map", + statement: + "UNWIND {props} AS prop MERGE (n:Group {name:prop.name}) SET n += prop.map", props: [] - } + }; - let baseStatement = "UNWIND {props} AS prop MERGE (n:Group {name: prop.name}) MERGE (m:{} {name:prop.member}) MERGE (m)-[r:MemberOf {isacl:false}]->(n)"; + let baseStatement = + "UNWIND {props} AS prop MERGE (n:Group {name: prop.name}) MERGE (m:{} {name:prop.member}) MERGE (m)-[r:MemberOf {isacl:false}]->(n)"; - $.each(chunk, function (_, group) { + $.each(chunk, function(_, group) { let name = group.Name; let properties = group.Properties; @@ -395,103 +447,109 @@ export function buildGroupJson(chunk) { processAceArray(aces, name, "Group", queries); let members = group.Members; - $.each(members, function (_, member) { + $.each(members, function(_, member) { let mname = member.MemberName; let mtype = member.MemberType; - let statement = baseStatement.format(mtype.toTitleCase()) - insert(queries, mtype, statement, { name: name, member: mname }) + let statement = baseStatement.format(mtype.toTitleCase()); + insert(queries, mtype, statement, { name: name, member: mname }); }); }); - return queries + return queries; } export function buildOuJson(chunk) { let queries = {}; queries.properties = { - statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.guid}) SET n += prop.map", + statement: + "UNWIND {props} AS prop MERGE (n:OU {guid:prop.guid}) SET n += prop.map", props: [] - } + }; queries.childous = { - statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.parent}) MERGE (m:OU {guid:prop.child}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + statement: + "UNWIND {props} AS prop MERGE (n:OU {guid:prop.parent}) MERGE (m:OU {guid:prop.child}) MERGE (n)-[r:Contains {isacl:false}]->(m)", props: [] - } + }; queries.computers = { - statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.ou}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + statement: + "UNWIND {props} AS prop MERGE (n:OU {guid:prop.ou}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:Contains {isacl:false}]->(m)", props: [] - } + }; queries.users = { - statement: "UNWIND {props} AS prop MERGE (n:OU {guid:prop.ou}) MERGE (m:User {name:prop.user}) MERGE (n)-[r:Contains {isacl:false}]->(m)", + statement: + "UNWIND {props} AS prop MERGE (n:OU {guid:prop.ou}) MERGE (m:User {name:prop.user}) MERGE (n)-[r:Contains {isacl:false}]->(m)", props: [] - } + }; - $.each(chunk, function (_, ou) { + $.each(chunk, function(_, ou) { let guid = ou.Guid; let properties = ou.Properties; queries.properties.props.push({ guid: guid, map: properties }); let childous = ou.ChildOus; - $.each(childous, function (_, cou) { + $.each(childous, function(_, cou) { queries.childous.props.push({ parent: guid, child: cou }); - }) + }); let computers = ou.Computers; - $.each(computers, function (_, computer) { - queries.computers.props.push({ ou: guid, comp: computer }) - }) + $.each(computers, function(_, computer) { + queries.computers.props.push({ ou: guid, comp: computer }); + }); - let users = ou.Users - $.each(users, function (_, user) { + let users = ou.Users; + $.each(users, function(_, user) { queries.users.props.push({ ou: guid, user: user }); }); - }) + }); return queries; } export function buildSessionJson(chunk) { - let queries = {} + let queries = {}; queries.sessions = { - statement: "UNWIND {props} AS prop MERGE (n:User {name:prop.user}) MERGE (m:Computer {name:prop.comp}) MERGE (m)-[r:HasSession {weight: prop.weight, isacl:false}]->(n)", + statement: + "UNWIND {props} AS prop MERGE (n:User {name:prop.user}) MERGE (m:Computer {name:prop.comp}) MERGE (m)-[r:HasSession {weight: prop.weight, isacl:false}]->(n)", props: [] - } + }; - $.each(chunk, function (_, session) { + $.each(chunk, function(_, session) { let name = session.UserName; let comp = session.ComputerName; let weight = session.Weight; - queries.sessions.props.push({ user: name, comp: comp, weight: weight }) - }) + queries.sessions.props.push({ user: name, comp: comp, weight: weight }); + }); return queries; } export function buildGpoAdminJson(chunk) { - let queries = {} + let queries = {}; - let baseQuery = "UNWIND {props} AS prop MERGE (n:{} {name:prop.admin}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:AdminTo {isacl:false}]->(m)" - $.each(chunk, function (_, gpoadmin) { + let baseQuery = + "UNWIND {props} AS prop MERGE (n:{} {name:prop.admin}) MERGE (m:Computer {name:prop.comp}) MERGE (n)-[r:AdminTo {isacl:false}]->(m)"; + $.each(chunk, function(_, gpoadmin) { let comp = gpoadmin.Computer; let admin = gpoadmin.Name; let type = gpoadmin.Type; let query = baseQuery.format(type.toTitleCase()); - insert(queries, type, query, { admin: admin, comp: comp }) + insert(queries, type, query, { admin: admin, comp: comp }); }); return queries; } export function buildUserJson(chunk) { - let queries = {} + let queries = {}; - $.each(chunk, function (_, user) { + $.each(chunk, function(_, user) { let name = user.Name; let properties = user.Properties; let primarygroup = user.PrimaryGroup; @@ -499,30 +557,37 @@ export function buildUserJson(chunk) { if (!queries.properties) { if (primarygroup === null) { queries.properties = { - statement: "UNWIND {props} AS prop MERGE (n:User {name:prop.name}) SET n += prop.map", + statement: + "UNWIND {props} AS prop MERGE (n:User {name:prop.name}) SET n += prop.map", props: [] - } + }; } else { queries.properties = { - statement: "UNWIND {props} AS prop MERGE (n:User {name:prop.name}) MERGE (m:Group {name:prop.pg}) MERGE (n)-[r:MemberOf {isacl:false}]->(m) SET n += prop.map", + statement: + "UNWIND {props} AS prop MERGE (n:User {name:prop.name}) MERGE (m:Group {name:prop.pg}) MERGE (n)-[r:MemberOf {isacl:false}]->(m) SET n += prop.map", props: [] - } + }; } } - queries.properties.props.push({ map: properties, name: name, pg: primarygroup }); + queries.properties.props.push({ + map: properties, + name: name, + pg: primarygroup + }); let aces = user.Aces; processAceArray(aces, name, "User", queries); }); - return queries + return queries; } export function buildComputerJson(chunk) { - let queries = {} - let baseQuery = "UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) MERGE (m:{} {name:prop.target}) MERGE (m)-[r:{} {isacl: false}]->(n)" + let queries = {}; + let baseQuery = + "UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) MERGE (m:{} {name:prop.target}) MERGE (m)-[r:{} {isacl: false}]->(n)"; - $.each(chunk, function (_, comp) { + $.each(chunk, function(_, comp) { let name = comp.Name; let properties = comp.Properties; let localadmins = comp.LocalAdmins; @@ -532,19 +597,25 @@ export function buildComputerJson(chunk) { if (!queries.properties) { if (primarygroup === null) { queries.properties = { - statement: "UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) SET n += prop.map", + statement: + "UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) SET n += prop.map", props: [] - } + }; } else { queries.properties = { - statement: "UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) MERGE (m:Group {name:prop.pg}) MERGE (n)-[r:MemberOf {isacl:false}]->(m) SET n += prop.map", + statement: + "UNWIND {props} AS prop MERGE (n:Computer {name:prop.name}) MERGE (m:Group {name:prop.pg}) MERGE (n)-[r:MemberOf {isacl:false}]->(m) SET n += prop.map", props: [] - } + }; } } - queries.properties.props.push({ map: properties, name: name, pg: primarygroup }); - $.each(localadmins, function (_, admin) { + queries.properties.props.push({ + map: properties, + name: name, + pg: primarygroup + }); + $.each(localadmins, function(_, admin) { let aType = admin.Type; let aName = admin.Name; let rel = "AdminTo"; @@ -554,9 +625,9 @@ export function buildComputerJson(chunk) { let statement = baseQuery.format(aType, rel); let p = { name: name, target: aName }; insert(queries, hash, statement, p); - }) + }); - $.each(rdpers, function (_, rdp) { + $.each(rdpers, function(_, rdp) { let aType = rdp.Type; let aName = rdp.Name; let rel = "CanRDP"; @@ -566,19 +637,19 @@ export function buildComputerJson(chunk) { let statement = baseQuery.format(aType, rel); let p = { name: name, target: aName }; insert(queries, hash, statement, p); - }) + }); }); - return queries + return queries; } function insert(obj, hash, statement, prop) { if (obj[hash]) { - obj[hash].props.push(prop) + obj[hash].props.push(prop); } else { - obj[hash] = {} + obj[hash] = {}; obj[hash].statement = statement; - obj[hash].props = [] - obj[hash].props.push(prop) + obj[hash].props = []; + obj[hash].props.push(prop); } } diff --git a/src/js/worker.js b/src/js/worker.js index 6060dfe..49d1f42 100644 --- a/src/js/worker.js +++ b/src/js/worker.js @@ -1,33 +1,27 @@ -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++) { - if (this[i].type !== this[0].type) - return false; + if (this[i].type !== this[0].type) return false; } return true; }; -// sigma.classes.graph.addMethod('outboundNodes', function(id) { -// return this.outNeighborsIndex.get(id).keyList(); -// }); - -// sigma.classes.graph.addMethod('inboundNodes', function(id) { -// return this.inNeighborsIndex.get(id).keyList(); -// }); - var sigmaInstance = new sigma(); - -process.on('message', function(m){ +process.on("message", function(m) { var data = JSON.parse(m); - params = {edge: data.edge,sibling: data.sibling, start: data.start, end: data.end}; + 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){ + sigmaInstance.graph.nodes().forEach(function(node) { node.degree = sigmaInstance.graph.degree(node.id); }); var result = collapseEdgeNodes(sigmaInstance, params, spotlightData); @@ -41,18 +35,22 @@ process.on('message', function(m){ spotlightData[node.id] = [node.label, 0, "", node.type, ""]; } }); - var toSend = {nodes: sigmaInstance.graph.nodes(), edges: sigmaInstance.graph.edges(), spotlight: spotlightData}; + var toSend = { + nodes: sigmaInstance.graph.nodes(), + edges: sigmaInstance.graph.edges(), + spotlight: spotlightData + }; process.send(toSend); }); -function collapseEdgeNodes(sigmaInstance, params, spotlightData){ +function collapseEdgeNodes(sigmaInstance, params, spotlightData) { var threshold = params.edge; - if (threshold === 0){ + if (threshold === 0) { return [sigmaInstance, spotlightData]; } - sigmaInstance.graph.nodes().forEach(function(node){ - if (node.degree < threshold){ + sigmaInstance.graph.nodes().forEach(function(node) { + if (node.degree < threshold) { return; } @@ -63,55 +61,66 @@ function collapseEdgeNodes(sigmaInstance, params, spotlightData){ if (params.start !== null && node.label === params.start) { return; } - - sigmaInstance.graph.adjacentNodes(node.id).forEach(function(anode){ - if (params.end !== null && anode.label === params.end){ + 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){ + 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)){ + 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')){ + 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]; + spotlightData[anode.id] = [ + anode.label, + node.id, + node.label, + anode.type, + node.type + ]; sigmaInstance.graph.dropNode(anode.id); } }); - if (node.folded.nodes.length > 0){ + if (node.folded.nodes.length > 0) { node.glyphs.push({ - 'position': 'bottom-left', - 'content': node.folded.nodes.length - }); + position: "bottom-left", + content: node.folded.nodes.length + }); } }); return [sigmaInstance, spotlightData]; } -function collapseSiblingNodes(sigmaInstance, params, spotlightData){ +function collapseSiblingNodes(sigmaInstance, params, spotlightData) { var threshold = params.sibling; - if (threshold === 0){ + if (threshold === 0) { return [sigmaInstance, spotlightData]; } - sigmaInstance.graph.nodes().forEach(function(node){ + 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 ){ + if (!node.type_computer || node.folded.nodes.length > 0) { return; } @@ -128,33 +137,33 @@ function collapseSiblingNodes(sigmaInstance, params, spotlightData){ var siblings = []; //Check to see if all the edges are the same type (i.e. AdminTo) - if (adjacent.length > 1 && adjacent.allEdgesSameType()){ + 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; - } - ); + var parents = adjacent.map(function(e) { + return e.source; + }); - //Generate our string to compare other nodes to + //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; + 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){ + sigmaInstance.graph.nodes().forEach(function(node2) { + testString = sigmaInstance.graph + .adjacentEdges(node2.id) + .map(function(e) { return e.source; - } - ).sort().join(','); - if (testString === checkString){ + }) + .sort() + .join(","); + if (testString === checkString) { siblings.push(node2); } }); - if (siblings.length >= threshold){ + if (siblings.length >= threshold) { //Generate a new ID for our grouped node var nodeId = generateUniqueId(sigmaInstance, true); @@ -164,13 +173,15 @@ function collapseSiblingNodes(sigmaInstance, params, spotlightData){ y: node.y, degree: siblings.length, label: "Grouped Computers", - type: 'Computer', + type: "Computer", type_computer: true, groupedNode: true, - glyphs: [{ - position: 'bottom-left', - content: siblings.length - }], + glyphs: [ + { + position: "bottom-left", + content: siblings.length + } + ], folded: { nodes: [], edges: [] @@ -178,15 +189,15 @@ function collapseSiblingNodes(sigmaInstance, params, spotlightData){ }); //Generate new edges for each parent going to our new node - parents.forEach(function(parent){ + parents.forEach(function(parent) { var id = generateUniqueId(sigmaInstance, false); sigmaInstance.graph.addEdge({ id: id, source: parent, target: nodeId, - label: 'AdminTo', - neo4j_type: 'AdminTo', + label: "AdminTo", + neo4j_type: "AdminTo", size: 1 }); }); @@ -194,13 +205,21 @@ function collapseSiblingNodes(sigmaInstance, params, spotlightData){ 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); - }); + 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]; + spotlightData[sibling.id] = [ + sibling.label, + nodeId, + n.label, + sibling.type, + n.type + ]; sigmaInstance.graph.dropNode(sibling.id); }); } @@ -209,17 +228,17 @@ function collapseSiblingNodes(sigmaInstance, params, spotlightData){ return [sigmaInstance, spotlightData]; } -function generateUniqueId(sigmaInstance, isNode){ +function generateUniqueId(sigmaInstance, isNode) { var i = Math.floor(Math.random() * (100000 - 10 + 1)) + 10; - if (isNode){ - while (typeof sigmaInstance.graph.nodes(i) !== 'undefined'){ + 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'){ + } 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 eb031946cd40740477c7f520bbd31c35f7fc45f8 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 11:51:43 -0400 Subject: [PATCH 06/67] Fix lastlogon/pwdlastset display --- src/components/SearchContainer/Tabs/NodeProps.jsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/SearchContainer/Tabs/NodeProps.jsx b/src/components/SearchContainer/Tabs/NodeProps.jsx index 5409aed..44f571b 100644 --- a/src/components/SearchContainer/Tabs/NodeProps.jsx +++ b/src/components/SearchContainer/Tabs/NodeProps.jsx @@ -10,12 +10,11 @@ export default class componentName extends Component { var type = typeof obj; if (type === "undefined") { return null; - } else if (obj.hasOwnProperty("low")) { - var t = obj.low; - if (t === 0 || t === -1) { + } else if (type === "number") { + if (obj === 0 || obj === -1) { return "Never"; } else { - return new Date(obj.low * 1000).toUTCString(); + return new Date(obj * 1000).toUTCString(); } } else if (type === "boolean") { return obj.toString().toTitleCase(); From 41c14320651ce78094fdcd78f5a188907f2f0fe2 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 12:52:57 -0400 Subject: [PATCH 07/67] Add graph reload to refresh button --- src/components/Graph.jsx | 13 +++++++++++-- src/components/Menu/MenuContainer.jsx | 9 +++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/components/Graph.jsx b/src/components/Graph.jsx index 4971df3..09250eb 100644 --- a/src/components/Graph.jsx +++ b/src/components/Graph.jsx @@ -127,6 +127,7 @@ export default class GraphContainer extends Component { emitter.on("query", this.doGenericQuery.bind(this)); emitter.on("spotlightClick", this.spotlightClickHandler.bind(this)); emitter.on("graphRefresh", this.relayout.bind(this)); + emitter.on("graphReload", this.reload.bind(this)); emitter.on("export", this.export.bind(this)); emitter.on("import", this.import.bind(this)); emitter.on("clearDB", this.clearGraph.bind(this)); @@ -163,6 +164,10 @@ export default class GraphContainer extends Component { } } + reload(){ + this.doQueryNative(this.state.currentQuery); + } + export(payload) { if (payload === "image") { let size = $("#graph").outerWidth(); @@ -238,7 +243,8 @@ export default class GraphContainer extends Component { edges: this.state.sigmaInstance.graph.edges(), spotlight: appStore.spotlightData, startNode: appStore.startNode, - endNode: appStore.endNode + endNode: appStore.endNode, + params: this.state.currentQuery }); } $.each(graph.nodes, function(i, node) { @@ -305,7 +311,8 @@ export default class GraphContainer extends Component { edges: this.state.sigmaInstance.graph.edges(), spotlight: appStore.spotlightData, startNode: appStore.startNode, - endNode: appStore.endNode + endNode: appStore.endNode, + params: this.state.currentQuery }); appStore.spotlightData = graph.spotlight; @@ -444,6 +451,7 @@ export default class GraphContainer extends Component { nodes: query.nodes, edges: query.edges }); + this.setState({currentQuery: query.params}) this.applyDesign(); appStore.spotlightData = query.spotlight; (appStore.startNode = query.startNode), @@ -504,6 +512,7 @@ export default class GraphContainer extends Component { emitter.emit("showLoadingIndicator", true); emitter.emit("updateLoadingText", "Querying Database"); emitter.emit("resetSpotlight"); + this.setState({"currentQuery": params}) session.run(params.statement, params.props).subscribe({ onNext: function(result) { $.each( diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index aae4737..8fdcf30 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -47,8 +47,13 @@ export default class MenuContainer extends Component { ); } - _refreshClick() { - emitter.emit("graphRefresh"); + _refreshClick(event) { + if (event.ctrlKey){ + emitter.emit("graphReload"); + }else{ + emitter.emit("graphRefresh"); + } + } _changeLayoutClick() { From c0bb845ba086aeaecf0f1b581cb174f1b000d420 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 14:07:18 -0400 Subject: [PATCH 08/67] Default to dagre --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 2001912..697d848 100644 --- a/src/index.js +++ b/src/index.js @@ -68,7 +68,7 @@ sigma.classes.graph.addMethod("outNeighbors", function(id) { }); global.appStore = { - dagre: false, + dagre: true, startNode: null, endNode: null, prebuiltQuery: [], From d96651635d91a1875da923f274243d244783ecee Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 14:07:30 -0400 Subject: [PATCH 09/67] Prefill login url --- src/components/Float/Login.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Float/Login.jsx b/src/components/Float/Login.jsx index 5ba0c46..76d1f02 100644 --- a/src/components/Float/Login.jsx +++ b/src/components/Float/Login.jsx @@ -4,7 +4,7 @@ export default class Login extends Component { constructor() { super(); this.state = { - url: "", + url: "bolt://localhost:7687", icon: null, loginEnabled: false, user: "", From f093a6863cf2d6048bd6b7c4408ea9704dc2d9f2 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 15:00:37 -0400 Subject: [PATCH 10/67] Support drag and drop file ingestion --- main.js | 1 - package.json | 3 +- src/AppContainer.jsx | 13 +++ src/components/Float/Login.jsx | 4 +- src/components/Menu/MenuContainer.jsx | 80 ++++++++++++++----- .../SearchContainer/SearchContainer.jsx | 4 +- src/components/tooltip.html | 12 +-- src/js/utils.js | 13 +++ 8 files changed, 96 insertions(+), 34 deletions(-) diff --git a/main.js b/main.js index f658eea..7f10eea 100644 --- a/main.js +++ b/main.js @@ -2,7 +2,6 @@ const electron = require('electron') var platform = require('os').platform() // Module to control application life. const app = electron.app -const Tray = electron.Tray const Menu = electron.Menu // Module to create native browser window. diff --git a/package.json b/package.json index f8e1c03..45786fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bloodhound", - "version": "2.0", + "version": "2.0.0", "description": "Graph Theory for Active Directory", "keywords": [ "Graph", @@ -58,6 +58,7 @@ "eventemitter2": "^4.1.0", "jquery": "^3.2.1", "linkurious": "^1.5.1", + "magic-number": "^0.1.6", "mustache": "^2.3.0", "neo4j-driver": "^1.6.2", "prop-types": "^15.6.2", diff --git a/src/AppContainer.jsx b/src/AppContainer.jsx index b586312..d73c3c5 100644 --- a/src/AppContainer.jsx +++ b/src/AppContainer.jsx @@ -20,6 +20,19 @@ import About from "./components/Modals/About.jsx"; import { CSSTransition, TransitionGroup } from "react-transition-group"; export default class AppContainer extends Component { + componentDidMount(){ + document.addEventListener('dragover', function(event){ + event.preventDefault(); + return false; + }, false) + + document.addEventListener('drop', function(event){ + emitter.emit("filedrop", event) + event.preventDefault(); + return false; + }, false) + } + render() { return ( diff --git a/src/components/Float/Login.jsx b/src/components/Float/Login.jsx index 76d1f02..8bd635d 100644 --- a/src/components/Float/Login.jsx +++ b/src/components/Float/Login.jsx @@ -39,6 +39,8 @@ export default class Login extends Component { if (this.state.password !== "") { this.checkDBCreds(); + }else{ + this.checkDBPresence(); } } @@ -59,8 +61,6 @@ export default class Login extends Component { checkDBPresence() { var url = this.state.url; var icon = this.state.icon; - var jicon = jQuery(icon); - var btn = jQuery(this.refs.loginButton); if (url === "") { return; diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index 8fdcf30..88d389c 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -12,16 +12,17 @@ import { buildOuJson } from "utils"; import { If, Then, Else } from "react-if"; -const { dialog, app } = require("electron").remote; -var fs = require("fs"); -var async = require("async"); -var unzip = require("unzipper"); -var fpath = require("path"); +import { remote } from "electron"; +const { dialog, app } = remote; +import { unlinkSync, createReadStream, createWriteStream } from "fs"; +import { eachSeries } from "async"; +import { Parse } from "unzipper"; +import { join } from "path"; -const Pick = require("stream-json/filters/Pick"); -const { streamArray } = require("stream-json/streamers/StreamArray"); -const { chain } = require("stream-chain"); -const Asm = require("stream-json/Assembler"); +import { withParser } from "stream-json/filters/Pick"; +import { streamArray } from "stream-json/streamers/StreamArray"; +import { chain } from "stream-chain"; +import { connectTo } from "stream-json/Assembler"; export default class MenuContainer extends Component { constructor() { @@ -35,6 +36,42 @@ export default class MenuContainer extends Component { }; emitter.on("cancelUpload", this.cancelUpload.bind(this)); + emitter.on("filedrop", this.fileDrop.bind(this)) + } + + fileDrop(e){ + let fileNames = [] + $.each(e.dataTransfer.files, function(_, file){ + fileNames.push({ path: file.path, name: file.name }); + }) + + this.unzipNecessary(fileNames).then( + function(results) { + eachSeries( + results, + function(file, callback) { + emitter.emit( + "showAlert", + "Processing file {}".format(file.name) + ); + this.getFileMeta(file.path, callback); + }.bind(this), + function done() { + setTimeout( + function() { + this.setState({ uploading: false }); + }.bind(this), + 3000 + ); + $.each(results, function(_, file) { + if (file.delete) { + unlinkSync(file.path); + } + }); + }.bind(this) + ); + }.bind(this) + ); } cancelUpload() { @@ -88,13 +125,13 @@ export default class MenuContainer extends Component { var input = jQuery(this.refs.fileInput); var fileNames = []; - $.each(input[0].files, function(index, file) { + $.each(input[0].files, function(_, file) { fileNames.push({ path: file.path, name: file.name }); }); this.unzipNecessary(fileNames).then( function(results) { - async.eachSeries( + eachSeries( results, function(file, callback) { emitter.emit( @@ -112,7 +149,7 @@ export default class MenuContainer extends Component { ); $.each(results, function(index, file) { if (file.delete) { - fs.unlinkSync(file.path); + unlinkSync(file.path); } }); }.bind(this) @@ -132,12 +169,11 @@ export default class MenuContainer extends Component { var name = files[index].name; if (path.endsWith(".zip")) { - await fs - .createReadStream(path) - .pipe(unzip.Parse()) + await createReadStream(path) + .pipe(Parse()) .on("entry", function(entry) { - var output = fpath.join(tempPath, entry.path); - entry.pipe(fs.createWriteStream(output)); + var output = join(tempPath, entry.path); + entry.pipe(createWriteStream(output)); processed.push({ path: output, name: entry.path, @@ -175,11 +211,11 @@ export default class MenuContainer extends Component { console.log(file); let pipeline = chain([ - fs.createReadStream(file, { encoding: "utf8" }), - Pick.withParser({ filter: "meta" }) + createReadStream(file, { encoding: "utf8" }), + withParser({ filter: "meta" }) ]); - let asm = Asm.connectTo(pipeline); + let asm = connectTo(pipeline); asm.on( "done", function(asm) { @@ -199,8 +235,8 @@ export default class MenuContainer extends Component { processJson(file, callback, count, type) { let pipeline = chain([ - fs.createReadStream(file, { encoding: "utf8" }), - Pick.withParser({ filter: type }), + createReadStream(file, { encoding: "utf8" }), + withParser({ filter: type }), streamArray() ]); diff --git a/src/components/SearchContainer/SearchContainer.jsx b/src/components/SearchContainer/SearchContainer.jsx index 5a8b22e..904323f 100644 --- a/src/components/SearchContainer/SearchContainer.jsx +++ b/src/components/SearchContainer/SearchContainer.jsx @@ -650,7 +650,7 @@ export default class SearchContainer extends Component { } query += - " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; + " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(m)) RETURN p"; emitter.emit("query", query, { aprop: start, bprop: end }); } @@ -809,7 +809,7 @@ export default class SearchContainer extends Component { } query += - " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; + " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(m)) RETURN p"; emitter.emit("query", query, { aprop: start, bprop: end }); } diff --git a/src/components/tooltip.html b/src/components/tooltip.html index 93dd01d..f4a453e 100644 --- a/src/components/tooltip.html +++ b/src/components/tooltip.html @@ -9,7 +9,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_user}} {{#type_computer}} @@ -19,7 +19,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_computer}} {{#type_group}} @@ -29,7 +29,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_group}} {{#type_gpo}} @@ -39,7 +39,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_gpo}} {{#type_ou}} @@ -49,7 +49,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_ou}} {{#type_domain}} @@ -59,7 +59,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_domain}} {{#expand}} diff --git a/src/js/utils.js b/src/js/utils.js index 5e98cd2..e95e91f 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -593,6 +593,7 @@ export function buildComputerJson(chunk) { let localadmins = comp.LocalAdmins; let rdpers = comp.RemoteDesktopUsers; let primarygroup = comp.PrimaryGroup; + let dcom = comp.DcomUsers; if (!queries.properties) { if (primarygroup === null) { @@ -638,6 +639,18 @@ export function buildComputerJson(chunk) { let p = { name: name, target: aName }; insert(queries, hash, statement, p); }); + + $.each(dcom, function(_, dcomu) { + let aType = dcomu.Type; + let aName = dcomu.Name; + let rel = "ExecuteDCOM"; + + let hash = rel + aType; + + let statement = baseQuery.format(aType, rel); + let p = { name: name, target: aName }; + insert(queries, hash, statement, p); + }); }); return queries; } From 37cc34c7cb6e66c65c538ea63696179aae15332b Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 15:11:42 -0400 Subject: [PATCH 11/67] Add proper zip file check --- package.json | 2 +- src/components/Menu/MenuContainer.jsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 45786fb..9394fa2 100644 --- a/package.json +++ b/package.json @@ -56,9 +56,9 @@ "dagre": "^0.7.4", "electron-store": "^1.3.0", "eventemitter2": "^4.1.0", + "is-zip-file": "^1.0.2", "jquery": "^3.2.1", "linkurious": "^1.5.1", - "magic-number": "^0.1.6", "mustache": "^2.3.0", "neo4j-driver": "^1.6.2", "prop-types": "^15.6.2", diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index 88d389c..23ca92b 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -23,6 +23,7 @@ import { withParser } from "stream-json/filters/Pick"; import { streamArray } from "stream-json/streamers/StreamArray"; import { chain } from "stream-chain"; import { connectTo } from "stream-json/Assembler"; +var iszip = require('is-zip-file'); export default class MenuContainer extends Component { constructor() { @@ -168,7 +169,7 @@ export default class MenuContainer extends Component { var path = files[index].path; var name = files[index].name; - if (path.endsWith(".zip")) { + if (iszip.isZipSync(path)) { await createReadStream(path) .pipe(Parse()) .on("entry", function(entry) { From b93010ae342c99797b9c49ce27b367de2e17f18b Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 15:51:41 -0400 Subject: [PATCH 12/67] Add extra properties to OU/GPO/Domain --- src/components/Graph.jsx | 4 +-- .../SearchContainer/Tabs/DomainNodeData.jsx | 26 +++++++++++++++++-- .../SearchContainer/Tabs/GpoNodeData.jsx | 24 ++++++++++++++++- .../SearchContainer/Tabs/OuNodeData.jsx | 23 +++++++++++++++- src/js/utils.js | 5 ++-- 5 files changed, 74 insertions(+), 8 deletions(-) diff --git a/src/components/Graph.jsx b/src/components/Graph.jsx index 09250eb..25332ff 100644 --- a/src/components/Graph.jsx +++ b/src/components/Graph.jsx @@ -671,8 +671,8 @@ export default class GraphContainer extends Component { }; if (data.hasOwnProperty("properties")) { - if (data.properties.hasOwnProperty("blocksInheritance")) { - node.blocksInheritance = data.properties.blocksInheritance; + if (data.properties.hasOwnProperty("blocksinheritance")) { + node.blocksInheritance = data.properties.blocksinheritance; } if (data.properties.hasOwnProperty("guid")) { diff --git a/src/components/SearchContainer/Tabs/DomainNodeData.jsx b/src/components/SearchContainer/Tabs/DomainNodeData.jsx index daf5e49..df6c4c1 100644 --- a/src/components/SearchContainer/Tabs/DomainNodeData.jsx +++ b/src/components/SearchContainer/Tabs/DomainNodeData.jsx @@ -4,6 +4,7 @@ import PropTypes from "prop-types"; import NodeCypherLink from "./NodeCypherLink.jsx"; import NodeCypherNoNumberLink from "./NodeCypherNoNumberLink"; import NodeCypherLinkComplex from "./NodeCypherLinkComplex"; +import NodeProps from "./NodeProps"; export default class DomainNodeData extends Component { constructor() { @@ -16,7 +17,12 @@ export default class DomainNodeData extends Component { computers: -1, ous: -1, gpos: -1, - driversessions: [] + driversessions: [], + propertyMap: {}, + displayMap: { + "description":"Description", + "functionallevel":"Domain Functional Level" + } }; emitter.on("domainNodeClicked", this.getNodeData.bind(this)); @@ -26,6 +32,7 @@ export default class DomainNodeData extends Component { $.each(this.state.driversessions, function(index, record) { record.close(); }); + this.setState({ label: payload, users: -1, @@ -35,6 +42,17 @@ export default class DomainNodeData extends Component { gpos: -1 }); + let props = driver.session(); + props + .run("MATCH (n:Domain {name:{name}}) RETURN n", { name: payload }) + .then( + function(result) { + var properties = result.records[0]._fields[0].properties; + this.setState({ propertyMap: properties }); + props.close(); + }.bind(this) + ); + let s1 = driver.session(); let s2 = driver.session(); let s3 = driver.session(); @@ -95,7 +113,11 @@ export default class DomainNodeData extends Component {
    Domain
    {this.state.label}
    -
    +
    Users
    {this.state.label}
    GUID
    {this.state.guid}
    +

    Affected Objects

    {this.state.guid}
    Blocks Inheritance
    {this.state.blocks}
    + Date: Mon, 16 Jul 2018 21:30:35 -0400 Subject: [PATCH 13/67] Add edge filtering window --- src/components/Graph.jsx | 13 ++ .../SearchContainer/SearchContainer.jsx | 209 +++++++++++++++++- src/components/tooltip.html | 12 +- src/css/styles.css | 34 +++ src/index.js | 32 ++- 5 files changed, 282 insertions(+), 18 deletions(-) diff --git a/src/components/Graph.jsx b/src/components/Graph.jsx index 25332ff..1b9c9a8 100644 --- a/src/components/Graph.jsx +++ b/src/components/Graph.jsx @@ -791,6 +791,19 @@ export default class GraphContainer extends Component { if (typeof props === "undefined") { props = {}; } + + let edges = [] + let stat = appStore.edgeincluded; + + $.each(Object.keys(stat), function(_, key){ + if (stat[key]){ + edges.push(key); + } + }) + + let finaledges = edges.join('|'); + statement = statement.format(finaledges) + this.doQueryNative({ statement: statement, allowCollapse: allowCollapse, diff --git a/src/components/SearchContainer/SearchContainer.jsx b/src/components/SearchContainer/SearchContainer.jsx index 904323f..3403255 100644 --- a/src/components/SearchContainer/SearchContainer.jsx +++ b/src/components/SearchContainer/SearchContainer.jsx @@ -12,13 +12,47 @@ export default class SearchContainer extends Component { mainPlaceholder: "Start typing to search for a node...", pathfindingIsOpen: false, mainValue: "", - pathfindValue: "" + pathfindValue: "", + edgeincluded: { + MemberOf: true, + HasSession: true, + AdminTo: true, + AllExtendedRights: true, + AddMember: true, + ForceChangePassword: true, + GenericAll: true, + GenericWrite: true, + Owns: true, + WriteDacl: true, + WriteOwner: true, + CanRDP: true, + ExecuteDCOM: true + } }; } + handleChange(event){ + let current = this.state.edgeincluded; + let eName = event.target.getAttribute("name"); + current[eName] = !current[eName]; + this.setState({edgeincluded: current}); + + appStore.edgeincluded = current; + conf.set("edgeincluded", current); + } + componentDidMount() { jQuery(this.refs.pathfinding).slideToggle(0); jQuery(this.refs.tabs).slideToggle(0); + jQuery(this.refs.edgeFilter).animate( + { + height: "toggle", + width: "toggle" + }, + "fast" + ); + + this.setState({edgeincluded: appStore.edgeincluded}); emitter.on("userNodeClicked", this.openNodeTab.bind(this)); emitter.on("groupNodeClicked", this.openNodeTab.bind(this)); emitter.on("computerNodeClicked", this.openNodeTab.bind(this)); @@ -228,7 +262,7 @@ export default class SearchContainer extends Component { } query += - " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; + " WITH m,n MATCH p=allShortestPaths((n)-[r:{}}*1..]->(m)) RETURN p"; emitter.emit("query", query, { aprop: start, bprop: end }); } @@ -484,7 +518,7 @@ export default class SearchContainer extends Component { } query += - " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p"; + " WITH m,n MATCH p=allShortestPaths((n)-[r:{}}*1..]->(m)) RETURN p"; emitter.emit("query", query, { aprop: start, bprop: end }); }.bind(this), @@ -574,6 +608,16 @@ export default class SearchContainer extends Component { }); } + _onFilterClick() { + jQuery(this.refs.edgeFilter).animate( + { + height: "toggle", + width: "toggle" + }, + "medium" + ); + } + _onPathfindClick() { jQuery(this.refs.pathfinding).slideToggle(); var p = !this.state.pathfindingIsOpen; @@ -650,7 +694,7 @@ export default class SearchContainer extends Component { } query += - " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(m)) RETURN p"; + " WITH m,n MATCH p=allShortestPaths((n)-[r:{}*1..]->(m)) RETURN p"; emitter.emit("query", query, { aprop: start, bprop: end }); } @@ -809,7 +853,7 @@ export default class SearchContainer extends Component { } query += - " WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(m)) RETURN p"; + " WITH m,n MATCH p=allShortestPaths((n)-[r:{}*1..]->(m)) RETURN p"; emitter.emit("query", query, { aprop: start, bprop: end }); } @@ -818,7 +862,151 @@ export default class SearchContainer extends Component { render() { return ( -
    +
    +
    +
    +

    Edge Filtering

    + +
    +

    Default Edges

    +
    + + +
    +
    + + +
    +
    + + +
    +

    ACL Edges

    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +

    Unprivileged Execution

    +
    + + +
    +
    + + +
    +
    + + +
    diff --git a/src/components/tooltip.html b/src/components/tooltip.html index f4a453e..eab3971 100644 --- a/src/components/tooltip.html +++ b/src/components/tooltip.html @@ -9,7 +9,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_user}} {{#type_computer}} @@ -19,7 +19,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_computer}} {{#type_group}} @@ -29,7 +29,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_group}} {{#type_gpo}} @@ -39,7 +39,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_gpo}} {{#type_ou}} @@ -49,7 +49,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_ou}} {{#type_domain}} @@ -59,7 +59,7 @@
  • Set as Ending Node
  • -
  • +
  • Shortest Paths to Here
  • {{/type_domain}} {{#expand}} diff --git a/src/css/styles.css b/src/css/styles.css index 1b8c098..ffe034a 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -135,6 +135,40 @@ div.tooltip-inner-custom { display: none; } +.edgeFilter{ + position: absolute; + left: 100%; + display: inline-block; + width: auto; + background-color: white; + box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.2); + margin-left: 2px; +} + +.edgeFilter > h4{ + display:inline-block; + white-space: nowrap; + margin-left: 8px; + margin-right: 8px; +} + +.edgeFilter > div > h3{ + display:inline; + white-space: nowrap; + margin-right: 8px; +} + +.edgeFilter > div{ + margin-left: 8px; + margin-right: 8px; + white-space: nowrap; +} + +.edgeFilter > div > input{ + margin-top: 0; + white-space: nowrap; +} + @media all and (min-width: 600px) { .searchdiv { position: absolute; diff --git a/src/index.js b/src/index.js index 697d848..b18a4ca 100644 --- a/src/index.js +++ b/src/index.js @@ -212,13 +212,25 @@ if (typeof conf.get("performance") === "undefined") { }); } -var custompath = join(app.getPath("userData"), "customqueries.json"); +if (typeof conf.get("edgeincluded") === "undefined"){ + conf.set("edgeincluded", { + MemberOf : true, + HasSession: true, + AdminTo: true, + AllExtendedRights: true, + AddMember: true, + ForceChangePassword: true, + GenericAll:true, + GenericWrite:true, + Owns: true, + WriteDacl: true, + WriteOwner: true, + CanRDP:true, + ExecuteDCOM:true + }) +} -stat(custompath, function(err, stats) { - if (err) { - writeFile(custompath, "[]"); - } -}); +appStore.edgeincluded = conf.get("edgeincluded"); appStore.performance = conf.get("performance"); @@ -227,6 +239,14 @@ if (typeof appStore.performance.edgeLabels === "undefined") { conf.set("performance", appStore.performance); } +var custompath = join(app.getPath("userData"), "customqueries.json"); + +stat(custompath, function(err, stats) { + if (err) { + writeFile(custompath, "{}"); + } +}); + renderEmit.on("login", function() { emitter.removeAllListeners(); ReactDOM.unmountComponentAtNode(document.getElementById("root")); From 3805d093b72036e13e46a53b36380616824a258b Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 21:55:36 -0400 Subject: [PATCH 14/67] Move domain admins -> sid --- src/components/Graph.jsx | 2 +- .../SearchContainer/Tabs/PrebuiltQueries.json | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/Graph.jsx b/src/components/Graph.jsx index 1b9c9a8..4f0a6bf 100644 --- a/src/components/Graph.jsx +++ b/src/components/Graph.jsx @@ -147,7 +147,7 @@ export default class GraphContainer extends Component { this.doQueryNative({ statement: - 'MATCH (n:Group) WHERE n.name =~ "(?i).*DOMAIN ADMINS.*" WITH n MATCH (n)<-[r:MemberOf*1..]-(m) RETURN n,r,m', + 'MATCH (n:Group) WHERE n.objectsid =~ "(?i)S-1-5.*-512" WITH n MATCH (n)<-[r:MemberOf*1..]-(m) RETURN n,r,m', //statement: 'MATCH (n)-[r]->(m) RETURN n,r,m', //statement: 'MATCH p=(n:Domain)-[r]-(m:Domain) RETURN p', allowCollapse: false, diff --git a/src/components/SearchContainer/Tabs/PrebuiltQueries.json b/src/components/SearchContainer/Tabs/PrebuiltQueries.json index 6fdc133..f762474 100644 --- a/src/components/SearchContainer/Tabs/PrebuiltQueries.json +++ b/src/components/SearchContainer/Tabs/PrebuiltQueries.json @@ -5,11 +5,10 @@ "queryList": [ { "final": true, - "title": "Select a domain...", "query": - "MATCH (n:Group) WHERE n.name =~ {name} WITH n MATCH p=(n)<-[r:MemberOf*1..]-(m) RETURN p", + "MATCH (n:Group) WHERE n.objectsid =~ {name} WITH n MATCH p=(n)<-[r:MemberOf*1..]-(m) RETURN p", "props": { - "name": "(?i).*DOMAIN ADMINS.*" + "name": "(?i)S-1-5-.*-512" }, "allowCollapse": false } @@ -22,15 +21,15 @@ "final": false, "title": "Select a Domain Admin group...", "query": - "MATCH (n:Group) WHERE n.name =~ {name} RETURN n.name ORDER BY n.name DESC", + "MATCH (n:Group) WHERE n.objectsid =~ {name} RETURN n.name ORDER BY n.name DESC", "props": { - "name": "(?i).*DOMAIN ADMINS.*" + "name": "(?i)S-1-5-.*-512" } }, { "final": true, "query": - "MATCH (n:User),(m:Group {name:{result}}),p=shortestPath((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p", + "MATCH (n:User),(m:Group {name:{result}}),p=shortestPath((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(m)) RETURN p", "allowCollapse": true, "endNode": "{}" } From 407a0d639e6eabf755393ac595b324408daabe65 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Mon, 16 Jul 2018 22:03:20 -0400 Subject: [PATCH 15/67] Add BloodHound Slack to About page --- src/components/Modals/About.jsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/Modals/About.jsx b/src/components/Modals/About.jsx index bec8a3a..535ca1b 100644 --- a/src/components/Modals/About.jsx +++ b/src/components/Modals/About.jsx @@ -58,7 +58,7 @@ export default class componentName extends Component { Version: {this.state.version}
    - Github:{" "} + GitHub:{" "}
    +
    + BloodHound Slack:{" "} + + https://bloodhoundgang.herokuapp.com/ + +
    Authors:{" "} Date: Mon, 16 Jul 2018 22:14:32 -0400 Subject: [PATCH 16/67] Increase default edge size --- src/index.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/index.js b/src/index.js index b18a4ca..c036356 100644 --- a/src/index.js +++ b/src/index.js @@ -174,6 +174,12 @@ global.appStore = { type: { by: "type", scheme: "edgeScheme" + }, + size: { + by: "degree", + bins: 1, + min: 4, + max: 4 } } }, @@ -197,6 +203,12 @@ global.appStore = { type: { by: "type", scheme: "edgeScheme" + }, + size: { + by: "degree", + bins: 1, + min: 4, + max: 4 } } } From 10373487d9126a337e965620e94f42dfaf331896 Mon Sep 17 00:00:00 2001 From: Rohan Vazarkar Date: Tue, 17 Jul 2018 17:33:30 -0400 Subject: [PATCH 17/67] Add owned prop, node editor --- src/AppContainer.jsx | 2 + src/components/Float/NodeEditor.jsx | 149 +++++++++++++ src/components/Float/NodeEditorRow.jsx | 204 ++++++++++++++++++ src/components/Menu/MenuContainer.jsx | 18 +- .../SearchContainer/Tabs/ComputerNodeData.jsx | 3 +- .../SearchContainer/Tabs/GroupNodeData.jsx | 22 +- .../SearchContainer/Tabs/UserNodeData.jsx | 3 +- src/components/tooltip.html | 6 + src/css/styles.css | 61 ++++++ 9 files changed, 464 insertions(+), 4 deletions(-) create mode 100644 src/components/Float/NodeEditor.jsx create mode 100644 src/components/Float/NodeEditorRow.jsx diff --git a/src/AppContainer.jsx b/src/AppContainer.jsx index d73c3c5..c8a4433 100644 --- a/src/AppContainer.jsx +++ b/src/AppContainer.jsx @@ -17,6 +17,7 @@ import ZoomContainer from "./components/Zoom/ZoomContainer"; import QueryNodeSelect from "./components/Float/QueryNodeSelect"; import SessionClearModal from "./components/Modals/SessionClearModal"; import About from "./components/Modals/About.jsx"; +import NodeEditor from "./components/Float/NodeEditor"; import { CSSTransition, TransitionGroup } from "react-transition-group"; export default class AppContainer extends Component { @@ -56,6 +57,7 @@ export default class AppContainer extends Component { +
    diff --git a/src/components/Float/NodeEditor.jsx b/src/components/Float/NodeEditor.jsx new file mode 100644 index 0000000..b2cd96e --- /dev/null +++ b/src/components/Float/NodeEditor.jsx @@ -0,0 +1,149 @@ +import React, { Component } from "react"; +import NodeEditorRow from "./NodeEditorRow.jsx"; + +export default class NodeEditor extends Component { + constructor() { + super(); + + this.state = { + label: "DOMAIN ADMINS@TESTLAB.LOCAL", + type: "group", + properties: {} + }; + } + + componentDidMount() { + emitter.on("editnode", this.getNodeData.bind(this)); + $(this.refs.outer).draggable({ + stop: function(event, ui) { + let target = jQuery(event.target); + target.css("width", "auto"); + target.css("height", "auto"); + }, + handle: "#nodeEditOuter" + }); + } + + getNodeData(name, type) { + $(this.refs.outer).fadeIn(); + let q = driver.session(); + this.setState({ label: name, type: type }); + let statement = "MATCH (n:{} {{}:{name}}) RETURN n"; + let key; + if (type === "ou") { + key = "guid"; + } else { + key = "name"; + } + + q.run(statement.format(type.toTitleCase(), key), { name: name }).then( + function(result) { + let props = result.records[0]._fields[0].properties; + let label = props.name; + delete props.name; + this.setState({ properties: props, label: label }); + }.bind(this) + ); + } + + closeEditor() { + $(this.refs.outer).fadeToggle(false); + } + + updateHandler(attrName, newVal) { + console.log(attrName, newVal) + } + + deletePropHandler(attrName) { + let key; + if (this.state.type === "ou") { + key = "guid"; + } else { + key = "name"; + } + let statement = "MATCH (n:{} {{}:{name}}) REMOVE n.{} RETURN n".format( + this.state.type.toTitleCase(), + key, + attrName + ); + let q = driver.session(); + q.run(statement, { name: this.state.label }).then( + function(result) { + let props = result.records[0]._fields[0].properties; + let label = props.name; + delete props.name; + this.setState({ properties: props, label: label }); + }.bind(this) + ); + } + + render() { + return ( +
    +
    + {this.state.label} + +
    + +
    +
    + + + + + + + + + + + {Object.keys(this.state.properties).map( + function(key) { + let val = this.state.properties[key]; + return ( + + ); + }.bind(this) + )} + +
    DeleteEditNameValue
    +
    + + + + + +
    +
    + ); + } +} diff --git a/src/components/Float/NodeEditorRow.jsx b/src/components/Float/NodeEditorRow.jsx new file mode 100644 index 0000000..869812f --- /dev/null +++ b/src/components/Float/NodeEditorRow.jsx @@ -0,0 +1,204 @@ +import React, { Component } from "react"; + +export default class NodeEditorRow extends Component { + constructor() { + super(); + } + + saveDelete() { + this.setState({ deleting: false }); + this.props.deleteHandler(this.props.attributeName); + } + + cancelDelete() { + this.setState({ deleting: false }); + } + + enableDelete() { + this.setState({ deleting: true }); + } + + changeVal() { + let val = this.state.val; + val = !val; + this.setState({ val: val }); + } + + cancelEdit() { + let input = jQuery(this.refs.input); + let val = this.props.val; + + if (input.is("div")) { + input.html(val); + input.removeAttr("contenteditable"); + } else if (input.is("textarea")){ + let tempval = val.join("\n"); + input.attr("disabled", ""); + input.val(tempval); + }else { + input.attr("disabled", ""); + } + this.setState({ editing: false, val: val }); + } + + saveEdit() { + console.log(this.state); + let input = jQuery(this.refs.input); + let val; + if (input.is("div")) { + val = input.html(); + } else if (input.is("textarea")){ + val = input.val(); + } else { + val = this.state.val; + } + + console.log(val); + } + + enableEdit() { + let input = jQuery(this.refs.input); + + if (input.is("div")) { + input.attr("contenteditable", true); + } else { + input.removeAttr("disabled"); + } + + this.setState({ editing: true }); + } + + componentDidMount() { + let type = typeof this.props.val; + if (type == "object") { + type = "array"; + } + this.setState({ + editing: false, + val: this.props.val, + deleting: false, + valtype: type + }); + } + + componentWillReceiveProps(nextProps) { + if ( + nextProps.val != this.props.val && + nextProps.val != this.state.val + ) { + let type = typeof nextProps.val + if (type == "object") { + type = "array"; + } + this.setState({ + val: nextProps.val, + editing: false, + deleting: false, + valtype: type + }); + } + } + + render() { + let type = this.state ? this.state.valtype : typeof this.props.val; + let valcolumn; + + if (type === "boolean") { + valcolumn = ( + + ); + } else if (type === "string") { + valcolumn = ( +
    + {!this.state ? this.props.val : this.state.val} +
    + ); + } else if (type === "number") { + valcolumn = ( +
    + {!this.state ? this.props.val : this.state.val} +
    + ); + } else if (type === "object" || type === "array") { + valcolumn = ( +