[Web] Show users the last known connections for SASL authentication
[Web] Feature: Log SASL authenticationmaster
parent
51e3521aac
commit
2d55b54904
|
@ -132,7 +132,7 @@ table.footable > tbody > tr.footable-empty > th {
|
||||||
content: "\f52a";
|
content: "\f52a";
|
||||||
}
|
}
|
||||||
.fooicon-remove:before {
|
.fooicon-remove:before {
|
||||||
content: "\f64f";
|
content: "\f62a";
|
||||||
}
|
}
|
||||||
.fooicon-sort:before {
|
.fooicon-sort:before {
|
||||||
content: "\f3c6";
|
content: "\f3c6";
|
||||||
|
@ -147,7 +147,7 @@ table.footable > tbody > tr.footable-empty > th {
|
||||||
content: "\f4c9";
|
content: "\f4c9";
|
||||||
}
|
}
|
||||||
.fooicon-trash:before {
|
.fooicon-trash:before {
|
||||||
content: "\f64f";
|
content: "\f62a";
|
||||||
}
|
}
|
||||||
.fooicon-eye-close:before {
|
.fooicon-eye-close:before {
|
||||||
content: "\f33f";
|
content: "\f33f";
|
||||||
|
|
|
@ -111,4 +111,12 @@ border-bottom-width: 3px;
|
||||||
padding: .1em .5em .1em;
|
padding: .1em .5em .1em;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.clear-last-logins {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size:90%;
|
||||||
|
font-style: italic;
|
||||||
|
color: #158cba;
|
||||||
|
user-select:none;
|
||||||
}
|
}
|
|
@ -29,6 +29,7 @@ $xmpp_status = xmpp_control('status');
|
||||||
<li role="presentation"><a href="#tab-rspamd-history" aria-controls="tab-rspamd-history" role="tab" data-toggle="tab">Rspamd</a></li>
|
<li role="presentation"><a href="#tab-rspamd-history" aria-controls="tab-rspamd-history" role="tab" data-toggle="tab">Rspamd</a></li>
|
||||||
<li role="presentation"><span class="dropdown-desc"><?=$lang['debug']['static_logs'];?></span></li>
|
<li role="presentation"><span class="dropdown-desc"><?=$lang['debug']['static_logs'];?></span></li>
|
||||||
<li role="presentation"><a href="#tab-ui" aria-controls="tab-ui" role="tab" data-toggle="tab">mailcow UI</a></li>
|
<li role="presentation"><a href="#tab-ui" aria-controls="tab-ui" role="tab" data-toggle="tab">mailcow UI</a></li>
|
||||||
|
<li role="presentation"><a href="#tab-sasl" aria-controls="tab-sasl" role="tab" data-toggle="tab">SASL</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -217,6 +218,23 @@ $xmpp_status = xmpp_control('status');
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-sasl">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">SASL <span class="badge badge-info table-lines"></span>
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="sasl_log_table" data-table="sasl_logs" data-log-url="ui" data-nrows="1000">+ 1000</button>
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="sasl_log_table" data-table="sasl_logs" data-log-url="ui" data-nrows="10000">+ 10000</button>
|
||||||
|
<button class="btn btn-xs btn-default refresh_table" data-draw="draw_sasl_logs" data-table="sasl_logs"><?=$lang['admin']['refresh'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-condensed" id="sasl_logs"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-dovecot-logs">
|
<div role="tabpanel" class="tab-pane" id="tab-dovecot-logs">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Dovecot <span class="badge badge-info table-lines"></span>
|
<div class="panel-heading">Dovecot <span class="badge badge-info table-lines"></span>
|
||||||
|
|
|
@ -251,20 +251,60 @@ function password_check($password1, $password2) {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
function last_login($user) {
|
function last_login($action, $username) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
$stmt = $pdo->prepare('SELECT `remote`, `time` FROM `logs`
|
switch ($action) {
|
||||||
WHERE JSON_EXTRACT(`call`, "$[0]") = "check_login"
|
case 'get':
|
||||||
AND JSON_EXTRACT(`call`, "$[1]") = :user
|
if (filter_var($username, FILTER_VALIDATE_EMAIL) && hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
|
||||||
AND `type` = "success" ORDER BY `time` DESC LIMIT 1');
|
$stmt = $pdo->prepare('SELECT `real_rip`, MAX(`datetime`) as `datetime`, `service` FROM `sasl_logs`
|
||||||
$stmt->execute(array(':user' => $user));
|
WHERE `username` = :username
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
AND `success` = 1
|
||||||
if (!empty($row)) {
|
GROUP BY `real_rip`, `service`
|
||||||
return $row;
|
ORDER BY `datetime` DESC
|
||||||
}
|
LIMIT 5;');
|
||||||
else {
|
$stmt->execute(array(':username' => $username));
|
||||||
return false;
|
$sasl = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
foreach ($sasl as $k => $v) {
|
||||||
|
if (!filter_var($sasl[$k]['real_rip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
|
||||||
|
$sasl[$k]['real_rip'] = 'Web/EAS/Internal (' . $sasl[$k]['real_rip'] . ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$sasl = array();
|
||||||
|
}
|
||||||
|
if ($_SESSION['mailcow_cc_role'] == "admin" || $username == $_SESSION['mailcow_cc_username']) {
|
||||||
|
$stmt = $pdo->prepare('SELECT `remote`, `time` FROM `logs`
|
||||||
|
WHERE JSON_EXTRACT(`call`, "$[0]") = "check_login"
|
||||||
|
AND JSON_EXTRACT(`call`, "$[1]") = :username
|
||||||
|
AND `type` = "success" ORDER BY `time` DESC LIMIT 1 OFFSET 1');
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
$ui = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$ui = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array('ui' => $ui, 'sasl' => $sasl);
|
||||||
|
break;
|
||||||
|
case 'reset':
|
||||||
|
if (filter_var($username, FILTER_VALIDATE_EMAIL) && hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
|
||||||
|
$stmt = $pdo->prepare('DELETE FROM `sasl_logs`
|
||||||
|
WHERE `username` = :username
|
||||||
|
AND `success` = 1;');
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
}
|
||||||
|
if ($_SESSION['mailcow_cc_role'] == "admin" || $username == $_SESSION['mailcow_cc_username']) {
|
||||||
|
$stmt = $pdo->prepare('DELETE FROM `logs`
|
||||||
|
WHERE JSON_EXTRACT(`call`, "$[0]") = "check_login"
|
||||||
|
AND JSON_EXTRACT(`call`, "$[1]") = :username
|
||||||
|
AND `type` = "success"');
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
function flush_memcached() {
|
function flush_memcached() {
|
||||||
try {
|
try {
|
||||||
|
@ -1862,6 +1902,26 @@ function get_logs($application, $lines = false) {
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($application == "sasl") {
|
||||||
|
if (isset($from) && isset($to)) {
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM `sasl_logs` ORDER BY `id` DESC LIMIT :from, :to");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':from' => $from - 1,
|
||||||
|
':to' => $to
|
||||||
|
));
|
||||||
|
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM `sasl_logs` ORDER BY `id` DESC LIMIT :lines");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':lines' => $lines + 1,
|
||||||
|
));
|
||||||
|
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
if (is_array($data)) {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Redis
|
// Redis
|
||||||
if ($application == "dovecot-mailcow") {
|
if ($application == "dovecot-mailcow") {
|
||||||
if (isset($from) && isset($to)) {
|
if (isset($from) && isset($to)) {
|
||||||
|
|
|
@ -3503,18 +3503,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$mailboxdata = array();
|
$mailboxdata = array();
|
||||||
$last_imap_login = $redis->Get('last-login/imap/' . $_data);
|
|
||||||
$last_smtp_login = $redis->Get('last-login/smtp/' . $_data);
|
|
||||||
$last_pop3_login = $redis->Get('last-login/pop3/' . $_data);
|
|
||||||
if ($last_imap_login === false || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
|
||||||
$last_imap_login = '0';
|
|
||||||
}
|
|
||||||
if ($last_smtp_login === false || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
|
||||||
$last_smtp_login = '0';
|
|
||||||
}
|
|
||||||
if ($last_pop3_login === false || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
|
||||||
$last_pop3_login = '0';
|
|
||||||
}
|
|
||||||
if (preg_match('/y|yes/i', getenv('MASTER'))) {
|
if (preg_match('/y|yes/i', getenv('MASTER'))) {
|
||||||
$stmt = $pdo->prepare("SELECT
|
$stmt = $pdo->prepare("SELECT
|
||||||
`domain`.`backupmx`,
|
`domain`.`backupmx`,
|
||||||
|
@ -3575,10 +3563,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||||
$mailboxdata['quota_used'] = intval($row['bytes']);
|
$mailboxdata['quota_used'] = intval($row['bytes']);
|
||||||
$mailboxdata['percent_in_use'] = ($row['quota'] == 0) ? '- ' : round((intval($row['bytes']) / intval($row['quota'])) * 100);
|
$mailboxdata['percent_in_use'] = ($row['quota'] == 0) ? '- ' : round((intval($row['bytes']) / intval($row['quota'])) * 100);
|
||||||
|
|
||||||
$mailboxdata['last_imap_login'] = $last_imap_login;
|
|
||||||
$mailboxdata['last_smtp_login'] = $last_smtp_login;
|
|
||||||
$mailboxdata['last_pop3_login'] = $last_pop3_login;
|
|
||||||
|
|
||||||
if ($mailboxdata['percent_in_use'] === '- ') {
|
if ($mailboxdata['percent_in_use'] === '- ') {
|
||||||
$mailboxdata['percent_class'] = "info";
|
$mailboxdata['percent_class'] = "info";
|
||||||
}
|
}
|
||||||
|
@ -3592,11 +3576,43 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||||
$mailboxdata['percent_class'] = "success";
|
$mailboxdata['percent_class'] = "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine last logins
|
||||||
|
$stmt = $pdo->prepare("SELECT MAX(`datetime`) AS `datetime`, `service` FROM `sasl_logs`
|
||||||
|
WHERE `username` = :mailbox
|
||||||
|
AND `success` = 1
|
||||||
|
GROUP BY `service` DESC");
|
||||||
|
$stmt->execute(array(':mailbox' => $_data));
|
||||||
|
$SaslLogsData = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
foreach ($SaslLogsData as $SaslLogs) {
|
||||||
|
if ($SaslLogs['service'] == 'imap') {
|
||||||
|
$last_imap_login = strtotime($SaslLogs['datetime']);
|
||||||
|
}
|
||||||
|
else if ($SaslLogs['service'] == 'smtp') {
|
||||||
|
$last_smtp_login = strtotime($SaslLogs['datetime']);
|
||||||
|
}
|
||||||
|
else if ($SaslLogs['service'] == 'pop3') {
|
||||||
|
$last_pop3_login = strtotime($SaslLogs['datetime']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isset($last_imap_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
||||||
|
$last_imap_login = 0;
|
||||||
|
}
|
||||||
|
if (!isset($last_smtp_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
||||||
|
$last_smtp_login = 0;
|
||||||
|
}
|
||||||
|
if (!isset($last_pop3_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
||||||
|
$last_pop3_login = 0;
|
||||||
|
}
|
||||||
|
$mailboxdata['last_imap_login'] = $last_imap_login;
|
||||||
|
$mailboxdata['last_smtp_login'] = $last_smtp_login;
|
||||||
|
$mailboxdata['last_pop3_login'] = $last_pop3_login;
|
||||||
|
|
||||||
if (!isset($_extra) || $_extra != 'reduced') {
|
if (!isset($_extra) || $_extra != 'reduced') {
|
||||||
$rl = ratelimit('get', 'mailbox', $_data);
|
$rl = ratelimit('get', 'mailbox', $_data);
|
||||||
$stmt = $pdo->prepare("SELECT `maxquota`, `quota` FROM `domain` WHERE `domain` = :domain");
|
$stmt = $pdo->prepare("SELECT `maxquota`, `quota` FROM `domain` WHERE `domain` = :domain");
|
||||||
$stmt->execute(array(':domain' => $row['domain']));
|
$stmt->execute(array(':domain' => $row['domain']));
|
||||||
$DomainQuota = $stmt->fetch(PDO::FETCH_ASSOC);
|
$DomainQuota = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT IFNULL(COUNT(`active`), 0) AS `pushover_active` FROM `pushover` WHERE `username` = :username AND `active` = 1");
|
$stmt = $pdo->prepare("SELECT IFNULL(COUNT(`active`), 0) AS `pushover_active` FROM `pushover` WHERE `username` = :username AND `active` = 1");
|
||||||
$stmt->execute(array(':username' => $_data));
|
$stmt->execute(array(':username' => $_data));
|
||||||
$PushoverActive = $stmt->fetch(PDO::FETCH_ASSOC);
|
$PushoverActive = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
|
@ -3,7 +3,7 @@ function init_db_schema() {
|
||||||
try {
|
try {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
|
||||||
$db_version = "27052021_2000";
|
$db_version = "03062021_2320";
|
||||||
|
|
||||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
@ -510,6 +510,30 @@ function init_db_schema() {
|
||||||
),
|
),
|
||||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
),
|
),
|
||||||
|
"sasl_logs" => array(
|
||||||
|
"cols" => array(
|
||||||
|
"id" => "INT NOT NULL AUTO_INCREMENT",
|
||||||
|
"success" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||||
|
"service" => "VARCHAR(32) NOT NULL DEFAULT ''",
|
||||||
|
"app_password" => "INT",
|
||||||
|
"username" => "VARCHAR(255) NOT NULL",
|
||||||
|
"real_rip" => "VARCHAR(64) NOT NULL",
|
||||||
|
"datetime" => "DATETIME(0) NOT NULL DEFAULT NOW(0)"
|
||||||
|
),
|
||||||
|
"keys" => array(
|
||||||
|
"primary" => array(
|
||||||
|
"" => array("id")
|
||||||
|
),
|
||||||
|
"key" => array(
|
||||||
|
"username" => array("username"),
|
||||||
|
"service" => array("service"),
|
||||||
|
"success" => array("success"),
|
||||||
|
"datetime" => array("datetime"),
|
||||||
|
"real_rip" => array("real_rip")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
|
),
|
||||||
"quota2" => array(
|
"quota2" => array(
|
||||||
"cols" => array(
|
"cols" => array(
|
||||||
"username" => "VARCHAR(255) NOT NULL",
|
"username" => "VARCHAR(255) NOT NULL",
|
||||||
|
|
|
@ -24,19 +24,16 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
||||||
if ($as == "admin") {
|
if ($as == "admin") {
|
||||||
$_SESSION['mailcow_cc_username'] = $login_user;
|
$_SESSION['mailcow_cc_username'] = $login_user;
|
||||||
$_SESSION['mailcow_cc_role'] = "admin";
|
$_SESSION['mailcow_cc_role'] = "admin";
|
||||||
$_SESSION['mailcow_cc_last_login'] = last_login($login_user);
|
|
||||||
header("Location: /admin");
|
header("Location: /admin");
|
||||||
}
|
}
|
||||||
elseif ($as == "domainadmin") {
|
elseif ($as == "domainadmin") {
|
||||||
$_SESSION['mailcow_cc_username'] = $login_user;
|
$_SESSION['mailcow_cc_username'] = $login_user;
|
||||||
$_SESSION['mailcow_cc_role'] = "domainadmin";
|
$_SESSION['mailcow_cc_role'] = "domainadmin";
|
||||||
$_SESSION['mailcow_cc_last_login'] = last_login($login_user);
|
|
||||||
header("Location: /mailbox");
|
header("Location: /mailbox");
|
||||||
}
|
}
|
||||||
elseif ($as == "user") {
|
elseif ($as == "user") {
|
||||||
$_SESSION['mailcow_cc_username'] = $login_user;
|
$_SESSION['mailcow_cc_username'] = $login_user;
|
||||||
$_SESSION['mailcow_cc_role'] = "user";
|
$_SESSION['mailcow_cc_role'] = "user";
|
||||||
$_SESSION['mailcow_cc_last_login'] = last_login($login_user);
|
|
||||||
$http_parameters = explode('&', $_SESSION['index_query_string']);
|
$http_parameters = explode('&', $_SESSION['index_query_string']);
|
||||||
unset($_SESSION['index_query_string']);
|
unset($_SESSION['index_query_string']);
|
||||||
if (in_array('mobileconfig', $http_parameters)) {
|
if (in_array('mobileconfig', $http_parameters)) {
|
||||||
|
|
|
@ -271,7 +271,7 @@ jQuery(function($){
|
||||||
{"name":"role","title":"Role"},
|
{"name":"role","title":"Role"},
|
||||||
{"name":"remote","title":"IP"},
|
{"name":"remote","title":"IP"},
|
||||||
{"name":"msg","title":lang.message,"style":{"word-break":"break-all"}},
|
{"name":"msg","title":lang.message,"style":{"word-break":"break-all"}},
|
||||||
{"name":"call","title":"Call","breakpoints": "all"},
|
{"name":"call","title":"Call","breakpoints": "all"}
|
||||||
],
|
],
|
||||||
"rows": $.ajax({
|
"rows": $.ajax({
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
|
@ -301,6 +301,43 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function draw_sasl_logs() {
|
||||||
|
ft_api_logs = FooTable.init('#sasl_logs', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"success","title":lang.success,"filterable": false,"style":{"width":"30px"}},
|
||||||
|
{"name":"username","title":lang.username},
|
||||||
|
{"name":"service","title":lang.service},
|
||||||
|
{"name":"real_rip","title":"IP"},
|
||||||
|
{"sorted": true,"sortValue": function(value){res = new Date(value);return res.getTime();},"direction":"DESC","name":"datetime","formatter":function date_format(datetime) { var date = new Date(datetime.replace(/-/g, "/")); return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});},"title":lang.login_time,"style":{"width":"170px"}},
|
||||||
|
],
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/logs/sasl',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw sasl log table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
return process_table_data(data, 'sasl_log_table');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"empty": lang.empty,
|
||||||
|
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||||
|
"filtering": {"enabled": true,"delay": 1200,"position": "left","connectors": false,"placeholder": lang.filter_table,"connectors": false},
|
||||||
|
"sorting": {"enabled": true},
|
||||||
|
"on": {
|
||||||
|
"destroy.ft.table": function(e, ft){
|
||||||
|
$('.refresh_table').attr('disabled', 'true');
|
||||||
|
},
|
||||||
|
"ready.ft.table": function(e, ft){
|
||||||
|
table_log_ready(ft, 'sasl_logs');
|
||||||
|
},
|
||||||
|
"after.ft.paging": function(e, ft){
|
||||||
|
table_log_paging(ft, 'sasl_logs');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
function draw_acme_logs() {
|
function draw_acme_logs() {
|
||||||
ft_acme_logs = FooTable.init('#acme_log', {
|
ft_acme_logs = FooTable.init('#acme_log', {
|
||||||
"columns": [
|
"columns": [
|
||||||
|
@ -666,6 +703,20 @@ jQuery(function($){
|
||||||
item.task = '<code>' + item.task + '</code>';
|
item.task = '<code>' + item.task + '</code>';
|
||||||
item.type = '<span class="label label-' + item.type + '">' + item.type + '</span>';
|
item.type = '<span class="label label-' + item.type + '">' + item.type + '</span>';
|
||||||
});
|
});
|
||||||
|
} else if (table == 'sasl_log_table') {
|
||||||
|
$.each(data, function (i, item) {
|
||||||
|
if (item === null) { return true; }
|
||||||
|
item.username = escapeHtml(item.username);
|
||||||
|
if (item.service == "smtp") { item.service = '<div class="label label-default">' + item.service.toUpperCase() + '<i class="bi bi-chevron-compact-right"></i></div>'; }
|
||||||
|
else if (item.service == "imap") { item.service = '<div class="label label-default"><i class="bi bi-chevron-compact-left"></i> ' + item.service.toUpperCase() + '</div>'; }
|
||||||
|
else { item.service = '<div class="label label-default">' + item.service.toUpperCase() + '</div>'; }
|
||||||
|
if (item.success == 0) {
|
||||||
|
item.success = '<span class="label label-danger"><i class="bi bi-person-x-fill"></i></span>';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
item.success = '<span class="label label-success"><i class="bi bi-person-check-fill"></i></span>';
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (table == 'general_syslog') {
|
} else if (table == 'general_syslog') {
|
||||||
$.each(data, function (i, item) {
|
$.each(data, function (i, item) {
|
||||||
if (item === null) { return true; }
|
if (item === null) { return true; }
|
||||||
|
@ -750,6 +801,7 @@ jQuery(function($){
|
||||||
draw_api_logs();
|
draw_api_logs();
|
||||||
draw_rl_logs();
|
draw_rl_logs();
|
||||||
draw_ui_logs();
|
draw_ui_logs();
|
||||||
|
draw_sasl_logs();
|
||||||
draw_netfilter_logs();
|
draw_netfilter_logs();
|
||||||
draw_rspamd_history();
|
draw_rspamd_history();
|
||||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||||
|
|
|
@ -42,6 +42,7 @@ $(document).ready(function() {
|
||||||
});
|
});
|
||||||
$(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
|
$(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
|
||||||
$("#pushover_delete").click(function() { return confirm(lang.delete_ays); });
|
$("#pushover_delete").click(function() { return confirm(lang.delete_ays); });
|
||||||
|
|
||||||
});
|
});
|
||||||
jQuery(function($){
|
jQuery(function($){
|
||||||
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
|
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
|
||||||
|
@ -70,8 +71,65 @@ jQuery(function($){
|
||||||
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
||||||
}
|
}
|
||||||
acl_data = JSON.parse(acl);
|
acl_data = JSON.parse(acl);
|
||||||
var last_login = $('.last_login_date').data('time');
|
|
||||||
$('.last_login_date').text(unix_time_format(last_login));
|
$('.clear-last-logins').on('click', function () {
|
||||||
|
if (confirm(lang.delete_ays)) {
|
||||||
|
last_logins('reset');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function last_logins(action) {
|
||||||
|
if (action == 'get') {
|
||||||
|
$.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/last-login/' + encodeURIComponent(mailcow_cc_username),
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('error reading last logins');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
$('.last-login').html();
|
||||||
|
if (data.ui.time) {
|
||||||
|
$('.last-login').html('<i class="bi bi-person-fill"></i> ' + lang.last_ui_login + ': ' + unix_time_format(data.ui.time));
|
||||||
|
} else {
|
||||||
|
$('.last-login').text(lang.no_last_login);
|
||||||
|
}
|
||||||
|
if (data.sasl) {
|
||||||
|
$('.last-login').append('<ul class="list-group">');
|
||||||
|
$.each(data.sasl, function (i, item) {
|
||||||
|
var datetime = new Date(item.datetime.replace(/-/g, "/"));
|
||||||
|
var local_datetime = datetime.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
||||||
|
if (item.service == "smtp") { service = '<div class="label label-default">' + item.service.toUpperCase() + '<i class="bi bi-chevron-compact-right"></i></div>'; }
|
||||||
|
else if (item.service == "imap") { service = '<div class="label label-default"><i class="bi bi-chevron-compact-left"></i> ' + item.service.toUpperCase() + '</div>'; }
|
||||||
|
else { service = '<div class="label label-default">' + item.service.toUpperCase() + '</div>'; }
|
||||||
|
if (item.real_rip.startsWith("Web")) {
|
||||||
|
real_rip = item.real_rip;
|
||||||
|
} else {
|
||||||
|
real_rip = '<a href="https://bgp.he.net/ip/' + item.real_rip + '" target="_blank">' + item.real_rip + '</a>';
|
||||||
|
}
|
||||||
|
$('.last-login').append('<li class="list-group-item">' +
|
||||||
|
local_datetime + ' ' + service + ' ' + lang.from + ' ' +
|
||||||
|
real_rip +
|
||||||
|
'</li>');
|
||||||
|
})
|
||||||
|
$('.last-login').append('</ul>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (action == 'reset') {
|
||||||
|
$.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/reset-last-login/' + encodeURIComponent(mailcow_cc_username),
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('cannot reset last logins');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
last_logins('get');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function draw_tla_table() {
|
function draw_tla_table() {
|
||||||
ft_tla_table = FooTable.init('#tla_table', {
|
ft_tla_table = FooTable.init('#tla_table', {
|
||||||
|
@ -132,7 +190,7 @@ jQuery(function($){
|
||||||
{"name":"log","title":"Log"},
|
{"name":"log","title":"Log"},
|
||||||
{"name":"active","filterable": false,"style":{"maxWidth":"70px","width":"70px"},"title":lang.active,"formatter": function(value){return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';}},
|
{"name":"active","filterable": false,"style":{"maxWidth":"70px","width":"70px"},"title":lang.active,"formatter": function(value){return 1==value?'<i class="bi bi-check-lg"></i>':0==value&&'<i class="bi bi-x-lg"></i>';}},
|
||||||
{"name":"is_running","filterable": false,"style":{"maxWidth":"120px","width":"100px"},"title":lang.status},
|
{"name":"is_running","filterable": false,"style":{"maxWidth":"120px","width":"100px"},"title":lang.status},
|
||||||
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"240px","width":"240px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
|
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","min-width":"260px","width":"260px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
|
||||||
],
|
],
|
||||||
"empty": lang.empty,
|
"empty": lang.empty,
|
||||||
"rows": $.ajax({
|
"rows": $.ajax({
|
||||||
|
@ -324,6 +382,7 @@ jQuery(function($){
|
||||||
draw_tla_table();
|
draw_tla_table();
|
||||||
draw_wl_policy_mailbox_table();
|
draw_wl_policy_mailbox_table();
|
||||||
draw_bl_policy_mailbox_table();
|
draw_bl_policy_mailbox_table();
|
||||||
|
last_logins('get');
|
||||||
|
|
||||||
// FIDO2 friendly name modal
|
// FIDO2 friendly name modal
|
||||||
$('#fido2ChangeFn').on('show.bs.modal', function (e) {
|
$('#fido2ChangeFn').on('show.bs.modal', function (e) {
|
||||||
|
|
|
@ -306,7 +306,6 @@ if (isset($_GET['query'])) {
|
||||||
$_SESSION["mailcow_cc_role"] = "domainadmin";
|
$_SESSION["mailcow_cc_role"] = "domainadmin";
|
||||||
}
|
}
|
||||||
$_SESSION["mailcow_cc_username"] = $process_fido2['username'];
|
$_SESSION["mailcow_cc_username"] = $process_fido2['username'];
|
||||||
$_SESSION['mailcow_cc_last_login'] = last_login($process_fido2['username']);
|
|
||||||
$_SESSION["fido2_cid"] = $process_fido2['cid'];
|
$_SESSION["fido2_cid"] = $process_fido2['cid'];
|
||||||
unset($_SESSION["challenge"]);
|
unset($_SESSION["challenge"]);
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
|
@ -640,6 +639,21 @@ if (isset($_GET['query'])) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "last-login":
|
||||||
|
if ($object) {
|
||||||
|
$data = last_login('get', $object);
|
||||||
|
process_get_return($data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Todo: move to delete
|
||||||
|
case "reset-last-login":
|
||||||
|
if ($object) {
|
||||||
|
$data = last_login('reset', $object);
|
||||||
|
process_get_return($data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case "transport":
|
case "transport":
|
||||||
switch ($object) {
|
switch ($object) {
|
||||||
case "all":
|
case "all":
|
||||||
|
@ -800,6 +814,17 @@ if (isset($_GET['query'])) {
|
||||||
}
|
}
|
||||||
echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
|
echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
|
||||||
break;
|
break;
|
||||||
|
case "sasl":
|
||||||
|
// 0 is first record, so empty is fine
|
||||||
|
if (isset($extra)) {
|
||||||
|
$extra = preg_replace('/[^\d\-]/i', '', $extra);
|
||||||
|
$logs = get_logs('sasl', $extra);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$logs = get_logs('sasl');
|
||||||
|
}
|
||||||
|
echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
|
||||||
|
break;
|
||||||
case "watchdog":
|
case "watchdog":
|
||||||
// 0 is first record, so empty is fine
|
// 0 is first record, so empty is fine
|
||||||
if (isset($extra)) {
|
if (isset($extra)) {
|
||||||
|
@ -1458,7 +1483,6 @@ if (isset($_GET['query'])) {
|
||||||
process_delete_return(dkim('delete', array('domains' => $items)));
|
process_delete_return(dkim('delete', array('domains' => $items)));
|
||||||
break;
|
break;
|
||||||
case "domain":
|
case "domain":
|
||||||
file_put_contents('/tmp/dssaa', $items);
|
|
||||||
process_delete_return(mailbox('delete', 'domain', array('domain' => $items)));
|
process_delete_return(mailbox('delete', 'domain', array('domain' => $items)));
|
||||||
break;
|
break;
|
||||||
case "alias-domain":
|
case "alias-domain":
|
||||||
|
|
|
@ -119,6 +119,10 @@
|
||||||
"validation_success": "Erfolgreich validiert"
|
"validation_success": "Erfolgreich validiert"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
|
"success": "Erfolg",
|
||||||
|
"service": "Dienst",
|
||||||
|
"login_time": "Zeit",
|
||||||
|
"username": "Benutzername",
|
||||||
"access": "Zugang",
|
"access": "Zugang",
|
||||||
"action": "Aktion",
|
"action": "Aktion",
|
||||||
"activate_api": "API aktivieren",
|
"activate_api": "API aktivieren",
|
||||||
|
@ -489,6 +493,10 @@
|
||||||
"started_at": "Gestartet am",
|
"started_at": "Gestartet am",
|
||||||
"solr_status": "Solr Status",
|
"solr_status": "Solr Status",
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
|
"success": "Erfolg",
|
||||||
|
"service": "Dienst",
|
||||||
|
"login_time": "Zeit",
|
||||||
|
"username": "Benutzername",
|
||||||
"started_on": "Gestartet am",
|
"started_on": "Gestartet am",
|
||||||
"static_logs": "Statische Logs",
|
"static_logs": "Statische Logs",
|
||||||
"system_containers": "System & Container",
|
"system_containers": "System & Container",
|
||||||
|
@ -1032,6 +1040,9 @@
|
||||||
"excludes": "Ausschlüsse",
|
"excludes": "Ausschlüsse",
|
||||||
"expire_in": "Ungültig in",
|
"expire_in": "Ungültig in",
|
||||||
"force_pw_update": "Das Passwort für diesen Benutzer <b>muss</b> geändert werden, damit die Zugriffssperre auf die Groupware-Komponenten wieder freigeschaltet wird.",
|
"force_pw_update": "Das Passwort für diesen Benutzer <b>muss</b> geändert werden, damit die Zugriffssperre auf die Groupware-Komponenten wieder freigeschaltet wird.",
|
||||||
|
"from": "von",
|
||||||
|
"recent_successful_connections": "Kürzlich erfolgreiche Verbindungen",
|
||||||
|
"clear_recent_successful_connections": "Alle erfolgreichen Verbindungen bereinigen",
|
||||||
"generate": "generieren",
|
"generate": "generieren",
|
||||||
"hour": "Stunde",
|
"hour": "Stunde",
|
||||||
"hourly": "Stündlich",
|
"hourly": "Stündlich",
|
||||||
|
@ -1041,6 +1052,7 @@
|
||||||
"is_catch_all": "Ist Catch-All-Adresse für Domain(s)",
|
"is_catch_all": "Ist Catch-All-Adresse für Domain(s)",
|
||||||
"last_mail_login": "Letzter Mail-Login",
|
"last_mail_login": "Letzter Mail-Login",
|
||||||
"last_run": "Letzte Ausführung",
|
"last_run": "Letzte Ausführung",
|
||||||
|
"last_ui_login": "Letzte UI Anmeldung",
|
||||||
"loading": "Lade...",
|
"loading": "Lade...",
|
||||||
"mailbox_details": "Mailbox-Details",
|
"mailbox_details": "Mailbox-Details",
|
||||||
"messages": "Nachrichten",
|
"messages": "Nachrichten",
|
||||||
|
|
|
@ -117,6 +117,10 @@
|
||||||
"validation_success": "Validated successfully"
|
"validation_success": "Validated successfully"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
|
"success": "Success",
|
||||||
|
"service": "Service",
|
||||||
|
"login_time": "Login time",
|
||||||
|
"username": "Username",
|
||||||
"access": "Access",
|
"access": "Access",
|
||||||
"action": "Action",
|
"action": "Action",
|
||||||
"activate_api": "Activate API",
|
"activate_api": "Activate API",
|
||||||
|
@ -486,8 +490,12 @@
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
"started_at": "Started at",
|
"started_at": "Started at",
|
||||||
"solr_status": "Solr status",
|
"solr_status": "Solr status",
|
||||||
"uptime": "Uptime",
|
|
||||||
"started_on": "Started on",
|
"started_on": "Started on",
|
||||||
|
"uptime": "Uptime",
|
||||||
|
"success": "Success",
|
||||||
|
"service": "Service",
|
||||||
|
"login_time": "Time",
|
||||||
|
"username": "Username",
|
||||||
"static_logs": "Static logs",
|
"static_logs": "Static logs",
|
||||||
"system_containers": "System & Containers",
|
"system_containers": "System & Containers",
|
||||||
"xmpp_status": "XMPP status"
|
"xmpp_status": "XMPP status"
|
||||||
|
@ -1030,6 +1038,9 @@
|
||||||
"excludes": "Excludes",
|
"excludes": "Excludes",
|
||||||
"expire_in": "Expire in",
|
"expire_in": "Expire in",
|
||||||
"force_pw_update": "You <b>must</b> set a new password to be able to access groupware related services.",
|
"force_pw_update": "You <b>must</b> set a new password to be able to access groupware related services.",
|
||||||
|
"from": "from",
|
||||||
|
"recent_successful_connections": "Seen successful connections",
|
||||||
|
"clear_recent_successful_connections": "Clear seen successful connections",
|
||||||
"generate": "generate",
|
"generate": "generate",
|
||||||
"hour": "hour",
|
"hour": "hour",
|
||||||
"hourly": "Hourly",
|
"hourly": "Hourly",
|
||||||
|
@ -1039,6 +1050,7 @@
|
||||||
"is_catch_all": "Catch-all for domain/s",
|
"is_catch_all": "Catch-all for domain/s",
|
||||||
"last_mail_login": "Last mail login",
|
"last_mail_login": "Last mail login",
|
||||||
"last_run": "Last run",
|
"last_run": "Last run",
|
||||||
|
"last_ui_login": "Last UI login",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"mailbox_details": "Mailbox details",
|
"mailbox_details": "Mailbox details",
|
||||||
"messages": "messages",
|
"messages": "messages",
|
||||||
|
|
|
@ -22,15 +22,8 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-offset-3 col-sm-9">
|
<div class="col-sm-offset-3 col-sm-9">
|
||||||
<p><a href="#pwChangeModal" data-toggle="modal">[<?=$lang['user']['change_password'];?>]</a></p>
|
<p><a href="#pwChangeModal" data-toggle="modal">[<?=$lang['user']['change_password'];?>]</a></p>
|
||||||
<p>
|
<div class="last-login"></div>
|
||||||
<?php
|
<div class="clear-last-logins"><?=$lang['user']['clear_recent_successful_connections'];?></div>
|
||||||
if ($_SESSION['mailcow_cc_last_login']['remote']):
|
|
||||||
?>
|
|
||||||
<i class="bi bi-person-bounding-box"></i> <span data-time="<?=$_SESSION['mailcow_cc_last_login']['time'];?>" class="last_login_date"></span> (<?=$_SESSION['mailcow_cc_last_login']['remote'];?>)
|
|
||||||
<?php
|
|
||||||
else: echo $lang['user']['no_last_login']; endif;
|
|
||||||
?>
|
|
||||||
</p>
|
|
||||||
<p>
|
<p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -181,15 +174,10 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
||||||
<p><a href="#pwChangeModal" data-toggle="modal">[<?=$lang['user']['change_password'];?>]</a></p>
|
<p><a href="#pwChangeModal" data-toggle="modal">[<?=$lang['user']['change_password'];?>]</a></p>
|
||||||
<p><a target="_blank" href="https://mailcow.github.io/mailcow-dockerized-docs/client/#<?=$clientconfigstr;?>">[<?=$lang['user']['client_configuration'];?>]</a></p>
|
<p><a target="_blank" href="https://mailcow.github.io/mailcow-dockerized-docs/client/#<?=$clientconfigstr;?>">[<?=$lang['user']['client_configuration'];?>]</a></p>
|
||||||
<p><a href="#userFilterModal" data-toggle="modal">[<?=$lang['user']['show_sieve_filters'];?>]</a></p>
|
<p><a href="#userFilterModal" data-toggle="modal">[<?=$lang['user']['show_sieve_filters'];?>]</a></p>
|
||||||
<p>
|
<hr>
|
||||||
<?php
|
<h4><?=$lang['user']['recent_successful_connections'];?></h4>
|
||||||
if ($_SESSION['mailcow_cc_last_login']['remote']):
|
<div class="last-login"></div>
|
||||||
?>
|
<div class="clear-last-logins"><?=$lang['user']['clear_recent_successful_connections'];?></div>
|
||||||
<i class="bi bi-person-bounding-box"></i> <span data-time="<?=$_SESSION['mailcow_cc_last_login']['time'];?>" class="last_login_date"></span> (<?=$_SESSION['mailcow_cc_last_login']['remote'];?>)
|
|
||||||
<?php
|
|
||||||
else: echo $lang['user']['no_last_login']; endif;
|
|
||||||
?>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
Loading…
Reference in New Issue