Add Pictures to nodes

Fix error on non-image/bad file
New loading indicator
master
Rohan Vazarkar 2018-08-04 17:35:51 -04:00
parent 8c73698fd4
commit ec88ff2948
17 changed files with 1077 additions and 38 deletions

View File

@ -52,22 +52,28 @@
"dependencies": {
"@fortawesome/fontawesome-free": "^5.1.1",
"async": "^2.6.0",
"base64-img": "^1.0.4",
"bootstrap": "^3.3.7",
"bootstrap-3-typeahead": "^4.0.2",
"dagre": "^0.7.4",
"electron-store": "^1.3.0",
"eventemitter2": "^4.1.0",
"fontfaceobserver": "^2.0.13",
"image-size": "^0.6.3",
"image-type": "^3.0.0",
"is-zip-file": "^1.0.2",
"jquery": "^3.2.1",
"linkurious": "^1.5.1",
"md5-file": "^4.0.0",
"mustache": "^2.3.0",
"neo4j-driver": "^1.6.2",
"prop-types": "^15.6.2",
"react": "^16.2.0",
"react": "^16.4.2",
"react-bootstrap": "^0.32.0",
"react-dom": "^16.2.0",
"react-dom": "^16.4.2",
"react-if": "^2.1.0",
"react-images": "^0.5.19",
"react-photo-gallery": "^6.1.2",
"react-transition-group": "^2.2.1",
"stream-json": "^1.1.0",
"unzipper": "^0.8.9",

View File

@ -31,10 +31,15 @@ export default class AppContainer extends Component {
event.preventDefault();
return false;
}, false)
document.addEventListener('drop', function(event){
emitter.emit("filedrop", event)
event.preventDefault();
if (jQuery("#tabcontainer").has(jQuery(event.target)).length === 1){
emitter.emit("imageupload", event)
}else{
emitter.emit("filedrop", event)
}
return false;
}, false)
}

View File

@ -35,7 +35,7 @@ export default class LoadingContainer extends Component {
return (
<div className="loadingIndicator" ref="load">
<div>{this.state.text}</div>
<img src="src/img/loading.gif" />
<img src="src/img/loading_new.gif" />
</div>
);
}

View File

@ -65,7 +65,7 @@ export default class MenuContainer extends Component {
},
3000
);
this.addOwnedProp();
this.addBaseProps();
$.each(results, function(_, file) {
if (file.delete) {
unlinkSync(file.path);
@ -150,7 +150,7 @@ export default class MenuContainer extends Component {
},
3000
);
this.addOwnedProp();
this.addBaseProps();
$.each(results, (_, file) => {
if (file.delete) {
unlinkSync(file.path);
@ -164,10 +164,11 @@ export default class MenuContainer extends Component {
);
}
async addOwnedProp(){
async addBaseProps(){
let s = driver.session();
await s.run("MATCH (n:User) WHERE NOT EXISTS(n.owned) SET n.owned=false");
await s.run("MATCH (n:Computer) WHERE NOT EXISTS(n.owned) SET n.owned=false");
await s.run("MATCH (n) WHERE NOT EXISTS(n.pics) SET n.pics=[]");
s.close();
}
@ -229,11 +230,17 @@ export default class MenuContainer extends Component {
let size = statSync(file).size;
createReadStream(file, {encoding: 'utf8', start: size-100, end: size}).on('data', chunk => {
type = /type.?:\s?"(\w*)"/g.exec(chunk)[1];
count = /count.?:\s?(\d*)/g.exec(chunk)[1];
let type;
try{
type = /type.?:\s?"(\w*)"/g.exec(chunk)[1];
count = /count.?:\s?(\d*)/g.exec(chunk)[1];
}catch(e){
type = null;
}
if (!acceptableTypes.includes(type)){
emitter.emit("showAlert", "Unrecognized JSON Type");
emitter.emit("showAlert", "Unrecognized File");
this.setState({
uploading: false
});

View File

@ -812,7 +812,7 @@ export default class SearchContainer extends Component {
</div>
</div>
<div ref="tabs">
<div id="tabcontainer" ref="tabs">
<TabContainer />
</div>
</div>

View File

@ -9,6 +9,8 @@ import DomainNodeData from "./Tabs/DomainNodeData";
import GpoNodeData from "./Tabs/GpoNodeData";
import OuNodeData from "./Tabs/OuNodeData";
import { Tabs, Tab } from "react-bootstrap";
import { openSync, readSync, closeSync } from "fs";
import imageType from "image-type";
export default class TabContainer extends Component {
constructor(props) {
@ -32,6 +34,24 @@ export default class TabContainer extends Component {
emitter.on("domainNodeClicked", this._domainNodeClicked.bind(this));
emitter.on("gpoNodeClicked", this._gpoNodeClicked.bind(this));
emitter.on("ouNodeClicked", this._ouNodeClicked.bind(this));
emitter.on("imageupload", this.uploadImage.bind(this));
}
uploadImage(event){
let files = [];
$.each(event.dataTransfer.files, (_, f) => {
let buf = Buffer.alloc(12);
let file = openSync(f.path, 'r')
readSync(file,buf, 0, 12, 0);
closeSync(file)
let type = imageType(buf);
if (type !== null && type.mime.includes("image")){
files.push({path: f.path, name: f.name})
}else{
emitter.emit("showAlert", `${f.name} is not an image`);
}
})
emitter.emit("imageUploadFinal", files);
}
_userNodeClicked() {
@ -136,9 +156,7 @@ export default class TabContainer extends Component {
/>
<UserNodeData visible={this.state.userVisible} />
<GroupNodeData visible={this.state.groupVisible} />
<ComputerNodeData
visible={this.state.computerVisible}
/>
<ComputerNodeData visible={this.state.computerVisible} />
<DomainNodeData visible={this.state.domainVisible} />
<GpoNodeData visible={this.state.gpoVisible} />
<OuNodeData visible={this.state.ouVisible} />

View File

@ -4,6 +4,16 @@ import NodeProps from "./NodeProps";
import NodeCypherLink from "./NodeCypherLink";
import NodeCypherNoNumberLink from "./NodeCypherNoNumberLink";
import NodeCypherLinkComplex from "./NodeCypherLinkComplex";
import Gallery from "react-photo-gallery";
import SelectedImage from "./SelectedImage";
import Lightbox from "react-images";
import { readFileSync, writeFileSync } from "fs";
import { base64Sync } from "base64-img";
import sizeOf from "image-size";
import md5File from "md5-file";
import { remote } from "electron";
const { app } = remote;
import { join } from "path";
export default class ComputerNodeData extends Component {
constructor() {
@ -20,14 +30,26 @@ export default class ComputerNodeData extends Component {
unconstraineddelegation: "Allows Unconstrained Delegation",
owned: "Compromised"
},
notes: null
notes: null,
pics: [],
currentImage: 0,
lightboxIsOpen: false
};
emitter.on("computerNodeClicked", this.getNodeData.bind(this));
emitter.on("imageUploadFinal", this.uploadImage.bind(this));
emitter.on("clickPhoto", this.openLightbox.bind(this));
emitter.on("deletePhoto", this.handleDelete.bind(this));
}
componentDidMount() {
jQuery(this.refs.complete).hide();
jQuery(this.refs.piccomplete).hide();
}
getNodeData(payload) {
jQuery(this.refs.complete).hide();
jQuery(this.refs.piccomplete).hide();
$.each(this.state.driversessions, function(_, record) {
record.close();
});
@ -37,6 +59,15 @@ export default class ComputerNodeData extends Component {
driversessions: []
});
let key = `computer_${this.state.label}`;
let c = imageconf.get(key);
let pics = [];
if (typeof c !== "undefined"){
this.setState({pics: c})
}else{
this.setState({pics: pics})
}
var propCollection = driver.session();
propCollection
.run("MATCH (c:Computer {name:{name}}) RETURN c", { name: payload })
@ -75,6 +106,96 @@ export default class ComputerNodeData extends Component {
this.setState({ driversessions: [propCollection] });
}
uploadImage(files) {
if (!this.props.visible || files.length === 0) {
return;
}
let p = this.state.pics;
let oLen = p.length;
let key = `computer_${this.state.label}`;
$.each(files, (_, f) => {
let exists = false;
let hash = md5File.sync(f.path);
$.each(p, (_, p1) => {
if (p1.hash === hash){
exists = true;
}
})
if (exists){
emitter.emit("showAlert", "Image already exists");
return;
}
let path = join(app.getPath("userData"), "images", hash);
let dimensions = sizeOf(f.path);
let data = readFileSync(f.path);
writeFileSync(path, data);
p.push({hash: hash, src: path, width: dimensions.width, height: dimensions.height})
});
if (p.length === oLen){
return;
}
this.setState({pics: p});
imageconf.set(key, p)
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
handleDelete(event) {
if (!this.props.visible) {
return;
}
let pics = this.state.pics;
let temp = pics[event.index];
pics.splice(event.index, 1);
this.setState({
pics: pics
})
let key = `computer_${this.state.label}`;
imageconf.set(key, pics);
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
openLightbox(event) {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: event.index,
lightboxIsOpen: true
});
}
closeLightbox() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: 0,
lightboxIsOpen: false
});
}
gotoPrevious() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage - 1
});
}
gotoNext() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage + 1
});
}
notesChanged(event){
this.setState({notes: event.target.value})
}
@ -97,6 +218,28 @@ export default class ComputerNodeData extends Component {
}
render() {
let gallery;
if (this.state.pics.length === 0){
gallery = (<span>Drop pictures on here to upload!</span>)
}else{
gallery = (
<React.Fragment>
<Gallery
photos={this.state.pics}
ImageComponent={SelectedImage}
className={"gallerymod"}
/>
<Lightbox
images={this.state.pics}
isOpen={this.state.lightboxIsOpen}
onClose={this.closeLightbox.bind(this)}
onClickPrev={this.gotoPrevious.bind(this)}
onClickNext={this.gotoNext.bind(this)}
currentImage={this.state.currentImage}
/>
</React.Fragment>)
}
return (
<div className={this.props.visible ? "" : "displaynone"}>
<dl className="dl-horizontal">
@ -278,6 +421,14 @@ export default class ComputerNodeData extends Component {
/>
</div>
<textarea onBlur={this.notesBlur.bind(this)} onChange={this.notesChanged.bind(this)} value={this.state.notes === null ? "" : this.state.notes} className={"node-notes-textarea"} ref="notes" />
<div>
<h4 className={"inline"}>Pictures</h4>
<i
ref="piccomplete"
className="fa fa-check-circle green-icon-color notes-check-style"
/>
</div>
{gallery}
</div>
);
}

View File

@ -5,6 +5,16 @@ import NodeCypherLink from "./NodeCypherLink.jsx";
import NodeCypherNoNumberLink from "./NodeCypherNoNumberLink";
import NodeCypherLinkComplex from "./NodeCypherLinkComplex";
import NodeProps from "./NodeProps";
import Gallery from "react-photo-gallery";
import SelectedImage from "./SelectedImage";
import Lightbox from "react-images";
import { readFileSync, writeFileSync } from "fs";
import { base64Sync } from "base64-img";
import sizeOf from "image-size";
import md5File from "md5-file";
import { remote } from "electron";
const { app } = remote;
import { join } from "path";
export default class DomainNodeData extends Component {
constructor() {
@ -23,10 +33,21 @@ export default class DomainNodeData extends Component {
"description":"Description",
"functionallevel":"Domain Functional Level"
},
notes: null
notes: null,
pics: [],
currentImage: 0,
lightboxIsOpen: false
};
emitter.on("domainNodeClicked", this.getNodeData.bind(this));
emitter.on("imageUploadFinal", this.uploadImage.bind(this));
emitter.on("clickPhoto", this.openLightbox.bind(this));
emitter.on("deletePhoto", this.handleDelete.bind(this));
}
componentDidMount() {
jQuery(this.refs.complete).hide();
jQuery(this.refs.piccomplete).hide();
}
getNodeData(payload) {
@ -44,6 +65,15 @@ export default class DomainNodeData extends Component {
gpos: -1
});
let key = `domain_${this.state.label}`;
let c = imageconf.get(key);
let pics = [];
if (typeof c !== "undefined"){
this.setState({pics: c})
}else{
this.setState({pics: pics})
}
let props = driver.session();
props
.run("MATCH (n:Domain {name:{name}}) RETURN n", { name: payload })
@ -136,7 +166,119 @@ export default class DomainNodeData extends Component {
check.fadeOut(2000);
}
uploadImage(files) {
if (!this.props.visible || files.length === 0) {
return;
}
let p = this.state.pics;
let oLen = p.length;
let key = `domain_${this.state.label}`;
$.each(files, (_, f) => {
let exists = false;
let hash = md5File.sync(f.path);
$.each(p, (_, p1) => {
if (p1.hash === hash){
exists = true;
}
})
if (exists){
emitter.emit("showAlert", "Image already exists");
return;
}
let path = join(app.getPath("userData"), "images", hash);
let dimensions = sizeOf(f.path);
let data = readFileSync(f.path);
writeFileSync(path, data);
p.push({hash: hash, src: path, width: dimensions.width, height: dimensions.height})
});
if (p.length === oLen){
return;
}
this.setState({pics: p});
imageconf.set(key, p)
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
handleDelete(event) {
if (!this.props.visible) {
return;
}
let pics = this.state.pics;
let temp = pics[event.index];
pics.splice(event.index, 1);
this.setState({
pics: pics
})
let key = `domain_${this.state.label}`;
imageconf.set(key, pics);
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
openLightbox(event) {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: event.index,
lightboxIsOpen: true
});
}
closeLightbox() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: 0,
lightboxIsOpen: false
});
}
gotoPrevious() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage - 1
});
}
gotoNext() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage + 1
});
}
render() {
let gallery;
if (this.state.pics.length === 0){
gallery = (<span>Drop pictures on here to upload!</span>)
}else{
gallery = (
<React.Fragment>
<Gallery
photos={this.state.pics}
ImageComponent={SelectedImage}
className={"gallerymod"}
/>
<Lightbox
images={this.state.pics}
isOpen={this.state.lightboxIsOpen}
onClose={this.closeLightbox.bind(this)}
onClickPrev={this.gotoPrevious.bind(this)}
onClickNext={this.gotoNext.bind(this)}
currentImage={this.state.currentImage}
/>
</React.Fragment>)
}
return (
<div className={this.props.visible ? "" : "displaynone"}>
<dl className="dl-horizontal">
@ -304,6 +446,14 @@ export default class DomainNodeData extends Component {
/>
</div>
<textarea onBlur={this.notesBlur.bind(this)} onChange={this.notesChanged.bind(this)} value={this.state.notes === null ? "" : this.state.notes} className={"node-notes-textarea"} ref="notes" />
<div>
<h4 className={"inline"}>Pictures</h4>
<i
ref="piccomplete"
className="fa fa-check-circle green-icon-color notes-check-style"
/>
</div>
{gallery}
</div>
);
}

View File

@ -3,6 +3,16 @@ import PropTypes from "prop-types";
import NodeCypherLink from "./NodeCypherLink.jsx";
import NodeCypherLinkComplex from "./NodeCypherLinkComplex.jsx";
import NodeProps from "./NodeProps";
import Gallery from "react-photo-gallery";
import SelectedImage from "./SelectedImage";
import Lightbox from "react-images";
import { readFileSync, writeFileSync } from "fs";
import { base64Sync } from "base64-img";
import sizeOf from "image-size";
import md5File from "md5-file";
import { remote } from "electron";
const { app } = remote;
import { join } from "path";
export default class GpoNodeData extends Component {
constructor() {
@ -15,10 +25,22 @@ export default class GpoNodeData extends Component {
displayMap: {
"description":"Description",
"gpcpath":"GPO File Path"
}
},
notes:null,
pics: [],
currentImage: 0,
lightboxIsOpen: false
};
emitter.on("gpoNodeClicked", this.getNodeData.bind(this));
emitter.on("imageUploadFinal", this.uploadImage.bind(this));
emitter.on("clickPhoto", this.openLightbox.bind(this));
emitter.on("deletePhoto", this.handleDelete.bind(this));
}
componentDidMount() {
jQuery(this.refs.complete).hide();
jQuery(this.refs.piccomplete).hide();
}
getNodeData(payload, guid) {
@ -27,6 +49,15 @@ export default class GpoNodeData extends Component {
label: payload,
guid: guid
});
let key = `gpo_${this.state.label}`;
let c = imageconf.get(key);
let pics = [];
if (typeof c !== "undefined"){
this.setState({pics: c})
}else{
this.setState({pics: pics})
}
let props = driver.session();
props
@ -46,6 +77,96 @@ export default class GpoNodeData extends Component {
);
}
uploadImage(files) {
if (!this.props.visible || files.length === 0) {
return;
}
let p = this.state.pics;
let oLen = p.length;
let key = `gpo_${this.state.label}`;
$.each(files, (_, f) => {
let exists = false;
let hash = md5File.sync(f.path);
$.each(p, (_, p1) => {
if (p1.hash === hash){
exists = true;
}
})
if (exists){
emitter.emit("showAlert", "Image already exists");
return;
}
let path = join(app.getPath("userData"), "images", hash);
let dimensions = sizeOf(f.path);
let data = readFileSync(f.path);
writeFileSync(path, data);
p.push({hash: hash, src: path, width: dimensions.width, height: dimensions.height})
});
if (p.length === oLen){
return;
}
this.setState({pics: p});
imageconf.set(key, p)
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
handleDelete(event) {
if (!this.props.visible) {
return;
}
let pics = this.state.pics;
let temp = pics[event.index];
pics.splice(event.index, 1);
this.setState({
pics: pics
})
let key = `gpo_${this.state.label}`;
imageconf.set(key, pics);
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
openLightbox(event) {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: event.index,
lightboxIsOpen: true
});
}
closeLightbox() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: 0,
lightboxIsOpen: false
});
}
gotoPrevious() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage - 1
});
}
gotoNext() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage + 1
});
}
notesChanged(event){
this.setState({notes: event.target.value})
}
@ -68,6 +189,28 @@ export default class GpoNodeData extends Component {
}
render() {
let gallery;
if (this.state.pics.length === 0){
gallery = (<span>Drop pictures on here to upload!</span>)
}else{
gallery = (
<React.Fragment>
<Gallery
photos={this.state.pics}
ImageComponent={SelectedImage}
className={"gallerymod"}
/>
<Lightbox
images={this.state.pics}
isOpen={this.state.lightboxIsOpen}
onClose={this.closeLightbox.bind(this)}
onClickPrev={this.gotoPrevious.bind(this)}
onClickNext={this.gotoNext.bind(this)}
currentImage={this.state.currentImage}
/>
</React.Fragment>)
}
return (
<div className={this.props.visible ? "" : "displaynone"}>
<dl className="dl-horizontal">
@ -162,6 +305,14 @@ export default class GpoNodeData extends Component {
/>
</div>
<textarea onBlur={this.notesBlur.bind(this)} onChange={this.notesChanged.bind(this)} value={this.state.notes === null ? "" : this.state.notes} className={"node-notes-textarea"} ref="notes" />
<div>
<h4 className={"inline"}>Pictures</h4>
<i
ref="piccomplete"
className="fa fa-check-circle green-icon-color notes-check-style"
/>
</div>
{gallery}
</div>
);
}

View File

@ -2,6 +2,16 @@ import React, { Component } from "react";
import PropTypes from "prop-types";
import NodeCypherLink from "./NodeCypherLink";
import NodeProps from "./NodeProps";
import Gallery from "react-photo-gallery";
import SelectedImage from "./SelectedImage";
import Lightbox from "react-images";
import { readFileSync, writeFileSync } from "fs";
import { base64Sync } from "base64-img";
import sizeOf from "image-size";
import md5File from "md5-file";
import { remote } from "electron";
const { app } = remote;
import { join } from "path";
export default class GroupNodeData extends Component {
constructor() {
@ -11,17 +21,31 @@ export default class GroupNodeData extends Component {
label: "",
driversessions: [],
propertyMap: {},
displayMap: {"description":"Description","admincount":"Admin Count"},
displayMap: {
description: "Description",
admincount: "Admin Count"
},
ServicePrincipalNames: [],
notes: null
notes: null,
pics: [],
currentImage: 0,
lightboxIsOpen: false
};
emitter.on("groupNodeClicked", this.getNodeData.bind(this));
emitter.on("imageUploadFinal", this.uploadImage.bind(this));
emitter.on("clickPhoto", this.openLightbox.bind(this));
emitter.on("deletePhoto", this.handleDelete.bind(this));
}
componentDidMount() {
jQuery(this.refs.complete).hide();
jQuery(this.refs.piccomplete).hide();
}
getNodeData(payload) {
jQuery(this.refs.complete).hide();
$.each(this.state.driversessions, function(index, record) {
$.each(this.state.driversessions, function(_, record) {
record.close();
});
@ -29,6 +53,15 @@ export default class GroupNodeData extends Component {
label: payload
});
let key = `group_${this.state.label}`;
let c = imageconf.get(key);
let pics = [];
if (typeof c !== "undefined"){
this.setState({pics: c})
}else{
this.setState({pics: pics})
}
var propCollection = driver.session();
propCollection
.run("MATCH (c:Group {name:{name}}) RETURN c", { name: payload })
@ -36,30 +69,42 @@ export default class GroupNodeData extends Component {
function(result) {
var properties = result.records[0]._fields[0].properties;
let notes;
if (!properties.notes){
if (!properties.notes) {
notes = null;
}else{
} else {
notes = properties.notes;
}
this.setState({ propertyMap: properties, notes: notes });
this.setState({
propertyMap: properties,
notes: notes
});
propCollection.close();
}.bind(this)
);
}
notesChanged(event){
this.setState({notes: event.target.value})
notesChanged(event) {
this.setState({ notes: event.target.value });
}
notesBlur(event){
let notes = this.state.notes === null || this.state.notes === "" ? null : this.state.notes;
notesBlur(event) {
let notes =
this.state.notes === null || this.state.notes === ""
? null
: this.state.notes;
let q = driver.session();
if (notes === null){
q.run("MATCH (n:Group {name:{name}}) REMOVE n.notes", {name: this.state.label}).then(x => {
if (notes === null) {
q.run("MATCH (n:Group {name:{name}}) REMOVE n.notes", {
name: this.state.label
}).then(x => {
q.close();
});
}else{
q.run("MATCH (n:Group {name:{name}}) SET n.notes = {notes}", {name: this.state.label, notes: this.state.notes}).then(x =>{
} else {
q.run("MATCH (n:Group {name:{name}}) SET n.notes = {notes}", {
name: this.state.label,
notes: this.state.notes
}).then(x => {
q.close();
});
}
@ -68,7 +113,118 @@ export default class GroupNodeData extends Component {
check.fadeOut(2000);
}
uploadImage(files) {
if (!this.props.visible || files.length === 0) {
return;
}
let p = this.state.pics;
let oLen = p.length;
let key = `group_${this.state.label}`;
$.each(files, (_, f) => {
let exists = false;
let hash = md5File.sync(f.path);
$.each(p, (_, p1) => {
if (p1.hash === hash){
exists = true;
}
})
if (exists){
emitter.emit("showAlert", "Image already exists");
return;
}
let path = join(app.getPath("userData"), "images", hash);
let dimensions = sizeOf(f.path);
let data = readFileSync(f.path);
writeFileSync(path, data);
p.push({hash: hash, src: path, width: dimensions.width, height: dimensions.height})
});
if (p.length === oLen){
return;
}
this.setState({pics: p});
imageconf.set(key, p)
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
handleDelete(event) {
if (!this.props.visible) {
return;
}
let pics = this.state.pics;
let temp = pics[event.index];
pics.splice(event.index, 1);
this.setState({
pics: pics
})
let key = `group_${this.state.label}`;
imageconf.set(key, pics);
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
openLightbox(event) {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: event.index,
lightboxIsOpen: true
});
}
closeLightbox() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: 0,
lightboxIsOpen: false
});
}
gotoPrevious() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage - 1
});
}
gotoNext() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage + 1
});
}
render() {
let gallery;
if (this.state.pics.length === 0){
gallery = (<span>Drop pictures on here to upload!</span>)
}else{
gallery = (
<React.Fragment>
<Gallery
photos={this.state.pics}
ImageComponent={SelectedImage}
className={"gallerymod"}
/>
<Lightbox
images={this.state.pics}
isOpen={this.state.lightboxIsOpen}
onClose={this.closeLightbox.bind(this)}
onClickPrev={this.gotoPrevious.bind(this)}
onClickNext={this.gotoNext.bind(this)}
currentImage={this.state.currentImage}
/>
</React.Fragment>)
}
return (
<div className={this.props.visible ? "" : "displaynone"}>
<dl className="dl-horizontal">
@ -253,7 +409,21 @@ export default class GroupNodeData extends Component {
className="fa fa-check-circle green-icon-color notes-check-style"
/>
</div>
<textarea onBlur={this.notesBlur.bind(this)} onChange={this.notesChanged.bind(this)} value={this.state.notes === null ? "" : this.state.notes} className={"node-notes-textarea"} ref="notes" />
<textarea
onBlur={this.notesBlur.bind(this)}
onChange={this.notesChanged.bind(this)}
value={this.state.notes === null ? "" : this.state.notes}
className={"node-notes-textarea"}
ref="notes"
/>
<div>
<h4 className={"inline"}>Pictures</h4>
<i
ref="piccomplete"
className="fa fa-check-circle green-icon-color notes-check-style"
/>
</div>
{gallery}
</div>
);
}

View File

@ -3,6 +3,16 @@ import PropTypes from "prop-types";
import NodeCypherLink from "./NodeCypherLink.jsx";
import NodeCypherNoNumberLink from "./NodeCypherNoNumberLink";
import NodeProps from "./NodeProps";
import Gallery from "react-photo-gallery";
import SelectedImage from "./SelectedImage";
import Lightbox from "react-images";
import { readFileSync, writeFileSync } from "fs";
import { base64Sync } from "base64-img";
import sizeOf from "image-size";
import md5File from "md5-file";
import { remote } from "electron";
const { app } = remote;
import { join } from "path";
export default class OuNodeData extends Component {
constructor() {
@ -16,10 +26,21 @@ export default class OuNodeData extends Component {
displayMap: {
"description":"Description"
},
notes: null
notes: null,
pics: [],
currentImage: 0,
lightboxIsOpen: false
};
emitter.on("ouNodeClicked", this.getNodeData.bind(this));
emitter.on("imageUploadFinal", this.uploadImage.bind(this));
emitter.on("clickPhoto", this.openLightbox.bind(this));
emitter.on("deletePhoto", this.handleDelete.bind(this));
}
componentDidMount() {
jQuery(this.refs.complete).hide();
jQuery(this.refs.piccomplete).hide();
}
getNodeData(payload, guid, blocksInheritance) {
@ -33,6 +54,15 @@ export default class OuNodeData extends Component {
blocks: bi
});
let key = `ou_${this.state.guid}`;
let c = imageconf.get(key);
let pics = [];
if (typeof c !== "undefined"){
this.setState({pics: c})
}else{
this.setState({pics: pics})
}
let props = driver.session();
props
.run("MATCH (n:OU {guid:{name}}) RETURN n", { name: guid })
@ -77,7 +107,119 @@ export default class OuNodeData extends Component {
check.fadeOut(2000);
}
uploadImage(files) {
if (!this.props.visible || files.length === 0) {
return;
}
let p = this.state.pics;
let oLen = p.length;
let key = `ou_${this.state.guid}`;
$.each(files, (_, f) => {
let exists = false;
let hash = md5File.sync(f.path);
$.each(p, (_, p1) => {
if (p1.hash === hash){
exists = true;
}
})
if (exists){
emitter.emit("showAlert", "Image already exists");
return;
}
let path = join(app.getPath("userData"), "images", hash);
let dimensions = sizeOf(f.path);
let data = readFileSync(f.path);
writeFileSync(path, data);
p.push({hash: hash, src: path, width: dimensions.width, height: dimensions.height})
});
if (p.length === oLen){
return;
}
this.setState({pics: p});
imageconf.set(key, p)
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
handleDelete(event) {
if (!this.props.visible) {
return;
}
let pics = this.state.pics;
let temp = pics[event.index];
pics.splice(event.index, 1);
this.setState({
pics: pics
})
let key = `ou_${this.state.guid}`;
imageconf.set(key, pics);
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
openLightbox(event) {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: event.index,
lightboxIsOpen: true
});
}
closeLightbox() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: 0,
lightboxIsOpen: false
});
}
gotoPrevious() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage - 1
});
}
gotoNext() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage + 1
});
}
render() {
let gallery;
if (this.state.pics.length === 0){
gallery = (<span>Drop pictures on here to upload!</span>)
}else{
gallery = (
<React.Fragment>
<Gallery
photos={this.state.pics}
ImageComponent={SelectedImage}
className={"gallerymod"}
/>
<Lightbox
images={this.state.pics}
isOpen={this.state.lightboxIsOpen}
onClose={this.closeLightbox.bind(this)}
onClickPrev={this.gotoPrevious.bind(this)}
onClickNext={this.gotoNext.bind(this)}
currentImage={this.state.currentImage}
/>
</React.Fragment>)
}
return (
<div className={this.props.visible ? "" : "displaynone"}>
<dl className="dl-horizontal">
@ -161,6 +303,14 @@ export default class OuNodeData extends Component {
/>
</div>
<textarea onBlur={this.notesBlur.bind(this)} onChange={this.notesChanged.bind(this)} value={this.state.notes === null ? "" : this.state.notes} className={"node-notes-textarea"} ref="notes" />
<div>
<h4 className={"inline"}>Pictures</h4>
<i
ref="piccomplete"
className="fa fa-check-circle green-icon-color notes-check-style"
/>
</div>
{gallery}
</div>
);
}

View File

@ -99,6 +99,15 @@
}
]
},
{
"name": "Shortest Paths to Unconstrained Delegation Systems",
"queryList": [
{
"final": true,
"query": "MATCH (m:Computer) WHERE m.unconstraineddelegation=true MATCH (n) MATCH p=shortestPath((n)-[r:{}*1..]->(m)) WHERE NOT m=n RETURN p"
}
]
},
{
"name": "Shortest Paths from Kerberoastable Users",
"queryList": [

View File

@ -0,0 +1,40 @@
import React, { Component } from "react";
const {clipboard} = require('electron')
export default class SelectedImage extends Component {
constructor(){
super()
}
componentDidMount(){
}
click(e, del){
let o = {
index: this.props.index,
photo: this.props.photo
}
if (del){
emitter.emit("deletePhoto", o);
}else{
emitter.emit("clickPhoto", o);
}
}
render(){
let style = {
margin: this.props.margin,
cursor: "pointer",
height: this.props.photo.height,
width: this.props.photo.width,
position: "relative"
};
return (<div style={style} className="gallery-img">
<i className="fa fa-copy" onClick={x => {emitter.emit("showAlert", "Copied file path to clipboard");clipboard.writeText(this.props.photo.src)}} />
<i className="fa fa-trash-alt" onClick={e => this.click(e, true)} />
<img className={"gallery-img"} onClick={e => this.click(e, false)} {...this.props.photo} style={style} />
</div>);
}
}

View File

@ -4,6 +4,16 @@ import NodeProps from "./NodeProps";
import NodeCypherLink from "./NodeCypherLink";
import NodeCypherNoNumberLink from "./NodeCypherNoNumberLink";
import NodeCypherLinkComplex from "./NodeCypherLinkComplex";
import Gallery from "react-photo-gallery";
import SelectedImage from "./SelectedImage";
import Lightbox from "react-images";
import { readFileSync, writeFileSync } from "fs";
import { base64Sync } from "base64-img";
import sizeOf from "image-size";
import md5File from "md5-file";
import { remote } from "electron";
const { app } = remote;
import { join } from "path";
export default class UserNodeData extends Component {
constructor() {
@ -28,14 +38,111 @@ export default class UserNodeData extends Component {
admincount: "AdminCount",
owned: "Compromised"
},
notes: null
notes: null,
pics: [],
currentImage: 0,
lightboxIsOpen: false
};
emitter.on("userNodeClicked", this.getNodeData.bind(this));
emitter.on("imageUploadFinal", this.uploadImage.bind(this));
emitter.on("clickPhoto", this.openLightbox.bind(this));
emitter.on("deletePhoto", this.handleDelete.bind(this));
}
uploadImage(files) {
if (!this.props.visible || files.length === 0) {
return;
}
let p = this.state.pics;
let oLen = p.length;
let key = `user_${this.state.label}`;
$.each(files, (_, f) => {
let exists = false;
let hash = md5File.sync(f.path);
$.each(p, (_, p1) => {
if (p1.hash === hash){
exists = true;
}
})
if (exists){
emitter.emit("showAlert", "Image already exists");
return;
}
let path = join(app.getPath("userData"), "images", hash);
let dimensions = sizeOf(f.path);
let data = readFileSync(f.path);
writeFileSync(path, data);
p.push({hash: hash, src: path, width: dimensions.width, height: dimensions.height})
});
if (p.length === oLen){
return;
}
this.setState({pics: p});
imageconf.set(key, p)
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
handleDelete(event) {
if (!this.props.visible) {
return;
}
let pics = this.state.pics;
let temp = pics[event.index];
pics.splice(event.index, 1);
this.setState({
pics: pics
})
let key = `user_${this.state.label}`;
imageconf.set(key, pics);
let check = jQuery(this.refs.piccomplete);
check.show();
check.fadeOut(2000);
}
openLightbox(event) {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: event.index,
lightboxIsOpen: true
});
}
closeLightbox() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: 0,
lightboxIsOpen: false
});
}
gotoPrevious() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage - 1
});
}
gotoNext() {
if (!this.props.visible) {
return;
}
this.setState({
currentImage: this.state.currentImage + 1
});
}
getNodeData(payload) {
jQuery(this.refs.complete).hide();
jQuery(this.refs.piccomplete).hide();
$.each(this.state.driversessions, function(index, record) {
record.close();
});
@ -47,6 +154,15 @@ export default class UserNodeData extends Component {
driversessions: []
});
let key = `user_${this.state.label}`;
let c = imageconf.get(key);
let pics = [];
if (typeof c !== "undefined"){
this.setState({pics: c})
}else{
this.setState({pics: pics})
}
var props = driver.session();
props
.run("MATCH (n:User {name:{name}}) RETURN n", { name: payload })
@ -108,6 +224,29 @@ export default class UserNodeData extends Component {
render() {
var domain = "@" + this.state.label.split("@").last();
let gallery;
if (this.state.pics.length === 0){
gallery = (<span>Drop pictures on here to upload!</span>)
}else{
gallery = (
<React.Fragment>
<Gallery
photos={this.state.pics}
ImageComponent={SelectedImage}
className={"gallerymod"}
/>
<Lightbox
images={this.state.pics}
isOpen={this.state.lightboxIsOpen}
onClose={this.closeLightbox.bind(this)}
onClickPrev={this.gotoPrevious.bind(this)}
onClickNext={this.gotoNext.bind(this)}
currentImage={this.state.currentImage}
/>
</React.Fragment>)
}
return (
<div className={this.props.visible ? "" : "displaynone"}>
<dl className="dl-horizontal">
@ -292,6 +431,14 @@ export default class UserNodeData extends Component {
/>
</div>
<textarea onBlur={this.notesBlur.bind(this)} onChange={this.notesChanged.bind(this)} value={this.state.notes === null ? "" : this.state.notes} className={"node-notes-textarea"} ref="notes" />
<div>
<h4 className={"inline"}>Pictures</h4>
<i
ref="piccomplete"
className="fa fa-check-circle green-icon-color notes-check-style"
/>
</div>
{gallery}
</div>
);
}

View File

@ -740,6 +740,34 @@ div.tooltip-inner-custom {
border-radius: 0;
}
.react-photo-gallery--gallery{
width: 99%;
}
.gallery-img i:first-child{
visibility: hidden;
position:absolute;
top:8px;
right: 18px;
z-index: 2;
font-size: 15px;
color: blue;
}
.gallery-img i:nth-child(2){
visibility: hidden;
position:absolute;
top:8px;
right: 2px;
z-index: 2;
font-size: 15px;
color: rgb(244, 65, 104)
}
.gallery-img:hover i{
visibility: visible;
}
.loadingIndicator {
position: absolute;
z-index: 25;
@ -754,7 +782,7 @@ div.tooltip-inner-custom {
}
.loadingIndicator img {
width: 80px;
width: 150px;
height: auto;
}

BIN
src/img/loading_new.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -7,18 +7,20 @@ import Login from "./components/Float/Login";
import { remote, shell } from "electron";
const { app } = remote;
import { stat, writeFile } from "fs";
import { join } from "path";
import { stat, writeFile, existsSync, mkdirSync } from "fs";
import ConfigStore from "electron-store";
global.conf = new ConfigStore();
global.imageconf = new ConfigStore({
name: "images"
})
import { EventEmitter2 as e } from "eventemitter2";
global.emitter = new e({});
global.renderEmit = new e({});
global.neo4j = require("neo4j-driver").v1;
global.Mustache = require("mustache");
//open links externally by default
$(document).on('click', 'a[href^="http"]', function(event) {
event.preventDefault();
@ -292,6 +294,11 @@ stat(custompath, function(err, stats) {
}
});
let imagepath = join(app.getPath("userData"), "images");
if (!existsSync(imagepath)){
mkdirSync(imagepath)
}
renderEmit.on("login", function() {
emitter.removeAllListeners();
ReactDOM.unmountComponentAtNode(document.getElementById("root"));