481 lines
20 KiB
Python
481 lines
20 KiB
Python
from tests.unit.manager.provisioning_helpers import *
|
|
|
|
|
|
def test_provisioning_filters_flag_groups(monkeypatch) -> None:
|
|
dummy_settings = types.SimpleNamespace(
|
|
mailu_domain="bstein.dev",
|
|
mailu_sync_url="http://mailu",
|
|
mailu_mailbox_wait_timeout_sec=1.0,
|
|
nextcloud_namespace="nextcloud",
|
|
nextcloud_mail_sync_cronjob="nextcloud-mail-sync",
|
|
provision_retry_cooldown_sec=0.0,
|
|
default_user_groups=["dev"],
|
|
allowed_flag_groups=["demo", "test"],
|
|
welcome_email_enabled=False,
|
|
portal_public_base_url="https://bstein.dev",
|
|
)
|
|
monkeypatch.setattr(prov, "settings", dummy_settings)
|
|
_patch_mailu_ready(monkeypatch, dummy_settings)
|
|
|
|
admin = DummyAdmin()
|
|
monkeypatch.setattr(prov, "keycloak_admin", admin)
|
|
monkeypatch.setattr(prov.mailu, "sync", lambda reason, force=False: None)
|
|
monkeypatch.setattr(prov.mailu, "wait_for_mailbox", lambda email, timeout: True)
|
|
monkeypatch.setattr(prov.nextcloud, "sync_mail", lambda username, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.wger, "sync_user", lambda username, email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.firefly, "sync_user", lambda email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.vaultwarden, "invite_user", lambda email: VaultwardenInvite(True, "invited"))
|
|
monkeypatch.setattr(prov.ProvisioningManager, "_all_tasks_ok", lambda *args, **kwargs: True)
|
|
monkeypatch.setattr(prov.ProvisioningManager, "_send_welcome_email", lambda *args, **kwargs: None)
|
|
|
|
row = {
|
|
"username": "alice",
|
|
"contact_email": "alice@example.com",
|
|
"email_verified_at": None,
|
|
"status": "approved",
|
|
"initial_password": None,
|
|
"initial_password_revealed_at": None,
|
|
"provision_attempted_at": None,
|
|
"approval_flags": ["demo", "admin"],
|
|
}
|
|
|
|
db = DummyDB(row)
|
|
storage = DummyStorage()
|
|
manager = prov.ProvisioningManager(db, storage)
|
|
manager.provision_access_request("REQ123")
|
|
|
|
assert "dev" in admin.groups
|
|
assert "demo" in admin.groups
|
|
assert "admin" not in admin.groups
|
|
|
|
def test_provisioning_creates_user_and_password(monkeypatch) -> None:
|
|
dummy_settings = types.SimpleNamespace(
|
|
mailu_domain="bstein.dev",
|
|
mailu_sync_url="http://mailu",
|
|
mailu_mailbox_wait_timeout_sec=1.0,
|
|
nextcloud_namespace="nextcloud",
|
|
nextcloud_mail_sync_cronjob="nextcloud-mail-sync",
|
|
provision_retry_cooldown_sec=0.0,
|
|
default_user_groups=["dev"],
|
|
allowed_flag_groups=["demo"],
|
|
welcome_email_enabled=False,
|
|
portal_public_base_url="https://bstein.dev",
|
|
)
|
|
monkeypatch.setattr(prov, "settings", dummy_settings)
|
|
_patch_mailu_ready(monkeypatch, dummy_settings)
|
|
|
|
class Admin(DummyAdmin):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.created_payload = None
|
|
self.reset_calls = []
|
|
|
|
def find_user(self, username):
|
|
return None
|
|
|
|
def get_user(self, user_id):
|
|
return {
|
|
"id": user_id,
|
|
"username": "alice",
|
|
"requiredActions": ["CONFIGURE_TOTP"],
|
|
"attributes": {},
|
|
}
|
|
|
|
def create_user(self, payload):
|
|
self.created_payload = payload
|
|
return "user-123"
|
|
|
|
def update_user(self, user_id, payload):
|
|
return None
|
|
|
|
def reset_password(self, user_id, password, temporary=False):
|
|
self.reset_calls.append((user_id, temporary))
|
|
|
|
admin = Admin()
|
|
monkeypatch.setattr(prov, "keycloak_admin", admin)
|
|
monkeypatch.setattr(prov.mailu, "sync", lambda reason, force=False: None)
|
|
monkeypatch.setattr(prov.mailu, "wait_for_mailbox", lambda email, timeout: True)
|
|
monkeypatch.setattr(prov.nextcloud, "sync_mail", lambda username, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.wger, "sync_user", lambda username, email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.firefly, "sync_user", lambda email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.vaultwarden, "invite_user", lambda email: VaultwardenInvite(True, "invited"))
|
|
monkeypatch.setattr(prov.ProvisioningManager, "_all_tasks_ok", lambda *args, **kwargs: True)
|
|
monkeypatch.setattr(prov.ProvisioningManager, "_send_welcome_email", lambda *args, **kwargs: None)
|
|
|
|
row = {
|
|
"username": "alice",
|
|
"first_name": "Alice",
|
|
"last_name": "Atlas",
|
|
"contact_email": "alice@example.com",
|
|
"email_verified_at": datetime.now(timezone.utc),
|
|
"status": "approved",
|
|
"initial_password": None,
|
|
"initial_password_revealed_at": None,
|
|
"provision_attempted_at": None,
|
|
"approval_flags": ["demo"],
|
|
}
|
|
|
|
db = DummyDB(row)
|
|
storage = DummyStorage()
|
|
manager = prov.ProvisioningManager(db, storage)
|
|
outcome = manager.provision_access_request("REQ124")
|
|
|
|
assert outcome.status == "awaiting_onboarding"
|
|
assert admin.created_payload is not None
|
|
assert admin.created_payload.get("firstName") == "Alice"
|
|
assert admin.created_payload.get("lastName") == "Atlas"
|
|
assert admin.reset_calls
|
|
|
|
def test_provisioning_create_user_fallback(monkeypatch) -> None:
|
|
dummy_settings = types.SimpleNamespace(
|
|
mailu_domain="bstein.dev",
|
|
mailu_sync_url="http://mailu",
|
|
mailu_mailbox_wait_timeout_sec=1.0,
|
|
nextcloud_namespace="nextcloud",
|
|
nextcloud_mail_sync_cronjob="nextcloud-mail-sync",
|
|
provision_retry_cooldown_sec=0.0,
|
|
default_user_groups=["dev"],
|
|
allowed_flag_groups=["demo"],
|
|
welcome_email_enabled=False,
|
|
portal_public_base_url="https://bstein.dev",
|
|
)
|
|
monkeypatch.setattr(prov, "settings", dummy_settings)
|
|
_patch_mailu_ready(monkeypatch, dummy_settings)
|
|
|
|
class Admin(DummyAdmin):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.find_calls = 0
|
|
self.reset_calls = []
|
|
|
|
def find_user(self, username):
|
|
self.find_calls += 1
|
|
if self.find_calls >= 2:
|
|
return {"id": "user-123", "username": username, "attributes": {}, "requiredActions": []}
|
|
return None
|
|
|
|
def get_user(self, user_id):
|
|
return {"id": user_id, "username": "alice", "attributes": {}, "requiredActions": []}
|
|
|
|
def create_user(self, payload):
|
|
raise RuntimeError("boom")
|
|
|
|
def reset_password(self, user_id, password, temporary=False):
|
|
self.reset_calls.append((user_id, temporary))
|
|
|
|
admin = Admin()
|
|
monkeypatch.setattr(prov, "keycloak_admin", admin)
|
|
monkeypatch.setattr(prov.mailu, "sync", lambda reason, force=False: None)
|
|
monkeypatch.setattr(prov.mailu, "wait_for_mailbox", lambda email, timeout: True)
|
|
monkeypatch.setattr(prov.nextcloud, "sync_mail", lambda username, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.wger, "sync_user", lambda username, email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.firefly, "sync_user", lambda email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.vaultwarden, "invite_user", lambda email: VaultwardenInvite(True, "invited"))
|
|
monkeypatch.setattr(prov.ProvisioningManager, "_all_tasks_ok", lambda *args, **kwargs: True)
|
|
monkeypatch.setattr(prov.ProvisioningManager, "_send_welcome_email", lambda *args, **kwargs: None)
|
|
|
|
row = {
|
|
"username": "alice",
|
|
"contact_email": "alice@example.com",
|
|
"email_verified_at": datetime.now(timezone.utc),
|
|
"status": "approved",
|
|
"initial_password": None,
|
|
"initial_password_revealed_at": None,
|
|
"provision_attempted_at": None,
|
|
"approval_flags": ["demo"],
|
|
}
|
|
|
|
db = DummyDB(row)
|
|
storage = DummyStorage()
|
|
manager = prov.ProvisioningManager(db, storage)
|
|
outcome = manager.provision_access_request("REQ125")
|
|
|
|
assert outcome.status == "awaiting_onboarding"
|
|
assert admin.find_calls >= 2
|
|
assert admin.reset_calls
|
|
|
|
def test_extract_attr_variants() -> None:
|
|
assert prov._extract_attr("bad", "key") == ""
|
|
assert prov._extract_attr({"key": ["value"]}, "key") == "value"
|
|
assert prov._extract_attr({"key": ["", " "]}, "key") == ""
|
|
assert prov._extract_attr({"key": "value"}, "key") == "value"
|
|
assert prov._extract_attr({}, "key") == ""
|
|
|
|
def test_provisioning_empty_request_code(monkeypatch) -> None:
|
|
db = DummyDB({})
|
|
storage = DummyStorage()
|
|
manager = prov.ProvisioningManager(db, storage)
|
|
outcome = manager.provision_access_request("")
|
|
assert outcome.status == "unknown"
|
|
|
|
def test_provisioning_admin_not_ready(monkeypatch) -> None:
|
|
monkeypatch.setattr(prov.keycloak_admin, "ready", lambda: False)
|
|
db = DummyDB({})
|
|
storage = DummyStorage()
|
|
manager = prov.ProvisioningManager(db, storage)
|
|
outcome = manager.provision_access_request("REQ")
|
|
assert outcome.status == "accounts_building"
|
|
|
|
def test_provisioning_lock_not_acquired(monkeypatch) -> None:
|
|
monkeypatch.setattr(prov.keycloak_admin, "ready", lambda: True)
|
|
row = {
|
|
"username": "alice",
|
|
"contact_email": "alice@example.com",
|
|
"email_verified_at": datetime.now(timezone.utc),
|
|
"status": "approved",
|
|
"initial_password": None,
|
|
"initial_password_revealed_at": None,
|
|
"provision_attempted_at": None,
|
|
"approval_flags": [],
|
|
}
|
|
db = DummyDB(row, locked=False)
|
|
storage = DummyStorage()
|
|
manager = prov.ProvisioningManager(db, storage)
|
|
outcome = manager.provision_access_request("REQ")
|
|
assert outcome.status == "accounts_building"
|
|
|
|
def test_provisioning_cooldown_short_circuit(monkeypatch) -> None:
|
|
dummy_settings = types.SimpleNamespace(
|
|
mailu_domain="bstein.dev",
|
|
mailu_sync_url="http://mailu",
|
|
mailu_mailbox_wait_timeout_sec=1.0,
|
|
nextcloud_namespace="nextcloud",
|
|
nextcloud_mail_sync_cronjob="nextcloud-mail-sync",
|
|
provision_retry_cooldown_sec=9999.0,
|
|
default_user_groups=["dev"],
|
|
allowed_flag_groups=["demo"],
|
|
welcome_email_enabled=False,
|
|
portal_public_base_url="https://bstein.dev",
|
|
provision_poll_interval_sec=1.0,
|
|
)
|
|
monkeypatch.setattr(prov, "settings", dummy_settings)
|
|
_patch_mailu_ready(monkeypatch, dummy_settings)
|
|
monkeypatch.setattr(prov.keycloak_admin, "ready", lambda: True)
|
|
|
|
row = {
|
|
"username": "alice",
|
|
"contact_email": "alice@example.com",
|
|
"email_verified_at": datetime.now(timezone.utc),
|
|
"status": "accounts_building",
|
|
"initial_password": None,
|
|
"initial_password_revealed_at": None,
|
|
"provision_attempted_at": datetime.now(timezone.utc),
|
|
"approval_flags": [],
|
|
}
|
|
db = DummyDB(row, locked=True)
|
|
storage = DummyStorage()
|
|
manager = prov.ProvisioningManager(db, storage)
|
|
outcome = manager.provision_access_request("REQ")
|
|
assert outcome.status == "accounts_building"
|
|
|
|
def test_provisioning_mailu_sync_disabled(monkeypatch) -> None:
|
|
dummy_settings = types.SimpleNamespace(
|
|
mailu_domain="bstein.dev",
|
|
mailu_sync_url="",
|
|
mailu_mailbox_wait_timeout_sec=1.0,
|
|
nextcloud_namespace="",
|
|
nextcloud_mail_sync_cronjob="",
|
|
provision_retry_cooldown_sec=0.0,
|
|
default_user_groups=["dev"],
|
|
allowed_flag_groups=["demo"],
|
|
welcome_email_enabled=False,
|
|
portal_public_base_url="https://bstein.dev",
|
|
)
|
|
monkeypatch.setattr(prov, "settings", dummy_settings)
|
|
_patch_mailu_ready(monkeypatch, dummy_settings)
|
|
|
|
class Admin(DummyAdmin):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.updated_actions = []
|
|
|
|
def find_user(self, username):
|
|
return {"id": "user-1"}
|
|
|
|
def get_user(self, user_id):
|
|
return {
|
|
"id": user_id,
|
|
"username": "alice",
|
|
"requiredActions": ["CONFIGURE_TOTP"],
|
|
"attributes": {
|
|
"mailu_email": ["alice@bstein.dev"],
|
|
"mailu_enabled": ["false"],
|
|
"wger_password": ["pw"],
|
|
"wger_password_updated_at": ["done"],
|
|
"firefly_password": ["pw"],
|
|
"firefly_password_updated_at": ["done"],
|
|
},
|
|
}
|
|
|
|
def update_user_safe(self, user_id, payload):
|
|
self.updated_actions.append(payload)
|
|
|
|
admin = Admin()
|
|
monkeypatch.setattr(prov, "keycloak_admin", admin)
|
|
monkeypatch.setattr(prov.wger, "sync_user", lambda username, email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.firefly, "sync_user", lambda email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.vaultwarden, "invite_user", lambda email: VaultwardenInvite(True, "invited"))
|
|
monkeypatch.setattr(prov.ProvisioningManager, "_all_tasks_ok", lambda *args, **kwargs: False)
|
|
|
|
row = {
|
|
"username": "alice",
|
|
"contact_email": "alice@example.com",
|
|
"email_verified_at": datetime.now(timezone.utc),
|
|
"status": "accounts_building",
|
|
"initial_password": "temp",
|
|
"initial_password_revealed_at": None,
|
|
"provision_attempted_at": None,
|
|
"approval_flags": [],
|
|
}
|
|
|
|
db = DummyDB(row)
|
|
storage = DummyStorage()
|
|
manager = prov.ProvisioningManager(db, storage)
|
|
outcome = manager.provision_access_request("REQ125")
|
|
assert outcome.status == "accounts_building"
|
|
assert admin.updated_actions
|
|
|
|
def test_provisioning_sets_missing_email(monkeypatch) -> None:
|
|
dummy_settings = types.SimpleNamespace(
|
|
mailu_domain="bstein.dev",
|
|
mailu_sync_url="http://mailu",
|
|
mailu_mailbox_wait_timeout_sec=1.0,
|
|
nextcloud_namespace="nextcloud",
|
|
nextcloud_mail_sync_cronjob="nextcloud-mail-sync",
|
|
provision_retry_cooldown_sec=0.0,
|
|
default_user_groups=["dev"],
|
|
allowed_flag_groups=[],
|
|
welcome_email_enabled=False,
|
|
portal_public_base_url="https://bstein.dev",
|
|
)
|
|
monkeypatch.setattr(prov, "settings", dummy_settings)
|
|
_patch_mailu_ready(monkeypatch, dummy_settings)
|
|
|
|
class Admin(DummyAdmin):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.updated_actions = []
|
|
|
|
def find_user(self, username):
|
|
return {"id": "user-1"}
|
|
|
|
def get_user(self, user_id):
|
|
return {"id": user_id, "username": "alice", "email": None, "attributes": {}}
|
|
|
|
def update_user_safe(self, user_id, payload):
|
|
self.updated_actions.append(payload)
|
|
|
|
admin = Admin()
|
|
monkeypatch.setattr(prov, "keycloak_admin", admin)
|
|
monkeypatch.setattr(prov.mailu, "sync", lambda reason, force=False: None)
|
|
monkeypatch.setattr(prov.mailu, "wait_for_mailbox", lambda email, timeout: True)
|
|
monkeypatch.setattr(prov.nextcloud, "sync_mail", lambda username, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.wger, "sync_user", lambda username, email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.firefly, "sync_user", lambda email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.vaultwarden, "invite_user", lambda email: VaultwardenInvite(True, "invited"))
|
|
monkeypatch.setattr(prov.ProvisioningManager, "_all_tasks_ok", lambda *args, **kwargs: False)
|
|
|
|
row = {
|
|
"username": "alice",
|
|
"contact_email": "alice@example.com",
|
|
"email_verified_at": datetime.now(timezone.utc),
|
|
"status": "accounts_building",
|
|
"initial_password": "temp",
|
|
"initial_password_revealed_at": None,
|
|
"provision_attempted_at": None,
|
|
"approval_flags": [],
|
|
}
|
|
|
|
db = DummyDB(row)
|
|
storage = DummyStorage()
|
|
manager = prov.ProvisioningManager(db, storage)
|
|
outcome = manager.provision_access_request("REQ126")
|
|
assert outcome.status == "accounts_building"
|
|
assert any("email" in payload for payload in admin.updated_actions)
|
|
|
|
def test_provisioning_mailbox_not_ready(monkeypatch) -> None:
|
|
dummy_settings = types.SimpleNamespace(
|
|
mailu_domain="bstein.dev",
|
|
mailu_sync_url="http://mailu",
|
|
mailu_mailbox_wait_timeout_sec=1.0,
|
|
nextcloud_namespace="nextcloud",
|
|
nextcloud_mail_sync_cronjob="nextcloud-mail-sync",
|
|
provision_retry_cooldown_sec=0.0,
|
|
default_user_groups=["dev"],
|
|
allowed_flag_groups=[],
|
|
welcome_email_enabled=False,
|
|
portal_public_base_url="https://bstein.dev",
|
|
)
|
|
monkeypatch.setattr(prov, "settings", dummy_settings)
|
|
_patch_mailu_ready(monkeypatch, dummy_settings)
|
|
|
|
admin = DummyAdmin()
|
|
monkeypatch.setattr(prov, "keycloak_admin", admin)
|
|
monkeypatch.setattr(prov.mailu, "sync", lambda reason, force=False: None)
|
|
monkeypatch.setattr(prov.mailu, "wait_for_mailbox", lambda email, timeout: False)
|
|
monkeypatch.setattr(prov.nextcloud, "sync_mail", lambda username, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.wger, "sync_user", lambda username, email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.firefly, "sync_user", lambda email, password, wait=True: {"status": "ok"})
|
|
monkeypatch.setattr(prov.vaultwarden, "invite_user", lambda email: VaultwardenInvite(True, "invited"))
|
|
monkeypatch.setattr(prov.ProvisioningManager, "_all_tasks_ok", lambda *args, **kwargs: False)
|
|
|
|
row = {
|
|
"username": "alice",
|
|
"contact_email": "alice@example.com",
|
|
"email_verified_at": datetime.now(timezone.utc),
|
|
"status": "approved",
|
|
"initial_password": "temp",
|
|
"initial_password_revealed_at": None,
|
|
"provision_attempted_at": None,
|
|
"approval_flags": [],
|
|
}
|
|
|
|
db = DummyDB(row)
|
|
storage = DummyStorage()
|
|
manager = prov.ProvisioningManager(db, storage)
|
|
outcome = manager.provision_access_request("REQ126")
|
|
assert outcome.status == "accounts_building"
|
|
|
|
def test_provisioning_sync_errors(monkeypatch) -> None:
|
|
dummy_settings = types.SimpleNamespace(
|
|
mailu_domain="bstein.dev",
|
|
mailu_sync_url="http://mailu",
|
|
mailu_mailbox_wait_timeout_sec=1.0,
|
|
nextcloud_namespace="nextcloud",
|
|
nextcloud_mail_sync_cronjob="nextcloud-mail-sync",
|
|
provision_retry_cooldown_sec=0.0,
|
|
default_user_groups=["dev"],
|
|
allowed_flag_groups=[],
|
|
welcome_email_enabled=False,
|
|
portal_public_base_url="https://bstein.dev",
|
|
)
|
|
monkeypatch.setattr(prov, "settings", dummy_settings)
|
|
_patch_mailu_ready(monkeypatch, dummy_settings)
|
|
|
|
admin = DummyAdmin()
|
|
monkeypatch.setattr(prov, "keycloak_admin", admin)
|
|
monkeypatch.setattr(prov.mailu, "sync", lambda reason, force=False: None)
|
|
monkeypatch.setattr(prov.mailu, "wait_for_mailbox", lambda email, timeout: True)
|
|
monkeypatch.setattr(prov.nextcloud, "sync_mail", lambda username, wait=True: {"status": "error"})
|
|
monkeypatch.setattr(prov.wger, "sync_user", lambda username, email, password, wait=True: {"status": "error"})
|
|
monkeypatch.setattr(prov.firefly, "sync_user", lambda email, password, wait=True: {"status": "error"})
|
|
monkeypatch.setattr(prov.vaultwarden, "invite_user", lambda email: VaultwardenInvite(False, "error", "fail"))
|
|
monkeypatch.setattr(prov.ProvisioningManager, "_all_tasks_ok", lambda *args, **kwargs: False)
|
|
|
|
row = {
|
|
"username": "alice",
|
|
"contact_email": "alice@example.com",
|
|
"email_verified_at": datetime.now(timezone.utc),
|
|
"status": "approved",
|
|
"initial_password": "temp",
|
|
"initial_password_revealed_at": None,
|
|
"provision_attempted_at": None,
|
|
"approval_flags": [],
|
|
}
|
|
|
|
db = DummyDB(row)
|
|
storage = DummyStorage()
|
|
manager = prov.ProvisioningManager(db, storage)
|
|
outcome = manager.provision_access_request("REQ127")
|
|
assert outcome.status == "accounts_building"
|