flows: add API for flow export
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
93bd95436f
commit
a57d524273
|
@ -148,11 +148,6 @@ urlpatterns = [
|
||||||
flows.FlowDebugExecuteView.as_view(),
|
flows.FlowDebugExecuteView.as_view(),
|
||||||
name="flow-execute",
|
name="flow-execute",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"flows/<uuid:pk>/export/",
|
|
||||||
flows.FlowExportView.as_view(),
|
|
||||||
name="flow-export",
|
|
||||||
),
|
|
||||||
# Property Mappings
|
# Property Mappings
|
||||||
path(
|
path(
|
||||||
"property-mappings/create/",
|
"property-mappings/create/",
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
|
||||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||||
)
|
)
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.http import HttpRequest, HttpResponse, JsonResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic import DetailView, FormView, UpdateView
|
from django.views.generic import DetailView, FormView, UpdateView
|
||||||
|
@ -15,8 +15,6 @@ from authentik.flows.exceptions import FlowNonApplicableException
|
||||||
from authentik.flows.forms import FlowForm, FlowImportForm
|
from authentik.flows.forms import FlowForm, FlowImportForm
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||||
from authentik.flows.transfer.common import DataclassEncoder
|
|
||||||
from authentik.flows.transfer.exporter import FlowExporter
|
|
||||||
from authentik.flows.transfer.importer import FlowImporter
|
from authentik.flows.transfer.importer import FlowImporter
|
||||||
from authentik.flows.views import SESSION_KEY_PLAN, FlowPlanner
|
from authentik.flows.views import SESSION_KEY_PLAN, FlowPlanner
|
||||||
from authentik.lib.utils.urls import redirect_with_qs
|
from authentik.lib.utils.urls import redirect_with_qs
|
||||||
|
@ -108,19 +106,3 @@ class FlowImportView(LoginRequiredMixin, FormView):
|
||||||
else:
|
else:
|
||||||
messages.success(self.request, _("Successfully imported flow."))
|
messages.success(self.request, _("Successfully imported flow."))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class FlowExportView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
|
||||||
"""Export Flow"""
|
|
||||||
|
|
||||||
model = Flow
|
|
||||||
permission_required = "authentik_flows.export_flow"
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def get(self, request: HttpRequest, pk: str) -> HttpResponse:
|
|
||||||
"""Debug exectue flow, setting the current user as pending user"""
|
|
||||||
flow: Flow = self.get_object()
|
|
||||||
exporter = FlowExporter(flow)
|
|
||||||
response = JsonResponse(exporter.export(), encoder=DataclassEncoder, safe=False)
|
|
||||||
response["Content-Disposition"] = f'attachment; filename="{flow.slug}.akflow"'
|
|
||||||
return response
|
|
||||||
|
|
|
@ -3,10 +3,13 @@ from dataclasses import dataclass
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
|
from django.http.response import JsonResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
from drf_yasg2 import openapi
|
||||||
from drf_yasg2.utils import swagger_auto_schema
|
from drf_yasg2.utils import swagger_auto_schema
|
||||||
from guardian.shortcuts import get_objects_for_user
|
from guardian.shortcuts import get_objects_for_user
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import (
|
from rest_framework.serializers import (
|
||||||
|
@ -20,6 +23,8 @@ from rest_framework.viewsets import ModelViewSet
|
||||||
from authentik.core.api.utils import CacheSerializer
|
from authentik.core.api.utils import CacheSerializer
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
from authentik.flows.planner import cache_key
|
from authentik.flows.planner import cache_key
|
||||||
|
from authentik.flows.transfer.common import DataclassEncoder
|
||||||
|
from authentik.flows.transfer.exporter import FlowExporter
|
||||||
|
|
||||||
|
|
||||||
class FlowSerializer(ModelSerializer):
|
class FlowSerializer(ModelSerializer):
|
||||||
|
@ -87,6 +92,24 @@ class FlowViewSet(ModelViewSet):
|
||||||
"""Info about cached flows"""
|
"""Info about cached flows"""
|
||||||
return Response(data={"count": len(cache.keys("flow_*"))})
|
return Response(data={"count": len(cache.keys("flow_*"))})
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
responses={
|
||||||
|
"200": openapi.Response(
|
||||||
|
"File Attachment", schema=openapi.Schema(type=openapi.TYPE_FILE)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@action(detail=True)
|
||||||
|
def export(self, request: Request, slug: str) -> Response:
|
||||||
|
"""Export flow to .akflow file"""
|
||||||
|
flow = self.get_object()
|
||||||
|
if not request.user.has_perm("authentik_flows.export_flow", flow):
|
||||||
|
raise PermissionDenied()
|
||||||
|
exporter = FlowExporter(flow)
|
||||||
|
response = JsonResponse(exporter.export(), encoder=DataclassEncoder, safe=False)
|
||||||
|
response["Content-Disposition"] = f'attachment; filename="{flow.slug}.akflow"'
|
||||||
|
return response
|
||||||
|
|
||||||
@swagger_auto_schema(responses={200: FlowDiagramSerializer()})
|
@swagger_auto_schema(responses={200: FlowDiagramSerializer()})
|
||||||
@action(detail=True, methods=["get"])
|
@action(detail=True, methods=["get"])
|
||||||
def diagram(self, request: Request, slug: str) -> Response:
|
def diagram(self, request: Request, slug: str) -> Response:
|
||||||
|
|
20
swagger.yaml
20
swagger.yaml
|
@ -2800,6 +2800,26 @@ paths:
|
||||||
type: string
|
type: string
|
||||||
format: slug
|
format: slug
|
||||||
pattern: ^[-a-zA-Z0-9_]+$
|
pattern: ^[-a-zA-Z0-9_]+$
|
||||||
|
/flows/instances/{slug}/export/:
|
||||||
|
get:
|
||||||
|
operationId: flows_instances_export
|
||||||
|
description: Export flow to .akflow file
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: File Attachment
|
||||||
|
schema:
|
||||||
|
type: file
|
||||||
|
tags:
|
||||||
|
- flows
|
||||||
|
parameters:
|
||||||
|
- name: slug
|
||||||
|
in: path
|
||||||
|
description: Visible in the URL.
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
format: slug
|
||||||
|
pattern: ^[-a-zA-Z0-9_]+$
|
||||||
/outposts/outposts/:
|
/outposts/outposts/:
|
||||||
get:
|
get:
|
||||||
operationId: outposts_outposts_list
|
operationId: outposts_outposts_list
|
||||||
|
|
|
@ -81,7 +81,7 @@ export class FlowListPage extends TablePage<Flow> {
|
||||||
<a class="pf-c-button pf-m-secondary ak-root-link" href="${AdminURLManager.flows(`${item.pk}/execute/?next=/%23${window.location.href}`)}">
|
<a class="pf-c-button pf-m-secondary ak-root-link" href="${AdminURLManager.flows(`${item.pk}/execute/?next=/%23${window.location.href}`)}">
|
||||||
${gettext("Execute")}
|
${gettext("Execute")}
|
||||||
</a>
|
</a>
|
||||||
<a class="pf-c-button pf-m-secondary ak-root-link" href="${AdminURLManager.flows(`${item.pk}/export/`)}">
|
<a class="pf-c-button pf-m-secondary ak-root-link" href="${`${DEFAULT_CONFIG.basePath}/flows/instances/${item.slug}/export/`}">
|
||||||
${gettext("Export")}
|
${gettext("Export")}
|
||||||
</a>
|
</a>
|
||||||
`,
|
`,
|
||||||
|
|
Reference in a new issue