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] Allow admins to limit EAS and DAV access for mailbox users

56ea4302
FreddleSpl0it <[email protected]> 6 months ago
data/conf/dovecot/auth/mailcowauth.php         | 15 +++++--
 data/web/autodiscover.php                      |  2 +-
 data/web/inc/functions.auth.inc.php            | 57 ++++++++------------------
 data/web/inc/functions.mailbox.inc.php         | 20 +++++++++
 data/web/inc/init_db.inc.php                   |  4 +-
 data/web/inc/prerequisites.inc.php             |  2 +-
 data/web/inc/triggers.admin.inc.php            |  2 +-
 data/web/inc/triggers.domainadmin.inc.php      |  2 +-
 data/web/inc/triggers.user.inc.php             |  2 +-
 data/web/inc/vars.inc.php                      |  6 +++
 data/web/js/site/mailbox.js                    | 32 +++++++++++++++
 data/web/sogo-auth.php                         | 14 ++++---
 data/web/templates/edit/mailbox-templates.twig |  2 +
 data/web/templates/edit/mailbox.twig           |  2 +
 data/web/templates/modals/mailbox.twig         |  4 ++
 data/web/templates/user/tab-user-auth.twig     |  2 +
 16 files changed, 111 insertions(+), 57 deletions(-)

Diff

diff --git a/data/conf/dovecot/auth/mailcowauth.php b/data/conf/dovecot/auth/mailcowauth.php
index 06c0bd99..d1c7381f 100644
--- a/data/conf/dovecot/auth/mailcowauth.php
+++ b/data/conf/dovecot/auth/mailcowauth.php
@@ -80,14 +80,21 @@ if ($isSOGoRequest) {
 }
 if ($result === false){
   // If it's a SOGo Request, don't check for protocol access
-  $service = ($isSOGoRequest) ? false : array($post['service'] => true);
-  $result = apppass_login($post['username'], $post['password'], $service, array(
+  if ($isSOGoRequest) {
+    $service = 'SOGO';
+    $post['service'] = 'NONE';
+  } else {
+    $service = $post['service'];
+  }
+
+  $result = apppass_login($post['username'], $post['password'], array(
+    'service' => $post['service'],
     'is_internal' => true,
     'remote_addr' => $post['real_rip']
   ));
   if ($result) {
-    error_log('MAILCOWAUTH: App auth for user ' . $post['username'] . " with service " . $post['service'] . " from IP " . $post['real_rip']);
-    set_sasl_log($post['username'], $post['real_rip'], $post['service']);
+    error_log('MAILCOWAUTH: App auth for user ' . $post['username'] . " with service " . $service . " from IP " . $post['real_rip']);
+    set_sasl_log($post['username'], $post['real_rip'], $service);
   }
 }
 if ($result === false){
diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php
index 224f94f7..d3cda400 100644
--- a/data/web/autodiscover.php
+++ b/data/web/autodiscover.php
@@ -79,7 +79,7 @@ if (empty($_SERVER['PHP_AUTH_USER']) || empty($_SERVER['PHP_AUTH_PW'])) {
   exit(0);
 }
 
-$login_role = check_login($login_user, $login_pass, array('eas' => TRUE));
+$login_role = check_login($login_user, $login_pass, array('service' => 'EAS'));
 
 if ($login_role === "user") {
   header("Content-Type: application/xml");
diff --git a/data/web/inc/functions.auth.inc.php b/data/web/inc/functions.auth.inc.php
index 059dd4cd..3903ba64 100644
--- a/data/web/inc/functions.auth.inc.php
+++ b/data/web/inc/functions.auth.inc.php
@@ -1,10 +1,11 @@
 <?php
-function check_login($user, $pass, $app_passwd_data = false, $extra = null) {
+function check_login($user, $pass, $extra = null) {
   global $pdo;
   global $redis;
 
   $is_internal = $extra['is_internal'];
   $role = $extra['role'];
+  $extra['service'] = !isset($extra['service']) ? 'NONE' : $extra['service'];
 
   // Try validate admin
   if (!isset($role) || $role == "admin") {
@@ -25,34 +26,20 @@ function check_login($user, $pass, $app_passwd_data = false, $extra = null) {
 
   // Try validate app password
   if (!isset($role) || $role == "app") {
-    $result = apppass_login($user, $pass, $app_passwd_data);
+    $result = apppass_login($user, $pass, $extra);
     if ($result !== false) {
-      if ($app_passwd_data['eas'] === true) {
-        $service = 'EAS';
-      } elseif ($app_passwd_data['dav'] === true) {
-        $service = 'DAV';
-      } else {
-        $service = 'NONE';
-      }
       $real_rip = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
-      set_sasl_log($user, $real_rip, $service, $pass);
+      set_sasl_log($user, $real_rip, $extra['service'], $pass);
       return $result;
     }
   }
 
   // Try validate user
   if (!isset($role) || $role == "user") {
-    $result = user_login($user, $pass);
+    $result = user_login($user, $pass, $extra);
     if ($result !== false) {
-      if ($app_passwd_data['eas'] === true) {
-        $service = 'EAS';
-      } elseif ($app_passwd_data['dav'] === true) {
-        $service = 'DAV';
-      } else {
-        $service = 'MAILCOWUI';
-      }
       $real_rip = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
-      set_sasl_log($user, $real_rip, $service);
+      set_sasl_log($user, $real_rip, $extra['service']);
       return $result;
     }
   }
@@ -193,7 +180,7 @@ function user_login($user, $pass, $extra = null){
   global $iam_settings;
 
   $is_internal = $extra['is_internal'];
-  $service = $extra['service'];
+  $extra['service'] = !isset($extra['service']) ? 'NONE' : $extra['service'];
 
   if (!filter_var($user, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $user))) {
     if (!$is_internal){
@@ -236,10 +223,10 @@ function user_login($user, $pass, $extra = null){
       $row = $stmt->fetch(PDO::FETCH_ASSOC);
 
       if (!empty($row)) {
-        // check if user has access to service (imap, smtp, pop3, sieve) if service is set
+        // check if user has access to service (imap, smtp, pop3, sieve, dav, eas) if service is set
         $row['attributes'] = json_decode($row['attributes'], true);
-        if (isset($service)) {
-          $key = strtolower($service) . "_access";
+        if ($extra['service'] != 'NONE') {
+          $key = strtolower($extra['service']) . "_access";
           if (isset($row['attributes'][$key]) && $row['attributes'][$key] != '1') {
             return false;
           }
@@ -253,8 +240,8 @@ function user_login($user, $pass, $extra = null){
 
   // check if user has access to service (imap, smtp, pop3, sieve) if service is set
   $row['attributes'] = json_decode($row['attributes'], true);
-  if (isset($service)) {
-    $key = strtolower($service) . "_access";
+  if ($extra['service'] != 'NONE') {
+    $key = strtolower($extra['service']) . "_access";
     if (isset($row['attributes'][$key]) && $row['attributes'][$key] != '1') {
       return false;
     }
@@ -408,7 +395,7 @@ function user_login($user, $pass, $extra = null){
 
   return false;
 }
-function apppass_login($user, $pass, $app_passwd_data, $extra = null){
+function apppass_login($user, $pass, $extra = null){
   global $pdo;
 
   $is_internal = $extra['is_internal'];
@@ -424,20 +411,8 @@ function apppass_login($user, $pass, $app_passwd_data, $extra = null){
     return false;
   }
 
-  $protocol = false;
-  if ($app_passwd_data['eas']){
-    $protocol = 'eas';
-  } else if ($app_passwd_data['dav']){
-    $protocol = 'dav';
-  } else if ($app_passwd_data['smtp']){
-    $protocol = 'smtp';
-  } else if ($app_passwd_data['imap']){
-    $protocol = 'imap';
-  } else if ($app_passwd_data['sieve']){
-    $protocol = 'sieve';
-  } else if ($app_passwd_data['pop3']){
-    $protocol = 'pop3';
-  } else if (!$is_internal) {
+  $extra['service'] = !isset($extra['service']) ? 'NONE' : $extra['service'];
+  if (!$is_internal && $extra['service'] == 'NONE') {
     return false;
   }
 
@@ -458,7 +433,7 @@ function apppass_login($user, $pass, $app_passwd_data, $extra = null){
   $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
 
   foreach ($rows as $row) {
-    if ($protocol && $row[$protocol . '_access'] != '1'){
+    if ($extra['service'] != 'NONE' && $row[strtolower($extra['service']) . '_access'] != '1'){
       continue;
     }
 
diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php
index d8e4e178..eb7d82df 100644
--- a/data/web/inc/functions.mailbox.inc.php
+++ b/data/web/inc/functions.mailbox.inc.php
@@ -1075,6 +1075,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
             $_data['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0;
             $_data['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
             $_data['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0;
+            $_data['eas_access'] = (in_array('eas', $_data['protocol_access'])) ? 1 : 0;
+            $_data['dav_access'] = (in_array('dav', $_data['protocol_access'])) ? 1 : 0;
           }
           $active = (isset($_data['active'])) ? intval($_data['active']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['active']);
           $force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']);
@@ -1085,6 +1087,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
           $pop3_access = (isset($_data['pop3_access'])) ? intval($_data['pop3_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']);
           $smtp_access = (isset($_data['smtp_access'])) ? intval($_data['smtp_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']);
           $sieve_access = (isset($_data['sieve_access'])) ? intval($_data['sieve_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['sieve_access']);
+          $eas_access = (isset($_data['eas_access'])) ? intval($_data['eas_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['eas_access']);
+          $dav_access = (isset($_data['dav_access'])) ? intval($_data['dav_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['dav_access']);
           $relayhost = (isset($_data['relayhost'])) ? intval($_data['relayhost']) : 0;
           $quarantine_notification = (isset($_data['quarantine_notification'])) ? strval($_data['quarantine_notification']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']);
           $quarantine_category = (isset($_data['quarantine_category'])) ? strval($_data['quarantine_category']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']);
@@ -1103,6 +1107,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
               'pop3_access' => strval($pop3_access),
               'smtp_access' => strval($smtp_access),
               'sieve_access' => strval($sieve_access),
+              'eas_access' => strval($eas_access),
+              'dav_access' => strval($dav_access),
               'relayhost' => strval($relayhost),
               'passwd_update' => time(),
               'mailbox_format' => strval($MAILBOX_DEFAULT_ATTRIBUTES['mailbox_format']),
@@ -1721,12 +1727,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
             $attr['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0;
             $attr['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
             $attr['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0;
+            $attr['eas_access'] = (in_array('eas', $_data['protocol_access'])) ? 1 : 0;
+            $attr['dav_access'] = (in_array('dav', $_data['protocol_access'])) ? 1 : 0;
           }
           else {
             $attr['imap_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['imap_access']);
             $attr['pop3_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']);
             $attr['smtp_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']);
             $attr['sieve_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['sieve_access']);
+            $attr['eas_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['eas_access']);
+            $attr['dav_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['dav_access']);
           }
           if (isset($_data['acl'])) {
             $_data['acl'] = (array)$_data['acl'];
@@ -3043,6 +3053,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
               $_data['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0;
               $_data['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
               $_data['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0;
+              $_data['eas_access'] = (in_array('eas', $_data['protocol_access'])) ? 1 : 0;
+              $_data['dav_access'] = (in_array('dav', $_data['protocol_access'])) ? 1 : 0;
             }
             if (!empty($is_now)) {
               $active               = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
@@ -3052,6 +3064,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
               (int)$pop3_access     = (isset($_data['pop3_access']) && hasACLAccess("protocol_access")) ? intval($_data['pop3_access']) : intval($is_now['attributes']['pop3_access']);
               (int)$smtp_access     = (isset($_data['smtp_access']) && hasACLAccess("protocol_access")) ? intval($_data['smtp_access']) : intval($is_now['attributes']['smtp_access']);
               (int)$sieve_access    = (isset($_data['sieve_access']) && hasACLAccess("protocol_access")) ? intval($_data['sieve_access']) : intval($is_now['attributes']['sieve_access']);
+              (int)$eas_access     = (isset($_data['eas_access']) && hasACLAccess("protocol_access")) ? intval($_data['eas_access']) : intval($is_now['attributes']['eas_access']);
+              (int)$dav_access    = (isset($_data['dav_access']) && hasACLAccess("protocol_access")) ? intval($_data['dav_access']) : intval($is_now['attributes']['dav_access']);
               (int)$relayhost       = (isset($_data['relayhost']) && hasACLAccess("mailbox_relayhost")) ? intval($_data['relayhost']) : intval($is_now['attributes']['relayhost']);
               (int)$quota_m         = (isset_has_content($_data['quota'])) ? intval($_data['quota']) : ($is_now['quota'] / 1048576);
               $name                 = (!empty($_data['name'])) ? ltrim(rtrim($_data['name'], '>'), '<') : $is_now['name'];
@@ -3335,6 +3349,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
                   `attributes` = JSON_SET(`attributes`, '$.pop3_access', :pop3_access),
                   `attributes` = JSON_SET(`attributes`, '$.relayhost', :relayhost),
                   `attributes` = JSON_SET(`attributes`, '$.smtp_access', :smtp_access),
+                  `attributes` = JSON_SET(`attributes`, '$.eas_access', :eas_access),
+                  `attributes` = JSON_SET(`attributes`, '$.dav_access', :dav_access),
                   `attributes` = JSON_SET(`attributes`, '$.recovery_email', :recovery_email),
                   `attributes` = JSON_SET(`attributes`, '$.attribute_hash', :attribute_hash)
                     WHERE `username` = :username");
@@ -3349,6 +3365,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
                 ':pop3_access' => $pop3_access,
                 ':sieve_access' => $sieve_access,
                 ':smtp_access' => $smtp_access,
+                ':eas_access' => $eas_access,
+                ':dav_access' => $dav_access,
                 ':recovery_email' => $pw_recovery_email,
                 ':relayhost' => $relayhost,
                 ':username' => $username,
@@ -3731,6 +3749,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
               $attr['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0;
               $attr['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
               $attr['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0;
+              $attr['eas_access'] = (in_array('eas', $_data['protocol_access'])) ? 1 : 0;
+              $attr['dav_access'] = (in_array('dav', $_data['protocol_access'])) ? 1 : 0;
             }
             else {
               foreach ($is_now as $key => $value){
diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php
index ffaf1209..67e98a5a 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 = "10312025_0525";
+    $db_version = "28012026_1000";
 
     $stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
     $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@@ -1394,6 +1394,8 @@ function init_db_schema()
     $pdo->query("UPDATE `mailbox` SET `attributes` =  JSON_SET(`attributes`, '$.imap_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.imap_access') IS NULL;");
     $pdo->query("UPDATE `mailbox` SET `attributes` =  JSON_SET(`attributes`, '$.pop3_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.pop3_access') IS NULL;");
     $pdo->query("UPDATE `mailbox` SET `attributes` =  JSON_SET(`attributes`, '$.smtp_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.smtp_access') IS NULL;");
+    $pdo->query("UPDATE `mailbox` SET `attributes` =  JSON_SET(`attributes`, '$.eas_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.eas_access') IS NULL;");
+    $pdo->query("UPDATE `mailbox` SET `attributes` =  JSON_SET(`attributes`, '$.dav_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.dav_access') IS NULL;");
     $pdo->query("UPDATE `mailbox` SET `attributes` =  JSON_SET(`attributes`, '$.mailbox_format', \"maildir:\") WHERE JSON_VALUE(`attributes`, '$.mailbox_format') IS NULL;");
     $pdo->query("UPDATE `mailbox` SET `attributes` =  JSON_SET(`attributes`, '$.quarantine_notification', \"never\") WHERE JSON_VALUE(`attributes`, '$.quarantine_notification') IS NULL;");
     $pdo->query("UPDATE `mailbox` SET `attributes` =  JSON_SET(`attributes`, '$.quarantine_category', \"reject\") WHERE JSON_VALUE(`attributes`, '$.quarantine_category') IS NULL;");
diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php
index deb5da8f..198e675a 100644
--- a/data/web/inc/prerequisites.inc.php
+++ b/data/web/inc/prerequisites.inc.php
@@ -121,7 +121,7 @@ class mailcowPdo extends OAuth2\Storage\Pdo {
     $this->config['user_table'] = 'mailbox';
   }
   public function checkUserCredentials($username, $password) {
-    if (check_login($username, $password) == 'user') {
+    if (check_login($username, $password, array("role" => "user", "service" => "NONE")) == 'user') {
       return true;
     }
     return false;
diff --git a/data/web/inc/triggers.admin.inc.php b/data/web/inc/triggers.admin.inc.php
index df46a459..2a02ba51 100644
--- a/data/web/inc/triggers.admin.inc.php
+++ b/data/web/inc/triggers.admin.inc.php
@@ -44,7 +44,7 @@ if (isset($_GET["cancel_tfa_login"])) {
 
 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" => "admin"));
+  $as = check_login($login_user, $_POST["pass_user"], array("role" => "admin", "service" => "MAILCOWUI"));
 
   if ($as == "admin") {
     session_regenerate_id(true);
diff --git a/data/web/inc/triggers.domainadmin.inc.php b/data/web/inc/triggers.domainadmin.inc.php
index a9f91368..764d9009 100644
--- a/data/web/inc/triggers.domainadmin.inc.php
+++ b/data/web/inc/triggers.domainadmin.inc.php
@@ -55,7 +55,7 @@ if (isset($_GET["cancel_tfa_login"])) {
 
 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" => "domain_admin"));
+  $as = check_login($login_user, $_POST["pass_user"], array("role" => "domain_admin", "service" => "MAILCOWUI"));
 
   if ($as == "domainadmin") {
     session_regenerate_id(true);
diff --git a/data/web/inc/triggers.user.inc.php b/data/web/inc/triggers.user.inc.php
index 36176c69..cc33596f 100644
--- a/data/web/inc/triggers.user.inc.php
+++ b/data/web/inc/triggers.user.inc.php
@@ -119,7 +119,7 @@ if (isset($_GET["cancel_tfa_login"])) {
 
 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"));
+  $as = check_login($login_user, $_POST["pass_user"], array("role" => "user", "service" => "MAILCOWUI"));
 
   if ($as == "user") {
     set_user_loggedin_session($login_user);
diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php
index 9f3208e3..dc163d62 100644
--- a/data/web/inc/vars.inc.php
+++ b/data/web/inc/vars.inc.php
@@ -215,6 +215,12 @@ $MAILBOX_DEFAULT_ATTRIBUTES['smtp_access'] = true;
 // Mailbox has sieve access by default
 $MAILBOX_DEFAULT_ATTRIBUTES['sieve_access'] = true;
 
+// Mailbox has ActiveSync/EAS access by default
+$MAILBOX_DEFAULT_ATTRIBUTES['eas_access'] = true;
+
+// Mailbox has CalDAV/CardDAV (DAV) access by default
+$MAILBOX_DEFAULT_ATTRIBUTES['dav_access'] = true;
+
 // Mailbox receives notifications about...
 // "add_header" - mail that was put into the Junk folder
 // "reject" - mail that was rejected
diff --git a/data/web/js/site/mailbox.js b/data/web/js/site/mailbox.js
index df61f872..7010077d 100644
--- a/data/web/js/site/mailbox.js
+++ b/data/web/js/site/mailbox.js
@@ -352,6 +352,12 @@ $(document).ready(function() {
     if (template.sieve_access == 1){
       protocol_access.push("sieve");
     }
+    if (template.eas_access == 1){
+      protocol_access.push("eas");
+    }
+    if (template.dav_access == 1){
+      protocol_access.push("dav");
+    }
     $('#protocol_access').selectpicker('val', protocol_access);
 
     var acl = [];
@@ -933,6 +939,8 @@ jQuery(function($){
             item.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
             item.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
             item.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
+            item.eas_access = '<i class="text-' + (item.attributes.eas_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.eas_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
+            item.dav_access = '<i class="text-' + (item.attributes.dav_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.dav_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
             if (item.attributes.quarantine_notification === 'never') {
               item.quarantine_notification = lang.never;
             } else if (item.attributes.quarantine_notification === 'hourly') {
@@ -1096,6 +1104,18 @@ jQuery(function($){
           defaultContent: '',
           className: 'none'
         },
+        {
+          title: 'EAS',
+          data: 'eas_access',
+          defaultContent: '',
+          className: 'none'
+        },
+        {
+          title: 'DAV',
+          data: 'dav_access',
+          defaultContent: '',
+          className: 'none'
+        },
         {
           title: lang.quarantine_notification,
           data: 'quarantine_notification',
@@ -1209,6 +1229,8 @@ jQuery(function($){
             item.attributes.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.imap_access == 1 ? '1' : '0') + '</span></i>';
             item.attributes.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.smtp_access == 1 ? '1' : '0') + '</span></i>';
             item.attributes.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sieve_access == 1 ? '1' : '0') + '</span></i>';
+            item.attributes.eas_access = '<i class="text-' + (item.attributes.eas_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.eas_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.eas_access == 1 ? '1' : '0') + '</span></i>';
+            item.attributes.dav_access = '<i class="text-' + (item.attributes.dav_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.dav_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.dav_access == 1 ? '1' : '0') + '</span></i>';
             item.attributes.sogo_access = '<i class="text-' + (item.attributes.sogo_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sogo_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sogo_access == 1 ? '1' : '0') + '</span></i>';
             if (item.attributes.quarantine_notification === 'never') {
               item.attributes.quarantine_notification = lang.never;
@@ -1317,6 +1339,16 @@ jQuery(function($){
           data: 'attributes.sieve_access',
           defaultContent: '',
         },
+        {
+          title: 'EAS',
+          data: 'attributes.eas_access',
+          defaultContent: '',
+        },
+        {
+          title: 'DAV',
+          data: 'attributes.dav_access',
+          defaultContent: '',
+        },
         {
           title: 'SOGO',
           data: 'attributes.sogo_access',
diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php
index 962627ba..2da28d4d 100644
--- a/data/web/sogo-auth.php
+++ b/data/web/sogo-auth.php
@@ -12,18 +12,21 @@ $session_var_pass = 'sogo-sso-pass';
 if (isset($_SERVER['PHP_AUTH_USER'])) {
   // load prerequisites only when required
   require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
+
   $username = $_SERVER['PHP_AUTH_USER'];
   $password = $_SERVER['PHP_AUTH_PW'];
-  $is_eas = false;
-  $is_dav = false;
+
+  // Determine service type for protocol access check
+  $service = 'NONE';
   $original_uri = isset($_SERVER['HTTP_X_ORIGINAL_URI']) ? $_SERVER['HTTP_X_ORIGINAL_URI'] : '';
   if (preg_match('/^(\/SOGo|)\/dav.*/', $original_uri) === 1) {
-    $is_dav = true;
+    $service = 'DAV';
   }
   elseif (preg_match('/^(\/SOGo|)\/Microsoft-Server-ActiveSync.*/', $original_uri) === 1) {
-    $is_eas = true;
+    $service = 'EAS';
   }
-  $login_check = check_login($username, $password, array('dav' => $is_dav, 'eas' => $is_eas));
+
+  $login_check = check_login($username, $password, array('service' => $service));
   if ($login_check === 'user') {
     header("X-User: $username");
     header("X-Auth: Basic ".base64_encode("$username:$password"));
@@ -57,7 +60,6 @@ elseif (isset($_GET['login'])) {
           $_SESSION['mailcow_cc_role']        = "user";
         }
         // update sasl logs
-        $service = ($app_passwd_data['eas'] === true) ? 'EAS' : 'DAV';
         $stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES ('SSO', 0, :username, :remote_addr)");
         $stmt->execute(array(
           ':username' => $login,
diff --git a/data/web/templates/edit/mailbox-templates.twig b/data/web/templates/edit/mailbox-templates.twig
index 65a83cd2..ddc13958 100644
--- a/data/web/templates/edit/mailbox-templates.twig
+++ b/data/web/templates/edit/mailbox-templates.twig
@@ -108,6 +108,8 @@
           <option value="pop3"{% if template.attributes.pop3_access == '1' %} selected{% endif %}>POP3</option>
           <option value="smtp"{% if template.attributes.smtp_access == '1' %} selected{% endif %}>SMTP</option>
           <option value="sieve"{% if template.attributes.sieve_access == '1' %} selected{% endif %}>Sieve</option>
+          <option value="eas"{% if template.attributes.eas_access == '1' %} selected{% endif %}>ActiveSync</option>
+          <option value="dav"{% if template.attributes.dav_access == '1' %} selected{% endif %}>CalDAV/CardDAV</option>
         </select>
       </div>
     </div>
diff --git a/data/web/templates/edit/mailbox.twig b/data/web/templates/edit/mailbox.twig
index a223a750..294957ee 100644
--- a/data/web/templates/edit/mailbox.twig
+++ b/data/web/templates/edit/mailbox.twig
@@ -281,6 +281,8 @@
                         <option value="pop3"{% if result.attributes.pop3_access == '1' %} selected{% endif %}>POP3</option>
                         <option value="smtp"{% if result.attributes.smtp_access == '1' %} selected{% endif %}>SMTP</option>
                         <option value="sieve"{% if result.attributes.sieve_access == '1' %} selected{% endif %}>Sieve</option>
+                        <option value="eas"{% if result.attributes.eas_access == '1' %} selected{% endif %}>ActiveSync</option>
+                        <option value="dav"{% if result.attributes.dav_access == '1' %} selected{% endif %}>CalDAV/CardDAV</option>
                       </select>
                     </div>
                   </div>
diff --git a/data/web/templates/modals/mailbox.twig b/data/web/templates/modals/mailbox.twig
index 1e8ee53f..b35c0caa 100644
--- a/data/web/templates/modals/mailbox.twig
+++ b/data/web/templates/modals/mailbox.twig
@@ -148,6 +148,8 @@
                 <option value="pop3">POP3</option>
                 <option value="smtp">SMTP</option>
                 <option value="sieve">Sieve</option>
+                <option value="eas">ActiveSync</option>
+                <option value="dav">CalDAV/CardDAV</option>
               </select>
             </div>
           </div>
@@ -335,6 +337,8 @@
                 <option value="pop3" selected>POP3</option>
                 <option value="smtp" selected>SMTP</option>
                 <option value="sieve" selected>Sieve</option>
+                <option value="activesync" selected>ActiveSync</option>
+                <option value="dav" selected>CalDAV/CardDAV</option>
               </select>
             </div>
           </div>
diff --git a/data/web/templates/user/tab-user-auth.twig b/data/web/templates/user/tab-user-auth.twig
index bace3348..171545d6 100644
--- a/data/web/templates/user/tab-user-auth.twig
+++ b/data/web/templates/user/tab-user-auth.twig
@@ -55,6 +55,8 @@
               {% if mailboxdata.attributes.smtp_access == 1 %}<div class="badge fs-6 bg-success m-2">SMTP <i class="bi bi-check-lg"></i></div>{% else %}<div class="badge fs-6 bg-danger m-2">SMTP <i class="bi bi-x-lg"></i></div>{% endif %}
               {% if mailboxdata.attributes.sieve_access == 1 %}<div class="badge fs-6 bg-success m-2">Sieve <i class="bi bi-check-lg"></i></div>{% else %}<div class="badge fs-6 bg-danger m-2">Sieve <i class="bi bi-x-lg"></i></div>{% endif %}
               {% if mailboxdata.attributes.pop3_access == 1 %}<div class="badge fs-6 bg-success m-2">POP3 <i class="bi bi-check-lg"></i></div>{% else %}<div class="badge fs-6 bg-danger m-2">POP3 <i class="bi bi-x-lg"></i></div>{% endif %}
+              {% if mailboxdata.attributes.eas_access == 1 %}<div class="badge fs-6 bg-success m-2">ActiveSync <i class="bi bi-check-lg"></i></div>{% else %}<div class="badge fs-6 bg-danger m-2">ActiveSync <i class="bi bi-x-lg"></i></div>{% endif %}
+              {% if mailboxdata.attributes.dav_access == 1 %}<div class="badge fs-6 bg-success m-2">CalDAV/CardDAV <i class="bi bi-check-lg"></i></div>{% else %}<div class="badge fs-6 bg-danger m-2">CalDAV/CardDAV <i class="bi bi-x-lg"></i></div>{% endif %}
               </div>
             </div>
           </div>