blueprints: initial
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
c592599633
commit
d811aabd38
|
@ -31,5 +31,5 @@ class AuthentikAPIConfig(AppConfig):
|
||||||
"type": "apiKey",
|
"type": "apiKey",
|
||||||
"in": "header",
|
"in": "header",
|
||||||
"name": "Authorization",
|
"name": "Authorization",
|
||||||
"scheme": "bearer",
|
"scheme": "Bearer",
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
"""Serializer mixin for managed models"""
|
"""Serializer mixin for managed models"""
|
||||||
|
from django.apps import apps
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.fields import CharField, DateTimeField, JSONField
|
from rest_framework.fields import CharField, DateTimeField, DictField, JSONField
|
||||||
from rest_framework.permissions import IsAdminUser
|
from rest_framework.permissions import IsAdminUser
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
@ -12,7 +13,8 @@ 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 StringImporter
|
from authentik.blueprints.v1.common import Blueprint, BlueprintEntry, BlueprintEntryDesiredState
|
||||||
|
from authentik.blueprints.v1.importer import StringImporter, is_model_allowed
|
||||||
from authentik.blueprints.v1.oci import OCI_PREFIX
|
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
|
||||||
|
@ -84,6 +86,32 @@ class BlueprintInstanceSerializer(ModelSerializer):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BlueprintEntrySerializer(PassiveSerializer):
|
||||||
|
"""Validate a single blueprint entry, similar to a subset of regular blueprints"""
|
||||||
|
|
||||||
|
model = CharField()
|
||||||
|
attrs = DictField()
|
||||||
|
|
||||||
|
def validate_model(self, fq_model: str) -> str:
|
||||||
|
"""Validate model is allowed"""
|
||||||
|
if "." not in fq_model:
|
||||||
|
raise ValidationError("Invalid model")
|
||||||
|
app, model_name = fq_model.split(".")
|
||||||
|
try:
|
||||||
|
model = apps.get_model(app, model_name)
|
||||||
|
if not is_model_allowed(model):
|
||||||
|
raise ValidationError("Invalid model")
|
||||||
|
except LookupError:
|
||||||
|
raise ValidationError("Invalid model")
|
||||||
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
class BlueprintProceduralSerializer(PassiveSerializer):
|
||||||
|
"""Validate a procedural blueprint, which is a subset of a regular blueprint"""
|
||||||
|
|
||||||
|
entries = ListSerializer(child=BlueprintEntrySerializer())
|
||||||
|
|
||||||
|
|
||||||
class BlueprintInstanceViewSet(UsedByMixin, ModelViewSet):
|
class BlueprintInstanceViewSet(UsedByMixin, ModelViewSet):
|
||||||
"""Blueprint instances"""
|
"""Blueprint instances"""
|
||||||
|
|
||||||
|
@ -127,3 +155,31 @@ class BlueprintInstanceViewSet(UsedByMixin, ModelViewSet):
|
||||||
blueprint = self.get_object()
|
blueprint = self.get_object()
|
||||||
apply_blueprint.delay(str(blueprint.pk)).get()
|
apply_blueprint.delay(str(blueprint.pk)).get()
|
||||||
return self.retrieve(request, *args, **kwargs)
|
return self.retrieve(request, *args, **kwargs)
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
request=BlueprintProceduralSerializer,
|
||||||
|
)
|
||||||
|
@action(
|
||||||
|
detail=False,
|
||||||
|
pagination_class=None,
|
||||||
|
filter_backends=[],
|
||||||
|
methods=["PUT"],
|
||||||
|
permission_classes=[IsAdminUser],
|
||||||
|
)
|
||||||
|
def procedural(self, request: Request) -> Response:
|
||||||
|
blueprint = Blueprint()
|
||||||
|
data = BlueprintProceduralSerializer(data=request.data)
|
||||||
|
data.is_valid(raise_exception=True)
|
||||||
|
for raw_entry in data.validated_data["entries"]:
|
||||||
|
entry = BlueprintEntrySerializer(data=raw_entry)
|
||||||
|
entry.is_valid(raise_exception=True)
|
||||||
|
blueprint.entries.append(
|
||||||
|
BlueprintEntry(
|
||||||
|
model=entry.data["model"],
|
||||||
|
state=BlueprintEntryDesiredState.PRESENT,
|
||||||
|
identifiers={},
|
||||||
|
attrs=entry.data["attrs"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(blueprint)
|
||||||
|
return Response(status=400)
|
||||||
|
|
62
schema.yml
62
schema.yml
|
@ -8513,6 +8513,39 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/GenericError'
|
$ref: '#/components/schemas/GenericError'
|
||||||
description: ''
|
description: ''
|
||||||
|
/managed/blueprints/procedural/:
|
||||||
|
put:
|
||||||
|
operationId: managed_blueprints_procedural_update
|
||||||
|
description: Blueprint instances
|
||||||
|
tags:
|
||||||
|
- managed
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/BlueprintProceduralRequest'
|
||||||
|
required: true
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/BlueprintInstance'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
/oauth2/access_tokens/:
|
/oauth2/access_tokens/:
|
||||||
get:
|
get:
|
||||||
operationId: oauth2_access_tokens_list
|
operationId: oauth2_access_tokens_list
|
||||||
|
@ -28000,6 +28033,20 @@ components:
|
||||||
* `REDIRECT` - Redirect Binding
|
* `REDIRECT` - Redirect Binding
|
||||||
* `POST` - POST Binding
|
* `POST` - POST Binding
|
||||||
* `POST_AUTO` - POST Binding with auto-confirmation
|
* `POST_AUTO` - POST Binding with auto-confirmation
|
||||||
|
BlueprintEntryRequest:
|
||||||
|
type: object
|
||||||
|
description: Validate a single blueprint entry, similar to a subset of regular
|
||||||
|
blueprints
|
||||||
|
properties:
|
||||||
|
model:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
attrs:
|
||||||
|
type: object
|
||||||
|
additionalProperties: {}
|
||||||
|
required:
|
||||||
|
- attrs
|
||||||
|
- model
|
||||||
BlueprintFile:
|
BlueprintFile:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -28101,6 +28148,17 @@ components:
|
||||||
* `error` - Error
|
* `error` - Error
|
||||||
* `orphaned` - Orphaned
|
* `orphaned` - Orphaned
|
||||||
* `unknown` - Unknown
|
* `unknown` - Unknown
|
||||||
|
BlueprintProceduralRequest:
|
||||||
|
type: object
|
||||||
|
description: Validate a procedural blueprint, which is a subset of a regular
|
||||||
|
blueprint
|
||||||
|
properties:
|
||||||
|
entries:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/BlueprintEntryRequest'
|
||||||
|
required:
|
||||||
|
- entries
|
||||||
Cache:
|
Cache:
|
||||||
type: object
|
type: object
|
||||||
description: Generic cache stats for an object
|
description: Generic cache stats for an object
|
||||||
|
@ -40941,6 +40999,6 @@ components:
|
||||||
type: apiKey
|
type: apiKey
|
||||||
in: header
|
in: header
|
||||||
name: Authorization
|
name: Authorization
|
||||||
scheme: bearer
|
scheme: Bearer
|
||||||
servers:
|
servers:
|
||||||
- url: /api/v3/
|
- url: /api/v3
|
||||||
|
|
Reference in a new issue