Compare commits

...
81 Commits
Author SHA1 Message Date
FreddleSpl0it 849e2cb25d Merge pull request #7597 from mailcow/staging
Automatic PR to nightly from 2026-11-05T10:31:25Z
2026-11-05 13:44:38 +01:00
FreddleSpl0it 329bca1d46 Merge pull request #7596 from mailcow/fix/7595
[Web] Mailbox protocols do not limit app passwords
2026-11-05 13:19:59 +01:00
FreddleSpl0it 6a83b9e0f0 [Web] Mailbox protocols do not limit app passwords 2026-11-05 12:46:22 +01:00
FreddleSpl0it 2eb2aebc53 Merge pull request #7592 from mailcow/staging
Automatic PR to nightly from 2026-11-04T16:51:26Z
2026-11-04 22:30:56 +01:00
FreddleSpl0it b8af86299b Merge pull request #7591 from mailcow/staging
Automatic PR to nightly from 2026-11-04T16:14:38Z
2026-11-04 17:41:40 +01:00
FreddleSpl0it 9ec6bca1f8 Merge pull request #7585 from mailcow/staging
Automatic PR to nightly from 2026-11-01T19:34:33Z
2026-11-04 17:07:28 +01:00
FreddleSpl0it 2e200bd887 Merge staging into nightly 2026-10-28 13:17:02 +01:00
FreddleSpl0it e70af42aef Merge pull request #7564 from mailcow/staging
Automatic PR to nightly from 2026-10-28T05:05:56Z
2026-10-28 09:06:14 +01:00
FreddleSpl0it fe414e5341 [PHP][SOGo][Dovecot][Netfilter][Dockerapi] Update Images 2026-10-27 14:19:53 +01:00
FreddleSpl0it b95227d648 Merge pull request #7561 from mailcow/feat/netfilter-rework
[Netfilter] Rework: robust bans, no restart loops, identity provider outages are no failed logins
2026-10-27 14:16:51 +01:00
FreddleSpl0it 0c9335f868 Merge pull request #7560 from mailcow/feat/groups
[Web][Dovecot][SOGo][DockerApi] Add mailbox groups, shared mailboxes and identity provider sync
2026-10-27 14:13:32 +01:00
FreddleSpl0it a61851410e Merge pull request #7559 from mailcow/feat/apikey-rework
[Web] Rework API keys: multiple read-only/read-write keys, service keys from mailcow.conf
2026-10-27 14:09:37 +01:00
FreddleSpl0it 0bc0ef6424 Merge pull request #7558 from mailcow/feat/imapsync-rework
[Web][Dovecot] Rework imapsync: sync sources, XOAUTH2, Python runner
2026-10-27 14:03:31 +01:00
FreddleSpl0itandClaude Opus 5.5 d3e0c1cbb2 Merge staging into nightly
nightly has the content of staging again, nightly-only changes are dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:25:39 +01:00
FreddleSpl0it c0be3347f8 Merge pull request #7026 from mailcow/staging
Automatic PR to nightly from 2026-01-29T09:19:39Z
2026-01-29 10:33:28 +01:00
FreddleSpl0it 4f08c4ed7d Merge remote-tracking branch 'origin/staging' into nightly 2026-01-29 07:58:15 +01:00
DerLinkman 0e76396f01 reuse nightly images where needed 2026-01-20 08:46:04 +01:00
DerLinkman 9bbac9f171 Merge branch 'staging' into nightly 2026-01-19 12:17:11 +01:00
FreddleSpl0it e6f83853ae Merge remote-tracking branch 'origin/staging' into nightly 2025-10-15 11:17:07 +02:00
FreddleSpl0it 7da088c931 Merge branch 'staging' into nightly 2025-10-06 13:58:40 +02:00
FreddleSpl0it bb3c2fb4fe Merge pull request #6731 from mailcow/staging
Automatic PR to nightly from 2025-09-11T07:38:50Z
2025-09-12 11:43:02 +02:00
FreddleSpl0it eb84847a5b Merge branch 'staging' into nightly 2025-09-11 10:26:42 +02:00
DerLinkman 0cfcde673c Merge branch 'staging' into nightly 2025-08-28 10:21:38 +02:00
FreddleSpl0it ed5be5d7dc Merge branch 'feat/mailcow-adm' into nightly 2025-08-19 11:57:22 +02:00
FreddleSpl0it ac90ecaf4f Merge remote-tracking branch 'origin/staging' into nightly 2025-08-19 11:51:54 +02:00
FreddleSpl0it fed3fc9514 [Controller] Add HTTPS_PORT env var to base_url 2025-08-19 11:30:20 +02:00
FreddleSpl0it 35b9940db4 [Controller] Fix function description in SyncjobModel 2025-08-19 11:28:54 +02:00
FreddleSpl0it ece940b000 [Controller] Fix missing password2 assignment in from_dict 2025-08-19 11:28:08 +02:00
DerLinkman 4b5fd0b50a compose: bump nginx nightly 2025-08-05 16:42:24 +02:00
DerLinkman 5aa9498f65 Merge branch 'feat/remove-ip6nat' into nightly 2025-08-05 16:41:15 +02:00
DerLinkman 690d511e54 reuse DOCKER_MAJOR Variable in ip6_controller 2025-08-05 16:37:09 +02:00
DerLinkmanandCopilot e2a2b42139 Update _modules/scripts/new_options.sh
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-05 16:36:29 +02:00
DerLinkmanandCopilot 4bbda8006d Update _modules/scripts/new_options.sh
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-05 16:36:22 +02:00
DerLinkman a281746958 ip6_controller: moved docker major detection upwards 2025-08-05 16:25:59 +02:00
DerLinkman cec51b6162 improve detection of ENABLE_IPV6 2025-08-05 16:22:51 +02:00
DerLinkman 107c5d2e7d improve ENABLE_IPV6 check in nginx bootstrap 2025-08-05 16:18:29 +02:00
DerLinkmanandCopilot 00c025f31a Update _modules/scripts/core.sh
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-05 16:12:11 +02:00
DerLinkmanandCopilot 9b6388d0d0 Update _modules/scripts/new_options.sh
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-05 16:11:39 +02:00
DerLinkman 2f25fcad77 removed unnecessary message on every call of function 2025-08-05 16:04:10 +02:00
DerLinkman 7067e2c714 move detect_major_update func to core submodule 2025-08-05 16:04:10 +02:00
DerLinkman 9f3cdfa713 adapted removal of ACME_CONTACT for nightly 2025-08-05 16:03:26 +02:00
DerLinkman 529acf5ff6 added error handling for blank daemon.json 2025-08-05 16:03:26 +02:00
DerLinkman 0371edcf5e reintegrated module loading (update.sh) 2025-08-05 16:03:25 +02:00
DerLinkman d20254d4ee improved _modules handling while running 2025-08-05 16:03:25 +02:00
DerLinkman befecfc31d fixed docker version check for daemon 2025-08-05 16:03:24 +02:00
DerLinkman 004fcf092b added jq as dependancy 2025-08-05 16:03:24 +02:00
DerLinkman a487fcd0bd fix broken EXIT_CODE var handling 2025-08-05 16:03:23 +02:00
DerLinkman 17e38a05f0 fixed/added comments for modules 2025-08-05 16:03:23 +02:00
DerLinkman c503abfe40 fixed missing fi in update.sh 2025-08-05 16:03:22 +02:00
DerLinkman 73929db796 rewrite to scripts after testing (improved error handling) 2025-08-05 16:03:22 +02:00
DerLinkman fb0685fa71 initial commit for script overhauls 2025-08-05 16:03:21 +02:00
DerLinkman df36670c7c nginx: renamed DISABLE_IPv6 to ENABLE_IPV6 to align 2025-08-05 16:02:41 +02:00
DerLinkman 3f9215678d ipv6: added ipv6 detection + removed ip6 nat container 2025-08-05 16:02:41 +02:00
FreddleSpl0it 0ac0e5c252 [DockerApi] Rename DockerApi to Controller and add mailcow-adm tool 2025-08-01 15:31:50 +02:00
DerLinkman af61c82077 adapted removal of ACME_CONTACT for nightly 2025-07-16 09:03:50 +02:00
DerLinkman c066273c79 Merge branch staging into nightly 2025-07-16 09:03:20 +02:00
DerLinkman 0c3e53e3a9 Merge branch 'feat/remove-ip6nat' into nightly 2025-05-27 16:32:08 +02:00
DerLinkman 5ca10d1cde added error handling for blank daemon.json 2025-05-27 16:31:22 +02:00
DerLinkman 7907d43af7 compose: bumped nginx container tag 2025-05-27 16:24:21 +02:00
DerLinkman d198f1d3f8 Merge branch 'feat/remove-ip6nat' into nightly 2025-05-27 16:19:42 +02:00
DerLinkman 102226723e reintegrated module loading (update.sh) 2025-05-27 16:18:55 +02:00
DerLinkman 2efaccf038 improved _modules handling while running 2025-05-27 16:16:23 +02:00
DerLinkman aa7b6fa4a9 improved _modules handling while running 2025-05-27 16:15:02 +02:00
DerLinkman 714727a129 Merge pull request #6561 from mailcow/feat/remove-ip6nat
core: rewrote ipv6 detection and core script splitting
2025-05-27 16:10:44 +02:00
DerLinkman 4e5e264e3e Merge branch 'staging' into nightly 2025-05-27 16:09:18 +02:00
DerLinkman 267c81b42e fixed docker version check for daemon 2025-05-27 15:55:58 +02:00
DerLinkman f2f3fbe497 added jq as dependancy 2025-05-27 15:54:58 +02:00
DerLinkman 6ba650820f fix broken EXIT_CODE var handling 2025-05-27 15:31:52 +02:00
DerLinkman baa6286471 fixed/added comments for modules 2025-05-27 15:30:56 +02:00
DerLinkman be8537d165 fixed missing fi in update.sh 2025-05-27 15:29:48 +02:00
DerLinkman 737fced7be rewrite to scripts after testing (improved error handling) 2025-05-27 15:26:08 +02:00
DerLinkman 5a532df8ce initial commit for script overhauls 2025-05-26 17:09:58 +02:00
DerLinkman f8ce7a71e6 nginx: renamed DISABLE_IPv6 to ENABLE_IPV6 to align 2025-05-26 17:09:37 +02:00
DerLinkman 2e876bda9a ipv6: added ipv6 detection + removed ip6 nat container 2025-05-26 15:19:42 +02:00
FreddleSpl0it d2e5926cce Merge pull request #6536 from mailcow/staging
Automatic PR to nightly from 2025-05-13T07:58:39Z
2025-05-13 11:22:48 +02:00
FreddleSpl0it e3b576be67 Merge pull request #6475 from mailcow/staging
Automatic PR to nightly from 2025-04-09T01:26:20Z
2025-05-13 09:52:49 +02:00
FreddleSpl0it 0f7e359686 Merge pull request #6467 from mailcow/staging
Automatic PR to nightly from 2025-04-07T05:55:15Z
2025-04-07 09:00:11 +02:00
FreddleSpl0it b9a0b2db6d Merge pull request #6456 from mailcow/staging
Automatic PR to nightly from 2025-04-03T11:26:19Z
2025-04-03 14:21:26 +02:00
FreddleSpl0it 93b876c473 Merge pull request #6446 from mailcow/staging
Automatic PR to nightly from 2025-04-01T13:48:33Z
2025-04-03 14:02:21 +02:00
FreddleSpl0it 92c2aa2023 Merge pull request #6420 from mailcow/staging
Automatic PR to nightly from 2025-03-27T05:23:47Z
2025-03-27 08:38:49 +01:00
FreddleSpl0it 9351cf24fe Merge pull request #6386 from mailcow/staging
Automatic PR to nightly from 2025-03-24T08:36:34Z
2025-03-27 07:45:25 +01:00
3 changed files with 126 additions and 2 deletions

No files matched your search

+2 -2
View File
@@ -500,10 +500,10 @@ function apppass_login($user, $pass, $extra = null, &$denied = false){
$key = strtolower($extra['service']) . '_access'; $key = strtolower($extra['service']) . '_access';
$mailbox_attributes = json_decode($row['mailbox_attributes'], true); $mailbox_attributes = json_decode($row['mailbox_attributes'], true);
// app passwords never grant a protocol the mailbox itself is denied // the protocols of the mailbox only limit direct user access, app passwords have their own
if ($row['active'] != '1' || $row['mailbox_active'] != '1' || $row['domain_active'] != '1' if ($row['active'] != '1' || $row['mailbox_active'] != '1' || $row['domain_active'] != '1'
|| ($mailbox_attributes['shared'] ?? '0') == '1' || ($mailbox_attributes['shared'] ?? '0') == '1'
|| ($extra['service'] != 'NONE' && ($row[$key] != '1' || (isset($mailbox_attributes[$key]) && $mailbox_attributes[$key] != '1')))) { || ($extra['service'] != 'NONE' && $row[$key] != '1')) {
// right password, but the app password does not allow this login // right password, but the app password does not allow this login
$denied = true; $denied = true;
continue; continue;
+113
View File
@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Auth;
use PDO;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../Support/legacy_auth_functions.php';
require_once __DIR__ . '/../../../data/web/inc/functions.auth.inc.php';
// Legacy apppass_login() with its own query, on an in-memory SQLite database
class AppPasswordLoginTest extends TestCase
{
private const PROTOCOLS = ['imap_access', 'smtp_access', 'pop3_access', 'sieve_access', 'eas_access', 'dav_access'];
private PDO $pdo;
protected function setUp(): void
{
$this->pdo = new PDO('sqlite::memory:');
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pdo->sqliteCreateFunction('regexp', fn ($pattern, $value) => preg_match('/' . $pattern . '/', (string)$value) === 1, 2);
$this->pdo->exec("CREATE TABLE `domain` (`domain` TEXT PRIMARY KEY, `active` INTEGER NOT NULL DEFAULT 1)");
$this->pdo->exec("CREATE TABLE `mailbox` (`username` TEXT PRIMARY KEY, `domain` TEXT, `kind` TEXT NOT NULL DEFAULT '',
`active` INTEGER NOT NULL DEFAULT 1, `attributes` TEXT)");
$this->pdo->exec("CREATE TABLE `app_passwd` (`id` INTEGER PRIMARY KEY, `name` TEXT, `mailbox` TEXT, `password` TEXT,
`active` INTEGER NOT NULL DEFAULT 1, " . implode(', ', array_map(fn ($p) => "`$p` INTEGER NOT NULL DEFAULT 0", self::PROTOCOLS)) . ")");
$GLOBALS['pdo'] = $this->pdo;
$_SESSION = [];
}
protected function tearDown(): void
{
unset($GLOBALS['pdo']);
$_SESSION = [];
}
public static function logins(): array
{
$off = array_fill_keys(self::PROTOCOLS, '0');
return [
// [mailbox, domain active, app password, service, password, result, denied]
'imap' => [[], 1, ['imap_access' => 1], 'imap', 'app-secret', 'user', false],
'imap, mailbox imap off' => [['attributes' => $off], 1, ['imap_access' => 1, 'smtp_access' => 1], 'imap', 'app-secret', 'user', false],
'smtp, mailbox smtp off' => [['attributes' => $off], 1, ['imap_access' => 1, 'smtp_access' => 1], 'smtp', 'app-secret', 'user', false],
'eas, mailbox eas off' => [['attributes' => $off], 1, ['eas_access' => 1], 'EAS', 'app-secret', 'user', false],
'dav, mailbox dav off' => [['attributes' => $off], 1, ['dav_access' => 1], 'DAV', 'app-secret', 'user', false],
// denied: right password, login not allowed
'protocol not in app password' => [[], 1, ['imap_access' => 1, 'smtp_access' => 1], 'pop3', 'app-secret', false, true],
'app password inactive' => [[], 1, ['imap_access' => 1, 'active' => 0], 'imap', 'app-secret', false, true],
'mailbox inactive' => [['active' => 0], 1, ['imap_access' => 1], 'imap', 'app-secret', false, true],
'mailbox login disabled' => [['active' => 2], 1, ['imap_access' => 1], 'imap', 'app-secret', false, true],
'domain inactive' => [[], 0, ['imap_access' => 1], 'imap', 'app-secret', false, true],
'shared mailbox' => [['attributes' => ['shared' => '1']], 1, ['imap_access' => 1], 'imap', 'app-secret', false, true],
// failed: counted by netfilter
'wrong password' => [[], 1, ['imap_access' => 1], 'imap', 'wrong', false, false],
'resource mailbox' => [['kind' => 'location'], 1, ['imap_access' => 1], 'imap', 'app-secret', false, false],
];
}
#[DataProvider('logins')]
public function testLogin(array $mailbox, int $domainActive, array $appPasswd, string $service, string $password, string|false $result, bool $denied): void
{
$this->addMailbox($mailbox, $domainActive);
$id = $this->addAppPasswd($appPasswd);
$isDenied = false;
$this->assertSame($result, apppass_login('user@example.org', $password, ['service' => $service, 'is_internal' => true], $isDenied));
$this->assertSame($denied, $isDenied);
if ($result !== false) {
$this->assertEquals($id, $_SESSION['app_passwd_id']);
}
}
public function testUiLoginWithoutServiceIsRefused(): void
{
$this->addMailbox([], 1);
$this->addAppPasswd(array_fill_keys(self::PROTOCOLS, 1));
$this->assertFalse(apppass_login('user@example.org', 'app-secret', ['is_internal' => false]));
}
public function testOtherAppPasswordOfTheMailboxIsTried(): void
{
$this->addMailbox(['attributes' => array_fill_keys(self::PROTOCOLS, '0')], 1);
$this->addAppPasswd(['eas_access' => 1], 'other-secret');
$id = $this->addAppPasswd(['imap_access' => 1]);
$this->assertSame('user', apppass_login('user@example.org', 'app-secret', ['service' => 'imap', 'is_internal' => true]));
$this->assertEquals($id, $_SESSION['app_passwd_id']);
}
private function addMailbox(array $mailbox, int $domainActive): void
{
$attributes = array_merge(array_fill_keys(self::PROTOCOLS, '1'), ['shared' => '0'], $mailbox['attributes'] ?? []);
$this->pdo->prepare("INSERT INTO `domain` (`domain`, `active`) VALUES ('example.org', ?)")->execute([$domainActive]);
$this->pdo->prepare("INSERT INTO `mailbox` (`username`, `domain`, `kind`, `active`, `attributes`) VALUES ('user@example.org', 'example.org', ?, ?, ?)")
->execute([$mailbox['kind'] ?? '', $mailbox['active'] ?? 1, json_encode($attributes)]);
}
private function addAppPasswd(array $appPasswd, string $password = 'app-secret'): int
{
$row = array_merge(array_fill_keys(self::PROTOCOLS, 0), ['active' => 1], $appPasswd);
$hash = '{BLF-CRYPT}' . password_hash($password, PASSWORD_BCRYPT, ['cost' => 4]);
$this->pdo->prepare("INSERT INTO `app_passwd` (`name`, `mailbox`, `password`, " . implode(', ', array_map(fn ($c) => "`$c`", array_keys($row))) . ")
VALUES ('test', 'user@example.org', ?, " . implode(', ', array_fill(0, count($row), '?')) . ")")
->execute(array_merge([$hash], array_values($row)));
return (int)$this->pdo->lastInsertId();
}
}
@@ -0,0 +1,11 @@
<?php
// Stand-in for verify_hash() of data/web/inc/functions.inc.php, used by AppPasswordLoginTest only:
// app passwords are stored as {BLF-CRYPT} (bcrypt), the default scheme of hash_password().
if (!function_exists('verify_hash')) {
function verify_hash($hash, $password)
{
return str_starts_with($hash, '{BLF-CRYPT}') && password_verify($password, substr($hash, strlen('{BLF-CRYPT}')));
}
}