From 4b7ebab8f32004a7a3e6329dccfb03cd6e7ff718 Mon Sep 17 00:00:00 2001 From: FreddleSpl0it <75116288+FreddleSpl0it@users.noreply.github.com> Date: Mon, 2 Nov 2026 09:56:24 +0100 Subject: [PATCH] [Web] Mask IdP secrets for read-only API keys, deny sync job OAuth to read-only and pending sessions --- data/web/json_api.php | 9 ++++++++- data/web/syncjob-oauth.php | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/data/web/json_api.php b/data/web/json_api.php index 3a6ad2659..b6e58c47d 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -1549,7 +1549,14 @@ if (isset($_GET['query'])) { if ($object == "sync-status") { process_get_return(\Mailcow\Identity\IdentityModule::fromGlobals()->api->getStatus()); } elseif($_SESSION['mailcow_cc_role'] === 'admin') { - process_get_return($iam_settings); + $iam_settings_out = $iam_settings; + // Read-only API keys never get the secrets of the identity provider + if (($_SESSION['mailcow_cc_api_access'] ?? '') == 'ro') { + foreach (array('client_secret', 'bindpass', 'access_token') as $iam_secret) { + if (isset($iam_settings_out[$iam_secret])) $iam_settings_out[$iam_secret] = '*'; + } + } + process_get_return($iam_settings_out); } else { process_get_return(null); } diff --git a/data/web/syncjob-oauth.php b/data/web/syncjob-oauth.php index 1046dc97a..44c60370c 100644 --- a/data/web/syncjob-oauth.php +++ b/data/web/syncjob-oauth.php @@ -35,7 +35,9 @@ if (empty($_SESSION['mailcow_cc_username']) || empty($_SESSION['mailcow_cc_role' } $context = \Mailcow\Core\Context::fromSession($_SESSION); $imapsync = \Mailcow\Imapsync\ImapsyncModule::fromGlobals($context); -if (!$context->hasAcl('syncjobs')) { +// Storing a token is a write: no read-only API keys, no session with pending 2FA setup or password update +if (!$context->hasAcl('syncjobs') || $context->isReadOnlyApi() + || !empty($_SESSION['pending_tfa_setup']) || !empty($_SESSION['pending_pw_update'])) { syncjob_oauth_finish(false, array('error' => 'access_denied')); }