diff --git a/data/web/debug.php b/data/web/admin/dashboard.php similarity index 81% rename from data/web/debug.php rename to data/web/admin/dashboard.php index e93b2a65..473f28de 100644 --- a/data/web/debug.php +++ b/data/web/admin/dashboard.php @@ -1,8 +1,17 @@ Get('LICENSE_STATUS_CAC $_SESSION['gal'] = json_decode($license_cache, true); } -$js_minifier->add('/web/js/site/debug.js'); +$js_minifier->add('/web/js/site/dashboard.js'); // vmail df $exec_fields = array('cmd' => 'system', 'task' => 'df', 'dir' => '/var/vmail'); @@ -59,7 +68,7 @@ foreach ($containers_info as $container => $container_info) { $hostname = getenv('MAILCOW_HOSTNAME'); $timezone = getenv('TZ'); -$template = 'debug.twig'; +$template = 'dashboard.twig'; $template_data = [ 'log_lines' => getenv('LOG_LINES'), 'vmail_df' => $vmail_df, diff --git a/data/web/admin/index.php b/data/web/admin/index.php new file mode 100644 index 00000000..05ba7033 --- /dev/null +++ b/data/web/admin/index.php @@ -0,0 +1,29 @@ + @$_SESSION['ldelay'] +]; + +$js_minifier->add('/web/js/site/index.js'); +require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php'; diff --git a/data/web/mailbox.php b/data/web/admin/mailbox.php similarity index 73% rename from data/web/mailbox.php rename to data/web/admin/mailbox.php index a84e32c4..d0073bbd 100644 --- a/data/web/mailbox.php +++ b/data/web/admin/mailbox.php @@ -1,10 +1,20 @@ add('/web/js/site/mailbox.js'); $js_minifier->add('/web/js/presets/sieveMailbox.js'); $js_minifier->add('/web/js/site/pwgen.js'); -$role = ($_SESSION['mailcow_cc_role'] == "admin") ? 'admin' : 'domainadmin'; +$role = "admin"; $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? 'true' : 'false'; $allow_admin_email_login = (preg_match("/^([yY][eE][sS]|[yY])+$/", $_ENV["ALLOW_ADMIN_EMAIL_LOGIN"])) ? 'true' : 'false'; diff --git a/data/web/queue.php b/data/web/admin/queue.php similarity index 55% rename from data/web/queue.php rename to data/web/admin/queue.php index ffce8d8b..85ec5940 100644 --- a/data/web/queue.php +++ b/data/web/admin/queue.php @@ -1,8 +1,17 @@ add('/web/js/site/queue.js'); $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; -$role = ($_SESSION['mailcow_cc_role'] == "admin") ? 'admin' : 'domainadmin'; +$role = "admin"; $template = 'queue.twig'; $template_data = [ diff --git a/data/web/admin.php b/data/web/admin/system.php similarity index 90% rename from data/web/admin.php rename to data/web/admin/system.php index 5a8895de..c21d43f0 100644 --- a/data/web/admin.php +++ b/data/web/admin/system.php @@ -1,8 +1,17 @@ thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td { padding: 3px; } -table tbody tr { - cursor: pointer; -} table tbody tr td input[type="checkbox"] { cursor: pointer; } diff --git a/data/web/domainadmin/index.php b/data/web/domainadmin/index.php new file mode 100644 index 00000000..2d909f97 --- /dev/null +++ b/data/web/domainadmin/index.php @@ -0,0 +1,28 @@ + @$_SESSION['ldelay'], +]; + +$js_minifier->add('/web/js/site/index.js'); +require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php'; diff --git a/data/web/domainadmin/mailbox.php b/data/web/domainadmin/mailbox.php new file mode 100644 index 00000000..bb2ef16f --- /dev/null +++ b/data/web/domainadmin/mailbox.php @@ -0,0 +1,58 @@ +add('/web/js/site/mailbox.js'); +$js_minifier->add('/web/js/presets/sieveMailbox.js'); +$js_minifier->add('/web/js/site/pwgen.js'); + +$role = "domainadmin"; +$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? 'true' : 'false'; +$allow_admin_email_login = (preg_match("/^([yY][eE][sS]|[yY])+$/", $_ENV["ALLOW_ADMIN_EMAIL_LOGIN"])) ? 'true' : 'false'; + +// domains +$domains = mailbox('get', 'domains'); + +// mailboxes +$mailboxes = []; +foreach ($domains as $domain) { + foreach (mailbox('get', 'mailboxes', $domain) as $mailbox) { + $mailboxes[] = $mailbox; + } +} + +$template = 'mailbox.twig'; +$template_data = [ + 'acl' => $_SESSION['acl'], + 'acl_json' => json_encode($_SESSION['acl']), + 'role' => $role, + 'is_dual' => $is_dual, + 'allow_admin_email_login' => $allow_admin_email_login, + 'global_filters' => mailbox('get', 'global_filter_details'), + 'domains' => $domains, + 'mailboxes' => $mailboxes, + 'lang_mailbox' => json_encode($lang['mailbox']), + 'lang_rl' => json_encode($lang['ratelimit']), + 'lang_edit' => json_encode($lang['edit']), + 'lang_datatables' => json_encode($lang['datatables']), +]; + +require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php'; \ No newline at end of file diff --git a/data/web/domainadmin/user.php b/data/web/domainadmin/user.php new file mode 100644 index 00000000..7f1b392e --- /dev/null +++ b/data/web/domainadmin/user.php @@ -0,0 +1,44 @@ + "get_friendly_names")); + $username = $_SESSION['mailcow_cc_username']; + + $template = 'domainadmin.twig'; + $template_data = [ + 'acl' => $_SESSION['acl'], + 'acl_json' => json_encode($_SESSION['acl']), + 'user_spam_score' => mailbox('get', 'spam_score', $username), + 'tfa_data' => $tfa_data, + 'fido2_data' => $fido2_data, + 'lang_user' => json_encode($lang['user']), + 'lang_datatables' => json_encode($lang['datatables']), + ]; +} +elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') { + header('Location: /admin/dashboard'); + exit(); +} +elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { + header('Location: /user'); + exit(); +} +else { + header('Location: /domainadmin'); + exit(); +} + +$js_minifier->add('/web/js/site/user.js'); +$js_minifier->add('/web/js/site/pwgen.js'); + +require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php'; diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index a434582c..7969c6bb 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -2337,12 +2337,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) { switch ($_data['authsource']) { case 'keycloak': - case 'generic-oidc': - if ($_data['authsource'] == 'keycloak') { - $url = "{$_data['server_url']}/realms/{$_data['realm']}/protocol/openid-connect/token"; - } else { - $url = $_data['token_url']; - } + $url = "{$_data['server_url']}/realms/{$_data['realm']}/protocol/openid-connect/token"; $req = http_build_query(array( 'grant_type' => 'client_credentials', 'client_id' => $_data['client_id'], @@ -2355,6 +2350,29 @@ function identity_provider($_action = null, $_data = null, $_extra = null) { curl_setopt($curl, CURLOPT_POSTFIELDS, $req); curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded')); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + if ($_data['ignore_ssl_error'] == "1"){ + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); + } + $res = curl_exec($curl); + $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + curl_close ($curl); + + if ($code != 200) { + return false; + } + break; + case 'generic-oidc': + $url = $_data['token_url']; + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_TIMEOUT, 7); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "OPTIONS"); + if ($_data['ignore_ssl_error'] == "1"){ + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); + } $res = curl_exec($curl); $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close ($curl); diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php index 3c672109..767a0024 100644 --- a/data/web/inc/init_db.inc.php +++ b/data/web/inc/init_db.inc.php @@ -4,7 +4,7 @@ function init_db_schema() try { global $pdo; - $db_version = "24012025_0923"; + $db_version = "27012025_1555"; $stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index 5738e7c0..deb5da8f 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -297,7 +297,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.rspamd.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.tls_policy_maps.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.transports.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/init_db.inc.php'; -require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.inc.php'; +require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.global.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/twig.inc.php'; init_db_schema(); if (isset($_SESSION['mailcow_cc_role'])) { diff --git a/data/web/inc/sessions.inc.php b/data/web/inc/sessions.inc.php index 67bdd35b..bbc08cf1 100644 --- a/data/web/inc/sessions.inc.php +++ b/data/web/inc/sessions.inc.php @@ -99,15 +99,30 @@ if (isset($_POST["logout"])) { unset($_SESSION['sogo-sso-user-allowed']); unset($_SESSION['sogo-sso-pass']); unset($_SESSION["dual-login"]); - header("Location: /mailbox"); + if ($_SESSION["mailcow_cc_role"] == "admin"){ + header("Location: /admin/mailbox"); + } elseif ($_SESSION["mailcow_cc_role"] == "domainadmin") { + header("Location: /domainadmin/mailbox"); + } else { + header("Location: /"); + } exit(); } else { + $role = $_SESSION["mailcow_cc_role"]; session_regenerate_id(true); session_unset(); session_destroy(); session_write_close(); - header("Location: /"); + if ($role == "admin") { + header("Location: /admin"); + } + elseif ($role == "domainadmin") { + header("Location: /domainadmin"); + } + else { + header("Location: /"); + } } } diff --git a/data/web/inc/triggers.admin.inc.php b/data/web/inc/triggers.admin.inc.php new file mode 100644 index 00000000..5b1061f7 --- /dev/null +++ b/data/web/inc/triggers.admin.inc.php @@ -0,0 +1,93 @@ + "admin")); + + if ($as == "admin") { + session_regenerate_id(true); + $_SESSION['mailcow_cc_username'] = $login_user; + $_SESSION['mailcow_cc_role'] = "admin"; + header("Location: /admin/dashboard"); + die(); + } + elseif ($as != "pending") { + unset($_SESSION['pending_mailcow_cc_username']); + unset($_SESSION['pending_mailcow_cc_role']); + unset($_SESSION['pending_tfa_methods']); + unset($_SESSION['mailcow_cc_username']); + unset($_SESSION['mailcow_cc_role']); + } +} + +if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin" && !isset($_SESSION['mailcow_cc_api'])) { + // TODO: Move file upload to API? + if (isset($_POST["submit_main_logo"])) { + if ($_FILES['main_logo']['error'] == 0) { + customize('add', 'main_logo', $_FILES); + } + if ($_FILES['main_logo_dark']['error'] == 0) { + customize('add', 'main_logo_dark', $_FILES); + } + } + if (isset($_POST["reset_main_logo"])) { + customize('delete', 'main_logo'); + customize('delete', 'main_logo_dark'); + } + // Some actions will not be available via API + if (isset($_POST["license_validate_now"])) { + license('verify'); + } + if (isset($_POST["admin_api"])) { + if (isset($_POST["admin_api"]["ro"])) { + admin_api('ro', 'edit', $_POST); + } + elseif (isset($_POST["admin_api"]["rw"])) { + admin_api('rw', 'edit', $_POST); + } + } + if (isset($_POST["admin_api_regen_key"])) { + if (isset($_POST["admin_api_regen_key"]["ro"])) { + admin_api('ro', 'regen_key', $_POST); + } + elseif (isset($_POST["admin_api_regen_key"]["rw"])) { + admin_api('rw', 'regen_key', $_POST); + } + } + if (isset($_POST["rspamd_ui"])) { + rspamd_ui('edit', $_POST); + } + if (isset($_POST["mass_send"])) { + sys_mail($_POST); + } +} +?> diff --git a/data/web/inc/triggers.domainadmin.inc.php b/data/web/inc/triggers.domainadmin.inc.php new file mode 100644 index 00000000..9ee53d67 --- /dev/null +++ b/data/web/inc/triggers.domainadmin.inc.php @@ -0,0 +1,62 @@ + "domain_admin")); + + if ($as == "domainadmin") { + session_regenerate_id(true); + $_SESSION['mailcow_cc_username'] = $login_user; + $_SESSION['mailcow_cc_role'] = "domainadmin"; + header("Location: /domainadmin/mailbox"); + die(); + } + elseif ($as != "pending") { + unset($_SESSION['pending_mailcow_cc_username']); + unset($_SESSION['pending_mailcow_cc_role']); + unset($_SESSION['pending_tfa_methods']); + unset($_SESSION['mailcow_cc_username']); + unset($_SESSION['mailcow_cc_role']); + } +} +?> diff --git a/data/web/inc/triggers.global.inc.php b/data/web/inc/triggers.global.inc.php new file mode 100644 index 00000000..dd88fad5 --- /dev/null +++ b/data/web/inc/triggers.global.inc.php @@ -0,0 +1,48 @@ + "unset_fido2_key", "post_data" => $_POST)); + } +} +?> diff --git a/data/web/inc/triggers.inc.php b/data/web/inc/triggers.inc.php deleted file mode 100644 index 02db519e..00000000 --- a/data/web/inc/triggers.inc.php +++ /dev/null @@ -1,272 +0,0 @@ - $_POST['new_password'], - 'new_password2' => $_POST['new_password2'], - 'token' => $_POST['token'], - 'username' => $username, - 'check_tfa' => True - )); - - if ($reset_result){ - header("Location: /"); - exit; - } -} -if (isset($_POST["verify_tfa_login"])) { - if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) { - if ($_SESSION['pending_mailcow_cc_role'] == "admin") { - $_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username']; - $_SESSION['mailcow_cc_role'] = "admin"; - unset($_SESSION['pending_mailcow_cc_username']); - unset($_SESSION['pending_mailcow_cc_role']); - unset($_SESSION['pending_tfa_methods']); - - header("Location: /debug"); - die(); - } - elseif ($_SESSION['pending_mailcow_cc_role'] == "domainadmin") { - $_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username']; - $_SESSION['mailcow_cc_role'] = "domainadmin"; - unset($_SESSION['pending_mailcow_cc_username']); - unset($_SESSION['pending_mailcow_cc_role']); - unset($_SESSION['pending_tfa_methods']); - - header("Location: /mailbox"); - die(); - } - elseif ($_SESSION['pending_mailcow_cc_role'] == "user") { - if (isset($_SESSION['pending_pw_reset_token']) && isset($_SESSION['pending_pw_new_password'])) { - reset_password("reset", array( - 'new_password' => $_SESSION['pending_pw_new_password'], - 'new_password2' => $_SESSION['pending_pw_new_password'], - 'token' => $_SESSION['pending_pw_reset_token'], - 'username' => $_SESSION['pending_mailcow_cc_username'] - )); - unset($_SESSION['pending_pw_reset_token']); - unset($_SESSION['pending_pw_new_password']); - unset($_SESSION['pending_mailcow_cc_username']); - unset($_SESSION['pending_tfa_methods']); - - header("Location: /"); - die(); - } else { - set_user_loggedin_session($_SESSION['pending_mailcow_cc_username']); - $user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']); - $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false; - if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual) { - header("Location: /SOGo/so/{$_SESSION['mailcow_cc_username']}"); - die(); - } else { - header("Location: /user"); - die(); - } - } - } - } - - unset($_SESSION['pending_mailcow_cc_username']); - unset($_SESSION['pending_mailcow_cc_role']); - unset($_SESSION['pending_tfa_methods']); -} - -if (isset($_GET["cancel_tfa_login"])) { - unset($_SESSION['pending_pw_reset_token']); - unset($_SESSION['pending_pw_new_password']); - unset($_SESSION['pending_mailcow_cc_username']); - unset($_SESSION['pending_mailcow_cc_role']); - unset($_SESSION['pending_tfa_methods']); - - header("Location: /"); -} - -if (isset($_POST["quick_release"])) { - quarantine('quick_release', $_POST["quick_release"]); -} - -if (isset($_POST["quick_delete"])) { - quarantine('quick_delete', $_POST["quick_delete"]); -} - -if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { - $login_user = strtolower(trim($_POST["login_user"])); - $as = check_login($login_user, $_POST["pass_user"]); - - if ($as == "admin") { - session_regenerate_id(true); - $_SESSION['mailcow_cc_username'] = $login_user; - $_SESSION['mailcow_cc_role'] = "admin"; - header("Location: /debug"); - die(); - } - elseif ($as == "domainadmin") { - session_regenerate_id(true); - $_SESSION['mailcow_cc_username'] = $login_user; - $_SESSION['mailcow_cc_role'] = "domainadmin"; - header("Location: /mailbox"); - die(); - } - elseif ($as == "user") { - set_user_loggedin_session($login_user); - $http_parameters = explode('&', $_SESSION['index_query_string']); - unset($_SESSION['index_query_string']); - if (in_array('mobileconfig', $http_parameters)) { - if (in_array('only_email', $http_parameters)) { - header("Location: /mobileconfig.php?only_email"); - die(); - } - header("Location: /mobileconfig.php"); - die(); - } - - $user_details = mailbox("get", "mailbox_details", $login_user); - $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false; - if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual) { - header("Location: /SOGo/so/{$login_user}"); - die(); - } else { - header("Location: /user"); - die(); - } - if (!isset($_SESSION['oauth2_request'])) { - header("Location: /user"); - die(); - } - } - elseif ($as != "pending") { - unset($_SESSION['pending_mailcow_cc_username']); - unset($_SESSION['pending_mailcow_cc_role']); - unset($_SESSION['pending_tfa_methods']); - unset($_SESSION['mailcow_cc_username']); - unset($_SESSION['mailcow_cc_role']); - } else { - session_regenerate_id(true); - } -} - -if (isset($_SESSION['mailcow_cc_role']) && (isset($_SESSION['acl']['login_as']) && $_SESSION['acl']['login_as'] == "1")) { - if (isset($_GET["duallogin"])) { - $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false; - if (!$is_dual) { - $duallogin = html_entity_decode(rawurldecode($_GET["duallogin"])); - if (filter_var($duallogin, FILTER_VALIDATE_EMAIL)) { - if (!empty(mailbox('get', 'mailbox_details', $duallogin))) { - $_SESSION["dual-login"]["username"] = $_SESSION['mailcow_cc_username']; - $_SESSION["dual-login"]["role"] = $_SESSION['mailcow_cc_role']; - $_SESSION['mailcow_cc_username'] = $duallogin; - $_SESSION['mailcow_cc_role'] = "user"; - header("Location: /user"); - } - } - else { - if (!empty(domain_admin('details', $duallogin))) { - $_SESSION["dual-login"]["username"] = $_SESSION['mailcow_cc_username']; - $_SESSION["dual-login"]["role"] = $_SESSION['mailcow_cc_role']; - $_SESSION['mailcow_cc_username'] = $duallogin; - $_SESSION['mailcow_cc_role'] = "domainadmin"; - header("Location: /user"); - } - } - } - } -} - -if (isset($_SESSION['mailcow_cc_role'])) { - if (isset($_POST["set_tfa"])) { - set_tfa($_POST); - } - if (isset($_POST["unset_tfa_key"])) { - unset_tfa_key($_POST); - } - if (isset($_POST["unset_fido2_key"])) { - fido2(array("action" => "unset_fido2_key", "post_data" => $_POST)); - } -} -if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin" && !isset($_SESSION['mailcow_cc_api'])) { - // TODO: Move file upload to API? - if (isset($_POST["submit_main_logo"])) { - if ($_FILES['main_logo']['error'] == 0) { - customize('add', 'main_logo', $_FILES); - } - if ($_FILES['main_logo_dark']['error'] == 0) { - customize('add', 'main_logo_dark', $_FILES); - } - } - if (isset($_POST["reset_main_logo"])) { - customize('delete', 'main_logo'); - customize('delete', 'main_logo_dark'); - } - // Some actions will not be available via API - if (isset($_POST["license_validate_now"])) { - license('verify'); - } - if (isset($_POST["admin_api"])) { - if (isset($_POST["admin_api"]["ro"])) { - admin_api('ro', 'edit', $_POST); - } - elseif (isset($_POST["admin_api"]["rw"])) { - admin_api('rw', 'edit', $_POST); - } - } - if (isset($_POST["admin_api_regen_key"])) { - if (isset($_POST["admin_api_regen_key"]["ro"])) { - admin_api('ro', 'regen_key', $_POST); - } - elseif (isset($_POST["admin_api_regen_key"]["rw"])) { - admin_api('rw', 'regen_key', $_POST); - } - } - if (isset($_POST["rspamd_ui"])) { - rspamd_ui('edit', $_POST); - } - if (isset($_POST["mass_send"])) { - sys_mail($_POST); - } -} -?> diff --git a/data/web/inc/triggers.user.inc.php b/data/web/inc/triggers.user.inc.php new file mode 100644 index 00000000..c16edc10 --- /dev/null +++ b/data/web/inc/triggers.user.inc.php @@ -0,0 +1,132 @@ + $_POST['new_password'], + 'new_password2' => $_POST['new_password2'], + 'token' => $_POST['token'], + 'username' => $username, + 'check_tfa' => True + )); + + if ($reset_result){ + header("Location: /"); + exit; + } +} +if (isset($_POST["verify_tfa_login"])) { + if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) { + if ($_SESSION['pending_mailcow_cc_role'] == "user") { + if (isset($_SESSION['pending_pw_reset_token']) && isset($_SESSION['pending_pw_new_password'])) { + reset_password("reset", array( + 'new_password' => $_SESSION['pending_pw_new_password'], + 'new_password2' => $_SESSION['pending_pw_new_password'], + 'token' => $_SESSION['pending_pw_reset_token'], + 'username' => $_SESSION['pending_mailcow_cc_username'] + )); + unset($_SESSION['pending_pw_reset_token']); + unset($_SESSION['pending_pw_new_password']); + unset($_SESSION['pending_mailcow_cc_username']); + unset($_SESSION['pending_tfa_methods']); + + header("Location: /"); + die(); + } else { + set_user_loggedin_session($_SESSION['pending_mailcow_cc_username']); + $user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']); + $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false; + if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual) { + header("Location: /SOGo/so/{$_SESSION['mailcow_cc_username']}"); + die(); + } else { + header("Location: /user"); + die(); + } + } + } + } + + unset($_SESSION['pending_mailcow_cc_username']); + unset($_SESSION['pending_mailcow_cc_role']); + unset($_SESSION['pending_tfa_methods']); +} + +if (isset($_GET["cancel_tfa_login"])) { + unset($_SESSION['pending_pw_reset_token']); + unset($_SESSION['pending_pw_new_password']); + unset($_SESSION['pending_mailcow_cc_username']); + unset($_SESSION['pending_mailcow_cc_role']); + unset($_SESSION['pending_tfa_methods']); + + header("Location: /"); +} + +if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { + $login_user = strtolower(trim($_POST["login_user"])); + $as = check_login($login_user, $_POST["pass_user"], false, array("role" => "user")); + + if ($as == "user") { + set_user_loggedin_session($login_user); + $http_parameters = explode('&', $_SESSION['index_query_string']); + unset($_SESSION['index_query_string']); + if (in_array('mobileconfig', $http_parameters)) { + if (in_array('only_email', $http_parameters)) { + header("Location: /mobileconfig.php?only_email"); + die(); + } + header("Location: /mobileconfig.php"); + die(); + } + + $user_details = mailbox("get", "mailbox_details", $login_user); + $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false; + if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual) { + header("Location: /SOGo/so/{$login_user}"); + die(); + } else { + header("Location: /user"); + die(); + } + } + elseif ($as != "pending") { + unset($_SESSION['pending_mailcow_cc_username']); + unset($_SESSION['pending_mailcow_cc_role']); + unset($_SESSION['pending_tfa_methods']); + unset($_SESSION['mailcow_cc_username']); + unset($_SESSION['mailcow_cc_role']); + } +} +?> diff --git a/data/web/index.php b/data/web/index.php index 0282e483..1e91cb78 100644 --- a/data/web/index.php +++ b/data/web/index.php @@ -1,5 +1,6 @@ @$_SESSION['oauth2_request'], 'is_mobileconfig' => str_contains($_SESSION['index_query_string'], 'mobileconfig'), diff --git a/data/web/js/site/debug.js b/data/web/js/site/dashboard.js similarity index 100% rename from data/web/js/site/debug.js rename to data/web/js/site/dashboard.js diff --git a/data/web/js/site/mailbox.js b/data/web/js/site/mailbox.js index 1bd4095a..28e6fd28 100644 --- a/data/web/js/site/mailbox.js +++ b/data/web/js/site/mailbox.js @@ -938,7 +938,7 @@ jQuery(function($){ ' ' + lang.remove + '' + ' Login'; if (ALLOW_ADMIN_EMAIL_LOGIN) { - item.action += ' SOGo'; + item.action += ' SOGo'; } item.action += ''; } diff --git a/data/web/lang/lang.de-de.json b/data/web/lang/lang.de-de.json index 782e4e68..9c54d3fc 100644 --- a/data/web/lang/lang.de-de.json +++ b/data/web/lang/lang.de-de.json @@ -206,6 +206,39 @@ "help_text": "Hilfstext unter Login-Maske (HTML ist zulässig)", "host": "Host", "html": "HTML", + "iam": "Identity Provider", + "iam_attribute_field": "Attribut Feld", + "iam_authorize_url": "Authorization Endpunkt", + "iam_auth_flow": "Authentication Flow", + "iam_auth_flow_info": "Zusätzlich zum Authorization Code Flow (dem Standard-Flow in Keycloak), der für Single-Sign-On-Logins verwendet wird, unterstützt mailcow auch den Authentication Flow mit direkten Anmeldeinformationen. Der Mailpassword Flow versucht, die Anmeldedaten des Benutzers über die Keycloak Admin REST API zu validieren. Dabei ruft mailcow das gehashte Passwort aus dem mailcow_password Attribut ab, das in Keycloak zugewiesen ist.", + "iam_basedn": "Base DN", + "iam_client_id": "Client ID", + "iam_client_secret": "Client Secret", + "iam_client_scopes": "Client Scopes", + "iam_description": "Konfiguriere einen externen Identity Provider für die Authentifizierung
Die Mailboxen der Benutzer werden bei ihrer ersten Anmeldung automatisch erstellt, vorausgesetzt, dass ein Attribut Mapping festgelegt wurde.", + "iam_extra_permission": "Damit die folgenden Einstellungen funktionieren, benötigt der mailcow Client in Keycloak ein Service-Konto und die Berechtigung view-users.", + "iam_host": "Host", + "iam_host_info": "Gib einen oder mehrere LDAP-Hosts ein, getrennt durch Kommas.", + "iam_import_users": "Import Users", + "iam_mapping": "Attribut Mapping", + "iam_bindpass": "Bind Passwort", + "iam_periodic_full_sync": "Periodic Full Sync", + "iam_port": "Port", + "iam_realm": "Realm", + "iam_redirect_url": "Redirect Url", + "iam_rest_flow": "Mailpassword Flow", + "iam_server_url": "Server Url", + "iam_sso": "Single Sign-On", + "iam_sync_interval": "Sync / Import interval (min)", + "iam_test_connection": "Verbindung Testen", + "iam_token_url": "Token Endpunkt", + "iam_userinfo_url": "User info Endpunkt", + "iam_username_field": "Username Feld", + "iam_binddn": "Bind DN", + "iam_use_ssl": "Benutze SSL", + "iam_use_tls": "Benutze TLS", + "iam_version": "Version", + "ignore_ssl_error": "Ignoriere SSL Errors", "import": "Importieren", "import_private_key": "Private Key importieren", "in_use_by": "Verwendet von", @@ -403,6 +436,7 @@ "goto_empty": "Eine Alias-Adresse muss auf mindestens eine gültige Ziel-Adresse zeigen", "goto_invalid": "Ziel-Adresse %s ist ungültig", "ham_learn_error": "Ham Lernfehler: %s", + "iam_test_connection": "Verbindung fehlgeschlagen", "imagick_exception": "Fataler Bildverarbeitungsfehler", "img_dimensions_exceeded": "Grafik überschreitet die maximale Bildgröße", "img_invalid": "Grafik konnte nicht validiert werden", @@ -766,6 +800,9 @@ "forgot_password": "> Passwort vergessen?", "invalid_pass_reset_token": "Der Rücksetz-Token für das Passwort ist ungültig oder abgelaufen.
Bitte fordern Sie einen neuen Link zur Passwortwiederherstellung an.", "login": "Anmelden", + "login_user": "Benutzer Anmelden", + "login_dadmin": "Domain-Administrator Anmelden", + "login_admin": "Administrator Anmelden", "mobileconfig_info": "Bitte als Mailbox-Benutzer einloggen, um das Verbindungsprofil herunterzuladen.", "new_password": "Neues Passwort", "new_password_confirm": "Neues Passwort bestätigen", @@ -1077,6 +1114,7 @@ "forwarding_host_removed": "Weiterleitungs-Host %s wurde entfernt", "global_filter_written": "Filterdatei wurde erfolgreich geschrieben", "hash_deleted": "Hash wurde gelöscht", + "iam_test_connection": "Verbindung erfolgreich", "ip_check_opt_in_modified": "IP Check wurde erfolgreich gespeichert", "item_deleted": "Objekt %s wurde entfernt", "item_released": "Objekt %s freigegeben", diff --git a/data/web/lang/lang.en-gb.json b/data/web/lang/lang.en-gb.json index 6d3580e1..362262bf 100644 --- a/data/web/lang/lang.en-gb.json +++ b/data/web/lang/lang.en-gb.json @@ -804,6 +804,9 @@ "forgot_password": "> Forgot Password?", "invalid_pass_reset_token": "The reset password token is invalid or has expired.
Please request a new password reset link.", "login": "Login", + "login_user": "User Login", + "login_dadmin": "Domain-Administrator Login", + "login_admin": "Administrator Login", "mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.", "new_password": "New Password", "new_password_confirm": "Confirm new password", diff --git a/data/web/reset-password.php b/data/web/reset-password.php index a0225dc6..7544d40c 100644 --- a/data/web/reset-password.php +++ b/data/web/reset-password.php @@ -2,11 +2,11 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') { - header('Location: /debug'); + header('Location: /admin/dashboard'); exit(); } elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { - header('Location: /mailbox'); + header('Location: /domainadmin/mailbox'); exit(); } elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { diff --git a/data/web/templates/admin/tab-config-identity-provider.twig b/data/web/templates/admin/tab-config-identity-provider.twig index e2cea7fa..3381fe4d 100644 --- a/data/web/templates/admin/tab-config-identity-provider.twig +++ b/data/web/templates/admin/tab-config-identity-provider.twig @@ -84,7 +84,7 @@
- Attribute + {{ lang.user.attribute }} {{ lang.mailbox.template }}
@@ -274,8 +274,8 @@
- Attribute - Template + {{ lang.user.attribute }} + {{ lang.mailbox.template }}
@@ -454,7 +454,7 @@
- Attribute + {{ lang.user.attribute }} {{ lang.mailbox.template }}
diff --git a/data/web/templates/admin_index.twig b/data/web/templates/admin_index.twig new file mode 100644 index 00000000..93a89284 --- /dev/null +++ b/data/web/templates/admin_index.twig @@ -0,0 +1,91 @@ +{% extends 'base.twig' %} + +{% block navbar %}{% endblock %} + +{% block content %} +
+
+
+
+ {{ lang.login.login_admin }} +
+ + +
+
+
+ + {% if ui_texts.ui_announcement_text and ui_texts.ui_announcement_active %} +
{{ ui_texts.ui_announcement_text|rot13 }}
+ {% endif %} +
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+
+
+ + +
+
+
+
{{ lang.login.other_logins }}
+ + {% if login_delay %} +

{{ lang.login.delayed|format(login_delay) }}

+ {% endif %} +
+ {% if (mailcow_apps or app_links) and not hide_mailcow_apps %} + {{ ui_texts.apps_name|raw }}
+
+ {% for app in mailcow_apps %} + {% if not app.hide %} + {% if not skip_sogo or not is_uri('SOGo', app.link) %} + + {% endif %} + {% endif %} + {% endfor %} + {% for row in app_links %} + {% for key, val in row %} + {% if not val.hide %} +
+ {{ key }} +
+ {% endif %} + {% endfor %} + {% endfor %} +
+ {% endif %} +
+
+
+
+{% endblock %} diff --git a/data/web/templates/base.twig b/data/web/templates/base.twig index 2634574d..4d0a3025 100644 --- a/data/web/templates/base.twig +++ b/data/web/templates/base.twig @@ -66,27 +66,36 @@ + {% endif %} - {% if mailcow_cc_role != 'admin' %} + {% if mailcow_cc_role == 'domainadmin' %} + + {% elseif mailcow_cc_role == 'user' %} {% endif %} - {% if mailcow_cc_role == 'admin' or mailcow_cc_role == 'domainadmin' %} + {% if mailcow_cc_role == 'domainadmin' %} {% endif %} @@ -246,7 +255,7 @@ function recursiveBase64StrToArrayBuffer(obj) { $(".totp-authenticator-selection").click(function(){ $(".totp-authenticator-selection").removeClass("active"); $(this).addClass("active"); - + var id = $(this).children('input').first().val(); $("#totp_selected_id").val(id); @@ -255,7 +264,7 @@ function recursiveBase64StrToArrayBuffer(obj) { if ($('.totp-authenticator-selection').length == 1 && $('#pending_tfa_tab_yubi_otp').length == 0 && $('.webauthn-authenticator-selection').length == 0){ - + // select default if only one authenticator exists $('.totp-authenticator-selection').addClass("active"); @@ -268,7 +277,7 @@ function recursiveBase64StrToArrayBuffer(obj) { $('#pending_tfa_tab_totp').on('shown.bs.tab', function() { // autofocus setTimeout(function() { $("#collapseTotpTFA").find('input[name="token"]').focus(); }, 200); - }); + }); // validate Yubi OTP tfa if ($('.webauthn-authenticator-selection').length == 0){ // autofocus @@ -287,10 +296,10 @@ function recursiveBase64StrToArrayBuffer(obj) { $(".webauthn-authenticator-selection").click(function(){ $(".webauthn-authenticator-selection").removeClass("active"); $(this).addClass("active"); - + var id = $(this).children('input').first().val(); $("#webauthn_selected_id").val(id); - + var webauthn_status_auth = document.getElementById('webauthn_status_auth'); webauthn_status_auth.style.setProperty('display', 'flex', 'important'); var webauthn_return_code = document.getElementById('webauthn_return_code'); @@ -313,7 +322,7 @@ function recursiveBase64StrToArrayBuffer(obj) { console.log(json); if (json.success === false) throw new Error(); if (json.type === "error") throw new Error(json.msg); - + recursiveBase64StrToArrayBuffer(json); return json; }).then(getCredentialArgs => { @@ -340,7 +349,7 @@ function recursiveBase64StrToArrayBuffer(obj) { webauthn_return_code.style.setProperty('display', 'block', 'important'); webauthn_return_code.innerHTML = lang_tfa.error_code + ': ' + err + ' ' + lang_tfa.reload_retry; }); - } + } }); $('#ConfirmTFAModal').on('hidden.bs.modal', function(){ // cancel pending login @@ -551,7 +560,7 @@ function recursiveBase64StrToArrayBuffer(obj) { Version: {{ mailcow_info.version_tag }} - {% endif %} + {% endif %} {% if mailcow_cc_username and mailcow_info.mailcow_branch|lower == "nightly" and mailcow_info.version_tag|default %} 🛠️🐮 + 🐋 = 💕 diff --git a/data/web/templates/debug.twig b/data/web/templates/dashboard.twig similarity index 99% rename from data/web/templates/debug.twig rename to data/web/templates/dashboard.twig index 494962f2..3c57afa5 100644 --- a/data/web/templates/debug.twig +++ b/data/web/templates/dashboard.twig @@ -42,8 +42,8 @@ mailcow-logo-dark
-
- +
+
diff --git a/data/web/templates/domainadmin_index.twig b/data/web/templates/domainadmin_index.twig new file mode 100644 index 00000000..41a9f259 --- /dev/null +++ b/data/web/templates/domainadmin_index.twig @@ -0,0 +1,91 @@ +{% extends 'base.twig' %} + +{% block navbar %}{% endblock %} + +{% block content %} +
+
+
+
+ {{ lang.login.login_dadmin }} +
+ + +
+
+
+ + {% if ui_texts.ui_announcement_text and ui_texts.ui_announcement_active %} +
{{ ui_texts.ui_announcement_text|rot13 }}
+ {% endif %} +
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+
+
+ + +
+
+ +
{{ lang.login.other_logins }}
+ + {% if login_delay %} +

{{ lang.login.delayed|format(login_delay) }}

+ {% endif %} +
+ {% if (mailcow_apps or app_links) and not hide_mailcow_apps %} + {{ ui_texts.apps_name|raw }}
+
+ {% for app in mailcow_apps %} + {% if not app.hide %} + {% if not skip_sogo or not is_uri('SOGo', app.link) %} + + {% endif %} + {% endif %} + {% endfor %} + {% for row in app_links %} + {% for key, val in row %} + {% if not val.hide %} +
+ {{ key }} +
+ {% endif %} + {% endfor %} + {% endfor %} +
+ {% endif %} +
+
+
+
+{% endblock %} diff --git a/data/web/templates/user/tab-user-auth.twig b/data/web/templates/user/tab-user-auth.twig index 5c90bedf..a7e02543 100644 --- a/data/web/templates/user/tab-user-auth.twig +++ b/data/web/templates/user/tab-user-auth.twig @@ -20,11 +20,11 @@ {{ lang.user.open_webmail_sso }} {% elseif dual_login %} - + {{ lang.user.open_webmail_sso }} {% else %} - + {{ lang.user.open_webmail_sso }} {% endif %} diff --git a/data/web/templates/index.twig b/data/web/templates/user_index.twig similarity index 99% rename from data/web/templates/index.twig rename to data/web/templates/user_index.twig index 07274e6b..950482c9 100644 --- a/data/web/templates/index.twig +++ b/data/web/templates/user_index.twig @@ -7,7 +7,7 @@
- {{ lang.login.login }} + {{ lang.login.login_user }}
diff --git a/data/web/user.php b/data/web/user.php index 19aafddd..7c34ba95 100644 --- a/data/web/user.php +++ b/data/web/user.php @@ -1,29 +1,8 @@ "get_friendly_names")); - $username = $_SESSION['mailcow_cc_username']; - - $template = 'domainadmin.twig'; - $template_data = [ - 'acl' => $_SESSION['acl'], - 'acl_json' => json_encode($_SESSION['acl']), - 'user_spam_score' => mailbox('get', 'spam_score', $username), - 'tfa_data' => $tfa_data, - 'fido2_data' => $fido2_data, - 'lang_user' => json_encode($lang['user']), - 'lang_datatables' => json_encode($lang['datatables']), - ]; -} -elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { +if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { /* / USER @@ -95,6 +74,14 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == ' 'lang_datatables' => json_encode($lang['datatables']), ]; } +elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') { + header('Location: /admin/dashboard'); + exit(); +} +elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { + header('Location: /domainadmin/mailbox'); + exit(); +} else { header('Location: /'); exit();
Hostname