authentik.core.setup.views
1from functools import lru_cache 2from http import HTTPMethod, HTTPStatus 3 4from django.contrib.staticfiles import finders 5from django.db import transaction 6from django.http import HttpRequest, HttpResponse 7from django.shortcuts import redirect 8from django.urls import reverse 9from django.views import View 10from structlog.stdlib import get_logger 11 12from authentik.blueprints.models import BlueprintInstance 13from authentik.core.apps import Setup 14from authentik.flows.models import Flow, FlowAuthenticationRequirement, in_memory_stage 15from authentik.flows.planner import FlowPlanner 16from authentik.flows.stage import StageView 17 18LOGGER = get_logger() 19FLOW_CONTEXT_START_BY = "goauthentik.io/core/setup/started-by" 20 21 22@lru_cache 23def read_static(path: str) -> str | None: 24 result = finders.find(path) 25 if not result: 26 return None 27 with open(result, encoding="utf8") as _file: 28 return _file.read() 29 30 31class SetupView(View): 32 33 setup_flow_slug = "initial-setup" 34 35 def dispatch(self, request: HttpRequest, *args, **kwargs): 36 if request.method != HTTPMethod.HEAD and Setup.get(): 37 return redirect(reverse("authentik_core:root-redirect")) 38 return super().dispatch(request, *args, **kwargs) 39 40 def head(self, request: HttpRequest, *args, **kwargs): 41 if Setup.get(): 42 return HttpResponse(status=HTTPStatus.SERVICE_UNAVAILABLE) 43 if not Flow.objects.filter(slug=self.setup_flow_slug).exists(): 44 return HttpResponse(status=HTTPStatus.SERVICE_UNAVAILABLE) 45 return HttpResponse(status=HTTPStatus.OK) 46 47 def get(self, request: HttpRequest): 48 flow = Flow.objects.filter(slug=self.setup_flow_slug).first() 49 if not flow: 50 LOGGER.info("Setup flow does not exist yet, waiting for worker to finish") 51 return HttpResponse( 52 read_static("dist/standalone/loading/startup.html"), 53 status=HTTPStatus.SERVICE_UNAVAILABLE, 54 ) 55 planner = FlowPlanner(flow) 56 plan = planner.plan(request, {FLOW_CONTEXT_START_BY: "setup"}) 57 plan.append_stage(in_memory_stage(PostSetupStageView)) 58 return plan.to_redirect(request, flow) 59 60 61class PostSetupStageView(StageView): 62 """Run post-setup tasks""" 63 64 def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: 65 """Wrapper when this stage gets hit with a post request""" 66 return self.get(request, *args, **kwargs) 67 68 def get(self, requeset: HttpRequest, *args, **kwargs): 69 with transaction.atomic(): 70 # Remember we're setup 71 Setup.set(True) 72 # Disable OOBE Blueprints 73 BlueprintInstance.objects.filter( 74 **{"metadata__labels__blueprints.goauthentik.io/system-oobe": "true"} 75 ).update(enabled=False) 76 # Make flow inaccessible 77 Flow.objects.filter(slug="initial-setup").update( 78 authentication=FlowAuthenticationRequirement.REQUIRE_SUPERUSER 79 ) 80 return self.executor.stage_ok()
LOGGER =
<BoundLoggerLazyProxy(logger=None, wrapper_class=None, processors=None, context_class=None, initial_values={}, logger_factory_args=())>
FLOW_CONTEXT_START_BY =
'goauthentik.io/core/setup/started-by'
@lru_cache
def
read_static(path: str) -> str | None:
class
SetupView(django.views.generic.base.View):
32class SetupView(View): 33 34 setup_flow_slug = "initial-setup" 35 36 def dispatch(self, request: HttpRequest, *args, **kwargs): 37 if request.method != HTTPMethod.HEAD and Setup.get(): 38 return redirect(reverse("authentik_core:root-redirect")) 39 return super().dispatch(request, *args, **kwargs) 40 41 def head(self, request: HttpRequest, *args, **kwargs): 42 if Setup.get(): 43 return HttpResponse(status=HTTPStatus.SERVICE_UNAVAILABLE) 44 if not Flow.objects.filter(slug=self.setup_flow_slug).exists(): 45 return HttpResponse(status=HTTPStatus.SERVICE_UNAVAILABLE) 46 return HttpResponse(status=HTTPStatus.OK) 47 48 def get(self, request: HttpRequest): 49 flow = Flow.objects.filter(slug=self.setup_flow_slug).first() 50 if not flow: 51 LOGGER.info("Setup flow does not exist yet, waiting for worker to finish") 52 return HttpResponse( 53 read_static("dist/standalone/loading/startup.html"), 54 status=HTTPStatus.SERVICE_UNAVAILABLE, 55 ) 56 planner = FlowPlanner(flow) 57 plan = planner.plan(request, {FLOW_CONTEXT_START_BY: "setup"}) 58 plan.append_stage(in_memory_stage(PostSetupStageView)) 59 return plan.to_redirect(request, flow)
Intentionally simple parent class for all views. Only implements dispatch-by-method and simple sanity checking.
def
head(self, request: django.http.request.HttpRequest, *args, **kwargs):
41 def head(self, request: HttpRequest, *args, **kwargs): 42 if Setup.get(): 43 return HttpResponse(status=HTTPStatus.SERVICE_UNAVAILABLE) 44 if not Flow.objects.filter(slug=self.setup_flow_slug).exists(): 45 return HttpResponse(status=HTTPStatus.SERVICE_UNAVAILABLE) 46 return HttpResponse(status=HTTPStatus.OK)
def
get(self, request: django.http.request.HttpRequest):
48 def get(self, request: HttpRequest): 49 flow = Flow.objects.filter(slug=self.setup_flow_slug).first() 50 if not flow: 51 LOGGER.info("Setup flow does not exist yet, waiting for worker to finish") 52 return HttpResponse( 53 read_static("dist/standalone/loading/startup.html"), 54 status=HTTPStatus.SERVICE_UNAVAILABLE, 55 ) 56 planner = FlowPlanner(flow) 57 plan = planner.plan(request, {FLOW_CONTEXT_START_BY: "setup"}) 58 plan.append_stage(in_memory_stage(PostSetupStageView)) 59 return plan.to_redirect(request, flow)
62class PostSetupStageView(StageView): 63 """Run post-setup tasks""" 64 65 def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: 66 """Wrapper when this stage gets hit with a post request""" 67 return self.get(request, *args, **kwargs) 68 69 def get(self, requeset: HttpRequest, *args, **kwargs): 70 with transaction.atomic(): 71 # Remember we're setup 72 Setup.set(True) 73 # Disable OOBE Blueprints 74 BlueprintInstance.objects.filter( 75 **{"metadata__labels__blueprints.goauthentik.io/system-oobe": "true"} 76 ).update(enabled=False) 77 # Make flow inaccessible 78 Flow.objects.filter(slug="initial-setup").update( 79 authentication=FlowAuthenticationRequirement.REQUIRE_SUPERUSER 80 ) 81 return self.executor.stage_ok()
Run post-setup tasks
def
post( self, request: django.http.request.HttpRequest, *args, **kwargs) -> django.http.response.HttpResponse:
65 def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: 66 """Wrapper when this stage gets hit with a post request""" 67 return self.get(request, *args, **kwargs)
Wrapper when this stage gets hit with a post request
def
get(self, requeset: django.http.request.HttpRequest, *args, **kwargs):
69 def get(self, requeset: HttpRequest, *args, **kwargs): 70 with transaction.atomic(): 71 # Remember we're setup 72 Setup.set(True) 73 # Disable OOBE Blueprints 74 BlueprintInstance.objects.filter( 75 **{"metadata__labels__blueprints.goauthentik.io/system-oobe": "true"} 76 ).update(enabled=False) 77 # Make flow inaccessible 78 Flow.objects.filter(slug="initial-setup").update( 79 authentication=FlowAuthenticationRequirement.REQUIRE_SUPERUSER 80 ) 81 return self.executor.stage_ok()