Files
authentik/authentik/providers/saml/tests/test_api.py
Jens L. 5b66dbe890 flows: provider invalidation (#5048)
* add initial

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add web stage for session end

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* migrate saml and tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* cleanup

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* group flow settings when providers have multiple flows

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* adjust name for default provider invalidation

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* re-make migrations

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add invalidation_flow to saml importer

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* re-do migrations again

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update web stuff to get rid of old libraries

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make unbind flow for ldap configurable

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* unrelated: fix flow inspector

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* handle invalidation_flow as optional, as it should be

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* also fix ldap outpost

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* don't generate URL in client

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* actually make it work???

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* format

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix migration breaking things...?

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* start fixing tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix fallback

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* re-migrate

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix duplicate flow setting

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add migration

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix race condition with brand

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix oauth test

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix SAML tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add to wizard, fix required

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update docs

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make required, start release notes

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-10-14 15:35:12 +02:00

190 lines
7.2 KiB
Python

"""SAML Provider API Tests"""
from json import loads
from tempfile import TemporaryFile
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.flows.models import FlowDesignation
from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import load_fixture
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
class TestSAMLProviderAPI(APITestCase):
"""SAML Provider API Tests"""
def setUp(self) -> None:
super().setUp()
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_detail(self):
"""Test detail"""
provider = SAMLProvider.objects.create(
name=generate_id(),
authorization_flow=create_test_flow(),
)
response = self.client.get(
reverse("authentik_api:samlprovider-detail", kwargs={"pk": provider.pk}),
)
self.assertEqual(200, response.status_code)
Application.objects.create(name=generate_id(), provider=provider, slug=generate_id())
response = self.client.get(
reverse("authentik_api:samlprovider-detail", kwargs={"pk": provider.pk}),
)
self.assertEqual(200, response.status_code)
def test_create_validate_signing_kp(self):
"""Test create"""
cert = create_test_cert()
response = self.client.post(
reverse("authentik_api:samlprovider-list"),
data={
"name": generate_id(),
"authorization_flow": create_test_flow().pk,
"invalidation_flow": create_test_flow().pk,
"acs_url": "http://localhost",
"signing_kp": cert.pk,
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content,
{
"non_field_errors": [
(
"With a signing keypair selected, at least one "
"of 'Sign assertion' and 'Sign Response' must be selected."
)
]
},
)
response = self.client.post(
reverse("authentik_api:samlprovider-list"),
data={
"name": generate_id(),
"authorization_flow": create_test_flow().pk,
"invalidation_flow": create_test_flow().pk,
"acs_url": "http://localhost",
"signing_kp": cert.pk,
"sign_assertion": True,
},
)
self.assertEqual(response.status_code, 201)
def test_metadata(self):
"""Test metadata export (normal)"""
self.client.logout()
provider = SAMLProvider.objects.create(
name=generate_id(),
authorization_flow=create_test_flow(),
)
Application.objects.create(name=generate_id(), provider=provider, slug=generate_id())
response = self.client.get(
reverse("authentik_api:samlprovider-metadata", kwargs={"pk": provider.pk}),
)
self.assertEqual(200, response.status_code)
def test_metadata_download(self):
"""Test metadata export (download)"""
self.client.logout()
provider = SAMLProvider.objects.create(
name=generate_id(),
authorization_flow=create_test_flow(),
)
Application.objects.create(name=generate_id(), provider=provider, slug=generate_id())
response = self.client.get(
reverse("authentik_api:samlprovider-metadata", kwargs={"pk": provider.pk})
+ "?download",
)
self.assertEqual(200, response.status_code)
self.assertIn("Content-Disposition", response)
def test_metadata_invalid(self):
"""Test metadata export (invalid)"""
self.client.logout()
# Provider without application
provider = SAMLProvider.objects.create(
name=generate_id(),
authorization_flow=create_test_flow(),
)
response = self.client.get(
reverse("authentik_api:samlprovider-metadata", kwargs={"pk": provider.pk}),
)
self.assertEqual(200, response.status_code)
response = self.client.get(
reverse("authentik_api:samlprovider-metadata", kwargs={"pk": "abc"}),
)
self.assertEqual(404, response.status_code)
def test_import_success(self):
"""Test metadata import (success case)"""
with TemporaryFile() as metadata:
metadata.write(load_fixture("fixtures/simple.xml").encode())
metadata.seek(0)
response = self.client.post(
reverse("authentik_api:samlprovider-import-metadata"),
{
"file": metadata,
"name": generate_id(),
"authorization_flow": create_test_flow(FlowDesignation.AUTHORIZATION).pk,
"invalidation_flow": create_test_flow(FlowDesignation.INVALIDATION).pk,
},
format="multipart",
)
self.assertEqual(204, response.status_code)
# We don't test the actual object being created here, that has its own tests
def test_import_failed(self):
"""Test metadata import (invalid xml)"""
with TemporaryFile() as metadata:
metadata.write(b"invalid")
metadata.seek(0)
response = self.client.post(
reverse("authentik_api:samlprovider-import-metadata"),
{
"file": metadata,
"name": generate_id(),
"authorization_flow": create_test_flow().pk,
},
format="multipart",
)
self.assertEqual(400, response.status_code)
def test_import_invalid(self):
"""Test metadata import (invalid input)"""
response = self.client.post(
reverse("authentik_api:samlprovider-import-metadata"),
{
"name": generate_id(),
},
format="multipart",
)
self.assertEqual(400, response.status_code)
@apply_blueprint("system/providers-saml.yaml")
def test_preview(self):
"""Test Preview API Endpoint"""
provider: SAMLProvider = SAMLProvider.objects.create(
name=generate_id(),
authorization_flow=create_test_flow(),
)
provider.property_mappings.set(SAMLPropertyMapping.objects.all())
Application.objects.create(name=generate_id(), provider=provider, slug=generate_id())
response = self.client.get(
reverse("authentik_api:samlprovider-preview-user", kwargs={"pk": provider.pk})
)
self.assertEqual(response.status_code, 200)
body = loads(response.content.decode())["preview"]["attributes"]
self.assertEqual(
[x for x in body if x["Name"] == "http://schemas.goauthentik.io/2021/02/saml/username"][
0
]["Value"],
[self.user.username],
)