Compare commits
No commits in common. "4c4c0867a74ba4cc77910b4a0b499f7e84c38556" and "f753f114c75c36ebcbd98a00f012d4578bc1517c" have entirely different histories.
4c4c0867a7
...
f753f114c7
@ -55,11 +55,11 @@ class _FakeResponse:
|
|||||||
|
|
||||||
|
|
||||||
class _FakeSession:
|
class _FakeSession:
|
||||||
def __init__(self, put_resp, get_resps):
|
def __init__(self, put_resp, get_resp):
|
||||||
self.put_resp = put_resp
|
self.put_resp = put_resp
|
||||||
self.get_resps = list(get_resps)
|
self.get_resp = get_resp
|
||||||
self.put_called = False
|
self.put_called = False
|
||||||
self.get_calls = 0
|
self.get_called = False
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
return _FakeResponse({"access_token": "dummy"})
|
return _FakeResponse({"access_token": "dummy"})
|
||||||
@ -69,26 +69,22 @@ class _FakeSession:
|
|||||||
return self.put_resp
|
return self.put_resp
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
self.get_calls += 1
|
self.get_called = True
|
||||||
if self.get_resps:
|
return self.get_resp
|
||||||
return self.get_resps.pop(0)
|
|
||||||
return _FakeResponse({})
|
|
||||||
|
|
||||||
|
|
||||||
def test_kc_update_attributes_succeeds(monkeypatch):
|
def test_kc_update_attributes_succeeds(monkeypatch):
|
||||||
sync = load_sync_module(monkeypatch)
|
sync = load_sync_module(monkeypatch)
|
||||||
current_resp = _FakeResponse({"attributes": {}})
|
|
||||||
ok_resp = _FakeResponse({"attributes": {"mailu_app_password": ["abc"]}})
|
ok_resp = _FakeResponse({"attributes": {"mailu_app_password": ["abc"]}})
|
||||||
sync.SESSION = _FakeSession(_FakeResponse({}), [current_resp, ok_resp])
|
sync.SESSION = _FakeSession(_FakeResponse({}), ok_resp)
|
||||||
sync.kc_update_attributes("token", {"id": "u1", "username": "u1"}, {"mailu_app_password": "abc"})
|
sync.kc_update_attributes("token", {"id": "u1", "username": "u1"}, {"mailu_app_password": "abc"})
|
||||||
assert sync.SESSION.put_called and sync.SESSION.get_calls == 2
|
assert sync.SESSION.put_called and sync.SESSION.get_called
|
||||||
|
|
||||||
|
|
||||||
def test_kc_update_attributes_raises_without_attribute(monkeypatch):
|
def test_kc_update_attributes_raises_without_attribute(monkeypatch):
|
||||||
sync = load_sync_module(monkeypatch)
|
sync = load_sync_module(monkeypatch)
|
||||||
current_resp = _FakeResponse({"attributes": {}})
|
|
||||||
missing_attr_resp = _FakeResponse({"attributes": {}}, status=200)
|
missing_attr_resp = _FakeResponse({"attributes": {}}, status=200)
|
||||||
sync.SESSION = _FakeSession(_FakeResponse({}), [current_resp, missing_attr_resp])
|
sync.SESSION = _FakeSession(_FakeResponse({}), missing_attr_resp)
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
sync.kc_update_attributes("token", {"id": "u1", "username": "u1"}, {"mailu_app_password": "abc"})
|
sync.kc_update_attributes("token", {"id": "u1", "username": "u1"}, {"mailu_app_password": "abc"})
|
||||||
|
|
||||||
@ -148,18 +144,8 @@ def test_main_generates_password_and_upserts(monkeypatch):
|
|||||||
sync = load_sync_module(monkeypatch)
|
sync = load_sync_module(monkeypatch)
|
||||||
monkeypatch.setattr(sync.bcrypt_sha256, "hash", lambda password: f"hash:{password}")
|
monkeypatch.setattr(sync.bcrypt_sha256, "hash", lambda password: f"hash:{password}")
|
||||||
users = [
|
users = [
|
||||||
{
|
{"id": "u1", "username": "user1", "email": "user1@example.com", "attributes": {}},
|
||||||
"id": "u1",
|
{"id": "u2", "username": "user2", "email": "user2@example.com", "attributes": {"mailu_app_password": ["keepme"]}},
|
||||||
"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": {}},
|
{"id": "u3", "username": "user3", "email": "user3@other.com", "attributes": {}},
|
||||||
]
|
]
|
||||||
updated = []
|
updated = []
|
||||||
@ -199,6 +185,6 @@ def test_main_generates_password_and_upserts(monkeypatch):
|
|||||||
|
|
||||||
sync.main()
|
sync.main()
|
||||||
|
|
||||||
# Only mail-enabled users are synced and backfilled.
|
# Always backfill mailu_email, even if Keycloak recovery email is external.
|
||||||
assert len(updated) == 2
|
assert len(updated) == 3
|
||||||
assert conns and len(conns[0]._cursor.executions) == 2
|
assert conns and len(conns[0]._cursor.executions) == 3
|
||||||
|
|||||||
@ -33,15 +33,6 @@ spec:
|
|||||||
export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}"
|
export PORTAL_E2E_CLIENT_ID="{{ .Data.data.client_id }}"
|
||||||
export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}"
|
export PORTAL_E2E_CLIENT_SECRET="{{ .Data.data.client_secret }}"
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ with secret "kv/data/atlas/shared/postmark-relay" }}
|
|
||||||
export SMTP_HOST="mail.bstein.dev"
|
|
||||||
export SMTP_PORT="587"
|
|
||||||
export SMTP_STARTTLS="true"
|
|
||||||
export SMTP_USE_TLS="false"
|
|
||||||
export SMTP_USERNAME="{{ index .Data.data "relay-username" }}"
|
|
||||||
export SMTP_PASSWORD="{{ index .Data.data "relay-password" }}"
|
|
||||||
export SMTP_FROM="no-reply-portal@bstein.dev"
|
|
||||||
{{ end }}
|
|
||||||
spec:
|
spec:
|
||||||
automountServiceAccountToken: true
|
automountServiceAccountToken: true
|
||||||
serviceAccountName: bstein-dev-home
|
serviceAccountName: bstein-dev-home
|
||||||
|
|||||||
@ -6,7 +6,6 @@ declare(strict_types=1);
|
|||||||
use FireflyIII\Console\Commands\Correction\CreatesGroupMemberships;
|
use FireflyIII\Console\Commands\Correction\CreatesGroupMemberships;
|
||||||
use FireflyIII\Models\Role;
|
use FireflyIII\Models\Role;
|
||||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||||
use FireflyIII\Support\Facades\FireflyConfig;
|
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Contracts\Console\Kernel as ConsoleKernel;
|
use Illuminate\Contracts\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
@ -71,12 +70,6 @@ $app = require $app_bootstrap;
|
|||||||
$kernel = $app->make(ConsoleKernel::class);
|
$kernel = $app->make(ConsoleKernel::class);
|
||||||
$kernel->bootstrap();
|
$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);
|
$repository = $app->make(UserRepositoryInterface::class);
|
||||||
|
|
||||||
$existing_user = User::where('email', $email)->first();
|
$existing_user = User::where('email', $email)->first();
|
||||||
|
|||||||
@ -220,14 +220,6 @@ spec:
|
|||||||
"permissions": {"view": ["admin"], "edit": ["admin"]},
|
"permissions": {"view": ["admin"], "edit": ["admin"]},
|
||||||
"validations": {"length": {"max": 255}},
|
"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",
|
"name": "nextcloud_mail_primary_email",
|
||||||
"displayName": "Nextcloud Mail Primary Email",
|
"displayName": "Nextcloud Mail Primary Email",
|
||||||
|
|||||||
@ -25,7 +25,6 @@ KC_CLIENT_SECRET = os.environ["KEYCLOAK_CLIENT_SECRET"]
|
|||||||
|
|
||||||
MAILU_DOMAIN = os.environ["MAILU_DOMAIN"]
|
MAILU_DOMAIN = os.environ["MAILU_DOMAIN"]
|
||||||
MAILU_DEFAULT_QUOTA = int(os.environ.get("MAILU_DEFAULT_QUOTA", "20000000000"))
|
MAILU_DEFAULT_QUOTA = int(os.environ.get("MAILU_DEFAULT_QUOTA", "20000000000"))
|
||||||
MAILU_ENABLED_ATTR = os.environ.get("MAILU_ENABLED_ATTR", "mailu_enabled")
|
|
||||||
|
|
||||||
DB_CONFIG = {
|
DB_CONFIG = {
|
||||||
"host": os.environ["MAILU_DB_HOST"],
|
"host": os.environ["MAILU_DB_HOST"],
|
||||||
@ -87,12 +86,7 @@ def kc_get_users(token):
|
|||||||
while True:
|
while True:
|
||||||
resp = SESSION.get(
|
resp = SESSION.get(
|
||||||
f"{KC_BASE}/admin/realms/{KC_REALM}/users",
|
f"{KC_BASE}/admin/realms/{KC_REALM}/users",
|
||||||
params={
|
params={"first": first, "max": max_results, "enabled": "true"},
|
||||||
"first": first,
|
|
||||||
"max": max_results,
|
|
||||||
"enabled": "true",
|
|
||||||
"briefRepresentation": "false",
|
|
||||||
},
|
|
||||||
headers=headers,
|
headers=headers,
|
||||||
timeout=20,
|
timeout=20,
|
||||||
)
|
)
|
||||||
@ -110,20 +104,17 @@ def kc_update_attributes(token, user, attributes):
|
|||||||
"Authorization": f"Bearer {token}",
|
"Authorization": f"Bearer {token}",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
|
payload = {
|
||||||
|
"firstName": user.get("firstName"),
|
||||||
|
"lastName": user.get("lastName"),
|
||||||
|
"email": user.get("email"),
|
||||||
|
"enabled": user.get("enabled", True),
|
||||||
|
"username": user["username"],
|
||||||
|
"emailVerified": user.get("emailVerified", False),
|
||||||
|
"attributes": attributes,
|
||||||
|
}
|
||||||
user_url = f"{KC_BASE}/admin/realms/{KC_REALM}/users/{user['id']}"
|
user_url = f"{KC_BASE}/admin/realms/{KC_REALM}/users/{user['id']}"
|
||||||
current = SESSION.get(
|
resp = SESSION.put(user_url, headers=headers, json=payload, timeout=20)
|
||||||
user_url,
|
|
||||||
headers={"Authorization": f"Bearer {token}"},
|
|
||||||
params={"briefRepresentation": "false"},
|
|
||||||
timeout=15,
|
|
||||||
)
|
|
||||||
current.raise_for_status()
|
|
||||||
current_payload = current.json()
|
|
||||||
current_attrs = current_payload.get("attributes") if isinstance(current_payload, dict) else None
|
|
||||||
if not isinstance(current_attrs, dict):
|
|
||||||
current_attrs = {}
|
|
||||||
current_attrs.update(attributes)
|
|
||||||
resp = SESSION.put(user_url, headers=headers, json={"attributes": current_attrs}, timeout=20)
|
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
verify = SESSION.get(
|
verify = SESSION.get(
|
||||||
user_url,
|
user_url,
|
||||||
@ -150,13 +141,6 @@ def get_attribute_value(attributes, key):
|
|||||||
return None
|
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):
|
def resolve_mailu_email(user, attributes):
|
||||||
explicit = get_attribute_value(attributes, "mailu_email")
|
explicit = get_attribute_value(attributes, "mailu_email")
|
||||||
if explicit:
|
if explicit:
|
||||||
@ -225,10 +209,6 @@ def main():
|
|||||||
|
|
||||||
for user in users:
|
for user in users:
|
||||||
attrs = user.get("attributes", {}) or {}
|
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")
|
app_pw = get_attribute_value(attrs, "mailu_app_password")
|
||||||
mailu_email = resolve_mailu_email(user, attrs)
|
mailu_email = resolve_mailu_email(user, attrs)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user