public
nobgit
read
NobMail
Based on mailcow: dockerized
Languages
Repository composition by tracked source files.
PHP
49%
JavaScript
35%
HTML
9%
CSS
4%
Shell
2%
Python
1%
Lua
0%
Perl
0%
Ruby
0%
SCSS
0%
Create file
Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git
Commit
Fix sasl_logs
805634f9
data/conf/dovecot/auth/mailcowauth.php | 15 ++++++---
data/conf/dovecot/auth/passwd-verify.lua | 9 +++---
data/web/inc/functions.auth.inc.php | 50 ++++++++++++++++++------------
data/web/inc/functions.inc.php | 28 +++++++++++++++++
data/web/js/site/user.js | 6 ----
data/web/templates/user/tab-user-auth.twig | 3 +-
6 files changed, 74 insertions(+), 37 deletions(-)
Diff
diff --git a/data/conf/dovecot/auth/mailcowauth.php b/data/conf/dovecot/auth/mailcowauth.php
index fc17df72..667419c5 100644
--- a/data/conf/dovecot/auth/mailcowauth.php
+++ b/data/conf/dovecot/auth/mailcowauth.php
@@ -69,29 +69,34 @@ require_once 'functions.acl.inc.php';
$isSOGoRequest = $post['real_rip'] == getenv('IPV4_NETWORK') . '.248';
$result = false;
-$protocol = $post['protocol'];
if ($isSOGoRequest) {
- $protocol = null;
// This is a SOGo Auth request. First check for SSO password.
$sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass");
if ($sogo_sso_pass === $post['password']){
error_log('MAILCOWAUTH: SOGo SSO auth for user ' . $post['username']);
+ set_sasl_log($post['username'], $post['real_rip'], "SOGO");
$result = true;
}
}
if ($result === false){
- $result = apppass_login($post['username'], $post['password'], $protocol, array(
+ $result = apppass_login($post['username'], $post['password'], array($post['service'] => true), array(
'is_internal' => true,
'remote_addr' => $post['real_rip']
));
- if ($result) error_log('MAILCOWAUTH: App auth for user ' . $post['username']);
+ if ($result) {
+ error_log('MAILCOWAUTH: App auth for user ' . $post['username']);
+ set_sasl_log($post['username'], $post['real_rip'], $post['service']);
+ }
}
if ($result === false){
// Init Identity Provider
$iam_provider = identity_provider('init');
$iam_settings = identity_provider('get');
$result = user_login($post['username'], $post['password'], array('is_internal' => true));
- if ($result) error_log('MAILCOWAUTH: User auth for user ' . $post['username']);
+ if ($result) {
+ error_log('MAILCOWAUTH: User auth for user ' . $post['username']);
+ set_sasl_log($post['username'], $post['real_rip'], $post['service']);
+ }
}
if ($result) {
diff --git a/data/conf/dovecot/auth/passwd-verify.lua b/data/conf/dovecot/auth/passwd-verify.lua
index cb2e928d..a6cb988d 100644
--- a/data/conf/dovecot/auth/passwd-verify.lua
+++ b/data/conf/dovecot/auth/passwd-verify.lua
@@ -12,12 +12,11 @@ function auth_password_verify(request, password)
username = request.user,
password = password,
real_rip = request.real_rip,
- protocol = {}
+ service = request.service
}
- req.protocol[request.service] = true
local req_json = json.encode(req)
- local res = {}
-
+ local res = {}
+
local b, c = https.request {
method = "POST",
url = "https://nginx:9082",
@@ -33,7 +32,7 @@ function auth_password_verify(request, password)
if api_response.success == true then
return dovecot.auth.PASSDB_RESULT_OK, ""
end
-
+
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Failed to authenticate"
end
diff --git a/data/web/inc/functions.auth.inc.php b/data/web/inc/functions.auth.inc.php
index f432c383..30d5943c 100644
--- a/data/web/inc/functions.auth.inc.php
+++ b/data/web/inc/functions.auth.inc.php
@@ -9,25 +9,51 @@ function check_login($user, $pass, $app_passwd_data = false, $extra = null) {
// Try validate admin
if (!isset($role) || $role == "admin") {
$result = admin_login($user, $pass);
- if ($result !== false) return $result;
+ if ($result !== false){
+ return $result;
+ }
}
// Try validate domain admin
if (!isset($role) || $role == "domain_admin") {
$result = domainadmin_login($user, $pass);
- if ($result !== false) return $result;
+ if ($result !== false) {
+ return $result;
+ }
}
// Try validate user
if (!isset($role) || $role == "user") {
$result = user_login($user, $pass);
- if ($result !== false) return $result;
+ if ($result !== false) {
+ if ($app_passwd_data['eas'] === true) {
+ $service = 'EAS';
+ } elseif ($app_passwd_data['dav'] === true) {
+ $service = 'DAV';
+ } else {
+ $service = 'MAILCOWUI';
+ }
+ $real_rip = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
+ set_sasl_log($user, $real_rip, $service);
+ return $result;
+ }
}
// Try validate app password
if (!isset($role) || $role == "app") {
$result = apppass_login($user, $pass, $app_passwd_data);
- if ($result !== false) return $result;
+ if ($result !== false) {
+ if ($app_passwd_data['eas'] === true) {
+ $service = 'EAS';
+ } elseif ($app_passwd_data['dav'] === true) {
+ $service = 'DAV';
+ } else {
+ $service = 'NONE';
+ }
+ $real_rip = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
+ set_sasl_log($user, $real_rip, $service, $pass);
+ return $result;
+ }
}
// skip log and only return false if it's an internal request
@@ -415,21 +441,7 @@ function apppass_login($user, $pass, $app_passwd_data, $extra = null){
// verify password
if (verify_hash($row['password'], $pass) !== false) {
- if ($is_internal){
- $remote_addr = $extra['remote_addr'];
- } else {
- $remote_addr = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
- }
-
- $service = strtoupper($is_app_passwd);
- $stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES (:service, :app_id, :username, :remote_addr)");
- $stmt->execute(array(
- ':service' => $service,
- ':app_id' => $row['app_passwd_id'],
- ':username' => $user,
- ':remote_addr' => $remote_addr
- ));
-
+ $_SESSION['app_passwd_id'] = $row['app_passwd_id'];
unset($_SESSION['ldelay']);
return "user";
}
diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php
index 6a70eb6e..49e26b97 100644
--- a/data/web/inc/functions.inc.php
+++ b/data/web/inc/functions.inc.php
@@ -350,6 +350,34 @@ function last_login($action, $username, $sasl_limit_days = 7, $ui_offset = 1) {
}
}
+function set_sasl_log($username, $real_rip, $service){
+ global $pdo;
+
+ try {
+ if (!empty($_SESSION['app_passwd_id'])) {
+ $app_password = $_SESSION['app_passwd_id'];
+ } else {
+ $app_password = 0;
+ }
+
+ $stmt = $pdo->prepare('REPLACE INTO `sasl_log` (`username`, `real_rip`, `service`, `app_password`) VALUES (:username, :real_rip, :service, :app_password)');
+ $stmt->execute(array(
+ ':username' => $username,
+ ':real_rip' => $real_rip,
+ ':service' => $service,
+ ':app_password' => $app_password
+ ));
+ } catch (PDOException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_data_log),
+ 'msg' => array('mysql_error', $e)
+ );
+ return false;
+ }
+
+ return true;
+}
function flush_memcached() {
try {
$m = new Memcached();
diff --git a/data/web/js/site/user.js b/data/web/js/site/user.js
index 8f64d4e2..678e23b1 100644
--- a/data/web/js/site/user.js
+++ b/data/web/js/site/user.js
@@ -90,13 +90,7 @@ jQuery(function($){
console.log('error reading last logins');
},
success: function (data) {
- $('.last-ui-login').html('');
$('.last-sasl-login').html('');
- if (data.ui.time) {
- $('.last-ui-login').html('<i class="bi bi-person-fill"></i> ' + lang.last_ui_login + ': ' + unix_time_format(data.ui.time));
- } else {
- $('.last-ui-login').text(lang.no_last_login);
- }
if (data.sasl) {
$('.last-sasl-login').append('<ul class="list-group">');
$.each(data.sasl, function (i, item) {
diff --git a/data/web/templates/user/tab-user-auth.twig b/data/web/templates/user/tab-user-auth.twig
index a7e02543..bace3348 100644
--- a/data/web/templates/user/tab-user-auth.twig
+++ b/data/web/templates/user/tab-user-auth.twig
@@ -184,7 +184,7 @@
</div>
{% endif %}
</div>
- <div class="ms-auto col-xl-3 col-lg-5 col-md-12 col-12 d-flex flex-column well flex-grow-1">
+ <div class="ms-auto col-xl-3 col-lg-5 col-md-12 col-12 d-flex flex-column well flex-grow-1" id="recent-logins">
<legend class="d-flex">
<span>{{ lang.user.recent_successful_connections }}</span>
<div id="spinner-last-login" class="ms-auto my-auto spinner-border spinner-border-sm d-none" role="status">
@@ -192,7 +192,6 @@
</div>
</legend>
<hr>
- <h6 class="last-ui-login"></h6>
<div class="d-flex">
<span class="clear-last-logins mt-auto mb-2">
{{ lang.user.clear_recent_successful_connections }}