This commit is contained in:
@@ -8,6 +8,7 @@ from apps.sources.services.fetcher import (
|
||||
FetchTimeoutError,
|
||||
PolicyBlockedError,
|
||||
RateLimitedError,
|
||||
_validate_connected_peer,
|
||||
fetch_url,
|
||||
)
|
||||
from apps.sources.services.url_security import ValidatedUrl
|
||||
@@ -100,6 +101,24 @@ def test_fetcher_rejects_dns_rebinding(monkeypatch):
|
||||
client.close()
|
||||
|
||||
|
||||
def test_production_transport_fails_closed_without_connected_peer():
|
||||
validated = ValidatedUrl(
|
||||
url="https://example.org/jobs",
|
||||
hostname="example.org",
|
||||
port=443,
|
||||
addresses=("93.184.216.34",),
|
||||
)
|
||||
response = httpx.Response(200)
|
||||
|
||||
with pytest.raises(FetchError, match="Verbonden IP-adres"):
|
||||
_validate_connected_peer(
|
||||
response,
|
||||
before=validated,
|
||||
after=validated,
|
||||
require_peer=True,
|
||||
)
|
||||
|
||||
|
||||
class _PeerStream:
|
||||
def __init__(self, address: str) -> None:
|
||||
self.address = address
|
||||
|
||||
@@ -89,6 +89,19 @@ def test_rotation_reencrypts_existing_credentials_with_primary_key(user):
|
||||
assert decrypt_mailbox_password(connection) == "rotate-me"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_rotation_skips_outlook_oauth_connections(user):
|
||||
MailboxConnection.objects.create(
|
||||
user=user,
|
||||
platform=MailboxConnection.Platform.INDEED,
|
||||
provider=MailboxConnection.Provider.OUTLOOK,
|
||||
username="vacatureradar@example.invalid",
|
||||
encrypted_password="",
|
||||
)
|
||||
|
||||
assert rotate_mailbox_credentials() == 0
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_private_imap_host_is_blocked_before_connection(user):
|
||||
key = Fernet.generate_key().decode("ascii")
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import base64
|
||||
|
||||
import pytest
|
||||
from django.test import override_settings
|
||||
|
||||
from apps.core.services.microsoft365 import (
|
||||
Microsoft365OAuthError,
|
||||
acquire_exchange_access_token,
|
||||
xoauth2_b64,
|
||||
)
|
||||
from apps.notifications.backends import Microsoft365OAuthEmailBackend
|
||||
|
||||
|
||||
@override_settings(
|
||||
M365_TENANT_ID="tenant-id",
|
||||
M365_CLIENT_ID="client-id",
|
||||
M365_CLIENT_SECRET="client-secret",
|
||||
M365_EXCHANGE_SCOPE="https://outlook.office365.com/.default",
|
||||
)
|
||||
def test_app_only_token_uses_tenant_authority_and_exchange_scope():
|
||||
calls = {}
|
||||
|
||||
class Client:
|
||||
def acquire_token_for_client(self, *, scopes):
|
||||
calls["scopes"] = scopes
|
||||
return {"access_token": "access-token"}
|
||||
|
||||
def factory(client_id, *, authority, client_credential):
|
||||
calls.update(
|
||||
client_id=client_id,
|
||||
authority=authority,
|
||||
client_credential=client_credential,
|
||||
)
|
||||
return Client()
|
||||
|
||||
assert acquire_exchange_access_token(client_factory=factory) == "access-token"
|
||||
assert calls == {
|
||||
"client_id": "client-id",
|
||||
"authority": "https://login.microsoftonline.com/tenant-id",
|
||||
"client_credential": "client-secret",
|
||||
"scopes": ["https://outlook.office365.com/.default"],
|
||||
}
|
||||
|
||||
|
||||
@override_settings(M365_TENANT_ID="", M365_CLIENT_ID="", M365_CLIENT_SECRET="")
|
||||
def test_missing_oauth_configuration_fails_without_secret_material():
|
||||
with pytest.raises(Microsoft365OAuthError, match="niet volledig") as error:
|
||||
acquire_exchange_access_token()
|
||||
assert "secret" not in str(error.value).lower()
|
||||
|
||||
|
||||
def test_xoauth2_payload_is_encoded_exactly():
|
||||
encoded = xoauth2_b64("mailbox@example.invalid", "token-value")
|
||||
assert base64.b64decode(encoded) == (
|
||||
b"user=mailbox@example.invalid\x01auth=Bearer token-value\x01\x01"
|
||||
)
|
||||
|
||||
|
||||
@override_settings(
|
||||
M365_MAILBOX_USER="vacatureradar@example.invalid",
|
||||
EMAIL_HOST="smtp.office365.com",
|
||||
EMAIL_PORT=587,
|
||||
DEFAULT_FROM_EMAIL="VacatureRadar <vacatureradar@example.invalid>",
|
||||
M365_SMTP_TIMEOUT_SECONDS=10,
|
||||
)
|
||||
def test_oauth_smtp_backend_authenticates_and_sends(monkeypatch):
|
||||
calls = []
|
||||
|
||||
class SMTP:
|
||||
def __init__(self, host, port, *, timeout):
|
||||
calls.append(("connect", host, port, timeout))
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
return None
|
||||
|
||||
def ehlo(self):
|
||||
calls.append(("ehlo",))
|
||||
|
||||
def starttls(self, *, context):
|
||||
calls.append(("starttls", bool(context)))
|
||||
|
||||
def docmd(self, command, argument):
|
||||
calls.append((command, argument))
|
||||
return 235, b"ok"
|
||||
|
||||
def sendmail(self, sender, recipients, message):
|
||||
calls.append(("sendmail", sender, recipients, message))
|
||||
|
||||
monkeypatch.setattr("apps.notifications.backends.smtplib.SMTP", SMTP)
|
||||
monkeypatch.setattr(
|
||||
"apps.notifications.backends.acquire_exchange_access_token",
|
||||
lambda: "token-value",
|
||||
)
|
||||
from django.core.mail import EmailMessage as DjangoEmailMessage
|
||||
|
||||
django_message = DjangoEmailMessage(
|
||||
subject="Test",
|
||||
body="test",
|
||||
from_email="vacatureradar@example.invalid",
|
||||
to=["recipient@example.invalid"],
|
||||
)
|
||||
assert Microsoft365OAuthEmailBackend().send_messages([django_message]) == 1
|
||||
assert calls[0] == ("connect", "smtp.office365.com", 587, 10)
|
||||
assert any(call[0] == "AUTH" and call[1].startswith("XOAUTH2 ") for call in calls)
|
||||
assert any(call[0] == "sendmail" for call in calls)
|
||||
Reference in New Issue
Block a user