mirror of
https://github.com/ziirish/burp-ui.git
synced 2026-05-21 06:45:24 -06:00
add: admin user sessions and user password changes
This commit is contained in:
parent
1ffc84ecf8
commit
55b2b5da3e
13 changed files with 507 additions and 26 deletions
|
|
@ -11,7 +11,7 @@
|
|||
<div class="form-container">
|
||||
<legend id="authentication">{{ _('Authentication') }}</legend>
|
||||
<div id="waiting-user-container" class="row">
|
||||
<span class="icon-refresh-animate glyphicon glyphicon-refresh"></span> {{ _('Loading, Please wait...') }}
|
||||
<i class="fa fa-spin fa-fw fa-refresh" aria-hidden="true"></i>{{ _('Loading, Please wait...') }}
|
||||
<br />
|
||||
<div class="progress progress-striped active">
|
||||
<div class="progress-bar progress-bar-info" style="width: 100%"></div>
|
||||
|
|
@ -46,14 +46,14 @@
|
|||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-warning">
|
||||
<span class="glyphicon glyphicon-question-sign"></span> {{ _('You are about to delete a user, are you sure?') }}
|
||||
<i class="fa fa-fw fa-2x fa-question-circle"></i>{{ _('You are about to delete a user, are you sure?') }}
|
||||
</div>
|
||||
<form class="form-horizontal" id="delete-user-form">
|
||||
<fieldset id="delete-details">
|
||||
</fieldset>
|
||||
</form>
|
||||
<div class="alert alert-danger" id="delete-confirm" style="display:none;">
|
||||
<strong><span class="glyphicon glyphicon-exclamation-sign"></span> {{ _('Warning!') }}</strong> {{ _('You are about to remove your <strong>current</strong> user.') }}
|
||||
<strong><i class="fa fa-fw fa-2x fa-exclamation-circle"></i>{{ _('Warning!') }}</strong> {{ _('You are about to remove your <strong>current</strong> user.') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
|
|
|||
34
burpui/templates/admin/authentication.html
Normal file
34
burpui/templates/admin/authentication.html
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
{% include "notifications.html" %}
|
||||
<div class="col-sm-12 col-md-12 main">
|
||||
{% include "small_topbar.html" %}
|
||||
<ul class="breadcrumb" style="margin-bottom: 5px;">
|
||||
<li><a href="{{ url_for('view.home') }}">{{ _('Home') }}</a></li>
|
||||
<li><a href="{{ url_for('view.admin') }}">{{ _('Administration') }}</a></li>
|
||||
<li class="active">{{ _('Authentication user %(backend)s/%(user)s', backend=backend, user=user) }}</li>
|
||||
</ul>
|
||||
<br />
|
||||
<div class="form-container">
|
||||
<legend id="authentication">{{ _('Authentication') }}</legend>
|
||||
<form class="form-horizontal well" name="changePassword" method="POST" action="{{ url_for('api.auth_users', name=user) }}">
|
||||
<fieldset>
|
||||
<legend>{{ _('Password of user %(user)s on %(backend)s', user=user, backend=backend) }}</legend>
|
||||
<input type="hidden" name="backend" value="{{ backend }}">
|
||||
<div class="form-group">
|
||||
<label for="inputPassword" class="col-lg-2 control-label">{{ _('New password') }}</label>
|
||||
<div class="col-lg-3">
|
||||
<input class="form-control input-sm" name="password" id="inputPassword" placeholder="{{ _('New password') }}" type="password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-10 col-lg-offset-2">
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-fw fa-floppy-o" aria-hidden="true"></i>{{ _('Save') }}</button>
|
||||
<button type="reset" class="btn btn-default"><i class="fa fa-fw fa-undo" aria-hidden="true"></i>{{ _('Cancel') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
55
burpui/templates/admin/sessions.html
Normal file
55
burpui/templates/admin/sessions.html
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
{% include "notifications.html" %}
|
||||
<div class="col-sm-12 col-md-12 main">
|
||||
{% include "small_topbar.html" %}
|
||||
<ul class="breadcrumb" style="margin-bottom: 5px;">
|
||||
<li><a href="{{ url_for('view.home') }}">{{ _('Home') }}</a></li>
|
||||
<li><a href="{{ url_for('view.admin') }}">{{ _('Administration') }}</a></li>
|
||||
<li class="active">{{ _('%(user)s\'s sessions', user=user) }}</li>
|
||||
</ul>
|
||||
<br />
|
||||
<div class="form-container">
|
||||
<legend id="authentication">{{ _('Sessions') }}</legend>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover nowrap" id="table-sessions" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('IP') }}</th>
|
||||
<th>{{ _('Last seen') }}</th>
|
||||
<th>{{ _('User-Agent') }}</th>
|
||||
<th>{{ _('API Login') }}</th>
|
||||
<th>{{ _('Current session') }}</th>
|
||||
<th>{{ _('Expire') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="confirmation-modal" class="modal fade">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">{{ _('Confirmation') }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-warning">
|
||||
<i class="fa fa-2x fa-fw fa-question-circle"></i>{{ _('You are about to revoke a session, are you sure?') }}
|
||||
<pre id="session-details"></pre>
|
||||
</div>
|
||||
<div class="alert alert-danger" id="error-confirm" style="display:none;">
|
||||
<strong><i class="fa fa-2x fa-fw fa-exclamation-circle"></i> {{ _('Warning!') }}</strong> {{ _('You are about to revoke your <strong>current</strong> session.') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ _('Cancel') }}</button>
|
||||
<button type="button" class="btn btn-info" data-dismiss="modal" id="perform-revoke">{{ _('Confirm') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -90,20 +90,20 @@ var notif = function(type, message, timeout) {
|
|||
switch(type) {
|
||||
case NOTIF_SUCCESS:
|
||||
t = 'success';
|
||||
i = '<span class="glyphicon glyphicon-ok-sign"></span> ';
|
||||
i = '<i class="fa fa-fw fa-check-circle" aria-hidden="true"></i> ';
|
||||
break;
|
||||
case NOTIF_WARNING:
|
||||
t = 'warning';
|
||||
i = '<span class="glyphicon glyphicon-question-sign"></span> ';
|
||||
i = '<i class="fa fa-fw fa-question-circle" aria-hidden="true"></i> ';
|
||||
break;
|
||||
case NOTIF_ERROR:
|
||||
t = 'danger';
|
||||
i = '<span class="glyphicon glyphicon-exclamation-sign"></span> ';
|
||||
i = '<i class="fa fa-fw fa-exclamation-circle" aria-hidden="true"></i> ';
|
||||
break;
|
||||
case NOTIF_INFO:
|
||||
default:
|
||||
t = 'info';
|
||||
i = '<span class="glyphicon glyphicon-info-sign"></span> ';
|
||||
i = '<i class="fa fa-fw fa-info-circle" aria-hidden="true"></i> ';
|
||||
break;
|
||||
}
|
||||
e = $('<div class="alert alert-dismissable alert-'+t+'">'+
|
||||
|
|
@ -359,7 +359,15 @@ $('#input-client').typeahead({
|
|||
{% endif -%}
|
||||
|
||||
{% if admin -%}
|
||||
{% if authentication -%}
|
||||
{% include "js/admin/authentication.js" %}
|
||||
{% elif authorization -%}
|
||||
{% include "js/admin/authorization.js" %}
|
||||
{% elif sessions -%}
|
||||
{% include "js/admin/sessions.js" %}
|
||||
{% else -%}
|
||||
{% include "js/admin.js" %}
|
||||
{% endif -%}
|
||||
{% endif -%}
|
||||
|
||||
var _fit_menu = function() {
|
||||
|
|
@ -440,25 +448,25 @@ $(function() {
|
|||
*/
|
||||
$('#refresh').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
{% if clients -%}
|
||||
AJAX_CACHE = false;
|
||||
{% if clients -%}
|
||||
_clients();
|
||||
{% endif -%}
|
||||
{% if client and is_client_func -%}
|
||||
AJAX_CACHE = false;
|
||||
_client();
|
||||
{% endif -%}
|
||||
{% if not login -%}
|
||||
_check_running();
|
||||
{% endif -%}
|
||||
{% if servers -%}
|
||||
AJAX_CACHE = false;
|
||||
_servers();
|
||||
{% endif -%}
|
||||
{% if me -%}
|
||||
AJAX_CACHE = false;
|
||||
_sessions();
|
||||
{% endif -%}
|
||||
{% if admin -%}
|
||||
_admin();
|
||||
{% endif -%}
|
||||
});
|
||||
|
||||
/***
|
||||
|
|
@ -502,6 +510,9 @@ $(function() {
|
|||
{% if me -%}
|
||||
_sessions();
|
||||
{% endif -%}
|
||||
{% if admin -%}
|
||||
_admin();
|
||||
{% endif -%}
|
||||
|
||||
{% if not report and not login -%}
|
||||
/***
|
||||
|
|
|
|||
|
|
@ -112,8 +112,10 @@ app.controller('AdminCtrl', ['$scope', '$http', '$scrollspy', 'DTOptionsBuilder'
|
|||
|
||||
var _me = undefined;
|
||||
var _users = {};
|
||||
var _auth_backends = {};
|
||||
var _users_array = [];
|
||||
var __promises = [];
|
||||
var __globals_promises = [];
|
||||
|
||||
var _users_table = $('#table-users').DataTable( {
|
||||
{{ macros.translate_datatable() }}
|
||||
|
|
@ -181,15 +183,22 @@ var _users_table = $('#table-users').DataTable( {
|
|||
{
|
||||
data: null,
|
||||
render: function ( data, type, row ) {
|
||||
return '<button data-member="'+data.id+'" class="btn btn-xs btn-danger btn-delete-user" title="{{ _("Remove") }}"><i class="fa fa-trash" aria-hidden="true"></i></button> <button data-member="'+data.id+'" class="btn btn-xs btn-info btn-edit-user" title="{{ _("Edit") }}"><i class="fa fa-pencil" aria-hidden="true"></i></button>';
|
||||
return '<button data-member="'+data.id+'" class="btn btn-xs btn-danger btn-delete-user" title="{{ _("Remove") }}"><i class="fa fa-trash" aria-hidden="true"></i></button> <button data-member="'+data.id+'" class="btn btn-xs btn-info btn-edit-user" title="{{ _("Edit") }}"><i class="fa fa-pencil" aria-hidden="true"></i></button> <button data-member="'+data.id+'" class="btn btn-xs btn-warning btn-sessions-user" title="{{ _("Sessions") }}"><i class="fa fa-list-alt" aria-hidden="true"></i></button>';
|
||||
}
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
$.getJSON('{{ url_for("api.admin_me") }}').done(function (data) {
|
||||
var g = $.getJSON('{{ url_for("api.admin_me") }}').done(function (data) {
|
||||
_me = data;
|
||||
});
|
||||
__globals_promises.push(g);
|
||||
g = $.getJSON('{{ url_for("api.auth_backends") }}').done(function (data) {
|
||||
$.each(data, function(i, back) {
|
||||
_auth_backends[back.name] = back;
|
||||
});
|
||||
});
|
||||
__globals_promises.push(g);
|
||||
|
||||
var _authentication = function() {
|
||||
$('#waiting-user-container').show();
|
||||
|
|
@ -260,7 +269,6 @@ var _authentication = function() {
|
|||
var _admin = function() {
|
||||
_authentication();
|
||||
};
|
||||
_admin();
|
||||
|
||||
{{ macros.page_length('#table-list-clients') }}
|
||||
{{ macros.page_length('#table-list-templates') }}
|
||||
|
|
@ -273,13 +281,19 @@ $( document ).ready(function () {
|
|||
});
|
||||
|
||||
/* Delete user */
|
||||
var _remove_selected = 0;
|
||||
$( document ).on('click', '.btn-delete-user', function(e) {
|
||||
var user_id = $(this).data('member');
|
||||
var user = _users[user_id];
|
||||
var content = '<legend>{{ _("Please select the backend(s) from which to remove the user from:") }}</legend>';
|
||||
var content = '<legend>{{ _("Please select the backend(s) from which to remove the user:") }}</legend>';
|
||||
$.each(user['backends'], function(i, back) {
|
||||
content += '<div class="form-group"><input type="checkbox" name="user_backend" id="user_backend_'+i+'" data-id="'+user_id+'" data-backend="'+back+'"><label for="user_backend_'+i+'"> '+back+'</label></div>';
|
||||
var disabled_legend = '{{ _("The backend does not support user removal") }}';
|
||||
var disabled = 'disabled title="'+disabled_legend+'"';
|
||||
var is_enabled = _auth_backends[back]['del'];
|
||||
content += '<div class="checkbox"><label><input type="checkbox" name="user_backend" data-id="'+user_id+'" data-backend="'+back+'" '+(is_enabled?'':disabled)+'>'+back+(is_enabled?'':' <em>('+disabled_legend+')</em>')+'</label></div>';
|
||||
});
|
||||
/* disable submit button while we did not select a backend */
|
||||
$('#perform-delete').prop('disabled', true);
|
||||
$('#delete-details').html(content);
|
||||
$('#delete-user-modal').modal('toggle');
|
||||
});
|
||||
|
|
@ -293,6 +307,16 @@ $( document ).on('change', 'input[name=user_backend]', function(e) {
|
|||
$('#delete-confirm').hide();
|
||||
}
|
||||
}
|
||||
if ($(this).is(':checked')) {
|
||||
_remove_selected++;
|
||||
} else {
|
||||
_remove_selected--;
|
||||
}
|
||||
if (_remove_selected > 0) {
|
||||
$('#perform-delete').prop('disabled', false);
|
||||
} else {
|
||||
$('#perform-delete').prop('disabled', true);
|
||||
}
|
||||
});
|
||||
$('#perform-delete').on('click', function(e) {
|
||||
$.each($('input[name=user_backend]'), function(i, elmt) {
|
||||
|
|
@ -315,11 +339,27 @@ $( document ).on('click', '.btn-edit-user', function(e) {
|
|||
var user = _users[user_id];
|
||||
var content = '<legend>{{ _("Please select the backend from which to edit the user from:") }}</legend>';
|
||||
content += '<div class="form-group"><label for="edit_backend" class="col-lg-2 control-label">Backend</label>';
|
||||
content += '<div class="col-lg-10"><select class="form-control" id="edit_backend" name="edit_backend">';
|
||||
content += '<div class="col-lg-10"><select class="form-control" id="edit_backend" name="edit_backend" data-id="'+user_id+'"><option disabled selected>'+'{{ _("Please select a backend") }}'+'</option>';
|
||||
$.each(user['backends'], function(i, back) {
|
||||
content += '<option>'+back+'</option>';
|
||||
is_enabled = _auth_backends[back]['mod'];
|
||||
content += '<option'+(is_enabled?'':' disabled')+'>'+back+'</option>';
|
||||
});
|
||||
content += '</select></div></div>';
|
||||
$('#perform-edit').prop('disabled', true);
|
||||
$('#edit-details').html(content);
|
||||
$('#edit-user-modal').modal('toggle');
|
||||
});
|
||||
$( document ).on('change', '#edit_backend', function(e) {
|
||||
if ($('#edit_backend option:selected').text() != '{{ _("Please select a backend") }}') {
|
||||
$('#perform-edit').prop('disabled', false);
|
||||
}
|
||||
});
|
||||
$('#perform-edit').on('click', function(e) {
|
||||
location = "{{ url_for('view.admin_authentication', user='') }}"+$('#edit_backend').data('id')+'?backend='+$('#edit_backend option:selected').text();
|
||||
});
|
||||
|
||||
/* user sessions */
|
||||
$( document ).on('click', '.btn-sessions-user', function(e) {
|
||||
var user_id = $(this).data('member');
|
||||
location = "{{ url_for('view.admin_sessions', user='') }}"+user_id;
|
||||
});
|
||||
|
|
|
|||
42
burpui/templates/js/admin/authentication.js
Normal file
42
burpui/templates/js/admin/authentication.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
var _admin = function() {
|
||||
// do nothing
|
||||
return true;
|
||||
};
|
||||
|
||||
$('form[name=changePassword]').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
var form = $(e.target);
|
||||
submit = form.find('button[type="submit"]');
|
||||
sav = submit.text();
|
||||
submit.html('<i class="fa fa-fw fa-spinner fa-pulse" aria-hidden="true"></i> {{ _("Saving...") }}');
|
||||
submit.attr('disabled', true);
|
||||
/* submit the data */
|
||||
$.ajax({
|
||||
url: form.attr('action'),
|
||||
type: 'POST',
|
||||
data: {
|
||||
'password': $('input[name=password]').val(),
|
||||
'backend': "{{ backend }}"
|
||||
},
|
||||
headers: { 'X-From-UI': true },
|
||||
})
|
||||
.fail(myFail)
|
||||
.done(function(data) {
|
||||
notifAll(data);
|
||||
})
|
||||
.always(function() {
|
||||
/* reset the submit button state */
|
||||
submit.text(sav);
|
||||
submit.attr('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
/* placeholder */
|
||||
var app = angular.module('MainApp', ['ngSanitize', 'ui.select', 'mgcrea.ngStrap', 'datatables']);
|
||||
|
||||
app.config(function(uiSelectConfig) {
|
||||
uiSelectConfig.theme = 'bootstrap';
|
||||
});
|
||||
|
||||
app.controller('AdminCtrl', ['$scope', '$http', '$scrollspy', 'DTOptionsBuilder', 'DTColumnDefBuilder', function($scope, $http, $scrollspy, DTOptionsBuilder, DTColumnDefBuilder) {
|
||||
}]);
|
||||
284
burpui/templates/js/admin/sessions.js
Normal file
284
burpui/templates/js/admin/sessions.js
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
/***
|
||||
* The session part is handled by datatables
|
||||
* the resulting JSON looks like:
|
||||
* [
|
||||
* {
|
||||
* "api": false,
|
||||
* "current": false,
|
||||
* "expire": "1970-01-01T00:00:00+01:00",
|
||||
* "ip": "::ffff:10.0.0.100",
|
||||
* "permanent": false,
|
||||
* "timestamp": "2016-09-01T15:28:59.222028+02:00",
|
||||
* "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0",
|
||||
* "uuid": "feaac9c7-6f61-4103-9536-d65aba335892"
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
{% import 'macros.html' as macros %}
|
||||
|
||||
var _cache_id = _EXTRA;
|
||||
|
||||
{{ macros.timestamp_filter() }}
|
||||
|
||||
var _sessions_table = $('#table-sessions').DataTable( {
|
||||
{{ macros.translate_datatable() }}
|
||||
{{ macros.get_page_length() }}
|
||||
responsive: true,
|
||||
processing: true,
|
||||
fixedHeader: true,
|
||||
select: {
|
||||
style: 'os',
|
||||
},
|
||||
ajax: {
|
||||
url: '{{ url_for("api.other_sessions", user=user) }}',
|
||||
headers: { 'X-From-UI': true },
|
||||
cache: AJAX_CACHE,
|
||||
error: myFail,
|
||||
data: function (request) {
|
||||
request._extra = _cache_id;
|
||||
},
|
||||
dataSrc: function (data) {
|
||||
return data;
|
||||
}
|
||||
},
|
||||
dom: "<'row'<'col-sm-6'l><'col-sm-6'f>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'><'col-sm-7'p>>" +
|
||||
"<'row'<'col-sm-5'i>>" +
|
||||
"<'row'B>",
|
||||
buttons: [
|
||||
{
|
||||
text: '<i class="fa fa-check-square-o" aria-hidden="true"></i>',
|
||||
titleAttr: '{{ _("Select all") }}',
|
||||
extend: 'selectAll',
|
||||
},
|
||||
{
|
||||
text: '<i class="fa fa-filter" aria-hidden="true"></i> <i class="fa fa-check-square-o" aria-hidden="true"></i>',
|
||||
titleAttr: '{{ _("Select all filtered") }}',
|
||||
extend: 'selectAll',
|
||||
action: function ( e, dt, node, config ) {
|
||||
dt.rows( { search: 'applied' } ).select();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '<i class="fa fa-square-o" aria-hidden="true"></i>',
|
||||
titleAttr: '{{ _("Deselect all") }}',
|
||||
extend: 'selectNone',
|
||||
},
|
||||
{
|
||||
text : '<i class="fa fa-filter" aria-hidden="true"></i> <i class="fa fa-square-o" aria-hidden="true"></i>',
|
||||
titleAttr: '{{ _("Deselect all filtered") }}',
|
||||
extend: 'selectNone',
|
||||
action: function ( e, dt, node, config ) {
|
||||
dt.rows( { search: 'applied' } ).deselect();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '<span aria-label="{{ _("Revoke") }}"><i class="fa fa-trash-o" aria-hidden="true"></i></span>',
|
||||
titleAttr: '{{ _("Revoke selected") }}',
|
||||
className: 'btn-danger',
|
||||
action: function ( e, dt, node, config ) {
|
||||
var rows = dt.rows( { selected: true } ).data();
|
||||
var current = false;
|
||||
var output = '';
|
||||
$.each(rows, function(i, row) {
|
||||
output += JSON.stringify(row, null, 4)+'\n';
|
||||
if (row.current) {
|
||||
current = true;
|
||||
}
|
||||
});
|
||||
if (current) {
|
||||
$('#error-confirm').show();
|
||||
} else {
|
||||
$('#error-confirm').hide();
|
||||
}
|
||||
$('#session-details').empty().text(output);
|
||||
$('#perform-revoke').data('multi', true);
|
||||
$('#confirmation-modal').modal('toggle');
|
||||
},
|
||||
enabled: false
|
||||
}
|
||||
],
|
||||
order: [[1, 'desc']],
|
||||
rowId: 'uuid',
|
||||
columns: [
|
||||
{ data: 'ip' },
|
||||
{
|
||||
data: 'timestamp',
|
||||
type: 'timestamp',
|
||||
render: function( data, type, row ) {
|
||||
if (type === 'filter') {
|
||||
return data;
|
||||
}
|
||||
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).subtract(3, 'seconds').fromNow()+'</span>';
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'ua',
|
||||
render: function( data, type, row ) {
|
||||
if (type === 'filter' || type === 'sort') {
|
||||
return data;
|
||||
}
|
||||
ret = '';
|
||||
// Browser version
|
||||
if (data.lastIndexOf('MSIE') > 0 || data.lastIndexOf('Trident') > 0) {
|
||||
ret += '<i class="fa fa-internet-explorer" aria-hidden="true"></i>';
|
||||
} else if (data.lastIndexOf('Edge') > 0) {
|
||||
ret += '<i class="fa fa-edge" aria-hidden="true"></i>';
|
||||
} else if (/Firefox[\/\s](\d+\.\d+)/.test(data)) {
|
||||
ret += '<i class="fa fa-firefox" aria-hidden="true"></i>';
|
||||
} else if (data.lastIndexOf('Chrome/') > 0) {
|
||||
ret += '<i class="fa fa-chrome" aria-hidden="true"></i>';
|
||||
} else if (data.lastIndexOf('Safari/') > 0) {
|
||||
ret += '<i class="fa fa-safari" aria-hidden="true"></i>';
|
||||
} else {
|
||||
ret += '<i class="fa fa-question" aria-hidden="true"></i>';
|
||||
}
|
||||
// Optionally add OS version
|
||||
if (data.lastIndexOf('Android') > 0) {
|
||||
ret += ' <i class="fa fa-android" aria-hidden="true"></i>';
|
||||
} else if (data.lastIndexOf('iPhone') > 0 || data.lastIndexOf('iPad') > 0 || data.lastIndexOf('Macintosh') > 0) {
|
||||
ret += ' <i class="fa fa-apple" aria-hidden="true"></i>';
|
||||
if (data.lastIndexOf('iPhone') > 0) {
|
||||
ret += ' <i class="fa fa-mobile" aria-hidden="true"></i>';
|
||||
} else if (data.lastIndexOf('iPad') > 0) {
|
||||
ret += ' <i class="fa fa-tablet" aria-hidden="true"></i>';
|
||||
}
|
||||
} else if (data.lastIndexOf('Linux') > 0) {
|
||||
ret += ' <i class="fa fa-linux" aria-hidden="true"></i>';
|
||||
} else if (data.lastIndexOf('Windows') > 0) {
|
||||
ret += ' <i class="fa fa-windows" aria-hidden="true"></i>';
|
||||
}
|
||||
return '<span data-toggle="tooltip" title="'+data+'">'+ret+'</span>';
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'api',
|
||||
render: function( data, type, row ) {
|
||||
return '<i class="fa fa-'+(data?'check':'remove')+'" aria-hidden="true"></i>';
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'current',
|
||||
render: function( data, type, row ) {
|
||||
return '<i class="fa fa-'+(data?'check':'remove')+'" aria-hidden="true"></i>';
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'expire',
|
||||
type: 'timestamp',
|
||||
render: function( data, type, row ) {
|
||||
if (type === 'filter') {
|
||||
return data;
|
||||
}
|
||||
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).fromNow()+'</span>';
|
||||
}
|
||||
},
|
||||
],
|
||||
});
|
||||
var first = true;
|
||||
|
||||
var _sessions = function() {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
if (!AJAX_CACHE) {
|
||||
_cache_id = new Date().getTime();
|
||||
}
|
||||
_sessions_table.ajax.reload( null, false );
|
||||
AJAX_CACHE = true;
|
||||
}
|
||||
};
|
||||
|
||||
var _events_callback = function() {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$('[data-toggle="revoke-session"]').on('click', function(e) {
|
||||
$me = $(this);
|
||||
if ($me.data('current')) {
|
||||
$('#error-confirm').show();
|
||||
} else {
|
||||
$('#error-confirm').hide();
|
||||
}
|
||||
$('#session-details').empty().text(unescape($me.data('misc')));
|
||||
$('#perform-revoke').data('id', $me.data('id'));
|
||||
$('#perform-revoke').data('current', $me.data('current'));
|
||||
$('#confirmation-modal').modal('toggle');
|
||||
});
|
||||
};
|
||||
|
||||
var select_event = function( e, dt, type, indexes ) {
|
||||
var selectedRows = _sessions_table.rows( { selected: true } ).count();
|
||||
_sessions_table.buttons( [3, 4] ).enable( selectedRows > 0 );
|
||||
};
|
||||
|
||||
_sessions_table.on('select.dt', select_event);
|
||||
_sessions_table.on('deselect.dt', select_event);
|
||||
_sessions_table.on('draw.dt', _events_callback);
|
||||
_sessions_table.on('responsive-display.dt', function ( e, datatable, row, showHide, update ) {
|
||||
_events_callback();
|
||||
});
|
||||
{{ macros.page_length('#table-sessions') }}
|
||||
|
||||
var revoke_session = function(id, refresh) {
|
||||
return $.ajax({
|
||||
url: '{{ url_for("api.user_sessions") }}/'+id,
|
||||
headers: { 'X-From-UI': true },
|
||||
type: 'DELETE'
|
||||
}).done(function(data) {
|
||||
notifAll(data);
|
||||
if (refresh && data[0] == NOTIF_SUCCESS) {
|
||||
AJAX_CACHE = false;
|
||||
_sessions();
|
||||
}
|
||||
}).fail(myFail);
|
||||
};
|
||||
|
||||
$('#perform-revoke').on('click', function(e) {
|
||||
$me = $(this);
|
||||
if ($me.data('multi')) {
|
||||
var rows = _sessions_table.rows( { selected: true } ).data();
|
||||
var current = undefined;
|
||||
var requests = [];
|
||||
var last;
|
||||
$.each(rows, function(i, row) {
|
||||
if (row.current) {
|
||||
current = row;
|
||||
return;
|
||||
}
|
||||
last = revoke_session(row.uuid, false);
|
||||
requests.push(last);
|
||||
});
|
||||
if (current) {
|
||||
$.when.apply( $, requests ).done(function() {
|
||||
window.location = '{{ url_for("view.logout") }}';
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
$.when.apply( $, requests ).done(function() {
|
||||
AJAX_CACHE = false;
|
||||
_sessions();
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($me.data('current')) {
|
||||
window.location = '{{ url_for("view.logout") }}';
|
||||
return;
|
||||
}
|
||||
revoke_session($me.data('id'), true);
|
||||
});
|
||||
|
||||
var _admin = function() {
|
||||
_sessions();
|
||||
};
|
||||
|
||||
|
||||
/* placeholder */
|
||||
var app = angular.module('MainApp', ['ngSanitize', 'ui.select', 'mgcrea.ngStrap', 'datatables']);
|
||||
|
||||
app.config(function(uiSelectConfig) {
|
||||
uiSelectConfig.theme = 'bootstrap';
|
||||
});
|
||||
|
||||
app.controller('AdminCtrl', ['$scope', '$http', '$scrollspy', 'DTOptionsBuilder', 'DTColumnDefBuilder', function($scope, $http, $scrollspy, DTOptionsBuilder, DTColumnDefBuilder) {
|
||||
}]);
|
||||
|
|
@ -84,7 +84,7 @@ app.controller('UserCtrl', function($timeout, $scope, $http, $scrollspy) {
|
|||
var form = $(e.target);
|
||||
submit = form.find('button[type="submit"]');
|
||||
sav = submit.text();
|
||||
submit.text('Saving...');
|
||||
submit.html('<i class="fa fa-fw fa-spinner fa-pulse" aria-hidden="true"></i> {{ _("Saving...") }}');
|
||||
submit.attr('disabled', true);
|
||||
/* submit the data */
|
||||
$.ajax({
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
<link href="{{ url_for('bower.static', filename='datatables.net-bs/css/dataTables.bootstrap.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('bower.static', filename='datatables.net-responsive-bs/css/responsive.bootstrap.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('bower.static', filename='datatables.net-fixedheader-bs/css/fixedHeader.bootstrap.min.css') }}" rel="stylesheet">
|
||||
{% if me -%}
|
||||
{% if me or sessions -%}
|
||||
<link href="{{ url_for('bower.static', filename='datatables.net-select-bs/css/select.bootstrap.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('bower.static', filename='datatables.net-buttons-bs/css/buttons.bootstrap.min.css') }}" rel="stylesheet">
|
||||
{% endif -%}
|
||||
|
|
@ -50,8 +50,10 @@
|
|||
|
||||
<div class="container-fluid">
|
||||
<div class="row" {% if ng_controller %}ng-controller="{{ ng_controller }}"{% endif %}>
|
||||
{% if not live and not login and not about and not authentication and not authorization -%}
|
||||
{% if not live and not login and not about and not authentication and not authorization and not sessions -%}
|
||||
{% include "sidebar.html" %}
|
||||
{% else -%}
|
||||
{% set no_sidebar = True %}
|
||||
{% endif -%}
|
||||
<span id="_top"></span>
|
||||
{% block body %}{% endblock %}
|
||||
|
|
@ -89,7 +91,7 @@
|
|||
<script src="{{ url_for('bower.static', filename='datatables.net-responsive/js/dataTables.responsive.min.js') }}"></script>
|
||||
<script src="{{ url_for('bower.static', filename='datatables.net-responsive-bs/js/responsive.bootstrap.min.js') }}"></script>
|
||||
<script src="{{ url_for('bower.static', filename='datatables.net-fixedheader/js/dataTables.fixedHeader.min.js') }}"></script>
|
||||
{% if me -%}
|
||||
{% if me or sessions -%}
|
||||
<script src="{{ url_for('bower.static', filename='datatables.net-select/js/dataTables.select.min.js') }}"></script>
|
||||
<script src="{{ url_for('bower.static', filename='datatables.net-buttons/js/dataTables.buttons.min.js') }}"></script>
|
||||
<script src="{{ url_for('bower.static', filename='datatables.net-buttons-bs/js/buttons.bootstrap.min.js') }}"></script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div style="position: fixed; width: 100%; z-index: 9999;">
|
||||
<div {% if not login %}class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2"{% endif %}>
|
||||
<div {% if not no_sidebar %}class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2"{% endif %}>
|
||||
<div id="bui-notifications">
|
||||
{% with messages = get_flashed_messages(with_categories=true) -%}
|
||||
{% if messages -%}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue