Compare commits

..

2 Commits

Author SHA1 Message Date
FreddleSpl0it
43f570e761 [Web] switch from GET to POST for datatable requests 2026-03-03 08:10:16 +01:00
milkmaker
7fce984cac [Web] Updated lang.lv-lv.json (#7069)
Co-authored-by: Edgars Andersons <Edgars+Mailcow+Weblate@gaitenis.id.lv>
2026-02-19 22:15:32 +01:00
11 changed files with 212 additions and 242 deletions

View File

@@ -155,11 +155,6 @@ while (true) {
continue;
}
// Determine if account is enabled in Keycloak
$keycloak_account_active = isset($user['enabled']) ? intval($user['enabled']) : 1;
$status = ($keycloak_account_active == 1) ? "enabled" : "disabled";
logMsg("info", "User " . $user['email'] . " is {$status} in Keycloak");
// try get mailbox user
$stmt = $pdo->prepare("SELECT
mailbox.*,
@@ -177,12 +172,6 @@ while (true) {
$_SESSION['access_all_exception'] = '1';
if (!$row && intval($iam_settings['import_users']) == 1){
// Skip disabled users during import if sync_disabled_users is enabled
if (intval($iam_settings['sync_disabled_users']) == 1 && $keycloak_account_active == 0) {
logMsg("info", "Skipping import of disabled user " . $user['email']);
continue;
}
if ($mapper_key === false){
if (!empty($iam_settings['default_template'])) {
$mbox_template = $iam_settings['default_template'];
@@ -213,26 +202,13 @@ while (true) {
continue;
}
$mbox_template = $iam_settings['templates'][$mapper_key];
// Prepare update data with active status
$update_data = array(
// mailbox user does exist, sync attribtues...
logMsg("info", "Syncing attributes for user " . $user['email']);
mailbox('edit', 'mailbox_from_template', array(
'username' => $user['email'],
'name' => $user['firstName'] . " " . $user['lastName'],
'template' => $mbox_template
);
// Add active status if sync_disabled_users is enabled
if (intval($iam_settings['sync_disabled_users']) == 1) {
if ($row['active'] != $keycloak_account_active) {
$update_data['active'] = $keycloak_account_active;
$status_change = ($keycloak_account_active == 1) ? "enabled" : "disabled";
logMsg("info", "Changing active status for user " . $user['email'] . " to {$status_change}");
}
}
// mailbox user does exist, sync attributes...
logMsg("info", "Syncing attributes for user " . $user['email']);
mailbox('edit', 'mailbox_from_template', $update_data);
));
} else {
// skip mailbox user
logMsg("info", "Skipping user " . $user['email']);

View File

@@ -118,7 +118,7 @@ if (!empty($iam_settings['filter'])) {
}
$response = $ldap_query->where($iam_settings['username_field'], "*")
->where($iam_settings['attribute_field'], "*")
->select([$iam_settings['username_field'], $iam_settings['attribute_field'], 'displayname', 'userAccountControl', 'pwdAccountLockedTime'])
->select([$iam_settings['username_field'], $iam_settings['attribute_field'], 'displayname'])
->paginate($max);
// Process the users
@@ -138,41 +138,6 @@ foreach ($response as $user) {
$user_template = $user[$iam_settings['attribute_field']][0];
$mapper_key = array_search($user_template, $iam_settings['mappers']);
// Determine if account is disabled in LDAP (multi-provider support)
$ldap_account_active = 1; // Default to active
$has_disabled_attr = false;
$disabled_check_method = "none";
// Try Active Directory userAccountControl first
if (isset($user['useraccountcontrol'][0])) {
$has_disabled_attr = true;
$disabled_check_method = "AD-userAccountControl";
$uac = intval($user['useraccountcontrol'][0]);
// UAC flag 0x0002 indicates ACCOUNTDISABLE
// If bit is set, account is disabled
$ldap_account_active = ($uac & 0x0002) ? 0 : 1;
$uac_status = ($ldap_account_active == 1) ? "enabled" : "disabled";
logMsg("info", "User " . $user[$iam_settings['username_field']][0] . " is {$uac_status} (AD UAC: {$uac})");
}
// Try OpenLDAP/389DS/FreeIPA pwdAccountLockedTime
elseif (isset($user['pwdaccountlockedtime'])) {
$has_disabled_attr = true;
$disabled_check_method = "OpenLDAP-pwdAccountLockedTime";
// If pwdAccountLockedTime attribute exists and has a value, account is locked/disabled
$ldap_account_active = (!empty($user['pwdaccountlockedtime'][0])) ? 0 : 1;
$status = ($ldap_account_active == 1) ? "enabled" : "disabled";
logMsg("info", "User " . $user[$iam_settings['username_field']][0] . " is {$status} (OpenLDAP/389DS)");
}
else {
// No disabled attribute found - this is normal for some LDAP implementations
// We'll skip disabled state sync for this user
logMsg("debug", "User " . $user[$iam_settings['username_field']][0] . " - no disabled attribute found (userAccountControl or pwdAccountLockedTime), skipping status sync");
}
if (empty($user[$iam_settings['username_field']][0])){
logMsg("warning", "Skipping user " . $user['displayname'][0] . " due to empty LDAP ". $iam_settings['username_field'] . " property.");
continue;
@@ -180,12 +145,6 @@ foreach ($response as $user) {
$_SESSION['access_all_exception'] = '1';
if (!$row && intval($iam_settings['import_users']) == 1){
// Skip disabled users during import if sync_disabled_users is enabled
if (intval($iam_settings['sync_disabled_users']) == 1 && $has_disabled_attr && $ldap_account_active == 0) {
logMsg("info", "Skipping import of disabled user " . $user[$iam_settings['username_field']][0] . " (method: {$disabled_check_method})");
continue;
}
if ($mapper_key === false){
if (!empty($iam_settings['default_template'])) {
$mbox_template = $iam_settings['default_template'];
@@ -215,26 +174,13 @@ foreach ($response as $user) {
continue;
}
$mbox_template = $iam_settings['templates'][$mapper_key];
// Prepare update data with active status
$update_data = array(
// mailbox user does exist, sync attribtues...
logMsg("info", "Syncing attributes for user " . $user[$iam_settings['username_field']][0]);
mailbox('edit', 'mailbox_from_template', array(
'username' => $user[$iam_settings['username_field']][0],
'name' => $user['displayname'][0],
'template' => $mbox_template
);
// Add active status if sync_disabled_users is enabled and a disabled attribute was found
if (intval($iam_settings['sync_disabled_users']) == 1 && $has_disabled_attr) {
if ($row['active'] != $ldap_account_active) {
$update_data['active'] = $ldap_account_active;
$status_change = ($ldap_account_active == 1) ? "enabled" : "disabled";
logMsg("info", "Changing active status for user " . $user[$iam_settings['username_field']][0] . " to {$status_change} (method: {$disabled_check_method})");
}
}
// mailbox user does exist, sync attributes...
logMsg("info", "Syncing attributes for user " . $user[$iam_settings['username_field']][0]);
mailbox('edit', 'mailbox_from_template', $update_data);
));
} else {
// skip mailbox user
logMsg("info", "Skipping user " . $user[$iam_settings['username_field']][0]);

View File

@@ -2383,7 +2383,6 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
case "use_tls":
case "login_provisioning":
case "ignore_ssl_errors":
case "sync_disabled_users":
$settings[$row["key"]] = boolval($row["value"]);
break;
default:
@@ -2463,14 +2462,13 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
$_data['login_provisioning'] = isset($_data['login_provisioning']) ? boolval($_data['login_provisioning']) : false;
switch ($_data['authsource']) {
case "keycloak":
$_data['server_url'] = (!empty($_data['server_url'])) ? rtrim($_data['server_url'], '/') : null;
$_data['mailpassword_flow'] = isset($_data['mailpassword_flow']) ? intval($_data['mailpassword_flow']) : 0;
$_data['periodic_sync'] = isset($_data['periodic_sync']) ? intval($_data['periodic_sync']) : 0;
$_data['import_users'] = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
$_data['sync_disabled_users'] = isset($_data['sync_disabled_users']) ? intval($_data['sync_disabled_users']) : 0;
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
$required_settings = array('authsource', 'server_url', 'realm', 'client_id', 'client_secret', 'redirect_url', 'version', 'mailpassword_flow', 'periodic_sync', 'import_users', 'sync_disabled_users', 'sync_interval', 'ignore_ssl_error', 'login_provisioning');
$_data['server_url'] = (!empty($_data['server_url'])) ? rtrim($_data['server_url'], '/') : null;
$_data['mailpassword_flow'] = isset($_data['mailpassword_flow']) ? intval($_data['mailpassword_flow']) : 0;
$_data['periodic_sync'] = isset($_data['periodic_sync']) ? intval($_data['periodic_sync']) : 0;
$_data['import_users'] = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
$required_settings = array('authsource', 'server_url', 'realm', 'client_id', 'client_secret', 'redirect_url', 'version', 'mailpassword_flow', 'periodic_sync', 'import_users', 'sync_interval', 'ignore_ssl_error', 'login_provisioning');
break;
case "generic-oidc":
$_data['authorize_url'] = (!empty($_data['authorize_url'])) ? $_data['authorize_url'] : null;
@@ -2480,19 +2478,18 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
$required_settings = array('authsource', 'authorize_url', 'token_url', 'client_id', 'client_secret', 'redirect_url', 'userinfo_url', 'client_scopes', 'ignore_ssl_error', 'login_provisioning');
break;
case "ldap":
$_data['host'] = (!empty($_data['host'])) ? str_replace(" ", "", $_data['host']) : "";
$_data['port'] = (!empty($_data['port'])) ? intval($_data['port']) : 389;
$_data['username_field'] = (!empty($_data['username_field'])) ? strtolower($_data['username_field']) : "mail";
$_data['attribute_field'] = (!empty($_data['attribute_field'])) ? strtolower($_data['attribute_field']) : "";
$_data['filter'] = (!empty($_data['filter'])) ? $_data['filter'] : "";
$_data['periodic_sync'] = isset($_data['periodic_sync']) ? intval($_data['periodic_sync']) : 0;
$_data['import_users'] = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
$_data['sync_disabled_users'] = isset($_data['sync_disabled_users']) ? intval($_data['sync_disabled_users']) : 0;
$_data['use_ssl'] = isset($_data['use_ssl']) ? boolval($_data['use_ssl']) : false;
$_data['use_tls'] = isset($_data['use_tls']) && !$_data['use_ssl'] ? boolval($_data['use_tls']) : false;
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
$required_settings = array('authsource', 'host', 'port', 'basedn', 'username_field', 'filter', 'attribute_field', 'binddn', 'bindpass', 'periodic_sync', 'import_users', 'sync_disabled_users', 'sync_interval', 'use_ssl', 'use_tls', 'ignore_ssl_error', 'login_provisioning');
$_data['host'] = (!empty($_data['host'])) ? str_replace(" ", "", $_data['host']) : "";
$_data['port'] = (!empty($_data['port'])) ? intval($_data['port']) : 389;
$_data['username_field'] = (!empty($_data['username_field'])) ? strtolower($_data['username_field']) : "mail";
$_data['attribute_field'] = (!empty($_data['attribute_field'])) ? strtolower($_data['attribute_field']) : "";
$_data['filter'] = (!empty($_data['filter'])) ? $_data['filter'] : "";
$_data['periodic_sync'] = isset($_data['periodic_sync']) ? intval($_data['periodic_sync']) : 0;
$_data['import_users'] = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
$_data['use_ssl'] = isset($_data['use_ssl']) ? boolval($_data['use_ssl']) : false;
$_data['use_tls'] = isset($_data['use_tls']) && !$_data['use_ssl'] ? boolval($_data['use_tls']) : false;
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
$required_settings = array('authsource', 'host', 'port', 'basedn', 'username_field', 'filter', 'attribute_field', 'binddn', 'bindpass', 'periodic_sync', 'import_users', 'sync_interval', 'use_ssl', 'use_tls', 'ignore_ssl_error', 'login_provisioning');
break;
}

View File

@@ -3691,11 +3691,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
}
}
// Override active status if explicitly provided (IDP sync takes priority over template)
if (isset($_data['active'])) {
$mailbox_attributes['active'] = $_data['active'];
}
$mailbox_attributes['quota'] = intval($mailbox_attributes['quota'] / 1048576);
$result = mailbox('edit', 'mailbox', $mailbox_attributes);
if ($result === false) return $result;

View File

@@ -140,17 +140,32 @@ function session_check() {
);
return false;
}
if (!empty($_POST)) {
if ($_SESSION['CSRF']['TOKEN'] != $_POST['csrf_token']) {
$_SESSION['return'][] = array(
'type' => 'warning',
'msg' => 'session_token'
);
return false;
// Check if this is a POST request (form-encoded or JSON)
$is_post_request = !empty($_POST) || (
isset($_SERVER['CONTENT_TYPE']) &&
strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== false
);
if ($is_post_request) {
// Skip CSRF check for DataTables server-side processing endpoints
// These are read-only operations (equivalent to GET) authenticated by session
$is_search_endpoint = (
isset($_GET['query']) &&
preg_match('#^search/(domain|mailbox)$#', $_GET['query'])
);
if (!$is_search_endpoint && !empty($_POST)) {
if ($_SESSION['CSRF']['TOKEN'] != $_POST['csrf_token']) {
$_SESSION['return'][] = array(
'type' => 'warning',
'msg' => 'session_token'
);
return false;
}
unset($_POST['csrf_token']);
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
$_SESSION['CSRF']['TIME'] = time();
}
unset($_POST['csrf_token']);
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
$_SESSION['CSRF']['TIME'] = time();
}
return true;
}

View File

@@ -471,8 +471,13 @@ jQuery(function($){
hideTableExpandCollapseBtn('#tab-domains', '#domain_table');
},
ajax: {
type: "GET",
url: "/api/v1/get/domain/datatables",
type: "POST",
url: "/api/v1/search/domain",
contentType: "application/json",
processData: false,
data: function(d) {
return JSON.stringify(d);
},
dataSrc: function(json){
$.each(json.data, function(i, item) {
item.domain_name = escapeHtml(item.domain_name);
@@ -898,8 +903,13 @@ jQuery(function($){
hideTableExpandCollapseBtn('#tab-mailboxes', '#mailbox_table');
},
ajax: {
type: "GET",
url: "/api/v1/get/mailbox/datatables",
type: "POST",
url: "/api/v1/search/mailbox",
contentType: "application/json",
processData: false,
data: function(d) {
return JSON.stringify(d);
},
dataSrc: function(json){
$.each(json.data, function (i, item) {
item.quota = {

View File

@@ -91,6 +91,11 @@ if (isset($_GET['query'])) {
if ($action == 'delete') {
$_POST['items'] = $request;
}
// search
if ($action == 'search') {
// placeholder for search, as the request body is already decoded and available in $requestDecoded
}
}
api_log($_POST);
@@ -457,47 +462,6 @@ if (isset($_GET['query'])) {
case "domain":
switch ($object) {
case "datatables":
$table = ['domain', 'd'];
$primaryKey = 'domain';
$columns = [
['db' => 'domain', 'dt' => 2],
['db' => 'aliases', 'dt' => 3, 'order_subquery' => "SELECT COUNT(*) FROM `alias` WHERE (`domain`= `d`.`domain` OR `domain` IN (SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = `d`.`domain`)) AND `address` NOT IN (SELECT `username` FROM `mailbox`)"],
['db' => 'mailboxes', 'dt' => 4, 'order_subquery' => "SELECT COUNT(*) FROM `mailbox` WHERE `mailbox`.`domain` = `d`.`domain` AND (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)"],
['db' => 'quota', 'dt' => 5, 'order_subquery' => "SELECT COALESCE(SUM(`mailbox`.`quota`), 0) FROM `mailbox` WHERE `mailbox`.`domain` = `d`.`domain` AND (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)"],
['db' => 'stats', 'dt' => 6, 'dummy' => true, 'order_subquery' => "SELECT SUM(bytes) FROM `quota2` WHERE `quota2`.`username` IN (SELECT `username` FROM `mailbox` WHERE `domain` = `d`.`domain`)"],
['db' => 'defquota', 'dt' => 7],
['db' => 'maxquota', 'dt' => 8],
['db' => 'backupmx', 'dt' => 10],
['db' => 'tags', 'dt' => 14, 'dummy' => true, 'search' => ['join' => 'LEFT JOIN `tags_domain` AS `td` ON `td`.`domain` = `d`.`domain`', 'where_column' => '`td`.`tag_name`']],
['db' => 'active', 'dt' => 15],
];
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/ssp.class.php';
global $pdo;
if($_SESSION['mailcow_cc_role'] === 'admin') {
$data = SSP::simple($_GET, $pdo, $table, $primaryKey, $columns);
} elseif ($_SESSION['mailcow_cc_role'] === 'domainadmin') {
$data = SSP::complex($_GET, $pdo, $table, $primaryKey, $columns,
'INNER JOIN domain_admins as da ON da.domain = d.domain',
[
'condition' => 'da.active = 1 and da.username = :username',
'bindings' => ['username' => $_SESSION['mailcow_cc_username']]
]);
}
if (!empty($data['data'])) {
$domainsData = [];
foreach ($data['data'] as $domain) {
if ($details = mailbox('get', 'domain_details', $domain[2])) {
$domainsData[] = $details;
}
}
$data['data'] = $domainsData;
}
process_get_return($data);
break;
case "all":
$tags = null;
if (isset($_GET['tags']) && $_GET['tags'] != '')
@@ -997,46 +961,6 @@ if (isset($_GET['query'])) {
break;
case "mailbox":
switch ($object) {
case "datatables":
$table = ['mailbox', 'm'];
$primaryKey = 'username';
$columns = [
['db' => 'username', 'dt' => 2],
['db' => 'quota', 'dt' => 3],
['db' => 'last_mail_login', 'dt' => 4, 'dummy' => true, 'order_subquery' => "SELECT MAX(`datetime`) FROM `sasl_log` WHERE `service` != 'SSO' AND `username` = `m`.`username`"],
['db' => 'last_pw_change', 'dt' => 5, 'dummy' => true, 'order_subquery' => "JSON_EXTRACT(attributes, '$.passwd_update')"],
['db' => 'in_use', 'dt' => 6, 'dummy' => true, 'order_subquery' => "(SELECT SUM(bytes) FROM `quota2` WHERE `quota2`.`username` = `m`.`username`) / `m`.`quota`"],
['db' => 'name', 'dt' => 7],
['db' => 'messages', 'dt' => 20, 'dummy' => true, 'order_subquery' => "SELECT SUM(messages) FROM `quota2` WHERE `quota2`.`username` = `m`.`username`"],
['db' => 'tags', 'dt' => 23, 'dummy' => true, 'search' => ['join' => 'LEFT JOIN `tags_mailbox` AS `tm` ON `tm`.`username` = `m`.`username`', 'where_column' => '`tm`.`tag_name`']],
['db' => 'active', 'dt' => 24],
];
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/ssp.class.php';
global $pdo;
if($_SESSION['mailcow_cc_role'] === 'admin') {
$data = SSP::complex($_GET, $pdo, $table, $primaryKey, $columns, null, "(`m`.`kind` = '' OR `m`.`kind` = NULL)");
} elseif ($_SESSION['mailcow_cc_role'] === 'domainadmin') {
$data = SSP::complex($_GET, $pdo, $table, $primaryKey, $columns,
'INNER JOIN domain_admins as da ON da.domain = m.domain',
[
'condition' => "(`m`.`kind` = '' OR `m`.`kind` = NULL) AND `da`.`active` = 1 AND `da`.`username` = :username",
'bindings' => ['username' => $_SESSION['mailcow_cc_username']]
]);
}
if (!empty($data['data'])) {
$mailboxData = [];
foreach ($data['data'] as $mailbox) {
if ($details = mailbox('get', 'mailbox_details', $mailbox[2])) {
$mailboxData[] = $details;
}
}
$data['data'] = $mailboxData;
}
process_get_return($data);
break;
case "all":
case "reduced":
$tags = null;
@@ -1625,6 +1549,136 @@ if (isset($_GET['query'])) {
}
}
break;
case "search":
function process_search_return($return) {
if ($return === false) {
echo json_encode(array(
'type' => 'error',
'msg' => 'Cannot get item'
));
}
else {
echo json_encode($return, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
}
// only allow POST requests to SEARCH API endpoints
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
http_response_code(405);
echo json_encode(array(
'type' => 'error',
'msg' => 'only POST method is allowed'
));
exit();
}
// Load SSP class
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/ssp.class.php';
global $pdo;
switch ($category) {
case "domain":
$table = ['domain', 'd'];
$primaryKey = 'domain';
$columns = [
['db' => 'domain', 'dt' => 2],
['db' => 'aliases', 'dt' => 3, 'order_subquery' => "SELECT COUNT(*) FROM `alias` WHERE (`domain`= `d`.`domain` OR `domain` IN (SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = `d`.`domain`)) AND `address` NOT IN (SELECT `username` FROM `mailbox`)"],
['db' => 'mailboxes', 'dt' => 4, 'order_subquery' => "SELECT COUNT(*) FROM `mailbox` WHERE `mailbox`.`domain` = `d`.`domain` AND (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)"],
['db' => 'quota', 'dt' => 5, 'order_subquery' => "SELECT COALESCE(SUM(`mailbox`.`quota`), 0) FROM `mailbox` WHERE `mailbox`.`domain` = `d`.`domain` AND (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)"],
['db' => 'stats', 'dt' => 6, 'dummy' => true, 'order_subquery' => "SELECT SUM(bytes) FROM `quota2` WHERE `quota2`.`username` IN (SELECT `username` FROM `mailbox` WHERE `domain` = `d`.`domain`)"],
['db' => 'defquota', 'dt' => 7],
['db' => 'maxquota', 'dt' => 8],
['db' => 'backupmx', 'dt' => 10],
['db' => 'tags', 'dt' => 14, 'dummy' => true, 'search' => ['join' => 'LEFT JOIN `tags_domain` AS `td` ON `td`.`domain` = `d`.`domain`', 'where_column' => '`td`.`tag_name`']],
['db' => 'active', 'dt' => 15],
];
if($_SESSION['mailcow_cc_role'] === 'admin') {
$data = SSP::simple($requestDecoded, $pdo, $table, $primaryKey, $columns);
} elseif ($_SESSION['mailcow_cc_role'] === 'domainadmin') {
$data = SSP::complex($requestDecoded, $pdo, $table, $primaryKey, $columns,
'INNER JOIN domain_admins as da ON da.domain = d.domain',
[
'condition' => 'da.active = 1 and da.username = :username',
'bindings' => ['username' => $_SESSION['mailcow_cc_username']]
]);
} else {
http_response_code(403);
echo json_encode(array(
'type' => 'error',
'msg' => 'Insufficient permissions'
));
exit();
}
if (!empty($data['data'])) {
$domainsData = [];
foreach ($data['data'] as $domain) {
if ($details = mailbox('get', 'domain_details', $domain[2])) {
$domainsData[] = $details;
}
}
$data['data'] = $domainsData;
}
process_search_return($data);
break;
case "mailbox":
$table = ['mailbox', 'm'];
$primaryKey = 'username';
$columns = [
['db' => 'username', 'dt' => 2],
['db' => 'quota', 'dt' => 3],
['db' => 'last_mail_login', 'dt' => 4, 'dummy' => true, 'order_subquery' => "SELECT MAX(`datetime`) FROM `sasl_log` WHERE `service` != 'SSO' AND `username` = `m`.`username`"],
['db' => 'last_pw_change', 'dt' => 5, 'dummy' => true, 'order_subquery' => "JSON_EXTRACT(attributes, '$.passwd_update')"],
['db' => 'in_use', 'dt' => 6, 'dummy' => true, 'order_subquery' => "(SELECT SUM(bytes) FROM `quota2` WHERE `quota2`.`username` = `m`.`username`) / `m`.`quota`"],
['db' => 'name', 'dt' => 7],
['db' => 'messages', 'dt' => 20, 'dummy' => true, 'order_subquery' => "SELECT SUM(messages) FROM `quota2` WHERE `quota2`.`username` = `m`.`username`"],
['db' => 'tags', 'dt' => 23, 'dummy' => true, 'search' => ['join' => 'LEFT JOIN `tags_mailbox` AS `tm` ON `tm`.`username` = `m`.`username`', 'where_column' => '`tm`.`tag_name`']],
['db' => 'active', 'dt' => 24],
];
if($_SESSION['mailcow_cc_role'] === 'admin') {
$data = SSP::complex($requestDecoded, $pdo, $table, $primaryKey, $columns, null,
"(`m`.`kind` = '' OR `m`.`kind` = NULL)");
} elseif ($_SESSION['mailcow_cc_role'] === 'domainadmin') {
$data = SSP::complex($requestDecoded, $pdo, $table, $primaryKey, $columns,
'INNER JOIN domain_admins as da ON da.domain = m.domain',
[
'condition' => "(`m`.`kind` = '' OR `m`.`kind` = NULL) AND `da`.`active` = 1 AND `da`.`username` = :username",
'bindings' => ['username' => $_SESSION['mailcow_cc_username']]
]);
} else {
http_response_code(403);
echo json_encode(array(
'type' => 'error',
'msg' => 'Insufficient permissions'
));
exit();
}
if (!empty($data['data'])) {
$mailboxData = [];
foreach ($data['data'] as $mailbox) {
if ($details = mailbox('get', 'mailbox_details', $mailbox[2])) {
$mailboxData[] = $details;
}
}
$data['data'] = $mailboxData;
}
process_search_return($data);
break;
default:
http_response_code(404);
echo json_encode(array(
'type' => 'error',
'msg' => 'Invalid search category'
));
break;
}
break;
case "delete":
if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username']) || !isset($_SESSION["mailcow_cc_username"])) {
http_response_code(403);

View File

@@ -234,8 +234,6 @@
"iam_mapping": "Attribut Mapping",
"iam_bindpass": "Bind Passwort",
"iam_periodic_full_sync": "Vollsynchronisation",
"iam_sync_disabled_users": "Deaktivierte Benutzer synchronisieren",
"iam_sync_disabled_users_info": "Mailcow-Benutzer automatisch deaktivieren, wenn sie in LDAP/Keycloak deaktiviert sind, und wieder aktivieren, wenn sie reaktiviert werden. Unterstützt Active Directory (userAccountControl), OpenLDAP, 389 Directory Server und FreeIPA (pwdAccountLockedTime).",
"iam_port": "Port",
"iam_realm": "Realm",
"iam_redirect_url": "Redirect Url",

View File

@@ -241,8 +241,6 @@
"iam_mapping": "Attribute Mapping",
"iam_bindpass": "Bind Password",
"iam_periodic_full_sync": "Periodic Full Sync",
"iam_sync_disabled_users": "Sync user disabled status",
"iam_sync_disabled_users_info": "Automatically disable mailcow users when they are disabled in LDAP/Keycloak, and re-enable them when they are re-enabled. Supports Active Directory (userAccountControl), OpenLDAP, 389 Directory Server, and FreeIPA (pwdAccountLockedTime).",
"iam_port": "Port",
"iam_realm": "Realm",
"iam_redirect_url": "Redirect Url",

View File

@@ -38,7 +38,7 @@
"alias_domain": "Aizstājdomēni",
"alias_domain_info": "<small>Tikai derīgi domēna vārdi (komatu atdalīti).</small>",
"automap": "Mēģiniet automatizēt mapes (\"Nosūtītie vienumi\", \"Nosūtītie\" => \"Nosūtītie\" etc.)",
"backup_mx_options": "Dublējuma MX iespējas",
"backup_mx_options": "Releja iespējas",
"delete1": "Izdzēst no avota pēc pabeigšanas",
"delete2": "Dzēsiet ziņojumus galamērķī, kas nav avotā",
"delete2duplicates": "Izdzēst atkārtojošos vienumus galamērķī",
@@ -65,8 +65,8 @@
"relay_all": "Pārsūtīt visus saņēmējus",
"relay_all_info": "↪ Ja izvēlies <b>nepārsūtīt</b> visus saņēmējus, tad Tev būs nepieciešams pievienot (\"aklo\") pastkasti katram saņēmējam, kas būtu jāpārsūta.",
"relay_domain": "Pārsūtīt šo domēnu",
"select": "Lūdzu izvēlaties...",
"select_domain": "Lūdzu sākumā izvēlaties domēnu",
"select": "Lūgums atlasīt…",
"select_domain": "Lūgums vispirms atlasīt domēnu",
"sieve_desc": "Īss apraksts",
"sieve_type": "Filtra tips",
"skipcrossduplicates": "Izlaist dublētus ziņojumus pa mapēm (pirmais nāk, pirmais kalpo)",
@@ -153,7 +153,7 @@
"r_info": "Pelēkie/atspējotie vienumi spēkā esošo ierobežojumu sarakstā mailcow nav zināmi kā derīgi ierobežojumi, un tos nevar pārvietot. Nezināmi ierobežojumi jebkurā gadījumā parādīšanas secībā. <br>Jaunus vienumus var pievienot <code>inc/vars.local.inc.php</code>, lai varētu tos pārslēgt.",
"recipients": "Adresāts",
"refresh": "Atsvaidzināt",
"regen_api_key": "Reģenerēt API atslēgu",
"regen_api_key": "Atkārtoti izveidot API atslēgu",
"relay_from": "\"No:\" adrese",
"relay_run": "Palaist testu",
"relayhosts_hint": "Norādīt no sūtītāja atkarīgas piegādes, lai varētu tos atlasīt domēnu konfigurācijas uzvednē.<br>\n Piegādes pakalpojums vienmēr ir \"smtp\", tādējādi tiks mēģināts TLS, kad piedāvāts. Iekļautais TLS (SMTPS) netiek atbalstīts. Tiek ņemts vērā lietotāja atsevišķais izejošā TLS nosacījuma iestatījums.<br>\n Ietekmē atlasītos domēnus, tajā skaitā aizstājdomēnus.",
@@ -185,7 +185,10 @@
"login_time": "Pieteikšanās laiks",
"iam_version": "Versija",
"quarantine_max_age": "Lielākais pieļaujamais vecums dienās<br><small>Vērtībai jābūt vienādai ar vai lielākai par 1 dienu.</small>",
"quarantine_max_score": "Atmest paziņojumu, ja e-pasta ziņojuma mēstuļu novērtējums ir augstāks par šo vērtību:<br><small>Noklusējums ir 9999.0</small>"
"quarantine_max_score": "Atmest paziņojumu, ja e-pasta ziņojuma mēstuļu novērtējums ir augstāks par šo vērtību:<br><small>Noklusējums ir 9999.0</small>",
"options": "Iespējas",
"password_reset_settings": "Paroļu atkopes iestatījumi",
"password_settings": "Paroļu iestatījumi"
},
"danger": {
"access_denied": "Piekļuve liegta, vai nepareizi dati",
@@ -257,7 +260,7 @@
"active": "Aktīvs",
"alias": "Labot aizstājvārdu",
"automap": "Mēģiniet automatizēt mapes (\"Nosūtītie vienumi\", \"Nosūtītie\" => \"Nosūtītie\" utt.)",
"backup_mx_options": "Dublēt MX iespējas",
"backup_mx_options": "Retranslācijas iespējams",
"delete1": "Izdzēst no avota pēc pabeigšanas",
"delete2": "Dzēsiet ziņojumus galamērķī, kas nav avotā",
"delete2duplicates": "Izdzēst atkārtojošos vienumus galamērķī",
@@ -558,7 +561,7 @@
"key_id_totp": "Identifikators Jūsu atslēgai",
"none": "Deaktivizēt",
"scan_qr_code": "Lūdzu, skenējiet šo kodu ar savu autentifikācijas lietojumprogrammu vai ievadiet kodu manuāli.",
"select": "Lūdzu izvēlaties",
"select": "Lūgums atlasīt",
"set_tfa": "Uzstādīt difi faktoru autentifik;acijas metodi",
"tfa": "Divpakāpju pieteikšanās",
"totp": "Uz laiku bāzēta vienreizēja parole (Google Autentifikātors utt.)",

View File

@@ -249,17 +249,6 @@
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_sync_disabled_users }}</label>
</div>
<div class="col-12 col-md-9">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="sync_disabled_users" value="1" {% if iam_settings.sync_disabled_users == 1 %}checked{% endif %}>
<label class="form-check-label text-muted small">{{ lang.admin.iam_sync_disabled_users_info }}</label>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_sync_interval }}</label>
@@ -707,17 +696,6 @@
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_sync_disabled_users }}</label>
</div>
<div class="col-12 col-md-9">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="sync_disabled_users" value="1" {% if iam_settings.sync_disabled_users == 1 %}checked{% endif %}>
<label class="form-check-label text-muted small">{{ lang.admin.iam_sync_disabled_users_info }}</label>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_sync_interval }}</label>