mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
enterprise/providers/ssf: more conformance fixes (#21521)
* enterprise/providers/ssf: more conformance fixes Signed-off-by: Jens Langhammer <jens@goauthentik.io> * include request when possible Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove null state Signed-off-by: Jens Langhammer <jens@goauthentik.io> * t Signed-off-by: Jens Langhammer <jens@goauthentik.io> * re-gen & format Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove None state Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix ci Signed-off-by: Jens Langhammer <jens@goauthentik.io> * revert a thing Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix ssf conformance test Signed-off-by: Jens Langhammer <jens@goauthentik.io> * no subtest Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix network Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add test for stream update Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@@ -282,10 +282,12 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- name: basic
|
||||
glob: tests/openid_conformance/test_basic.py
|
||||
- name: implicit
|
||||
glob: tests/openid_conformance/test_implicit.py
|
||||
- name: oidc_basic
|
||||
glob: tests/openid_conformance/test_oidc_basic.py
|
||||
- name: oidc_implicit
|
||||
glob: tests/openid_conformance/test_oidc_implicit.py
|
||||
- name: ssf_transmitter
|
||||
glob: tests/openid_conformance/test_ssf_transmitter.py
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- name: Setup authentik env
|
||||
|
||||
+106
@@ -1,6 +1,7 @@
|
||||
# Generated by Django 5.2.12 on 2026-04-04 16:58
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.contrib.postgres.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@@ -40,4 +41,109 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="stream",
|
||||
name="events_requested",
|
||||
field=django.contrib.postgres.fields.ArrayField(
|
||||
base_field=models.TextField(
|
||||
choices=[
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
|
||||
"Caep Session Revoked",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/token-claims-change",
|
||||
"Caep Token Claims Change",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/credential-change",
|
||||
"Caep Credential Change",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/assurance-level-change",
|
||||
"Caep Assurance Level Change",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/device-compliance-change",
|
||||
"Caep Device Compliance Change",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-established",
|
||||
"Caep Session Established",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-presented",
|
||||
"Caep Session Presented",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/risk-level-change",
|
||||
"Caep Risk Level Change",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/ssf/event-type/verification",
|
||||
"Set Verification",
|
||||
),
|
||||
]
|
||||
),
|
||||
default=list,
|
||||
size=None,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="stream",
|
||||
name="status",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
("enabled", "Enabled"),
|
||||
("paused", "Paused"),
|
||||
("disabled", "Disabled"),
|
||||
("disabled_deleted", "Disabled Deleted"),
|
||||
],
|
||||
default="enabled",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="streamevent",
|
||||
name="type",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
|
||||
"Caep Session Revoked",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/token-claims-change",
|
||||
"Caep Token Claims Change",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/credential-change",
|
||||
"Caep Credential Change",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/assurance-level-change",
|
||||
"Caep Assurance Level Change",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/device-compliance-change",
|
||||
"Caep Device Compliance Change",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-established",
|
||||
"Caep Session Established",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-presented",
|
||||
"Caep Session Presented",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/caep/event-type/risk-level-change",
|
||||
"Caep Risk Level Change",
|
||||
),
|
||||
(
|
||||
"https://schemas.openid.net/secevent/ssf/event-type/verification",
|
||||
"Set Verification",
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -24,8 +24,31 @@ class EventTypes(models.TextChoices):
|
||||
"""SSF Event types supported by authentik"""
|
||||
|
||||
CAEP_SESSION_REVOKED = "https://schemas.openid.net/secevent/caep/event-type/session-revoked"
|
||||
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.1"""
|
||||
CAEP_TOKEN_CLAIMS_CHANGE = (
|
||||
"https://schemas.openid.net/secevent/caep/event-type/token-claims-change"
|
||||
)
|
||||
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.2"""
|
||||
CAEP_CREDENTIAL_CHANGE = "https://schemas.openid.net/secevent/caep/event-type/credential-change"
|
||||
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.3"""
|
||||
CAEP_ASSURANCE_LEVEL_CHANGE = (
|
||||
"https://schemas.openid.net/secevent/caep/event-type/assurance-level-change"
|
||||
)
|
||||
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.4"""
|
||||
CAEP_DEVICE_COMPLIANCE_CHANGE = (
|
||||
"https://schemas.openid.net/secevent/caep/event-type/device-compliance-change"
|
||||
)
|
||||
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.5"""
|
||||
CAEP_SESSION_ESTABLISHED = (
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-established"
|
||||
)
|
||||
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.6"""
|
||||
CAEP_SESSION_PRESENTED = "https://schemas.openid.net/secevent/caep/event-type/session-presented"
|
||||
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.7"""
|
||||
CAEP_RISK_LEVEL_CHANGE = "https://schemas.openid.net/secevent/caep/event-type/risk-level-change"
|
||||
"""https://openid.net/specs/openid-caep-1_0-final.html#section-3.8"""
|
||||
SET_VERIFICATION = "https://schemas.openid.net/secevent/ssf/event-type/verification"
|
||||
"""https://openid.net/specs/openid-sharedsignals-framework-1_0.html#section-8.1.4.1"""
|
||||
|
||||
|
||||
class DeliveryMethods(models.TextChoices):
|
||||
@@ -46,10 +69,12 @@ class SSFEventStatus(models.TextChoices):
|
||||
|
||||
|
||||
class StreamStatus(models.TextChoices):
|
||||
"""SSF Stream status"""
|
||||
|
||||
ENABLED = "enabled"
|
||||
PAUSED = "paused"
|
||||
DISABLED = "disabled"
|
||||
DISABLED_DELETED = "disabled_deleted"
|
||||
|
||||
|
||||
class SSFProvider(TasksModel, BackchannelProvider):
|
||||
|
||||
@@ -108,13 +108,13 @@ def send_ssf_event(stream_uuid: UUID, event_data: dict[str, Any]):
|
||||
event.save()
|
||||
self.info("Event successfully sent", status=response.status_code)
|
||||
# Cleanup, if we were the last pending message for this stream and it has been deleted
|
||||
# (status=StreamStatus.DISABLED), then we can delete the stream
|
||||
# (status=StreamStatus.DISABLED_DELETED), then we can delete the stream
|
||||
if (
|
||||
not StreamEvent.objects.filter(
|
||||
stream=stream,
|
||||
status__in=[SSFEventStatus.PENDING_FAILED, SSFEventStatus.PENDING_NEW],
|
||||
).exists()
|
||||
and stream.status == StreamStatus.DISABLED
|
||||
and stream.status == StreamStatus.DISABLED_DELETED
|
||||
):
|
||||
LOGGER.info(
|
||||
"Deleting inactive stream as all pending messages were sent.", stream=stream
|
||||
|
||||
@@ -62,7 +62,7 @@ class TestSSFAuth(APITestCase):
|
||||
self.assertEqual(event.status, SSFEventStatus.PENDING_FAILED)
|
||||
self.assertEqual(
|
||||
event.payload["events"],
|
||||
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {"state": None}},
|
||||
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {}},
|
||||
)
|
||||
|
||||
def test_stream_add_oidc(self):
|
||||
@@ -115,7 +115,7 @@ class TestSSFAuth(APITestCase):
|
||||
self.assertEqual(event.status, SSFEventStatus.PENDING_FAILED)
|
||||
self.assertEqual(
|
||||
event.payload["events"],
|
||||
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {"state": None}},
|
||||
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {}},
|
||||
)
|
||||
|
||||
def test_token_invalid(self):
|
||||
|
||||
@@ -54,7 +54,7 @@ class TestStream(APITestCase):
|
||||
self.assertEqual(event.status, SSFEventStatus.PENDING_FAILED)
|
||||
self.assertEqual(
|
||||
event.payload["events"],
|
||||
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {"state": None}},
|
||||
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {}},
|
||||
)
|
||||
|
||||
def test_stream_add_poll(self):
|
||||
@@ -96,7 +96,7 @@ class TestStream(APITestCase):
|
||||
)
|
||||
self.assertEqual(res.status_code, 204)
|
||||
stream.refresh_from_db()
|
||||
self.assertEqual(stream.status, StreamStatus.DISABLED)
|
||||
self.assertEqual(stream.status, StreamStatus.DISABLED_DELETED)
|
||||
|
||||
def test_stream_get(self):
|
||||
"""get stream"""
|
||||
@@ -225,3 +225,26 @@ class TestStream(APITestCase):
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_stream_status_update(self):
|
||||
stream = Stream.objects.create(provider=self.provider)
|
||||
res = self.client.post(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream-status",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
data={
|
||||
"stream_id": str(stream.pk),
|
||||
"status": StreamStatus.DISABLED,
|
||||
},
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
stream.refresh_from_db()
|
||||
self.assertJSONEqual(
|
||||
res.content,
|
||||
{
|
||||
"stream_id": str(stream.pk),
|
||||
"status": str(stream.status),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -33,7 +33,7 @@ class TestTasks(APITestCase):
|
||||
)
|
||||
event_data = stream.prepare_event_payload(
|
||||
EventTypes.SET_VERIFICATION,
|
||||
{"state": None},
|
||||
{},
|
||||
sub_id={"format": "opaque", "id": str(stream.uuid)},
|
||||
)
|
||||
with Mocker() as mocker:
|
||||
@@ -46,7 +46,7 @@ class TestTasks(APITestCase):
|
||||
)
|
||||
jwt = decode_complete(mocker.request_history[0].body, options={"verify_signature": False})
|
||||
self.assertEqual(jwt["header"]["typ"], "secevent+jwt")
|
||||
self.assertIsNone(jwt["payload"]["events"][EventTypes.SET_VERIFICATION]["state"])
|
||||
self.assertEqual(jwt["payload"]["events"][EventTypes.SET_VERIFICATION], {})
|
||||
|
||||
def test_push_auth(self):
|
||||
auth = generate_id()
|
||||
@@ -58,7 +58,7 @@ class TestTasks(APITestCase):
|
||||
)
|
||||
event_data = stream.prepare_event_payload(
|
||||
EventTypes.SET_VERIFICATION,
|
||||
{"state": None},
|
||||
{},
|
||||
sub_id={"format": "opaque", "id": str(stream.uuid)},
|
||||
)
|
||||
with Mocker() as mocker:
|
||||
@@ -72,7 +72,7 @@ class TestTasks(APITestCase):
|
||||
)
|
||||
jwt = decode_complete(mocker.request_history[0].body, options={"verify_signature": False})
|
||||
self.assertEqual(jwt["header"]["typ"], "secevent+jwt")
|
||||
self.assertIsNone(jwt["payload"]["events"][EventTypes.SET_VERIFICATION]["state"])
|
||||
self.assertEqual(jwt["payload"]["events"][EventTypes.SET_VERIFICATION], {})
|
||||
|
||||
def test_push_stream_disable(self):
|
||||
auth = generate_id()
|
||||
@@ -81,11 +81,11 @@ class TestTasks(APITestCase):
|
||||
delivery_method=DeliveryMethods.RFC_PUSH,
|
||||
endpoint_url="http://localhost/ssf-push",
|
||||
authorization_header=auth,
|
||||
status=StreamStatus.DISABLED,
|
||||
status=StreamStatus.DISABLED_DELETED,
|
||||
)
|
||||
event_data = stream.prepare_event_payload(
|
||||
EventTypes.SET_VERIFICATION,
|
||||
{"state": None},
|
||||
{},
|
||||
sub_id={"format": "opaque", "id": str(stream.uuid)},
|
||||
)
|
||||
with Mocker() as mocker:
|
||||
@@ -95,7 +95,7 @@ class TestTasks(APITestCase):
|
||||
).get_result(block=True, timeout=1)
|
||||
jwt = decode_complete(mocker.request_history[0].body, options={"verify_signature": False})
|
||||
self.assertEqual(jwt["header"]["typ"], "secevent+jwt")
|
||||
self.assertIsNone(jwt["payload"]["events"][EventTypes.SET_VERIFICATION]["state"])
|
||||
self.assertEqual(jwt["payload"]["events"][EventTypes.SET_VERIFICATION], {})
|
||||
self.assertFalse(Stream.objects.filter(pk=stream.pk).exists())
|
||||
|
||||
def test_push_error(self):
|
||||
@@ -106,7 +106,7 @@ class TestTasks(APITestCase):
|
||||
)
|
||||
event_data = stream.prepare_event_payload(
|
||||
EventTypes.SET_VERIFICATION,
|
||||
{"state": None},
|
||||
{},
|
||||
sub_id={"format": "opaque", "id": str(stream.uuid)},
|
||||
)
|
||||
with Mocker() as mocker:
|
||||
|
||||
@@ -24,10 +24,10 @@ class SSFView(APIView):
|
||||
|
||||
|
||||
class SSFStreamView(SSFView):
|
||||
def get_object(self, any_status=False) -> Stream:
|
||||
streams = Stream.objects.filter(provider=self.provider)
|
||||
if not any_status:
|
||||
streams = streams.filter(status__in=[StreamStatus.ENABLED, StreamStatus.PAUSED])
|
||||
def get_object(self) -> Stream:
|
||||
streams = Stream.objects.filter(provider=self.provider).exclude(
|
||||
status=StreamStatus.DISABLED_DELETED
|
||||
)
|
||||
if "stream_id" in self.request.query_params:
|
||||
streams = streams.filter(pk=self.request.query_params["stream_id"])
|
||||
if "stream_id" in self.request.data:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from uuid import uuid4
|
||||
|
||||
from django.http import HttpRequest
|
||||
from django.http import Http404, HttpRequest
|
||||
from django.urls import reverse
|
||||
from rest_framework.exceptions import PermissionDenied, ValidationError
|
||||
from rest_framework.fields import CharField, ChoiceField, ListField, SerializerMethodField
|
||||
@@ -106,7 +106,11 @@ class StreamResponseSerializer(PassiveSerializer):
|
||||
}
|
||||
|
||||
def get_events_supported(self, instance: Stream) -> list[str]:
|
||||
return [x.value for x in EventTypes]
|
||||
return [
|
||||
EventTypes.CAEP_SESSION_REVOKED,
|
||||
EventTypes.CAEP_CREDENTIAL_CHANGE,
|
||||
EventTypes.SET_VERIFICATION,
|
||||
]
|
||||
|
||||
|
||||
class StreamView(SSFStreamView):
|
||||
@@ -128,10 +132,9 @@ class StreamView(SSFStreamView):
|
||||
LOGGER.info("Sending verification event", stream=instance)
|
||||
send_ssf_events(
|
||||
EventTypes.SET_VERIFICATION,
|
||||
{
|
||||
"state": None,
|
||||
},
|
||||
{},
|
||||
stream_filter={"pk": instance.uuid},
|
||||
request=request,
|
||||
sub_id={"format": "opaque", "id": str(instance.uuid)},
|
||||
)
|
||||
response = StreamResponseSerializer(instance=instance, context={"request": request}).data
|
||||
@@ -159,7 +162,9 @@ class StreamView(SSFStreamView):
|
||||
|
||||
def delete(self, request: Request, *args, **kwargs) -> Response:
|
||||
stream = self.get_object()
|
||||
stream.status = StreamStatus.DISABLED
|
||||
if stream.status == StreamStatus.DISABLED_DELETED:
|
||||
raise Http404
|
||||
stream.status = StreamStatus.DISABLED_DELETED
|
||||
stream.save()
|
||||
return Response(status=204)
|
||||
|
||||
@@ -175,6 +180,7 @@ class StreamVerifyView(SSFStreamView):
|
||||
"state": state,
|
||||
},
|
||||
stream_filter={"pk": stream.uuid},
|
||||
request=request,
|
||||
sub_id={"format": "opaque", "id": str(stream.uuid)},
|
||||
)
|
||||
return Response(status=204)
|
||||
@@ -182,8 +188,25 @@ class StreamVerifyView(SSFStreamView):
|
||||
|
||||
class StreamStatusView(SSFStreamView):
|
||||
|
||||
class StreamStatusSerializer(PassiveSerializer):
|
||||
stream_id = CharField()
|
||||
status = ChoiceField(choices=StreamStatus.choices)
|
||||
|
||||
def get(self, request: Request, *args, **kwargs):
|
||||
stream = self.get_object(any_status=True)
|
||||
stream = self.get_object()
|
||||
return Response(
|
||||
{
|
||||
"stream_id": str(stream.pk),
|
||||
"status": str(stream.status),
|
||||
}
|
||||
)
|
||||
|
||||
def post(self, request: Request, *args, **kwargs):
|
||||
stream = self.get_object()
|
||||
serializer = self.StreamStatusSerializer(stream, data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
stream.status = serializer.validated_data["status"]
|
||||
stream.save()
|
||||
return Response(
|
||||
{
|
||||
"stream_id": str(stream.pk),
|
||||
|
||||
@@ -19,8 +19,20 @@
|
||||
export const EventsRequestedEnum = {
|
||||
HttpsSchemasOpenidNetSeceventCaepEventTypeSessionRevoked:
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
|
||||
HttpsSchemasOpenidNetSeceventCaepEventTypeTokenClaimsChange:
|
||||
"https://schemas.openid.net/secevent/caep/event-type/token-claims-change",
|
||||
HttpsSchemasOpenidNetSeceventCaepEventTypeCredentialChange:
|
||||
"https://schemas.openid.net/secevent/caep/event-type/credential-change",
|
||||
HttpsSchemasOpenidNetSeceventCaepEventTypeAssuranceLevelChange:
|
||||
"https://schemas.openid.net/secevent/caep/event-type/assurance-level-change",
|
||||
HttpsSchemasOpenidNetSeceventCaepEventTypeDeviceComplianceChange:
|
||||
"https://schemas.openid.net/secevent/caep/event-type/device-compliance-change",
|
||||
HttpsSchemasOpenidNetSeceventCaepEventTypeSessionEstablished:
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-established",
|
||||
HttpsSchemasOpenidNetSeceventCaepEventTypeSessionPresented:
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-presented",
|
||||
HttpsSchemasOpenidNetSeceventCaepEventTypeRiskLevelChange:
|
||||
"https://schemas.openid.net/secevent/caep/event-type/risk-level-change",
|
||||
HttpsSchemasOpenidNetSeceventSsfEventTypeVerification:
|
||||
"https://schemas.openid.net/secevent/ssf/event-type/verification",
|
||||
UnknownDefaultOpenApi: "11184809",
|
||||
|
||||
@@ -20,6 +20,7 @@ export const SSFStreamStatusEnum = {
|
||||
Enabled: "enabled",
|
||||
Paused: "paused",
|
||||
Disabled: "disabled",
|
||||
DisabledDeleted: "disabled_deleted",
|
||||
UnknownDefaultOpenApi: "11184809",
|
||||
} as const;
|
||||
export type SSFStreamStatusEnum = (typeof SSFStreamStatusEnum)[keyof typeof SSFStreamStatusEnum];
|
||||
|
||||
@@ -39031,7 +39031,13 @@ components:
|
||||
EventsRequestedEnum:
|
||||
enum:
|
||||
- https://schemas.openid.net/secevent/caep/event-type/session-revoked
|
||||
- https://schemas.openid.net/secevent/caep/event-type/token-claims-change
|
||||
- https://schemas.openid.net/secevent/caep/event-type/credential-change
|
||||
- https://schemas.openid.net/secevent/caep/event-type/assurance-level-change
|
||||
- https://schemas.openid.net/secevent/caep/event-type/device-compliance-change
|
||||
- https://schemas.openid.net/secevent/caep/event-type/session-established
|
||||
- https://schemas.openid.net/secevent/caep/event-type/session-presented
|
||||
- https://schemas.openid.net/secevent/caep/event-type/risk-level-change
|
||||
- https://schemas.openid.net/secevent/ssf/event-type/verification
|
||||
type: string
|
||||
ExpiringBaseGrantModel:
|
||||
@@ -55618,6 +55624,7 @@ components:
|
||||
- enabled
|
||||
- paused
|
||||
- disabled
|
||||
- disabled_deleted
|
||||
type: string
|
||||
Schedule:
|
||||
type: object
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from os import makedirs
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
from typing import Any
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as ec
|
||||
@@ -64,27 +65,27 @@ class TestOpenIDConformance(SeleniumTestCase):
|
||||
"client_registration": "static_client",
|
||||
}
|
||||
|
||||
def run_test(self, test_name: str, test_plan_config: dict):
|
||||
# Create a Conformance instance...
|
||||
def run_test(
|
||||
self, test_name: str, test_plan_config: dict[str, Any], test_variant: dict[str, Any]
|
||||
):
|
||||
self.conformance = Conformance(f"https://{self.host}:8443/", None, verify_ssl=False)
|
||||
|
||||
test_plan = self.conformance.create_test_plan(
|
||||
test_name,
|
||||
test_plan_config,
|
||||
self.test_variant,
|
||||
test_variant,
|
||||
)
|
||||
plan_id = test_plan["id"]
|
||||
for test in test_plan["modules"]:
|
||||
with self.subTest(test["testModule"], **test["variant"]):
|
||||
# Fetch name and variant of the next test to run
|
||||
module_name = test["testModule"]
|
||||
variant = test["variant"]
|
||||
module_instance = self.conformance.create_test_from_plan_with_variant(
|
||||
plan_id, module_name, variant
|
||||
)
|
||||
module_id = module_instance["id"]
|
||||
self.run_single_test(module_id)
|
||||
self.conformance.wait_for_state(module_id, ["FINISHED"], timeout=self.wait_timeout)
|
||||
# Fetch name and variant of the next test to run
|
||||
module_name = test["testModule"]
|
||||
variant = test["variant"]
|
||||
module_instance = self.conformance.create_test_from_plan_with_variant(
|
||||
plan_id, module_name, variant
|
||||
)
|
||||
module_id = module_instance["id"]
|
||||
self.run_single_test(module_id)
|
||||
self.conformance.wait_for_state(module_id, ["FINISHED"], timeout=self.wait_timeout)
|
||||
sleep(2)
|
||||
self.conformance.export_html(plan_id, Path(__file__).parent / "exports")
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@ services:
|
||||
mongodb:
|
||||
image: mongo:6.0.13
|
||||
nginx:
|
||||
image: ghcr.io/beryju/oidc-conformance-suite-nginx:v5.1.41
|
||||
image: ghcr.io/beryju/oidc-conformance-suite-nginx:v5.1.43
|
||||
ports:
|
||||
- "8443:8443"
|
||||
- "8444:8444"
|
||||
depends_on:
|
||||
- server
|
||||
server:
|
||||
image: ghcr.io/beryju/oidc-conformance-suite-server:v5.1.41
|
||||
image: ghcr.io/beryju/oidc-conformance-suite-server:v5.1.43
|
||||
ports:
|
||||
- "9999:9999"
|
||||
extra_hosts:
|
||||
@@ -19,8 +19,8 @@ services:
|
||||
-Xdebug -Xrunjdwp:transport=dt_socket,address=*:9999,server=y,suspend=n
|
||||
-jar /server/fapi-test-suite.jar
|
||||
-Djdk.tls.maxHandshakeMessageSize=65536
|
||||
--fintechlabs.base_url=https://host.docker.internal:8443
|
||||
--fintechlabs.base_mtls_url=https://host.docker.internal:8444
|
||||
--fintechlabs.base_url=https://localhost:8443
|
||||
--fintechlabs.base_mtls_url=https://localhost:8444
|
||||
--fintechlabs.devmode=true
|
||||
--fintechlabs.startredir=true
|
||||
links:
|
||||
|
||||
+3
-2
@@ -6,5 +6,6 @@ class TestOpenIDConformanceBasic(TestOpenIDConformance):
|
||||
|
||||
@retry()
|
||||
def test_oidcc_basic_certification_test(self):
|
||||
test_plan_name = "oidcc-basic-certification-test-plan"
|
||||
self.run_test(test_plan_name, self.test_plan_config)
|
||||
self.run_test(
|
||||
"oidcc-basic-certification-test-plan", self.test_plan_config, self.test_variant
|
||||
)
|
||||
+3
-2
@@ -6,5 +6,6 @@ class TestOpenIDConformanceImplicit(TestOpenIDConformance):
|
||||
|
||||
@retry()
|
||||
def test_oidcc_implicit_certification_test_plan(self):
|
||||
test_plan_name = "oidcc-implicit-certification-test-plan"
|
||||
self.run_test(test_plan_name, self.test_plan_config)
|
||||
self.run_test(
|
||||
"oidcc-implicit-certification-test-plan", self.test_plan_config, self.test_variant
|
||||
)
|
||||
@@ -0,0 +1,49 @@
|
||||
from authentik.core.models import Application
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.enterprise.providers.ssf.models import SSFProvider
|
||||
from authentik.lib.generators import generate_id
|
||||
from tests.decorators import retry
|
||||
from tests.live import SSLLiveMixin
|
||||
from tests.openid_conformance.base import TestOpenIDConformance
|
||||
|
||||
|
||||
class TestOpenIDConformanceSSFTransmitter(TestOpenIDConformance, SSLLiveMixin):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.provider = SSFProvider.objects.create(
|
||||
name=generate_id(),
|
||||
signing_key=CertificateKeyPair.objects.get(name="authentik Self-signed Certificate"),
|
||||
backchannel_application=Application.objects.get(slug="oidc-conformance-1"),
|
||||
push_verify_certificates=False,
|
||||
)
|
||||
|
||||
@retry()
|
||||
def test_openid_ssf_transmitter_test_plan(self):
|
||||
iss = self.url(
|
||||
"authentik_providers_ssf:configuration",
|
||||
application_slug="oidc-conformance-1",
|
||||
)
|
||||
self.run_test(
|
||||
"openid-ssf-transmitter-test-plan",
|
||||
{
|
||||
"alias": "authentik",
|
||||
"description": "authentik",
|
||||
"ssf": {
|
||||
"transmitter": {
|
||||
"issuer": iss,
|
||||
"configuration_metadata_endpoint": iss,
|
||||
"access_token": self.provider.token.key,
|
||||
}
|
||||
},
|
||||
},
|
||||
test_variant={
|
||||
"client_auth_type": "client_secret_post",
|
||||
"ssf_server_metadata": "static",
|
||||
"server_metadata": "static",
|
||||
"ssf_auth_mode": "static",
|
||||
"ssf_delivery_mode": "push",
|
||||
"ssf_profile": "caep_interop",
|
||||
"client_registration": "static_client",
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user