flows: add API for flow export
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
		| @ -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 New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer