diff --git a/authentik/blueprints/management/commands/make_blueprint_schema.py b/authentik/blueprints/management/commands/make_blueprint_schema.py index ff6ccec3e..1ac014858 100644 --- a/authentik/blueprints/management/commands/make_blueprint_schema.py +++ b/authentik/blueprints/management/commands/make_blueprint_schema.py @@ -9,7 +9,7 @@ from rest_framework.fields import Field, JSONField, UUIDField from rest_framework.serializers import Serializer from structlog.stdlib import get_logger -from authentik.blueprints.v1.importer import is_model_allowed +from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT, is_model_allowed from authentik.blueprints.v1.meta.registry import registry from authentik.lib.models import SerializerModel @@ -81,7 +81,11 @@ class Command(BaseCommand): model_instance: Model = model() if not isinstance(model_instance, SerializerModel): continue - serializer = model_instance.serializer() + serializer = model_instance.serializer( + context={ + SERIALIZER_CONTEXT_BLUEPRINT: False, + } + ) model_path = f"{model._meta.app_label}.{model._meta.model_name}" self.schema["properties"]["entries"]["items"]["oneOf"].append( self.template_entry(model_path, serializer) @@ -96,7 +100,7 @@ class Command(BaseCommand): self.schema["$defs"][def_name] = model_schema return { "type": "object", - "required": ["model", "attrs"], + "required": ["model", "identifiers"], "properties": { "model": {"const": model_path}, "id": {"type": "string"}, diff --git a/authentik/blueprints/tests/fixtures/conditional_fields.yaml b/authentik/blueprints/tests/fixtures/conditional_fields.yaml new file mode 100644 index 000000000..6720f2c28 --- /dev/null +++ b/authentik/blueprints/tests/fixtures/conditional_fields.yaml @@ -0,0 +1,41 @@ +version: 1 +metadata: + name: test conditional fields + labels: + blueprints.goauthentik.io/description: | + Some models have conditional fields that are only allowed in blueprint contexts + - Token (key) + - Application (icon) + - Source (icon) + - Flow (background) +entries: + - model: authentik_core.token + identifiers: + identifier: %(uid)s-token + attrs: + key: %(uid)s + user: %(user)s + intent: api + - model: authentik_core.application + identifiers: + slug: %(uid)s-app + attrs: + name: %(uid)s-app + icon: https://goauthentik.io/img/icon.png + - model: authentik_sources_oauth.oauthsource + identifiers: + slug: %(uid)s-source + attrs: + name: %(uid)s-source + provider_type: azuread + consumer_key: %(uid)s + consumer_secret: %(uid)s + icon: https://goauthentik.io/img/icon.png + - model: authentik_flows.flow + identifiers: + slug: %(uid)s-flow + attrs: + name: %(uid)s-flow + title: %(uid)s-flow + designation: authentication + background: https://goauthentik.io/img/icon.png diff --git a/authentik/blueprints/tests/test_v1_conditional_fields.py b/authentik/blueprints/tests/test_v1_conditional_fields.py new file mode 100644 index 000000000..1372030e5 --- /dev/null +++ b/authentik/blueprints/tests/test_v1_conditional_fields.py @@ -0,0 +1,47 @@ +"""Test blueprints v1""" +from django.test import TransactionTestCase + +from authentik.blueprints.v1.importer import Importer +from authentik.core.models import Application, Token +from authentik.core.tests.utils import create_test_admin_user +from authentik.flows.models import Flow +from authentik.lib.generators import generate_id +from authentik.lib.tests.utils import load_fixture +from authentik.sources.oauth.models import OAuthSource + + +class TestBlueprintsV1ConditionalFields(TransactionTestCase): + """Test Blueprints conditional fields""" + + def setUp(self) -> None: + user = create_test_admin_user() + self.uid = generate_id() + import_yaml = load_fixture("fixtures/conditional_fields.yaml", uid=self.uid, user=user.pk) + + importer = Importer(import_yaml) + self.assertTrue(importer.validate()[0]) + self.assertTrue(importer.apply()) + + def test_token(self): + """Test token""" + token = Token.objects.filter(identifier=f"{self.uid}-token").first() + self.assertIsNotNone(token) + self.assertEqual(token.key, self.uid) + + def test_application(self): + """Test application""" + app = Application.objects.filter(slug=f"{self.uid}-app").first() + self.assertIsNotNone(app) + self.assertEqual(app.meta_icon, "https://goauthentik.io/img/icon.png") + + def test_source(self): + """Test source""" + source = OAuthSource.objects.filter(slug=f"{self.uid}-source").first() + self.assertIsNotNone(source) + self.assertEqual(source.icon, "https://goauthentik.io/img/icon.png") + + def test_flow(self): + """Test flow""" + flow = Flow.objects.filter(slug=f"{self.uid}-flow").first() + self.assertIsNotNone(flow) + self.assertEqual(flow.background, "https://goauthentik.io/img/icon.png") diff --git a/authentik/core/api/applications.py b/authentik/core/api/applications.py index 5bf0bcb56..76b02903f 100644 --- a/authentik/core/api/applications.py +++ b/authentik/core/api/applications.py @@ -11,7 +11,7 @@ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema from guardian.shortcuts import get_objects_for_user from rest_framework.decorators import action -from rest_framework.fields import ReadOnlyField, SerializerMethodField +from rest_framework.fields import CharField, ReadOnlyField, SerializerMethodField from rest_framework.parsers import MultiPartParser from rest_framework.request import Request from rest_framework.response import Response @@ -23,6 +23,7 @@ from structlog.testing import capture_logs from authentik.admin.api.metrics import CoordinateSerializer from authentik.api.decorators import permission_required +from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT from authentik.core.api.providers import ProviderSerializer from authentik.core.api.used_by import UsedByMixin from authentik.core.models import Application, User @@ -61,6 +62,11 @@ class ApplicationSerializer(ModelSerializer): user = self.context["request"].user return app.get_launch_url(user) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + if SERIALIZER_CONTEXT_BLUEPRINT in self.context: + self.fields["icon"] = CharField(source="meta_icon", required=False) + class Meta: model = Application fields = [ diff --git a/authentik/core/api/sources.py b/authentik/core/api/sources.py index 7c405fa36..292f38cd3 100644 --- a/authentik/core/api/sources.py +++ b/authentik/core/api/sources.py @@ -5,16 +5,18 @@ from django_filters.rest_framework import DjangoFilterBackend from drf_spectacular.utils import OpenApiResponse, extend_schema from rest_framework import mixins from rest_framework.decorators import action +from rest_framework.fields import CharField, ReadOnlyField, SerializerMethodField from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.parsers import MultiPartParser from rest_framework.request import Request from rest_framework.response import Response -from rest_framework.serializers import ModelSerializer, ReadOnlyField, SerializerMethodField +from rest_framework.serializers import ModelSerializer from rest_framework.viewsets import GenericViewSet from structlog.stdlib import get_logger from authentik.api.authorization import OwnerFilter, OwnerSuperuserPermissions from authentik.api.decorators import permission_required +from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT from authentik.core.api.used_by import UsedByMixin from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer from authentik.core.models import Source, UserSourceConnection @@ -44,6 +46,11 @@ class SourceSerializer(ModelSerializer, MetaNameSerializer): return "" return obj.component + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + if SERIALIZER_CONTEXT_BLUEPRINT in self.context: + self.fields["icon"] = CharField(required=False) + class Meta: model = Source fields = [ diff --git a/authentik/flows/api/flows.py b/authentik/flows/api/flows.py index c7b17baab..07bf9f009 100644 --- a/authentik/flows/api/flows.py +++ b/authentik/flows/api/flows.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext as _ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiResponse, extend_schema from rest_framework.decorators import action -from rest_framework.fields import BooleanField, DictField, ListField, ReadOnlyField +from rest_framework.fields import BooleanField, CharField, DictField, ListField, ReadOnlyField from rest_framework.parsers import MultiPartParser from rest_framework.request import Request from rest_framework.response import Response @@ -16,7 +16,7 @@ from structlog.stdlib import get_logger from authentik.api.decorators import permission_required from authentik.blueprints.v1.exporter import FlowExporter -from authentik.blueprints.v1.importer import Importer +from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT, Importer from authentik.core.api.used_by import UsedByMixin from authentik.core.api.utils import CacheSerializer, LinkSerializer, PassiveSerializer from authentik.events.utils import sanitize_dict @@ -52,6 +52,11 @@ class FlowSerializer(ModelSerializer): """Get export URL for flow""" return reverse("authentik_api:flow-export", kwargs={"slug": flow.slug}) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + if SERIALIZER_CONTEXT_BLUEPRINT in self.context: + self.fields["background"] = CharField(required=False) + class Meta: model = Flow fields = [ diff --git a/blueprints/schema.json b/blueprints/schema.json index 4446c8377..182551633 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -45,7 +45,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -81,7 +81,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -117,7 +117,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -153,7 +153,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -189,7 +189,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -225,7 +225,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -261,7 +261,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -297,7 +297,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -333,7 +333,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -369,7 +369,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -405,7 +405,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -441,7 +441,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -477,7 +477,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -513,7 +513,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -549,7 +549,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -585,7 +585,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -621,7 +621,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -657,7 +657,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -693,7 +693,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -729,7 +729,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -765,7 +765,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -801,7 +801,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -837,7 +837,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -873,7 +873,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -909,7 +909,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -945,7 +945,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -981,7 +981,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1017,7 +1017,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1053,7 +1053,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1089,7 +1089,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1125,7 +1125,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1161,7 +1161,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1197,7 +1197,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1233,7 +1233,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1269,7 +1269,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1305,7 +1305,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1341,7 +1341,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1377,7 +1377,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1413,7 +1413,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1449,7 +1449,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1485,7 +1485,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1521,7 +1521,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1557,7 +1557,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1593,7 +1593,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1629,7 +1629,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1665,7 +1665,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1701,7 +1701,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1737,7 +1737,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1773,7 +1773,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1809,7 +1809,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1845,7 +1845,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1881,7 +1881,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1917,7 +1917,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1953,7 +1953,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -1989,7 +1989,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2025,7 +2025,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2061,7 +2061,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2097,7 +2097,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2133,7 +2133,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2169,7 +2169,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2205,7 +2205,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2241,7 +2241,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2277,7 +2277,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2313,7 +2313,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2349,7 +2349,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2385,7 +2385,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2421,7 +2421,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2457,7 +2457,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2493,7 +2493,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2529,7 +2529,7 @@ "type": "object", "required": [ "model", - "attrs" + "identifiers" ], "properties": { "model": { @@ -2867,6 +2867,11 @@ "title": "Designation", "description": "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik." }, + "background": { + "type": "string", + "minLength": 1, + "title": "Background" + }, "policy_engine_mode": { "type": "string", "enum": [ @@ -4675,6 +4680,11 @@ "minLength": 1, "title": "User path template" }, + "icon": { + "type": "string", + "minLength": 1, + "title": "Icon" + }, "server_uri": { "type": "string", "minLength": 1, @@ -4858,6 +4868,11 @@ "minLength": 1, "title": "User path template" }, + "icon": { + "type": "string", + "minLength": 1, + "title": "Icon" + }, "provider_type": { "type": "string", "enum": [ @@ -4966,6 +4981,11 @@ "null" ], "title": "Access token" + }, + "icon": { + "type": "string", + "minLength": 1, + "title": "Icon" } }, "required": [] @@ -5026,6 +5046,11 @@ "minLength": 1, "title": "User path template" }, + "icon": { + "type": "string", + "minLength": 1, + "title": "Icon" + }, "client_id": { "type": "string", "minLength": 1, @@ -5068,6 +5093,11 @@ "type": "string", "minLength": 1, "title": "Plex token" + }, + "icon": { + "type": "string", + "minLength": 1, + "title": "Icon" } }, "required": [] @@ -5128,6 +5158,11 @@ "minLength": 1, "title": "User path template" }, + "icon": { + "type": "string", + "minLength": 1, + "title": "Icon" + }, "pre_authentication_flow": { "type": "integer", "title": "Pre authentication flow", @@ -5228,6 +5263,11 @@ "type": "string", "minLength": 1, "title": "Identifier" + }, + "icon": { + "type": "string", + "minLength": 1, + "title": "Icon" } }, "required": [] @@ -8187,6 +8227,11 @@ "group": { "type": "string", "title": "Group" + }, + "icon": { + "type": "string", + "minLength": 1, + "title": "Icon" } }, "required": [] @@ -8236,6 +8281,11 @@ "expiring": { "type": "boolean", "title": "Expiring" + }, + "key": { + "type": "string", + "minLength": 1, + "title": "Key" } }, "required": [] diff --git a/website/developer-docs/blueprints/index.md b/website/developer-docs/blueprints/index.md index 0713fb9a0..68507028c 100644 --- a/website/developer-docs/blueprints/index.md +++ b/website/developer-docs/blueprints/index.md @@ -15,7 +15,7 @@ Blueprints are yaml files, whose format is described further in [File structure] - As a Blueprint instance, which is a YAML file mounted into the authentik (worker) container. This file is read and applied regularly (every 60 minutes). Multiple instances can be created for a single blueprint file, and instances can be given context key:value attributes to configure the blueprint. :::info - Starting with authentik 2022.12.1, authentik listens for file modification/creation events in the blueprint directory and will automatically trigger a discovery when a new blueprint file is created, and trigger a blueprint apply when a file is modified. + Starting with authentik 2022.12.1, authentik watches for file modification/creation events in the blueprint directory and will automatically trigger a discovery when a new blueprint file is created, and trigger a blueprint apply when a file is modified. ::: - As a Flow import, which is a YAML file uploaded via the Browser/API. This file is validated and applied directly after being uploaded, but is not further monitored/applied. diff --git a/website/developer-docs/blueprints/v1/meta.md b/website/developer-docs/blueprints/v1/meta.md index 4bc34a6ec..432ede89e 100644 --- a/website/developer-docs/blueprints/v1/meta.md +++ b/website/developer-docs/blueprints/v1/meta.md @@ -1,6 +1,6 @@ # Meta models -Since blueprints have a pretty strict mapping of each entry mapping to an instance of a model in the database, _meta models_ have been added to trigger other actions within authentik that don't directly map to a model. +Since blueprints have a pretty strict mapping of each entry mapping to an instance of a model in the database, _meta models_ exist to trigger other actions within authentik that don't directly map to a model. ### `authentik_blueprints.metaapplyblueprint` diff --git a/website/developer-docs/blueprints/v1/models.md b/website/developer-docs/blueprints/v1/models.md index 2303aaf31..b6d07cf5d 100644 --- a/website/developer-docs/blueprints/v1/models.md +++ b/website/developer-docs/blueprints/v1/models.md @@ -25,3 +25,65 @@ For example: user: !KeyOf my-user intent: api ``` + +### `authentik_core.application` + +:::info +Requires authentik 2023.5 +::: + +Application icons can be directly set to URLs with the `icon` field. + +For example: + +```yaml +# [...] +- model: authentik_core.application + identifiers: + slug: my-app + attrs: + name: My App + icon: https://goauthentik.io/img/icon.png +``` + +### `authentik_sources_oauth.oauthsource`, `authentik_sources_saml.samlsource`, `authentik_sources_plex.plexsource` + +:::info +Requires authentik 2023.5 +::: + +Source icons can be directly set to URLs with the `icon` field. + +For example: + +```yaml +# [...] +- model: authentik_sources_oauth.oauthsource + identifiers: + slug: my-source + attrs: + name: My source + icon: https://goauthentik.io/img/icon.png +``` + +### `authentik_flows.flow` + +:::info +Requires authentik 2023.5 +::: + +Flow backgrounds can be directly set to URLs with the `background` field. + +For example: + +```yaml +# [...] +- model: authentik_flows.flow + identifiers: + slug: my-flow + attrs: + name: my-flow + title: My flow + designation: authentication + background: https://goauthentik.io/img/icon.png +``` diff --git a/website/docs/flow/context/index.md b/website/docs/flow/context/index.md index b4cc18954..a98512c25 100644 --- a/website/docs/flow/context/index.md +++ b/website/docs/flow/context/index.md @@ -1,6 +1,5 @@ --- title: Flow Context -toc_max_heading_level: 5 --- Each flow execution has an independent _context_. This context holds all of the arbitrary data about that specific flow, data which can then be used and transformed by stages and policies. diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index a9ef08f48..f81112e69 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -117,6 +117,9 @@ module.exports = { ], copyright: `Copyright © ${new Date().getFullYear()} Authentik Security Inc. Built with Docusaurus.`, }, + tableOfContents: { + maxHeadingLevel: 5, + }, colorMode: { respectPrefersColorScheme: true, }, diff --git a/website/docusaurus.docs-only.js b/website/docusaurus.docs-only.js index 7908e34ee..9db7171ee 100644 --- a/website/docusaurus.docs-only.js +++ b/website/docusaurus.docs-only.js @@ -49,6 +49,7 @@ module.exports = { }, footer: mainConfig.themeConfig.footer, colorMode: mainConfig.themeConfig.colorMode, + tableOfContents: mainConfig.themeConfig.tableOfContents, }, presets: [ [ diff --git a/website/sidebarsDev.js b/website/sidebarsDev.js index cbbc255e1..d4545345b 100644 --- a/website/sidebarsDev.js +++ b/website/sidebarsDev.js @@ -16,8 +16,15 @@ module.exports = { "blueprints/v1/structure", "blueprints/v1/tags", "blueprints/v1/example", - "blueprints/v1/models", - "blueprints/v1/meta", + { + type: "category", + label: "Models", + link: { + type: "doc", + id: "blueprints/v1/models", + }, + items: ["blueprints/v1/meta"], + }, ], }, {