840 lines
35 KiB
HTML
840 lines
35 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>Bloodhound</title>
|
|
<link rel="icon" href="img/favicon.ico">
|
|
<script type="text/javascript" src="js/jquery-2.2.1.min.js"></script>
|
|
<script type="text/javascript" src="js/jquery-ui.min.js"></script>
|
|
<script type="text/javascript" src="js/circle-progress/circle-progress.js"></script>
|
|
<link rel="stylesheet" type="text/css" href="css/font-awesome.min.css">
|
|
<link type="text/css" rel="stylesheet" href="css/bootstrap.min.css">
|
|
<link type="text/css" rel="stylesheet" href="css/bootstrap-theme.min.css">
|
|
<link rel="stylesheet" type="text/css" href="css/styles.css">
|
|
<link rel="stylesheet" type="text/css" href="css/animation.css">
|
|
<link rel="stylesheet" type="text/css" href="css/tooltip.css">
|
|
<link rel="stylesheet" type="text/css" href="css/simple-slider.css">
|
|
<link rel="stylesheet" type="text/css" href="css/simple-slider-volume.css">
|
|
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
|
<script type="text/javascript" src="js/sigma.min.js"></script>
|
|
<script type="text/javascript" src="js/plugins.min.js"></script>
|
|
<script type="text/javascript" src="js/bootstrap3-typeahead.min.js"></script>
|
|
<script type="text/javascript" src="js/mustache.min.js"></script>
|
|
<script type="text/javascript" src="js/bloodhound.js"></script>
|
|
<script type="text/javascript" src="js/sigma.renderers.linkurious.min.js"></script>
|
|
<script type="text/javascript" src="js/sigma.plugins.design.min.js"></script>
|
|
<script type="text/javascript" src="js/sigma.layouts.dagre.min.js"></script>
|
|
<script type="text/javascript" src="js/sigma.layouts.noverlap.js"></script>
|
|
<script type="text/javascript" src="js/sigma.renderers.glyphs.min.js"></script>
|
|
<script type="text/javascript" src="js/sigma.exporters.image.min.js"></script>
|
|
<script type="text/javascript" src="js/dagre.min.js"></script>
|
|
<script type="text/javascript" src="js/simple-slider.min.js"></script>
|
|
|
|
<script type="javascript/worker" id="ingestworker">
|
|
function createXHR(url, auth){
|
|
var xmlhttp = new XMLHttpRequest();
|
|
xmlhttp.open("POST", url, false)
|
|
xmlhttp.setRequestHeader('Accepts', 'application/json');
|
|
xmlhttp.setRequestHeader('Content-Type', 'application/json');
|
|
xmlhttp.setRequestHeader('Authorization', auth);
|
|
xmlhttp.setRequestHeader('X-Stream', true);
|
|
return xmlhttp
|
|
}
|
|
self.addEventListener('message', function(e){
|
|
var currentTransaction = null;
|
|
var groups = [], i;
|
|
var sent = 0
|
|
importScripts(e.data.url + "/js/papaparse.min.js");
|
|
var data = Papa.parse(e.data.data, {header:true, skipEmptyLines: true}).data;
|
|
var testElement = data[0];
|
|
for (i=0; i < data.length; i += 1000){
|
|
groups.push(data.slice(i, i+1000));
|
|
}
|
|
if (e.data.type === 'localadmin'){
|
|
var starttime = new Date();
|
|
if (testElement.hasOwnProperty('Server') && testElement.hasOwnProperty('AccountName') && testElement.hasOwnProperty('IsGroup')){
|
|
var topost = []
|
|
topost.message = 'start'
|
|
topost.len = data.length
|
|
postMessage(topost)
|
|
var xmlhttp = createXHR(e.data.dburl + '/db/data/transaction', e.data.auth);
|
|
xmlhttp.send()
|
|
currentTransaction = JSON.parse(xmlhttp.responseText).commit.split('/').slice(-2)[0]
|
|
var postdata = null
|
|
groups.forEach(function(g){
|
|
postdata = {}
|
|
postdata.statements = []
|
|
g.forEach(function(o){
|
|
if (o.IsGroup == 'False'){
|
|
var user = o['AccountName'].replace('\\','/').split('/')[1].toUpperCase()
|
|
var computer = o['Server'].toUpperCase()
|
|
|
|
var obj = {
|
|
"statement": 'MERGE (user:User {name: "' + user + '"}) WITH user MERGE (computer:Computer {name: "' + computer + '" }) WITH user,computer MERGE (user)-[:AdminTo]->(computer)'
|
|
}
|
|
|
|
postdata.statements.push(obj)
|
|
}else{
|
|
var group = o['AccountName'].replace('\\','/').split('/')[1].toUpperCase()
|
|
var computer = o['Server'].toUpperCase()
|
|
|
|
var obj = {
|
|
"statement": 'MERGE (group:Group {name: "' + group + '"}) WITH group MERGE (computer:Computer {name: "' + computer +'"}) WITH group,computer MERGE (group)-[:AdminTo]->(computer)'
|
|
}
|
|
|
|
postdata.statements.push(obj)
|
|
}
|
|
|
|
})
|
|
|
|
xmlhttp = createXHR(e.data.dburl + "/db/data/transaction/" + currentTransaction, e.data.auth)
|
|
xmlhttp.send(JSON.stringify(postdata));
|
|
|
|
sent = sent + g.length
|
|
var topost = []
|
|
topost.message = 'progress'
|
|
topost.len = data.length
|
|
topost.progress = sent
|
|
postMessage(topost)
|
|
if ((sent % 20000) == 0){
|
|
xmlhttp = createXHR(e.data.dburl + "/db/data/transaction/" + currentTransaction + "/commit", e.data.auth);
|
|
xmlhttp.send()
|
|
var topost = []
|
|
topost.message = 'commit'
|
|
postMessage(topost)
|
|
xmlhttp = createXHR(e.data.dburl + "/db/data/transaction", e.data.auth);
|
|
xmlhttp.send()
|
|
currentTransaction = JSON.parse(xmlhttp.responseText).commit.split('/').slice(-2)[0]
|
|
}
|
|
})
|
|
|
|
var xmlhttp = createXHR(e.data.dburl + "/db/data/transaction/" + currentTransaction + "/commit", e.data.auth);
|
|
xmlhttp.send()
|
|
var endtime = new Date()
|
|
var timediff = Math.abs(endtime.getTime() - starttime.getTime())
|
|
var diffsec = Math.ceil(timediff / 1000)
|
|
var topost = []
|
|
topost.message = 'end'
|
|
topost.len = data.length
|
|
topost.time = diffsec
|
|
postMessage(topost)
|
|
}else{
|
|
var topost = []
|
|
topost.message = 'baddata'
|
|
postMessage(topost)
|
|
}
|
|
}else if (e.data.type == 'domainmembership'){
|
|
var starttime = new Date();
|
|
if (testElement.hasOwnProperty('MemberName') && testElement.hasOwnProperty('GroupName') && testElement.hasOwnProperty('IsGroup')){
|
|
var topost = []
|
|
topost.message = 'start'
|
|
topost.len = data.length
|
|
postMessage(topost)
|
|
var xmlhttp = createXHR(e.data.dburl + '/db/data/transaction', e.data.auth);
|
|
xmlhttp.send()
|
|
currentTransaction = JSON.parse(xmlhttp.responseText).commit.split('/').slice(-2)[0]
|
|
var postdata = null
|
|
groups.forEach(function(g){
|
|
postdata = {}
|
|
postdata.statements = []
|
|
g.forEach(function(o){
|
|
if (o.IsGroup == 'False'){
|
|
var user = o['MemberName'].toUpperCase()
|
|
var group = o['GroupName'].toUpperCase()
|
|
|
|
var obj = {
|
|
"statement": 'MERGE (user:User {name: "' + user + '"}) WITH user MERGE (group:Group {name: "' + group + '" }) WITH user,group MERGE (user)-[:MemberOf]->(group)'
|
|
}
|
|
|
|
postdata.statements.push(obj)
|
|
}else{
|
|
var ga = o['MemberName'].toUpperCase()
|
|
var gb = o['GroupName'].toUpperCase()
|
|
|
|
var obj = {
|
|
"statement": 'MERGE (group1:Group {name: "' + ga + '"}) WITH group1 MERGE (group2:Group {name: "' + gb +'"}) WITH group1,group2 MERGE (group1)-[:MemberOf]->(group2)'
|
|
}
|
|
|
|
postdata.statements.push(obj)
|
|
}
|
|
})
|
|
|
|
xmlhttp = createXHR(e.data.dburl + "/db/data/transaction/" + currentTransaction, e.data.auth)
|
|
xmlhttp.send(JSON.stringify(postdata));
|
|
|
|
sent = sent + g.length
|
|
var topost = []
|
|
topost.message = 'progress'
|
|
topost.len = data.length
|
|
topost.progress = sent
|
|
postMessage(topost)
|
|
if ((sent % 20000) == 0){
|
|
xmlhttp = createXHR(e.data.dburl + "/db/data/transaction/" + currentTransaction + "/commit", e.data.auth);
|
|
xmlhttp.send()
|
|
var topost = []
|
|
topost.message = 'commit'
|
|
postMessage(topost)
|
|
xmlhttp = createXHR(e.data.dburl + "/db/data/transaction", e.data.auth);
|
|
xmlhttp.send()
|
|
currentTransaction = JSON.parse(xmlhttp.responseText).commit.split('/').slice(-2)[0]
|
|
}
|
|
})
|
|
|
|
var xmlhttp = createXHR(e.data.dburl + "/db/data/transaction/" + currentTransaction + "/commit", e.data.auth);
|
|
xmlhttp.send()
|
|
var endtime = new Date()
|
|
var timediff = Math.abs(endtime.getTime() - starttime.getTime())
|
|
var diffsec = Math.ceil(timediff / 1000)
|
|
var topost = []
|
|
topost.message = 'end'
|
|
topost.len = data.length
|
|
topost.time = diffsec
|
|
postMessage(topost)
|
|
}else{
|
|
var topost = []
|
|
topost.message = 'baddata'
|
|
postMessage(topost)
|
|
}
|
|
}else if (e.data.type == 'sessions'){
|
|
var starttime = new Date();
|
|
if (testElement.hasOwnProperty('Computer') && testElement.hasOwnProperty('User')){
|
|
var topost = []
|
|
topost.message = 'start'
|
|
topost.len = data.length
|
|
postMessage(topost)
|
|
var xmlhttp = createXHR(e.data.dburl + '/db/data/transaction', e.data.auth);
|
|
xmlhttp.send()
|
|
currentTransaction = JSON.parse(xmlhttp.responseText).commit.split('/').slice(-2)[0]
|
|
var postdata = null
|
|
groups.forEach(function(g){
|
|
postdata = {}
|
|
postdata.statements = []
|
|
g.forEach(function(o){
|
|
var user = o['User'].toUpperCase()
|
|
var computer = o['Computer'].toUpperCase()
|
|
|
|
var obj = {
|
|
"statement": 'MERGE (user:User {name: "' + user + '"}) WITH user MERGE (computer:Computer {name: "' + computer + '" }) WITH user,computer MERGE (computer)-[:HasSession]->(user)'
|
|
}
|
|
|
|
postdata.statements.push(obj)
|
|
})
|
|
|
|
xmlhttp = createXHR(e.data.dburl + "/db/data/transaction/" + currentTransaction, e.data.auth)
|
|
xmlhttp.send(JSON.stringify(postdata));
|
|
|
|
sent = sent + g.length
|
|
var topost = []
|
|
topost.message = 'progress'
|
|
topost.len = data.length
|
|
topost.progress = sent
|
|
postMessage(topost)
|
|
if ((sent % 20000) == 0){
|
|
xmlhttp = createXHR(e.data.dburl + "/db/data/transaction/" + currentTransaction + "/commit", e.data.auth);
|
|
xmlhttp.send()
|
|
var topost = []
|
|
topost.message = 'commit'
|
|
postMessage(topost)
|
|
xmlhttp = createXHR(e.data.dburl + "/db/data/transaction", e.data.auth);
|
|
xmlhttp.send()
|
|
currentTransaction = JSON.parse(xmlhttp.responseText).commit.split('/').slice(-2)[0]
|
|
}
|
|
})
|
|
|
|
var xmlhttp = createXHR(e.data.dburl + "/db/data/transaction/" + currentTransaction + "/commit", e.data.auth);
|
|
xmlhttp.send()
|
|
var endtime = new Date()
|
|
var timediff = Math.abs(endtime.getTime() - starttime.getTime())
|
|
var diffsec = Math.ceil(timediff / 1000)
|
|
var topost = []
|
|
topost.message = 'end'
|
|
topost.len = data.length
|
|
topost.time = diffsec
|
|
postMessage(topost)
|
|
}else{
|
|
var topost = []
|
|
topost.message = 'baddata'
|
|
postMessage(topost)
|
|
}
|
|
}
|
|
}, false)
|
|
</script>
|
|
|
|
<script id="datatemplate" type="text/template">
|
|
<ul class='nav nav-tabs nav-justified' id="abc" role="tablist">
|
|
<li role='presentation' class='active'><a href='#dbinfo' role='tab' data-toggle='tab' aria-controls='dbinfo'>Database Info</a></li>
|
|
<li role='presentation'><a href='#nodeinfo' role='tab' data-toggle='tab' aria-controls='nodeinfo'>Node Info</a></li>
|
|
<li role='presentation'><a href='#pbqueries' role='tab' data-toggle='tab' aria-controls='pbqueries'>Queries</a></li>
|
|
</ul>
|
|
|
|
<div class="tab-content content">
|
|
<div role="tabpanel" class="tab-pane active" id="dbinfo">
|
|
{{{dbinfo}}}
|
|
</div>
|
|
<div role="tabpanel" class="tab-pane" id="nodeinfo">
|
|
{{{nodeinfo}}}
|
|
</div>
|
|
<div role="tabpanel" class="tab-pane" id="pbqueries">
|
|
<h3 align="center">
|
|
Pre-Built Analytics Queries
|
|
</h3>
|
|
<div style="padding: 0 .5em .5em .5em">
|
|
<a href="#" onclick="shortestPathsToDA()">Find Shortest Paths to DA</a><br>
|
|
<a href="#" onclick="doQuery('start n=node(*) match n<-[r:HasSession]-() WHERE NOT n.name="ANONYMOUS LOGON" AND NOT n.name="" with n, count(r) as rel_count order by rel_count desc LIMIT 1 MATCH (m)-[r:HasSession]->n RETURN n,r,m')">Find User with Most Sessions</a><br>
|
|
<a href="#" onclick="doQuery('MATCH (n:Domain) MATCH p=(n)-[r]-() RETURN p')">Map Domain Trusts</a><br>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
|
|
<script id="dbdata" type="text/template">
|
|
<div>
|
|
<h3 align="center">Database Info</h3>
|
|
<dl class="dl-horizontal dl-horizontal-fix">
|
|
<dt>DB Address</dt>
|
|
<dd>{{url}}</dd>
|
|
<dt>DB User</dt>
|
|
<dd>{{user}}</dd>
|
|
<dt>Users</dt>
|
|
<dd>{{num_users}}</dd>
|
|
<dt>Computers</dt>
|
|
<dd>{{num_computers}}</dd>
|
|
<dt>Groups</dt>
|
|
<dd>{{num_groups}}</dd>
|
|
<dt>Relationships</dt>
|
|
<dd>{{num_relationships}}</dd>
|
|
</dl>
|
|
<div class="text-center">
|
|
<div class="dbbuttons">
|
|
<button type="button" class="btn btn-success" onclick="getDBInfo()">Refresh DB Stats</button>
|
|
<button type="button" class="btn btn-warning" data-toggle="modal" data-target="#logoutModal">Log Out/Switch DB</button>
|
|
<button type="button" class="btn btn-danger" data-toggle="modal" data-target="#clearDBWarn">Clear Database</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
|
|
<script id="usernodetemplate" type="text/template">
|
|
<dl class='dl-horizontal'>
|
|
<dt>Node:</dt>
|
|
<dd>{{label}}</dd>
|
|
<dt>SAMAccountName</dt>
|
|
<dd> None </dd>
|
|
<dt>Display Name</dt>
|
|
<dd> None </dd>
|
|
<dt>Password Last Changed</dt>
|
|
<dd> None </dd>
|
|
<br>
|
|
<dt>First Degree Group Memberships</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:User {name:"{{label}}"}), (target:Group),p=allShortestPaths((n)-[:MemberOf*1]->(target)) RETURN p')">{{first_degree_group}}</a></dd>
|
|
<dt>Unrolled Group Memberships</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:User {name:"{{label}}"}), (target:Group),p=allShortestPaths((n)-[:MemberOf*1..]->(target)) RETURN p', "{{label}}")">{{unrolled}}</a></dd>
|
|
<dt>Foreign Group Memberships</dt>
|
|
<dd></dd>
|
|
<br>
|
|
<dt>First Degree Local Admin</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:User {name:"{{label}}"}), (target:Computer), p=allShortestPaths((n)-[:AdminTo*1]->(target)) RETURN p')">{{first_degree_admin}}</a></dd>
|
|
<dt>Group Delegated Local Admin Rights</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:User {name:"{{label}}"}), (m:Group), x=allShortestPaths((n)-[r:MemberOf*1..]->(m)) WITH n,m,r MATCH (m)-[s:AdminTo*1..]->(p:Computer) RETURN n,m,r,s,p', "{{label}}", "")">{{dlar}}</a></dd>
|
|
<dt>Derivative Local Admin Rights</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:User {name:"{{label}}"}), (m:Computer), p=allShortestPaths((n)-[r*]->(m)) RETURN p', "{{label}}")">{{derivative}}</a></dd>
|
|
<dt>Sessions</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:Computer)-[r:HasSession]->(m:User {name:"{{label}}"}) RETURN n,r,m')">{{sessions}}</a></dd>
|
|
</dl>
|
|
</script>
|
|
|
|
<script id="computertemplate" type="text/template">
|
|
<dl class='dl-horizontal'>
|
|
<dt>Node</dt>
|
|
<dd>{{label}}</dd>
|
|
<dt>OS</dt>
|
|
<dd> None </dd>
|
|
<dt>Allows Unconstrained Delegation Name</dt>
|
|
<dd> None </dd>
|
|
<br>
|
|
<dt>Explicit Admins</dt>
|
|
<dd> <a href="#" onclick="doQuery('MATCH (n)-[r:AdminTo]->(m:Computer {name:"{{label}}"}) RETURN n,r,m')">{{explicit_admins}}</a></dd>
|
|
<dt>Unrolled Admins</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:User),(target:Computer {name:"{{label}}"}), p=allShortestPaths((n)-[:AdminTo|MemberOf*1..]->(target)) RETURN p', "{{label}}")">{{unrolled_admin}}</a></dd>
|
|
<br>
|
|
<dt>First Degree Group Membership</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:Computer {name:"{{label}}"}),(target:Group), (n)-[r:MemberOf]->(target) RETURN n,r,target', "{{label}}")">{{first_degree_group}}</a></dd>
|
|
<dt>Unrolled Group Membership</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:Computer {name:"{{label}}"}), (target:Group),p=allShortestPaths((n)-[:MemberOf*1..]->(target)) RETURN p', "{{label}}")">{{unrolled_group_membership}}</a></dd>
|
|
<dt>Sessions</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (m:Computer {name:"{{label}}"})-[r:HasSession]->(n:User) WITH n,r,m WHERE NOT n.name ENDS WITH "$" RETURN n,r,m')">{{sessions}}</a></dd>
|
|
</dl>
|
|
</script>
|
|
|
|
<script id="grouptemplate" type="text/template">
|
|
<dl class='dl-horizontal'>
|
|
<dt>Node</dt>
|
|
<dd>{{label}}</dd>
|
|
<br>
|
|
<dt>Direct Members</dt>
|
|
<dd> <a href="#" onclick="doQuery('MATCH (n)-[r:MemberOf]->(m:Group {name:"{{label}}"}) RETURN n,r,m')">{{explicit_members}}</a></dd>
|
|
<dt>Unrolled Members</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:User), (m:Group {name:"{{label}}"}), p=allShortestPaths((n)-[:MemberOf*1..]->(m)) RETURN p', "{{label}}")">{{unrolled_members}}</a></dd>
|
|
<br>
|
|
<dt>Direct Admin To</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:Group {name:"{{label}}"})-[r:AdminTo]->(m:Computer) RETURN n,r,m', "{{label}}")">{{adminto}}</a></dd>
|
|
<dt>Derivative Admin To</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:Group {name:"{{label}}"}), (target:Computer), p=allShortestPaths((n)-[*]->(target)) RETURN p', "{{label}}")">{{derivative_admin}}</a>
|
|
<dt>Unrolled Member Of</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:Group {name:"{{label}}"}), (target:Group), p=allShortestPaths((n)-[r:MemberOf*1..]->(target)) RETURN p', "{{label}}")">{{unrolled_member_of}}</a></dd>
|
|
<dt>Sessions</dt>
|
|
<dd><a href="#" onclick="doQuery('MATCH (n:User), (m:Group {name: "{{label}}"}), p=allShortestPaths((n)-[r:MemberOf*1..]->(m)) WITH n,m,r MATCH (n)-[s:HasSession]-(o:Computer) RETURN m,n,r,o,s', "{{label}}")">{{sessions}}</a></dd>
|
|
</dl>
|
|
</script>
|
|
|
|
<script id="ingestProgressBar" type="text/template">
|
|
<div class="panel-heading">
|
|
Ingesting Data
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="progress" style="width:100%">
|
|
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="{{progress}}" aria-valuemin="0" aria-valuemax="{{total}}" style="width:{{width}}%">
|
|
<span>{{progress}} / {{total}}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel-footer">
|
|
<button id="startingestbutton" class="btn btn-large" onclick="cancelIngest()">Cancel</button>
|
|
</div>
|
|
</script>
|
|
|
|
<script id="ingestComplete" type="text/template">
|
|
<div class="panel-heading">
|
|
Ingest Finished
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="progress" style="width:100%">
|
|
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="{{total}}" aria-valuemin="0" aria-valuemax="{{total}}" style="width:100%">
|
|
</div>
|
|
</div>
|
|
<div class="text-center">
|
|
<span>Ingest Complete in {{time}} seconds! <i class="fa fa-check-circle green-icon-color"></i></span>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
|
|
<script id="ingestCancelled" type="text/template">
|
|
<div class="panel-heading">
|
|
Ingest Cancelled
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="text-center">
|
|
<span>Ingest Cancelled! <i class="fa fa-times-circle red-icon-color"></i></span>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
</head>
|
|
|
|
<body role="document">
|
|
<div id="overlay" class="loginwindow">
|
|
<div id="loginpanel">
|
|
<img src="img/logo-white-transparent-full.png">
|
|
<div class="text-center">
|
|
<span>Log in to Neo4j Database</span>
|
|
</div>
|
|
<form>
|
|
<div class="form-group has-feedback">
|
|
<div class="input-group">
|
|
<span class="input-group-addon" id="dburladdon">Database URL</span>
|
|
<input id="dburl" type="text" class="form-control" placeholder="http://db-ip:dp-port" aria-describedby="dburladdon">
|
|
<i id="dburlspinner" class="fa fa-spinner fa-spin form-control-feedback"></i>
|
|
</div>
|
|
<p id="dbHelpBlock" class="help-block help-block-add hide">No Neo4j REST API Found</p>
|
|
<div class="input-group spacing">
|
|
<span class="input-group-addon" id="dbuseraddon"> DB Username</span>
|
|
<input id="dbusername" type="text" class="form-control" placeholder="neo4j" aria-describedby="dbuseraddon">
|
|
</div>
|
|
<div class="input-group spacing">
|
|
<span class="input-group-addon" id="dbpwaddon"> DB Password</span>
|
|
<input id="dbpassword" type="password" class="form-control" placeholder="neo4j" aria-describedby="dbpwaddon">
|
|
</div>
|
|
<p id="loginbadpw" class="help-block help-block-add hide" style="color: #d9534f">Wrong username or password</p>
|
|
<button id="loginbutton" disabled=true type="button" class="btn btn-primary loginbutton has-spinner">
|
|
Login
|
|
<span class="spinner"><i class="fa fa-spinner fa-spin"></i></span>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div id="checkconnectionpanel">
|
|
<span id="connectionstatus">Checking Stored Credentials <i class="fa fa-spinner fa-spin"></i></span>
|
|
</div>
|
|
|
|
<div id="settingspanel">
|
|
|
|
</div>
|
|
</div>
|
|
<div id="nodataalert" class="alert alert-warning alert-dismissible alertdiv" role="alert">
|
|
<button type="button" class="close" onclick="$('#nodataalert').fadeToggle(false)" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>No data returned from query.
|
|
</div>
|
|
<div id="layoutchange" class="alert alert-warning alert-dismissible alertdiv" role="alert">
|
|
<button type="button" class="close" onclick="$('#layoutchange').fadeToggle(false)" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>Changed Layout To Hierarchical
|
|
</div>
|
|
<div id="graph" class="graph"> </div>
|
|
<div id= "bottomdiv" class="bottomdiv">
|
|
<button id="bottomSlide" class="slideupbutton">
|
|
<span class="glyphicon glyphicon-chevron-up"></span> Raw Query <span class="glyphicon glyphicon-chevron-up"></span>
|
|
</button>
|
|
<input type="text" class="form-control queryInput" autocomplete="off" id="rawQueryBox" placeholder="Enter a raw query...">
|
|
</div>
|
|
<div id="searchDiv" class="searchdiv">
|
|
<div class="input-group input-group-unstyled">
|
|
<span id="menu" class="input-group-addon spanfix" data-toggle="tooltip" data-placement="bottom" title="More Info">
|
|
<i class="glyphicon glyphicon-menu-hamburger menuglyph"></i></span>
|
|
<input id="searchBar" type="search" class="form-control searchbox" autocomplete="off" placeholder="Start typing to search for a node...">
|
|
<span id="openpath" class="input-group-addon spanfix" data-toggle="tooltip" data-placement="bottom" title="Pathfinding">
|
|
<i class="glyphicon glyphicon glyphicon-road menuglyph"></i></span>
|
|
<span id="back" class="input-group-addon spanfix" data-toggle="tooltip" data-placement="bottom" title="Back">
|
|
<i class="glyphicon glyphicon-step-backward menuglyph"></i></span>
|
|
</div>
|
|
<div id="pathfindingbox">
|
|
<div class="input-group input-group-unstyled">
|
|
<span class="input-group-addon spanfix" style="visibility: hidden">
|
|
<i class="glyphicon glyphicon-menu-hamburger menuglyph"></i></span>
|
|
<input id="endNode" type="search" class="form-control searchbox" autocomplete="off" placeholder="Target Node">
|
|
<span class="input-group-addon spanfix" style="visibility: hidden">
|
|
<i class="glyphicon glyphicon glyphicon-road menuglyph"></i></span>
|
|
<span id="play" class="input-group-addon spanfix" data-toggle="tooltip" data-placement="bottom" title="Findpath">
|
|
<i class="glyphicon glyphicon-play menuglyph"></i></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="nodedatabox" id="nodedatabox">
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div id="menuDiv" class="menudiv">
|
|
<div>
|
|
<button id="refreshbutton" class="btn">
|
|
<span class="glyphicon glyphicon-refresh rightspan"></span>
|
|
</button>
|
|
</div>
|
|
<div>
|
|
<button id="exportbutton" class="btn">
|
|
<span class="glyphicon glyphicon-export rightspan"></span>
|
|
</button>
|
|
</div>
|
|
<div>
|
|
<button id="importbutton" class="btn">
|
|
<span class="glyphicon glyphicon-import rightspan"></span>
|
|
</button>
|
|
</div>
|
|
<div>
|
|
<button id="uploadbutton" class="btn">
|
|
<span class="glyphicon glyphicon-upload rightspan"></span>
|
|
</button>
|
|
</div>
|
|
|
|
<div>
|
|
<button id="layoutbutton" class="btn">
|
|
<span style="width:14px" class="fa fa-line-chart rightspan"></span>
|
|
</button>
|
|
</div>
|
|
|
|
<div>
|
|
<button id="settingsbutton" class="btn">
|
|
<span style="width:14px" class="fa fa-cogs rightspan"></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="uploadSelectDiv" class="uploadSelectDiv panel panel-default">
|
|
<div class="panel-heading">
|
|
Ingest CSV
|
|
<button type="button" class="close" onclick="$('#uploadSelectDiv').fadeToggle(false)" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="panel-body ingestinput">
|
|
<div class="input-group" id="fileselectdiv">
|
|
<span class="input-group-addon" id="addonforfile">File Name:</span>
|
|
<input id="uploadFileSelected" aria-described-by="addonforfile" class="form-control">
|
|
<span class="input-group-btn">
|
|
<button id="selectUploadFile" class="btn btn-default">
|
|
<span class="glyphicon glyphicon-file"></span>
|
|
</button>
|
|
</span>
|
|
</div>
|
|
|
|
<div class="list-group" style="margin-top:15px;">
|
|
<a href="#" id="ingestlocaladmin" class="list-group-item active">
|
|
<h4 class="list-group-item-heading">Local Admin Membership</h4>
|
|
<p class="list-group-item-text">
|
|
Data containing members of local Administrator groups
|
|
</p>
|
|
</a>
|
|
|
|
<a href="#" id="ingestdomaingroup" class="list-group-item">
|
|
<h4 class="list-group-item-heading">Domain Group Membership</h4>
|
|
<p class="list-group-item-text">
|
|
Data containing members of domain groups
|
|
</p>
|
|
</a>
|
|
|
|
<a href="#" id="ingestusersessions" class="list-group-item">
|
|
<h4 class="list-group-item-heading">User Sessions</h4>
|
|
<p class="list-group-item-text">
|
|
Data containing login sessions for users
|
|
</p>
|
|
</a>
|
|
</div>
|
|
|
|
<div style="text-align: center">
|
|
<button id="startingestbutton" class="btn btn-large">Ingest Data</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="exportSelectDiv" class="uploadSelectDiv panel panel-default">
|
|
<div class="panel-heading">
|
|
Export Graph
|
|
<button type="button" class="close" onclick="$('#exportSelectDiv').fadeToggle(false)" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="panel-body">
|
|
<div class="list-group">
|
|
<a href="#" id="exportjson" class="list-group-item active">
|
|
<h4 class="list-group-item-heading">Export to JSON</h4>
|
|
<p class="list-group-item-text">
|
|
Use this format to export data and re-import it later
|
|
</p>
|
|
</a>
|
|
|
|
<a href="#" id="exportimage" class="list-group-item">
|
|
<h4 class="list-group-item-heading">Export to Image</h4>
|
|
<p class="list-group-item-text">
|
|
Use this format to export data and view it as an image
|
|
</p>
|
|
</a>
|
|
</div>
|
|
|
|
<div style="text-align: center">
|
|
<button id="exportFinishButton" class="btn btn-lg">Export Data</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button id="refreshbuttonhidden" class="btn rightmenubutton hideme">
|
|
Refresh<span class="glyphicon glyphicon-refresh rightglyph"></span>
|
|
</button>
|
|
<button id="exportbuttonhidden" class="btn rightmenubutton hideme">
|
|
Export Graph<span class="glyphicon glyphicon-export rightglyph"></span>
|
|
</button>
|
|
<button id="importbuttonhidden" class="btn rightmenubutton hideme">
|
|
Import Graph<span class="glyphicon glyphicon-import rightglyph"></span>
|
|
</button>
|
|
<button id="uploadbuttonhidden" class="btn rightmenubutton hideme">
|
|
Upload Data<span class="glyphicon glyphicon-upload rightglyph"></span>
|
|
</button>
|
|
<button id="layoutbuttonhidden" class="btn rightmenubutton hideme">
|
|
Change Layout Type<span class="fa fa-line-chart rightglyph"></span>
|
|
</button>
|
|
<button id="settingsbuttonhidden" class="btn rightmenubutton hideme">
|
|
Settings<span class="fa fa-cogs rightglyph"></span>
|
|
</button>
|
|
<input type="file" accept=".json" id="fileloader" name="files" title="Open JSON" style="display:none">
|
|
<input type="file" accept=".csv" id="uploader" name="import" title="Open CSV for Import" style="display:none">
|
|
|
|
<div id="logoutModal" class="modal fade" role="dialog" aria-labelledby="logoutModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
<h4 class="modal-title" id="logoutModalLabel">Logout</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Are you sure you want to logout?</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-danger" onclick="resetUI()">Logout</button>
|
|
<button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="clearDBWarn" class="modal fade" role="dialog" aria-labelledby="clearDBWarnLabel" aria-hidden="true">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
<h4 class="modal-title" id="clearDBWarnLabel">Clear Database</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Are you sure you want to clear the database? This is irreversible and may take some time!</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-danger" data-toggle="modal" data-target="#clearDBConfirm" data-dismiss="modal">Clear Database</button>
|
|
<button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="clearDBConfirm" class="modal fade" role="dialog" aria-labelledby="clearDBConfirmLabel" aria-hidden="true">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
<h4 class="modal-title" id="clearDBConfirmLabel">Clear Database</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Are you ABSOLUTELY sure you want to clear the database?</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-danger" data-dismiss="modal" data-toggle="modal" data-target="#deleteProgressModal" onclick="clearDB()">Clear Database</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="badDataModal" class="modal fade" role="dialog" aria-labelledby="badDataLabel" aria-hidden="true">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
<h4 class="modal-title" id="badDataLabel">Invalid Data</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Provided CSV doesn't match the selected data type!</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-primary" data-dismiss="modal">Ok</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="deleteProgressModal" class="modal fade" role="dialog" data-keyboard="false" data-backdrop="static" aria-labelledby="deleteDBLabel" aria-hidden="true">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title" id="deleteDBLabel">Clearing Database</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="progress">
|
|
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="zoomBox" class="zoomBox">
|
|
<div>
|
|
<button id="zoomIn" class="btn zoomIn">
|
|
<span class="fa fa-plus"></span>
|
|
</button>
|
|
</div>
|
|
<div>
|
|
<button id="zoomOut" class="btn zoomOut">
|
|
<span class="fa fa-minus"></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="spotlight" class="spotlight">
|
|
<div class="input-group input-group-unstyled unborder">
|
|
<span class="input-group-addon spanfix">
|
|
<i class="glyphicon glyphicon-search"></i>
|
|
</span>
|
|
<input id="spotlightBar" type="search" class="form-control searchbox" autocomplete="off" placeholder="Explore Nodes" data-type="search">
|
|
</div>
|
|
|
|
<div class="nodeList">
|
|
<table id="spotlightTable" data-role="table" class="table table-striped" data-filter="true" data-input="#spotlightBar">
|
|
<thead style="text-align:center">
|
|
<tr>
|
|
<td>Node Label</td>
|
|
<td>Collapsed Into</td>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="nodeBody" class="searchable">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="nodeSelector" class="spotlight">
|
|
<div class="input-group input-group-unstyled unborder">
|
|
<span class="input-group-addon spanfix">
|
|
<i class="glyphicon glyphicon-search"></i>
|
|
</span>
|
|
<input id="nodeSelectorBar" type="search" class="form-control searchbox" autocomplete="off" placeholder="Filter Nodes" data-type="search">
|
|
</div>
|
|
<div class="nodeList">
|
|
<table id="nodeSelectorTable" data-role="table" class="table table-striped" data-filter="true" data-input="#nodeSelectorBar">
|
|
<thead style="text-align:center">
|
|
<tr>
|
|
<td>Choose a Node</td>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="nodeSelectorBody" class="searchablex">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="queryLoad" class="loadingIndicator">
|
|
<div id="loadingText">Loading</div>
|
|
<div id="circle"></div>
|
|
</div>
|
|
|
|
<div id="settingsDiv" class="settingsDiv panel panel-default">
|
|
<div class="panel-heading">
|
|
Settings
|
|
<button type="button" class="close" onclick="$('#settingsDiv').fadeToggle(false)" aria-label="Close">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="panel-body sliderfix">
|
|
<div>
|
|
<strong>Sibling Collapse Threshold</strong> <i data-toggle="tooltip" data-placement="right" title="Merge nodes that have the same parent. 0 to Disable, Default 10" id="siblingCollapseQuestion" class="glyphicon glyphicon-question-sign"></i><br>
|
|
<input type="text" data-slider="true" data-slider-range="0,20" data-slider-step="1" data-slider-theme="volume slideinline" id="siblingCollapseSlider">
|
|
<span>
|
|
<input type="number" min="0" max="20" class="sliderinput" id="siblingCollapseInput">
|
|
</span>
|
|
</div>
|
|
|
|
<div>
|
|
<strong>Node Collapse Threshold</strong> <i data-toggle="tooltip" data-placement="right" title="Collapse nodes at the end of paths that only have one relationship. 0 to Disable, Default 5" id="nodeCollapseQuestion" class="glyphicon glyphicon-question-sign"></i><br>
|
|
<input type="text" data-slider="true" data-slider-range="0,20" data-slider-step="1" data-slider-theme="volume slideinline" id="nodeCollapseSlider">
|
|
<span>
|
|
<input type="number" min="0" max="20" class="sliderinput" id="nodeCollapseInput">
|
|
</span>
|
|
</div>
|
|
<div class="checkbox-inline" style="padding-top:5px">
|
|
<label>
|
|
<input id="lowDetailCheck" type="checkbox"> Low Detail Mode
|
|
</label>
|
|
</div>
|
|
<i data-toggle="tooltip" data-placement="right" title="Lower detail of graph to improve performance" id="siblingCollapseQuestion" class="glyphicon glyphicon-question-sign"></i>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|