mirror of
https://github.com/ziirish/burp-ui.git
synced 2026-05-21 06:45:24 -06:00
add: servers report (fixes #15)
This commit is contained in:
parent
d3d7958060
commit
d498b334e3
8 changed files with 294 additions and 12 deletions
|
|
@ -11,13 +11,18 @@ Current
|
|||
- Add the ability to `chain multiple authentication backends <https://git.ziirish.me/ziirish/burp-ui/issues/79>`_
|
||||
- Add display versions `within the interface <https://git.ziirish.me/ziirish/burp-ui/issues/89>`_
|
||||
- Add support for `zip64 <https://git.ziirish.me/ziirish/burp-ui/issues/97>`_
|
||||
- Add Basic HTTP Authentication
|
||||
- Add new `report <https://git.ziirish.me/ziirish/burp-ui/issues/15>`_
|
||||
- Add "restart" option to debian init script thanks to @Larsen
|
||||
- Add Basic HTTP Authentication (mostly for the API)
|
||||
- Add full documented API
|
||||
- Fix issue `#81 <https://git.ziirish.me/ziirish/burp-ui/issues/81>`_
|
||||
- Fix issue `#87 <https://git.ziirish.me/ziirish/burp-ui/issues/87>`_
|
||||
- Fix issue `#88 <https://git.ziirish.me/ziirish/burp-ui/issues/88>`_
|
||||
- Fix issue `#92 <https://git.ziirish.me/ziirish/burp-ui/issues/92>`_
|
||||
- Fix issue `#95 <https://git.ziirish.me/ziirish/burp-ui/issues/95>`_
|
||||
- Fix issue `#99 <https://git.ziirish.me/ziirish/burp-ui/issues/99>`_
|
||||
- Fix issue `#100 <https://git.ziirish.me/ziirish/burp-ui/issues/100>`_
|
||||
- Fix issue `#101 <https://git.ziirish.me/ziirish/burp-ui/issues/101>`_
|
||||
- `demo <https://demo.ziirish.me/>`_
|
||||
- API refactoring
|
||||
- Security fixes
|
||||
|
|
|
|||
|
|
@ -56,22 +56,24 @@ class ServersStats(Resource):
|
|||
"""
|
||||
|
||||
r = []
|
||||
if hasattr(api.bui.cli, 'servers'): # pragma: no cover
|
||||
allowed = []
|
||||
if hasattr(api.bui.cli, 'servers'):
|
||||
restrict = []
|
||||
check = False
|
||||
if (api.bui.acl and not
|
||||
api.bui.acl.is_admin(current_user.get_id())):
|
||||
allowed = api.bui.acl.servers(current_user.get_id())
|
||||
check = True
|
||||
restrict = api.bui.acl.servers(current_user.get_id())
|
||||
|
||||
def get_servers_info(serv, output, allowed, username):
|
||||
def get_servers_info(serv, output, restrict, check, username):
|
||||
try:
|
||||
if allowed and serv in allowed:
|
||||
if check and serv in restrict:
|
||||
output.put({
|
||||
'name': serv,
|
||||
'clients': len(api.bui.acl.clients(username, serv)),
|
||||
'alive': api.bui.cli.servers[serv].ping()
|
||||
})
|
||||
return
|
||||
elif not allowed:
|
||||
elif not check:
|
||||
output.put({
|
||||
'name': serv,
|
||||
'clients': len(api.bui.cli.servers[serv].get_all_clients(serv)),
|
||||
|
|
@ -82,6 +84,139 @@ class ServersStats(Resource):
|
|||
except BUIserverException as e:
|
||||
output.put(str(e))
|
||||
|
||||
r = parallel_loop(get_servers_info, api.bui.cli.servers, allowed, current_user.get_id())
|
||||
r = parallel_loop(get_servers_info, api.bui.cli.servers, restrict, check, current_user.get_id())
|
||||
|
||||
return r
|
||||
|
||||
|
||||
@ns.route('/report', endpoint='servers_report')
|
||||
class ServersReport(Resource):
|
||||
"""The :class:`burpui.api.servers.ServersReport` resource allows you to
|
||||
retrieve a report about servers/agents.
|
||||
|
||||
This resource is part of the :mod:`burpui.api.servers` module.
|
||||
"""
|
||||
stats_fields = api.model('ServersStats', {
|
||||
'total': fields.Integer(required=True, description='Number of files', default=0),
|
||||
'totsize': fields.Integer(required=True, description='Total size occupied by all the backups of this server', default=0),
|
||||
'linux': fields.Integer(required=True, description='Total number of Linux/Unix clients on this server', default=0),
|
||||
'windows': fields.Integer(required=True, description='Total number of Windows clients on this server', default=0),
|
||||
'unknown': fields.Integer(required=True, description='Total number of Unknown clients on this server', default=0),
|
||||
})
|
||||
server_fields = api.model('ServersReport', {
|
||||
'name': fields.String(required=True, description='Server name'),
|
||||
'stats': fields.Nested(stats_fields, required=True),
|
||||
})
|
||||
backup_fields = api.model('ServersBackup', {
|
||||
'name': fields.String(required=True, description='Server name'),
|
||||
'number': fields.Integer(required=True, description='Number of backups on this server', default=0),
|
||||
})
|
||||
report_fields = api.model('ServersReportFull', {
|
||||
'backups': fields.Nested(backup_fields, as_list=True, required=True),
|
||||
'servers': fields.Nested(server_fields, as_list=True, required=True),
|
||||
})
|
||||
@api.cache.cached(timeout=1800, key_prefix=cache_key)
|
||||
@api.marshal_with(report_fields, code=200, description='Success')
|
||||
@api.doc(
|
||||
responses={
|
||||
403: 'Insufficient permissions',
|
||||
500: 'Internal failure',
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
"""Returns a global report about all the servers managed by Burp-UI
|
||||
|
||||
**GET** method provided by the webservice.
|
||||
|
||||
The *JSON* returned is:
|
||||
::
|
||||
|
||||
{
|
||||
"backups": [
|
||||
{
|
||||
"name": "AGENT1",
|
||||
"number": 49
|
||||
}
|
||||
],
|
||||
"servers": [
|
||||
{
|
||||
"name": "AGENT1",
|
||||
"stats": {
|
||||
"linux": 4,
|
||||
"total": 349705,
|
||||
"totsize": 119400711726,
|
||||
"unknown": 0,
|
||||
"windows": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
The output is filtered by the :mod:`burpui.misc.acl` module so that you
|
||||
only see stats about the clients/servers you are authorized to.
|
||||
|
||||
:returns: The *JSON* described above.
|
||||
"""
|
||||
r = {}
|
||||
if hasattr(api.bui.cli, 'servers'):
|
||||
restrict = []
|
||||
check = False
|
||||
if (api.bui.acl and not
|
||||
api.bui.acl.is_admin(current_user.get_id())):
|
||||
check = True
|
||||
restrict = api.bui.acl.servers(current_user.get_id())
|
||||
|
||||
stats = []
|
||||
|
||||
def get_servers_stats(serv, output, restrict, check, username):
|
||||
try:
|
||||
out = {
|
||||
'name': serv,
|
||||
'stats': {
|
||||
'total': 0,
|
||||
'totsize': 0,
|
||||
'linux': 0,
|
||||
'windows': 0,
|
||||
'unknown': 0,
|
||||
},
|
||||
'number': 0
|
||||
}
|
||||
if check and serv not in restrict:
|
||||
output.put(None)
|
||||
return
|
||||
clients = []
|
||||
if (api.bui.acl and not
|
||||
api.bui.acl.is_admin(username)):
|
||||
clients = [{'name': x} for x in api.bui.acl.clients(username, serv)]
|
||||
else:
|
||||
clients = api.bui.cli.get_all_clients(agent=serv)
|
||||
|
||||
j = api.bui.cli.get_clients_report(clients, serv)
|
||||
if 'clients' not in j or 'backups' not in j:
|
||||
output.put(None)
|
||||
return
|
||||
for stats in j['clients']:
|
||||
for key in ['total', 'totsize']:
|
||||
out['stats'][key] += stats['stats'][key]
|
||||
if stats['stats']['windows'] == 'true':
|
||||
out['stats']['windows'] += 1
|
||||
elif stats['stats']['windows'] == 'false':
|
||||
out['stats']['linux'] += 1
|
||||
else:
|
||||
out['stats']['unknown'] += 1
|
||||
for bkp in j['backups']:
|
||||
out['number'] += bkp['number']
|
||||
output.put(out)
|
||||
except BUIserverException as e:
|
||||
output.put(str(e))
|
||||
|
||||
stats = parallel_loop(get_servers_stats, api.bui.cli.servers, restrict, check, current_user.get_id())
|
||||
backups = []
|
||||
servers = []
|
||||
for serv in stats:
|
||||
backups.append({'name': serv['name'], 'number': serv['number']})
|
||||
servers.append({'name': serv['name'], 'stats': serv['stats']})
|
||||
|
||||
r['backups'] = backups
|
||||
r['servers'] = servers
|
||||
return r
|
||||
|
|
|
|||
|
|
@ -210,6 +210,12 @@ def servers():
|
|||
return render_template('servers.html', servers=True, overview=True)
|
||||
|
||||
|
||||
@view.route('/servers-report', methods=['GET'])
|
||||
@login_required
|
||||
def servers_report():
|
||||
return render_template('servers-report.html', servers=True, report=True)
|
||||
|
||||
|
||||
@view.route('/login', methods=['POST', 'GET'])
|
||||
def login():
|
||||
form = LoginForm(request.form)
|
||||
|
|
|
|||
|
|
@ -210,6 +210,10 @@ $('#input-client').typeahead({
|
|||
{% include "js/servers.js" %}
|
||||
{% endif -%}
|
||||
|
||||
{% if servers and report -%}
|
||||
{% include "js/servers-report.js" %}
|
||||
{% endif -%}
|
||||
|
||||
{% if clients and overview -%}
|
||||
{% include "js/clients.js" %}
|
||||
{% endif -%}
|
||||
|
|
@ -303,7 +307,7 @@ $(function() {
|
|||
{% if not login -%}
|
||||
_check_running();
|
||||
{% endif -%}
|
||||
{% if servers and overview -%}
|
||||
{% if servers -%}
|
||||
_servers();
|
||||
{% endif -%}
|
||||
});
|
||||
|
|
@ -332,7 +336,7 @@ $(function() {
|
|||
{% if client and is_client_func -%}
|
||||
_client();
|
||||
{% endif -%}
|
||||
{% if servers and overview -%}
|
||||
{% if servers -%}
|
||||
_servers();
|
||||
{% endif -%}
|
||||
|
||||
|
|
|
|||
94
burpui/templates/js/servers-report.js
Normal file
94
burpui/templates/js/servers-report.js
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
var _charts = [ 'repartition', 'size', 'files', 'backups' ];
|
||||
var _charts_obj = [];
|
||||
var initialized = false;
|
||||
|
||||
var _servers = function() {
|
||||
if (!initialized) {
|
||||
$.each(_charts, function(i, j) {
|
||||
tmp = nv.models.pieChart()
|
||||
.x(function(d) { return d.label })
|
||||
.y(function(d) { return d.value })
|
||||
.showLabels(true)
|
||||
.labelThreshold(.05)
|
||||
.labelType("percent")
|
||||
.valueFormat(d3.format('f'))
|
||||
.color(d3.scale.category20c().range())
|
||||
.labelThreshold(.05)
|
||||
.donutRatio(0.55)
|
||||
.donut(true)
|
||||
;
|
||||
|
||||
tmp.tooltip.contentGenerator(function(obj) { return '<h3>'+obj.data.label+'</h3><p>'+(j == 'size' ? _bytes_human_readable(obj.data.value, false) : obj.data.value)+'</p>'; });
|
||||
|
||||
_charts_obj.push({ 'key': 'chart_'+j, 'obj': tmp, 'data': [] });
|
||||
});
|
||||
}
|
||||
url = '{{ url_for("api.servers_report") }}';
|
||||
$.getJSON(url, function(d) {
|
||||
rep = [];
|
||||
size = [];
|
||||
files = [];
|
||||
rep = [];
|
||||
backups = [];
|
||||
$('.mycharts').each(function() {
|
||||
$(this).parent().show();
|
||||
});
|
||||
$.each(d['servers'], function(k, s) {
|
||||
size.push({'label': s.name, 'value': s.stats.totsize});
|
||||
files.push({'label': s.name, 'value': s.stats.total});
|
||||
if (s.stats.windows > 0) {
|
||||
rep.push({'label': s.name + ' - Windows', 'value': s.stats.windows});
|
||||
}
|
||||
if (s.stats.linux > 0) {
|
||||
rep.push({'label': s.name + ' - Unix/Linux', 'value': s.stats.linux});
|
||||
}
|
||||
if (s.stats.unknown > 0) {
|
||||
rep.push({'label': s.name + ' - Unknown', 'value': s.stats.unknown});
|
||||
}
|
||||
});
|
||||
$.each(d['backups'], function(k, b) {
|
||||
backups.push({'label': b.name, 'value': b.number});
|
||||
});
|
||||
$.each(_charts_obj, function(i, c) {
|
||||
switch (c.key) {
|
||||
case 'chart_repartition':
|
||||
c.data = rep;
|
||||
break;
|
||||
case 'chart_size':
|
||||
c.data = size;
|
||||
break;
|
||||
case 'chart_files':
|
||||
c.data = files;
|
||||
break;
|
||||
case 'chart_backups':
|
||||
c.data = backups;
|
||||
break;
|
||||
}
|
||||
});
|
||||
})
|
||||
.fail(myFail)
|
||||
.fail(function () {
|
||||
$('.mycharts').each(function() {
|
||||
$(this).parent().hide();
|
||||
});
|
||||
});
|
||||
_redraw();
|
||||
};
|
||||
|
||||
var _redraw = function() {
|
||||
$.each(_charts_obj, function(i, j) {
|
||||
nv.addGraph(function() {
|
||||
|
||||
d3.select('#'+j.key+' svg')
|
||||
.datum(j.data)
|
||||
.transition().duration(500)
|
||||
.call(j.obj);
|
||||
|
||||
nv.utils.windowResize(j.obj.update);
|
||||
|
||||
return j.obj;
|
||||
});
|
||||
});
|
||||
initialized = true;
|
||||
};
|
||||
38
burpui/templates/servers-report.html
Normal file
38
burpui/templates/servers-report.html
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
{% include "notifications.html" %}
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
|
||||
{% include "small_topbar.html" %}
|
||||
<ul class="breadcrumb" style="margin-bottom: 5px;">
|
||||
<li class="active">Home</li>
|
||||
</ul>
|
||||
<br />
|
||||
<h1 class="page-header">Global report</h1>
|
||||
<div class="row placeholders">
|
||||
<div class="col-xs-6 col-sm-6 placeholder">
|
||||
<div class="mycharts" id="chart_repartition" style="height: 350px;">
|
||||
<svg></svg>
|
||||
</div>
|
||||
<h4>Clients repartition per server</h4>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-6 placeholder">
|
||||
<div class="mycharts" id="chart_size" style="height: 350px;">
|
||||
<svg></svg>
|
||||
</div>
|
||||
<h4>Used space</h4>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-6 placeholder">
|
||||
<div class="mycharts" id="chart_files" style="height: 350px;">
|
||||
<svg></svg>
|
||||
</div>
|
||||
<h4>Total files</h4>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-6 placeholder">
|
||||
<div class="mycharts" id="chart_backups" style="height: 350px;">
|
||||
<svg></svg>
|
||||
</div>
|
||||
<h4>Number of backups</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
{% elif backup %}
|
||||
<a href="{{ url_for('view.backup_report', name=cname, backup=nbackup, server=server) }}"><span class="glyphicon glyphicon-stats"></span> Reports</a>
|
||||
{% elif servers %}
|
||||
<a href="{{ url_for('view.servers') }}"><span class="glyphicon glyphicon-stats"></span> Reports</a>
|
||||
<a href="{{ url_for('view.servers_report') }}"><span class="glyphicon glyphicon-stats"></span> Reports</a>
|
||||
{% else %}
|
||||
<a href="#"><span class="glyphicon glyphicon-stats"></span> Reports</a>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{% elif clients -%}
|
||||
<a class="btn btn-default {% if report %}active{% endif %}" href="{{ url_for('view.clients_report', server=server) }}"><span class="glyphicon glyphicon-stats"></span> Reports</a>
|
||||
{% elif servers -%}
|
||||
<a class="btn btn-default {% if report %}active{% endif %}" href="{{ url_for('view.servers') }}"><span class="glyphicon glyphicon-stats"></span> Reports</a>
|
||||
<a class="btn btn-default {% if report %}active{% endif %}" href="{{ url_for('view.servers_report') }}"><span class="glyphicon glyphicon-stats"></span> Reports</a>
|
||||
{% elif backup -%}
|
||||
<a class="btn btn-default {% if report %}active{% endif %}" href="{{ url_for('view.backup_report', name=cname, backup=nbackup, server=server) }}"><span class="glyphicon glyphicon-stats"></span> Reports</a>
|
||||
{% else -%}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue