159 lines
5.0 KiB
Python
159 lines
5.0 KiB
Python
from unittest.mock import patch
|
|
from urllib.parse import parse_qs, urlparse
|
|
|
|
from django.urls import reverse
|
|
from django.utils.http import urlencode
|
|
|
|
import pytest
|
|
|
|
from allauth.account.models import EmailAddress
|
|
from allauth.socialaccount.models import SocialAccount
|
|
from allauth.socialaccount.providers.saml.utils import build_saml_config
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"relay_state, expected_url",
|
|
[
|
|
(None, "/accounts/profile/"),
|
|
("/foo", "/foo"),
|
|
],
|
|
)
|
|
def test_acs(
|
|
client,
|
|
db,
|
|
saml_settings,
|
|
acs_saml_response,
|
|
mocked_signature_validation,
|
|
expected_url,
|
|
relay_state,
|
|
):
|
|
data = {"SAMLResponse": acs_saml_response}
|
|
if relay_state is not None:
|
|
data["RelayState"] = relay_state
|
|
resp = client.post(
|
|
reverse("saml_acs", kwargs={"organization_slug": "org"}), data=data
|
|
)
|
|
finish_url = reverse("saml_finish_acs", kwargs={"organization_slug": "org"})
|
|
assert resp.status_code == 302
|
|
assert resp["location"] == finish_url
|
|
resp = client.get(finish_url)
|
|
assert resp["location"] == expected_url
|
|
account = SocialAccount.objects.get(
|
|
provider="urn:dev-123.us.auth0.com", uid="dummysamluid"
|
|
)
|
|
assert account.extra_data["Role"] == ["view-profile", "manage-account-links"]
|
|
email = EmailAddress.objects.get(user=account.user)
|
|
assert email.email == "john.doe@email.org"
|
|
|
|
|
|
def test_acs_error(client, db, saml_settings):
|
|
data = {"SAMLResponse": "bad-response"}
|
|
resp = client.post(
|
|
reverse("saml_acs", kwargs={"organization_slug": "org"}), data=data
|
|
)
|
|
assert resp.status_code == 200
|
|
assert "socialaccount/authentication_error.html" in (t.name for t in resp.templates)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"query,expected_relay_state",
|
|
[
|
|
("", None),
|
|
("?process=connect", "/social/connections/"),
|
|
("?process=connect&next=/foo", "/foo"),
|
|
("?next=/bar", "/bar"),
|
|
],
|
|
)
|
|
def test_login(client, db, saml_settings, query, expected_relay_state):
|
|
resp = client.get(
|
|
reverse("saml_login", kwargs={"organization_slug": "org"}) + query
|
|
)
|
|
assert resp.status_code == 302
|
|
location = resp["location"]
|
|
assert location.startswith("https://dev-123.us.auth0.com/samlp/456?SAMLRequest=")
|
|
resp_query = parse_qs(urlparse(location).query)
|
|
if expected_relay_state is None:
|
|
assert "RelayState" not in resp_query
|
|
else:
|
|
assert resp_query.get("RelayState")[0] == expected_relay_state
|
|
|
|
|
|
def test_metadata(
|
|
client,
|
|
db,
|
|
saml_settings,
|
|
):
|
|
resp = client.get(reverse("saml_metadata", kwargs={"organization_slug": "org"}))
|
|
assert resp.status_code == 200
|
|
assert resp.content.startswith(
|
|
b'<?xml version="1.0"?>\n<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata'
|
|
)
|
|
|
|
|
|
def test_sls(auth_client, db, saml_settings, user_factory, sls_saml_request):
|
|
with patch("allauth.account.adapter.DefaultAccountAdapter.logout") as logout_mock:
|
|
resp = auth_client.get(
|
|
reverse("saml_sls", kwargs={"organization_slug": "org"})
|
|
+ "?"
|
|
+ urlencode({"SAMLRequest": sls_saml_request})
|
|
)
|
|
assert logout_mock.call_count == 1
|
|
assert resp.status_code == 302
|
|
assert resp["location"].startswith(
|
|
"https://dev-123.us.auth0.com/samlp/456?SAMLResponse="
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"provider_config",
|
|
[
|
|
{
|
|
"idp": {
|
|
"entity_id": "dummy",
|
|
"sso_url": "https://idp.org/sso/",
|
|
"slo_url": "https://idp.saml.org/slo/",
|
|
"x509cert": "cert",
|
|
}
|
|
},
|
|
],
|
|
)
|
|
def test_build_saml_config_without_metadata_url(rf, provider_config):
|
|
request = rf.get("/")
|
|
config = build_saml_config(request, provider_config, "org")
|
|
assert config["idp"]["entityId"] == "dummy"
|
|
assert config["idp"]["x509cert"] == "cert"
|
|
assert config["idp"]["singleSignOnService"] == {"url": "https://idp.org/sso/"}
|
|
assert config["idp"]["singleLogoutService"] == {"url": "https://idp.saml.org/slo/"}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"provider_config",
|
|
[
|
|
{
|
|
"idp": {
|
|
"entity_id": "dummy",
|
|
"metadata_url": "https://idp.org/sso/",
|
|
}
|
|
},
|
|
],
|
|
)
|
|
def test_build_saml_config(rf, provider_config):
|
|
request = rf.get("/")
|
|
with patch(
|
|
"onelogin.saml2.idp_metadata_parser.OneLogin_Saml2_IdPMetadataParser.parse_remote"
|
|
) as parse_mock:
|
|
parse_mock.return_value = {
|
|
"idp": {
|
|
"entityId": "dummy",
|
|
"singleSignOnService": {"url": "https://idp.org/sso/"},
|
|
"singleLogoutService": {"url": "https://idp.saml.org/slo/"},
|
|
"x509cert": "cert",
|
|
}
|
|
}
|
|
config = build_saml_config(request, provider_config, "org")
|
|
|
|
assert config["idp"]["entityId"] == "dummy"
|
|
assert config["idp"]["x509cert"] == "cert"
|
|
assert config["idp"]["singleSignOnService"] == {"url": "https://idp.org/sso/"}
|
|
assert config["idp"]["singleLogoutService"] == {"url": "https://idp.saml.org/slo/"}
|