NobGit
public nobgit read

NobMail

Based on mailcow: dockerized

Languages

Repository composition by tracked source files.

PHP
PHP 49% JavaScript 35% HTML 9% CSS 4% Shell 2% Python 1% Lua 0% Perl 0% Ruby 0% SCSS 0%
Create file Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git

Commit

[Web] Various fixes; Allow users to login with FIDO2, SOGo SSO is a wip

d156a93a
andryyy <[email protected]> 5 years ago
data/web/inc/functions.inc.php         |  98 +++++++++--------
 data/web/inc/functions.mailbox.inc.php |   8 ++
 data/web/inc/triggers.inc.php          |   2 +-
 data/web/js/site/user.js               |   4 +-
 data/web/json_api.php                  |  41 +++++---
 data/web/modals/footer.php             |   4 +-
 data/web/user.php                      | 187 ++++++++++++++++++++++++---------
 7 files changed, 229 insertions(+), 115 deletions(-)

Diff

diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php
index 95d5571a..23687761 100644
--- a/data/web/inc/functions.inc.php
+++ b/data/web/inc/functions.inc.php
@@ -1097,8 +1097,7 @@ function set_tfa($_data) {
   $_data_log = $_data;
   !isset($_data_log['confirm_password']) ?: $_data_log['confirm_password'] = '*';
   $username = $_SESSION['mailcow_cc_username'];
-  if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-    $_SESSION['mailcow_cc_role'] != "admin") {
+  if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
       $_SESSION['return'][] =  array(
         'type' => 'danger',
         'log' => array(__FUNCTION__, $_data_log),
@@ -1107,18 +1106,35 @@ function set_tfa($_data) {
       return false;
   }
   $stmt = $pdo->prepare("SELECT `password` FROM `admin`
-      WHERE `username` = :user");
-  $stmt->execute(array(':user' => $username));
+      WHERE `username` = :username");
+  $stmt->execute(array(':username' => $username));
   $row = $stmt->fetch(PDO::FETCH_ASSOC);
-  if (!verify_hash($row['password'], $_data["confirm_password"])) {
-    $_SESSION['return'][] =  array(
-      'type' => 'danger',
-      'log' => array(__FUNCTION__, $_data_log),
-      'msg' => 'access_denied'
-    );
-    return false;
+  $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+  if (!empty($num_results)) {
+    if (!verify_hash($row['password'], $_data["confirm_password"])) {
+      $_SESSION['return'][] =  array(
+        'type' => 'danger',
+        'log' => array(__FUNCTION__, $_data_log),
+        'msg' => 'access_denied'
+      );
+      return false;
+    }
+  }
+  $stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
+      WHERE `username` = :username");
+  $stmt->execute(array(':username' => $username));
+  $row = $stmt->fetch(PDO::FETCH_ASSOC);
+  $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+  if (!empty($num_results)) {
+    if (!verify_hash($row['password'], $_data["confirm_password"])) {
+      $_SESSION['return'][] =  array(
+        'type' => 'danger',
+        'log' => array(__FUNCTION__, $_data_log),
+        'msg' => 'access_denied'
+      );
+      return false;
+    }
   }
-
 	switch ($_data["tfa_method"]) {
 		case "yubi_otp":
       $key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
@@ -1241,8 +1257,7 @@ function fido2($_data) {
 	switch ($_data["action"]) {
 		case "register":
       $username = $_SESSION['mailcow_cc_username'];
-      if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-        $_SESSION['mailcow_cc_role'] != "admin") {
+      if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
           $_SESSION['return'][] =  array(
             'type' => 'danger',
             'log' => array(__FUNCTION__, $_data["action"]),
@@ -1273,9 +1288,8 @@ function fido2($_data) {
 		case "get_user_cids":
       // Used to exclude existing CredentialIds while registering
       $username = $_SESSION['mailcow_cc_username'];
-      if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-        $_SESSION['mailcow_cc_role'] != "admin") {
-          return false;
+      if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
+        return false;
       }
       $stmt = $pdo->prepare("SELECT `credentialId` FROM `fido2` WHERE `username` = :username");
       $stmt->execute(array(':username' => $username));
@@ -1312,9 +1326,8 @@ function fido2($_data) {
 		break;
 		case "get_friendly_names":
       $username = $_SESSION['mailcow_cc_username'];
-      if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-        $_SESSION['mailcow_cc_role'] != "admin") {
-          return false;
+      if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
+        return false;
       }
       $stmt = $pdo->prepare("SELECT SHA2(`credentialId`, 256) AS `cid`, `created`, `certificateSubject`, `friendlyName` FROM `fido2` WHERE `username` = :username");
       $stmt->execute(array(':username' => $username));
@@ -1330,14 +1343,13 @@ function fido2($_data) {
 		break;
 		case "unset_fido2_key":
       $username = $_SESSION['mailcow_cc_username'];
-      if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-        $_SESSION['mailcow_cc_role'] != "admin") {
-          $_SESSION['return'][] =  array(
-            'type' => 'danger',
-            'log' => array(__FUNCTION__, $_data["action"]),
-            'msg' => 'access_denied'
-          );
-          return false;
+      if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
+        $_SESSION['return'][] =  array(
+          'type' => 'danger',
+          'log' => array(__FUNCTION__, $_data["action"]),
+          'msg' => 'access_denied'
+        );
+        return false;
       }
       $stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username AND SHA2(`credentialId`, 256) = :cid");
       $stmt->execute(array(
@@ -1352,14 +1364,13 @@ function fido2($_data) {
 		break;
     case "edit_fn":
       $username = $_SESSION['mailcow_cc_username'];
-      if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-        $_SESSION['mailcow_cc_role'] != "admin") {
-          $_SESSION['return'][] =  array(
-            'type' => 'danger',
-            'log' => array(__FUNCTION__, $_data["action"]),
-            'msg' => 'access_denied'
-          );
-          return false;
+      if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
+        $_SESSION['return'][] =  array(
+          'type' => 'danger',
+          'log' => array(__FUNCTION__, $_data["action"]),
+          'msg' => 'access_denied'
+        );
+        return false;
       }
       $stmt = $pdo->prepare("UPDATE `fido2` SET `friendlyName` = :friendlyName WHERE SHA2(`credentialId`, 256) = :cid AND `username` = :username");
       $stmt->execute(array(
@@ -1383,14 +1394,13 @@ function unset_tfa_key($_data) {
   $_data_log = $_data;
   $id = intval($_data['unset_tfa_key']);
   $username = $_SESSION['mailcow_cc_username'];
-  if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-    $_SESSION['mailcow_cc_role'] != "admin") {
-      $_SESSION['return'][] =  array(
-        'type' => 'danger',
-        'log' => array(__FUNCTION__, $_data_log),
-        'msg' => 'access_denied'
-      );
-      return false;
+  if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
+    $_SESSION['return'][] =  array(
+      'type' => 'danger',
+      'log' => array(__FUNCTION__, $_data_log),
+      'msg' => 'access_denied'
+    );
+    return false;
   }
   try {
     if (!is_numeric($id)) {
diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php
index 6d5d409c..ebeeebd5 100644
--- a/data/web/inc/functions.mailbox.inc.php
+++ b/data/web/inc/functions.mailbox.inc.php
@@ -4263,6 +4263,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
             $stmt->execute(array(
               ':username' => $username
             ));
+            $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
+            $stmt->execute(array(
+              ':username' => $username,
+            ));
+            $stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
+            $stmt->execute(array(
+              ':username' => $username,
+            ));
             $stmt = $pdo->prepare("SELECT `address`, `goto` FROM `alias`
                 WHERE `goto` REGEXP :username");
             $stmt->execute(array(':username' => '(^|,)'.$username.'($|,)'));
diff --git a/data/web/inc/triggers.inc.php b/data/web/inc/triggers.inc.php
index 4389ab35..fc8e37b5 100644
--- a/data/web/inc/triggers.inc.php
+++ b/data/web/inc/triggers.inc.php
@@ -79,7 +79,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['acl']['login_as'] == "1")
   }
 }
 
-if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
+if (isset($_SESSION['mailcow_cc_role'])) {
 	if (isset($_POST["set_tfa"])) {
 		set_tfa($_POST);
 	}
diff --git a/data/web/js/site/user.js b/data/web/js/site/user.js
index fb771f2c..99eb53fb 100644
--- a/data/web/js/site/user.js
+++ b/data/web/js/site/user.js
@@ -1,3 +1,5 @@
+// Base64 functions
+var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
 $(document).ready(function() {
   // Spam score slider
   var spam_slider = $('#spam_score')[0];
@@ -75,7 +77,7 @@ jQuery(function($){
   $('.clear-last-logins').on('click', function () {if (confirm(lang.delete_ays)) {last_logins('reset');}})
   $(".login-history").on('click', function(e) {e.preventDefault(); last_logins('get', $(this).data('days'));$(this).addClass('active').siblings().removeClass('active');});
 
-  function last_logins(action, days = 7) {
+  function last_logins(action, days = 1) {
     if (action == 'get') {
       $('.last-login').html('<i class="bi bi-hourglass"></i>' +  lang.waiting);
       $.ajax({
diff --git a/data/web/json_api.php b/data/web/json_api.php
index ad4d9dbe..cdefb83a 100644
--- a/data/web/json_api.php
+++ b/data/web/json_api.php
@@ -141,7 +141,7 @@ if (isset($_GET['query'])) {
         // fido2-registration via POST
         case "fido2-registration":
           header('Content-Type: application/json');
-          if (isset($_SESSION["mailcow_cc_role"]) && ($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin")) {
+          if (isset($_SESSION["mailcow_cc_role"])) {
             $post = trim(file_get_contents('php://input'));
             if ($post) {
               $post = json_decode($post);
@@ -302,9 +302,22 @@ if (isset($_GET['query'])) {
           if ($obj_props['superadmin'] === 1) {
             $_SESSION["mailcow_cc_role"] = "admin";
           }
-          else {
+          elseif ($obj_props['superadmin'] === 0) {
             $_SESSION["mailcow_cc_role"] = "domainadmin";
           }
+          else {
+            $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
+            $stmt->execute(array(':username' => $process_fido2['username']));
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            if ($row['username'] == $process_fido2['username']) {
+              $_SESSION["mailcow_cc_role"] = "user";
+            }
+          }
+          if (empty($_SESSION["mailcow_cc_role"])) {
+            session_unset();
+            session_destroy();
+            exit;
+          }
           $_SESSION["mailcow_cc_username"] = $process_fido2['username'];
           $_SESSION["fido2_cid"] = $process_fido2['cid'];
           unset($_SESSION["challenge"]);
@@ -339,17 +352,15 @@ if (isset($_GET['query'])) {
       switch ($category) {
         case "u2f-registration":
           header('Content-Type: application/javascript');
-          if (isset($_SESSION["mailcow_cc_role"]) &&
-            ($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") &&
-            $_SESSION["mailcow_cc_username"] == $object) {
-              list($req, $sigs) = $u2f->getRegisterData(get_u2f_registrations($object));
-              $_SESSION['regReq'] = json_encode($req);
-              $_SESSION['regSigs'] = json_encode($sigs);
-              echo 'var req = ' . json_encode($req) . ';';
-              echo 'var registeredKeys = ' . json_encode($sigs) . ';';
-              echo 'var appId = req.appId;';
-              echo 'var registerRequests = [{version: req.version, challenge: req.challenge}];';
-              return;
+          if (isset($_SESSION["mailcow_cc_role"]) && $_SESSION["mailcow_cc_username"] == $object) {
+            list($req, $sigs) = $u2f->getRegisterData(get_u2f_registrations($object));
+            $_SESSION['regReq'] = json_encode($req);
+            $_SESSION['regSigs'] = json_encode($sigs);
+            echo 'var req = ' . json_encode($req) . ';';
+            echo 'var registeredKeys = ' . json_encode($sigs) . ';';
+            echo 'var appId = req.appId;';
+            echo 'var registerRequests = [{version: req.version, challenge: req.challenge}];';
+            return;
           }
           else {
             return;
@@ -358,9 +369,7 @@ if (isset($_GET['query'])) {
         // fido2-registration via GET
         case "fido2-registration":
           header('Content-Type: application/json');
-          if (isset($_SESSION["mailcow_cc_role"]) &&
-            ($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") &&
-            $_SESSION["mailcow_cc_username"] == $object) {
+          if (isset($_SESSION["mailcow_cc_role"])) {
               // Exclude existing CredentialIds, if any
               $excludeCredentialIds = fido2(array("action" => "get_user_cids"));
               $createArgs = $WebAuthn->getCreateArgs($_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], 30, true, $GLOBALS['FIDO2_UV_FLAG_REGISTER'], $excludeCredentialIds);
diff --git a/data/web/modals/footer.php b/data/web/modals/footer.php
index 3b460435..2c39a378 100644
--- a/data/web/modals/footer.php
+++ b/data/web/modals/footer.php
@@ -1,5 +1,5 @@
 <?php
-if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin")):
+if (isset($_SESSION['mailcow_cc_role'])) {
 ?>
 <div class="modal fade" id="YubiOTPModal" tabindex="-1" role="dialog" aria-labelledby="YubiOTPModalLabel">
   <div class="modal-dialog" role="document">
@@ -127,7 +127,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 </div>
 
 <?php
-endif;
+}
 if (isset($_SESSION['pending_tfa_method'])):
   $tfa_method = $_SESSION['pending_tfa_method'];
 ?>
diff --git a/data/web/user.php b/data/web/user.php
index 10af338a..4cafec12 100644
--- a/data/web/user.php
+++ b/data/web/user.php
@@ -27,23 +27,26 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
       </div>
     </div>
     <hr>
-    
+
     <? // TFA ?>
     <div class="row">
       <div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?></div>
         <div class="col-sm-9 col-xs-7">
           <p id="tfa_pretty"><?=$tfa_data['pretty'];?></p>
-            <table id="tfa_keys">
-              <?php if (!empty($tfa_data['additional'])):
-              foreach ($tfa_data['additional'] as $key_info): ?>
+            <div id="tfa_keys">
+              <?php
+              if (!empty($tfa_data['additional'])) {
+                foreach ($tfa_data['additional'] as $key_info) { ?>
                 <form style="display:inline;" method="post">
-                <input type="hidden" name="unset_tfa_key" value="<?=$key_info['id'];?>" />
-                <div class="label label-default">🔑 <?=$key_info['key_id'];?> <a href="#" style="font-weight:bold;color:white" onClick="$(this).closest('form').submit()">[<?=strtolower($lang['admin']['remove']);?>]</a></div>
-              </form>
-              <?php endforeach;
-              endif;?>
-            </table>
-            <br />
+                  <input type="hidden" name="unset_tfa_key" value="<?=$key_info['id'];?>" />
+                  <div class="label label-default"><i class="bi bi-key-fill"></i> <?=$key_info['key_id'];?> <a href="#" style="font-weight:bold;color:white" onClick='return confirm("<?=$lang['user']['delete_ays'];?>")?$(this).closest("form").submit():"";'>[<?=strtolower($lang['admin']['remove']);?>]</a></div>
+                </form>
+                <?php
+                }
+              }
+              ?>
+            </div>
+            <br>
         </div>
     </div>
     <div class="row">
@@ -58,12 +61,13 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
       </div>
     </div>
 
+    <hr>
     <? // FIDO2 ?>
-    <legend style="margin-top:20px">
-    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-      <path d="M17.81 4.47c-.08 0-.16-.02-.23-.06C15.66 3.42 14 3 12.01 3c-1.98 0-3.86.47-5.57 1.41-.24.13-.54.04-.68-.2-.13-.24-.04-.55.2-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67-.09.18-.26.28-.44.28zM3.5 9.72c-.1 0-.2-.03-.29-.09-.23-.16-.28-.47-.12-.7.99-1.4 2.25-2.5 3.75-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25.16.22.11.54-.12.7-.23.16-.54.11-.7-.12-.9-1.26-2.04-2.25-3.39-2.94-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5 1.7-3.4 2.96-.08.14-.23.21-.39.21zm6.25 12.07c-.13 0-.26-.05-.35-.15-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39c0 .28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19 0-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39 0-.28.22-.5.5-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64-.03 1.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91 22c-.04 0-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1-1.4-1.39-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08-.87 2.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.44 1.58-6.61 4.03-.39.81-.59 1.76-.59 2.8 0 .78.07 2.01.67 3.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29-.49-1.31-.73-2.61-.73-3.96 0-1.2.23-2.29.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z"/>
-    </svg>
-    <?=$lang['fido2']['fido2_auth'];?></legend>
+    <div class="row">
+      <div class="col-sm-3 col-xs-5 text-right">
+        <p><i class="bi bi-shield-fill-check"></i> <?=$lang['fido2']['fido2_auth'];?></p>
+      </div>
+    </div>
     <div class="row">
       <div class="col-sm-3 col-xs-5 text-right"><?=$lang['fido2']['known_ids'];?>:</div>
       <div class="col-sm-9 col-xs-7">
@@ -86,7 +90,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
                 <input type="hidden" name="unset_fido2_key" value="<?=$key_info['cid'];?>" />
                 <div class="btn-group">
                 <a href="#" class="btn btn-xs btn-default" data-cid="<?=$key_info['cid'];?>" data-subject="<?=base64_encode($key_info['subject']);?>" data-toggle="modal" data-target="#fido2ChangeFn"><i class="bi bi-pencil-fill"></i> <?=strtolower($lang['fido2']['rename']);?></a>
-                <a href="#" onClick='return confirm("<?=$lang['admin']['ays'];?>")?$(this).closest("form").submit():"";' class="btn btn-xs btn-danger"><i class="bi bi-trash"></i> <?=strtolower($lang['admin']['remove']);?></a>
+                <a href="#" onClick='return confirm("<?=$lang['user']['delete_ays'];?>")?$(this).closest("form").submit():"";' class="btn btn-xs btn-danger"><i class="bi bi-trash"></i> <?=strtolower($lang['admin']['remove']);?></a>
                 </form>
                 </div>
               </td>
@@ -130,6 +134,8 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
   $username = $_SESSION['mailcow_cc_username'];
   $mailboxdata = mailbox('get', 'mailbox_details', $username);
   $pushover_data = pushover('get', $username);
+  $tfa_data = get_tfa();
+  $fido2_data = fido2(array("action" => "get_friendly_names"));
 
   $clientconfigstr = "host=" . urlencode($mailcow_hostname) . "&email=" . urlencode($username) . "&name=" . urlencode($mailboxdata['name']) . "&ui=" . urlencode(strtok($_SERVER['HTTP_HOST'], ':')) . "&port=" . urlencode($autodiscover_config['caldav']['port']);
   if ($autodiscover_config['useEASforOutlook'] == 'yes')
@@ -150,7 +156,14 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
 
   <!-- Nav tabs -->
   <ul class="nav nav-tabs" role="tablist">
-    <li role="presentation" class="active"><a href="#userSettings" aria-controls="userSettings" role="tab" data-toggle="tab"><?=$lang['user']['mailbox_details'];?></a></li>
+    <li class="dropdown active">
+      <a class="dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['user']['mailbox'];?><span class="caret"></span></a>
+      <ul class="dropdown-menu">
+        <li role="presentation" class="active" data-dont-remember="1"><a href="#tab-user-auth" aria-controls="tab-user-auth" role="tab" data-toggle="tab"><?=$lang['user']['mailbox_general'];?></a></li>
+        <li role="presentation"><a href="#tab-user-details" aria-controls="tab-config-fwdhosts" role="tab" data-toggle="tab"><?=$lang['user']['mailbox_details'];?></a></li>
+        <li role="presentation"><a href="#tab-user-settings" aria-controls="tab-config-f2b" role="tab" data-toggle="tab"><?=$lang['user']['mailbox_settings'];?></a></li>
+      </ul>
+    </li>
     <li role="presentation"><a href="#SpamAliases" aria-controls="SpamAliases" role="tab" data-toggle="tab"><?=$lang['user']['spam_aliases'];?></a></li>
     <li role="presentation"><a href="#Spamfilter" aria-controls="Spamfilter" role="tab" data-toggle="tab"><?=$lang['user']['spamfilter'];?></a></li>
     <li role="presentation"><a href="#Syncjobs" aria-controls="Syncjobs" role="tab" data-toggle="tab"><?=$lang['user']['sync_jobs'];?></a></li>
@@ -161,16 +174,99 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
 
   <div class="tab-content">
 
-  <div role="tabpanel" class="tab-pane active" id="userSettings">
+  <div role="tabpanel" class="tab-pane active" id="tab-user-auth">
     <div class="panel panel-default">
-      <div class="panel-heading"><?=$lang['user']['mailbox_details'];?></div>
+      <div class="panel-heading"><?=$lang['user']['mailbox_general'];?></div>
       <div class="panel-body">
+        <div class="row">
+          <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['in_use'];?>:</div>
+          <div class="col-md-5 col-xs-7">
+            <div class="progress">
+              <div class="progress-bar progress-bar-<?=$mailboxdata['percent_class'];?>" role="progressbar" aria-valuenow="<?=$mailboxdata['percent_in_use'];?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$mailboxdata['percent_in_use'];?>%;">
+                <?=$mailboxdata['percent_in_use'];?>%
+              </div>
+            </div>
+            <p><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=($mailboxdata['quota'] == 0) ? '∞' : formatBytes($mailboxdata['quota'], 2);?><br><?=$mailboxdata['messages'];?> <?=$lang['user']['messages'];?></p>
+            <hr>
+            <p><a href="#pwChangeModal" data-toggle="modal"><i class="bi bi-pencil-fill"></i> <?=$lang['user']['change_password'];?></a></p>
+          </div>
+        </div>
+        <hr>
+        <? // FIDO2 ?>
+        <div class="row">
+          <div class="col-sm-3 col-xs-5 text-right">
+            <p><i class="bi bi-shield-fill-check"></i> <?=$lang['fido2']['fido2_auth'];?></p>
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-sm-3 col-xs-5 text-right">
+          <?=$lang['fido2']['known_ids'];?>:
+          </div>
+          <div class="col-sm-9 col-xs-7">
+            <div class="table-responsive">
+            <table class="table table-striped table-hover table-condensed" id="fido2_keys">
+              <tr>
+                <th>ID</th>
+                <th style="min-width:240px;text-align: right"><?=$lang['admin']['action'];?></th>
+              </tr>
+              <?php
+              if (!empty($fido2_data)) {
+                foreach ($fido2_data as $key_info) {
+              ?>
+              <tr>
+                <td>
+                  <?=($_SESSION['fido2_cid'] == $key_info['cid']) ? '<i class="bi bi-unlock-fill"></i> ' : NULL; ?><?=(!empty($key_info['fn']))?$key_info['fn']:$key_info['subject'];?>
+                </td>
+                <td style="min-width:240px;text-align: right">
+                  <form style="display:inline;" method="post">
+                  <input type="hidden" name="unset_fido2_key" value="<?=$key_info['cid'];?>" />
+                  <div class="btn-group">
+                  <a href="#" class="btn btn-xs btn-default" data-cid="<?=$key_info['cid'];?>" data-subject="<?=base64_encode($key_info['subject']);?>" data-toggle="modal" data-target="#fido2ChangeFn"><i class="bi bi-pencil-fill"></i> <?=strtolower($lang['fido2']['rename']);?></a>
+                  <a href="#" onClick='return confirm("<?=$lang['user']['delete_ays'];?>")?$(this).closest("form").submit():"";' class="btn btn-xs btn-danger"><i class="bi bi-trash"></i> <?=strtolower($lang['admin']['remove']);?></a>
+                  </form>
+                  </div>
+                </td>
+              </tr>
+              <?php
+                }
+              }
+              ?>
+            </table>
+            </div>
+            <br>
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-sm-offset-3 col-sm-9">
+            <button class="btn btn-sm btn-primary" id="register-fido2"><?=$lang['fido2']['set_fido2'];?></button>
+          </div>
+        </div>
+        <br>
+        <div class="row" id="status-fido2">
+          <div class="col-sm-3 col-xs-5 text-right"><?=$lang['fido2']['register_status'];?>:</div>
+          <div class="col-sm-9 col-xs-7">
+            <div id="fido2-alerts">-</div>
+          </div>
+          <br>
+        </div>
+        <hr>
+        <div class="row">
+          <div class="col-md-3 col-xs-5 text-right"><i class="bi bi-file-earmark-text"></i> <?=$lang['user']['apple_connection_profile'];?>:</div>
+          <div class="col-md-9 col-xs-7">
+            <p><i class="bi bi-file-earmark-post"></i> <a href="/mobileconfig.php?only_email"><?=$lang['user']['email'];?></a> <small>IMAP, SMTP</small></p>
+            <p class="help-block"><?=$lang['user']['apple_connection_profile_mailonly'];?></p>
+            <?php if (getenv('SKIP_SOGO') != "y") { ?>
+            <p><i class="bi bi-file-earmark-post"></i> <a href="/mobileconfig.php"><?=$lang['user']['email_and_dav'];?></a> <small>IMAP, SMTP, Cal/CardDAV</small></p>
+            <p class="help-block"><?=$lang['user']['apple_connection_profile_complete'];?></p>
+            <?php } ?>
+          </div>
+        </div>
+        <hr>
         <div class="row">
           <div class="col-sm-offset-3 col-sm-9">
             <?php if ($mailboxdata['attributes']['force_pw_update'] == "1"): ?>
             <div class="alert alert-danger"><?=$lang['user']['force_pw_update'];?></div>
             <?php endif; ?>
-            <p><a href="#pwChangeModal" data-toggle="modal">[<?=$lang['user']['change_password'];?>]</a></p>
             <p><a target="_blank" href="https://mailcow.github.io/mailcow-dockerized-docs/client/#<?=$clientconfigstr;?>">[<?=$lang['user']['client_configuration'];?>]</a></p>
             <p><a href="#userFilterModal" data-toggle="modal">[<?=$lang['user']['show_sieve_filters'];?>]</a></p>
             <hr>
@@ -178,9 +274,9 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
             <div class="dropdown pull-left">
               <button class="btn btn-default btn-xs dropdown-toggle" type="button" id="history_sasl_days" data-toggle="dropdown"><?=$lang['user']['login_history'];?> <span class="caret"></span></button>
               <ul class="dropdown-menu">
-                <li class="login-history" data-days="1"><a href="#">1 <?=$lang['user']['day'];?></a></li>
+                <li class="login-history active" data-days="1"><a href="#">1 <?=$lang['user']['day'];?></a></li>
                 <li class="login-history" data-days="7"><a href="#">1 <?=$lang['user']['week'];?></a></li>
-                <li class="login-history active" data-days="14"><a href="#">2 <?=$lang['user']['weeks'];?></a></li>
+                <li class="login-history" data-days="14"><a href="#">2 <?=$lang['user']['weeks'];?></a></li>
                 <li class="login-history" data-days="31"><a href="#">1 <?=$lang['user']['month'];?></a></li>
               </ul>
             </div>
@@ -191,20 +287,15 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
             </span>
           </div>
         </div>
-        <hr>
-        <div class="row">
-          <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['apple_connection_profile'];?>:</div>
-          <div class="col-md-9 col-xs-7">
-            <p><i class="bi bi-file-earmark-post"></i> <a href="/mobileconfig.php?only_email"><?=$lang['user']['email'];?></a> <small>IMAP, SMTP</small></p>
-            <p class="help-block"><?=$lang['user']['apple_connection_profile_mailonly'];?></p>
-            <?php if (getenv('SKIP_SOGO') != "y") { ?>
-            <p><i class="bi bi-file-earmark-post"></i> <a href="/mobileconfig.php"><?=$lang['user']['email_and_dav'];?></a> <small>IMAP, SMTP, Cal/CardDAV</small></p>
-            <p class="help-block"><?=$lang['user']['apple_connection_profile_complete'];?></p>
-            <?php } ?>
-          </div>
-        </div>
+      </div>
+    </div>
+  </div>
+
+  <div role="tabpanel" class="tab-pane" id="tab-user-details">
+    <div class="panel panel-default">
+      <div class="panel-heading"><?=$lang['user']['mailbox_details'];?></div>
+      <div class="panel-body">
         <?php if ($mailboxdata['attributes']['xmpp_access'] == 1 && $mailboxdata['domain_xmpp'] == 1) { ?>
-        <hr>
         <div class="row">
           <div class="col-md-3 col-xs-5 text-right">
             <img class="img-responsive pull-right xmpp-logo-user" alt="XMPP Logo" src="data:image/png;base64,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">
@@ -260,7 +351,6 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
             foreach (array_filter($user_get_alias_details['shared_aliases']) as $shared_alias => $shared_alias_meta) {
               (!empty($shared_alias_meta['public_comment'])) ?
                 printf('%s &mdash; <span class="bg-info">%s</span><br>', $shared_alias, $shared_alias_meta['public_comment']) :
-
                 printf('%s<br>', $shared_alias);
             }
           }
@@ -286,19 +376,14 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
           <p><?=(empty($user_get_alias_details['is_catch_all'])) ? '<i class="bi bi-x-lg"></i>' : '' ;?></p>
           </div>
         </div>
-        <hr>
-        <div class="row">
-          <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['in_use'];?>:</div>
-          <div class="col-md-5 col-xs-7">
-            <div class="progress">
-              <div class="progress-bar progress-bar-<?=$mailboxdata['percent_class'];?>" role="progressbar" aria-valuenow="<?=$mailboxdata['percent_in_use'];?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$mailboxdata['percent_in_use'];?>%;">
-                <?=$mailboxdata['percent_in_use'];?>%
-              </div>
-            </div>
-            <p><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=($mailboxdata['quota'] == 0) ? '∞' : formatBytes($mailboxdata['quota'], 2);?><br><?=$mailboxdata['messages'];?> <?=$lang['user']['messages'];?></p>
-          </div>
-        </div>
-        <hr>
+      </div>
+    </div>
+  </div>
+
+  <div role="tabpanel" class="tab-pane" id="tab-user-settings">
+    <div class="panel panel-default">
+      <div class="panel-heading"><?=$lang['user']['mailbox_settings'];?></div>
+      <div class="panel-body">
         <?php
         // Show tagging options
         $get_tagging_options = mailbox('get', 'delimiter_action', $username);
@@ -481,7 +566,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
       </div>
     </div>
 	</div>
-
+  
 	<div role="tabpanel" class="tab-pane" id="Spamfilter">
 		<h4><?=$lang['user']['spamfilter_behavior'];?></h4>
 		<form class="form-horizontal" role="form" data-id="spam_score" method="post">