diff --git a/data/web/admin.php b/data/web/admin.php index 8766b211..64ad77ec 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -32,7 +32,7 @@ $tfa_data = get_tfa();
-
+
@@ -55,7 +55,7 @@ $tfa_data = get_tfa();
- +
@@ -94,20 +94,27 @@ $tfa_data = get_tfa();
-
- -
-
+
-
-
+
+
+
+
+ + +
- - +
-
- +
@@ -118,7 +125,7 @@ $tfa_data = get_tfa();
- ".htmlspecialchars($domain).""; @@ -148,13 +155,11 @@ $tfa_data = get_tfa();
- +
-
-
diff --git a/data/web/edit.php b/data/web/edit.php index b13626ea..d9121401 100644 --- a/data/web/edit.php +++ b/data/web/edit.php @@ -68,17 +68,17 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
"> - +
- +
- +
- +
- diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 28f76bfd..f6fe886a 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -193,11 +193,10 @@ function edit_admin_account($postarray) { ); return false; } - $username = $postarray['admin_user']; $username_now = $_SESSION['mailcow_cc_username']; + $username = $postarray['admin_user']; $password = $postarray['admin_pass']; $password2 = $postarray['admin_pass2']; - if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) { $_SESSION['return'] = array( 'type' => 'danger', @@ -425,7 +424,8 @@ function add_domain_admin($postarray) { $username = strtolower(trim($postarray['username'])); $password = $postarray['password']; $password2 = $postarray['password2']; - $active = intval($postarray['active']); + $domains = (array)$postarray['domains']; + $active = intval($postarray['active']); if ($_SESSION['mailcow_cc_role'] != "admin") { $_SESSION['return'] = array( @@ -434,7 +434,7 @@ function add_domain_admin($postarray) { ); return false; } - if (empty($postarray['domain'])) { + if (empty($domains)) { $_SESSION['return'] = array( 'type' => 'danger', 'msg' => sprintf($lang['danger']['domain_invalid']) @@ -496,7 +496,7 @@ function add_domain_admin($postarray) { return false; } $password_hashed = hash_password($password); - foreach ($postarray['domain'] as $domain) { + foreach ($domains as $domain) { if (!is_valid_domain_name($domain)) { $_SESSION['return'] = array( 'type' => 'danger', @@ -562,35 +562,37 @@ function delete_domain_admin($postarray) { ); return false; } - $username = $postarray['username']; - if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['username_invalid']) - ); - return false; - } - try { - $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username"); - $stmt->execute(array( - ':username' => $username, - )); - $stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username"); - $stmt->execute(array( - ':username' => $username, - )); - } - catch (PDOException $e) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => 'MySQL: '.$e - ); - return false; - } - $_SESSION['return'] = array( - 'type' => 'success', - 'msg' => sprintf($lang['success']['domain_admin_removed'], htmlspecialchars($username)) - ); + $usernames = (array)$postarray['username']; + foreach ($usernames as $username) { + if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['username_invalid']) + ); + return false; + } + try { + $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username"); + $stmt->execute(array( + ':username' => $username, + )); + $stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username"); + $stmt->execute(array( + ':username' => $username, + )); + } + catch (PDOException $e) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => 'MySQL: '.$e + ); + return false; + } + } + $_SESSION['return'] = array( + 'type' => 'success', + 'msg' => sprintf($lang['success']['domain_admin_removed'], htmlspecialchars(implode(', ', $usernames))) + ); } function get_domain_admins() { global $pdo; @@ -1069,73 +1071,151 @@ function edit_domain_admin($postarray) { } // Administrator if ($_SESSION['mailcow_cc_role'] == "admin") { - $username = $postarray['username']; - $username_now = $postarray['username_now']; - $password = $postarray['password']; - $password2 = $postarray['password2']; - $active = intval($postarray['active']); + if (!is_array($postarray['username'])) { + $usernames = array(); + $usernames[] = $postarray['username']; + } + else { + $usernames = $postarray['username']; + } + foreach ($usernames as $username) { + $is_now = get_domain_admin_details($username); + $domains = (isset($postarray['domains'])) ? (array)$postarray['domains'] : null; + if (!empty($is_now)) { + $active = (isset($postarray['active'])) ? $postarray['active'] : $is_now['active_int']; + $domains = (!empty($domains)) ? $domains : $is_now['selected_domains']; + $username_new = (!empty($postarray['username_new'])) ? $postarray['username_new'] : $is_now['username']; + } + else { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['access_denied']) + ); + return false; + } + $password = $postarray['password']; + $password2 = $postarray['password2']; - if(isset($postarray['domain'])) { - foreach ($postarray['domain'] as $domain) { - if (!is_valid_domain_name($domain)) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['domain_invalid']) - ); - return false; + if (!empty($domains)) { + foreach ($domains as $domain) { + if (!is_valid_domain_name($domain)) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['domain_invalid']) + ); + return false; + } } } - } - - if (empty($postarray['domain'])) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['domain_invalid']) - ); - return false; - } - - if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['username_invalid']) - ); - return false; - } - if ($username != $username_now) { - if (empty(get_domain_admin_details($username_now)['username']) || !empty(get_domain_admin_details($username)['username'])) { + if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) { $_SESSION['return'] = array( 'type' => 'danger', 'msg' => sprintf($lang['danger']['username_invalid']) ); return false; } - } - try { - $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username"); - $stmt->execute(array( - ':username' => $username_now, - )); - } - catch (PDOException $e) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => 'MySQL: '.$e - ); - return false; - } + if ($username_new != $username) { + if (!empty(get_domain_admin_details($username_new)['username'])) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['username_invalid']) + ); + return false; + } + } + try { + $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username"); + $stmt->execute(array( + ':username' => $username, + )); + } + catch (PDOException $e) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => 'MySQL: '.$e + ); + return false; + } - if (isset($postarray['domain'])) { - foreach ($postarray['domain'] as $domain) { + if (!empty($domains)) { + foreach ($domains as $domain) { + try { + $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`) + VALUES (:username_new, :domain, :created, :active)"); + $stmt->execute(array( + ':username_new' => $username_new, + ':domain' => $domain, + ':created' => date('Y-m-d H:i:s'), + ':active' => $active + )); + } + catch (PDOException $e) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => 'MySQL: '.$e + ); + return false; + } + } + } + + if (!empty($password) && !empty($password2)) { + if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['password_complexity']) + ); + return false; + } + if ($password != $password2) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['password_mismatch']) + ); + return false; + } + $password_hashed = hash_password($password); try { - $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`) - VALUES (:username, :domain, :created, :active)"); + $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username"); $stmt->execute(array( + ':password_hashed' => $password_hashed, + ':username_new' => $username_new, ':username' => $username, - ':domain' => $domain, - ':created' => date('Y-m-d H:i:s'), ':active' => $active )); + if (isset($postarray['disable_tfa'])) { + $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); + $stmt->execute(array(':username' => $username)); + } + else { + $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username"); + $stmt->execute(array(':username_new' => $username_new, ':username' => $username)); + } + } + catch (PDOException $e) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => 'MySQL: '.$e + ); + return false; + } + } + else { + try { + $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username"); + $stmt->execute(array( + ':username_new' => $username_new, + ':username' => $username, + ':active' => $active + )); + if (isset($postarray['disable_tfa'])) { + $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); + $stmt->execute(array(':username' => $username)); + } + else { + $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username"); + $stmt->execute(array(':username_new' => $username_new, ':username' => $username)); + } } catch (PDOException $e) { $_SESSION['return'] = array( @@ -1146,76 +1226,9 @@ function edit_domain_admin($postarray) { } } } - - if (!empty($password) && !empty($password2)) { - if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['password_complexity']) - ); - return false; - } - if ($password != $password2) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['password_mismatch']) - ); - return false; - } - $password_hashed = hash_password($password); - try { - $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `active` = :active, `password` = :password_hashed WHERE `username` = :username2"); - $stmt->execute(array( - ':password_hashed' => $password_hashed, - ':username1' => $username, - ':username2' => $username_now, - ':active' => $active - )); - if (isset($postarray['disable_tfa'])) { - $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); - $stmt->execute(array(':username' => $username_now)); - } - else { - $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username WHERE `username` = :username_now"); - $stmt->execute(array(':username' => $username, ':username_now' => $username_now)); - } - } - catch (PDOException $e) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => 'MySQL: '.$e - ); - return false; - } - } - else { - try { - $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `active` = :active WHERE `username` = :username2"); - $stmt->execute(array( - ':username1' => $username, - ':username2' => $username_now, - ':active' => $active - )); - if (isset($postarray['disable_tfa'])) { - $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); - $stmt->execute(array(':username' => $username)); - } - else { - $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username WHERE `username` = :username_now"); - $stmt->execute(array(':username' => $username, ':username_now' => $username_now)); - } - } - catch (PDOException $e) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => 'MySQL: '.$e - ); - return false; - } - } $_SESSION['return'] = array( 'type' => 'success', - 'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars($username)) + 'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars(implode(', ', $usernames))) ); } // Domain administrator diff --git a/data/web/inc/triggers.inc.php b/data/web/inc/triggers.inc.php index 43e65484..1fcaf256 100644 --- a/data/web/inc/triggers.inc.php +++ b/data/web/inc/triggers.inc.php @@ -63,12 +63,6 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi if (isset($_POST["dkim_add_key"])) { dkim_add_key($_POST); } - if (isset($_POST["add_domain_admin"])) { - add_domain_admin($_POST); - } - if (isset($_POST["delete_domain_admin"])) { - delete_domain_admin($_POST); - } if (isset($_POST["add_forwarding_host"])) { add_forwarding_host($_POST); } diff --git a/data/web/js/admin.js b/data/web/js/admin.js index 810653ee..f99bdac7 100644 --- a/data/web/js/admin.js +++ b/data/web/js/admin.js @@ -175,6 +175,7 @@ jQuery(function($){ function draw_domain_admins() { ft_domainadmins = FooTable.init('#domainadminstable', { "columns": [ + {"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"}, {"sorted": true,"name":"username","title":lang.username,"style":{"width":"250px"}}, {"name":"selected_domains","title":lang.admin_domains,"breakpoints":"xs sm"}, {"name":"tfa_active","title":"TFA", "filterable": false,"style":{"maxWidth":"80px","width":"80px"}}, @@ -190,9 +191,9 @@ jQuery(function($){ }, success: function (data) { $.each(data, function (i, item) { + item.chkbox = ''; item.action = ''; }); } @@ -214,7 +215,7 @@ jQuery(function($){ }); } function draw_fwd_hosts() { - ft_domainadmins = FooTable.init('#forwardinghoststable', { + ft_forwardinghoststable = FooTable.init('#forwardinghoststable', { "columns": [ {"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"}, {"name":"host","type":"text","title":lang.host,"style":{"width":"250px"}}, diff --git a/data/web/js/api.js b/data/web/js/api.js index 0343df43..f1f760be 100644 --- a/data/web/js/api.js +++ b/data/web/js/api.js @@ -1,4 +1,19 @@ $(document).ready(function() { + $.fn.serializeObject = function() { + var o = {}; + var a = this.serializeArray(); + $.each(a, function() { + if (o[this.name]) { + if (!o[this.name].push) { + o[this.name] = [o[this.name]]; + } + o[this.name].push(this.value || ''); + } else { + o[this.name] = this.value || ''; + } + }); + return o; + }; // Collect values of input fields with name "multi_select" an same data-id to js array multi_data[data-id] var multi_data = []; $(document).on('change', 'input[name=multi_select]:checkbox', function() { @@ -43,10 +58,7 @@ $(document).ready(function() { // If clicked element #edit_selected is in a form with the same data-id as the button, // we merge all input fields by {"name":"value"} into api-attr if ($(this).closest("form").data('id') == id) { - var attr_to_merge = {}; - $.each($(this).closest("form").serializeArray(), function(i, field) { - attr_to_merge[field.name] = field.value; - }); + var attr_to_merge = $(this).closest("form").serializeObject(); var api_attr = $.extend(api_attr, attr_to_merge) } // If clicked element #edit_selected has data-item attribute, it is added to "items" @@ -59,6 +71,7 @@ $(document).ready(function() { } if (typeof multi_data[id] == "undefined") return; api_items = multi_data[id]; + if (Object.keys(api_items).length !== 0) { $.ajax({ type: "POST", @@ -85,10 +98,7 @@ $(document).ready(function() { // If clicked button is in a form with the same data-id as the button, // we merge all input fields by {"name":"value"} into api-attr if ($(this).closest("form").data('id') == id) { - var attr_to_merge = {}; - $.each($(this).closest("form").serializeArray(), function(i, field) { - attr_to_merge[field.name] = field.value; - }); + var attr_to_merge = $(this).closest("form").serializeObject(); var api_attr = $.extend(api_attr, attr_to_merge) } $.ajax({ @@ -125,30 +135,33 @@ $(document).ready(function() { if (typeof multi_data[id] == "undefined" || multi_data[id] == "") return; data_array = multi_data[id]; api_url = $(this).data('api-url'); - $(document).on('show.bs.modal','#ConfirmDeleteModal', function () { - $("#ItemsToDelete").empty(); - for (var i in data_array) { - $("#ItemsToDelete").append("
  • " + data_array[i] + "
  • "); + $(document).on('show.bs.modal','#ConfirmDeleteModal', function () { + $("#ItemsToDelete").empty(); + for (var i in data_array) { + $("#ItemsToDelete").append("
  • " + data_array[i] + "
  • "); + } + }) + $('#ConfirmDeleteModal').modal({ + backdrop: 'static', + keyboard: false + }) + .one('click', '#IsConfirmed', function(e) { + $.ajax({ + type: "POST", + dataType: "json", + cache: false, + data: { "items": JSON.stringify(data_array), "csrf_token": csrf_token }, + url: '/api/v1/' + api_url, + jsonp: false, + complete: function (data) { + window.location = window.location.href.split("#")[0]; } - }) - $('#ConfirmDeleteModal').modal({ - backdrop: 'static', - keyboard: false - }) - .one('click', '#IsConfirmed', function(e) { - $.ajax({ - type: "POST", - dataType: "json", - data: { "items": JSON.stringify(data_array), "csrf_token": csrf_token }, - url: '/api/v1/' + api_url, - jsonp: false, - complete: function (data) { - window.location = window.location.href.split("#")[0]; - } - }); - }) - .one('click', '#isCanceled', function(e) { - $('#ConfirmDeleteModal').modal('hide'); - });; + }); + }) + .one('click', '#isCanceled', function(e) { + // Remove event handler to allow to close modal and restart dialog without multiple submits + $('#ConfirmDeleteModal').off(); + $('#ConfirmDeleteModal').modal('hide'); + }); }); }); \ No newline at end of file diff --git a/data/web/json_api.php b/data/web/json_api.php index 5c9d729f..70e42a35 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -60,6 +60,39 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u )); } break; + case "domain_admin": + if (isset($_POST['attr'])) { + $attr = (array)json_decode($_POST['attr'], true); + if (add_domain_admin($attr) === false) { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Cannot add item' + )); + } + } + else { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'success', + 'msg' => 'Task completed' + )); + } + } + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Cannot find attributes in post data' + )); + } + break; } break; case "get": @@ -947,6 +980,47 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u )); } break; + case "domain_admin": + if (isset($_POST['items'])) { + $items = (array)json_decode($_POST['items'], true); + if (is_array($items)) { + if (delete_domain_admin(array('username' => $items)) === false) { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Task failed' + )); + } + } + else { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'success', + 'msg' => 'Task completed' + )); + } + } + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Cannot find name array in post data' + )); + } + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Cannot find items in post data' + )); + } + break; } break; case "edit": @@ -1391,6 +1465,85 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u )); } break; + case "domain_admin": + if (isset($_POST['items']) && isset($_POST['attr'])) { + $items = (array)json_decode($_POST['items'], true); + $attr = (array)json_decode($_POST['attr'], true); + $postarray = array_merge(array('username' => $items), $attr); + if (is_array($postarray['username'])) { + if (edit_domain_admin($postarray) === false) { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Edit failed' + )); + } + exit(); + } + else { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'success', + 'msg' => 'Task completed' + )); + } + } + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Incomplete post data' + )); + } + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Incomplete post data' + )); + } + break; + case "admin": + // No items as there is only one admin + if (isset($_POST['attr'])) { + $attr = (array)json_decode($_POST['attr'], true); + if (edit_admin_account($attr) === false) { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Edit failed' + )); + } + exit(); + } + else { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'success', + 'msg' => 'Task completed' + )); + } + } + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Incomplete post data' + )); + } + break; } break; } diff --git a/data/web/user.php b/data/web/user.php index 4fa4abc8..63ee6858 100644 --- a/data/web/user.php +++ b/data/web/user.php @@ -31,7 +31,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma foreach ($tfa_data['additional'] as $key_info): ?> -
    ?? []
    +
    🔑 []