BloodHound/Bloodhound.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=&quot;ANONYMOUS LOGON&quot; AND NOT n.name=&quot;&quot; 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:&quot;{{label}}&quot;}), (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:&quot;{{label}}&quot;}), (target:Group),p=allShortestPaths((n)-[:MemberOf*1..]->(target)) RETURN p', &quot;{{label}}&quot;)">{{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:&quot;{{label}}&quot;}), (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:&quot;{{label}}&quot;}), (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', &quot;{{label}}&quot;, &quot;&quot;)">{{dlar}}</a></dd>
<dt>Derivative Local Admin Rights</dt>
<dd><a href="#" onclick="doQuery('MATCH (n:User {name:&quot;{{label}}&quot;}), (m:Computer), p=allShortestPaths((n)-[r*]->(m)) RETURN p', &quot;{{label}}&quot;)">{{derivative}}</a></dd>
<dt>Sessions</dt>
<dd><a href="#" onclick="doQuery('MATCH (n:Computer)-[r:HasSession]->(m:User {name:&quot;{{label}}&quot;}) 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:&quot;{{label}}&quot;}) RETURN n,r,m')">{{explicit_admins}}</a></dd>
<dt>Unrolled Admins</dt>
<dd><a href="#" onclick="doQuery('MATCH (n:User),(target:Computer {name:&quot;{{label}}&quot;}), p=allShortestPaths((n)-[:AdminTo|MemberOf*1..]->(target)) RETURN p', &quot;{{label}}&quot;)">{{unrolled_admin}}</a></dd>
<br>
<dt>First Degree Group Membership</dt>
<dd><a href="#" onclick="doQuery('MATCH (n:Computer {name:&quot;{{label}}&quot;}),(target:Group), (n)-[r:MemberOf]->(target) RETURN n,r,target', &quot;{{label}}&quot;)">{{first_degree_group}}</a></dd>
<dt>Unrolled Group Membership</dt>
<dd><a href="#" onclick="doQuery('MATCH (n:Computer {name:&quot;{{label}}&quot;}), (target:Group),p=allShortestPaths((n)-[:MemberOf*1..]->(target)) RETURN p', &quot;{{label}}&quot;)">{{unrolled_group_membership}}</a></dd>
<dt>Sessions</dt>
<dd><a href="#" onclick="doQuery('MATCH (m:Computer {name:&quot;{{label}}&quot;})-[r:HasSession]->(n:User) WITH n,r,m WHERE NOT n.name ENDS WITH &quot;$&quot; 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:&quot;{{label}}&quot;}) RETURN n,r,m')">{{explicit_members}}</a></dd>
<dt>Unrolled Members</dt>
<dd><a href="#" onclick="doQuery('MATCH (n:User), (m:Group {name:&quot;{{label}}&quot;}), p=allShortestPaths((n)-[:MemberOf*1..]->(m)) RETURN p', &quot;{{label}}&quot;)">{{unrolled_members}}</a></dd>
<br>
<dt>Direct Admin To</dt>
<dd><a href="#" onclick="doQuery('MATCH (n:Group {name:&quot;{{label}}&quot;})-[r:AdminTo]->(m:Computer) RETURN n,r,m', &quot;{{label}}&quot;)">{{adminto}}</a></dd>
<dt>Derivative Admin To</dt>
<dd><a href="#" onclick="doQuery('MATCH (n:Group {name:&quot;{{label}}&quot;}), (target:Computer), p=allShortestPaths((n)-[*]->(target)) RETURN p', &quot;{{label}}&quot;)">{{derivative_admin}}</a>
<dt>Unrolled Member Of</dt>
<dd><a href="#" onclick="doQuery('MATCH (n:Group {name:&quot;{{label}}&quot;}), (target:Group), p=allShortestPaths((n)-[r:MemberOf*1..]->(target)) RETURN p', &quot;{{label}}&quot;)">{{unrolled_member_of}}</a></dd>
<dt>Sessions</dt>
<dd><a href="#" onclick="doQuery('MATCH (n:User), (m:Group {name: &quot;{{label}}&quot;}), p=allShortestPaths((n)-[r:MemberOf*1..]->(m)) WITH n,m,r MATCH (n)-[s:HasSession]-(o:Computer) RETURN m,n,r,o,s', &quot;{{label}}&quot;)">{{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">&nbsp;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">&nbsp;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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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>