mirror of
https://github.com/goauthentik/authentik
synced 2026-05-15 11:26:31 +02:00
Compare commits
28 Commits
website/sw
...
core/objec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b96c9cf1d6 | ||
|
|
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 |
@@ -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
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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,
|
||||
|
||||
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";
|
||||
|
||||
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
|
||||
|
||||
@@ -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>`;
|
||||
|
||||
@@ -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)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user