[Web] Separate Login pages

This commit is contained in:
FreddleSpl0it
2025-01-27 15:59:50 +01:00
parent 1e70a20188
commit aca01c8aa2
29 changed files with 798 additions and 338 deletions
+91
View File
@@ -0,0 +1,91 @@
{% extends 'base.twig' %}
{% block navbar %}{% endblock %}
{% block content %}
<div class="row mb-4" style="margin-top: 60px">
<div class="col-12 col-md-7 col-lg-6 col-xl-5 ms-auto me-auto">
<div class="card">
<div class="card-header d-flex align-items-center">
<i class="bi bi-person-fill me-2"></i> {{ lang.login.login }}
<div class="ms-auto form-check form-switch my-auto d-flex align-items-center">
<label class="form-check-label"><i class="bi bi-moon-fill"></i></label>
<input class="form-check-input ms-2" type="checkbox" id="dark-mode-toggle">
</div>
</div>
<div class="card-body">
<div class="text-center mailcow-logo mb-4">
<img class="main-logo" src="{{ logo|default('/img/cow_mailcow.svg') }}" alt="mailcow">
<img class="main-logo-dark" src="{{ logo_dark|default('/img/cow_mailcow.svg') }}" alt="mailcow-logo-dark">
</div>
{% if ui_texts.ui_announcement_text and ui_texts.ui_announcement_active %}
<div class="my-4 alert alert-{{ ui_texts.ui_announcement_type }} rot-enc ui-announcement-alert">{{ ui_texts.ui_announcement_text|rot13 }}</div>
{% endif %}
<form method="post" autofill="off">
<div class="d-flex mt-3">
<label class="visually-hidden" for="login_user">{{ lang.login.username }}</label>
<div class="input-group">
<div class="input-group-text"><i class="bi bi-person-fill"></i></div>
<input name="login_user" autocorrect="off" autocapitalize="none" type="text" id="login_user" class="form-control" placeholder="{{ lang.login.username }}" required="" autofocus="" autocomplete="username">
</div>
</div>
<div class="d-flex mt-3">
<label class="visually-hidden" for="pass_user">{{ lang.login.password }}</label>
<div class="input-group">
<div class="input-group-text"><i class="bi bi-lock-fill"></i></div>
<input name="pass_user" type="password" id="pass_user" class="form-control" placeholder="{{ lang.login.password }}" required="" autocomplete="current-password">
</div>
</div>
<div class="d-flex justify-content-between mt-4" style="position: relative">
<button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button> <div class="d-grid d-sm-block">
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="btn btn-secondary ms-auto dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span>
</button>
<ul class="dropdown-menu ms-auto login">
{% for key, val in available_languages %}
<li>
<a class="dropdown-item {% if mailcow_locale == key %}active{% endif %}" href="?{{ query_string({'lang': key}) }}">
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</form>
<div class="hr-title mt-5"><strong>{{ lang.login.other_logins }}</strong></div>
<div class="d-flex flex-column align-items-center">
<a class="btn btn-xs-lg btn-secondary w-100" style="max-width: 400px;" href="#" id="fido2-login"><i class="bi bi-shield-fill-check"></i> {{ lang.login.fido2_webauthn }}</a>
</div>
{% if login_delay %}
<p><div class="my-4 alert alert-info">{{ lang.login.delayed|format(login_delay) }}</b></div></p>
{% endif %}
<div class="my-4" id="fido2-alerts"></div>
{% if (mailcow_apps or app_links) and not hide_mailcow_apps %}
<legend><i class="bi bi-link-45deg"></i> {{ ui_texts.apps_name|raw }}</legend><hr />
<div class="my-2 d-grid gap-2 d-sm-block apps">
{% for app in mailcow_apps %}
{% if not app.hide %}
{% if not skip_sogo or not is_uri('SOGo', app.link) %}
<div class="m-2">
<a href="{{ app.link }}" role="button" {% if app.description %}title="{{ app.description }}"{% endif %} class="btn btn-primary btn-block">{{ app.name }}</a>
</div>
{% endif %}
{% endif %}
{% endfor %}
{% for row in app_links %}
{% for key, val in row %}
{% if not val.hide %}
<div class="m-2">
<a href="{{ val.link }}" role="button" class="btn btn-primary btn-block">{{ key }}</a>
</div>
{% endif %}
{% endfor %}
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
+26 -17
View File
@@ -66,27 +66,36 @@
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-expanded="false">{{ lang.header.mailcow_system }}</a>
<ul class="dropdown-menu">
<li><a href="/debug" class="dropdown-item {% if is_uri('debug') %}active{% endif %}">{{ lang.header.debug }}</a></li>
<li><a href="/admin" class="dropdown-item {% if is_uri('admin') %}active{% endif %}">{{ lang.header.mailcow_config }}</a></li>
<li><a href="/admin/dashboard" class="dropdown-item {% if is_uri('dashboard') %}active{% endif %}">{{ lang.header.debug }}</a></li>
<li><a href="/admin/system" class="dropdown-item {% if is_uri('system') %}active{% endif %}">{{ lang.header.mailcow_config }}</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-expanded="false">{{ lang.header.email }}</a>
<ul class="dropdown-menu">
<li><a href="/admin/mailbox" class="dropdown-item {% if is_uri('mailbox') %}active{% endif %}">{{ lang.header.mailcow_config }}</a></li>
<li><a href="/quarantine" class="dropdown-item {% if is_uri('quarantine') %}active{% endif %}">{{ lang.header.quarantine }}</a></li>
<li><a href="/admin/queue" class="dropdown-item {% if is_uri('queue') %}active{% endif %}">{{ lang.queue.queue_manager }}</a></li>
<li><a href="#" class="dropdown-item" data-bs-toggle="modal" data-container="sogo-mailcow" data-bs-target="#RestartContainer">{{ lang.header.restart_sogo }}</a></li>
</ul>
</li>
{% endif %}
{% if mailcow_cc_role != 'admin' %}
{% if mailcow_cc_role == 'domainadmin' %}
<li class="nav-item dropdown">
<a href="/domainadmin/user" class="nav-link" role="button" aria-expanded="false">{{ lang.header.user_settings }}</a>
</li>
{% elseif mailcow_cc_role == 'user' %}
<li class="nav-item dropdown">
<a href="/user" class="nav-link" role="button" aria-expanded="false">{{ lang.header.user_settings }}</a>
</li>
{% endif %}
{% if mailcow_cc_role == 'admin' or mailcow_cc_role == 'domainadmin' %}
{% if mailcow_cc_role == 'domainadmin' %}
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-expanded="false">{{ lang.header.email }}</a>
<ul class="dropdown-menu">
<li><a href="/mailbox" class="dropdown-item {% if is_uri('mailbox') %}active{% endif %}">{{ lang.header.mailcow_config }}</a></li>
<li><a href="/domainadmin/mailbox" class="dropdown-item {% if is_uri('mailbox') %}active{% endif %}">{{ lang.header.mailcow_config }}</a></li>
<li><a href="/quarantine" class="dropdown-item {% if is_uri('quarantine') %}active{% endif %}">{{ lang.header.quarantine }}</a></li>
{% if mailcow_cc_role == 'admin' %}
<li><a href="/queue" class="dropdown-item {% if is_uri('queue') %}active{% endif %}">{{ lang.queue.queue_manager }}</a></li>
<li><a href="#" class="dropdown-item" data-bs-toggle="modal" data-container="sogo-mailcow" data-bs-target="#RestartContainer">{{ lang.header.restart_sogo }}</a></li>
{% endif %}
</ul>
</li>
{% 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: <a href="{{ mailcow_info.git_project_url }}/releases/tag/{{ mailcow_info.version_tag }}" target="_blank">{{ mailcow_info.version_tag }}
</a>
</span>
{% endif %}
{% endif %}
{% if mailcow_cc_username and mailcow_info.mailcow_branch|lower == "nightly" and mailcow_info.version_tag|default %}
<span class="version">
🛠️🐮 + 🐋 = 💕
@@ -42,8 +42,8 @@
<img class="main-logo-dark img-responsive my-auto m-auto" alt="mailcow-logo-dark" style="max-width: 85%; max-height: 85%;" src="{{ logo_dark|default('/img/cow_mailcow.svg') }}">
</div>
<div class="col-sm-12 col-md-8">
<div class="table-responsive" style="margin-top: 10px;">
<table class="table table-striped table-condensed w-100">
<div style="margin-top: 10px;">
<table class="table table-striped w-100">
<tbody>
<tr>
<td>Hostname</td>
+91
View File
@@ -0,0 +1,91 @@
{% extends 'base.twig' %}
{% block navbar %}{% endblock %}
{% block content %}
<div class="row mb-4" style="margin-top: 60px">
<div class="col-12 col-md-7 col-lg-6 col-xl-5 ms-auto me-auto">
<div class="card">
<div class="card-header d-flex align-items-center">
<i class="bi bi-person-fill me-2"></i> {{ lang.login.login }}
<div class="ms-auto form-check form-switch my-auto d-flex align-items-center">
<label class="form-check-label"><i class="bi bi-moon-fill"></i></label>
<input class="form-check-input ms-2" type="checkbox" id="dark-mode-toggle">
</div>
</div>
<div class="card-body">
<div class="text-center mailcow-logo mb-4">
<img class="main-logo" src="{{ logo|default('/img/cow_mailcow.svg') }}" alt="mailcow">
<img class="main-logo-dark" src="{{ logo_dark|default('/img/cow_mailcow.svg') }}" alt="mailcow-logo-dark">
</div>
{% if ui_texts.ui_announcement_text and ui_texts.ui_announcement_active %}
<div class="my-4 alert alert-{{ ui_texts.ui_announcement_type }} rot-enc ui-announcement-alert">{{ ui_texts.ui_announcement_text|rot13 }}</div>
{% endif %}
<form method="post" autofill="off">
<div class="d-flex mt-3">
<label class="visually-hidden" for="login_user">{{ lang.login.username }}</label>
<div class="input-group">
<div class="input-group-text"><i class="bi bi-person-fill"></i></div>
<input name="login_user" autocorrect="off" autocapitalize="none" type="text" id="login_user" class="form-control" placeholder="{{ lang.login.username }}" required="" autofocus="" autocomplete="username">
</div>
</div>
<div class="d-flex mt-3">
<label class="visually-hidden" for="pass_user">{{ lang.login.password }}</label>
<div class="input-group">
<div class="input-group-text"><i class="bi bi-lock-fill"></i></div>
<input name="pass_user" type="password" id="pass_user" class="form-control" placeholder="{{ lang.login.password }}" required="" autocomplete="current-password">
</div>
</div>
<div class="d-flex justify-content-between mt-4" style="position: relative">
<button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button> <div class="d-grid d-sm-block">
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="btn btn-secondary ms-auto dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span>
</button>
<ul class="dropdown-menu ms-auto login">
{% for key, val in available_languages %}
<li>
<a class="dropdown-item {% if mailcow_locale == key %}active{% endif %}" href="?{{ query_string({'lang': key}) }}">
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</form>
<div class="hr-title mt-5"><strong>{{ lang.login.other_logins }}</strong></div>
<div class="d-flex flex-column align-items-center">
<a class="btn btn-xs-lg btn-secondary w-100" style="max-width: 400px;" href="#" id="fido2-login"><i class="bi bi-shield-fill-check"></i> {{ lang.login.fido2_webauthn }}</a>
</div>
{% if login_delay %}
<p><div class="my-4 alert alert-info">{{ lang.login.delayed|format(login_delay) }}</b></div></p>
{% endif %}
<div class="my-4" id="fido2-alerts"></div>
{% if (mailcow_apps or app_links) and not hide_mailcow_apps %}
<legend><i class="bi bi-link-45deg"></i> {{ ui_texts.apps_name|raw }}</legend><hr />
<div class="my-2 d-grid gap-2 d-sm-block apps">
{% for app in mailcow_apps %}
{% if not app.hide %}
{% if not skip_sogo or not is_uri('SOGo', app.link) %}
<div class="m-2">
<a href="{{ app.link }}" role="button" {% if app.description %}title="{{ app.description }}"{% endif %} class="btn btn-primary btn-block">{{ app.name }}</a>
</div>
{% endif %}
{% endif %}
{% endfor %}
{% for row in app_links %}
{% for key, val in row %}
{% if not val.hide %}
<div class="m-2">
<a href="{{ val.link }}" role="button" class="btn btn-primary btn-block">{{ key }}</a>
</div>
{% endif %}
{% endfor %}
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
+2 -2
View File
@@ -20,11 +20,11 @@
{{ lang.user.open_webmail_sso }} <i class="bi bi-arrow-right"></i>
</button>
{% elseif dual_login %}
<a target="_blank" href="/sogo-auth.php?login={{ mailcow_cc_username }}" role="button" class="btn btn-primary btn-lg btn-block btn-xs-lg w-100">
<a href="/sogo-auth.php?login={{ mailcow_cc_username }}" role="button" class="btn btn-primary btn-lg btn-block btn-xs-lg w-100">
{{ lang.user.open_webmail_sso }} <i class="bi bi-arrow-right"></i>
</a>
{% else %}
<a target="_blank" href="/SOGo/so" role="button" class="btn btn-primary btn-lg btn-block btn-xs-lg w-100">
<a href="/SOGo/so" role="button" class="btn btn-primary btn-lg btn-block btn-xs-lg w-100">
{{ lang.user.open_webmail_sso }} <i class="bi bi-arrow-right"></i>
</a>
{% endif %}