flows: add API for flow export

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-03-18 14:35:43 +01:00
parent 93bd95436f
commit a57d524273
5 changed files with 45 additions and 25 deletions

View File

@ -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/",

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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>
`, `,