Created
July 19, 2024 12:28
-
-
Save ephes/aecc9aeed0688bc09c86703b9a29f89c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import base64 | |
import pytest | |
from django.urls import reverse | |
from saml2 import server | |
from saml2.config import Config as PysamlConfig | |
@pytest.fixture(scope="session") | |
def idp_metadata_path(tmp_path_factory): | |
fn = tmp_path_factory.mktemp("data") / "idp.xml" | |
with fn.open("w") as f: | |
f.write('<EntityDescriptor entityID="https://localhost:8088/idp.xml" />') | |
return fn | |
SP_XML_TEMPLATE = """ | |
<ns0:EntityDescriptor | |
xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" | |
entityID="{entity_id}" | |
> | |
<ns0:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" /> | |
</ns0:EntityDescriptor> | |
""" | |
class SamlResponse: | |
test_host = "http://testserver" | |
entity_id = f"{test_host}/sso/metadata/" | |
acs_path = reverse("django_saml2_auth:acs") | |
next_url = "/next-url/" | |
def __init__(self, username, idp_metadata_path): | |
self.username = username | |
self.idp_metadata_path = idp_metadata_path | |
@property | |
def acs_url(self): | |
return f"{self.test_host}{self.acs_path}" | |
@property | |
def identity_provider(self): | |
sp_metadata = SP_XML_TEMPLATE.format(test_host=self.test_host, entity_id=self.entity_id) | |
config_data = { | |
"entityid": "https://localhost:8088/idp.xml", | |
"service": {"idp": {}}, | |
"metadata": {"inline": [sp_metadata]}, | |
} | |
config = PysamlConfig().load(config_data) | |
idp = server.Server(config=config) | |
idp.ticket = {} | |
return idp | |
def create_authn_response(self, user_identity): | |
resp_args = { | |
"in_response_to": "id-some-id", | |
"sp_entity_id": self.entity_id, | |
"name_id_policy": None, | |
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", | |
"destination": self.acs_url, | |
"authn": {"authn_auth": "http://www.example.com/authn"}, | |
} | |
response = self.identity_provider.create_authn_response(user_identity, **resp_args) | |
encoded_response = base64.b64encode(str(response).encode("utf-8")) | |
return encoded_response | |
def modify_saml2_auth_settings(self, settings): | |
settings.SAML2_AUTH["ENTITY_ID"] = self.entity_id | |
settings.SAML2_AUTH["METADATA_LOCAL_FILE_PATH"] = self.idp_metadata_path | |
settings.SAML2_AUTH["DEFAULT_NEXT_URL"] = self.next_url | |
del settings.SAML2_AUTH["METADATA_AUTO_CONF_URL"] | |
@pytest.mark.django_db | |
def test_sso_signin(client, settings, idp_metadata_path, django_user_model): | |
# Given a SAML response with a username and an identity provider metadata file | |
username = "testuser" | |
saml_response = SamlResponse(username, idp_metadata_path) | |
# And the SAML2_AUTH settings are modified to use the SAML response's entity ID | |
saml_response.modify_saml2_auth_settings(settings) | |
# When the encoded SAML response is posted to the ACS URL | |
user_identity = {"uid": saml_response.username} | |
encoded_response = saml_response.create_authn_response(user_identity) | |
r = client.post(saml_response.acs_url, data={"SAMLResponse": [encoded_response]}) | |
# Then the response is a redirect pointing to the DEFAULT_NEXT_URL | |
assert r.status_code == 302 | |
assert r.url == saml_response.next_url | |
# And a user with the SAML response's username is created and can only authenticate via SSO | |
sso_user = django_user_model.objects.get(username=username) | |
assert sso_user.has_usable_password() is False |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment