mirror of
https://github.com/goauthentik/authentik
synced 2026-05-15 03:16:22 +02:00
Compare commits
35 Commits
web/main/d
...
core/objec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b96c9cf1d6 | ||
|
|
a3c50ae92a | ||
|
|
3ef36b9e9e | ||
|
|
691e173cad | ||
|
|
68a6b04749 | ||
|
|
046dbdabe2 | ||
|
|
aae1b32c61 | ||
|
|
87a95eddea | ||
|
|
a555570418 | ||
|
|
aa8463a6a8 | ||
|
|
e616cb8bac | ||
|
|
ddadbba685 | ||
|
|
b70dfe1cf0 | ||
|
|
aba6932a2d | ||
|
|
4b66289798 | ||
|
|
ba6060be77 | ||
|
|
e835418e76 | ||
|
|
e67c78ea85 | ||
|
|
5bbe099528 | ||
|
|
949b5d671a | ||
|
|
4eef34e223 | ||
|
|
e58cfd3b70 | ||
|
|
4ea9451e5f | ||
|
|
d6867895aa | ||
|
|
4727a0a69a | ||
|
|
1c226196b4 | ||
|
|
74f0def068 | ||
|
|
59afc1c7d9 | ||
|
|
6fda71763a | ||
|
|
059acf477e | ||
|
|
d5b9071fa7 | ||
|
|
607b4d6a7c | ||
|
|
09cb76bf7c | ||
|
|
a4e18ba849 | ||
|
|
d70bdc68ec |
2
.github/actions/setup/action.yml
vendored
2
.github/actions/setup/action.yml
vendored
@@ -64,7 +64,7 @@ runs:
|
||||
rustflags: ""
|
||||
- name: Setup rust dependencies
|
||||
if: ${{ contains(inputs.dependencies, 'rust') }}
|
||||
uses: taiki-e/install-action@e3134ec54b36203e18f2d1e80652058bd078dd91 # v2
|
||||
uses: taiki-e/install-action@ec28e287910af896fd98e04056d31fa68607e7ad # v2
|
||||
with:
|
||||
tool: cargo-deny cargo-machete cargo-llvm-cov nextest
|
||||
- name: Setup node (web)
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
"""Meta API"""
|
||||
|
||||
from django.apps import apps
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.fields import BooleanField, CharField
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from authentik.api.validation import validate
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.core.models import AttributesMixin
|
||||
from authentik.lib.api import Models
|
||||
from authentik.lib.utils.reflection import get_apps
|
||||
|
||||
@@ -36,12 +39,19 @@ class AppsViewSet(ViewSet):
|
||||
class ModelViewSet(ViewSet):
|
||||
"""Read-only view list all installed models"""
|
||||
|
||||
class ModelFilterSerializer(PassiveSerializer):
|
||||
filter_has_attributes = BooleanField(allow_null=True, default=None)
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(responses={200: AppSerializer(many=True)})
|
||||
def list(self, request: Request) -> Response:
|
||||
@extend_schema(responses={200: AppSerializer(many=True)}, parameters=[ModelFilterSerializer])
|
||||
@validate(ModelFilterSerializer, "query")
|
||||
def list(self, request: Request, query: ModelFilterSerializer) -> Response:
|
||||
"""Read-only view list all installed models"""
|
||||
data = []
|
||||
for name, label in Models.choices:
|
||||
if query.validated_data["filter_has_attributes"]:
|
||||
if not issubclass(apps.get_model(name), AttributesMixin):
|
||||
continue
|
||||
data.append({"name": name, "label": label})
|
||||
return Response(AppSerializer(data, many=True).data)
|
||||
|
||||
@@ -6,6 +6,7 @@ from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
||||
from authentik.core.api.object_attributes import AttributesMixinSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
from authentik.core.models import (
|
||||
@@ -14,7 +15,7 @@ from authentik.core.models import (
|
||||
)
|
||||
|
||||
|
||||
class ApplicationEntitlementSerializer(ModelSerializer):
|
||||
class ApplicationEntitlementSerializer(AttributesMixinSerializer, ModelSerializer):
|
||||
"""ApplicationEntitlement Serializer"""
|
||||
|
||||
def validate_app(self, app: Application) -> Application:
|
||||
|
||||
@@ -30,6 +30,7 @@ from authentik.api.search.fields import (
|
||||
JSONSearchField,
|
||||
)
|
||||
from authentik.api.validation import validate
|
||||
from authentik.core.api.object_attributes import AttributesMixinSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import JSONDictField, ModelSerializer, PassiveSerializer
|
||||
from authentik.core.models import Group, User
|
||||
@@ -146,7 +147,7 @@ class RelatedGroupSerializer(ModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class GroupSerializer(ModelSerializer):
|
||||
class GroupSerializer(AttributesMixinSerializer, ModelSerializer):
|
||||
"""Group Serializer"""
|
||||
|
||||
attributes = JSONDictField(required=False)
|
||||
|
||||
94
authentik/core/api/object_attributes.py
Normal file
94
authentik/core/api/object_attributes.py
Normal file
@@ -0,0 +1,94 @@
|
||||
from typing import Any
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import CharField, SerializerMethodField
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
from authentik.core.models import AttributesMixin, ObjectAttribute
|
||||
from authentik.lib.utils.dict import get_path_from_dict
|
||||
|
||||
|
||||
class AttributesMixinSerializer(ModelSerializer):
|
||||
|
||||
def validate(self, data: dict[str, Any]) -> dict[str, Any]:
|
||||
model = self.Meta.model
|
||||
attrs = data.get("attributes", {})
|
||||
attributes = ObjectAttribute.objects.filter(
|
||||
object_type=ContentType.objects.get_for_model(model),
|
||||
enabled=True,
|
||||
)
|
||||
for attr in attributes:
|
||||
value = get_path_from_dict(attrs, attr.key)
|
||||
attr.run_validation(value)
|
||||
return data
|
||||
|
||||
|
||||
class ContentTypeSerializer(ModelSerializer):
|
||||
app_label = CharField(read_only=True)
|
||||
model = CharField(read_only=True)
|
||||
verbose_name_plural = SerializerMethodField()
|
||||
fully_qualified_model = SerializerMethodField()
|
||||
|
||||
def get_fully_qualified_model(self, ct: ContentType) -> str:
|
||||
return f"{ct.app_label}.{ct.model}"
|
||||
|
||||
def get_verbose_name_plural(self, ct: ContentType) -> str:
|
||||
return ct.model_class()._meta.verbose_name_plural
|
||||
|
||||
class Meta:
|
||||
model = ContentType
|
||||
fields = ("id", "app_label", "model", "verbose_name_plural", "fully_qualified_model")
|
||||
|
||||
|
||||
class ObjectAttributeSerializer(ModelSerializer):
|
||||
|
||||
object_type = CharField()
|
||||
object_type_obj = ContentTypeSerializer(read_only=True, source="object_type")
|
||||
|
||||
def validate_object_type(self, fqm: str) -> ContentType:
|
||||
app_label, _, model = fqm.partition(".")
|
||||
ct = ContentType.objects.filter(app_label=app_label, model=model).first()
|
||||
if not ct or not issubclass(ct.model_class(), AttributesMixin):
|
||||
raise ValidationError("Invalid object type")
|
||||
return ct
|
||||
|
||||
def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
||||
if attrs.get("is_unique") and attrs.get("is_array"):
|
||||
raise ValidationError(_("Unique cannot be enabled for arrays."))
|
||||
return super().validate(attrs)
|
||||
|
||||
class Meta:
|
||||
model = ObjectAttribute
|
||||
fields = [
|
||||
"pk",
|
||||
"object_type",
|
||||
"object_type_obj",
|
||||
"enabled",
|
||||
"created",
|
||||
"key",
|
||||
"label",
|
||||
"last_updated",
|
||||
"regex",
|
||||
"type",
|
||||
"group",
|
||||
"managed",
|
||||
"is_unique",
|
||||
"is_required",
|
||||
"is_array",
|
||||
]
|
||||
extra_kwargs = {
|
||||
"last_updated": {"read_only": True},
|
||||
"created": {"read_only": True},
|
||||
"pk": {"read_only": True},
|
||||
}
|
||||
|
||||
|
||||
class ObjectAttributeViewSet(ModelViewSet):
|
||||
serializer_class = ObjectAttributeSerializer
|
||||
queryset = ObjectAttribute.objects.all()
|
||||
filterset_fields = ["object_type__model", "object_type__app_label", "enabled"]
|
||||
search_fields = ["key", "label", "group", "object_type__model", "object_type__app_label"]
|
||||
ordering = ["key"]
|
||||
@@ -65,6 +65,7 @@ from authentik.api.search.fields import (
|
||||
from authentik.api.validation import validate
|
||||
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
||||
from authentik.brands.models import Brand
|
||||
from authentik.core.api.object_attributes import AttributesMixinSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import (
|
||||
JSONDictField,
|
||||
@@ -134,7 +135,7 @@ class PartialGroupSerializer(ModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class UserSerializer(ModelSerializer):
|
||||
class UserSerializer(AttributesMixinSerializer, ModelSerializer):
|
||||
"""User Serializer"""
|
||||
|
||||
is_superuser = SerializerMethodField()
|
||||
|
||||
62
authentik/core/migrations/0058_objectattribute.py
Normal file
62
authentik/core/migrations/0058_objectattribute.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# Generated by Django 5.2.13 on 2026-04-11 18:35
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_core", "0057_remove_user_groups_remove_user_user_permissions_and_more"),
|
||||
("contenttypes", "0002_remove_content_type_name"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ObjectAttribute",
|
||||
fields=[
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("last_updated", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"managed",
|
||||
models.TextField(
|
||||
default=None,
|
||||
help_text="Objects that are managed by authentik. These objects are created and updated automatically. This flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update.",
|
||||
null=True,
|
||||
unique=True,
|
||||
verbose_name="Managed by authentik",
|
||||
),
|
||||
),
|
||||
(
|
||||
"attribute_id",
|
||||
models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False),
|
||||
),
|
||||
("enabled", models.BooleanField(default=True)),
|
||||
("label", models.TextField()),
|
||||
("group", models.TextField(blank=True)),
|
||||
("key", models.TextField()),
|
||||
(
|
||||
"type",
|
||||
models.TextField(
|
||||
choices=[("text", "Text"), ("number", "Number"), ("boolean", "Boolean")]
|
||||
),
|
||||
),
|
||||
("is_unique", models.BooleanField(default=False)),
|
||||
("is_required", models.BooleanField(default=False)),
|
||||
("regex", models.TextField(blank=True)),
|
||||
("is_array", models.BooleanField(default=False)),
|
||||
(
|
||||
"object_type",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="contenttypes.contenttype"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Object Attribute",
|
||||
"verbose_name_plural": "Object Attributes",
|
||||
"unique_together": {("object_type", "key", "enabled")},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -13,6 +13,7 @@ from deepmerge import always_merger
|
||||
from django.contrib.auth.hashers import check_password, identify_hasher
|
||||
from django.contrib.auth.models import AbstractUser, Permission
|
||||
from django.contrib.auth.models import UserManager as DjangoUserManager
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.sessions.base_session import AbstractBaseSession
|
||||
from django.core.validators import validate_slug
|
||||
from django.db import models
|
||||
@@ -26,6 +27,7 @@ from guardian.models import RoleModelPermission, RoleObjectPermission
|
||||
from model_utils.managers import InheritanceManager
|
||||
from psqlextra.indexes import UniqueIndex
|
||||
from psqlextra.models import PostgresMaterializedViewModel
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.serializers import Serializer
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
@@ -1392,3 +1394,61 @@ class AuthenticatedSession(SerializerModel):
|
||||
session=Session.objects.filter(session_key=request.session.session_key).first(),
|
||||
user=user,
|
||||
)
|
||||
|
||||
|
||||
class ObjectAttribute(SerializerModel, ManagedModel, CreatedUpdatedModel):
|
||||
"""User-defined schema for models' `attributes` JSON field."""
|
||||
|
||||
class AttributeType(models.TextChoices):
|
||||
TEXT = "text"
|
||||
NUMBER = "number"
|
||||
BOOLEAN = "boolean"
|
||||
|
||||
attribute_id = models.UUIDField(default=uuid4, primary_key=True)
|
||||
|
||||
enabled = models.BooleanField(default=True)
|
||||
|
||||
object_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
label = models.TextField()
|
||||
group = models.TextField(blank=True)
|
||||
key = models.TextField()
|
||||
|
||||
type = models.TextField(choices=AttributeType.choices)
|
||||
is_unique = models.BooleanField(default=False)
|
||||
is_required = models.BooleanField(default=False)
|
||||
regex = models.TextField(blank=True)
|
||||
is_array = models.BooleanField(default=False)
|
||||
|
||||
def run_validation(self, value: Any) -> None:
|
||||
err_key = f"attributes_{self.key.replace(".", "_")}"
|
||||
if self.is_required and value is None:
|
||||
raise ValidationError({err_key: _("This field is required")})
|
||||
if self.is_array:
|
||||
if not isinstance(value, (list, tuple)):
|
||||
raise ValidationError({err_key: _("Value must be an array.")})
|
||||
if self.regex != "":
|
||||
if not all(re.fullmatch(self.regex, v) for v in value):
|
||||
raise ValidationError({err_key: _("Value does not match configured pattern.")})
|
||||
else:
|
||||
if self.is_unique:
|
||||
model: type[models.Model] = self.object_type.model_class()
|
||||
lookup_key = f"attributes__{self.key.replace(".", "__")}"
|
||||
if model.objects.filter(**{lookup_key: value}).exists():
|
||||
raise ValidationError({err_key: _("Value is not unique.")})
|
||||
if self.regex != "":
|
||||
if not re.fullmatch(self.regex, value):
|
||||
raise ValidationError({err_key: _("Value does not match configured pattern.")})
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.core.api.object_attributes import ObjectAttributeSerializer
|
||||
|
||||
return ObjectAttributeSerializer
|
||||
|
||||
def __str__(self):
|
||||
return f"Object attribute '{self.key}' for content type {self.object_type_id}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Object Attribute")
|
||||
verbose_name_plural = _("Object Attributes")
|
||||
unique_together = (("object_type", "key", "enabled"),)
|
||||
|
||||
198
authentik/core/tests/test_object_attributes_api.py
Normal file
198
authentik/core/tests/test_object_attributes_api.py
Normal file
@@ -0,0 +1,198 @@
|
||||
"""Test object attributes API"""
|
||||
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.core.api.object_attributes import ContentType
|
||||
from authentik.core.models import ObjectAttribute, User
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_user
|
||||
from authentik.lib.generators import generate_id
|
||||
|
||||
|
||||
class TestObjectAttributesAPI(APITestCase):
|
||||
"""Test object attributes API"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
self.user = create_test_admin_user()
|
||||
self.client.force_login(self.user)
|
||||
|
||||
def test_create(self):
|
||||
res = self.client.post(
|
||||
reverse("authentik_api:objectattribute-list"),
|
||||
data={
|
||||
"object_type": "authentik_core.user",
|
||||
"enabled": False,
|
||||
"key": "employeeNumber",
|
||||
"label": "Employee Number",
|
||||
"type": "text",
|
||||
"group": "Employee",
|
||||
"is_unique": False,
|
||||
"is_required": False,
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
attr = ObjectAttribute.objects.filter(key="employeeNumber").first()
|
||||
self.assertIsNotNone(attr)
|
||||
|
||||
def test_create_invalid(self):
|
||||
res = self.client.post(
|
||||
reverse("authentik_api:objectattribute-list"),
|
||||
data={
|
||||
"object_type": "authentik_core.objectattribute",
|
||||
"enabled": False,
|
||||
"key": "employeeNumber",
|
||||
"label": "Employee Number",
|
||||
"type": "text",
|
||||
"group": "Employee",
|
||||
"is_unique": False,
|
||||
"is_required": False,
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertJSONEqual(res.content, {"object_type": ["Invalid object type"]})
|
||||
|
||||
def test_create_invalid_array_unique(self):
|
||||
res = self.client.post(
|
||||
reverse("authentik_api:objectattribute-list"),
|
||||
data={
|
||||
"object_type": "authentik_core.user",
|
||||
"enabled": False,
|
||||
"key": "employeeNumber",
|
||||
"label": "Employee Number",
|
||||
"type": "text",
|
||||
"group": "Employee",
|
||||
"is_unique": True,
|
||||
"is_required": False,
|
||||
"is_array": True,
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertJSONEqual(
|
||||
res.content, {"non_field_errors": ["Unique cannot be enabled for arrays."]}
|
||||
)
|
||||
|
||||
def test_update(self):
|
||||
attr = ObjectAttribute.objects.create(
|
||||
object_type=ContentType.objects.get_for_model(User),
|
||||
label="foo",
|
||||
key=generate_id(),
|
||||
type=ObjectAttribute.AttributeType.TEXT,
|
||||
)
|
||||
res = self.client.put(
|
||||
reverse("authentik_api:objectattribute-detail", kwargs={"pk": attr.pk}),
|
||||
data={
|
||||
"object_type": "authentik_core.user",
|
||||
"enabled": False,
|
||||
"key": attr.key,
|
||||
"label": "Employee Number",
|
||||
"type": "text",
|
||||
"group": "Employee",
|
||||
"is_unique": False,
|
||||
"is_required": False,
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
attr.refresh_from_db()
|
||||
self.assertEqual(attr.label, "Employee Number")
|
||||
|
||||
def test_user_attrib_validation_required(self):
|
||||
attr = ObjectAttribute.objects.create(
|
||||
object_type=ContentType.objects.get_for_model(User),
|
||||
label="foo",
|
||||
key=generate_id(),
|
||||
type=ObjectAttribute.AttributeType.TEXT,
|
||||
is_required=True,
|
||||
)
|
||||
res = self.client.patch(
|
||||
reverse("authentik_api:user-detail", kwargs={"pk": self.user.pk}),
|
||||
data={
|
||||
"attributes": {},
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertJSONEqual(res.content, {f"attributes_{attr.key}": ["This field is required"]})
|
||||
|
||||
def test_user_attrib_validation_unique(self):
|
||||
attr = ObjectAttribute.objects.create(
|
||||
object_type=ContentType.objects.get_for_model(User),
|
||||
label="foo",
|
||||
key=generate_id(),
|
||||
type=ObjectAttribute.AttributeType.TEXT,
|
||||
is_unique=True,
|
||||
)
|
||||
other_user = create_test_user()
|
||||
other_user.attributes[attr.key] = "foo"
|
||||
other_user.save()
|
||||
res = self.client.patch(
|
||||
reverse("authentik_api:user-detail", kwargs={"pk": self.user.pk}),
|
||||
data={
|
||||
"attributes": {attr.key: "foo"},
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertJSONEqual(res.content, {f"attributes_{attr.key}": ["Value is not unique."]})
|
||||
|
||||
def test_user_attrib_validation_regex(self):
|
||||
attr = ObjectAttribute.objects.create(
|
||||
object_type=ContentType.objects.get_for_model(User),
|
||||
label="foo",
|
||||
key=generate_id(),
|
||||
type=ObjectAttribute.AttributeType.TEXT,
|
||||
regex="bar",
|
||||
)
|
||||
res = self.client.patch(
|
||||
reverse("authentik_api:user-detail", kwargs={"pk": self.user.pk}),
|
||||
data={
|
||||
"attributes": {attr.key: "foo"},
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertJSONEqual(
|
||||
res.content, {f"attributes_{attr.key}": ["Value does not match configured pattern."]}
|
||||
)
|
||||
|
||||
def test_user_attrib_validation_array(self):
|
||||
attr = ObjectAttribute.objects.create(
|
||||
object_type=ContentType.objects.get_for_model(User),
|
||||
label="foo",
|
||||
key=generate_id(),
|
||||
type=ObjectAttribute.AttributeType.TEXT,
|
||||
is_array=True,
|
||||
)
|
||||
res = self.client.patch(
|
||||
reverse("authentik_api:user-detail", kwargs={"pk": self.user.pk}),
|
||||
data={
|
||||
"attributes": {attr.key: "foo"},
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertJSONEqual(res.content, {f"attributes_{attr.key}": ["Value must be an array."]})
|
||||
|
||||
res = self.client.patch(
|
||||
reverse("authentik_api:user-detail", kwargs={"pk": self.user.pk}),
|
||||
data={
|
||||
"attributes": {attr.key: ["foo"]},
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_user_attrib_validation_array_regex(self):
|
||||
attr = ObjectAttribute.objects.create(
|
||||
object_type=ContentType.objects.get_for_model(User),
|
||||
label="foo",
|
||||
key=generate_id(),
|
||||
type=ObjectAttribute.AttributeType.TEXT,
|
||||
is_array=True,
|
||||
regex="bar",
|
||||
)
|
||||
res = self.client.patch(
|
||||
reverse("authentik_api:user-detail", kwargs={"pk": self.user.pk}),
|
||||
data={
|
||||
"attributes": {attr.key: ["foo"]},
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 400)
|
||||
self.assertJSONEqual(
|
||||
res.content, {f"attributes_{attr.key}": ["Value does not match configured pattern."]}
|
||||
)
|
||||
@@ -8,6 +8,7 @@ from authentik.core.api.applications import ApplicationViewSet
|
||||
from authentik.core.api.authenticated_sessions import AuthenticatedSessionViewSet
|
||||
from authentik.core.api.devices import AdminDeviceViewSet, DeviceViewSet
|
||||
from authentik.core.api.groups import GroupViewSet
|
||||
from authentik.core.api.object_attributes import ObjectAttributeViewSet
|
||||
from authentik.core.api.property_mappings import PropertyMappingViewSet
|
||||
from authentik.core.api.providers import ProviderViewSet
|
||||
from authentik.core.api.sources import (
|
||||
@@ -87,6 +88,7 @@ api_urlpatterns = [
|
||||
("core/groups", GroupViewSet),
|
||||
("core/users", UserViewSet),
|
||||
("core/tokens", TokenViewSet),
|
||||
("core/object_attributes", ObjectAttributeViewSet),
|
||||
("sources/all", SourceViewSet),
|
||||
("sources/user_connections/all", UserSourceConnectionViewSet),
|
||||
("sources/group_connections/all", GroupSourceConnectionViewSet),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.object_attributes import AttributesMixinSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
from authentik.endpoints.models import DeviceAccessGroup
|
||||
|
||||
|
||||
class DeviceAccessGroupSerializer(ModelSerializer):
|
||||
class DeviceAccessGroupSerializer(AttributesMixinSerializer, ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = DeviceAccessGroup
|
||||
|
||||
@@ -31,7 +31,6 @@ class DeviceUser(VirtualUser):
|
||||
username = "authentik:endpoints:device"
|
||||
|
||||
def has_perm(self, perm: str, obj: Model | None = None) -> bool:
|
||||
print(perm)
|
||||
if perm in [
|
||||
"authentik_core.view_user",
|
||||
"authentik_core.view_group",
|
||||
|
||||
@@ -4,13 +4,13 @@ from django.urls import reverse
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework import mixins
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField, SerializerMethodField
|
||||
from rest_framework.permissions import BasePermission
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from authentik.core.api.groups import PartialUserSerializer
|
||||
from authentik.core.api.object_attributes import ContentTypeSerializer
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
from authentik.enterprise.api import EnterpriseRequiredMixin
|
||||
from authentik.enterprise.reports.models import DataExport
|
||||
@@ -18,19 +18,6 @@ from authentik.enterprise.reports.tasks import generate_export
|
||||
from authentik.rbac.permissions import HasPermission
|
||||
|
||||
|
||||
class ContentTypeSerializer(ModelSerializer):
|
||||
app_label = CharField(read_only=True)
|
||||
model = CharField(read_only=True)
|
||||
verbose_name_plural = SerializerMethodField()
|
||||
|
||||
def get_verbose_name_plural(self, ct: ContentType) -> str:
|
||||
return ct.model_class()._meta.verbose_name_plural
|
||||
|
||||
class Meta:
|
||||
model = ContentType
|
||||
fields = ("id", "app_label", "model", "verbose_name_plural")
|
||||
|
||||
|
||||
class DataExportSerializer(EnterpriseRequiredMixin, ModelSerializer):
|
||||
requested_by = PartialUserSerializer(read_only=True)
|
||||
content_type = ContentTypeSerializer(read_only=True)
|
||||
|
||||
@@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, Any
|
||||
|
||||
from cachetools import TLRUCache, cached
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db.models import Model
|
||||
from django.http import HttpRequest
|
||||
from django.utils.text import slugify
|
||||
from django.utils.timezone import now
|
||||
@@ -22,6 +23,7 @@ from structlog.stdlib import get_logger
|
||||
from authentik.core.models import User
|
||||
from authentik.events.models import Event
|
||||
from authentik.lib.expression.exceptions import ControlFlowException
|
||||
from authentik.lib.utils.dict import get_path_from_dict
|
||||
from authentik.lib.utils.email import normalize_addresses
|
||||
from authentik.lib.utils.http import get_http_session
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
@@ -70,6 +72,7 @@ class BaseEvaluator:
|
||||
"ak_send_email": self.expr_send_email,
|
||||
"ak_user_by": BaseEvaluator.expr_user_by,
|
||||
"ak_user_has_authenticator": BaseEvaluator.expr_func_user_has_authenticator,
|
||||
"ak_obj_attr": BaseEvaluator.expr_obj_attr,
|
||||
"ip_address": ip_address,
|
||||
"ip_network": ip_network,
|
||||
"list_flatten": BaseEvaluator.expr_flatten,
|
||||
@@ -162,6 +165,16 @@ class BaseEvaluator:
|
||||
return False
|
||||
return len(list(user_devices)) > 0
|
||||
|
||||
@staticmethod
|
||||
def expr_obj_attr(obj: Model, attr_key: str, fallback: str) -> Any:
|
||||
"""Get an attribute of the given object if set by its dotted path, otherwise
|
||||
return fallback value."""
|
||||
attrs = getattr(obj, "attributes", {})
|
||||
value = get_path_from_dict(attrs, attr_key)
|
||||
if value is None and fallback:
|
||||
return getattr(obj, fallback)
|
||||
return value
|
||||
|
||||
def expr_event_create(self, action: str, **kwargs):
|
||||
"""Create event with supplied data and try to extract as much relevant data
|
||||
from the context"""
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -296,6 +296,46 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"model",
|
||||
"identifiers"
|
||||
],
|
||||
"properties": {
|
||||
"model": {
|
||||
"const": "authentik_core.objectattribute"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"absent",
|
||||
"created",
|
||||
"must_created",
|
||||
"present"
|
||||
],
|
||||
"default": "present"
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"$ref": "#/$defs/model_authentik_core.objectattribute_permissions"
|
||||
},
|
||||
"attrs": {
|
||||
"$ref": "#/$defs/model_authentik_core.objectattribute"
|
||||
},
|
||||
"identifiers": {
|
||||
"$ref": "#/$defs/model_authentik_core.objectattribute"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -5420,6 +5460,95 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"model_authentik_core.objectattribute": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"object_type": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Object type"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"title": "Enabled"
|
||||
},
|
||||
"key": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Key"
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Label"
|
||||
},
|
||||
"regex": {
|
||||
"type": "string",
|
||||
"title": "Regex"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"text",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"title": "Type"
|
||||
},
|
||||
"group": {
|
||||
"type": "string",
|
||||
"title": "Group"
|
||||
},
|
||||
"managed": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"minLength": 1,
|
||||
"title": "Managed by authentik",
|
||||
"description": "Objects that are managed by authentik. These objects are created and updated automatically. This flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update."
|
||||
},
|
||||
"is_unique": {
|
||||
"type": "boolean",
|
||||
"title": "Is unique"
|
||||
},
|
||||
"is_required": {
|
||||
"type": "boolean",
|
||||
"title": "Is required"
|
||||
},
|
||||
"is_array": {
|
||||
"type": "boolean",
|
||||
"title": "Is array"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
},
|
||||
"model_authentik_core.objectattribute_permissions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"permission"
|
||||
],
|
||||
"properties": {
|
||||
"permission": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"add_objectattribute",
|
||||
"change_objectattribute",
|
||||
"delete_objectattribute",
|
||||
"view_objectattribute"
|
||||
]
|
||||
},
|
||||
"user": {
|
||||
"type": "integer"
|
||||
},
|
||||
"role": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"model_authentik_core.token": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -5610,6 +5739,7 @@
|
||||
"authentik_core.add_groupancestrynode",
|
||||
"authentik_core.add_groupparentagenode",
|
||||
"authentik_core.add_groupsourceconnection",
|
||||
"authentik_core.add_objectattribute",
|
||||
"authentik_core.add_propertymapping",
|
||||
"authentik_core.add_provider",
|
||||
"authentik_core.add_source",
|
||||
@@ -5624,6 +5754,7 @@
|
||||
"authentik_core.change_groupancestrynode",
|
||||
"authentik_core.change_groupparentagenode",
|
||||
"authentik_core.change_groupsourceconnection",
|
||||
"authentik_core.change_objectattribute",
|
||||
"authentik_core.change_propertymapping",
|
||||
"authentik_core.change_provider",
|
||||
"authentik_core.change_source",
|
||||
@@ -5637,6 +5768,7 @@
|
||||
"authentik_core.delete_groupancestrynode",
|
||||
"authentik_core.delete_groupparentagenode",
|
||||
"authentik_core.delete_groupsourceconnection",
|
||||
"authentik_core.delete_objectattribute",
|
||||
"authentik_core.delete_propertymapping",
|
||||
"authentik_core.delete_provider",
|
||||
"authentik_core.delete_source",
|
||||
@@ -5657,6 +5789,7 @@
|
||||
"authentik_core.view_groupancestrynode",
|
||||
"authentik_core.view_groupparentagenode",
|
||||
"authentik_core.view_groupsourceconnection",
|
||||
"authentik_core.view_objectattribute",
|
||||
"authentik_core.view_propertymapping",
|
||||
"authentik_core.view_provider",
|
||||
"authentik_core.view_source",
|
||||
@@ -9085,6 +9218,7 @@
|
||||
"authentik_core.application",
|
||||
"authentik_core.applicationentitlement",
|
||||
"authentik_core.token",
|
||||
"authentik_core.objectattribute",
|
||||
"authentik_crypto.certificatekeypair",
|
||||
"authentik_endpoints.deviceuserbinding",
|
||||
"authentik_endpoints.deviceaccessgroup",
|
||||
@@ -11376,6 +11510,7 @@
|
||||
"authentik_core.add_groupancestrynode",
|
||||
"authentik_core.add_groupparentagenode",
|
||||
"authentik_core.add_groupsourceconnection",
|
||||
"authentik_core.add_objectattribute",
|
||||
"authentik_core.add_propertymapping",
|
||||
"authentik_core.add_provider",
|
||||
"authentik_core.add_source",
|
||||
@@ -11390,6 +11525,7 @@
|
||||
"authentik_core.change_groupancestrynode",
|
||||
"authentik_core.change_groupparentagenode",
|
||||
"authentik_core.change_groupsourceconnection",
|
||||
"authentik_core.change_objectattribute",
|
||||
"authentik_core.change_propertymapping",
|
||||
"authentik_core.change_provider",
|
||||
"authentik_core.change_source",
|
||||
@@ -11403,6 +11539,7 @@
|
||||
"authentik_core.delete_groupancestrynode",
|
||||
"authentik_core.delete_groupparentagenode",
|
||||
"authentik_core.delete_groupsourceconnection",
|
||||
"authentik_core.delete_objectattribute",
|
||||
"authentik_core.delete_propertymapping",
|
||||
"authentik_core.delete_provider",
|
||||
"authentik_core.delete_source",
|
||||
@@ -11423,6 +11560,7 @@
|
||||
"authentik_core.view_groupancestrynode",
|
||||
"authentik_core.view_groupparentagenode",
|
||||
"authentik_core.view_groupsourceconnection",
|
||||
"authentik_core.view_objectattribute",
|
||||
"authentik_core.view_propertymapping",
|
||||
"authentik_core.view_provider",
|
||||
"authentik_core.view_source",
|
||||
|
||||
145
blueprints/system/object-attributes-user.yaml
Normal file
145
blueprints/system/object-attributes-user.yaml
Normal file
@@ -0,0 +1,145 @@
|
||||
version: 1
|
||||
metadata:
|
||||
labels:
|
||||
blueprints.goauthentik.io/system: "true"
|
||||
name: System - Object Attributes - User
|
||||
entries:
|
||||
identity:
|
||||
- model: authentik_core.objectattribute
|
||||
identifiers:
|
||||
key: givenName
|
||||
managed: goauthentik.io/object-attrs/user/identity/givenName
|
||||
attrs:
|
||||
group: Identity
|
||||
object_type: authentik_core.user
|
||||
label: Given Name
|
||||
enabled: false
|
||||
is_required: false
|
||||
is_unique: false
|
||||
type: text
|
||||
- model: authentik_core.objectattribute
|
||||
identifiers:
|
||||
key: familyName
|
||||
managed: goauthentik.io/object-attrs/user/identity/familyName
|
||||
attrs:
|
||||
group: Identity
|
||||
object_type: authentik_core.user
|
||||
label: Family Name
|
||||
enabled: false
|
||||
is_required: false
|
||||
is_unique: false
|
||||
type: text
|
||||
ak:
|
||||
- model: authentik_core.objectattribute
|
||||
identifiers:
|
||||
key: settings.locale
|
||||
managed: goauthentik.io/object-attrs/user/settings/locale
|
||||
attrs:
|
||||
group: Settings
|
||||
object_type: authentik_core.user
|
||||
label: Locale
|
||||
enabled: true
|
||||
is_required: false
|
||||
is_unique: false
|
||||
type: text
|
||||
address:
|
||||
- model: authentik_core.objectattribute
|
||||
identifiers:
|
||||
key: street
|
||||
managed: goauthentik.io/object-attrs/user/address/street
|
||||
attrs:
|
||||
group: Address
|
||||
object_type: authentik_core.user
|
||||
label: Street
|
||||
enabled: false
|
||||
is_required: false
|
||||
is_unique: false
|
||||
type: text
|
||||
- model: authentik_core.objectattribute
|
||||
identifiers:
|
||||
key: state
|
||||
managed: goauthentik.io/object-attrs/user/address/state
|
||||
attrs:
|
||||
group: Address
|
||||
object_type: authentik_core.user
|
||||
label: State
|
||||
enabled: false
|
||||
is_required: false
|
||||
is_unique: false
|
||||
type: text
|
||||
- model: authentik_core.objectattribute
|
||||
identifiers:
|
||||
key: location
|
||||
managed: goauthentik.io/object-attrs/user/address/location
|
||||
attrs:
|
||||
group: Address
|
||||
object_type: authentik_core.user
|
||||
label: Location
|
||||
enabled: false
|
||||
is_required: false
|
||||
is_unique: false
|
||||
type: text
|
||||
- model: authentik_core.objectattribute
|
||||
identifiers:
|
||||
key: postalCode
|
||||
managed: goauthentik.io/object-attrs/user/address/postal-code
|
||||
attrs:
|
||||
group: Address
|
||||
object_type: authentik_core.user
|
||||
label: Postal code
|
||||
enabled: false
|
||||
is_required: false
|
||||
is_unique: false
|
||||
type: text
|
||||
contact:
|
||||
- model: authentik_core.objectattribute
|
||||
identifiers:
|
||||
key: phoneNumber
|
||||
managed: goauthentik.io/object-attrs/user/contact/phone-number
|
||||
attrs:
|
||||
group: Contact
|
||||
object_type: authentik_core.user
|
||||
label: Phone number(s)
|
||||
enabled: false
|
||||
is_required: false
|
||||
is_unique: false
|
||||
type: text
|
||||
is_array: true
|
||||
unix:
|
||||
- model: authentik_core.objectattribute
|
||||
identifiers:
|
||||
key: shell
|
||||
managed: goauthentik.io/object-attrs/user/unix/shell
|
||||
attrs:
|
||||
group: Unix
|
||||
object_type: authentik_core.user
|
||||
label: Shell
|
||||
enabled: false
|
||||
is_required: false
|
||||
is_unique: false
|
||||
type: text
|
||||
employee:
|
||||
- model: authentik_core.objectattribute
|
||||
identifiers:
|
||||
key: employeeNumber
|
||||
managed: goauthentik.io/object-attrs/user/employee/number
|
||||
attrs:
|
||||
group: Employee
|
||||
object_type: authentik_core.user
|
||||
label: Employee Number
|
||||
enabled: false
|
||||
is_required: false
|
||||
is_unique: false
|
||||
type: text
|
||||
- model: authentik_core.objectattribute
|
||||
identifiers:
|
||||
key: jobTitle
|
||||
managed: goauthentik.io/object-attrs/user/employee/title
|
||||
attrs:
|
||||
group: Employee
|
||||
object_type: authentik_core.user
|
||||
label: Title
|
||||
enabled: false
|
||||
is_required: false
|
||||
is_unique: false
|
||||
type: text
|
||||
@@ -35,14 +35,13 @@ entries:
|
||||
description: "General Profile Information"
|
||||
expression: |
|
||||
return {
|
||||
# Because authentik only saves the user's full name, and has no concept of first and last names,
|
||||
# the full name is used as given name.
|
||||
# You can override this behaviour in custom mappings, i.e. `request.user.name.split(" ")`
|
||||
"name": request.user.name,
|
||||
"given_name": request.user.name,
|
||||
"given_name": ak_obj_attr(request.user, "givenName", "name"),
|
||||
"family_name": ak_obj_attr(request.user, "familyName"),
|
||||
"preferred_username": request.user.username,
|
||||
"nickname": request.user.username,
|
||||
"groups": [group.name for group in request.user.groups.all()],
|
||||
"picture": request.user.avatar,
|
||||
}
|
||||
- identifiers:
|
||||
managed: goauthentik.io/providers/oauth2/scope-entitlements
|
||||
|
||||
@@ -42,8 +42,8 @@ entries:
|
||||
"userName": request.user.username,
|
||||
"name": {
|
||||
"formatted": formatted,
|
||||
"givenName": givenName,
|
||||
"familyName": familyName,
|
||||
"givenName": ak_obj_attr(request.user, "givenName") or givenName,
|
||||
"familyName": ak_obj_attr(request.user, "familyName") or familyName,
|
||||
},
|
||||
"displayName": request.user.name,
|
||||
"photos": photos,
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-05-06 00:27+0000\n"
|
||||
"POT-Creation-Date: 2026-05-13 05:39+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -226,6 +226,10 @@ msgstr ""
|
||||
msgid "The slug '{slug}' is reserved and cannot be used for applications."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/api/groups.py
|
||||
msgid "User does not have permission to add members to this group."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/api/providers.py
|
||||
msgid ""
|
||||
"When not set all providers are returned. When set to true, only backchannel "
|
||||
@@ -256,6 +260,14 @@ msgstr ""
|
||||
msgid "Setting a user to internal service account is not allowed."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/api/users.py
|
||||
msgid "User does not have permission to add members to a superuser group."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/api/users.py
|
||||
msgid "User does not have permission to assign roles."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/api/users.py
|
||||
msgid "Can't modify internal service account users"
|
||||
msgstr ""
|
||||
|
||||
Binary file not shown.
18
packages/client-ts/src/apis/AdminApi.ts
generated
18
packages/client-ts/src/apis/AdminApi.ts
generated
@@ -58,6 +58,10 @@ export interface AdminFileUsedByListRequest {
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface AdminModelsListRequest {
|
||||
filterHasAttributes?: boolean | null;
|
||||
}
|
||||
|
||||
export interface AdminSettingsPartialUpdateRequest {
|
||||
patchedSettingsRequest?: PatchedSettingsRequest;
|
||||
}
|
||||
@@ -400,9 +404,15 @@ export class AdminApi extends runtime.BaseAPI {
|
||||
/**
|
||||
* Creates request options for adminModelsList without sending the request
|
||||
*/
|
||||
async adminModelsListRequestOpts(): Promise<runtime.RequestOpts> {
|
||||
async adminModelsListRequestOpts(
|
||||
requestParameters: AdminModelsListRequest,
|
||||
): Promise<runtime.RequestOpts> {
|
||||
const queryParameters: any = {};
|
||||
|
||||
if (requestParameters["filterHasAttributes"] != null) {
|
||||
queryParameters["filter_has_attributes"] = requestParameters["filterHasAttributes"];
|
||||
}
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
@@ -428,9 +438,10 @@ export class AdminApi extends runtime.BaseAPI {
|
||||
* Read-only view list all installed models
|
||||
*/
|
||||
async adminModelsListRaw(
|
||||
requestParameters: AdminModelsListRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<runtime.ApiResponse<Array<App>>> {
|
||||
const requestOptions = await this.adminModelsListRequestOpts();
|
||||
const requestOptions = await this.adminModelsListRequestOpts(requestParameters);
|
||||
const response = await this.request(requestOptions, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(AppFromJSON));
|
||||
@@ -440,9 +451,10 @@ export class AdminApi extends runtime.BaseAPI {
|
||||
* Read-only view list all installed models
|
||||
*/
|
||||
async adminModelsList(
|
||||
requestParameters: AdminModelsListRequest = {},
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<Array<App>> {
|
||||
const response = await this.adminModelsListRaw(initOverrides);
|
||||
const response = await this.adminModelsListRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
|
||||
460
packages/client-ts/src/apis/CoreApi.ts
generated
460
packages/client-ts/src/apis/CoreApi.ts
generated
@@ -28,11 +28,14 @@ import type {
|
||||
ImpersonationRequest,
|
||||
IntentEnum,
|
||||
Link,
|
||||
ObjectAttribute,
|
||||
ObjectAttributeRequest,
|
||||
PaginatedApplicationEntitlementList,
|
||||
PaginatedApplicationList,
|
||||
PaginatedAuthenticatedSessionList,
|
||||
PaginatedBrandList,
|
||||
PaginatedGroupList,
|
||||
PaginatedObjectAttributeList,
|
||||
PaginatedTokenList,
|
||||
PaginatedUserConsentList,
|
||||
PaginatedUserList,
|
||||
@@ -40,6 +43,7 @@ import type {
|
||||
PatchedApplicationRequest,
|
||||
PatchedBrandRequest,
|
||||
PatchedGroupRequest,
|
||||
PatchedObjectAttributeRequest,
|
||||
PatchedTokenRequest,
|
||||
PatchedUserRequest,
|
||||
PolicyTestResult,
|
||||
@@ -80,11 +84,14 @@ import {
|
||||
GroupRequestToJSON,
|
||||
ImpersonationRequestToJSON,
|
||||
LinkFromJSON,
|
||||
ObjectAttributeFromJSON,
|
||||
ObjectAttributeRequestToJSON,
|
||||
PaginatedApplicationEntitlementListFromJSON,
|
||||
PaginatedApplicationListFromJSON,
|
||||
PaginatedAuthenticatedSessionListFromJSON,
|
||||
PaginatedBrandListFromJSON,
|
||||
PaginatedGroupListFromJSON,
|
||||
PaginatedObjectAttributeListFromJSON,
|
||||
PaginatedTokenListFromJSON,
|
||||
PaginatedUserConsentListFromJSON,
|
||||
PaginatedUserListFromJSON,
|
||||
@@ -92,6 +99,7 @@ import {
|
||||
PatchedApplicationRequestToJSON,
|
||||
PatchedBrandRequestToJSON,
|
||||
PatchedGroupRequestToJSON,
|
||||
PatchedObjectAttributeRequestToJSON,
|
||||
PatchedTokenRequestToJSON,
|
||||
PatchedUserRequestToJSON,
|
||||
PolicyTestResultFromJSON,
|
||||
@@ -332,6 +340,38 @@ export interface CoreGroupsUsedByListRequest {
|
||||
groupUuid: string;
|
||||
}
|
||||
|
||||
export interface CoreObjectAttributesCreateRequest {
|
||||
objectAttributeRequest: ObjectAttributeRequest;
|
||||
}
|
||||
|
||||
export interface CoreObjectAttributesDestroyRequest {
|
||||
attributeId: string;
|
||||
}
|
||||
|
||||
export interface CoreObjectAttributesListRequest {
|
||||
enabled?: boolean;
|
||||
objectTypeAppLabel?: string;
|
||||
objectTypeModel?: string;
|
||||
ordering?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
search?: string;
|
||||
}
|
||||
|
||||
export interface CoreObjectAttributesPartialUpdateRequest {
|
||||
attributeId: string;
|
||||
patchedObjectAttributeRequest?: PatchedObjectAttributeRequest;
|
||||
}
|
||||
|
||||
export interface CoreObjectAttributesRetrieveRequest {
|
||||
attributeId: string;
|
||||
}
|
||||
|
||||
export interface CoreObjectAttributesUpdateRequest {
|
||||
attributeId: string;
|
||||
objectAttributeRequest: ObjectAttributeRequest;
|
||||
}
|
||||
|
||||
export interface CoreTokensCreateRequest {
|
||||
tokenRequest: TokenRequest;
|
||||
}
|
||||
@@ -3237,6 +3277,426 @@ export class CoreApi extends runtime.BaseAPI {
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates request options for coreObjectAttributesCreate without sending the request
|
||||
*/
|
||||
async coreObjectAttributesCreateRequestOpts(
|
||||
requestParameters: CoreObjectAttributesCreateRequest,
|
||||
): Promise<runtime.RequestOpts> {
|
||||
if (requestParameters["objectAttributeRequest"] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
"objectAttributeRequest",
|
||||
'Required parameter "objectAttributeRequest" was null or undefined when calling coreObjectAttributesCreate().',
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
headerParameters["Content-Type"] = "application/json";
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
const token = this.configuration.accessToken;
|
||||
const tokenString = await token("authentik", []);
|
||||
|
||||
if (tokenString) {
|
||||
headerParameters["Authorization"] = `Bearer ${tokenString}`;
|
||||
}
|
||||
}
|
||||
|
||||
let urlPath = `/core/object_attributes/`;
|
||||
|
||||
return {
|
||||
path: urlPath,
|
||||
method: "POST",
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
body: ObjectAttributeRequestToJSON(requestParameters["objectAttributeRequest"]),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesCreateRaw(
|
||||
requestParameters: CoreObjectAttributesCreateRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<runtime.ApiResponse<ObjectAttribute>> {
|
||||
const requestOptions = await this.coreObjectAttributesCreateRequestOpts(requestParameters);
|
||||
const response = await this.request(requestOptions, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) =>
|
||||
ObjectAttributeFromJSON(jsonValue),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesCreate(
|
||||
requestParameters: CoreObjectAttributesCreateRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<ObjectAttribute> {
|
||||
const response = await this.coreObjectAttributesCreateRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates request options for coreObjectAttributesDestroy without sending the request
|
||||
*/
|
||||
async coreObjectAttributesDestroyRequestOpts(
|
||||
requestParameters: CoreObjectAttributesDestroyRequest,
|
||||
): Promise<runtime.RequestOpts> {
|
||||
if (requestParameters["attributeId"] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
"attributeId",
|
||||
'Required parameter "attributeId" was null or undefined when calling coreObjectAttributesDestroy().',
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
const token = this.configuration.accessToken;
|
||||
const tokenString = await token("authentik", []);
|
||||
|
||||
if (tokenString) {
|
||||
headerParameters["Authorization"] = `Bearer ${tokenString}`;
|
||||
}
|
||||
}
|
||||
|
||||
let urlPath = `/core/object_attributes/{attribute_id}/`;
|
||||
urlPath = urlPath.replace(
|
||||
`{${"attribute_id"}}`,
|
||||
encodeURIComponent(String(requestParameters["attributeId"])),
|
||||
);
|
||||
|
||||
return {
|
||||
path: urlPath,
|
||||
method: "DELETE",
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesDestroyRaw(
|
||||
requestParameters: CoreObjectAttributesDestroyRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<runtime.ApiResponse<void>> {
|
||||
const requestOptions = await this.coreObjectAttributesDestroyRequestOpts(requestParameters);
|
||||
const response = await this.request(requestOptions, initOverrides);
|
||||
|
||||
return new runtime.VoidApiResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesDestroy(
|
||||
requestParameters: CoreObjectAttributesDestroyRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<void> {
|
||||
await this.coreObjectAttributesDestroyRaw(requestParameters, initOverrides);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates request options for coreObjectAttributesList without sending the request
|
||||
*/
|
||||
async coreObjectAttributesListRequestOpts(
|
||||
requestParameters: CoreObjectAttributesListRequest,
|
||||
): Promise<runtime.RequestOpts> {
|
||||
const queryParameters: any = {};
|
||||
|
||||
if (requestParameters["enabled"] != null) {
|
||||
queryParameters["enabled"] = requestParameters["enabled"];
|
||||
}
|
||||
|
||||
if (requestParameters["objectTypeAppLabel"] != null) {
|
||||
queryParameters["object_type__app_label"] = requestParameters["objectTypeAppLabel"];
|
||||
}
|
||||
|
||||
if (requestParameters["objectTypeModel"] != null) {
|
||||
queryParameters["object_type__model"] = requestParameters["objectTypeModel"];
|
||||
}
|
||||
|
||||
if (requestParameters["ordering"] != null) {
|
||||
queryParameters["ordering"] = requestParameters["ordering"];
|
||||
}
|
||||
|
||||
if (requestParameters["page"] != null) {
|
||||
queryParameters["page"] = requestParameters["page"];
|
||||
}
|
||||
|
||||
if (requestParameters["pageSize"] != null) {
|
||||
queryParameters["page_size"] = requestParameters["pageSize"];
|
||||
}
|
||||
|
||||
if (requestParameters["search"] != null) {
|
||||
queryParameters["search"] = requestParameters["search"];
|
||||
}
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
const token = this.configuration.accessToken;
|
||||
const tokenString = await token("authentik", []);
|
||||
|
||||
if (tokenString) {
|
||||
headerParameters["Authorization"] = `Bearer ${tokenString}`;
|
||||
}
|
||||
}
|
||||
|
||||
let urlPath = `/core/object_attributes/`;
|
||||
|
||||
return {
|
||||
path: urlPath,
|
||||
method: "GET",
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesListRaw(
|
||||
requestParameters: CoreObjectAttributesListRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<runtime.ApiResponse<PaginatedObjectAttributeList>> {
|
||||
const requestOptions = await this.coreObjectAttributesListRequestOpts(requestParameters);
|
||||
const response = await this.request(requestOptions, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) =>
|
||||
PaginatedObjectAttributeListFromJSON(jsonValue),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesList(
|
||||
requestParameters: CoreObjectAttributesListRequest = {},
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<PaginatedObjectAttributeList> {
|
||||
const response = await this.coreObjectAttributesListRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates request options for coreObjectAttributesPartialUpdate without sending the request
|
||||
*/
|
||||
async coreObjectAttributesPartialUpdateRequestOpts(
|
||||
requestParameters: CoreObjectAttributesPartialUpdateRequest,
|
||||
): Promise<runtime.RequestOpts> {
|
||||
if (requestParameters["attributeId"] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
"attributeId",
|
||||
'Required parameter "attributeId" was null or undefined when calling coreObjectAttributesPartialUpdate().',
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
headerParameters["Content-Type"] = "application/json";
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
const token = this.configuration.accessToken;
|
||||
const tokenString = await token("authentik", []);
|
||||
|
||||
if (tokenString) {
|
||||
headerParameters["Authorization"] = `Bearer ${tokenString}`;
|
||||
}
|
||||
}
|
||||
|
||||
let urlPath = `/core/object_attributes/{attribute_id}/`;
|
||||
urlPath = urlPath.replace(
|
||||
`{${"attribute_id"}}`,
|
||||
encodeURIComponent(String(requestParameters["attributeId"])),
|
||||
);
|
||||
|
||||
return {
|
||||
path: urlPath,
|
||||
method: "PATCH",
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
body: PatchedObjectAttributeRequestToJSON(
|
||||
requestParameters["patchedObjectAttributeRequest"],
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesPartialUpdateRaw(
|
||||
requestParameters: CoreObjectAttributesPartialUpdateRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<runtime.ApiResponse<ObjectAttribute>> {
|
||||
const requestOptions =
|
||||
await this.coreObjectAttributesPartialUpdateRequestOpts(requestParameters);
|
||||
const response = await this.request(requestOptions, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) =>
|
||||
ObjectAttributeFromJSON(jsonValue),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesPartialUpdate(
|
||||
requestParameters: CoreObjectAttributesPartialUpdateRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<ObjectAttribute> {
|
||||
const response = await this.coreObjectAttributesPartialUpdateRaw(
|
||||
requestParameters,
|
||||
initOverrides,
|
||||
);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates request options for coreObjectAttributesRetrieve without sending the request
|
||||
*/
|
||||
async coreObjectAttributesRetrieveRequestOpts(
|
||||
requestParameters: CoreObjectAttributesRetrieveRequest,
|
||||
): Promise<runtime.RequestOpts> {
|
||||
if (requestParameters["attributeId"] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
"attributeId",
|
||||
'Required parameter "attributeId" was null or undefined when calling coreObjectAttributesRetrieve().',
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
const token = this.configuration.accessToken;
|
||||
const tokenString = await token("authentik", []);
|
||||
|
||||
if (tokenString) {
|
||||
headerParameters["Authorization"] = `Bearer ${tokenString}`;
|
||||
}
|
||||
}
|
||||
|
||||
let urlPath = `/core/object_attributes/{attribute_id}/`;
|
||||
urlPath = urlPath.replace(
|
||||
`{${"attribute_id"}}`,
|
||||
encodeURIComponent(String(requestParameters["attributeId"])),
|
||||
);
|
||||
|
||||
return {
|
||||
path: urlPath,
|
||||
method: "GET",
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesRetrieveRaw(
|
||||
requestParameters: CoreObjectAttributesRetrieveRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<runtime.ApiResponse<ObjectAttribute>> {
|
||||
const requestOptions =
|
||||
await this.coreObjectAttributesRetrieveRequestOpts(requestParameters);
|
||||
const response = await this.request(requestOptions, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) =>
|
||||
ObjectAttributeFromJSON(jsonValue),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesRetrieve(
|
||||
requestParameters: CoreObjectAttributesRetrieveRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<ObjectAttribute> {
|
||||
const response = await this.coreObjectAttributesRetrieveRaw(
|
||||
requestParameters,
|
||||
initOverrides,
|
||||
);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates request options for coreObjectAttributesUpdate without sending the request
|
||||
*/
|
||||
async coreObjectAttributesUpdateRequestOpts(
|
||||
requestParameters: CoreObjectAttributesUpdateRequest,
|
||||
): Promise<runtime.RequestOpts> {
|
||||
if (requestParameters["attributeId"] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
"attributeId",
|
||||
'Required parameter "attributeId" was null or undefined when calling coreObjectAttributesUpdate().',
|
||||
);
|
||||
}
|
||||
|
||||
if (requestParameters["objectAttributeRequest"] == null) {
|
||||
throw new runtime.RequiredError(
|
||||
"objectAttributeRequest",
|
||||
'Required parameter "objectAttributeRequest" was null or undefined when calling coreObjectAttributesUpdate().',
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
headerParameters["Content-Type"] = "application/json";
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
const token = this.configuration.accessToken;
|
||||
const tokenString = await token("authentik", []);
|
||||
|
||||
if (tokenString) {
|
||||
headerParameters["Authorization"] = `Bearer ${tokenString}`;
|
||||
}
|
||||
}
|
||||
|
||||
let urlPath = `/core/object_attributes/{attribute_id}/`;
|
||||
urlPath = urlPath.replace(
|
||||
`{${"attribute_id"}}`,
|
||||
encodeURIComponent(String(requestParameters["attributeId"])),
|
||||
);
|
||||
|
||||
return {
|
||||
path: urlPath,
|
||||
method: "PUT",
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
body: ObjectAttributeRequestToJSON(requestParameters["objectAttributeRequest"]),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesUpdateRaw(
|
||||
requestParameters: CoreObjectAttributesUpdateRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<runtime.ApiResponse<ObjectAttribute>> {
|
||||
const requestOptions = await this.coreObjectAttributesUpdateRequestOpts(requestParameters);
|
||||
const response = await this.request(requestOptions, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) =>
|
||||
ObjectAttributeFromJSON(jsonValue),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
async coreObjectAttributesUpdate(
|
||||
requestParameters: CoreObjectAttributesUpdateRequest,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<ObjectAttribute> {
|
||||
const response = await this.coreObjectAttributesUpdateRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates request options for coreTokensCreate without sending the request
|
||||
*/
|
||||
|
||||
14
packages/client-ts/src/models/ContentType.ts
generated
14
packages/client-ts/src/models/ContentType.ts
generated
@@ -42,6 +42,12 @@ export interface ContentType {
|
||||
* @memberof ContentType
|
||||
*/
|
||||
readonly verboseNamePlural: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ContentType
|
||||
*/
|
||||
readonly fullyQualifiedModel: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,6 +58,8 @@ export function instanceOfContentType(value: object): value is ContentType {
|
||||
if (!("appLabel" in value) || value["appLabel"] === undefined) return false;
|
||||
if (!("model" in value) || value["model"] === undefined) return false;
|
||||
if (!("verboseNamePlural" in value) || value["verboseNamePlural"] === undefined) return false;
|
||||
if (!("fullyQualifiedModel" in value) || value["fullyQualifiedModel"] === undefined)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -68,6 +76,7 @@ export function ContentTypeFromJSONTyped(json: any, ignoreDiscriminator: boolean
|
||||
appLabel: json["app_label"],
|
||||
model: json["model"],
|
||||
verboseNamePlural: json["verbose_name_plural"],
|
||||
fullyQualifiedModel: json["fully_qualified_model"],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -76,7 +85,10 @@ export function ContentTypeToJSON(json: any): ContentType {
|
||||
}
|
||||
|
||||
export function ContentTypeToJSONTyped(
|
||||
value?: Omit<ContentType, "id" | "app_label" | "model" | "verbose_name_plural"> | null,
|
||||
value?: Omit<
|
||||
ContentType,
|
||||
"id" | "app_label" | "model" | "verbose_name_plural" | "fully_qualified_model"
|
||||
> | null,
|
||||
ignoreDiscriminator: boolean = false,
|
||||
): any {
|
||||
if (value == null) {
|
||||
|
||||
1
packages/client-ts/src/models/ModelEnum.ts
generated
1
packages/client-ts/src/models/ModelEnum.ts
generated
@@ -23,6 +23,7 @@ export const ModelEnum = {
|
||||
AuthentikCoreApplication: "authentik_core.application",
|
||||
AuthentikCoreApplicationentitlement: "authentik_core.applicationentitlement",
|
||||
AuthentikCoreToken: "authentik_core.token",
|
||||
AuthentikCoreObjectattribute: "authentik_core.objectattribute",
|
||||
AuthentikCryptoCertificatekeypair: "authentik_crypto.certificatekeypair",
|
||||
AuthentikEndpointsDeviceuserbinding: "authentik_endpoints.deviceuserbinding",
|
||||
AuthentikEndpointsDeviceaccessgroup: "authentik_endpoints.deviceaccessgroup",
|
||||
|
||||
191
packages/client-ts/src/models/ObjectAttribute.ts
generated
Normal file
191
packages/client-ts/src/models/ObjectAttribute.ts
generated
Normal file
@@ -0,0 +1,191 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* authentik
|
||||
* Making authentication simple.
|
||||
*
|
||||
* The version of the OpenAPI document: 2026.5.0-rc1
|
||||
* Contact: hello@goauthentik.io
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import type { ContentType } from "./ContentType";
|
||||
import { ContentTypeFromJSON } from "./ContentType";
|
||||
import type { ObjectAttributeTypeEnum } from "./ObjectAttributeTypeEnum";
|
||||
import {
|
||||
ObjectAttributeTypeEnumFromJSON,
|
||||
ObjectAttributeTypeEnumToJSON,
|
||||
} from "./ObjectAttributeTypeEnum";
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface ObjectAttribute
|
||||
*/
|
||||
export interface ObjectAttribute {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
readonly pk: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
objectType: string;
|
||||
/**
|
||||
*
|
||||
* @type {ContentType}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
readonly objectTypeObj: ContentType;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
enabled?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {Date}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
readonly created: Date;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
*
|
||||
* @type {Date}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
readonly lastUpdated: Date;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
regex?: string;
|
||||
/**
|
||||
*
|
||||
* @type {ObjectAttributeTypeEnum}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
type: ObjectAttributeTypeEnum;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
group?: string;
|
||||
/**
|
||||
* Objects that are managed by authentik. These objects are created and updated automatically. This flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update.
|
||||
* @type {string}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
managed?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
isUnique?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
isRequired?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ObjectAttribute
|
||||
*/
|
||||
isArray?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the ObjectAttribute interface.
|
||||
*/
|
||||
export function instanceOfObjectAttribute(value: object): value is ObjectAttribute {
|
||||
if (!("pk" in value) || value["pk"] === undefined) return false;
|
||||
if (!("objectType" in value) || value["objectType"] === undefined) return false;
|
||||
if (!("objectTypeObj" in value) || value["objectTypeObj"] === undefined) return false;
|
||||
if (!("created" in value) || value["created"] === undefined) return false;
|
||||
if (!("key" in value) || value["key"] === undefined) return false;
|
||||
if (!("label" in value) || value["label"] === undefined) return false;
|
||||
if (!("lastUpdated" in value) || value["lastUpdated"] === undefined) return false;
|
||||
if (!("type" in value) || value["type"] === undefined) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function ObjectAttributeFromJSON(json: any): ObjectAttribute {
|
||||
return ObjectAttributeFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function ObjectAttributeFromJSONTyped(
|
||||
json: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): ObjectAttribute {
|
||||
if (json == null) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
pk: json["pk"],
|
||||
objectType: json["object_type"],
|
||||
objectTypeObj: ContentTypeFromJSON(json["object_type_obj"]),
|
||||
enabled: json["enabled"] == null ? undefined : json["enabled"],
|
||||
created: new Date(json["created"]),
|
||||
key: json["key"],
|
||||
label: json["label"],
|
||||
lastUpdated: new Date(json["last_updated"]),
|
||||
regex: json["regex"] == null ? undefined : json["regex"],
|
||||
type: ObjectAttributeTypeEnumFromJSON(json["type"]),
|
||||
group: json["group"] == null ? undefined : json["group"],
|
||||
managed: json["managed"] == null ? undefined : json["managed"],
|
||||
isUnique: json["is_unique"] == null ? undefined : json["is_unique"],
|
||||
isRequired: json["is_required"] == null ? undefined : json["is_required"],
|
||||
isArray: json["is_array"] == null ? undefined : json["is_array"],
|
||||
};
|
||||
}
|
||||
|
||||
export function ObjectAttributeToJSON(json: any): ObjectAttribute {
|
||||
return ObjectAttributeToJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function ObjectAttributeToJSONTyped(
|
||||
value?: Omit<ObjectAttribute, "pk" | "object_type_obj" | "created" | "last_updated"> | null,
|
||||
ignoreDiscriminator: boolean = false,
|
||||
): any {
|
||||
if (value == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return {
|
||||
object_type: value["objectType"],
|
||||
enabled: value["enabled"],
|
||||
key: value["key"],
|
||||
label: value["label"],
|
||||
regex: value["regex"],
|
||||
type: ObjectAttributeTypeEnumToJSON(value["type"]),
|
||||
group: value["group"],
|
||||
managed: value["managed"],
|
||||
is_unique: value["isUnique"],
|
||||
is_required: value["isRequired"],
|
||||
is_array: value["isArray"],
|
||||
};
|
||||
}
|
||||
157
packages/client-ts/src/models/ObjectAttributeRequest.ts
generated
Normal file
157
packages/client-ts/src/models/ObjectAttributeRequest.ts
generated
Normal file
@@ -0,0 +1,157 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* authentik
|
||||
* Making authentication simple.
|
||||
*
|
||||
* The version of the OpenAPI document: 2026.5.0-rc1
|
||||
* Contact: hello@goauthentik.io
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import type { ObjectAttributeTypeEnum } from "./ObjectAttributeTypeEnum";
|
||||
import {
|
||||
ObjectAttributeTypeEnumFromJSON,
|
||||
ObjectAttributeTypeEnumToJSON,
|
||||
} from "./ObjectAttributeTypeEnum";
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface ObjectAttributeRequest
|
||||
*/
|
||||
export interface ObjectAttributeRequest {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ObjectAttributeRequest
|
||||
*/
|
||||
objectType: string;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ObjectAttributeRequest
|
||||
*/
|
||||
enabled?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ObjectAttributeRequest
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ObjectAttributeRequest
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ObjectAttributeRequest
|
||||
*/
|
||||
regex?: string;
|
||||
/**
|
||||
*
|
||||
* @type {ObjectAttributeTypeEnum}
|
||||
* @memberof ObjectAttributeRequest
|
||||
*/
|
||||
type: ObjectAttributeTypeEnum;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ObjectAttributeRequest
|
||||
*/
|
||||
group?: string;
|
||||
/**
|
||||
* Objects that are managed by authentik. These objects are created and updated automatically. This flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update.
|
||||
* @type {string}
|
||||
* @memberof ObjectAttributeRequest
|
||||
*/
|
||||
managed?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ObjectAttributeRequest
|
||||
*/
|
||||
isUnique?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ObjectAttributeRequest
|
||||
*/
|
||||
isRequired?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ObjectAttributeRequest
|
||||
*/
|
||||
isArray?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the ObjectAttributeRequest interface.
|
||||
*/
|
||||
export function instanceOfObjectAttributeRequest(value: object): value is ObjectAttributeRequest {
|
||||
if (!("objectType" in value) || value["objectType"] === undefined) return false;
|
||||
if (!("key" in value) || value["key"] === undefined) return false;
|
||||
if (!("label" in value) || value["label"] === undefined) return false;
|
||||
if (!("type" in value) || value["type"] === undefined) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function ObjectAttributeRequestFromJSON(json: any): ObjectAttributeRequest {
|
||||
return ObjectAttributeRequestFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function ObjectAttributeRequestFromJSONTyped(
|
||||
json: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): ObjectAttributeRequest {
|
||||
if (json == null) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
objectType: json["object_type"],
|
||||
enabled: json["enabled"] == null ? undefined : json["enabled"],
|
||||
key: json["key"],
|
||||
label: json["label"],
|
||||
regex: json["regex"] == null ? undefined : json["regex"],
|
||||
type: ObjectAttributeTypeEnumFromJSON(json["type"]),
|
||||
group: json["group"] == null ? undefined : json["group"],
|
||||
managed: json["managed"] == null ? undefined : json["managed"],
|
||||
isUnique: json["is_unique"] == null ? undefined : json["is_unique"],
|
||||
isRequired: json["is_required"] == null ? undefined : json["is_required"],
|
||||
isArray: json["is_array"] == null ? undefined : json["is_array"],
|
||||
};
|
||||
}
|
||||
|
||||
export function ObjectAttributeRequestToJSON(json: any): ObjectAttributeRequest {
|
||||
return ObjectAttributeRequestToJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function ObjectAttributeRequestToJSONTyped(
|
||||
value?: ObjectAttributeRequest | null,
|
||||
ignoreDiscriminator: boolean = false,
|
||||
): any {
|
||||
if (value == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return {
|
||||
object_type: value["objectType"],
|
||||
enabled: value["enabled"],
|
||||
key: value["key"],
|
||||
label: value["label"],
|
||||
regex: value["regex"],
|
||||
type: ObjectAttributeTypeEnumToJSON(value["type"]),
|
||||
group: value["group"],
|
||||
managed: value["managed"],
|
||||
is_unique: value["isUnique"],
|
||||
is_required: value["isRequired"],
|
||||
is_array: value["isArray"],
|
||||
};
|
||||
}
|
||||
59
packages/client-ts/src/models/ObjectAttributeTypeEnum.ts
generated
Normal file
59
packages/client-ts/src/models/ObjectAttributeTypeEnum.ts
generated
Normal file
@@ -0,0 +1,59 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* authentik
|
||||
* Making authentication simple.
|
||||
*
|
||||
* The version of the OpenAPI document: 2026.5.0-rc1
|
||||
* Contact: hello@goauthentik.io
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const ObjectAttributeTypeEnum = {
|
||||
Text: "text",
|
||||
Number: "number",
|
||||
Boolean: "boolean",
|
||||
UnknownDefaultOpenApi: "11184809",
|
||||
} as const;
|
||||
export type ObjectAttributeTypeEnum =
|
||||
(typeof ObjectAttributeTypeEnum)[keyof typeof ObjectAttributeTypeEnum];
|
||||
|
||||
export function instanceOfObjectAttributeTypeEnum(value: any): boolean {
|
||||
for (const key in ObjectAttributeTypeEnum) {
|
||||
if (Object.prototype.hasOwnProperty.call(ObjectAttributeTypeEnum, key)) {
|
||||
if (ObjectAttributeTypeEnum[key as keyof typeof ObjectAttributeTypeEnum] === value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function ObjectAttributeTypeEnumFromJSON(json: any): ObjectAttributeTypeEnum {
|
||||
return ObjectAttributeTypeEnumFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function ObjectAttributeTypeEnumFromJSONTyped(
|
||||
json: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): ObjectAttributeTypeEnum {
|
||||
return json as ObjectAttributeTypeEnum;
|
||||
}
|
||||
|
||||
export function ObjectAttributeTypeEnumToJSON(value?: ObjectAttributeTypeEnum | null): any {
|
||||
return value as any;
|
||||
}
|
||||
|
||||
export function ObjectAttributeTypeEnumToJSONTyped(
|
||||
value: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): ObjectAttributeTypeEnum {
|
||||
return value as ObjectAttributeTypeEnum;
|
||||
}
|
||||
93
packages/client-ts/src/models/PaginatedObjectAttributeList.ts
generated
Normal file
93
packages/client-ts/src/models/PaginatedObjectAttributeList.ts
generated
Normal file
@@ -0,0 +1,93 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* authentik
|
||||
* Making authentication simple.
|
||||
*
|
||||
* The version of the OpenAPI document: 2026.5.0-rc1
|
||||
* Contact: hello@goauthentik.io
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import type { ObjectAttribute } from "./ObjectAttribute";
|
||||
import { ObjectAttributeFromJSON, ObjectAttributeToJSON } from "./ObjectAttribute";
|
||||
import type { Pagination } from "./Pagination";
|
||||
import { PaginationFromJSON, PaginationToJSON } from "./Pagination";
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface PaginatedObjectAttributeList
|
||||
*/
|
||||
export interface PaginatedObjectAttributeList {
|
||||
/**
|
||||
*
|
||||
* @type {Pagination}
|
||||
* @memberof PaginatedObjectAttributeList
|
||||
*/
|
||||
pagination: Pagination;
|
||||
/**
|
||||
*
|
||||
* @type {Array<ObjectAttribute>}
|
||||
* @memberof PaginatedObjectAttributeList
|
||||
*/
|
||||
results: Array<ObjectAttribute>;
|
||||
/**
|
||||
*
|
||||
* @type {{ [key: string]: any; }}
|
||||
* @memberof PaginatedObjectAttributeList
|
||||
*/
|
||||
autocomplete: { [key: string]: any };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the PaginatedObjectAttributeList interface.
|
||||
*/
|
||||
export function instanceOfPaginatedObjectAttributeList(
|
||||
value: object,
|
||||
): value is PaginatedObjectAttributeList {
|
||||
if (!("pagination" in value) || value["pagination"] === undefined) return false;
|
||||
if (!("results" in value) || value["results"] === undefined) return false;
|
||||
if (!("autocomplete" in value) || value["autocomplete"] === undefined) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function PaginatedObjectAttributeListFromJSON(json: any): PaginatedObjectAttributeList {
|
||||
return PaginatedObjectAttributeListFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function PaginatedObjectAttributeListFromJSONTyped(
|
||||
json: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): PaginatedObjectAttributeList {
|
||||
if (json == null) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
pagination: PaginationFromJSON(json["pagination"]),
|
||||
results: (json["results"] as Array<any>).map(ObjectAttributeFromJSON),
|
||||
autocomplete: json["autocomplete"],
|
||||
};
|
||||
}
|
||||
|
||||
export function PaginatedObjectAttributeListToJSON(json: any): PaginatedObjectAttributeList {
|
||||
return PaginatedObjectAttributeListToJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function PaginatedObjectAttributeListToJSONTyped(
|
||||
value?: PaginatedObjectAttributeList | null,
|
||||
ignoreDiscriminator: boolean = false,
|
||||
): any {
|
||||
if (value == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return {
|
||||
pagination: PaginationToJSON(value["pagination"]),
|
||||
results: (value["results"] as Array<any>).map(ObjectAttributeToJSON),
|
||||
autocomplete: value["autocomplete"],
|
||||
};
|
||||
}
|
||||
155
packages/client-ts/src/models/PatchedObjectAttributeRequest.ts
generated
Normal file
155
packages/client-ts/src/models/PatchedObjectAttributeRequest.ts
generated
Normal file
@@ -0,0 +1,155 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* authentik
|
||||
* Making authentication simple.
|
||||
*
|
||||
* The version of the OpenAPI document: 2026.5.0-rc1
|
||||
* Contact: hello@goauthentik.io
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import type { ObjectAttributeTypeEnum } from "./ObjectAttributeTypeEnum";
|
||||
import {
|
||||
ObjectAttributeTypeEnumFromJSON,
|
||||
ObjectAttributeTypeEnumToJSON,
|
||||
} from "./ObjectAttributeTypeEnum";
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface PatchedObjectAttributeRequest
|
||||
*/
|
||||
export interface PatchedObjectAttributeRequest {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PatchedObjectAttributeRequest
|
||||
*/
|
||||
objectType?: string;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof PatchedObjectAttributeRequest
|
||||
*/
|
||||
enabled?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PatchedObjectAttributeRequest
|
||||
*/
|
||||
key?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PatchedObjectAttributeRequest
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PatchedObjectAttributeRequest
|
||||
*/
|
||||
regex?: string;
|
||||
/**
|
||||
*
|
||||
* @type {ObjectAttributeTypeEnum}
|
||||
* @memberof PatchedObjectAttributeRequest
|
||||
*/
|
||||
type?: ObjectAttributeTypeEnum;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PatchedObjectAttributeRequest
|
||||
*/
|
||||
group?: string;
|
||||
/**
|
||||
* Objects that are managed by authentik. These objects are created and updated automatically. This flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update.
|
||||
* @type {string}
|
||||
* @memberof PatchedObjectAttributeRequest
|
||||
*/
|
||||
managed?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof PatchedObjectAttributeRequest
|
||||
*/
|
||||
isUnique?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof PatchedObjectAttributeRequest
|
||||
*/
|
||||
isRequired?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof PatchedObjectAttributeRequest
|
||||
*/
|
||||
isArray?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the PatchedObjectAttributeRequest interface.
|
||||
*/
|
||||
export function instanceOfPatchedObjectAttributeRequest(
|
||||
value: object,
|
||||
): value is PatchedObjectAttributeRequest {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function PatchedObjectAttributeRequestFromJSON(json: any): PatchedObjectAttributeRequest {
|
||||
return PatchedObjectAttributeRequestFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function PatchedObjectAttributeRequestFromJSONTyped(
|
||||
json: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): PatchedObjectAttributeRequest {
|
||||
if (json == null) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
objectType: json["object_type"] == null ? undefined : json["object_type"],
|
||||
enabled: json["enabled"] == null ? undefined : json["enabled"],
|
||||
key: json["key"] == null ? undefined : json["key"],
|
||||
label: json["label"] == null ? undefined : json["label"],
|
||||
regex: json["regex"] == null ? undefined : json["regex"],
|
||||
type: json["type"] == null ? undefined : ObjectAttributeTypeEnumFromJSON(json["type"]),
|
||||
group: json["group"] == null ? undefined : json["group"],
|
||||
managed: json["managed"] == null ? undefined : json["managed"],
|
||||
isUnique: json["is_unique"] == null ? undefined : json["is_unique"],
|
||||
isRequired: json["is_required"] == null ? undefined : json["is_required"],
|
||||
isArray: json["is_array"] == null ? undefined : json["is_array"],
|
||||
};
|
||||
}
|
||||
|
||||
export function PatchedObjectAttributeRequestToJSON(json: any): PatchedObjectAttributeRequest {
|
||||
return PatchedObjectAttributeRequestToJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function PatchedObjectAttributeRequestToJSONTyped(
|
||||
value?: PatchedObjectAttributeRequest | null,
|
||||
ignoreDiscriminator: boolean = false,
|
||||
): any {
|
||||
if (value == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return {
|
||||
object_type: value["objectType"],
|
||||
enabled: value["enabled"],
|
||||
key: value["key"],
|
||||
label: value["label"],
|
||||
regex: value["regex"],
|
||||
type: ObjectAttributeTypeEnumToJSON(value["type"]),
|
||||
group: value["group"],
|
||||
managed: value["managed"],
|
||||
is_unique: value["isUnique"],
|
||||
is_required: value["isRequired"],
|
||||
is_array: value["isArray"],
|
||||
};
|
||||
}
|
||||
5
packages/client-ts/src/models/index.ts
generated
5
packages/client-ts/src/models/index.ts
generated
@@ -344,6 +344,9 @@ export * from "./OAuthSource";
|
||||
export * from "./OAuthSourcePropertyMapping";
|
||||
export * from "./OAuthSourcePropertyMappingRequest";
|
||||
export * from "./OAuthSourceRequest";
|
||||
export * from "./ObjectAttribute";
|
||||
export * from "./ObjectAttributeRequest";
|
||||
export * from "./ObjectAttributeTypeEnum";
|
||||
export * from "./OpenIDConnectConfiguration";
|
||||
export * from "./OperatingSystem";
|
||||
export * from "./OperatingSystemRequest";
|
||||
@@ -439,6 +442,7 @@ export * from "./PaginatedNotificationWebhookMappingList";
|
||||
export * from "./PaginatedOAuth2ProviderList";
|
||||
export * from "./PaginatedOAuthSourceList";
|
||||
export * from "./PaginatedOAuthSourcePropertyMappingList";
|
||||
export * from "./PaginatedObjectAttributeList";
|
||||
export * from "./PaginatedOutpostList";
|
||||
export * from "./PaginatedPasswordExpiryPolicyList";
|
||||
export * from "./PaginatedPasswordPolicyList";
|
||||
@@ -595,6 +599,7 @@ export * from "./PatchedNotificationWebhookMappingRequest";
|
||||
export * from "./PatchedOAuth2ProviderRequest";
|
||||
export * from "./PatchedOAuthSourcePropertyMappingRequest";
|
||||
export * from "./PatchedOAuthSourceRequest";
|
||||
export * from "./PatchedObjectAttributeRequest";
|
||||
export * from "./PatchedOutpostRequest";
|
||||
export * from "./PatchedPasswordExpiryPolicyRequest";
|
||||
export * from "./PatchedPasswordPolicyRequest";
|
||||
|
||||
@@ -85,7 +85,7 @@ dev = [
|
||||
"coverage[toml]==7.13.5",
|
||||
"daphne==4.2.1",
|
||||
"debugpy==1.8.20",
|
||||
"django-stubs[compatible-mypy]==6.0.3",
|
||||
"django-stubs[compatible-mypy]==6.0.4",
|
||||
"djangorestframework-stubs[compatible-mypy]==3.16.9",
|
||||
"drf-jsonschema-serializer==3.0.0",
|
||||
"freezegun==1.5.5",
|
||||
|
||||
333
schema.yml
333
schema.yml
@@ -134,6 +134,12 @@ paths:
|
||||
get:
|
||||
operationId: admin_models_list
|
||||
description: Read-only view list all installed models
|
||||
parameters:
|
||||
- in: query
|
||||
name: filter_has_attributes
|
||||
schema:
|
||||
type: boolean
|
||||
nullable: true
|
||||
tags:
|
||||
- admin
|
||||
security:
|
||||
@@ -3720,6 +3726,172 @@ paths:
|
||||
$ref: '#/components/responses/ValidationErrorResponse'
|
||||
'403':
|
||||
$ref: '#/components/responses/GenericErrorResponse'
|
||||
/core/object_attributes/:
|
||||
get:
|
||||
operationId: core_object_attributes_list
|
||||
parameters:
|
||||
- in: query
|
||||
name: enabled
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: object_type__app_label
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: object_type__model
|
||||
schema:
|
||||
type: string
|
||||
- $ref: '#/components/parameters/QueryPaginationOrdering'
|
||||
- $ref: '#/components/parameters/QueryPaginationPage'
|
||||
- $ref: '#/components/parameters/QueryPaginationPageSize'
|
||||
- $ref: '#/components/parameters/QuerySearch'
|
||||
tags:
|
||||
- core
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedObjectAttributeList'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/responses/ValidationErrorResponse'
|
||||
'403':
|
||||
$ref: '#/components/responses/GenericErrorResponse'
|
||||
post:
|
||||
operationId: core_object_attributes_create
|
||||
tags:
|
||||
- core
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ObjectAttributeRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ObjectAttribute'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/responses/ValidationErrorResponse'
|
||||
'403':
|
||||
$ref: '#/components/responses/GenericErrorResponse'
|
||||
/core/object_attributes/{attribute_id}/:
|
||||
get:
|
||||
operationId: core_object_attributes_retrieve
|
||||
parameters:
|
||||
- in: path
|
||||
name: attribute_id
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Object Attribute.
|
||||
required: true
|
||||
tags:
|
||||
- core
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ObjectAttribute'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/responses/ValidationErrorResponse'
|
||||
'403':
|
||||
$ref: '#/components/responses/GenericErrorResponse'
|
||||
put:
|
||||
operationId: core_object_attributes_update
|
||||
parameters:
|
||||
- in: path
|
||||
name: attribute_id
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Object Attribute.
|
||||
required: true
|
||||
tags:
|
||||
- core
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ObjectAttributeRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ObjectAttribute'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/responses/ValidationErrorResponse'
|
||||
'403':
|
||||
$ref: '#/components/responses/GenericErrorResponse'
|
||||
patch:
|
||||
operationId: core_object_attributes_partial_update
|
||||
parameters:
|
||||
- in: path
|
||||
name: attribute_id
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Object Attribute.
|
||||
required: true
|
||||
tags:
|
||||
- core
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedObjectAttributeRequest'
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ObjectAttribute'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/responses/ValidationErrorResponse'
|
||||
'403':
|
||||
$ref: '#/components/responses/GenericErrorResponse'
|
||||
delete:
|
||||
operationId: core_object_attributes_destroy
|
||||
parameters:
|
||||
- in: path
|
||||
name: attribute_id
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Object Attribute.
|
||||
required: true
|
||||
tags:
|
||||
- core
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
$ref: '#/components/responses/ValidationErrorResponse'
|
||||
'403':
|
||||
$ref: '#/components/responses/GenericErrorResponse'
|
||||
/core/tokens/:
|
||||
get:
|
||||
operationId: core_tokens_list
|
||||
@@ -36858,8 +37030,12 @@ components:
|
||||
verbose_name_plural:
|
||||
type: string
|
||||
readOnly: true
|
||||
fully_qualified_model:
|
||||
type: string
|
||||
readOnly: true
|
||||
required:
|
||||
- app_label
|
||||
- fully_qualified_model
|
||||
- id
|
||||
- model
|
||||
- verbose_name_plural
|
||||
@@ -43421,6 +43597,7 @@ components:
|
||||
- authentik_core.application
|
||||
- authentik_core.applicationentitlement
|
||||
- authentik_core.token
|
||||
- authentik_core.objectattribute
|
||||
- authentik_crypto.certificatekeypair
|
||||
- authentik_endpoints.deviceuserbinding
|
||||
- authentik_endpoints.deviceaccessgroup
|
||||
@@ -44702,6 +44879,111 @@ components:
|
||||
- name
|
||||
- provider_type
|
||||
- slug
|
||||
ObjectAttribute:
|
||||
type: object
|
||||
properties:
|
||||
pk:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
title: Attribute id
|
||||
object_type:
|
||||
type: string
|
||||
object_type_obj:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/ContentType'
|
||||
readOnly: true
|
||||
enabled:
|
||||
type: boolean
|
||||
default: true
|
||||
created:
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
key:
|
||||
type: string
|
||||
label:
|
||||
type: string
|
||||
last_updated:
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
regex:
|
||||
type: string
|
||||
type:
|
||||
$ref: '#/components/schemas/ObjectAttributeTypeEnum'
|
||||
group:
|
||||
type: string
|
||||
managed:
|
||||
type: string
|
||||
nullable: true
|
||||
title: Managed by authentik
|
||||
description: Objects that are managed by authentik. These objects are created
|
||||
and updated automatically. This flag only indicates that an object can
|
||||
be overwritten by migrations. You can still modify the objects via the
|
||||
API, but expect changes to be overwritten in a later update.
|
||||
is_unique:
|
||||
type: boolean
|
||||
is_required:
|
||||
type: boolean
|
||||
is_array:
|
||||
type: boolean
|
||||
required:
|
||||
- created
|
||||
- key
|
||||
- label
|
||||
- last_updated
|
||||
- object_type
|
||||
- object_type_obj
|
||||
- pk
|
||||
- type
|
||||
ObjectAttributeRequest:
|
||||
type: object
|
||||
properties:
|
||||
object_type:
|
||||
type: string
|
||||
minLength: 1
|
||||
enabled:
|
||||
type: boolean
|
||||
default: true
|
||||
key:
|
||||
type: string
|
||||
minLength: 1
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
regex:
|
||||
type: string
|
||||
type:
|
||||
$ref: '#/components/schemas/ObjectAttributeTypeEnum'
|
||||
group:
|
||||
type: string
|
||||
managed:
|
||||
type: string
|
||||
nullable: true
|
||||
minLength: 1
|
||||
title: Managed by authentik
|
||||
description: Objects that are managed by authentik. These objects are created
|
||||
and updated automatically. This flag only indicates that an object can
|
||||
be overwritten by migrations. You can still modify the objects via the
|
||||
API, but expect changes to be overwritten in a later update.
|
||||
is_unique:
|
||||
type: boolean
|
||||
is_required:
|
||||
type: boolean
|
||||
is_array:
|
||||
type: boolean
|
||||
required:
|
||||
- key
|
||||
- label
|
||||
- object_type
|
||||
- type
|
||||
ObjectAttributeTypeEnum:
|
||||
enum:
|
||||
- text
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
OpenIDConnectConfiguration:
|
||||
type: object
|
||||
description: rest_framework Serializer for OIDC Configuration
|
||||
@@ -46252,6 +46534,21 @@ components:
|
||||
- autocomplete
|
||||
- pagination
|
||||
- results
|
||||
PaginatedObjectAttributeList:
|
||||
type: object
|
||||
properties:
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ObjectAttribute'
|
||||
autocomplete:
|
||||
$ref: '#/components/schemas/Autocomplete'
|
||||
required:
|
||||
- autocomplete
|
||||
- pagination
|
||||
- results
|
||||
PaginatedOutpostList:
|
||||
type: object
|
||||
properties:
|
||||
@@ -50034,6 +50331,42 @@ components:
|
||||
- $ref: '#/components/schemas/AuthorizationCodeAuthMethodEnum'
|
||||
description: How to perform authentication during an authorization_code
|
||||
token request flow
|
||||
PatchedObjectAttributeRequest:
|
||||
type: object
|
||||
properties:
|
||||
object_type:
|
||||
type: string
|
||||
minLength: 1
|
||||
enabled:
|
||||
type: boolean
|
||||
default: true
|
||||
key:
|
||||
type: string
|
||||
minLength: 1
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
regex:
|
||||
type: string
|
||||
type:
|
||||
$ref: '#/components/schemas/ObjectAttributeTypeEnum'
|
||||
group:
|
||||
type: string
|
||||
managed:
|
||||
type: string
|
||||
nullable: true
|
||||
minLength: 1
|
||||
title: Managed by authentik
|
||||
description: Objects that are managed by authentik. These objects are created
|
||||
and updated automatically. This flag only indicates that an object can
|
||||
be overwritten by migrations. You can still modify the objects via the
|
||||
API, but expect changes to be overwritten in a later update.
|
||||
is_unique:
|
||||
type: boolean
|
||||
is_required:
|
||||
type: boolean
|
||||
is_array:
|
||||
type: boolean
|
||||
PatchedOutpostRequest:
|
||||
type: object
|
||||
description: Outpost Serializer
|
||||
|
||||
8
uv.lock
generated
8
uv.lock
generated
@@ -394,7 +394,7 @@ dev = [
|
||||
{ name = "coverage", extras = ["toml"], specifier = "==7.13.5" },
|
||||
{ name = "daphne", specifier = "==4.2.1" },
|
||||
{ name = "debugpy", specifier = "==1.8.20" },
|
||||
{ name = "django-stubs", extras = ["compatible-mypy"], specifier = "==6.0.3" },
|
||||
{ name = "django-stubs", extras = ["compatible-mypy"], specifier = "==6.0.4" },
|
||||
{ name = "djangorestframework-stubs", extras = ["compatible-mypy"], specifier = "==3.16.9" },
|
||||
{ name = "drf-jsonschema-serializer", specifier = "==3.0.0" },
|
||||
{ name = "freezegun", specifier = "==1.5.5" },
|
||||
@@ -1269,7 +1269,7 @@ s3 = [
|
||||
|
||||
[[package]]
|
||||
name = "django-stubs"
|
||||
version = "6.0.3"
|
||||
version = "6.0.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
@@ -1277,9 +1277,9 @@ dependencies = [
|
||||
{ name = "types-pyyaml" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/86/0c/8d0d875af79bf774c1c3997c84aa118dba3a77be12086b9c14e130e8ec72/django_stubs-6.0.3.tar.gz", hash = "sha256:ee895f403c373608eeb50822f0733f9d9ec5ab12731d4ab58956053bb95fdd9e", size = 278214, upload-time = "2026-04-18T15:11:22.327Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/82/ccf2a2dc9cdb4bd9cbe91f11e887589bf2da7609506db00ccbc73bd8a6da/django_stubs-6.0.4.tar.gz", hash = "sha256:7aee77e8de9c14c0d9cf84988befe826d93cbc15a87e0ade2943f14d553451cf", size = 280019, upload-time = "2026-05-09T21:24:30.436Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/80/a3/6751b7684d20fc4f228bdd3dd8341d382ab3faaf65d3d050c0d59ab0a1b0/django_stubs-6.0.3-py3-none-any.whl", hash = "sha256:5fee22bcbbad59a78c727a820b6f4e68ff442ca76a922b7002e57c25dd7cb390", size = 541570, upload-time = "2026-04-18T15:11:20.711Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/e7/5128914ada94dd6277626ef5a4a5680a4def7d2f9366214d26c1cd86723b/django_stubs-6.0.4-py3-none-any.whl", hash = "sha256:e991c68f77239663577a5f4fc75e99c84f867f378cafc97cbf4acc5aff378279", size = 543791, upload-time = "2026-05-09T21:24:28.218Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
|
||||
@@ -87,6 +87,10 @@ export const ROUTES: Route[] = [
|
||||
await import("#admin/policies/reputation/ReputationListPage");
|
||||
return html`<ak-policy-reputation-list></ak-policy-reputation-list>`;
|
||||
}),
|
||||
new Route(new RegExp("^/identity/object-attributes$"), async () => {
|
||||
await import("#admin/object-attributes/ObjectAttributeListPage");
|
||||
return html`<ak-object-attribute-list></ak-object-attribute-list>`;
|
||||
}),
|
||||
new Route(new RegExp("^/identity/groups$"), async () => {
|
||||
await import("#admin/groups/GroupListPage");
|
||||
return html`<ak-group-list></ak-group-list>`;
|
||||
|
||||
@@ -29,16 +29,15 @@ import { renderDialog } from "#elements/dialogs";
|
||||
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import { WithNotifications } from "#elements/mixins/notifications";
|
||||
import { canAccessAdmin, WithSession } from "#elements/mixins/session";
|
||||
import { navigate } from "#elements/router/RouterOutlet";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import { AKDrawerChangeEvent } from "#components/notifications/events";
|
||||
import { AKDrawerChangeEvent } from "#elements/notifications/events";
|
||||
import {
|
||||
DrawerState,
|
||||
persistDrawerParams,
|
||||
readDrawerParams,
|
||||
renderNotificationDrawerPanel,
|
||||
} from "#components/notifications/utils";
|
||||
} from "#elements/notifications/utils";
|
||||
import { navigate } from "#elements/router/RouterOutlet";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import Styles from "#admin/ak-interface-admin.css";
|
||||
import { ROUTES } from "#admin/Routes";
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import "#elements/CodeMirror";
|
||||
import "#elements/forms/HorizontalFormElement";
|
||||
import "#elements/forms/Radio";
|
||||
import "#elements/forms/SearchSelect/index";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
|
||||
import { ModelForm } from "#elements/forms/ModelForm";
|
||||
import { ObjectAttributeModelForm } from "#admin/object-attributes/renderAttributes";
|
||||
|
||||
import { ApplicationEntitlement, CoreApi } from "@goauthentik/api";
|
||||
|
||||
import YAML from "yaml";
|
||||
import { ApplicationEntitlement, CoreApi, ModelEnum } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
@@ -18,7 +15,12 @@ import { customElement, property } from "lit/decorators.js";
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
|
||||
@customElement("ak-application-entitlement-form")
|
||||
export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement, string> {
|
||||
export class ApplicationEntitlementForm extends ObjectAttributeModelForm<
|
||||
ApplicationEntitlement,
|
||||
string
|
||||
> {
|
||||
public model = ModelEnum.AuthentikCoreApplicationentitlement;
|
||||
|
||||
async loadInstance(pk: string): Promise<ApplicationEntitlement> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsRetrieve({
|
||||
pbmUuid: pk,
|
||||
@@ -61,16 +63,7 @@ export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Attributes")} name="attributes">
|
||||
<ak-codemirror
|
||||
mode="yaml"
|
||||
value="${YAML.stringify(this.instance?.attributes ?? {})}"
|
||||
>
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Set custom attributes using YAML or JSON.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
${this.renderObjectAttributes(this.objAttributes, this.instance)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,16 @@ import "#elements/forms/HorizontalFormElement";
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { PFSize } from "#common/enums";
|
||||
|
||||
import { ModelForm } from "#elements/forms/ModelForm";
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
|
||||
import { DeviceAccessGroup, DeviceAccessGroupRequest, EndpointsApi } from "@goauthentik/api";
|
||||
import { ObjectAttributeModelForm } from "#admin/object-attributes/renderAttributes";
|
||||
|
||||
import {
|
||||
DeviceAccessGroup,
|
||||
DeviceAccessGroupRequest,
|
||||
EndpointsApi,
|
||||
ModelEnum,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html } from "lit";
|
||||
@@ -20,7 +26,11 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
* @prop {string} instancePk - The primary key of the instance to load.
|
||||
*/
|
||||
@customElement("ak-endpoints-device-access-groups-form")
|
||||
export class DeviceAccessGroupForm extends WithBrandConfig(ModelForm<DeviceAccessGroup, string>) {
|
||||
export class DeviceAccessGroupForm extends WithBrandConfig(
|
||||
ObjectAttributeModelForm<DeviceAccessGroup, string>,
|
||||
) {
|
||||
public model = ModelEnum.AuthentikEndpointsDeviceaccessgroup;
|
||||
|
||||
public static override verboseName = msg("Device Access Group");
|
||||
public static override verboseNamePlural = msg("Device Access Groups");
|
||||
|
||||
@@ -53,13 +63,14 @@ export class DeviceAccessGroupForm extends WithBrandConfig(ModelForm<DeviceAcces
|
||||
|
||||
protected override renderForm() {
|
||||
return html`<ak-text-input
|
||||
name="name"
|
||||
autocomplete="off"
|
||||
placeholder=${msg("Type a group name...")}
|
||||
label=${msg("Group Name")}
|
||||
value=${ifDefined(this.instance?.name)}
|
||||
required
|
||||
></ak-text-input>`;
|
||||
name="name"
|
||||
autocomplete="off"
|
||||
placeholder=${msg("Type a group name...")}
|
||||
label=${msg("Group Name")}
|
||||
value=${ifDefined(this.instance?.name)}
|
||||
required
|
||||
></ak-text-input>
|
||||
${this.renderObjectAttributes(this.objAttributes, this.instance)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import "#admin/groups/ak-group-member-table";
|
||||
import "#components/ak-switch-input";
|
||||
import "#components/ak-text-input";
|
||||
import "#elements/CodeMirror";
|
||||
import "#elements/ak-dual-select/ak-dual-select-provider";
|
||||
import "#elements/chips/Chip";
|
||||
import "#elements/chips/ChipGroup";
|
||||
import "#elements/forms/HorizontalFormElement";
|
||||
import "#elements/forms/SearchSelect/index";
|
||||
import "#components/ak-text-input";
|
||||
import "#components/ak-switch-input";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
|
||||
import { DataProvision, DualSelectPair } from "#elements/ak-dual-select/types";
|
||||
import { ModelForm } from "#elements/forms/ModelForm";
|
||||
|
||||
import { CoreApi, Group, RbacApi, RelatedGroup, Role } from "@goauthentik/api";
|
||||
import { ObjectAttributeModelForm } from "#admin/object-attributes/renderAttributes";
|
||||
|
||||
import YAML from "yaml";
|
||||
import { CoreApi, Group, ModelEnum, RbacApi, RelatedGroup, Role } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, CSSResult, html, TemplateResult } from "lit";
|
||||
@@ -30,7 +29,8 @@ export function rbacRolePair(item: Role): DualSelectPair {
|
||||
}
|
||||
|
||||
@customElement("ak-group-form")
|
||||
export class GroupForm extends ModelForm<Group, string> {
|
||||
export class GroupForm extends ObjectAttributeModelForm<Group, string> {
|
||||
public model = ModelEnum.AuthentikCoreGroup;
|
||||
static styles: CSSResult[] = [
|
||||
...super.styles,
|
||||
css`
|
||||
@@ -144,16 +144,7 @@ export class GroupForm extends ModelForm<Group, string> {
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Attributes")} name="attributes">
|
||||
<ak-codemirror
|
||||
mode="yaml"
|
||||
value="${YAML.stringify(this.instance?.attributes ?? {})}"
|
||||
>
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Set custom attributes using YAML or JSON.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
${this.renderObjectAttributes(this.objAttributes, this.instance)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ export const createAdminSidebarEntries = (): readonly SidebarEntry[] => [
|
||||
["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]],
|
||||
["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]],
|
||||
["/identity/roles", msg("Roles"), [`^/identity/roles/(?<id>${UUID_REGEX})$`]],
|
||||
["/identity/object-attributes", msg("Object attributes")],
|
||||
["/identity/initial-permissions", msg("Initial Permissions"), [`^/identity/initial-permissions/(?<id>${ID_REGEX})$`]],
|
||||
["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?<slug>${SLUG_REGEX})$`]],
|
||||
["/core/tokens", msg("Tokens and App passwords")],
|
||||
|
||||
163
web/src/admin/object-attributes/ObjectAttributeForm.ts
Normal file
163
web/src/admin/object-attributes/ObjectAttributeForm.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import "#elements/forms/FormGroup";
|
||||
import "#elements/forms/HorizontalFormElement";
|
||||
import "#elements/forms/Radio";
|
||||
import "#elements/forms/SearchSelect/index";
|
||||
import "#components/ak-text-input";
|
||||
import "#components/ak-switch-input";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
|
||||
import { ModelForm } from "#elements/forms/ModelForm";
|
||||
|
||||
import {
|
||||
AdminApi,
|
||||
AdminModelsListRequest,
|
||||
App,
|
||||
CoreApi,
|
||||
ObjectAttribute,
|
||||
ObjectAttributeTypeEnum,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-object-attribute-form")
|
||||
export class ObjectAttributeForm extends ModelForm<ObjectAttribute, string> {
|
||||
async loadInstance(pk: string): Promise<ObjectAttribute> {
|
||||
return await new CoreApi(DEFAULT_CONFIG).coreObjectAttributesRetrieve({
|
||||
attributeId: pk,
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated attribute.")
|
||||
: msg("Successfully created attribute.");
|
||||
}
|
||||
|
||||
async send(data: ObjectAttribute): Promise<ObjectAttribute> {
|
||||
data.regex = data.regex !== "" ? data.regex : undefined;
|
||||
if (this.instance?.pk) {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreObjectAttributesUpdate({
|
||||
attributeId: this.instance.pk,
|
||||
objectAttributeRequest: data,
|
||||
});
|
||||
}
|
||||
return new CoreApi(DEFAULT_CONFIG).coreObjectAttributesCreate({
|
||||
objectAttributeRequest: data,
|
||||
});
|
||||
}
|
||||
|
||||
//#region Renders
|
||||
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html`<ak-text-input
|
||||
name="label"
|
||||
value="${this.instance?.label ?? ""}"
|
||||
label=${msg("Label")}
|
||||
placeholder=${msg("Type a human-readable name...")}
|
||||
required
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="key"
|
||||
value="${this.instance?.key ?? ""}"
|
||||
label=${msg("Key")}
|
||||
placeholder=${msg("Type a unique identifier...")}
|
||||
required
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="group"
|
||||
value="${this.instance?.group ?? ""}"
|
||||
label=${msg("Group")}
|
||||
placeholder=${msg("Type an optional group identifier...")}
|
||||
></ak-text-input>
|
||||
<ak-switch-input
|
||||
name="enabled"
|
||||
label=${msg("Enabled")}
|
||||
?checked=${this.instance?.enabled ?? true}
|
||||
help=${msg("Value of the attribute cannot be empty.")}
|
||||
></ak-switch-input>
|
||||
<ak-form-element-horizontal label=${msg("Type")} required name="type">
|
||||
<ak-radio
|
||||
.options=${[
|
||||
{
|
||||
label: msg("Text"),
|
||||
value: ObjectAttributeTypeEnum.Text,
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
label: msg("Number"),
|
||||
value: ObjectAttributeTypeEnum.Number,
|
||||
},
|
||||
{
|
||||
label: msg("Boolean"),
|
||||
value: ObjectAttributeTypeEnum.Boolean,
|
||||
},
|
||||
]}
|
||||
.value=${this.instance?.type}
|
||||
>
|
||||
</ak-radio>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label="Object type" name="objectType" required>
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (): Promise<App[]> => {
|
||||
const args: AdminModelsListRequest = {
|
||||
filterHasAttributes: true,
|
||||
};
|
||||
return await new AdminApi(DEFAULT_CONFIG).adminModelsList(args);
|
||||
}}
|
||||
.renderElement=${(app: App): string => {
|
||||
return app.label;
|
||||
}}
|
||||
.value=${(app: App | undefined): string | undefined => {
|
||||
return app?.name;
|
||||
}}
|
||||
.selected=${(app: App): boolean => {
|
||||
return app.name === this.instance?.objectTypeObj.fullyQualifiedModel;
|
||||
}}
|
||||
>
|
||||
</ak-search-select>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group label=${msg("Validation")} open>
|
||||
<div class="pf-c-form">
|
||||
<ak-switch-input
|
||||
name="isRequired"
|
||||
label=${msg("Attribute is required")}
|
||||
?checked=${this.instance?.isRequired}
|
||||
help=${msg("Value of the attribute cannot be empty.")}
|
||||
></ak-switch-input>
|
||||
<ak-switch-input
|
||||
name="isUnique"
|
||||
label=${msg("Attribute is unique")}
|
||||
?checked=${this.instance?.isUnique}
|
||||
help=${msg(
|
||||
"Value of the attribute must be unique across all instances of the selected object type.",
|
||||
)}
|
||||
></ak-switch-input>
|
||||
<ak-switch-input
|
||||
name="isArray"
|
||||
label=${msg("Attribute is an array")}
|
||||
?checked=${this.instance?.isArray}
|
||||
help=${msg("Value can have multiple entries.")}
|
||||
></ak-switch-input>
|
||||
<ak-text-input
|
||||
name="regex"
|
||||
value="${this.instance?.regex ?? ""}"
|
||||
label=${msg("RegEx")}
|
||||
input-hint="code"
|
||||
placeholder=${msg("Enter a regex for validation...")}
|
||||
></ak-text-input>
|
||||
</div>
|
||||
</ak-form-group>`;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-object-attribute-form": ObjectAttributeForm;
|
||||
}
|
||||
}
|
||||
129
web/src/admin/object-attributes/ObjectAttributeListPage.ts
Normal file
129
web/src/admin/object-attributes/ObjectAttributeListPage.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import "#admin/rbac/ObjectPermissionModal";
|
||||
import "#admin/object-attributes/ObjectAttributeForm";
|
||||
import "#elements/forms/DeleteBulkForm";
|
||||
import "#elements/forms/ModalForm";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
import "#components/ak-status-label";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
|
||||
import { PaginatedResponse, TableColumn } from "#elements/table/Table";
|
||||
import { TablePage } from "#elements/table/TablePage";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import { CoreApi, ObjectAttribute, ObjectAttributeTypeEnum } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
export function objectAttributeTypeToLabel(type?: ObjectAttributeTypeEnum): string {
|
||||
if (!type) return "";
|
||||
switch (type) {
|
||||
case ObjectAttributeTypeEnum.Text:
|
||||
return msg("Text");
|
||||
case ObjectAttributeTypeEnum.Number:
|
||||
return msg("Number");
|
||||
case ObjectAttributeTypeEnum.Boolean:
|
||||
return msg("Boolean");
|
||||
}
|
||||
return msg("Unknown type");
|
||||
}
|
||||
|
||||
@customElement("ak-object-attribute-list")
|
||||
export class ObjectAttributeListPage extends TablePage<ObjectAttribute> {
|
||||
protected override searchEnabled = true;
|
||||
public pageTitle = msg("Object attributes");
|
||||
public pageDescription = "Configure attributes on objects such as users and groups.";
|
||||
public pageIcon = "pf-icon pf-icon-flavor";
|
||||
|
||||
protected override rowLabel(item: ObjectAttribute): string | null {
|
||||
return item.pk ?? null;
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
|
||||
@property()
|
||||
order = "key";
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<ObjectAttribute>> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreObjectAttributesList(
|
||||
await this.defaultEndpointConfig(),
|
||||
);
|
||||
}
|
||||
|
||||
protected columns: TableColumn[] = [
|
||||
[msg("Label"), "label"],
|
||||
[msg("Type"), "type"],
|
||||
[msg("Status"), "enabled"],
|
||||
[msg("Object type"), "object_type"],
|
||||
[msg("Actions"), null, msg("Row Actions")],
|
||||
];
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
object-label=${msg("Object Attribute(s)")}
|
||||
.objects=${this.selectedElements}
|
||||
.metadata=${(item: ObjectAttribute) => {
|
||||
return [
|
||||
{ key: msg("Object type"), value: item.objectTypeObj.verboseNamePlural },
|
||||
{ key: msg("Label"), value: item.label },
|
||||
{ key: msg("Key"), value: item.key },
|
||||
];
|
||||
}}
|
||||
.delete=${(item: ObjectAttribute) => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreObjectAttributesDestroy({
|
||||
attributeId: item.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${msg("Delete")}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">${msg("Create")}</span>
|
||||
<span slot="header">${msg("New Attribute")}</span>
|
||||
<ak-object-attribute-form slot="form"> </ak-object-attribute-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${msg("Create")}</button>
|
||||
</ak-forms-modal>
|
||||
`;
|
||||
}
|
||||
|
||||
row(item: ObjectAttribute): SlottedTemplateResult[] {
|
||||
return [
|
||||
html`<div>
|
||||
<div>${item.group}: ${item.label}</div>
|
||||
<code>${item.key}</code>
|
||||
</div>`,
|
||||
html`${objectAttributeTypeToLabel(item.type)}`,
|
||||
html`<ak-status-label ?good=${item.enabled} type="info"></ak-status-label>`,
|
||||
html`${item.objectTypeObj.verboseNamePlural}`,
|
||||
html`<ak-forms-modal>
|
||||
<span slot="submit">${msg("Save Changes")}</span>
|
||||
<span slot="header">${msg("Update Attribute")}</span>
|
||||
<ak-object-attribute-form
|
||||
slot="form"
|
||||
.instancePk=${item.pk}
|
||||
></ak-object-attribute-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-plain">
|
||||
<pf-tooltip position="top" content=${msg("Edit")}>
|
||||
<i class="fas fa-edit" aria-hidden="true"></i>
|
||||
</pf-tooltip>
|
||||
</button>
|
||||
</ak-forms-modal> `,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-object-attribute-list": ObjectAttributeListPage;
|
||||
}
|
||||
}
|
||||
104
web/src/admin/object-attributes/renderAttributes.ts
Normal file
104
web/src/admin/object-attributes/renderAttributes.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import "#components/ak-text-input";
|
||||
import "#components/ak-switch-input";
|
||||
import "#components/ak-number-input";
|
||||
import "#elements/forms/FormGroup";
|
||||
import "#elements/CodeMirror/ak-codemirror";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { groupBy } from "#common/utils";
|
||||
|
||||
import { ModelForm } from "#elements/forms/ModelForm";
|
||||
|
||||
import { CoreApi, ModelEnum, ObjectAttribute, ObjectAttributeTypeEnum } from "@goauthentik/api";
|
||||
|
||||
import YAML from "yaml";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html, nothing } from "lit-html";
|
||||
import { state } from "lit/decorators.js";
|
||||
|
||||
export interface ObjectAttributeOptions {
|
||||
disableRawAttributes: boolean;
|
||||
}
|
||||
|
||||
export type AttributesMixin = {
|
||||
attributes?: { [key: string]: unknown };
|
||||
};
|
||||
|
||||
export abstract class ObjectAttributeModelForm<
|
||||
T extends object = object,
|
||||
PKT extends string | number = string | number,
|
||||
D = T,
|
||||
> extends ModelForm<T, PKT, D> {
|
||||
@state()
|
||||
objAttributes: ObjectAttribute[] = [];
|
||||
|
||||
public abstract model: ModelEnum;
|
||||
|
||||
async load() {
|
||||
const [app, model] = this.model.split(".");
|
||||
this.objAttributes = (
|
||||
await new CoreApi(DEFAULT_CONFIG).coreObjectAttributesList({
|
||||
objectTypeAppLabel: app,
|
||||
objectTypeModel: model,
|
||||
enabled: true,
|
||||
})
|
||||
).results;
|
||||
}
|
||||
|
||||
renderObjectAttributes(
|
||||
defs: ObjectAttribute[],
|
||||
obj: AttributesMixin | null,
|
||||
options?: ObjectAttributeOptions,
|
||||
) {
|
||||
const attrs = obj?.attributes || {};
|
||||
const renderSingleAttribute = (attr: ObjectAttribute) => {
|
||||
switch (attr.type) {
|
||||
case ObjectAttributeTypeEnum.Text:
|
||||
return html`<ak-text-input
|
||||
name="attributes.${attr.key}"
|
||||
label=${attr.label}
|
||||
autocomplete="off"
|
||||
.value="${attrs[attr.key]}"
|
||||
?required=${attr.isRequired}
|
||||
></ak-text-input>`;
|
||||
case ObjectAttributeTypeEnum.Number:
|
||||
return html`<ak-number-input
|
||||
name="attributes.${attr.key}"
|
||||
label=${attr.label}
|
||||
.value="${attrs[attr.key]}"
|
||||
?required=${attr.isRequired}
|
||||
></ak-number-input>`;
|
||||
case ObjectAttributeTypeEnum.Boolean:
|
||||
return html`<ak-switch-input
|
||||
name="attributes.${attr.key}"
|
||||
label=${attr.label}
|
||||
?checked=${attrs[attr.key]}
|
||||
?required=${attr.isRequired}
|
||||
>
|
||||
</ak-switch-input>`;
|
||||
}
|
||||
};
|
||||
return html`${groupBy(defs, (def) => def.group || "").map(([group, attrs]) => {
|
||||
if (group === "") {
|
||||
return html`${attrs.map((attr) => renderSingleAttribute(attr))}`;
|
||||
}
|
||||
return html`<ak-form-group label=${group}>
|
||||
<div class="pf-c-form">${attrs.map((attr) => renderSingleAttribute(attr))}</div>
|
||||
</ak-form-group>`;
|
||||
})}
|
||||
${options?.disableRawAttributes
|
||||
? nothing
|
||||
: html`<ak-form-group label=${msg("Advanced settings")}>
|
||||
<div class="pf-c-form">
|
||||
<ak-form-element-horizontal label=${msg("Attributes")} name="attributes">
|
||||
<ak-codemirror mode="yaml" value="${YAML.stringify(attrs)}">
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Set custom attributes using YAML or JSON.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>`}`;
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,14 @@ import "#components/ak-switch-input";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
|
||||
import { ModelForm } from "#elements/forms/ModelForm";
|
||||
import { RadioOption } from "#elements/forms/Radio";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import { CoreApi, Group, RbacApi, Role, User, UserTypeEnum } from "@goauthentik/api";
|
||||
import { ObjectAttributeModelForm } from "#admin/object-attributes/renderAttributes";
|
||||
|
||||
import { CoreApi, Group, ModelEnum, RbacApi, Role, User, UserTypeEnum } from "@goauthentik/api";
|
||||
|
||||
import { match } from "ts-pattern";
|
||||
import YAML from "yaml";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { css, CSSResult, html } from "lit";
|
||||
@@ -44,8 +44,11 @@ const UserTypeOptions: readonly RadioOption<UserTypeEnum>[] = [
|
||||
description: html`${msg("Machine-to-machine authentication or other automations.")}`,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("ak-user-form")
|
||||
export class UserForm extends ModelForm<User, number> {
|
||||
export class UserForm extends ObjectAttributeModelForm<User, number> {
|
||||
public model = ModelEnum.AuthentikCoreUser;
|
||||
|
||||
#coreAPI = new CoreApi(DEFAULT_CONFIG);
|
||||
#rbacAPI = new RbacApi(DEFAULT_CONFIG);
|
||||
|
||||
@@ -242,7 +245,6 @@ export class UserForm extends ModelForm<User, number> {
|
||||
)}
|
||||
>
|
||||
</ak-switch-input>
|
||||
|
||||
<ak-text-input
|
||||
name="path"
|
||||
label=${msg("Path")}
|
||||
@@ -263,18 +265,7 @@ export class UserForm extends ModelForm<User, number> {
|
||||
</p>`}
|
||||
></ak-text-input>
|
||||
|
||||
<ak-form-element-horizontal label=${msg("Attributes")} name="attributes">
|
||||
<ak-codemirror
|
||||
mode="yaml"
|
||||
value="${YAML.stringify(
|
||||
this.instance?.attributes ?? UserForm.defaultUserAttributes,
|
||||
)}"
|
||||
>
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Set custom attributes using YAML or JSON.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
${this.renderObjectAttributes(this.objAttributes, this.instance)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ import { formatUserDisplayName } from "#common/users";
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { WithNotifications } from "#elements/mixins/notifications";
|
||||
import { WithSession } from "#elements/mixins/session";
|
||||
import { AKDrawerChangeEvent } from "#elements/notifications/events";
|
||||
import { isDefaultAvatar } from "#elements/utils/images";
|
||||
|
||||
import Styles from "#components/ak-nav-button.css";
|
||||
import { AKDrawerChangeEvent } from "#components/notifications/events";
|
||||
|
||||
import { CoreApi } from "@goauthentik/api";
|
||||
|
||||
|
||||
@@ -13,10 +13,9 @@ import {
|
||||
NotificationsMixin,
|
||||
} from "#elements/mixins/notifications";
|
||||
import { SessionMixin } from "#elements/mixins/session";
|
||||
import { createPaginatedNotificationListFrom } from "#elements/notifications/utils";
|
||||
import type { ReactiveElementHost } from "#elements/types";
|
||||
|
||||
import { createPaginatedNotificationListFrom } from "#components/notifications/utils";
|
||||
|
||||
import { EventsApi } from "@goauthentik/api";
|
||||
|
||||
import { ContextProvider } from "@lit/context";
|
||||
|
||||
@@ -20,10 +20,9 @@ import {
|
||||
SessionMixin,
|
||||
UIConfigContext,
|
||||
} from "#elements/mixins/session";
|
||||
import { AKDrawerChangeEvent } from "#elements/notifications/events";
|
||||
import type { ReactiveElementHost } from "#elements/types";
|
||||
|
||||
import { AKDrawerChangeEvent } from "#components/notifications/events";
|
||||
|
||||
import { CoreApi, SessionUser } from "@goauthentik/api";
|
||||
|
||||
import { setUser } from "@sentry/browser";
|
||||
|
||||
@@ -5,6 +5,8 @@ import { isControlElement } from "#elements/ControlElement";
|
||||
import { isFormField } from "#elements/forms/form-associated-element";
|
||||
import { isNamedElement, NamedElement } from "#elements/utils/inputs";
|
||||
|
||||
import { deepmerge } from "deepmerge-ts";
|
||||
|
||||
function isIgnored<T extends Element>(element: T) {
|
||||
if (!(element instanceof HTMLElement)) return false;
|
||||
|
||||
@@ -22,7 +24,7 @@ function assignValue(
|
||||
let parent = destination;
|
||||
|
||||
if (!element.name?.includes(".")) {
|
||||
parent[element.name] = value;
|
||||
parent[element.name] = deepmerge(parent[element.name], value);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,10 @@ import { MessageLevel } from "#common/messages";
|
||||
|
||||
import { ContextControllerRegistry } from "#elements/controllers/ContextControllerRegistry";
|
||||
import { showMessage } from "#elements/messages/MessageContainer";
|
||||
import { AKDrawerChangeEvent } from "#elements/notifications/events";
|
||||
import { createPaginatedNotificationListFrom } from "#elements/notifications/utils";
|
||||
import { createMixin } from "#elements/types";
|
||||
|
||||
import { AKDrawerChangeEvent } from "#components/notifications/events";
|
||||
import { createPaginatedNotificationListFrom } from "#components/notifications/utils";
|
||||
|
||||
import { ConsoleLogger } from "#logger/browser";
|
||||
|
||||
import {
|
||||
|
||||
@@ -5,8 +5,7 @@ import { globalAK } from "#common/global";
|
||||
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { listen } from "#elements/decorators/listen";
|
||||
|
||||
import { AKDrawerChangeEvent } from "#components/notifications/events";
|
||||
import { AKDrawerChangeEvent } from "#elements/notifications/events";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, CSSResult, html, TemplateResult } from "lit";
|
||||
@@ -81,9 +80,14 @@ export class APIDrawer extends AKElement {
|
||||
|
||||
@listen(AKRequestPostEvent, { target: window })
|
||||
protected enqueueRequest = ({ requestInfo }: AKRequestPostEvent) => {
|
||||
this.requests = [requestInfo, ...this.requests]
|
||||
.toSorted((a, b) => b.time - a.time)
|
||||
.slice(0, 50);
|
||||
this.requests.push(requestInfo);
|
||||
|
||||
this.requests.sort((a, b) => a.time - b.time).reverse();
|
||||
if (this.requests.length > 50) {
|
||||
this.requests.shift();
|
||||
}
|
||||
|
||||
this.requestUpdate();
|
||||
};
|
||||
|
||||
render(): TemplateResult {
|
||||
@@ -10,11 +10,10 @@ import { formatElapsedTime } from "#common/temporal";
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { WithNotifications } from "#elements/mixins/notifications";
|
||||
import { WithSession } from "#elements/mixins/session";
|
||||
import { AKDrawerChangeEvent } from "#elements/notifications/events";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
import { ifPresent } from "#elements/utils/attributes";
|
||||
|
||||
import { AKDrawerChangeEvent } from "#components/notifications/events";
|
||||
|
||||
import { Notification } from "@goauthentik/api";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DrawerState, readDrawerParams } from "#components/notifications/utils";
|
||||
import { DrawerState, readDrawerParams } from "#elements/notifications/utils";
|
||||
|
||||
/**
|
||||
* Event dispatched when the state of the interface drawers changes.
|
||||
@@ -2,8 +2,8 @@
|
||||
* @file Notification drawer utilities.
|
||||
*/
|
||||
|
||||
import "#components/notifications/APIDrawer";
|
||||
import "#components/notifications/NotificationDrawer";
|
||||
import "#elements/notifications/APIDrawer";
|
||||
import "#elements/notifications/NotificationDrawer";
|
||||
|
||||
import { getURLParam, updateURLParams } from "#elements/router/RouteMatch";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "#components/ak-nav-buttons";
|
||||
import "#elements/banner/EnterpriseStatusBanner";
|
||||
import "#components/notifications/APIDrawer";
|
||||
import "#components/notifications/NotificationDrawer";
|
||||
import "#elements/notifications/APIDrawer";
|
||||
import "#elements/notifications/NotificationDrawer";
|
||||
import "#elements/router/RouterOutlet";
|
||||
|
||||
import { globalAK } from "#common/global";
|
||||
@@ -13,16 +13,15 @@ import { AuthenticatedInterface } from "#elements/AuthenticatedInterface";
|
||||
import { listen } from "#elements/decorators/listen";
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { canAccessAdmin, WithSession } from "#elements/mixins/session";
|
||||
import { ifPresent } from "#elements/utils/attributes";
|
||||
import { ThemedImage } from "#elements/utils/images";
|
||||
|
||||
import { AKDrawerChangeEvent } from "#components/notifications/events";
|
||||
import { AKDrawerChangeEvent } from "#elements/notifications/events";
|
||||
import {
|
||||
DrawerState,
|
||||
persistDrawerParams,
|
||||
readDrawerParams,
|
||||
renderNotificationDrawerPanel,
|
||||
} from "#components/notifications/utils";
|
||||
} from "#elements/notifications/utils";
|
||||
import { ifPresent } from "#elements/utils/attributes";
|
||||
import { ThemedImage } from "#elements/utils/images";
|
||||
|
||||
import Styles from "#user/ak-interface-user.css";
|
||||
import { ROUTES } from "#user/Routes";
|
||||
|
||||
@@ -114,6 +114,10 @@ The worker status reporting change also uses one fewer PostgreSQL connection per
|
||||
|
||||
The Admin interface is also less resource-intensive in the browser due to lazy-loaded modals.
|
||||
|
||||
### Fewer packages, smaller attack surface
|
||||
|
||||
We’ve removed 17 packages, trimming bloat and tightening security in one move. Fewer components mean fewer potential vulnerabilities, helping keep your authentik deployments faster, lighter, and more resilient.
|
||||
|
||||
### OAuth2 configurable grant types
|
||||
|
||||
[OAuth2 providers](../../add-secure-apps/providers/oauth2/index.mdx#oauth-20-flows-and-grant-types) now have a **Grant Types** setting that lets admins explicitly choose which grant types a given provider may use. The available options are Authorization Code, Implicit, Hybrid, Refresh token, Client credentials, Password, and Device-code. Existing providers default to having all grant types enabled to preserve current behavior, but you can now disable any grant types you don't want a particular client to use — useful for tightening security on individual integrations and disabling legacy flows like Implicit or Password where they aren't needed.
|
||||
|
||||
@@ -8,11 +8,64 @@ Invitations are another way to create a user, by inviting someone to join your a
|
||||
|
||||
You can configure invitations either by:
|
||||
|
||||
- using [pre-built blueprints](#use-pre-built-blueprints-to-configure-invitations) (recommended for quick setup).
|
||||
- using the [invitation wizard](#use-the-invitation-wizard) (recommended; creates the enrollment flow and the invitation in one guided process).
|
||||
- using [pre-built blueprints](#use-pre-built-blueprints-to-configure-invitations) (good for showcasing multiple flow variations).
|
||||
- [manually creating flows and stages](#manual-setup-without-blueprints) (for custom configurations).
|
||||
|
||||
:::info
|
||||
You can also create a [policy](../../../customize/policies/) to see if the invitation was ever used.
|
||||
You can also create a [policy](../../../customize/policies/) to check whether the invitation was ever used.
|
||||
:::
|
||||
|
||||
## Use the invitation wizard
|
||||
|
||||
The invitation wizard, available from the **Directory** > **Invitations** page in the Admin interface, walks you through creating an invitation and (optionally) the enrollment flow it binds to in a single guided process.
|
||||
|
||||
### Step 1. Open the wizard
|
||||
|
||||
1. Log in to authentik as an administrator and open the authentik Admin interface.
|
||||
2. Navigate to **Directory** > **Invitations**.
|
||||
3. Click the caret (>) next to the **New Invitation** button and choose how the wizard should handle the invitation:
|
||||
- **with Existing Enrollment Flow...**: bind the new invitation to an existing enrollment flow. Only enrollment flows that have an invitation stage bound to them are listed. This is also what the **New Invitation** button does by default.
|
||||
- **with New Enrollment Flow and Invitation Stage...**: create a new minimal enrollment flow, including an invitation stage, then bind the invitation to it. Use this option when you do not yet have an enrollment flow set up, or when you want a separate enrollment flow for an invitation.
|
||||
|
||||
:::info Automatic flow selection
|
||||
If you choose **with Existing Enrollment Flow...** and only one eligible flow exists, the wizard skips the flow selection step and takes you directly to the invitation details.
|
||||
:::
|
||||
|
||||
### Step 2. Configure the enrollment flow
|
||||
|
||||
- If you picked an existing flow, select it from the **Enrollment flow** drop-down and click **Next**.
|
||||
- If you are creating a new flow, fill in:
|
||||
- **Flow name**: display name of the new enrollment flow.
|
||||
- **Flow slug**: the slug for the flow which is included in the URL.
|
||||
- **Invitation stage name**: name of the invitation stage that will be bound to the new flow.
|
||||
- **User type**: the user type for users enrolled via this flow.
|
||||
- **Continue flow without invitation**: when enabled, the flow proceeds to the next stage even when no invitation token is supplied. When disabled, the flow is cancelled if a valid invitation is not provided.
|
||||
|
||||
### Step 3. Configure the invitation details
|
||||
|
||||
- **Name**: provide a slug-style name for your invitation object (lowercase letters, numbers, and hyphens only).
|
||||
- **Expires**: select a date and time for when the invitation should expire. Defaults to 48 hours from now.
|
||||
- **Flow**: read-only; reflects the flow chosen in the previous step.
|
||||
- **Custom attributes**: (_optional_) YAML or JSON that is loaded into the flow's `prompt_data` context to pre-fill user information. Field keys must match the keys configured in the flow's [prompt stage](../../add-secure-apps/flows-stages/stages/prompt/index.md). See the [example custom attributes](#step-3-create-the-invitation-object) below for sample payloads.
|
||||
- **Single use**: when enabled, the invitation is deleted after the first successful enrollment.
|
||||
|
||||
Click **Next** to create the invitation. If you chose **with New Enrollment Flow and Invitation Stage...**, the supporting blueprint is imported at this point as well.
|
||||
|
||||
### Step 4. Share the invitation
|
||||
|
||||
After the invitation is created, the wizard's final step shows the **Link to use the invitation**. From there you can:
|
||||
|
||||
- Click **Copy Link** to copy the invitation URL to your clipboard.
|
||||
- Click **Send via Email** to open the email step inside the wizard. Enter:
|
||||
- **To**: one email per line, or comma/semicolon separated. Each recipient receives a separate email.
|
||||
- **CC** / **BCC**: (_optional_) recipients for carbon and blind carbon copies.
|
||||
- **Template**: the email template to use (the default `Invitation` template is recommended).
|
||||
|
||||
Click **Send** to queue the emails. They are sent asynchronously by the background worker. Check **System Tasks** for delivery status.
|
||||
|
||||
:::note Email configuration required
|
||||
To send invitation emails, you must have configured email in authentik. Refer to the [Email configuration](../../install-config/email.mdx) documentation for details.
|
||||
:::
|
||||
|
||||
## Use pre-built blueprints to configure invitations
|
||||
|
||||
Reference in New Issue
Block a user