mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
enterprise/providers/ssf: test conformance (#21383)
* bump conformance server Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add support for rfc push Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make format and aud optional Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix some endpoints Signed-off-by: Jens Langhammer <jens@goauthentik.io> * force 401 Signed-off-by: Jens Langhammer <jens@goauthentik.io> * implement get and patch for streams Signed-off-by: Jens Langhammer <jens@goauthentik.io> * cleanup Signed-off-by: Jens Langhammer <jens@goauthentik.io> * enable async stream deletion Signed-off-by: Jens Langhammer <jens@goauthentik.io> * allow configuring remote certificate validation Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add verification endpoint Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add support for authorization_header Signed-off-by: Jens Langhammer <jens@goauthentik.io> * set default aud cause spec cant agree with itself Signed-off-by: Jens Langhammer <jens@goauthentik.io> * bump timeout Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix header `typ` Signed-off-by: Jens Langhammer <jens@goauthentik.io> * enabled -> status Signed-off-by: Jens Langhammer <jens@goauthentik.io> * re-migrate Signed-off-by: Jens Langhammer <jens@goauthentik.io> * gen Signed-off-by: Jens Langhammer <jens@goauthentik.io> * more tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * more tests and a fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make streams deletable Signed-off-by: Jens Langhammer <jens@goauthentik.io> * and more logs and fix a silly bug Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add stream status endpoint Signed-off-by: Jens Langhammer <jens@goauthentik.io> * move ssf out of preview Signed-off-by: Jens Langhammer <jens@goauthentik.io> * unrelated typing fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * format Signed-off-by: Jens Langhammer <jens@goauthentik.io> * sigh Signed-off-by: Jens Langhammer <jens@goauthentik.io> * more tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@@ -796,11 +796,11 @@ class Application(SerializerModel, PolicyBindingModel):
|
||||
|
||||
def backchannel_provider_for[T: Provider](self, provider_type: type[T], **kwargs) -> T | None:
|
||||
"""Get Backchannel provider for a specific type"""
|
||||
providers = self.backchannel_providers.filter(
|
||||
provider: BackchannelProvider | None = self.backchannel_providers.filter(
|
||||
**{f"{provider_type._meta.model_name}__isnull": False},
|
||||
**kwargs,
|
||||
)
|
||||
return getattr(providers.first(), provider_type._meta.model_name)
|
||||
).first()
|
||||
return getattr(provider, provider_type._meta.model_name) if provider else None
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name)
|
||||
|
||||
@@ -52,6 +52,7 @@ class SSFProviderSerializer(EnterpriseRequiredMixin, ProviderSerializer):
|
||||
"oidc_auth_providers_obj",
|
||||
"ssf_url",
|
||||
"event_retention",
|
||||
"push_verify_certificates",
|
||||
]
|
||||
extra_kwargs = {}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""SSF Stream API Views"""
|
||||
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
from rest_framework import mixins
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
from authentik.enterprise.providers.ssf.api.providers import SSFProviderSerializer
|
||||
@@ -16,6 +17,7 @@ class SSFStreamSerializer(ModelSerializer):
|
||||
model = Stream
|
||||
fields = [
|
||||
"pk",
|
||||
"status",
|
||||
"provider",
|
||||
"provider_obj",
|
||||
"delivery_method",
|
||||
@@ -27,7 +29,12 @@ class SSFStreamSerializer(ModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class SSFStreamViewSet(ReadOnlyModelViewSet):
|
||||
class SSFStreamViewSet(
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
GenericViewSet,
|
||||
):
|
||||
"""SSFStream Viewset"""
|
||||
|
||||
queryset = Stream.objects.all()
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
# Generated by Django 5.2.12 on 2026-04-04 16:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_providers_ssf", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="ssfprovider",
|
||||
name="push_verify_certificates",
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="stream",
|
||||
name="authorization_header",
|
||||
field=models.TextField(default=None, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="stream",
|
||||
name="status",
|
||||
field=models.TextField(
|
||||
choices=[("enabled", "Enabled"), ("paused", "Paused"), ("disabled", "Disabled")],
|
||||
default="enabled",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="stream",
|
||||
name="delivery_method",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
("https://schemas.openid.net/secevent/risc/delivery-method/push", "Risc Push"),
|
||||
("https://schemas.openid.net/secevent/risc/delivery-method/poll", "Risc Poll"),
|
||||
("urn:ietf:rfc:8935", "SSF RFC Push"),
|
||||
("urn:ietf:rfc:8936", "SSF RFC Pull"),
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -33,6 +33,8 @@ class DeliveryMethods(models.TextChoices):
|
||||
|
||||
RISC_PUSH = "https://schemas.openid.net/secevent/risc/delivery-method/push"
|
||||
RISC_POLL = "https://schemas.openid.net/secevent/risc/delivery-method/poll"
|
||||
RFC_PUSH = "urn:ietf:rfc:8935", _("SSF RFC Push")
|
||||
RFC_PULL = "urn:ietf:rfc:8936", _("SSF RFC Pull")
|
||||
|
||||
|
||||
class SSFEventStatus(models.TextChoices):
|
||||
@@ -43,6 +45,13 @@ class SSFEventStatus(models.TextChoices):
|
||||
SENT = "sent"
|
||||
|
||||
|
||||
class StreamStatus(models.TextChoices):
|
||||
|
||||
ENABLED = "enabled"
|
||||
PAUSED = "paused"
|
||||
DISABLED = "disabled"
|
||||
|
||||
|
||||
class SSFProvider(TasksModel, BackchannelProvider):
|
||||
"""Shared Signals Framework provider to allow applications to
|
||||
receive user events from authentik."""
|
||||
@@ -54,6 +63,8 @@ class SSFProvider(TasksModel, BackchannelProvider):
|
||||
help_text=_("Key used to sign the SSF Events."),
|
||||
)
|
||||
|
||||
push_verify_certificates = models.BooleanField(default=True)
|
||||
|
||||
oidc_auth_providers = models.ManyToManyField(OAuth2Provider, blank=True, default=None)
|
||||
|
||||
token = models.ForeignKey(Token, on_delete=models.CASCADE, null=True, default=None)
|
||||
@@ -106,10 +117,14 @@ class Stream(models.Model):
|
||||
"""SSF Stream"""
|
||||
|
||||
uuid = models.UUIDField(default=uuid4, primary_key=True, editable=False)
|
||||
|
||||
status = models.TextField(choices=StreamStatus.choices, default=StreamStatus.ENABLED)
|
||||
|
||||
provider = models.ForeignKey(SSFProvider, on_delete=models.CASCADE)
|
||||
|
||||
delivery_method = models.TextField(choices=DeliveryMethods.choices)
|
||||
endpoint_url = models.TextField(null=True)
|
||||
authorization_header = models.TextField(null=True, default=None)
|
||||
|
||||
events_requested = ArrayField(models.TextField(choices=EventTypes.choices), default=list)
|
||||
format = models.TextField()
|
||||
@@ -146,7 +161,7 @@ class Stream(models.Model):
|
||||
}
|
||||
|
||||
def encode(self, data: dict) -> str:
|
||||
headers = {}
|
||||
headers = {"typ": "secevent+jwt"}
|
||||
if self.provider.signing_key:
|
||||
headers["kid"] = self.provider.signing_key.kid
|
||||
key, alg = self.provider.jwt_key
|
||||
|
||||
@@ -16,6 +16,7 @@ from authentik.enterprise.providers.ssf.models import (
|
||||
SSFEventStatus,
|
||||
Stream,
|
||||
StreamEvent,
|
||||
StreamStatus,
|
||||
)
|
||||
from authentik.lib.utils.http import get_http_session
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
@@ -88,23 +89,42 @@ def send_ssf_event(stream_uuid: UUID, event_data: dict[str, Any]):
|
||||
self.set_uid(event.pk)
|
||||
if event.status == SSFEventStatus.SENT:
|
||||
return
|
||||
if stream.delivery_method != DeliveryMethods.RISC_PUSH:
|
||||
if stream.delivery_method not in [DeliveryMethods.RISC_PUSH, DeliveryMethods.RFC_PUSH]:
|
||||
return
|
||||
|
||||
headers = {"Content-Type": "application/secevent+jwt", "Accept": "application/json"}
|
||||
if stream.authorization_header:
|
||||
headers["Authorization"] = stream.authorization_header
|
||||
try:
|
||||
response = session.post(
|
||||
event.stream.endpoint_url,
|
||||
data=event.stream.encode(event.payload),
|
||||
headers={"Content-Type": "application/secevent+jwt", "Accept": "application/json"},
|
||||
headers=headers,
|
||||
verify=stream.provider.push_verify_certificates,
|
||||
timeout=180,
|
||||
)
|
||||
response.raise_for_status()
|
||||
event.status = SSFEventStatus.SENT
|
||||
event.save()
|
||||
return
|
||||
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
|
||||
if (
|
||||
not StreamEvent.objects.filter(
|
||||
stream=stream,
|
||||
status__in=[SSFEventStatus.PENDING_FAILED, SSFEventStatus.PENDING_NEW],
|
||||
).exists()
|
||||
and stream.status == StreamStatus.DISABLED
|
||||
):
|
||||
LOGGER.info(
|
||||
"Deleting inactive stream as all pending messages were sent.", stream=stream
|
||||
)
|
||||
self.info("Deleting inactive stream as all pending messages were sent.")
|
||||
stream.delete()
|
||||
except RequestException as exc:
|
||||
LOGGER.warning("Failed to send SSF event", exc=exc)
|
||||
LOGGER.warning("Failed to send SSF event", exc=exc, stream=stream)
|
||||
attrs = {}
|
||||
if exc.response:
|
||||
if exc.response is not None:
|
||||
attrs["response"] = {
|
||||
"content": exc.response.text,
|
||||
"status": exc.response.status_code,
|
||||
@@ -113,5 +133,6 @@ def send_ssf_event(stream_uuid: UUID, event_data: dict[str, Any]):
|
||||
self.warning("Failed to send request", **attrs)
|
||||
# Re-up the expiry of the stream event
|
||||
event.expires = now() + timedelta_from_string(event.stream.provider.event_retention)
|
||||
self.info(f"Event will be re-sent at {event.expires}")
|
||||
event.status = SSFEventStatus.PENDING_FAILED
|
||||
event.save()
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
import json
|
||||
from dataclasses import asdict
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.core.models import Application, Token, TokenIntents
|
||||
from authentik.core.tests.utils import (
|
||||
create_test_admin_user,
|
||||
create_test_cert,
|
||||
create_test_flow,
|
||||
create_test_user,
|
||||
)
|
||||
from authentik.enterprise.providers.ssf.models import (
|
||||
SSFEventStatus,
|
||||
SSFProvider,
|
||||
Stream,
|
||||
StreamEvent,
|
||||
)
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.providers.oauth2.id_token import IDToken
|
||||
from authentik.providers.oauth2.models import AccessToken, OAuth2Provider
|
||||
|
||||
|
||||
class TestSSFAuth(APITestCase):
|
||||
def setUp(self):
|
||||
self.application = Application.objects.create(name=generate_id(), slug=generate_id())
|
||||
self.provider = SSFProvider.objects.create(
|
||||
name=generate_id(),
|
||||
signing_key=create_test_cert(),
|
||||
backchannel_application=self.application,
|
||||
)
|
||||
|
||||
def test_stream_add_token(self):
|
||||
"""test stream add (token auth)"""
|
||||
res = self.client.post(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
data={
|
||||
"iss": "https://authentik.company/.well-known/ssf-configuration/foo/5",
|
||||
"aud": ["https://app.authentik.company"],
|
||||
"delivery": {
|
||||
"method": "https://schemas.openid.net/secevent/risc/delivery-method/push",
|
||||
"endpoint_url": "https://app.authentik.company",
|
||||
},
|
||||
"events_requested": [
|
||||
"https://schemas.openid.net/secevent/caep/event-type/credential-change",
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
|
||||
],
|
||||
"format": "iss_sub",
|
||||
},
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
stream = Stream.objects.filter(provider=self.provider).first()
|
||||
self.assertIsNotNone(stream)
|
||||
event = StreamEvent.objects.filter(stream=stream).first()
|
||||
self.assertIsNotNone(event)
|
||||
self.assertEqual(event.status, SSFEventStatus.PENDING_FAILED)
|
||||
self.assertEqual(
|
||||
event.payload["events"],
|
||||
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {"state": None}},
|
||||
)
|
||||
|
||||
def test_stream_add_oidc(self):
|
||||
"""test stream add (oidc auth)"""
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name=generate_id(),
|
||||
authorization_flow=create_test_flow(),
|
||||
)
|
||||
self.application.provider = provider
|
||||
self.application.save()
|
||||
user = create_test_admin_user()
|
||||
token = AccessToken.objects.create(
|
||||
provider=provider,
|
||||
user=user,
|
||||
token=generate_id(),
|
||||
auth_time=timezone.now(),
|
||||
_scope="openid user profile",
|
||||
_id_token=json.dumps(
|
||||
asdict(
|
||||
IDToken("foo", "bar"),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
res = self.client.post(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
data={
|
||||
"iss": "https://authentik.company/.well-known/ssf-configuration/foo/5",
|
||||
"aud": ["https://app.authentik.company"],
|
||||
"delivery": {
|
||||
"method": "https://schemas.openid.net/secevent/risc/delivery-method/push",
|
||||
"endpoint_url": "https://app.authentik.company",
|
||||
},
|
||||
"events_requested": [
|
||||
"https://schemas.openid.net/secevent/caep/event-type/credential-change",
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
|
||||
],
|
||||
"format": "iss_sub",
|
||||
},
|
||||
HTTP_AUTHORIZATION=f"Bearer {token.token}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
stream = Stream.objects.filter(provider=self.provider).first()
|
||||
self.assertIsNotNone(stream)
|
||||
event = StreamEvent.objects.filter(stream=stream).first()
|
||||
self.assertIsNotNone(event)
|
||||
self.assertEqual(event.status, SSFEventStatus.PENDING_FAILED)
|
||||
self.assertEqual(
|
||||
event.payload["events"],
|
||||
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {"state": None}},
|
||||
)
|
||||
|
||||
def test_token_invalid(self):
|
||||
res = self.client.post(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
data={
|
||||
"iss": "https://authentik.company/.well-known/ssf-configuration/foo/5",
|
||||
"aud": ["https://app.authentik.company"],
|
||||
"delivery": {
|
||||
"method": "https://schemas.openid.net/secevent/risc/delivery-method/push",
|
||||
"endpoint_url": "https://app.authentik.company",
|
||||
},
|
||||
"events_requested": [
|
||||
"https://schemas.openid.net/secevent/caep/event-type/credential-change",
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
|
||||
],
|
||||
"format": "iss_sub",
|
||||
},
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}a",
|
||||
)
|
||||
# Response code needs to be 401 according to spec
|
||||
self.assertEqual(res.status_code, 401)
|
||||
|
||||
def test_token_unrelated(self):
|
||||
token = Token.objects.create(
|
||||
identifier=generate_id(), user=create_test_user(), intent=TokenIntents.INTENT_API
|
||||
)
|
||||
res = self.client.post(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
data={
|
||||
"iss": "https://authentik.company/.well-known/ssf-configuration/foo/5",
|
||||
"aud": ["https://app.authentik.company"],
|
||||
"delivery": {
|
||||
"method": "https://schemas.openid.net/secevent/risc/delivery-method/push",
|
||||
"endpoint_url": "https://app.authentik.company",
|
||||
},
|
||||
"events_requested": [
|
||||
"https://schemas.openid.net/secevent/caep/event-type/credential-change",
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
|
||||
],
|
||||
"format": "iss_sub",
|
||||
},
|
||||
HTTP_AUTHORIZATION=f"Bearer {token.key}",
|
||||
)
|
||||
# Response code needs to be 401 according to spec
|
||||
self.assertEqual(res.status_code, 401)
|
||||
@@ -44,3 +44,15 @@ class TestConfiguration(APITestCase):
|
||||
self.assertEqual(res.status_code, 200)
|
||||
content = json.loads(res.content)
|
||||
self.assertEqual(content["spec_version"], "1_0-ID2")
|
||||
|
||||
def test_config_not_found(self):
|
||||
"""test SSF configuration (authenticated)"""
|
||||
self.provider.delete()
|
||||
res = self.client.get(
|
||||
reverse(
|
||||
"authentik_providers_ssf:configuration",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import json
|
||||
from dataclasses import asdict
|
||||
from uuid import uuid4
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.core.models import Application
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
|
||||
from authentik.core.tests.utils import create_test_cert
|
||||
from authentik.enterprise.providers.ssf.models import (
|
||||
SSFEventStatus,
|
||||
SSFProvider,
|
||||
Stream,
|
||||
StreamEvent,
|
||||
StreamStatus,
|
||||
)
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.providers.oauth2.id_token import IDToken
|
||||
from authentik.providers.oauth2.models import AccessToken, OAuth2Provider
|
||||
|
||||
|
||||
class TestStream(APITestCase):
|
||||
@@ -87,29 +84,71 @@ class TestStream(APITestCase):
|
||||
{"delivery": {"method": ["Polling for SSF events is not currently supported."]}},
|
||||
)
|
||||
|
||||
def test_stream_add_oidc(self):
|
||||
"""test stream add (oidc auth)"""
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name=generate_id(),
|
||||
authorization_flow=create_test_flow(),
|
||||
)
|
||||
self.application.provider = provider
|
||||
self.application.save()
|
||||
user = create_test_admin_user()
|
||||
token = AccessToken.objects.create(
|
||||
provider=provider,
|
||||
user=user,
|
||||
token=generate_id(),
|
||||
auth_time=timezone.now(),
|
||||
_scope="openid user profile",
|
||||
_id_token=json.dumps(
|
||||
asdict(
|
||||
IDToken("foo", "bar"),
|
||||
)
|
||||
def test_stream_delete(self):
|
||||
"""delete stream"""
|
||||
stream = Stream.objects.create(provider=self.provider)
|
||||
res = self.client.delete(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 204)
|
||||
stream.refresh_from_db()
|
||||
self.assertEqual(stream.status, StreamStatus.DISABLED)
|
||||
|
||||
res = self.client.post(
|
||||
def test_stream_get(self):
|
||||
"""get stream"""
|
||||
Stream.objects.create(provider=self.provider)
|
||||
res = self.client.get(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_stream_get_filter_query(self):
|
||||
"""get stream"""
|
||||
other_stream = Stream.objects.create(provider=self.provider)
|
||||
stream = Stream.objects.create(provider=self.provider)
|
||||
res = self.client.get(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
)
|
||||
+ f"?stream_id={stream.pk}",
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertIn(str(stream.pk), res.content.decode())
|
||||
self.assertNotIn(str(other_stream.pk), res.content.decode())
|
||||
|
||||
def test_stream_patch(self):
|
||||
"""patch stream"""
|
||||
other_stream = Stream.objects.create(provider=self.provider)
|
||||
stream = Stream.objects.create(provider=self.provider)
|
||||
res = self.client.patch(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
data={
|
||||
"delivery": {"endpoint_url": "https://localhost"},
|
||||
"stream_id": str(stream.pk),
|
||||
},
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertIn(str(stream.pk), res.content.decode())
|
||||
self.assertNotIn(str(other_stream.pk), res.content.decode())
|
||||
|
||||
def test_stream_put(self):
|
||||
"""put stream"""
|
||||
stream = Stream.objects.create(provider=self.provider)
|
||||
res = self.client.put(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
@@ -126,29 +165,63 @@ class TestStream(APITestCase):
|
||||
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
|
||||
],
|
||||
"format": "iss_sub",
|
||||
"stream_id": str(stream.pk),
|
||||
},
|
||||
HTTP_AUTHORIZATION=f"Bearer {token.token}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
stream = Stream.objects.filter(provider=self.provider).first()
|
||||
self.assertIsNotNone(stream)
|
||||
event = StreamEvent.objects.filter(stream=stream).first()
|
||||
self.assertIsNotNone(event)
|
||||
self.assertEqual(event.status, SSFEventStatus.PENDING_FAILED)
|
||||
self.assertEqual(
|
||||
event.payload["events"],
|
||||
{"https://schemas.openid.net/secevent/ssf/event-type/verification": {"state": None}},
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertIn(str(stream.pk), res.content.decode())
|
||||
stream.refresh_from_db()
|
||||
self.assertEqual(stream.aud, ["https://app.authentik.company"])
|
||||
|
||||
def test_stream_delete(self):
|
||||
"""delete stream"""
|
||||
def test_stream_verify(self):
|
||||
"""Test stream verify"""
|
||||
stream = Stream.objects.create(provider=self.provider)
|
||||
res = self.client.delete(
|
||||
res = self.client.post(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream",
|
||||
"authentik_providers_ssf:stream-verify",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
data={
|
||||
"stream_id": str(stream.pk),
|
||||
},
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 204)
|
||||
self.assertFalse(Stream.objects.filter(pk=stream.pk).exists())
|
||||
|
||||
def test_stream_status(self):
|
||||
"""Test stream status"""
|
||||
stream = Stream.objects.create(provider=self.provider)
|
||||
res = self.client.get(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream-status",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
data={
|
||||
"stream_id": str(stream.pk),
|
||||
},
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertJSONEqual(
|
||||
res.content,
|
||||
{
|
||||
"stream_id": str(stream.pk),
|
||||
"status": str(stream.status),
|
||||
},
|
||||
)
|
||||
|
||||
def test_stream_status_not_found(self):
|
||||
"""Test stream status"""
|
||||
Stream.objects.create(provider=self.provider)
|
||||
res = self.client.get(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream-status",
|
||||
kwargs={"application_slug": self.application.slug},
|
||||
),
|
||||
data={
|
||||
"stream_id": str(uuid4()),
|
||||
},
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.provider.token.key}",
|
||||
)
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
from jwt import decode_complete
|
||||
from requests_mock import Mocker
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.core.models import Application
|
||||
from authentik.core.tests.utils import create_test_cert
|
||||
from authentik.enterprise.providers.ssf.models import (
|
||||
DeliveryMethods,
|
||||
EventTypes,
|
||||
SSFProvider,
|
||||
Stream,
|
||||
StreamStatus,
|
||||
)
|
||||
from authentik.enterprise.providers.ssf.tasks import send_ssf_event
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.tasks.models import TaskLog
|
||||
|
||||
|
||||
class TestTasks(APITestCase):
|
||||
def setUp(self):
|
||||
self.application = Application.objects.create(name=generate_id(), slug=generate_id())
|
||||
self.provider = SSFProvider.objects.create(
|
||||
name=generate_id(),
|
||||
signing_key=create_test_cert(),
|
||||
backchannel_application=self.application,
|
||||
)
|
||||
|
||||
def test_push_simple(self):
|
||||
stream = Stream.objects.create(
|
||||
provider=self.provider,
|
||||
delivery_method=DeliveryMethods.RFC_PUSH,
|
||||
endpoint_url="http://localhost/ssf-push",
|
||||
)
|
||||
event_data = stream.prepare_event_payload(
|
||||
EventTypes.SET_VERIFICATION,
|
||||
{"state": None},
|
||||
sub_id={"format": "opaque", "id": str(stream.uuid)},
|
||||
)
|
||||
with Mocker() as mocker:
|
||||
mocker.post("http://localhost/ssf-push", status_code=202)
|
||||
send_ssf_event.send_with_options(
|
||||
args=(stream.pk, event_data), rel_obj=stream.provider
|
||||
).get_result(block=True, timeout=1)
|
||||
self.assertEqual(
|
||||
mocker.request_history[0].headers["Content-Type"], "application/secevent+jwt"
|
||||
)
|
||||
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"])
|
||||
|
||||
def test_push_auth(self):
|
||||
auth = generate_id()
|
||||
stream = Stream.objects.create(
|
||||
provider=self.provider,
|
||||
delivery_method=DeliveryMethods.RFC_PUSH,
|
||||
endpoint_url="http://localhost/ssf-push",
|
||||
authorization_header=auth,
|
||||
)
|
||||
event_data = stream.prepare_event_payload(
|
||||
EventTypes.SET_VERIFICATION,
|
||||
{"state": None},
|
||||
sub_id={"format": "opaque", "id": str(stream.uuid)},
|
||||
)
|
||||
with Mocker() as mocker:
|
||||
mocker.post("http://localhost/ssf-push", status_code=202)
|
||||
send_ssf_event.send_with_options(
|
||||
args=(stream.pk, event_data), rel_obj=stream.provider
|
||||
).get_result(block=True, timeout=1)
|
||||
self.assertEqual(mocker.request_history[0].headers["Authorization"], auth)
|
||||
self.assertEqual(
|
||||
mocker.request_history[0].headers["Content-Type"], "application/secevent+jwt"
|
||||
)
|
||||
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"])
|
||||
|
||||
def test_push_stream_disable(self):
|
||||
auth = generate_id()
|
||||
stream = Stream.objects.create(
|
||||
provider=self.provider,
|
||||
delivery_method=DeliveryMethods.RFC_PUSH,
|
||||
endpoint_url="http://localhost/ssf-push",
|
||||
authorization_header=auth,
|
||||
status=StreamStatus.DISABLED,
|
||||
)
|
||||
event_data = stream.prepare_event_payload(
|
||||
EventTypes.SET_VERIFICATION,
|
||||
{"state": None},
|
||||
sub_id={"format": "opaque", "id": str(stream.uuid)},
|
||||
)
|
||||
with Mocker() as mocker:
|
||||
mocker.post("http://localhost/ssf-push", status_code=202)
|
||||
send_ssf_event.send_with_options(
|
||||
args=(stream.pk, event_data), rel_obj=stream.provider
|
||||
).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.assertFalse(Stream.objects.filter(pk=stream.pk).exists())
|
||||
|
||||
def test_push_error(self):
|
||||
stream = Stream.objects.create(
|
||||
provider=self.provider,
|
||||
delivery_method=DeliveryMethods.RFC_PUSH,
|
||||
endpoint_url="http://localhost/ssf-push",
|
||||
)
|
||||
event_data = stream.prepare_event_payload(
|
||||
EventTypes.SET_VERIFICATION,
|
||||
{"state": None},
|
||||
sub_id={"format": "opaque", "id": str(stream.uuid)},
|
||||
)
|
||||
with Mocker() as mocker:
|
||||
mocker.post("http://localhost/ssf-push", text="error", status_code=400)
|
||||
send_ssf_event.send_with_options(
|
||||
args=(stream.pk, event_data), rel_obj=stream.provider
|
||||
).get_result(block=True, timeout=1)
|
||||
logs = (
|
||||
TaskLog.objects.filter(task__actor_name=send_ssf_event.actor_name)
|
||||
.order_by("timestamp")
|
||||
.filter(event="Failed to send request")
|
||||
.first()
|
||||
)
|
||||
self.assertEqual(logs.attributes, {"response": {"status": 400, "content": "error"}})
|
||||
@@ -6,7 +6,11 @@ from authentik.enterprise.providers.ssf.api.providers import SSFProviderViewSet
|
||||
from authentik.enterprise.providers.ssf.api.streams import SSFStreamViewSet
|
||||
from authentik.enterprise.providers.ssf.views.configuration import ConfigurationView
|
||||
from authentik.enterprise.providers.ssf.views.jwks import JWKSview
|
||||
from authentik.enterprise.providers.ssf.views.stream import StreamView
|
||||
from authentik.enterprise.providers.ssf.views.stream import (
|
||||
StreamStatusView,
|
||||
StreamVerifyView,
|
||||
StreamView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
@@ -24,6 +28,16 @@ urlpatterns = [
|
||||
StreamView.as_view(),
|
||||
name="stream",
|
||||
),
|
||||
path(
|
||||
"application/ssf/<slug:application_slug>/stream/verify/",
|
||||
StreamVerifyView.as_view(),
|
||||
name="stream-verify",
|
||||
),
|
||||
path(
|
||||
"application/ssf/<slug:application_slug>/stream/status/",
|
||||
StreamStatusView.as_view(),
|
||||
name="stream-status",
|
||||
),
|
||||
]
|
||||
|
||||
api_urlpatterns = [
|
||||
|
||||
@@ -64,3 +64,7 @@ class SSFTokenAuth(BaseAuthentication):
|
||||
if jwt_token:
|
||||
return (jwt_token.user, token)
|
||||
return None
|
||||
|
||||
# Required to correctly propagate a 401 header which the SSF spec requires
|
||||
def authenticate_header(self, request):
|
||||
return "SSF"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from django.http import HttpRequest
|
||||
from django.http import Http404, HttpRequest
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.views import APIView
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
|
||||
from authentik.core.models import Application
|
||||
from authentik.enterprise.providers.ssf.models import SSFProvider
|
||||
from authentik.enterprise.providers.ssf.models import SSFProvider, Stream, StreamStatus
|
||||
from authentik.enterprise.providers.ssf.views.auth import SSFTokenAuth
|
||||
|
||||
|
||||
@@ -21,3 +21,18 @@ class SSFView(APIView):
|
||||
|
||||
def get_authenticators(self):
|
||||
return [SSFTokenAuth(self)]
|
||||
|
||||
|
||||
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])
|
||||
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:
|
||||
streams = streams.filter(pk=self.request.data["stream_id"])
|
||||
stream = streams.first()
|
||||
if not stream:
|
||||
raise Http404()
|
||||
return stream
|
||||
|
||||
@@ -47,9 +47,23 @@ class ConfigurationView(SSFView):
|
||||
},
|
||||
)
|
||||
),
|
||||
"delivery_methods_supported": [
|
||||
DeliveryMethods.RISC_PUSH,
|
||||
],
|
||||
"verification_endpoint": self.request.build_absolute_uri(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream-verify",
|
||||
kwargs={
|
||||
"application_slug": application.slug,
|
||||
},
|
||||
)
|
||||
),
|
||||
"status_endpoint": self.request.build_absolute_uri(
|
||||
reverse(
|
||||
"authentik_providers_ssf:stream-status",
|
||||
kwargs={
|
||||
"application_slug": application.slug,
|
||||
},
|
||||
)
|
||||
),
|
||||
"delivery_methods_supported": [DeliveryMethods.RISC_PUSH, DeliveryMethods.RFC_PUSH],
|
||||
"authorization_schemes": [{"spec_urn": "urn:ietf:rfc:6749"}],
|
||||
}
|
||||
return JsonResponse(data)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from uuid import uuid4
|
||||
|
||||
from django.http import HttpRequest
|
||||
from django.urls import reverse
|
||||
from rest_framework.exceptions import PermissionDenied, ValidationError
|
||||
@@ -13,9 +15,10 @@ from authentik.enterprise.providers.ssf.models import (
|
||||
EventTypes,
|
||||
SSFProvider,
|
||||
Stream,
|
||||
StreamStatus,
|
||||
)
|
||||
from authentik.enterprise.providers.ssf.tasks import send_ssf_events
|
||||
from authentik.enterprise.providers.ssf.views.base import SSFView
|
||||
from authentik.enterprise.providers.ssf.views.base import SSFStreamView
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
@@ -23,6 +26,7 @@ LOGGER = get_logger()
|
||||
class StreamDeliverySerializer(PassiveSerializer):
|
||||
method = ChoiceField(choices=[(x.value, x.value) for x in DeliveryMethods])
|
||||
endpoint_url = CharField(required=False)
|
||||
authorization_header = CharField(required=False)
|
||||
|
||||
def validate_method(self, method: DeliveryMethods):
|
||||
"""Currently only push is supported"""
|
||||
@@ -31,7 +35,7 @@ class StreamDeliverySerializer(PassiveSerializer):
|
||||
return method
|
||||
|
||||
def validate(self, attrs: dict) -> dict:
|
||||
if attrs["method"] == DeliveryMethods.RISC_PUSH:
|
||||
if attrs.get("method") in [DeliveryMethods.RISC_PUSH, DeliveryMethods.RFC_PUSH]:
|
||||
if not attrs.get("endpoint_url"):
|
||||
raise ValidationError("Endpoint URL is required when using push.")
|
||||
return attrs
|
||||
@@ -42,8 +46,8 @@ class StreamSerializer(ModelSerializer):
|
||||
events_requested = ListField(
|
||||
child=ChoiceField(choices=[(x.value, x.value) for x in EventTypes])
|
||||
)
|
||||
format = CharField()
|
||||
aud = ListField(child=CharField())
|
||||
format = CharField(default="iss_sub")
|
||||
aud = ListField(child=CharField(), allow_empty=True, default=list)
|
||||
|
||||
def create(self, validated_data):
|
||||
provider: SSFProvider = validated_data["provider"]
|
||||
@@ -58,15 +62,19 @@ class StreamSerializer(ModelSerializer):
|
||||
)
|
||||
# Ensure that streams always get SET verification events sent to them
|
||||
validated_data["events_requested"].append(EventTypes.SET_VERIFICATION)
|
||||
stream_id = uuid4()
|
||||
default_aud = f"goauthentik.io/providers/ssf/{str(stream_id)}"
|
||||
return super().create(
|
||||
{
|
||||
"delivery_method": validated_data["delivery"]["method"],
|
||||
"endpoint_url": validated_data["delivery"].get("endpoint_url"),
|
||||
"authorization_header": validated_data["delivery"].get("authorization_header"),
|
||||
"format": validated_data["format"],
|
||||
"provider": validated_data["provider"],
|
||||
"events_requested": validated_data["events_requested"],
|
||||
"aud": validated_data["aud"],
|
||||
"aud": validated_data["aud"] or [default_aud],
|
||||
"iss": iss,
|
||||
"pk": stream_id,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -101,7 +109,14 @@ class StreamResponseSerializer(PassiveSerializer):
|
||||
return [x.value for x in EventTypes]
|
||||
|
||||
|
||||
class StreamView(SSFView):
|
||||
class StreamView(SSFStreamView):
|
||||
|
||||
def get(self, request: Request, *args, **kwargs):
|
||||
stream = self.get_object()
|
||||
return Response(
|
||||
StreamResponseSerializer(instance=stream, context={"request": request}).data
|
||||
)
|
||||
|
||||
@validate(StreamSerializer)
|
||||
def post(self, request: Request, *args, body: StreamSerializer, **kwargs) -> Response:
|
||||
if not request.user.has_perm("authentik_providers_ssf.add_stream", self.provider):
|
||||
@@ -109,6 +124,8 @@ class StreamView(SSFView):
|
||||
"User does not have permission to create stream for this provider."
|
||||
)
|
||||
instance: Stream = body.save(provider=self.provider)
|
||||
|
||||
LOGGER.info("Sending verification event", stream=instance)
|
||||
send_ssf_events(
|
||||
EventTypes.SET_VERIFICATION,
|
||||
{
|
||||
@@ -120,10 +137,56 @@ class StreamView(SSFView):
|
||||
response = StreamResponseSerializer(instance=instance, context={"request": request}).data
|
||||
return Response(response, status=201)
|
||||
|
||||
def patch(self, request: Request, *args, **kwargs) -> Response:
|
||||
stream = self.get_object()
|
||||
serializer = StreamSerializer(stream, data=request.data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
response = StreamResponseSerializer(
|
||||
instance=serializer.instance, context={"request": request}
|
||||
).data
|
||||
return Response(response, status=200)
|
||||
|
||||
def put(self, request: Request, *args, **kwargs) -> Response:
|
||||
stream = self.get_object()
|
||||
serializer = StreamSerializer(stream, data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
response = StreamResponseSerializer(
|
||||
instance=serializer.instance, context={"request": request}
|
||||
).data
|
||||
return Response(response, status=200)
|
||||
|
||||
def delete(self, request: Request, *args, **kwargs) -> Response:
|
||||
streams = Stream.objects.filter(provider=self.provider)
|
||||
# Technically this parameter is required by the spec...
|
||||
if "stream_id" in request.query_params:
|
||||
streams = streams.filter(stream_id=request.query_params["stream_id"])
|
||||
streams.delete()
|
||||
stream = self.get_object()
|
||||
stream.status = StreamStatus.DISABLED
|
||||
stream.save()
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
class StreamVerifyView(SSFStreamView):
|
||||
|
||||
def post(self, request: Request, *args, **kwargs):
|
||||
stream = self.get_object()
|
||||
state = request.data.get("state", None)
|
||||
send_ssf_events(
|
||||
EventTypes.SET_VERIFICATION,
|
||||
{
|
||||
"state": state,
|
||||
},
|
||||
stream_filter={"pk": stream.uuid},
|
||||
sub_id={"format": "opaque", "id": str(stream.uuid)},
|
||||
)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
class StreamStatusView(SSFStreamView):
|
||||
|
||||
def get(self, request: Request, *args, **kwargs):
|
||||
stream = self.get_object(any_status=True)
|
||||
return Response(
|
||||
{
|
||||
"stream_id": str(stream.pk),
|
||||
"status": str(stream.status),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -7507,6 +7507,10 @@
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Event retention"
|
||||
},
|
||||
"push_verify_certificates": {
|
||||
"type": "boolean",
|
||||
"title": "Push verify certificates"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
||||
Generated
+113
@@ -23,6 +23,119 @@ import (
|
||||
// SsfAPIService SsfAPI service
|
||||
type SsfAPIService service
|
||||
|
||||
type ApiSsfStreamsDestroyRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *SsfAPIService
|
||||
uuid string
|
||||
}
|
||||
|
||||
func (r ApiSsfStreamsDestroyRequest) Execute() (*http.Response, error) {
|
||||
return r.ApiService.SsfStreamsDestroyExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
SsfStreamsDestroy Method for SsfStreamsDestroy
|
||||
|
||||
SSFStream Viewset
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@param uuid A UUID string identifying this SSF Stream.
|
||||
@return ApiSsfStreamsDestroyRequest
|
||||
*/
|
||||
func (a *SsfAPIService) SsfStreamsDestroy(ctx context.Context, uuid string) ApiSsfStreamsDestroyRequest {
|
||||
return ApiSsfStreamsDestroyRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
uuid: uuid,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
func (a *SsfAPIService) SsfStreamsDestroyExecute(r ApiSsfStreamsDestroyRequest) (*http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodDelete
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SsfAPIService.SsfStreamsDestroy")
|
||||
if err != nil {
|
||||
return nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/ssf/streams/{uuid}/"
|
||||
localVarPath = strings.Replace(localVarPath, "{"+"uuid"+"}", url.PathEscape(parameterValueToString(r.uuid, "uuid")), -1)
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 400 {
|
||||
var v ValidationError
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 403 {
|
||||
var v GenericError
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
}
|
||||
return localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiSsfStreamsListRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *SsfAPIService
|
||||
|
||||
@@ -23,12 +23,16 @@ type DeliveryMethodEnum string
|
||||
const (
|
||||
DELIVERYMETHODENUM_HTTPS___SCHEMAS_OPENID_NET_SECEVENT_RISC_DELIVERY_METHOD_PUSH DeliveryMethodEnum = "https://schemas.openid.net/secevent/risc/delivery-method/push"
|
||||
DELIVERYMETHODENUM_HTTPS___SCHEMAS_OPENID_NET_SECEVENT_RISC_DELIVERY_METHOD_POLL DeliveryMethodEnum = "https://schemas.openid.net/secevent/risc/delivery-method/poll"
|
||||
DELIVERYMETHODENUM_URN_IETF_RFC_8935 DeliveryMethodEnum = "urn:ietf:rfc:8935"
|
||||
DELIVERYMETHODENUM_URN_IETF_RFC_8936 DeliveryMethodEnum = "urn:ietf:rfc:8936"
|
||||
)
|
||||
|
||||
// All allowed values of DeliveryMethodEnum enum
|
||||
var AllowedDeliveryMethodEnumEnumValues = []DeliveryMethodEnum{
|
||||
"https://schemas.openid.net/secevent/risc/delivery-method/push",
|
||||
"https://schemas.openid.net/secevent/risc/delivery-method/poll",
|
||||
"urn:ietf:rfc:8935",
|
||||
"urn:ietf:rfc:8936",
|
||||
}
|
||||
|
||||
func (v *DeliveryMethodEnum) UnmarshalJSON(src []byte) error {
|
||||
|
||||
+41
-4
@@ -22,10 +22,11 @@ var _ MappedNullable = &PatchedSSFProviderRequest{}
|
||||
type PatchedSSFProviderRequest struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
// Key used to sign the SSF Events.
|
||||
SigningKey *string `json:"signing_key,omitempty"`
|
||||
OidcAuthProviders []int32 `json:"oidc_auth_providers,omitempty"`
|
||||
EventRetention *string `json:"event_retention,omitempty"`
|
||||
AdditionalProperties map[string]interface{}
|
||||
SigningKey *string `json:"signing_key,omitempty"`
|
||||
OidcAuthProviders []int32 `json:"oidc_auth_providers,omitempty"`
|
||||
EventRetention *string `json:"event_retention,omitempty"`
|
||||
PushVerifyCertificates *bool `json:"push_verify_certificates,omitempty"`
|
||||
AdditionalProperties map[string]interface{}
|
||||
}
|
||||
|
||||
type _PatchedSSFProviderRequest PatchedSSFProviderRequest
|
||||
@@ -175,6 +176,38 @@ func (o *PatchedSSFProviderRequest) SetEventRetention(v string) {
|
||||
o.EventRetention = &v
|
||||
}
|
||||
|
||||
// GetPushVerifyCertificates returns the PushVerifyCertificates field value if set, zero value otherwise.
|
||||
func (o *PatchedSSFProviderRequest) GetPushVerifyCertificates() bool {
|
||||
if o == nil || IsNil(o.PushVerifyCertificates) {
|
||||
var ret bool
|
||||
return ret
|
||||
}
|
||||
return *o.PushVerifyCertificates
|
||||
}
|
||||
|
||||
// GetPushVerifyCertificatesOk returns a tuple with the PushVerifyCertificates field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *PatchedSSFProviderRequest) GetPushVerifyCertificatesOk() (*bool, bool) {
|
||||
if o == nil || IsNil(o.PushVerifyCertificates) {
|
||||
return nil, false
|
||||
}
|
||||
return o.PushVerifyCertificates, true
|
||||
}
|
||||
|
||||
// HasPushVerifyCertificates returns a boolean if a field has been set.
|
||||
func (o *PatchedSSFProviderRequest) HasPushVerifyCertificates() bool {
|
||||
if o != nil && !IsNil(o.PushVerifyCertificates) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetPushVerifyCertificates gets a reference to the given bool and assigns it to the PushVerifyCertificates field.
|
||||
func (o *PatchedSSFProviderRequest) SetPushVerifyCertificates(v bool) {
|
||||
o.PushVerifyCertificates = &v
|
||||
}
|
||||
|
||||
func (o PatchedSSFProviderRequest) MarshalJSON() ([]byte, error) {
|
||||
toSerialize, err := o.ToMap()
|
||||
if err != nil {
|
||||
@@ -197,6 +230,9 @@ func (o PatchedSSFProviderRequest) ToMap() (map[string]interface{}, error) {
|
||||
if !IsNil(o.EventRetention) {
|
||||
toSerialize["event_retention"] = o.EventRetention
|
||||
}
|
||||
if !IsNil(o.PushVerifyCertificates) {
|
||||
toSerialize["push_verify_certificates"] = o.PushVerifyCertificates
|
||||
}
|
||||
|
||||
for key, value := range o.AdditionalProperties {
|
||||
toSerialize[key] = value
|
||||
@@ -223,6 +259,7 @@ func (o *PatchedSSFProviderRequest) UnmarshalJSON(data []byte) (err error) {
|
||||
delete(additionalProperties, "signing_key")
|
||||
delete(additionalProperties, "oidc_auth_providers")
|
||||
delete(additionalProperties, "event_retention")
|
||||
delete(additionalProperties, "push_verify_certificates")
|
||||
o.AdditionalProperties = additionalProperties
|
||||
}
|
||||
|
||||
|
||||
Generated
+44
-7
@@ -32,13 +32,14 @@ type SSFProvider struct {
|
||||
// Return internal model name
|
||||
MetaModelName string `json:"meta_model_name"`
|
||||
// Key used to sign the SSF Events.
|
||||
SigningKey string `json:"signing_key"`
|
||||
TokenObj Token `json:"token_obj"`
|
||||
OidcAuthProviders []int32 `json:"oidc_auth_providers,omitempty"`
|
||||
OidcAuthProvidersObj []Provider `json:"oidc_auth_providers_obj"`
|
||||
SsfUrl NullableString `json:"ssf_url"`
|
||||
EventRetention *string `json:"event_retention,omitempty"`
|
||||
AdditionalProperties map[string]interface{}
|
||||
SigningKey string `json:"signing_key"`
|
||||
TokenObj Token `json:"token_obj"`
|
||||
OidcAuthProviders []int32 `json:"oidc_auth_providers,omitempty"`
|
||||
OidcAuthProvidersObj []Provider `json:"oidc_auth_providers_obj"`
|
||||
SsfUrl NullableString `json:"ssf_url"`
|
||||
EventRetention *string `json:"event_retention,omitempty"`
|
||||
PushVerifyCertificates *bool `json:"push_verify_certificates,omitempty"`
|
||||
AdditionalProperties map[string]interface{}
|
||||
}
|
||||
|
||||
type _SSFProvider SSFProvider
|
||||
@@ -376,6 +377,38 @@ func (o *SSFProvider) SetEventRetention(v string) {
|
||||
o.EventRetention = &v
|
||||
}
|
||||
|
||||
// GetPushVerifyCertificates returns the PushVerifyCertificates field value if set, zero value otherwise.
|
||||
func (o *SSFProvider) GetPushVerifyCertificates() bool {
|
||||
if o == nil || IsNil(o.PushVerifyCertificates) {
|
||||
var ret bool
|
||||
return ret
|
||||
}
|
||||
return *o.PushVerifyCertificates
|
||||
}
|
||||
|
||||
// GetPushVerifyCertificatesOk returns a tuple with the PushVerifyCertificates field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *SSFProvider) GetPushVerifyCertificatesOk() (*bool, bool) {
|
||||
if o == nil || IsNil(o.PushVerifyCertificates) {
|
||||
return nil, false
|
||||
}
|
||||
return o.PushVerifyCertificates, true
|
||||
}
|
||||
|
||||
// HasPushVerifyCertificates returns a boolean if a field has been set.
|
||||
func (o *SSFProvider) HasPushVerifyCertificates() bool {
|
||||
if o != nil && !IsNil(o.PushVerifyCertificates) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetPushVerifyCertificates gets a reference to the given bool and assigns it to the PushVerifyCertificates field.
|
||||
func (o *SSFProvider) SetPushVerifyCertificates(v bool) {
|
||||
o.PushVerifyCertificates = &v
|
||||
}
|
||||
|
||||
func (o SSFProvider) MarshalJSON() ([]byte, error) {
|
||||
toSerialize, err := o.ToMap()
|
||||
if err != nil {
|
||||
@@ -402,6 +435,9 @@ func (o SSFProvider) ToMap() (map[string]interface{}, error) {
|
||||
if !IsNil(o.EventRetention) {
|
||||
toSerialize["event_retention"] = o.EventRetention
|
||||
}
|
||||
if !IsNil(o.PushVerifyCertificates) {
|
||||
toSerialize["push_verify_certificates"] = o.PushVerifyCertificates
|
||||
}
|
||||
|
||||
for key, value := range o.AdditionalProperties {
|
||||
toSerialize[key] = value
|
||||
@@ -466,6 +502,7 @@ func (o *SSFProvider) UnmarshalJSON(data []byte) (err error) {
|
||||
delete(additionalProperties, "oidc_auth_providers_obj")
|
||||
delete(additionalProperties, "ssf_url")
|
||||
delete(additionalProperties, "event_retention")
|
||||
delete(additionalProperties, "push_verify_certificates")
|
||||
o.AdditionalProperties = additionalProperties
|
||||
}
|
||||
|
||||
|
||||
+41
-4
@@ -23,10 +23,11 @@ var _ MappedNullable = &SSFProviderRequest{}
|
||||
type SSFProviderRequest struct {
|
||||
Name string `json:"name"`
|
||||
// Key used to sign the SSF Events.
|
||||
SigningKey string `json:"signing_key"`
|
||||
OidcAuthProviders []int32 `json:"oidc_auth_providers,omitempty"`
|
||||
EventRetention *string `json:"event_retention,omitempty"`
|
||||
AdditionalProperties map[string]interface{}
|
||||
SigningKey string `json:"signing_key"`
|
||||
OidcAuthProviders []int32 `json:"oidc_auth_providers,omitempty"`
|
||||
EventRetention *string `json:"event_retention,omitempty"`
|
||||
PushVerifyCertificates *bool `json:"push_verify_certificates,omitempty"`
|
||||
AdditionalProperties map[string]interface{}
|
||||
}
|
||||
|
||||
type _SSFProviderRequest SSFProviderRequest
|
||||
@@ -162,6 +163,38 @@ func (o *SSFProviderRequest) SetEventRetention(v string) {
|
||||
o.EventRetention = &v
|
||||
}
|
||||
|
||||
// GetPushVerifyCertificates returns the PushVerifyCertificates field value if set, zero value otherwise.
|
||||
func (o *SSFProviderRequest) GetPushVerifyCertificates() bool {
|
||||
if o == nil || IsNil(o.PushVerifyCertificates) {
|
||||
var ret bool
|
||||
return ret
|
||||
}
|
||||
return *o.PushVerifyCertificates
|
||||
}
|
||||
|
||||
// GetPushVerifyCertificatesOk returns a tuple with the PushVerifyCertificates field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *SSFProviderRequest) GetPushVerifyCertificatesOk() (*bool, bool) {
|
||||
if o == nil || IsNil(o.PushVerifyCertificates) {
|
||||
return nil, false
|
||||
}
|
||||
return o.PushVerifyCertificates, true
|
||||
}
|
||||
|
||||
// HasPushVerifyCertificates returns a boolean if a field has been set.
|
||||
func (o *SSFProviderRequest) HasPushVerifyCertificates() bool {
|
||||
if o != nil && !IsNil(o.PushVerifyCertificates) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetPushVerifyCertificates gets a reference to the given bool and assigns it to the PushVerifyCertificates field.
|
||||
func (o *SSFProviderRequest) SetPushVerifyCertificates(v bool) {
|
||||
o.PushVerifyCertificates = &v
|
||||
}
|
||||
|
||||
func (o SSFProviderRequest) MarshalJSON() ([]byte, error) {
|
||||
toSerialize, err := o.ToMap()
|
||||
if err != nil {
|
||||
@@ -180,6 +213,9 @@ func (o SSFProviderRequest) ToMap() (map[string]interface{}, error) {
|
||||
if !IsNil(o.EventRetention) {
|
||||
toSerialize["event_retention"] = o.EventRetention
|
||||
}
|
||||
if !IsNil(o.PushVerifyCertificates) {
|
||||
toSerialize["push_verify_certificates"] = o.PushVerifyCertificates
|
||||
}
|
||||
|
||||
for key, value := range o.AdditionalProperties {
|
||||
toSerialize[key] = value
|
||||
@@ -228,6 +264,7 @@ func (o *SSFProviderRequest) UnmarshalJSON(data []byte) (err error) {
|
||||
delete(additionalProperties, "signing_key")
|
||||
delete(additionalProperties, "oidc_auth_providers")
|
||||
delete(additionalProperties, "event_retention")
|
||||
delete(additionalProperties, "push_verify_certificates")
|
||||
o.AdditionalProperties = additionalProperties
|
||||
}
|
||||
|
||||
|
||||
Generated
+37
@@ -22,6 +22,7 @@ var _ MappedNullable = &SSFStream{}
|
||||
// SSFStream SSFStream Serializer
|
||||
type SSFStream struct {
|
||||
Pk string `json:"pk"`
|
||||
Status *SSFStreamStatusEnum `json:"status,omitempty"`
|
||||
Provider int32 `json:"provider"`
|
||||
ProviderObj SSFProvider `json:"provider_obj"`
|
||||
DeliveryMethod DeliveryMethodEnum `json:"delivery_method"`
|
||||
@@ -82,6 +83,38 @@ func (o *SSFStream) SetPk(v string) {
|
||||
o.Pk = v
|
||||
}
|
||||
|
||||
// GetStatus returns the Status field value if set, zero value otherwise.
|
||||
func (o *SSFStream) GetStatus() SSFStreamStatusEnum {
|
||||
if o == nil || IsNil(o.Status) {
|
||||
var ret SSFStreamStatusEnum
|
||||
return ret
|
||||
}
|
||||
return *o.Status
|
||||
}
|
||||
|
||||
// GetStatusOk returns a tuple with the Status field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *SSFStream) GetStatusOk() (*SSFStreamStatusEnum, bool) {
|
||||
if o == nil || IsNil(o.Status) {
|
||||
return nil, false
|
||||
}
|
||||
return o.Status, true
|
||||
}
|
||||
|
||||
// HasStatus returns a boolean if a field has been set.
|
||||
func (o *SSFStream) HasStatus() bool {
|
||||
if o != nil && !IsNil(o.Status) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetStatus gets a reference to the given SSFStreamStatusEnum and assigns it to the Status field.
|
||||
func (o *SSFStream) SetStatus(v SSFStreamStatusEnum) {
|
||||
o.Status = &v
|
||||
}
|
||||
|
||||
// GetProvider returns the Provider field value
|
||||
func (o *SSFStream) GetProvider() int32 {
|
||||
if o == nil {
|
||||
@@ -320,6 +353,9 @@ func (o SSFStream) MarshalJSON() ([]byte, error) {
|
||||
func (o SSFStream) ToMap() (map[string]interface{}, error) {
|
||||
toSerialize := map[string]interface{}{}
|
||||
toSerialize["pk"] = o.Pk
|
||||
if !IsNil(o.Status) {
|
||||
toSerialize["status"] = o.Status
|
||||
}
|
||||
toSerialize["provider"] = o.Provider
|
||||
toSerialize["provider_obj"] = o.ProviderObj
|
||||
toSerialize["delivery_method"] = o.DeliveryMethod
|
||||
@@ -383,6 +419,7 @@ func (o *SSFStream) UnmarshalJSON(data []byte) (err error) {
|
||||
|
||||
if err = json.Unmarshal(data, &additionalProperties); err == nil {
|
||||
delete(additionalProperties, "pk")
|
||||
delete(additionalProperties, "status")
|
||||
delete(additionalProperties, "provider")
|
||||
delete(additionalProperties, "provider_obj")
|
||||
delete(additionalProperties, "delivery_method")
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
authentik
|
||||
|
||||
Making authentication simple.
|
||||
|
||||
API version: 2026.5.0-rc1
|
||||
Contact: hello@goauthentik.io
|
||||
*/
|
||||
|
||||
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// SSFStreamStatusEnum the model 'SSFStreamStatusEnum'
|
||||
type SSFStreamStatusEnum string
|
||||
|
||||
// List of SSFStreamStatusEnum
|
||||
const (
|
||||
SSFSTREAMSTATUSENUM_ENABLED SSFStreamStatusEnum = "enabled"
|
||||
SSFSTREAMSTATUSENUM_PAUSED SSFStreamStatusEnum = "paused"
|
||||
SSFSTREAMSTATUSENUM_DISABLED SSFStreamStatusEnum = "disabled"
|
||||
)
|
||||
|
||||
// All allowed values of SSFStreamStatusEnum enum
|
||||
var AllowedSSFStreamStatusEnumEnumValues = []SSFStreamStatusEnum{
|
||||
"enabled",
|
||||
"paused",
|
||||
"disabled",
|
||||
}
|
||||
|
||||
func (v *SSFStreamStatusEnum) UnmarshalJSON(src []byte) error {
|
||||
var value string
|
||||
err := json.Unmarshal(src, &value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enumTypeValue := SSFStreamStatusEnum(value)
|
||||
for _, existing := range AllowedSSFStreamStatusEnumEnumValues {
|
||||
if existing == enumTypeValue {
|
||||
*v = enumTypeValue
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("%+v is not a valid SSFStreamStatusEnum", value)
|
||||
}
|
||||
|
||||
// NewSSFStreamStatusEnumFromValue returns a pointer to a valid SSFStreamStatusEnum
|
||||
// for the value passed as argument, or an error if the value passed is not allowed by the enum
|
||||
func NewSSFStreamStatusEnumFromValue(v string) (*SSFStreamStatusEnum, error) {
|
||||
ev := SSFStreamStatusEnum(v)
|
||||
if ev.IsValid() {
|
||||
return &ev, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid value '%v' for SSFStreamStatusEnum: valid values are %v", v, AllowedSSFStreamStatusEnumEnumValues)
|
||||
}
|
||||
}
|
||||
|
||||
// IsValid return true if the value is valid for the enum, false otherwise
|
||||
func (v SSFStreamStatusEnum) IsValid() bool {
|
||||
for _, existing := range AllowedSSFStreamStatusEnumEnumValues {
|
||||
if existing == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Ptr returns reference to SSFStreamStatusEnum value
|
||||
func (v SSFStreamStatusEnum) Ptr() *SSFStreamStatusEnum {
|
||||
return &v
|
||||
}
|
||||
|
||||
type NullableSSFStreamStatusEnum struct {
|
||||
value *SSFStreamStatusEnum
|
||||
isSet bool
|
||||
}
|
||||
|
||||
func (v NullableSSFStreamStatusEnum) Get() *SSFStreamStatusEnum {
|
||||
return v.value
|
||||
}
|
||||
|
||||
func (v *NullableSSFStreamStatusEnum) Set(val *SSFStreamStatusEnum) {
|
||||
v.value = val
|
||||
v.isSet = true
|
||||
}
|
||||
|
||||
func (v NullableSSFStreamStatusEnum) IsSet() bool {
|
||||
return v.isSet
|
||||
}
|
||||
|
||||
func (v *NullableSSFStreamStatusEnum) Unset() {
|
||||
v.value = nil
|
||||
v.isSet = false
|
||||
}
|
||||
|
||||
func NewNullableSSFStreamStatusEnum(val *SSFStreamStatusEnum) *NullableSSFStreamStatusEnum {
|
||||
return &NullableSSFStreamStatusEnum{value: val, isSet: true}
|
||||
}
|
||||
|
||||
func (v NullableSSFStreamStatusEnum) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.value)
|
||||
}
|
||||
|
||||
func (v *NullableSSFStreamStatusEnum) UnmarshalJSON(src []byte) error {
|
||||
v.isSet = true
|
||||
return json.Unmarshal(src, &v.value)
|
||||
}
|
||||
Generated
+51
@@ -12,6 +12,15 @@ use serde::{Deserialize, Serialize, de::Error as _};
|
||||
use super::{ContentType, Error, configuration};
|
||||
use crate::{apis::ResponseContent, models};
|
||||
|
||||
/// struct for typed errors of method [`ssf_streams_destroy`]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum SsfStreamsDestroyError {
|
||||
Status400(models::ValidationError),
|
||||
Status403(models::GenericError),
|
||||
UnknownValue(serde_json::Value),
|
||||
}
|
||||
|
||||
/// struct for typed errors of method [`ssf_streams_list`]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
@@ -30,6 +39,48 @@ pub enum SsfStreamsRetrieveError {
|
||||
UnknownValue(serde_json::Value),
|
||||
}
|
||||
|
||||
/// SSFStream Viewset
|
||||
pub async fn ssf_streams_destroy(
|
||||
configuration: &configuration::Configuration,
|
||||
uuid: &str,
|
||||
) -> Result<(), Error<SsfStreamsDestroyError>> {
|
||||
// add a prefix to parameters to efficiently prevent name collisions
|
||||
let p_path_uuid = uuid;
|
||||
|
||||
let uri_str = format!(
|
||||
"{}/ssf/streams/{uuid}/",
|
||||
configuration.base_path,
|
||||
uuid = crate::apis::urlencode(p_path_uuid)
|
||||
);
|
||||
let mut req_builder = configuration
|
||||
.client
|
||||
.request(reqwest::Method::DELETE, &uri_str);
|
||||
|
||||
if let Some(ref user_agent) = configuration.user_agent {
|
||||
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
|
||||
}
|
||||
if let Some(ref token) = configuration.bearer_access_token {
|
||||
req_builder = req_builder.bearer_auth(token.to_owned());
|
||||
};
|
||||
|
||||
let req = req_builder.build()?;
|
||||
let resp = configuration.client.execute(req).await?;
|
||||
|
||||
let status = resp.status();
|
||||
|
||||
if !status.is_client_error() && !status.is_server_error() {
|
||||
Ok(())
|
||||
} else {
|
||||
let content = resp.text().await?;
|
||||
let entity: Option<SsfStreamsDestroyError> = serde_json::from_str(&content).ok();
|
||||
Err(Error::ResponseError(ResponseContent {
|
||||
status,
|
||||
content,
|
||||
entity,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// SSFStream Viewset
|
||||
pub async fn ssf_streams_list(
|
||||
configuration: &configuration::Configuration,
|
||||
|
||||
@@ -17,6 +17,10 @@ pub enum DeliveryMethodEnum {
|
||||
HttpsColonSlashSlashSchemasOpenidNetSlashSeceventSlashRiscSlashDeliveryMethodSlashPush,
|
||||
#[serde(rename = "https://schemas.openid.net/secevent/risc/delivery-method/poll")]
|
||||
HttpsColonSlashSlashSchemasOpenidNetSlashSeceventSlashRiscSlashDeliveryMethodSlashPoll,
|
||||
#[serde(rename = "urn:ietf:rfc:8935")]
|
||||
UrnColonIetfColonRfcColon8935,
|
||||
#[serde(rename = "urn:ietf:rfc:8936")]
|
||||
UrnColonIetfColonRfcColon8936,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DeliveryMethodEnum {
|
||||
@@ -24,6 +28,8 @@ impl std::fmt::Display for DeliveryMethodEnum {
|
||||
match self {
|
||||
Self::HttpsColonSlashSlashSchemasOpenidNetSlashSeceventSlashRiscSlashDeliveryMethodSlashPush => write!(f, "https://schemas.openid.net/secevent/risc/delivery-method/push"),
|
||||
Self::HttpsColonSlashSlashSchemasOpenidNetSlashSeceventSlashRiscSlashDeliveryMethodSlashPoll => write!(f, "https://schemas.openid.net/secevent/risc/delivery-method/poll"),
|
||||
Self::UrnColonIetfColonRfcColon8935 => write!(f, "urn:ietf:rfc:8935"),
|
||||
Self::UrnColonIetfColonRfcColon8936 => write!(f, "urn:ietf:rfc:8936"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+2
@@ -1528,6 +1528,8 @@ pub mod ssf_provider_request;
|
||||
pub use self::ssf_provider_request::SsfProviderRequest;
|
||||
pub mod ssf_stream;
|
||||
pub use self::ssf_stream::SsfStream;
|
||||
pub mod ssf_stream_status_enum;
|
||||
pub use self::ssf_stream_status_enum::SsfStreamStatusEnum;
|
||||
pub mod stage;
|
||||
pub use self::stage::Stage;
|
||||
pub mod stage_mode_enum;
|
||||
|
||||
@@ -25,6 +25,11 @@ pub struct PatchedSsfProviderRequest {
|
||||
pub oidc_auth_providers: Option<Vec<i32>>,
|
||||
#[serde(rename = "event_retention", skip_serializing_if = "Option::is_none")]
|
||||
pub event_retention: Option<String>,
|
||||
#[serde(
|
||||
rename = "push_verify_certificates",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub push_verify_certificates: Option<bool>,
|
||||
}
|
||||
|
||||
impl PatchedSsfProviderRequest {
|
||||
@@ -35,6 +40,7 @@ impl PatchedSsfProviderRequest {
|
||||
signing_key: None,
|
||||
oidc_auth_providers: None,
|
||||
event_retention: None,
|
||||
push_verify_certificates: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -45,6 +45,11 @@ pub struct SsfProvider {
|
||||
pub ssf_url: Option<String>,
|
||||
#[serde(rename = "event_retention", skip_serializing_if = "Option::is_none")]
|
||||
pub event_retention: Option<String>,
|
||||
#[serde(
|
||||
rename = "push_verify_certificates",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub push_verify_certificates: Option<bool>,
|
||||
}
|
||||
|
||||
impl SsfProvider {
|
||||
@@ -74,6 +79,7 @@ impl SsfProvider {
|
||||
oidc_auth_providers_obj,
|
||||
ssf_url,
|
||||
event_retention: None,
|
||||
push_verify_certificates: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,11 @@ pub struct SsfProviderRequest {
|
||||
pub oidc_auth_providers: Option<Vec<i32>>,
|
||||
#[serde(rename = "event_retention", skip_serializing_if = "Option::is_none")]
|
||||
pub event_retention: Option<String>,
|
||||
#[serde(
|
||||
rename = "push_verify_certificates",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub push_verify_certificates: Option<bool>,
|
||||
}
|
||||
|
||||
impl SsfProviderRequest {
|
||||
@@ -35,6 +40,7 @@ impl SsfProviderRequest {
|
||||
signing_key,
|
||||
oidc_auth_providers: None,
|
||||
event_retention: None,
|
||||
push_verify_certificates: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -15,6 +15,8 @@ use crate::models;
|
||||
pub struct SsfStream {
|
||||
#[serde(rename = "pk")]
|
||||
pub pk: uuid::Uuid,
|
||||
#[serde(rename = "status", skip_serializing_if = "Option::is_none")]
|
||||
pub status: Option<models::SsfStreamStatusEnum>,
|
||||
#[serde(rename = "provider")]
|
||||
pub provider: i32,
|
||||
#[serde(rename = "provider_obj")]
|
||||
@@ -50,6 +52,7 @@ impl SsfStream {
|
||||
) -> SsfStream {
|
||||
SsfStream {
|
||||
pk,
|
||||
status: None,
|
||||
provider,
|
||||
provider_obj,
|
||||
delivery_method,
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// authentik
|
||||
//
|
||||
// Making authentication simple.
|
||||
//
|
||||
// The version of the OpenAPI document: 2026.5.0-rc1
|
||||
// Contact: hello@goauthentik.io
|
||||
// Generated by: https://openapi-generator.tech
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::models;
|
||||
|
||||
///
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
||||
pub enum SsfStreamStatusEnum {
|
||||
#[serde(rename = "enabled")]
|
||||
Enabled,
|
||||
#[serde(rename = "paused")]
|
||||
Paused,
|
||||
#[serde(rename = "disabled")]
|
||||
Disabled,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SsfStreamStatusEnum {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Enabled => write!(f, "enabled"),
|
||||
Self::Paused => write!(f, "paused"),
|
||||
Self::Disabled => write!(f, "disabled"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SsfStreamStatusEnum {
|
||||
fn default() -> SsfStreamStatusEnum {
|
||||
Self::Enabled
|
||||
}
|
||||
}
|
||||
Generated
+1
-1
@@ -15,4 +15,4 @@ build:
|
||||
--git-repo-id authentik \
|
||||
--git-user-id goauthentik
|
||||
rm -rf "${PWD}/.openapi-generator"
|
||||
npx prettier --cache --write -u "${PWD}"
|
||||
npx prettier --write -u "${PWD}"
|
||||
|
||||
Generated
+67
@@ -16,6 +16,10 @@ import type { DeliveryMethodEnum, PaginatedSSFStreamList, SSFStream } from "../m
|
||||
import { PaginatedSSFStreamListFromJSON, SSFStreamFromJSON } from "../models/index";
|
||||
import * as runtime from "../runtime";
|
||||
|
||||
export interface SsfStreamsDestroyRequest {
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
export interface SsfStreamsListRequest {
|
||||
deliveryMethod?: DeliveryMethodEnum;
|
||||
endpointUrl?: string;
|
||||
@@ -34,6 +38,69 @@ export interface SsfStreamsRetrieveRequest {
|
||||
*
|
||||
*/
|
||||
export class SsfApi extends runtime.BaseAPI {
|
||||
/**
|
||||
* Creates request options for ssfStreamsDestroy without sending the request
|
||||
*/
|
||||
async ssfStreamsDestroyRequestOpts(
|
||||
requestParameters: SsfStreamsDestroyRequest,
|
||||
): Promise<runtime.RequestOpts> {
|
||||
if (requestParameters["uuid"] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
"uuid",
|
||||
'Required parameter "uuid" was null or undefined when calling ssfStreamsDestroy().',
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
const token = this.configuration.accessToken;
|
||||
const tokenString = await token("authentik", []);
|
||||
|
||||
if (tokenString) {
|
||||
headerParameters["Authorization"] = `Bearer ${tokenString}`;
|
||||
}
|
||||
}
|
||||
|
||||
let urlPath = `/ssf/streams/{uuid}/`;
|
||||
urlPath = urlPath.replace(
|
||||
`{${"uuid"}}`,
|
||||
encodeURIComponent(String(requestParameters["uuid"])),
|
||||
);
|
||||
|
||||
return {
|
||||
path: urlPath,
|
||||
method: "DELETE",
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* SSFStream Viewset
|
||||
*/
|
||||
async ssfStreamsDestroyRaw(
|
||||
requestParameters: SsfStreamsDestroyRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<runtime.ApiResponse<void>> {
|
||||
const requestOptions = await this.ssfStreamsDestroyRequestOpts(requestParameters);
|
||||
const response = await this.request(requestOptions, initOverrides);
|
||||
|
||||
return new runtime.VoidApiResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSFStream Viewset
|
||||
*/
|
||||
async ssfStreamsDestroy(
|
||||
requestParameters: SsfStreamsDestroyRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<void> {
|
||||
await this.ssfStreamsDestroyRaw(requestParameters, initOverrides);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates request options for ssfStreamsList without sending the request
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,8 @@ export const DeliveryMethodEnum = {
|
||||
"https://schemas.openid.net/secevent/risc/delivery-method/push",
|
||||
HttpsSchemasOpenidNetSeceventRiscDeliveryMethodPoll:
|
||||
"https://schemas.openid.net/secevent/risc/delivery-method/poll",
|
||||
UrnIetfRfc8935: "urn:ietf:rfc:8935",
|
||||
UrnIetfRfc8936: "urn:ietf:rfc:8936",
|
||||
UnknownDefaultOpenApi: "11184809",
|
||||
} as const;
|
||||
export type DeliveryMethodEnum = (typeof DeliveryMethodEnum)[keyof typeof DeliveryMethodEnum];
|
||||
|
||||
@@ -42,6 +42,12 @@ export interface PatchedSSFProviderRequest {
|
||||
* @memberof PatchedSSFProviderRequest
|
||||
*/
|
||||
eventRetention?: string;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof PatchedSSFProviderRequest
|
||||
*/
|
||||
pushVerifyCertificates?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +76,8 @@ export function PatchedSSFProviderRequestFromJSONTyped(
|
||||
oidcAuthProviders:
|
||||
json["oidc_auth_providers"] == null ? undefined : json["oidc_auth_providers"],
|
||||
eventRetention: json["event_retention"] == null ? undefined : json["event_retention"],
|
||||
pushVerifyCertificates:
|
||||
json["push_verify_certificates"] == null ? undefined : json["push_verify_certificates"],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -90,5 +98,6 @@ export function PatchedSSFProviderRequestToJSONTyped(
|
||||
signing_key: value["signingKey"],
|
||||
oidc_auth_providers: value["oidcAuthProviders"],
|
||||
event_retention: value["eventRetention"],
|
||||
push_verify_certificates: value["pushVerifyCertificates"],
|
||||
};
|
||||
}
|
||||
|
||||
+9
@@ -95,6 +95,12 @@ export interface SSFProvider {
|
||||
* @memberof SSFProvider
|
||||
*/
|
||||
eventRetention?: string;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof SSFProvider
|
||||
*/
|
||||
pushVerifyCertificates?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,6 +143,8 @@ export function SSFProviderFromJSONTyped(json: any, ignoreDiscriminator: boolean
|
||||
oidcAuthProvidersObj: (json["oidc_auth_providers_obj"] as Array<any>).map(ProviderFromJSON),
|
||||
ssfUrl: json["ssf_url"],
|
||||
eventRetention: json["event_retention"] == null ? undefined : json["event_retention"],
|
||||
pushVerifyCertificates:
|
||||
json["push_verify_certificates"] == null ? undefined : json["push_verify_certificates"],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -167,5 +175,6 @@ export function SSFProviderToJSONTyped(
|
||||
signing_key: value["signingKey"],
|
||||
oidc_auth_providers: value["oidcAuthProviders"],
|
||||
event_retention: value["eventRetention"],
|
||||
push_verify_certificates: value["pushVerifyCertificates"],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -42,6 +42,12 @@ export interface SSFProviderRequest {
|
||||
* @memberof SSFProviderRequest
|
||||
*/
|
||||
eventRetention?: string;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof SSFProviderRequest
|
||||
*/
|
||||
pushVerifyCertificates?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +76,8 @@ export function SSFProviderRequestFromJSONTyped(
|
||||
oidcAuthProviders:
|
||||
json["oidc_auth_providers"] == null ? undefined : json["oidc_auth_providers"],
|
||||
eventRetention: json["event_retention"] == null ? undefined : json["event_retention"],
|
||||
pushVerifyCertificates:
|
||||
json["push_verify_certificates"] == null ? undefined : json["push_verify_certificates"],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -90,5 +98,6 @@ export function SSFProviderRequestToJSONTyped(
|
||||
signing_key: value["signingKey"],
|
||||
oidc_auth_providers: value["oidcAuthProviders"],
|
||||
event_retention: value["eventRetention"],
|
||||
push_verify_certificates: value["pushVerifyCertificates"],
|
||||
};
|
||||
}
|
||||
|
||||
+10
@@ -18,6 +18,8 @@ import type { EventsRequestedEnum } from "./EventsRequestedEnum";
|
||||
import { EventsRequestedEnumFromJSON, EventsRequestedEnumToJSON } from "./EventsRequestedEnum";
|
||||
import type { SSFProvider } from "./SSFProvider";
|
||||
import { SSFProviderFromJSON } from "./SSFProvider";
|
||||
import type { SSFStreamStatusEnum } from "./SSFStreamStatusEnum";
|
||||
import { SSFStreamStatusEnumFromJSON, SSFStreamStatusEnumToJSON } from "./SSFStreamStatusEnum";
|
||||
|
||||
/**
|
||||
* SSFStream Serializer
|
||||
@@ -31,6 +33,12 @@ export interface SSFStream {
|
||||
* @memberof SSFStream
|
||||
*/
|
||||
readonly pk: string;
|
||||
/**
|
||||
*
|
||||
* @type {SSFStreamStatusEnum}
|
||||
* @memberof SSFStream
|
||||
*/
|
||||
status?: SSFStreamStatusEnum;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
@@ -104,6 +112,7 @@ export function SSFStreamFromJSONTyped(json: any, ignoreDiscriminator: boolean):
|
||||
}
|
||||
return {
|
||||
pk: json["pk"],
|
||||
status: json["status"] == null ? undefined : SSFStreamStatusEnumFromJSON(json["status"]),
|
||||
provider: json["provider"],
|
||||
providerObj: SSFProviderFromJSON(json["provider_obj"]),
|
||||
deliveryMethod: DeliveryMethodEnumFromJSON(json["delivery_method"]),
|
||||
@@ -131,6 +140,7 @@ export function SSFStreamToJSONTyped(
|
||||
}
|
||||
|
||||
return {
|
||||
status: SSFStreamStatusEnumToJSON(value["status"]),
|
||||
provider: value["provider"],
|
||||
delivery_method: DeliveryMethodEnumToJSON(value["deliveryMethod"]),
|
||||
endpoint_url: value["endpointUrl"],
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* authentik
|
||||
* Making authentication simple.
|
||||
*
|
||||
* The version of the OpenAPI document: 2026.5.0-rc1
|
||||
* Contact: hello@goauthentik.io
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const SSFStreamStatusEnum = {
|
||||
Enabled: "enabled",
|
||||
Paused: "paused",
|
||||
Disabled: "disabled",
|
||||
UnknownDefaultOpenApi: "11184809",
|
||||
} as const;
|
||||
export type SSFStreamStatusEnum = (typeof SSFStreamStatusEnum)[keyof typeof SSFStreamStatusEnum];
|
||||
|
||||
export function instanceOfSSFStreamStatusEnum(value: any): boolean {
|
||||
for (const key in SSFStreamStatusEnum) {
|
||||
if (Object.prototype.hasOwnProperty.call(SSFStreamStatusEnum, key)) {
|
||||
if (SSFStreamStatusEnum[key as keyof typeof SSFStreamStatusEnum] === value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function SSFStreamStatusEnumFromJSON(json: any): SSFStreamStatusEnum {
|
||||
return SSFStreamStatusEnumFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function SSFStreamStatusEnumFromJSONTyped(
|
||||
json: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): SSFStreamStatusEnum {
|
||||
return json as SSFStreamStatusEnum;
|
||||
}
|
||||
|
||||
export function SSFStreamStatusEnumToJSON(value?: SSFStreamStatusEnum | null): any {
|
||||
return value as any;
|
||||
}
|
||||
|
||||
export function SSFStreamStatusEnumToJSONTyped(
|
||||
value: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): SSFStreamStatusEnum {
|
||||
return value as SSFStreamStatusEnum;
|
||||
}
|
||||
Generated
+1
@@ -745,6 +745,7 @@ export * from "./SMSDeviceRequest";
|
||||
export * from "./SSFProvider";
|
||||
export * from "./SSFProviderRequest";
|
||||
export * from "./SSFStream";
|
||||
export * from "./SSFStreamStatusEnum";
|
||||
export * from "./Schedule";
|
||||
export * from "./ScheduleRequest";
|
||||
export * from "./ScopeMapping";
|
||||
|
||||
+38
@@ -26629,6 +26629,28 @@ paths:
|
||||
$ref: '#/components/responses/ValidationErrorResponse'
|
||||
'403':
|
||||
$ref: '#/components/responses/GenericErrorResponse'
|
||||
delete:
|
||||
operationId: ssf_streams_destroy
|
||||
description: SSFStream Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this SSF Stream.
|
||||
required: true
|
||||
tags:
|
||||
- ssf
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
$ref: '#/components/responses/ValidationErrorResponse'
|
||||
'403':
|
||||
$ref: '#/components/responses/GenericErrorResponse'
|
||||
/stages/all/:
|
||||
get:
|
||||
operationId: stages_all_list
|
||||
@@ -36574,6 +36596,8 @@ components:
|
||||
enum:
|
||||
- https://schemas.openid.net/secevent/risc/delivery-method/push
|
||||
- https://schemas.openid.net/secevent/risc/delivery-method/poll
|
||||
- urn:ietf:rfc:8935
|
||||
- urn:ietf:rfc:8936
|
||||
type: string
|
||||
DeniedActionEnum:
|
||||
enum:
|
||||
@@ -50276,6 +50300,8 @@ components:
|
||||
event_retention:
|
||||
type: string
|
||||
minLength: 1
|
||||
push_verify_certificates:
|
||||
type: boolean
|
||||
PatchedScheduleRequest:
|
||||
type: object
|
||||
properties:
|
||||
@@ -54696,6 +54722,8 @@ components:
|
||||
readOnly: true
|
||||
event_retention:
|
||||
type: string
|
||||
push_verify_certificates:
|
||||
type: boolean
|
||||
required:
|
||||
- component
|
||||
- meta_model_name
|
||||
@@ -54725,6 +54753,8 @@ components:
|
||||
event_retention:
|
||||
type: string
|
||||
minLength: 1
|
||||
push_verify_certificates:
|
||||
type: boolean
|
||||
required:
|
||||
- name
|
||||
- signing_key
|
||||
@@ -54737,6 +54767,8 @@ components:
|
||||
format: uuid
|
||||
readOnly: true
|
||||
title: Uuid
|
||||
status:
|
||||
$ref: '#/components/schemas/SSFStreamStatusEnum'
|
||||
provider:
|
||||
type: integer
|
||||
provider_obj:
|
||||
@@ -54767,6 +54799,12 @@ components:
|
||||
- pk
|
||||
- provider
|
||||
- provider_obj
|
||||
SSFStreamStatusEnum:
|
||||
enum:
|
||||
- enabled
|
||||
- paused
|
||||
- disabled
|
||||
type: string
|
||||
Schedule:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo:6.0.13
|
||||
httpd:
|
||||
image: ghcr.io/beryju/oidc-conformance-suite-httpd:v5.1.32
|
||||
nginx:
|
||||
image: ghcr.io/beryju/oidc-conformance-suite-nginx:v5.1.41
|
||||
ports:
|
||||
- "8443:8443"
|
||||
- "8444:8444"
|
||||
depends_on:
|
||||
- server
|
||||
server:
|
||||
image: ghcr.io/beryju/oidc-conformance-suite-server:v5.1.32
|
||||
image: ghcr.io/beryju/oidc-conformance-suite-server:v5.1.41
|
||||
ports:
|
||||
- "9999:9999"
|
||||
extra_hosts:
|
||||
|
||||
@@ -6,6 +6,7 @@ import "#elements/forms/FormGroup";
|
||||
import "#elements/forms/HorizontalFormElement";
|
||||
import "#elements/forms/SearchSelect/index";
|
||||
import "#elements/utils/TimeDeltaHelp";
|
||||
import "#components/ak-switch-input";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
|
||||
@@ -76,6 +77,12 @@ export class SSFProviderFormPage extends BaseProviderForm<SSFProvider> {
|
||||
></ak-crypto-certificate-search>
|
||||
<p class="pf-c-form__helper-text">${msg("Key used to sign the events.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-switch-input
|
||||
name="pushVerifyCertificates"
|
||||
label=${msg("Verify Push stream endpoints' certificate")}
|
||||
?checked=${this.instance?.pushVerifyCertificates ?? true}
|
||||
>
|
||||
</ak-switch-input>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Event Retention")}
|
||||
required
|
||||
|
||||
@@ -24,7 +24,6 @@ import { msg } from "@lit/localize";
|
||||
import { CSSResult, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
@@ -58,7 +57,6 @@ export class SSFProviderViewPage extends AKElement {
|
||||
PFDescriptionList,
|
||||
PFForm,
|
||||
PFFormControl,
|
||||
PFBanner,
|
||||
PFList,
|
||||
PFList,
|
||||
];
|
||||
@@ -120,80 +118,70 @@ export class SSFProviderViewPage extends AKElement {
|
||||
return nothing;
|
||||
}
|
||||
const [appLabel, modelName] = ModelEnum.AuthentikProvidersSsfSsfprovider.split(".");
|
||||
return html`<div slot="header" class="pf-c-banner pf-m-info">
|
||||
${msg("SSF Provider is in preview.")}
|
||||
<a href="mailto:hello+feature/ssf@goauthentik.io">${msg("Send us feedback!")}</a>
|
||||
return html`<div
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter"
|
||||
>
|
||||
<div class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-4-col-on-xl pf-m-4-col-on-2xl">
|
||||
<div class="pf-c-card__body">
|
||||
${renderDescriptionList([
|
||||
[msg("Name"), html`${this.provider.name}`],
|
||||
[
|
||||
msg("URL"),
|
||||
html`<input
|
||||
class="pf-c-form-control pf-m-monospace"
|
||||
readonly
|
||||
type="text"
|
||||
value=${this.provider.ssfUrl || ""}
|
||||
placeholder=${this.provider.ssfUrl
|
||||
? msg("SSF URL")
|
||||
: msg("No assigned application")}
|
||||
/>`,
|
||||
],
|
||||
[
|
||||
msg("Federated OAuth2/OpenID Providers"),
|
||||
(this.provider.oidcAuthProvidersObj || []).length > 0
|
||||
? html`<ul class="pf-c-list">
|
||||
${this.provider.oidcAuthProvidersObj.map((provider) => {
|
||||
return html`
|
||||
<li>
|
||||
<a href="#/core/providers/${provider.pk}">
|
||||
${provider.name}
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
})}
|
||||
</ul>`
|
||||
: html`-`,
|
||||
],
|
||||
[
|
||||
msg("Related actions"),
|
||||
html`<ak-forms-modal>
|
||||
<span slot="submit">${msg("Save Changes")}</span>
|
||||
<span slot="header">${msg("Update SSF Provider")}</span>
|
||||
<ak-provider-ssf-form slot="form" .instancePk=${this.provider.pk}>
|
||||
</ak-provider-ssf-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary pf-m-block">
|
||||
${msg("Edit")}
|
||||
</button>
|
||||
</ak-forms-modal>`,
|
||||
],
|
||||
])}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
|
||||
<div
|
||||
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-4-col-on-xl pf-m-4-col-on-2xl"
|
||||
>
|
||||
<div class="pf-c-card__body">
|
||||
${renderDescriptionList([
|
||||
[msg("Name"), html`${this.provider.name}`],
|
||||
[
|
||||
msg("URL"),
|
||||
html`<input
|
||||
class="pf-c-form-control pf-m-monospace"
|
||||
readonly
|
||||
type="text"
|
||||
value=${this.provider.ssfUrl || ""}
|
||||
placeholder=${this.provider.ssfUrl
|
||||
? msg("SSF URL")
|
||||
: msg("No assigned application")}
|
||||
/>`,
|
||||
],
|
||||
[
|
||||
msg("Federated OAuth2/OpenID Providers"),
|
||||
(this.provider.oidcAuthProvidersObj || []).length > 0
|
||||
? html`<ul class="pf-c-list">
|
||||
${this.provider.oidcAuthProvidersObj.map((provider) => {
|
||||
return html`
|
||||
<li>
|
||||
<a href="#/core/providers/${provider.pk}">
|
||||
${provider.name}
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
})}
|
||||
</ul>`
|
||||
: html`-`,
|
||||
],
|
||||
[
|
||||
msg("Related actions"),
|
||||
html`<ak-forms-modal>
|
||||
<span slot="submit">${msg("Save Changes")}</span>
|
||||
<span slot="header">${msg("Update SSF Provider")}</span>
|
||||
<ak-provider-ssf-form
|
||||
slot="form"
|
||||
.instancePk=${this.provider.pk}
|
||||
>
|
||||
</ak-provider-ssf-form>
|
||||
<button
|
||||
slot="trigger"
|
||||
class="pf-c-button pf-m-primary pf-m-block"
|
||||
>
|
||||
${msg("Edit")}
|
||||
</button>
|
||||
</ak-forms-modal>`,
|
||||
],
|
||||
])}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card pf-l-grid__item pf-m-8-col-on-2xl">
|
||||
<div class="pf-c-card__title">${msg("Streams")}</div>
|
||||
<ak-provider-ssf-stream-list .providerId=${this.providerID}>
|
||||
</ak-provider-ssf-stream-list>
|
||||
</div>
|
||||
<div class="pf-c-card pf-l-grid__item pf-m-12-col-on-2xl">
|
||||
<div class="pf-c-card__title">${msg("Tasks")}</div>
|
||||
<ak-task-list
|
||||
.relObjAppLabel=${appLabel}
|
||||
.relObjModel=${modelName}
|
||||
.relObjId="${this.provider.pk}"
|
||||
></ak-task-list>
|
||||
</div>
|
||||
</div>`;
|
||||
<div class="pf-c-card pf-l-grid__item pf-m-8-col-on-2xl">
|
||||
<div class="pf-c-card__title">${msg("Streams")}</div>
|
||||
<ak-provider-ssf-stream-list .providerId=${this.providerID}>
|
||||
</ak-provider-ssf-stream-list>
|
||||
</div>
|
||||
<div class="pf-c-card pf-l-grid__item pf-m-12-col-on-2xl">
|
||||
<div class="pf-c-card__title">${msg("Tasks")}</div>
|
||||
<ak-task-list
|
||||
.relObjAppLabel=${appLabel}
|
||||
.relObjModel=${modelName}
|
||||
.relObjId="${this.provider.pk}"
|
||||
></ak-task-list>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,17 +8,24 @@ import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import renderDescriptionList from "#components/DescriptionList";
|
||||
|
||||
import { SSFDeliveryMethodToLabel } from "#admin/providers/ssf/utils";
|
||||
|
||||
import { SsfApi, SSFStream } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html } from "lit";
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
|
||||
@customElement("ak-provider-ssf-stream-list")
|
||||
export class SSFProviderStreamList extends Table<SSFStream> {
|
||||
protected override searchEnabled = true;
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
public checkbox: boolean = true;
|
||||
public clearOnRefresh: boolean = true;
|
||||
public expandable: boolean = true;
|
||||
|
||||
@property({ type: Number })
|
||||
providerId?: number;
|
||||
@@ -26,13 +33,43 @@ export class SSFProviderStreamList extends Table<SSFStream> {
|
||||
@property()
|
||||
order = "name";
|
||||
|
||||
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<SSFStream>> {
|
||||
return new SsfApi(DEFAULT_CONFIG).ssfStreamsList({
|
||||
provider: this.providerId,
|
||||
...(await this.defaultEndpointConfig()),
|
||||
pageSize: 10,
|
||||
});
|
||||
}
|
||||
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
object-label=${msg("Stream(s)")}
|
||||
.objects=${this.selectedElements}
|
||||
.delete=${(item: SSFStream) => {
|
||||
return new SsfApi(DEFAULT_CONFIG).ssfStreamsDestroy({
|
||||
uuid: item.pk,
|
||||
});
|
||||
}}
|
||||
.metadata=${(item: SSFStream) => {
|
||||
return [
|
||||
{ key: msg("Audience"), value: item.aud },
|
||||
{
|
||||
key: msg("Delivery method"),
|
||||
value: SSFDeliveryMethodToLabel(item.deliveryMethod),
|
||||
},
|
||||
{ key: msg("Endpoint"), value: item.endpointUrl ?? "-" },
|
||||
];
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${msg("Delete")}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
protected override rowLabel(item: SSFStream): string | null {
|
||||
return item.aud?.join(", ") ?? null;
|
||||
}
|
||||
@@ -40,10 +77,18 @@ export class SSFProviderStreamList extends Table<SSFStream> {
|
||||
protected columns: TableColumn[] = [
|
||||
// ---
|
||||
[msg("Audience"), "aud"],
|
||||
[msg("Delivery Method"), "delivery_method"],
|
||||
];
|
||||
|
||||
protected renderExpanded(item: SSFStream): SlottedTemplateResult {
|
||||
return html`${renderDescriptionList([
|
||||
[msg("Delivery method"), html`${SSFDeliveryMethodToLabel(item.deliveryMethod)}`],
|
||||
[msg("Endpoint"), html`${item.endpointUrl ?? "-"}`],
|
||||
])}`;
|
||||
}
|
||||
|
||||
row(item: SSFStream): SlottedTemplateResult[] {
|
||||
return [html`${item.aud}`];
|
||||
return [html`${item.aud}`, html`${SSFDeliveryMethodToLabel(item.deliveryMethod)}`];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { DeliveryMethodEnum } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
|
||||
export function SSFDeliveryMethodToLabel(method?: DeliveryMethodEnum): string {
|
||||
if (!method) return "";
|
||||
switch (method) {
|
||||
case DeliveryMethodEnum.HttpsSchemasOpenidNetSeceventRiscDeliveryMethodPoll:
|
||||
case DeliveryMethodEnum.UrnIetfRfc8936:
|
||||
return msg("Pull");
|
||||
case DeliveryMethodEnum.HttpsSchemasOpenidNetSeceventRiscDeliveryMethodPush:
|
||||
case DeliveryMethodEnum.UrnIetfRfc8935:
|
||||
return msg("Push");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -4,8 +4,7 @@ sidebar_label: SSF Provider
|
||||
description: "Overview of SSF and the authentik SSF provider"
|
||||
authentik_version: "2025.2.0"
|
||||
authentik_enterprise: true
|
||||
authentik_preview: true
|
||||
tags: [Shared Signals Framework, SSF, Apple Business Manager]
|
||||
tags: [Shared Signals Framework, SSF, Apple Business Manager, Apple School Manager]
|
||||
---
|
||||
|
||||
The Shared Signals Framework (SSF) provider allows you to integrate applications with the Shared Signals Framework protocol.
|
||||
|
||||
Reference in New Issue
Block a user