diff --git a/authentik/blueprints/tests/fixtures/conditional_fields.yaml b/authentik/blueprints/tests/fixtures/conditional_fields.yaml index 5ce07719cd..4e87f5f155 100644 --- a/authentik/blueprints/tests/fixtures/conditional_fields.yaml +++ b/authentik/blueprints/tests/fixtures/conditional_fields.yaml @@ -8,45 +8,62 @@ metadata: - Application (icon) - Source (icon) - Flow (background) + - Endpoint Enrollment token (key) 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 - - model: authentik_core.user - identifiers: - username: "%(uid)s" - attrs: - name: "%(uid)s" - password: "%(uid)s" - - model: authentik_core.user - identifiers: - username: "%(uid)s-no-password" - attrs: - name: "%(uid)s" + token: + - model: authentik_core.token + identifiers: + identifier: "%(uid)s-token" + attrs: + key: "%(uid)s" + user: "%(user)s" + intent: api + app: + - model: authentik_core.application + identifiers: + slug: "%(uid)s-app" + attrs: + name: "%(uid)s-app" + icon: https://goauthentik.io/img/icon.png + source: + - 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 + flow: + - 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 + user: + - model: authentik_core.user + identifiers: + username: "%(uid)s" + attrs: + name: "%(uid)s" + password: "%(uid)s" + - model: authentik_core.user + identifiers: + username: "%(uid)s-no-password" + attrs: + name: "%(uid)s" + endpoint: + - model: authentik_endpoints_connectors_agent.agentconnector + id: connector + identifiers: + name: "%(uid)s" + - model: authentik_endpoints_connectors_agent.enrollmenttoken + identifiers: + name: "%(uid)s" + attrs: + key: "%(uid)s" + connector: !KeyOf connector diff --git a/authentik/blueprints/tests/test_v1_conditional_fields.py b/authentik/blueprints/tests/test_v1_conditional_fields.py index d2fc78108b..d3207ff090 100644 --- a/authentik/blueprints/tests/test_v1_conditional_fields.py +++ b/authentik/blueprints/tests/test_v1_conditional_fields.py @@ -5,6 +5,7 @@ from django.test import TransactionTestCase from authentik.blueprints.v1.importer import Importer from authentik.core.models import Token, User from authentik.core.tests.utils import create_test_admin_user +from authentik.endpoints.connectors.agent.models import EnrollmentToken from authentik.lib.generators import generate_id from authentik.lib.tests.utils import load_fixture @@ -29,12 +30,18 @@ class TestBlueprintsV1ConditionalFields(TransactionTestCase): def test_user(self): """Test user""" - user: User = User.objects.filter(username=self.uid).first() + user = User.objects.filter(username=self.uid).first() self.assertIsNotNone(user) self.assertTrue(user.check_password(self.uid)) def test_user_null(self): """Test user""" - user: User = User.objects.filter(username=f"{self.uid}-no-password").first() + user = User.objects.filter(username=f"{self.uid}-no-password").first() self.assertIsNotNone(user) self.assertFalse(user.has_usable_password()) + + def test_enrollment_token(self): + """Test endpoint enrollment token""" + token = EnrollmentToken.objects.filter(name=self.uid).first() + self.assertIsNotNone(token) + self.assertEqual(token.key, self.uid) diff --git a/authentik/endpoints/connectors/agent/api/enrollment_tokens.py b/authentik/endpoints/connectors/agent/api/enrollment_tokens.py index eb6be7b947..7adccb6c1e 100644 --- a/authentik/endpoints/connectors/agent/api/enrollment_tokens.py +++ b/authentik/endpoints/connectors/agent/api/enrollment_tokens.py @@ -1,9 +1,11 @@ from drf_spectacular.utils import OpenApiResponse, extend_schema from rest_framework.decorators import action +from rest_framework.fields import CharField from rest_framework.request import Request from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet +from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT from authentik.core.api.tokens import TokenViewSerializer from authentik.core.api.used_by import UsedByMixin from authentik.core.api.utils import ModelSerializer @@ -19,6 +21,11 @@ class EnrollmentTokenSerializer(ModelSerializer): source="device_group", read_only=True, required=False ) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + if SERIALIZER_CONTEXT_BLUEPRINT in self.context: + self.fields["key"] = CharField(required=False) + class Meta: model = EnrollmentToken fields = [ diff --git a/blueprints/schema.json b/blueprints/schema.json index 9c208269cd..033329a7f6 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -6276,6 +6276,11 @@ ], "format": "date-time", "title": "Expires" + }, + "key": { + "type": "string", + "minLength": 1, + "title": "Key" } }, "required": [] diff --git a/website/docs/customize/blueprints/v1/models.mdx b/website/docs/customize/blueprints/v1/models.mdx index 58a67e97ef..cc23122c73 100644 --- a/website/docs/customize/blueprints/v1/models.mdx +++ b/website/docs/customize/blueprints/v1/models.mdx @@ -181,3 +181,24 @@ Example with multiple bindings to the same application: attrs: group: !Find [authentik_core.group, [name, admins]] ``` + +## `authentik_endpoints_connectors_agent.enrollmenttoken` + +### `key` + +Via the standard API, a token's key cannot be changed, it can only be rotated. This is to ensure a high entropy in it's key, and to prevent insecure data from being used. However, when provisioning enrollment tokens via a blueprint, it may be required to set a token to an existing value. + +With blueprints, the field `key` can be set, to set the enrollment token's key to any value. + +For example: + +```yaml +# [...] +- model: authentik_endpoints_connectors_agent.enrollmenttoken + state: present + identifiers: + name: my-token + attrs: + key: this-should-be-a-long-value + connector: !KeyOf connector +```