outposts: fix Outpost reconcile not re-assigning managed attribute (#8014)
* outposts: fix Outpost reconcile not re-assigning managed attribute Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rework reconcile to find both name and managed outpost Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
0e1646ca1b
commit
1c3cce1f89
|
@ -40,7 +40,7 @@ class ManagedAppConfig(AppConfig):
|
||||||
meth()
|
meth()
|
||||||
self._logger.debug("Successfully reconciled", name=name)
|
self._logger.debug("Successfully reconciled", name=name)
|
||||||
except (DatabaseError, ProgrammingError, InternalError) as exc:
|
except (DatabaseError, ProgrammingError, InternalError) as exc:
|
||||||
self._logger.debug("Failed to run reconcile", name=name, exc=exc)
|
self._logger.warning("Failed to run reconcile", name=name, exc=exc)
|
||||||
|
|
||||||
|
|
||||||
class AuthentikBlueprintsConfig(ManagedAppConfig):
|
class AuthentikBlueprintsConfig(ManagedAppConfig):
|
||||||
|
|
|
@ -18,7 +18,7 @@ from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||||
from authentik.core.models import Provider
|
from authentik.core.models import Provider
|
||||||
from authentik.outposts.api.service_connections import ServiceConnectionSerializer
|
from authentik.outposts.api.service_connections import ServiceConnectionSerializer
|
||||||
from authentik.outposts.apps import MANAGED_OUTPOST
|
from authentik.outposts.apps import MANAGED_OUTPOST, MANAGED_OUTPOST_NAME
|
||||||
from authentik.outposts.models import (
|
from authentik.outposts.models import (
|
||||||
Outpost,
|
Outpost,
|
||||||
OutpostConfig,
|
OutpostConfig,
|
||||||
|
@ -47,6 +47,16 @@ class OutpostSerializer(ModelSerializer):
|
||||||
source="service_connection", read_only=True
|
source="service_connection", read_only=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def validate_name(self, name: str) -> str:
|
||||||
|
"""Validate name (especially for embedded outpost)"""
|
||||||
|
if not self.instance:
|
||||||
|
return name
|
||||||
|
if self.instance.managed == MANAGED_OUTPOST:
|
||||||
|
raise ValidationError("Embedded outpost's name cannot be changed")
|
||||||
|
if self.instance.name == MANAGED_OUTPOST_NAME:
|
||||||
|
self.instance.managed = MANAGED_OUTPOST
|
||||||
|
return name
|
||||||
|
|
||||||
def validate_providers(self, providers: list[Provider]) -> list[Provider]:
|
def validate_providers(self, providers: list[Provider]) -> list[Provider]:
|
||||||
"""Check that all providers match the type of the outpost"""
|
"""Check that all providers match the type of the outpost"""
|
||||||
type_map = {
|
type_map = {
|
||||||
|
|
|
@ -15,6 +15,7 @@ GAUGE_OUTPOSTS_LAST_UPDATE = Gauge(
|
||||||
["outpost", "uid", "version"],
|
["outpost", "uid", "version"],
|
||||||
)
|
)
|
||||||
MANAGED_OUTPOST = "goauthentik.io/outposts/embedded"
|
MANAGED_OUTPOST = "goauthentik.io/outposts/embedded"
|
||||||
|
MANAGED_OUTPOST_NAME = "authentik Embedded Outpost"
|
||||||
|
|
||||||
|
|
||||||
class AuthentikOutpostConfig(ManagedAppConfig):
|
class AuthentikOutpostConfig(ManagedAppConfig):
|
||||||
|
@ -39,10 +40,14 @@ class AuthentikOutpostConfig(ManagedAppConfig):
|
||||||
OutpostType,
|
OutpostType,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if outpost := Outpost.objects.filter(name=MANAGED_OUTPOST_NAME, managed="").first():
|
||||||
|
outpost.managed = MANAGED_OUTPOST
|
||||||
|
outpost.save()
|
||||||
|
return
|
||||||
outpost, updated = Outpost.objects.update_or_create(
|
outpost, updated = Outpost.objects.update_or_create(
|
||||||
defaults={
|
defaults={
|
||||||
"name": "authentik Embedded Outpost",
|
|
||||||
"type": OutpostType.PROXY,
|
"type": OutpostType.PROXY,
|
||||||
|
"name": MANAGED_OUTPOST_NAME,
|
||||||
},
|
},
|
||||||
managed=MANAGED_OUTPOST,
|
managed=MANAGED_OUTPOST,
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
|
from authentik.blueprints.tests import reconcile_app
|
||||||
from authentik.core.models import PropertyMapping
|
from authentik.core.models import PropertyMapping
|
||||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
from authentik.outposts.api.outposts import OutpostSerializer
|
from authentik.outposts.api.outposts import OutpostSerializer
|
||||||
from authentik.outposts.models import OutpostType, default_outpost_config
|
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||||
|
from authentik.outposts.models import Outpost, OutpostType, default_outpost_config
|
||||||
from authentik.providers.ldap.models import LDAPProvider
|
from authentik.providers.ldap.models import LDAPProvider
|
||||||
from authentik.providers.proxy.models import ProxyProvider
|
from authentik.providers.proxy.models import ProxyProvider
|
||||||
|
|
||||||
|
@ -22,7 +24,36 @@ class TestOutpostServiceConnectionsAPI(APITestCase):
|
||||||
self.user = create_test_admin_user()
|
self.user = create_test_admin_user()
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
def test_outpost_validaton(self):
|
@reconcile_app("authentik_outposts")
|
||||||
|
def test_managed_name_change(self):
|
||||||
|
"""Test name change for embedded outpost"""
|
||||||
|
embedded_outpost = Outpost.objects.filter(managed=MANAGED_OUTPOST).first()
|
||||||
|
self.assertIsNotNone(embedded_outpost)
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("authentik_api:outpost-detail", kwargs={"pk": embedded_outpost.pk}),
|
||||||
|
{"name": "foo"},
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
self.assertJSONEqual(
|
||||||
|
response.content, {"name": ["Embedded outpost's name cannot be changed"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
@reconcile_app("authentik_outposts")
|
||||||
|
def test_managed_without_managed(self):
|
||||||
|
"""Test name change for embedded outpost"""
|
||||||
|
embedded_outpost = Outpost.objects.filter(managed=MANAGED_OUTPOST).first()
|
||||||
|
self.assertIsNotNone(embedded_outpost)
|
||||||
|
embedded_outpost.managed = ""
|
||||||
|
embedded_outpost.save()
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("authentik_api:outpost-detail", kwargs={"pk": embedded_outpost.pk}),
|
||||||
|
{"name": "foo"},
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
embedded_outpost.refresh_from_db()
|
||||||
|
self.assertEqual(embedded_outpost.managed, MANAGED_OUTPOST)
|
||||||
|
|
||||||
|
def test_outpost_validation(self):
|
||||||
"""Test Outpost validation"""
|
"""Test Outpost validation"""
|
||||||
valid = OutpostSerializer(
|
valid = OutpostSerializer(
|
||||||
data={
|
data={
|
||||||
|
|
Reference in New Issue