|
@@ -905,7 +905,7 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|
}
|
|
}
|
|
|
|
|
|
// Validate mailbox user
|
|
// Validate mailbox user
|
|
- $stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
|
|
|
|
|
|
+ $stmt = $pdo->prepare("SELECT `password`, JSON_UNQUOTE(JSON_EXTRACT(`attributes`, '$.force_pw_update')) AS `force_pw_update` FROM `mailbox`
|
|
INNER JOIN domain on mailbox.domain = domain.domain
|
|
INNER JOIN domain on mailbox.domain = domain.domain
|
|
WHERE `kind` NOT REGEXP 'location|thing|group'
|
|
WHERE `kind` NOT REGEXP 'location|thing|group'
|
|
AND `mailbox`.`active`='1'
|
|
AND `mailbox`.`active`='1'
|
|
@@ -939,11 +939,17 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|
$stmt->execute(array(':user' => $user));
|
|
$stmt->execute(array(':user' => $user));
|
|
$rows = array_merge($rows, $stmt->fetchAll(PDO::FETCH_ASSOC));
|
|
$rows = array_merge($rows, $stmt->fetchAll(PDO::FETCH_ASSOC));
|
|
}
|
|
}
|
|
- foreach ($rows as $row) {
|
|
|
|
|
|
+ foreach ($rows as $row) {
|
|
// verify password
|
|
// verify password
|
|
if (verify_hash($row['password'], $pass) !== false) {
|
|
if (verify_hash($row['password'], $pass) !== false) {
|
|
- if (!array_key_exists("app_passwd_id", $row)){
|
|
|
|
|
|
+ if (!array_key_exists("app_passwd_id", $row)){
|
|
// password is not a app password
|
|
// password is not a app password
|
|
|
|
+
|
|
|
|
+ // check if pw update is required
|
|
|
|
+ if (array_key_exists('force_pw_update', $row) && intval($row['force_pw_update']) == 1) {
|
|
|
|
+ $_SESSION['pending_pw_update'] = True;
|
|
|
|
+ }
|
|
|
|
+
|
|
// check for tfa authenticators
|
|
// check for tfa authenticators
|
|
$authenticators = get_tfa($user);
|
|
$authenticators = get_tfa($user);
|
|
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 &&
|
|
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 &&
|
|
@@ -1028,7 +1034,7 @@ function update_sogo_static_view($mailbox = null) {
|
|
// Check if the mailbox exists
|
|
// Check if the mailbox exists
|
|
$stmt = $pdo->prepare("SELECT username FROM mailbox WHERE username = :mailbox AND active = '1'");
|
|
$stmt = $pdo->prepare("SELECT username FROM mailbox WHERE username = :mailbox AND active = '1'");
|
|
$stmt->execute(array(':mailbox' => $mailbox));
|
|
$stmt->execute(array(':mailbox' => $mailbox));
|
|
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
|
|
+ $row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
if ($row){
|
|
if ($row){
|
|
$mailbox_exists = true;
|
|
$mailbox_exists = true;
|
|
}
|
|
}
|
|
@@ -1056,7 +1062,7 @@ function update_sogo_static_view($mailbox = null) {
|
|
LEFT OUTER JOIN grouped_sender_acl_external external_acl ON external_acl.username = mailbox.username
|
|
LEFT OUTER JOIN grouped_sender_acl_external external_acl ON external_acl.username = mailbox.username
|
|
WHERE
|
|
WHERE
|
|
mailbox.active = '1'";
|
|
mailbox.active = '1'";
|
|
-
|
|
|
|
|
|
+
|
|
if ($mailbox_exists) {
|
|
if ($mailbox_exists) {
|
|
$query .= " AND mailbox.username = :mailbox";
|
|
$query .= " AND mailbox.username = :mailbox";
|
|
$stmt = $pdo->prepare($query);
|
|
$stmt = $pdo->prepare($query);
|
|
@@ -1065,9 +1071,9 @@ function update_sogo_static_view($mailbox = null) {
|
|
$query .= " GROUP BY mailbox.username";
|
|
$query .= " GROUP BY mailbox.username";
|
|
$stmt = $pdo->query($query);
|
|
$stmt = $pdo->query($query);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
$stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");
|
|
$stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");
|
|
-
|
|
|
|
|
|
+
|
|
flush_memcached();
|
|
flush_memcached();
|
|
}
|
|
}
|
|
function edit_user_account($_data) {
|
|
function edit_user_account($_data) {
|
|
@@ -1079,9 +1085,10 @@ function edit_user_account($_data) {
|
|
!isset($_data_log['user_new_pass2']) ?: $_data_log['user_new_pass2'] = '*';
|
|
!isset($_data_log['user_new_pass2']) ?: $_data_log['user_new_pass2'] = '*';
|
|
!isset($_data_log['user_old_pass']) ?: $_data_log['user_old_pass'] = '*';
|
|
!isset($_data_log['user_old_pass']) ?: $_data_log['user_old_pass'] = '*';
|
|
|
|
|
|
- $username = $_SESSION['mailcow_cc_username'];
|
|
|
|
- $role = $_SESSION['mailcow_cc_role'];
|
|
|
|
|
|
+ $username = (!empty($_data['username'])) ? $_data['username'] : $_SESSION['mailcow_cc_username'];
|
|
|
|
+ $role = (!empty($_data['role'])) ? $_data['role'] : $_SESSION['mailcow_cc_role'];
|
|
$password_old = $_data['user_old_pass'];
|
|
$password_old = $_data['user_old_pass'];
|
|
|
|
+ $skip_old_password_check = $_data['skip_old_password_check'];
|
|
$pw_recovery_email = $_data['pw_recovery_email'];
|
|
$pw_recovery_email = $_data['pw_recovery_email'];
|
|
|
|
|
|
if (filter_var($username, FILTER_VALIDATE_EMAIL === false) || $role != 'user') {
|
|
if (filter_var($username, FILTER_VALIDATE_EMAIL === false) || $role != 'user') {
|
|
@@ -1094,22 +1101,24 @@ function edit_user_account($_data) {
|
|
}
|
|
}
|
|
|
|
|
|
// edit password
|
|
// edit password
|
|
- if (!empty($password_old) && !empty($_data['user_new_pass']) && !empty($_data['user_new_pass2'])) {
|
|
|
|
- $stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
|
|
|
|
- WHERE `kind` NOT REGEXP 'location|thing|group'
|
|
|
|
- AND `username` = :user");
|
|
|
|
- $stmt->execute(array(':user' => $username));
|
|
|
|
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
-
|
|
|
|
- if (!verify_hash($row['password'], $password_old)) {
|
|
|
|
- $_SESSION['return'][] = array(
|
|
|
|
- 'type' => 'danger',
|
|
|
|
- 'log' => array(__FUNCTION__, $_data_log),
|
|
|
|
- 'msg' => 'access_denied'
|
|
|
|
- );
|
|
|
|
- return false;
|
|
|
|
|
|
+ if ((!empty($password_old) || $skip_old_password_check) && !empty($_data['user_new_pass']) && !empty($_data['user_new_pass2'])) {
|
|
|
|
+ if (!$skip_old_password_check) {
|
|
|
|
+ $stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
|
|
|
|
+ WHERE `kind` NOT REGEXP 'location|thing|group'
|
|
|
|
+ AND `username` = :user");
|
|
|
|
+ $stmt->execute(array(':user' => $username));
|
|
|
|
+ $row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
+
|
|
|
|
+ if (!verify_hash($row['password'], $password_old)) {
|
|
|
|
+ $_SESSION['return'][] = array(
|
|
|
|
+ 'type' => 'danger',
|
|
|
|
+ 'log' => array(__FUNCTION__, $_data_log),
|
|
|
|
+ 'msg' => 'access_denied'
|
|
|
|
+ );
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
$password_new = $_data['user_new_pass'];
|
|
$password_new = $_data['user_new_pass'];
|
|
$password_new2 = $_data['user_new_pass2'];
|
|
$password_new2 = $_data['user_new_pass2'];
|
|
if (password_check($password_new, $password_new2) !== true) {
|
|
if (password_check($password_new, $password_new2) !== true) {
|
|
@@ -1124,7 +1133,7 @@ function edit_user_account($_data) {
|
|
':password_hashed' => $password_hashed,
|
|
':password_hashed' => $password_hashed,
|
|
':username' => $username
|
|
':username' => $username
|
|
));
|
|
));
|
|
-
|
|
|
|
|
|
+
|
|
update_sogo_static_view();
|
|
update_sogo_static_view();
|
|
}
|
|
}
|
|
// edit password recovery email
|
|
// edit password recovery email
|
|
@@ -1152,6 +1161,7 @@ function edit_user_account($_data) {
|
|
'log' => array(__FUNCTION__, $_data_log),
|
|
'log' => array(__FUNCTION__, $_data_log),
|
|
'msg' => array('mailbox_modified', htmlspecialchars($username))
|
|
'msg' => array('mailbox_modified', htmlspecialchars($username))
|
|
);
|
|
);
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
function user_get_alias_details($username) {
|
|
function user_get_alias_details($username) {
|
|
global $pdo;
|
|
global $pdo;
|
|
@@ -1374,7 +1384,7 @@ function set_tfa($_data) {
|
|
$_data['registration']->certificate,
|
|
$_data['registration']->certificate,
|
|
0
|
|
0
|
|
));
|
|
));
|
|
-
|
|
|
|
|
|
+
|
|
$_SESSION['return'][] = array(
|
|
$_SESSION['return'][] = array(
|
|
'type' => 'success',
|
|
'type' => 'success',
|
|
'log' => array(__FUNCTION__, $_data_log),
|
|
'log' => array(__FUNCTION__, $_data_log),
|
|
@@ -1544,7 +1554,7 @@ function unset_tfa_key($_data) {
|
|
|
|
|
|
try {
|
|
try {
|
|
if (!is_numeric($id)) $access_denied = true;
|
|
if (!is_numeric($id)) $access_denied = true;
|
|
-
|
|
|
|
|
|
+
|
|
// set access_denied error
|
|
// set access_denied error
|
|
if ($access_denied){
|
|
if ($access_denied){
|
|
$_SESSION['return'][] = array(
|
|
$_SESSION['return'][] = array(
|
|
@@ -1553,7 +1563,7 @@ function unset_tfa_key($_data) {
|
|
'msg' => 'access_denied'
|
|
'msg' => 'access_denied'
|
|
);
|
|
);
|
|
return false;
|
|
return false;
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
// check if it's last key
|
|
// check if it's last key
|
|
$stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
|
|
$stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
|
|
@@ -1602,7 +1612,7 @@ function get_tfa($username = null, $id = null) {
|
|
WHERE `username` = :username AND `active` = '1'");
|
|
WHERE `username` = :username AND `active` = '1'");
|
|
$stmt->execute(array(':username' => $username));
|
|
$stmt->execute(array(':username' => $username));
|
|
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
-
|
|
|
|
|
|
+
|
|
// no tfa methods found
|
|
// no tfa methods found
|
|
if (count($results) == 0) {
|
|
if (count($results) == 0) {
|
|
$data['name'] = 'none';
|
|
$data['name'] = 'none';
|
|
@@ -1810,8 +1820,8 @@ function verify_tfa_login($username, $_data) {
|
|
'msg' => array('webauthn_authenticator_failed')
|
|
'msg' => array('webauthn_authenticator_failed')
|
|
);
|
|
);
|
|
return false;
|
|
return false;
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
if (empty($process_webauthn['publicKey']) || $process_webauthn['publicKey'] === false) {
|
|
if (empty($process_webauthn['publicKey']) || $process_webauthn['publicKey'] === false) {
|
|
$_SESSION['return'][] = array(
|
|
$_SESSION['return'][] = array(
|
|
'type' => 'danger',
|
|
'type' => 'danger',
|
|
@@ -2173,7 +2183,7 @@ function cors($action, $data = null) {
|
|
'msg' => 'access_denied'
|
|
'msg' => 'access_denied'
|
|
);
|
|
);
|
|
return false;
|
|
return false;
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
$allowed_origins = isset($data['allowed_origins']) ? $data['allowed_origins'] : array($_SERVER['SERVER_NAME']);
|
|
$allowed_origins = isset($data['allowed_origins']) ? $data['allowed_origins'] : array($_SERVER['SERVER_NAME']);
|
|
$allowed_origins = !is_array($allowed_origins) ? array_filter(array_map('trim', explode("\n", $allowed_origins))) : $allowed_origins;
|
|
$allowed_origins = !is_array($allowed_origins) ? array_filter(array_map('trim', explode("\n", $allowed_origins))) : $allowed_origins;
|
|
@@ -2206,7 +2216,7 @@ function cors($action, $data = null) {
|
|
$redis->hMSet('CORS_SETTINGS', array(
|
|
$redis->hMSet('CORS_SETTINGS', array(
|
|
'allowed_origins' => implode(', ', $allowed_origins),
|
|
'allowed_origins' => implode(', ', $allowed_origins),
|
|
'allowed_methods' => implode(', ', $allowed_methods)
|
|
'allowed_methods' => implode(', ', $allowed_methods)
|
|
- ));
|
|
|
|
|
|
+ ));
|
|
} catch (RedisException $e) {
|
|
} catch (RedisException $e) {
|
|
$_SESSION['return'][] = array(
|
|
$_SESSION['return'][] = array(
|
|
'type' => 'danger',
|
|
'type' => 'danger',
|
|
@@ -2258,10 +2268,10 @@ function cors($action, $data = null) {
|
|
header('Access-Control-Allow-Headers: Accept, Content-Type, X-Api-Key, Origin');
|
|
header('Access-Control-Allow-Headers: Accept, Content-Type, X-Api-Key, Origin');
|
|
|
|
|
|
// Access-Control settings requested, this is just a preflight request
|
|
// Access-Control settings requested, this is just a preflight request
|
|
- if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS' &&
|
|
|
|
|
|
+ if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS' &&
|
|
isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']) &&
|
|
isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']) &&
|
|
isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
|
|
isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
|
|
-
|
|
|
|
|
|
+
|
|
$allowed_methods = explode(', ', $cors_settings["allowed_methods"]);
|
|
$allowed_methods = explode(', ', $cors_settings["allowed_methods"]);
|
|
if (in_array($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'], $allowed_methods, true))
|
|
if (in_array($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'], $allowed_methods, true))
|
|
// method allowed send 200 OK
|
|
// method allowed send 200 OK
|
|
@@ -2315,7 +2325,7 @@ function reset_password($action, $data = null) {
|
|
break;
|
|
break;
|
|
case 'issue':
|
|
case 'issue':
|
|
$username = $data;
|
|
$username = $data;
|
|
-
|
|
|
|
|
|
+
|
|
// perform cleanup
|
|
// perform cleanup
|
|
$stmt = $pdo->prepare("DELETE FROM `reset_password` WHERE created < DATE_SUB(NOW(), INTERVAL :lifetime MINUTE);");
|
|
$stmt = $pdo->prepare("DELETE FROM `reset_password` WHERE created < DATE_SUB(NOW(), INTERVAL :lifetime MINUTE);");
|
|
$stmt->execute(array(':lifetime' => $PW_RESET_TOKEN_LIFETIME));
|
|
$stmt->execute(array(':lifetime' => $PW_RESET_TOKEN_LIFETIME));
|
|
@@ -2397,8 +2407,8 @@ function reset_password($action, $data = null) {
|
|
$request_date = new DateTime();
|
|
$request_date = new DateTime();
|
|
$locale_date = locale_get_default();
|
|
$locale_date = locale_get_default();
|
|
$date_formatter = new IntlDateFormatter(
|
|
$date_formatter = new IntlDateFormatter(
|
|
- $locale_date,
|
|
|
|
- IntlDateFormatter::FULL,
|
|
|
|
|
|
+ $locale_date,
|
|
|
|
+ IntlDateFormatter::FULL,
|
|
IntlDateFormatter::FULL
|
|
IntlDateFormatter::FULL
|
|
);
|
|
);
|
|
$formatted_request_date = $date_formatter->format($request_date);
|
|
$formatted_request_date = $date_formatter->format($request_date);
|
|
@@ -2514,7 +2524,7 @@ function reset_password($action, $data = null) {
|
|
$stmt->execute(array(
|
|
$stmt->execute(array(
|
|
':username' => $username
|
|
':username' => $username
|
|
));
|
|
));
|
|
-
|
|
|
|
|
|
+
|
|
$_SESSION['return'][] = array(
|
|
$_SESSION['return'][] = array(
|
|
'type' => 'success',
|
|
'type' => 'success',
|
|
'log' => array(__FUNCTION__, $action, $_data_log),
|
|
'log' => array(__FUNCTION__, $action, $_data_log),
|
|
@@ -2557,7 +2567,7 @@ function reset_password($action, $data = null) {
|
|
$text = $data['text'];
|
|
$text = $data['text'];
|
|
$html = $data['html'];
|
|
$html = $data['html'];
|
|
$subject = $data['subject'];
|
|
$subject = $data['subject'];
|
|
-
|
|
|
|
|
|
+
|
|
if (!filter_var($from, FILTER_VALIDATE_EMAIL)) {
|
|
if (!filter_var($from, FILTER_VALIDATE_EMAIL)) {
|
|
$_SESSION['return'][] = array(
|
|
$_SESSION['return'][] = array(
|
|
'type' => 'danger',
|
|
'type' => 'danger',
|
|
@@ -2590,7 +2600,7 @@ function reset_password($action, $data = null) {
|
|
);
|
|
);
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
ini_set('max_execution_time', 0);
|
|
ini_set('max_execution_time', 0);
|
|
ini_set('max_input_time', 0);
|
|
ini_set('max_input_time', 0);
|
|
$mail = new PHPMailer;
|
|
$mail = new PHPMailer;
|
|
@@ -2622,7 +2632,7 @@ function reset_password($action, $data = null) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
$mail->ClearAllRecipients();
|
|
$mail->ClearAllRecipients();
|
|
-
|
|
|
|
|
|
+
|
|
return true;
|
|
return true;
|
|
break;
|
|
break;
|
|
}
|
|
}
|