blueprints: fix API validation with OCI blueprint path (#5822)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
b055adec2a
commit
e141a11475
|
@ -13,6 +13,7 @@ from rest_framework.viewsets import ModelViewSet
|
||||||
from authentik.api.decorators import permission_required
|
from authentik.api.decorators import permission_required
|
||||||
from authentik.blueprints.models import BlueprintInstance
|
from authentik.blueprints.models import BlueprintInstance
|
||||||
from authentik.blueprints.v1.importer import Importer
|
from authentik.blueprints.v1.importer import Importer
|
||||||
|
from authentik.blueprints.v1.oci import OCI_PREFIX
|
||||||
from authentik.blueprints.v1.tasks import apply_blueprint, blueprints_find_dict
|
from authentik.blueprints.v1.tasks import apply_blueprint, blueprints_find_dict
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
|
@ -36,7 +37,7 @@ class BlueprintInstanceSerializer(ModelSerializer):
|
||||||
|
|
||||||
def validate_path(self, path: str) -> str:
|
def validate_path(self, path: str) -> str:
|
||||||
"""Ensure the path (if set) specified is retrievable"""
|
"""Ensure the path (if set) specified is retrievable"""
|
||||||
if path == "":
|
if path == "" or path.startswith(OCI_PREFIX):
|
||||||
return path
|
return path
|
||||||
files: list[dict] = blueprints_find_dict.delay().get()
|
files: list[dict] = blueprints_find_dict.delay().get()
|
||||||
if path not in [file["path"] for file in files]:
|
if path not in [file["path"] for file in files]:
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework.serializers import Serializer
|
from rest_framework.serializers import Serializer
|
||||||
from structlog import get_logger
|
from structlog import get_logger
|
||||||
|
|
||||||
from authentik.blueprints.v1.oci import BlueprintOCIClient, OCIException
|
from authentik.blueprints.v1.oci import OCI_PREFIX, BlueprintOCIClient, OCIException
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.lib.models import CreatedUpdatedModel, SerializerModel
|
from authentik.lib.models import CreatedUpdatedModel, SerializerModel
|
||||||
from authentik.lib.sentry import SentryIgnoredException
|
from authentik.lib.sentry import SentryIgnoredException
|
||||||
|
@ -72,7 +72,7 @@ class BlueprintInstance(SerializerModel, ManagedModel, CreatedUpdatedModel):
|
||||||
|
|
||||||
def retrieve_oci(self) -> str:
|
def retrieve_oci(self) -> str:
|
||||||
"""Get blueprint from an OCI registry"""
|
"""Get blueprint from an OCI registry"""
|
||||||
client = BlueprintOCIClient(self.path.replace("oci://", "https://"))
|
client = BlueprintOCIClient(self.path.replace(OCI_PREFIX, "https://"))
|
||||||
try:
|
try:
|
||||||
manifests = client.fetch_manifests()
|
manifests = client.fetch_manifests()
|
||||||
return client.fetch_blobs(manifests)
|
return client.fetch_blobs(manifests)
|
||||||
|
@ -90,7 +90,7 @@ class BlueprintInstance(SerializerModel, ManagedModel, CreatedUpdatedModel):
|
||||||
|
|
||||||
def retrieve(self) -> str:
|
def retrieve(self) -> str:
|
||||||
"""Retrieve blueprint contents"""
|
"""Retrieve blueprint contents"""
|
||||||
if self.path.startswith("oci://"):
|
if self.path.startswith(OCI_PREFIX):
|
||||||
return self.retrieve_oci()
|
return self.retrieve_oci()
|
||||||
if self.path != "":
|
if self.path != "":
|
||||||
return self.retrieve_file()
|
return self.retrieve_file()
|
||||||
|
|
|
@ -44,6 +44,14 @@ class TestBlueprintsV1API(APITestCase):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_api_oci(self):
|
||||||
|
"""Test validation with OCI path"""
|
||||||
|
res = self.client.post(
|
||||||
|
reverse("authentik_api:blueprintinstance-list"),
|
||||||
|
data={"name": "foo", "path": "oci://foo/bar"},
|
||||||
|
)
|
||||||
|
self.assertEqual(res.status_code, 201)
|
||||||
|
|
||||||
def test_api_blank(self):
|
def test_api_blank(self):
|
||||||
"""Test blank"""
|
"""Test blank"""
|
||||||
res = self.client.post(
|
res = self.client.post(
|
||||||
|
|
|
@ -19,6 +19,7 @@ from authentik.lib.sentry import SentryIgnoredException
|
||||||
from authentik.lib.utils.http import authentik_user_agent
|
from authentik.lib.utils.http import authentik_user_agent
|
||||||
|
|
||||||
OCI_MEDIA_TYPE = "application/vnd.goauthentik.blueprint.v1+yaml"
|
OCI_MEDIA_TYPE = "application/vnd.goauthentik.blueprint.v1+yaml"
|
||||||
|
OCI_PREFIX = "oci://"
|
||||||
|
|
||||||
|
|
||||||
class OCIException(SentryIgnoredException):
|
class OCIException(SentryIgnoredException):
|
||||||
|
|
|
@ -28,6 +28,7 @@ from authentik.blueprints.models import (
|
||||||
from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata, EntryInvalidError
|
from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata, EntryInvalidError
|
||||||
from authentik.blueprints.v1.importer import Importer
|
from authentik.blueprints.v1.importer import Importer
|
||||||
from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_INSTANTIATE
|
from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_INSTANTIATE
|
||||||
|
from authentik.blueprints.v1.oci import OCI_PREFIX
|
||||||
from authentik.events.monitored_tasks import (
|
from authentik.events.monitored_tasks import (
|
||||||
MonitoredTask,
|
MonitoredTask,
|
||||||
TaskResult,
|
TaskResult,
|
||||||
|
@ -228,7 +229,7 @@ def apply_blueprint(self: MonitoredTask, instance_pk: str):
|
||||||
def clear_failed_blueprints():
|
def clear_failed_blueprints():
|
||||||
"""Remove blueprints which couldn't be fetched"""
|
"""Remove blueprints which couldn't be fetched"""
|
||||||
# Exclude OCI blueprints as those might be temporarily unavailable
|
# Exclude OCI blueprints as those might be temporarily unavailable
|
||||||
for blueprint in BlueprintInstance.objects.exclude(path__startswith="oci://"):
|
for blueprint in BlueprintInstance.objects.exclude(path__startswith=OCI_PREFIX):
|
||||||
try:
|
try:
|
||||||
blueprint.retrieve()
|
blueprint.retrieve()
|
||||||
except BlueprintRetrievalFailed:
|
except BlueprintRetrievalFailed:
|
||||||
|
|
Reference in a new issue