From 418d201da066179a3d20f1dfb078bf9410e0c1be Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 18 Jan 2026 00:47:38 -0300 Subject: [PATCH] mailu: gate sync to approved users --- scripts/tests/test_mailu_sync.py | 20 ++++++++++++++----- .../finance/scripts/firefly_user_sync.php | 7 +++++++ services/keycloak/realm-settings-job.yaml | 8 ++++++++ services/mailu/scripts/mailu_sync.py | 12 +++++++++++ 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/scripts/tests/test_mailu_sync.py b/scripts/tests/test_mailu_sync.py index 49bd2e4..c12bc8d 100644 --- a/scripts/tests/test_mailu_sync.py +++ b/scripts/tests/test_mailu_sync.py @@ -144,8 +144,18 @@ def test_main_generates_password_and_upserts(monkeypatch): sync = load_sync_module(monkeypatch) monkeypatch.setattr(sync.bcrypt_sha256, "hash", lambda password: f"hash:{password}") users = [ - {"id": "u1", "username": "user1", "email": "user1@example.com", "attributes": {}}, - {"id": "u2", "username": "user2", "email": "user2@example.com", "attributes": {"mailu_app_password": ["keepme"]}}, + { + "id": "u1", + "username": "user1", + "email": "user1@example.com", + "attributes": {"mailu_enabled": ["true"]}, + }, + { + "id": "u2", + "username": "user2", + "email": "user2@example.com", + "attributes": {"mailu_app_password": ["keepme"], "mailu_enabled": ["true"]}, + }, {"id": "u3", "username": "user3", "email": "user3@other.com", "attributes": {}}, ] updated = [] @@ -185,6 +195,6 @@ def test_main_generates_password_and_upserts(monkeypatch): sync.main() - # Always backfill mailu_email, even if Keycloak recovery email is external. - assert len(updated) == 3 - assert conns and len(conns[0]._cursor.executions) == 3 + # Only mail-enabled users are synced and backfilled. + assert len(updated) == 2 + assert conns and len(conns[0]._cursor.executions) == 2 diff --git a/services/finance/scripts/firefly_user_sync.php b/services/finance/scripts/firefly_user_sync.php index dcb78ea..4036c3d 100644 --- a/services/finance/scripts/firefly_user_sync.php +++ b/services/finance/scripts/firefly_user_sync.php @@ -6,6 +6,7 @@ declare(strict_types=1); use FireflyIII\Console\Commands\Correction\CreatesGroupMemberships; use FireflyIII\Models\Role; use FireflyIII\Repositories\User\UserRepositoryInterface; +use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\User; use Illuminate\Contracts\Console\Kernel as ConsoleKernel; @@ -70,6 +71,12 @@ $app = require $app_bootstrap; $kernel = $app->make(ConsoleKernel::class); $kernel->bootstrap(); +try { + FireflyConfig::set('single_user_mode', true); +} catch (Throwable $exc) { + error_line('failed to enforce single_user_mode: '.$exc->getMessage()); +} + $repository = $app->make(UserRepositoryInterface::class); $existing_user = User::where('email', $email)->first(); diff --git a/services/keycloak/realm-settings-job.yaml b/services/keycloak/realm-settings-job.yaml index 483bc0c..4259876 100644 --- a/services/keycloak/realm-settings-job.yaml +++ b/services/keycloak/realm-settings-job.yaml @@ -220,6 +220,14 @@ spec: "permissions": {"view": ["admin"], "edit": ["admin"]}, "validations": {"length": {"max": 255}}, }, + { + "name": "mailu_enabled", + "displayName": "Atlas Mailbox Enabled", + "multivalued": False, + "annotations": {"group": "user-metadata"}, + "permissions": {"view": ["admin"], "edit": ["admin"]}, + "validations": {"length": {"max": 16}}, + }, { "name": "nextcloud_mail_primary_email", "displayName": "Nextcloud Mail Primary Email", diff --git a/services/mailu/scripts/mailu_sync.py b/services/mailu/scripts/mailu_sync.py index d1754cb..8f2071a 100644 --- a/services/mailu/scripts/mailu_sync.py +++ b/services/mailu/scripts/mailu_sync.py @@ -25,6 +25,7 @@ KC_CLIENT_SECRET = os.environ["KEYCLOAK_CLIENT_SECRET"] MAILU_DOMAIN = os.environ["MAILU_DOMAIN"] MAILU_DEFAULT_QUOTA = int(os.environ.get("MAILU_DEFAULT_QUOTA", "20000000000")) +MAILU_ENABLED_ATTR = os.environ.get("MAILU_ENABLED_ATTR", "mailu_enabled") DB_CONFIG = { "host": os.environ["MAILU_DB_HOST"], @@ -141,6 +142,13 @@ def get_attribute_value(attributes, key): return None +def mailu_enabled(attributes) -> bool: + raw = get_attribute_value(attributes, MAILU_ENABLED_ATTR) + if raw is None: + return False + return str(raw).strip().lower() in {"1", "true", "yes", "y", "on"} + + def resolve_mailu_email(user, attributes): explicit = get_attribute_value(attributes, "mailu_email") if explicit: @@ -209,6 +217,10 @@ def main(): for user in users: attrs = user.get("attributes", {}) or {} + if user.get("enabled") is False: + continue + if not mailu_enabled(attrs): + continue app_pw = get_attribute_value(attrs, "mailu_app_password") mailu_email = resolve_mailu_email(user, attrs)