diff --git a/data/web/admin/index.php b/data/web/admin/index.php index 05ba70337..9ae4a0380 100644 --- a/data/web/admin/index.php +++ b/data/web/admin/index.php @@ -22,7 +22,8 @@ $_SESSION['index_query_string'] = $_SERVER['QUERY_STRING']; $template = 'admin_index.twig'; $template_data = [ - 'login_delay' => @$_SESSION['ldelay'] + 'login_delay' => @$_SESSION['ldelay'], + 'custom_login' => customize('get', 'custom_login'), ]; $js_minifier->add('/web/js/site/index.js'); diff --git a/data/web/admin/system.php b/data/web/admin/system.php index c21d43f0d..9fd44e0d8 100644 --- a/data/web/admin/system.php +++ b/data/web/admin/system.php @@ -125,6 +125,7 @@ $template_data = [ 'logo_specs' => customize('get', 'main_logo_specs'), 'logo_dark_specs' => customize('get', 'main_logo_dark_specs'), 'ip_check' => customize('get', 'ip_check'), + 'custom_login' => customize('get', 'custom_login'), 'password_complexity' => password_complexity('get'), 'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'], 'cors_settings' => $cors_settings, diff --git a/data/web/domainadmin/index.php b/data/web/domainadmin/index.php index 2d909f97f..0d70ec3ae 100644 --- a/data/web/domainadmin/index.php +++ b/data/web/domainadmin/index.php @@ -22,6 +22,7 @@ $_SESSION['index_query_string'] = $_SERVER['QUERY_STRING']; $template = 'domainadmin_index.twig'; $template_data = [ 'login_delay' => @$_SESSION['ldelay'], + 'custom_login' => customize('get', 'custom_login'), ]; $js_minifier->add('/web/js/site/index.js'); diff --git a/data/web/inc/functions.customize.inc.php b/data/web/inc/functions.customize.inc.php index 8c0feb69f..dc86bfe65 100644 --- a/data/web/inc/functions.customize.inc.php +++ b/data/web/inc/functions.customize.inc.php @@ -204,6 +204,35 @@ function customize($_action, $_item, $_data = null) { 'msg' => 'ip_check_opt_in_modified' ); break; + case 'custom_login': + $hide_user_quicklink = ($_data['hide_user_quicklink'] == "1") ? 1 : 0; + $hide_domainadmin_quicklink = ($_data['hide_domainadmin_quicklink'] == "1") ? 1 : 0; + $hide_admin_quicklink = ($_data['hide_admin_quicklink'] == "1") ? 1 : 0; + $force_sso = ($_data['force_sso'] == "1") ? 1 : 0; + + $custom_login = array( + "hide_user_quicklink" => $hide_user_quicklink, + "hide_domainadmin_quicklink" => $hide_domainadmin_quicklink, + "hide_admin_quicklink" => $hide_admin_quicklink, + "force_sso" => $force_sso, + ); + try { + $redis->set('CUSTOM_LOGIN', json_encode($custom_login)); + } + catch (RedisException $e) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_item, $_data), + 'msg' => array('redis_error', $e) + ); + return false; + } + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_item, $_data), + 'msg' => 'custom_login_modified' + ); + break; } break; case 'delete': @@ -357,6 +386,20 @@ function customize($_action, $_item, $_data = null) { return false; } break; + case 'custom_login': + try { + $custom_login = ($custom_login = $redis->get('CUSTOM_LOGIN')) ? $custom_login : array(); + return json_decode($custom_login, true); + } + catch (RedisException $e) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_item, $_data), + 'msg' => array('redis_error', $e) + ); + return false; + } + break; } break; } diff --git a/data/web/index.php b/data/web/index.php index f306f1aed..d21e54364 100644 --- a/data/web/index.php +++ b/data/web/index.php @@ -33,16 +33,18 @@ $_SESSION['index_query_string'] = $_SERVER['QUERY_STRING']; $has_iam_sso = false; if ($iam_provider){ - $has_iam_sso = identity_provider("get-redirect") ? true : false; + $iam_redirect_url = identity_provider("get-redirect"); + $has_iam_sso = $iam_redirect_url ? true : false; } - +$custom_login = customize('get', 'custom_login'); $template = 'user_index.twig'; $template_data = [ 'oauth2_request' => @$_SESSION['oauth2_request'], 'is_mobileconfig' => str_contains($_SESSION['index_query_string'], 'mobileconfig'), 'login_delay' => @$_SESSION['ldelay'], - 'has_iam_sso' => $has_iam_sso + 'has_iam_sso' => $has_iam_sso, + 'custom_login' => $custom_login, ]; $js_minifier->add('/web/js/site/index.js'); diff --git a/data/web/json_api.php b/data/web/json_api.php index f2a222b85..a480b0ae0 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -1976,6 +1976,9 @@ if (isset($_GET['query'])) { case "ip_check": process_edit_return(customize('edit', 'ip_check', $attr)); break; + case "custom_login": + process_edit_return(customize('edit', 'custom_login', $attr)); + break; case "self": if ($_SESSION['mailcow_cc_role'] == "domainadmin") { process_edit_return(domain_admin('edit', $attr)); diff --git a/data/web/lang/lang.de-de.json b/data/web/lang/lang.de-de.json index 911ebfc67..06d43b565 100644 --- a/data/web/lang/lang.de-de.json +++ b/data/web/lang/lang.de-de.json @@ -134,6 +134,7 @@ "admin_domains": "Domain-Zuweisungen", "admins": "Administratoren", "admins_ldap": "LDAP-Administratoren", + "admin_quicklink": "Quicklink zur Admin-Loginseite ausblenden", "advanced_settings": "Erweiterte Einstellungen", "api_allow_from": "IP-Adressen oder Netzwerke (CIDR Notation) für Zugriff auf API", "api_info": "Die API befindet sich noch in Entwicklung, die Dokumentation kann unter /api abgerufen werden.", @@ -155,6 +156,7 @@ "credentials_transport_warning": "Warnung: Das Hinzufügen einer neuen Regel bewirkt die Aktualisierung der Authentifizierungsdaten aller vorhandenen Einträge mit identischem Next Hop.", "customer_id": "Kunde", "customize": "UI-Anpassung", + "login_page": "Login-Seite", "destination": "Ziel", "dkim_add_key": "ARC/DKIM-Key hinzufügen", "dkim_domains_selector": "Selector", @@ -173,6 +175,7 @@ "domain": "Domain", "domain_admin": "Administrator hinzufügen", "domain_admins": "Domain-Administratoren", + "domainadmin_quicklink": "Quicklink zur Domainadmin-Loginseite ausblenden", "domain_s": "Domain(s)", "duplicate": "Duplizieren", "duplicate_dkim": "DKIM duplizieren", @@ -195,6 +198,8 @@ "f2b_retry_window": "Wiederholungen im Zeitraum von (s)", "f2b_whitelist": "Whitelist für Netzwerke und Hosts", "filter_table": "Tabelle filtern", + "force_sso_text": "Wenn ein externer OIDC-Provider konfiguriert ist, blendet diese Option die mailcow Loginform aus und zeigt nur den Single Sign-On-Button an.", + "force_sso": "mailcow Login deaktivieren und nur Single Sign-On anzeigen", "forwarding_hosts": "Weiterleitungs-Hosts", "forwarding_hosts_add_hint": "Sie können entweder IPv4-/IPv6-Adressen, Netzwerke in CIDR-Notation, Hostnamen (die zu IP-Adressen aufgelöst werden), oder Domainnamen (die zu IP-Adressen aufgelöst werden, indem ihr SPF-Record abgefragt wird oder, in dessen Abwesenheit, ihre MX-Records) angeben.", "forwarding_hosts_hint": "Eingehende Nachrichten werden von den hier gelisteten Hosts bedingungslos akzeptiert. Diese Hosts werden dann nicht mit DNSBLs abgeglichen oder Greylisting unterworfen. Von ihnen empfangener Spam wird nie abgelehnt, optional kann er aber in den Spam-Ordner einsortiert werden. Die übliche Verwendung für diese Funktion ist, um Mailserver anzugeben, auf denen eine Weiterleitung zu Ihrem mailcow-Server eingerichtet wurde.", @@ -308,6 +313,7 @@ "quarantine_release_format_att": "Als Anhang", "quarantine_release_format_raw": "Unverändertes Original", "quarantine_retention_size": "Rückhaltungen pro Mailbox:
0 bedeutet inaktiv.", + "quicklink_text": "Quicklinks zu anderen Login-Seiten unter der Loginform ein- oder ausblenden", "quota_notification_html": "Benachrichtigungs-E-Mail Inhalt:
Leer lassen, um Standard-Template wiederherzustellen.", "quota_notification_sender": "Benachrichtigungs-E-Mail Absender", "quota_notification_subject": "Benachrichtigungs-E-Mail Betreff", @@ -387,6 +393,7 @@ "unchanged_if_empty": "Unverändert, wenn leer", "upload": "Hochladen", "username": "Benutzername", + "user_quicklink": "Quicklink zur Benutzer-Loginseite ausblenden", "validate_license_now": "GUID erneut verifizieren", "verify": "Verifizieren", "yes": "✓", @@ -807,6 +814,10 @@ "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_linkstext": "Nicht der richtige Login?", + "login_usertext": "Als Benutzer anmelden", + "login_domainadmintext": "Als Domainadmin anmelden", + "login_admintext": "Als Admin anmelden", "login_user": "Anmeldung als Benutzer", "login_dadmin": "Anmeldung als Domain-Administrator", "login_admin": "Anmeldung als Administrator", @@ -1096,6 +1107,7 @@ "bcc_edited": "BCC-Map-Eintrag %s wurde geändert", "bcc_saved": "BCC- Map-Eintrag wurde gespeichert", "cors_headers_edited": "CORS Einstellungen wurden erfolgreich gespeichert", + "custom_login_modified": "Login Anpassung wurde erfolgreich gespeichert", "db_init_complete": "Datenbankinitialisierung abgeschlossen", "delete_filter": "Filter-ID %s wurde gelöscht", "delete_filters": "Filter gelöscht: %s", diff --git a/data/web/lang/lang.en-gb.json b/data/web/lang/lang.en-gb.json index ca8bb3adf..707e2a60e 100644 --- a/data/web/lang/lang.en-gb.json +++ b/data/web/lang/lang.en-gb.json @@ -134,6 +134,7 @@ "admin_domains": "Domain assignments", "admins": "Administrators", "admins_ldap": "LDAP Administrators", + "admin_quicklink": "Hide Quicklink to Admin Login Page", "advanced_settings": "Advanced settings", "allowed_methods": "Access-Control-Allow-Methods", "allowed_origins": "Access-Control-Allow-Origin", @@ -161,6 +162,7 @@ "credentials_transport_warning": "Warning: Adding a new transport map entry will update the credentials for all entries with a matching next hop column.", "customer_id": "Customer ID", "customize": "Customize", + "login_page": "Login Page", "destination": "Destination", "dkim_add_key": "Add ARC/DKIM key", "dkim_domains_selector": "Selector", @@ -179,6 +181,7 @@ "domain": "Domain", "domain_admin": "Domain administrator", "domain_admins": "Domain administrators", + "domainadmin_quicklink": "Hide Quicklink to Domainadmin Login Page", "domain_s": "Domain/s", "duplicate": "Duplicate", "duplicate_dkim": "Duplicate DKIM record", @@ -202,6 +205,8 @@ "f2b_whitelist": "Whitelisted networks/hosts", "filter": "Filter", "filter_table": "Filter table", + "force_sso_text": "If an external OIDC provider is configured, this option hides the default mailcow login froms and only shows the Singe Sign-On button", + "force_sso": "Disable mailcow Login and show only Singe Sign-On", "forwarding_hosts": "Forwarding Hosts", "forwarding_hosts_add_hint": "You can either specify IPv4/IPv6 addresses, networks in CIDR notation, host names (which will be resolved to IP addresses), or domain names (which will be resolved to IP addresses by querying SPF records or, in their absence, MX records).", "forwarding_hosts_hint": "Incoming messages are unconditionally accepted from any hosts listed here. These hosts are then not checked against DNSBLs or subjected to greylisting. Spam received from them is never rejected, but optionally it can be filed into the Junk folder. The most common use for this is to specify mail servers on which you have set up a rule that forwards incoming emails to your mailcow server.", @@ -317,6 +322,7 @@ "quarantine_release_format_att": "As attachment", "quarantine_release_format_raw": "Unmodified original", "quarantine_retention_size": "Retentions per mailbox:
0 indicates inactive.", + "quicklink_text": "Show or hide quick links to other login pages under the login form", "quota_notification_html": "Notification email template:
Leave empty to restore default template.", "quota_notification_sender": "Notification email sender", "quota_notification_subject": "Notification email subject", @@ -398,6 +404,7 @@ "upload": "Upload", "username": "Username", "user_link": "User-Link", + "user_quicklink": "Hide Quicklink to User Login Page", "validate_license_now": "Validate GUID against license server", "verify": "Verify", "yes": "✓" @@ -809,6 +816,10 @@ "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_linkstext": "Not the correct login?", + "login_usertext": "Log in as user", + "login_domainadmintext": "Log in as domain admin", + "login_admintext": "Log in as admin", "login_user": "User Login", "login_dadmin": "Domain-Administrator Login", "login_admin": "Administrator Login", @@ -1105,6 +1116,7 @@ "bcc_edited": "BCC map entry %s edited", "bcc_saved": "BCC map entry saved", "cors_headers_edited": "CORS settings have been saved", + "custom_login_modified": "Login customisation was saved successfully", "db_init_complete": "Database initialization completed", "delete_filter": "Deleted filters ID %s", "delete_filters": "Deleted filters: %s", diff --git a/data/web/templates/admin/tab-config-customize.twig b/data/web/templates/admin/tab-config-customize.twig index c2071c399..aecf1846e 100644 --- a/data/web/templates/admin/tab-config-customize.twig +++ b/data/web/templates/admin/tab-config-customize.twig @@ -51,7 +51,41 @@

- {{ lang.admin.app_links }}
+ {{ lang.admin.login_page }}
+
+
+

{{ lang.admin.quicklink_text }}

+
+ + +
+
+ + +
+
+ + +
+

{{ lang.admin.force_sso_text|raw }}

+
+ + +
+

+ +

+
+
+ {{ lang.admin.app_links }}

{{ lang.admin.merged_vars_hint|raw }}

diff --git a/data/web/templates/admin_index.twig b/data/web/templates/admin_index.twig index 6f223889e..0dcb9145e 100644 --- a/data/web/templates/admin_index.twig +++ b/data/web/templates/admin_index.twig @@ -5,13 +5,28 @@ {% block content %}
+
-
+
{{ lang.login.login_admin }}
+
+ + +
-
- - -
+
-
{{ lang.login.other_logins }}
+
{{ lang.login.other_logins }}
@@ -86,6 +88,15 @@ {% endif %}
+ + {% if custom_login.hide_user_quicklink != 1 or custom_login.hide_domainadmin_quicklink != 1 %} +

+ {{ lang.login.login_linkstext }}
+ {% if custom_login.hide_user_quicklink != 1 %}{{ lang.login.login_usertext }}{% endif %} + {% if custom_login.hide_user_quicklink != 1 and custom_login.hide_domainadmin_quicklink != 1 %}|{% endif %} + {% if custom_login.hide_domainadmin_quicklink != 1 %}{{ lang.login.login_domainadmintext }}{% endif %} +

+ {% endif %}
{% endblock %} diff --git a/data/web/templates/domainadmin_index.twig b/data/web/templates/domainadmin_index.twig index 003fdba1f..2fc7bad98 100644 --- a/data/web/templates/domainadmin_index.twig +++ b/data/web/templates/domainadmin_index.twig @@ -5,13 +5,28 @@ {% block content %}
+
-
+
{{ lang.login.login_dadmin }}
+
+ + +
-
- - -
+
-
{{ lang.login.other_logins }}
+
{{ lang.login.other_logins }}
@@ -86,6 +88,15 @@ {% endif %}
+ + {% if custom_login.hide_user_quicklink != 1 or custom_login.hide_admin_quicklink != 1 %} +

+ {{ lang.login.login_linkstext }}
+ {% if custom_login.hide_user_quicklink != 1 %}{{ lang.login.login_usertext }}{% endif %} + {% if custom_login.hide_user_quicklink != 1 and custom_login.hide_admin_quicklink != 1 %}|{% endif %} + {% if custom_login.hide_admin_quicklink != 1 %}{{ lang.login.login_admintext }}{% endif %} +

+ {% endif %}
{% endblock %} diff --git a/data/web/templates/user_index.twig b/data/web/templates/user_index.twig index 4ed942143..234aa01cb 100644 --- a/data/web/templates/user_index.twig +++ b/data/web/templates/user_index.twig @@ -5,13 +5,30 @@ {% block content %}
+
-
+
{{ lang.login.login_user }}
+ {% if not oauth2_request %} +
+ + +
+ {% endif %}
+
- - {% if not oauth2_request %} -
- - -
- {% endif %} +
- -
{{ lang.login.other_logins }}
+
{{ lang.login.other_logins }}
+ {% endif %}
{% if has_iam_sso %} {{ lang.admin.iam_sso }} {% endif %} + {% if custom_login.force_sso != 1 %} {{ lang.login.fido2_webauthn }} + {% endif %}
{% if login_delay %}

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

@@ -96,9 +101,20 @@ {% endfor %} {% endfor %}
+
+
{% endif %}
+ + {% if custom_login.hide_admin_quicklink != 1 or custom_login.hide_domainadmin_quicklink != 1 %} +

+ {{ lang.login.login_linkstext }}
+ {% if custom_login.hide_admin_quicklink != 1 %}{{ lang.login.login_admintext }}{% endif %} + {% if custom_login.hide_admin_quicklink != 1 and custom_login.hide_domainadmin_quicklink != 1 %}|{% endif %} + {% if custom_login.hide_domainadmin_quicklink != 1 %}{{ lang.login.login_domainadmintext }}{% endif %} +

+ {% endif %}
{% if not oauth2_request and ui_texts.help_text %}