*: add validator to ensure JSON Fields only receive dicts
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
a0daaabfde
commit
4f27a97e10
|
@ -1,13 +1,17 @@
|
||||||
"""Groups API Viewset"""
|
"""Groups API Viewset"""
|
||||||
|
from rest_framework.fields import JSONField
|
||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
|
from authentik.core.api.utils import is_dict
|
||||||
from authentik.core.models import Group
|
from authentik.core.models import Group
|
||||||
|
|
||||||
|
|
||||||
class GroupSerializer(ModelSerializer):
|
class GroupSerializer(ModelSerializer):
|
||||||
"""Group Serializer"""
|
"""Group Serializer"""
|
||||||
|
|
||||||
|
attributes = JSONField(validators=[is_dict])
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
||||||
model = Group
|
model = Group
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.utils.http import urlencode
|
||||||
from drf_yasg.utils import swagger_auto_schema, swagger_serializer_method
|
from drf_yasg.utils import swagger_auto_schema, swagger_serializer_method
|
||||||
from guardian.utils import get_anonymous_user
|
from guardian.utils import get_anonymous_user
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.fields import CharField, SerializerMethodField
|
from rest_framework.fields import CharField, JSONField, SerializerMethodField
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import BooleanField, ModelSerializer
|
from rest_framework.serializers import BooleanField, ModelSerializer
|
||||||
|
@ -12,7 +12,7 @@ from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h
|
from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h
|
||||||
from authentik.api.decorators import permission_required
|
from authentik.api.decorators import permission_required
|
||||||
from authentik.core.api.utils import LinkSerializer, PassiveSerializer
|
from authentik.core.api.utils import LinkSerializer, PassiveSerializer, is_dict
|
||||||
from authentik.core.middleware import (
|
from authentik.core.middleware import (
|
||||||
SESSION_IMPERSONATE_ORIGINAL_USER,
|
SESSION_IMPERSONATE_ORIGINAL_USER,
|
||||||
SESSION_IMPERSONATE_USER,
|
SESSION_IMPERSONATE_USER,
|
||||||
|
@ -26,6 +26,7 @@ class UserSerializer(ModelSerializer):
|
||||||
|
|
||||||
is_superuser = BooleanField(read_only=True)
|
is_superuser = BooleanField(read_only=True)
|
||||||
avatar = CharField(read_only=True)
|
avatar = CharField(read_only=True)
|
||||||
|
attributes = JSONField(validators=[is_dict])
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,20 @@
|
||||||
"""API Utilities"""
|
"""API Utilities"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
from rest_framework.fields import CharField, IntegerField
|
from rest_framework.fields import CharField, IntegerField
|
||||||
from rest_framework.serializers import Serializer, SerializerMethodField
|
from rest_framework.serializers import (
|
||||||
|
Serializer,
|
||||||
|
SerializerMethodField,
|
||||||
|
ValidationError,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def is_dict(value: Any):
|
||||||
|
"""Ensure a value is a dictionary, useful for JSONFields"""
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return
|
||||||
|
raise ValidationError("Value must be a dictionary.")
|
||||||
|
|
||||||
|
|
||||||
class PassiveSerializer(Serializer):
|
class PassiveSerializer(Serializer):
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
"""Test API Utils"""
|
||||||
|
from rest_framework.exceptions import ValidationError
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
|
from authentik.core.api.utils import is_dict
|
||||||
|
|
||||||
|
|
||||||
|
class TestAPIUtils(APITestCase):
|
||||||
|
"""Test API Utils"""
|
||||||
|
|
||||||
|
def test_is_dict(self):
|
||||||
|
"""Test is_dict"""
|
||||||
|
self.assertIsNone(is_dict({}))
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
is_dict("foo")
|
|
@ -8,14 +8,14 @@ from rest_framework.serializers import JSONField, ModelSerializer
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik.core.api.providers import ProviderSerializer
|
from authentik.core.api.providers import ProviderSerializer
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import PassiveSerializer, is_dict
|
||||||
from authentik.outposts.models import Outpost, default_outpost_config
|
from authentik.outposts.models import Outpost, default_outpost_config
|
||||||
|
|
||||||
|
|
||||||
class OutpostSerializer(ModelSerializer):
|
class OutpostSerializer(ModelSerializer):
|
||||||
"""Outpost Serializer"""
|
"""Outpost Serializer"""
|
||||||
|
|
||||||
_config = JSONField()
|
_config = JSONField(validators=[is_dict])
|
||||||
providers_obj = ProviderSerializer(source="providers", many=True, read_only=True)
|
providers_obj = ProviderSerializer(source="providers", many=True, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from rest_framework.fields import BooleanField, CharField, JSONField, ListField
|
from rest_framework.fields import BooleanField, CharField, JSONField, ListField
|
||||||
from rest_framework.relations import PrimaryKeyRelatedField
|
from rest_framework.relations import PrimaryKeyRelatedField
|
||||||
|
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import PassiveSerializer, is_dict
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ class PolicyTestSerializer(PassiveSerializer):
|
||||||
"""Test policy execution for a user with context"""
|
"""Test policy execution for a user with context"""
|
||||||
|
|
||||||
user = PrimaryKeyRelatedField(queryset=User.objects.all())
|
user = PrimaryKeyRelatedField(queryset=User.objects.all())
|
||||||
context = JSONField(required=False)
|
context = JSONField(required=False, validators=[is_dict])
|
||||||
|
|
||||||
|
|
||||||
class PolicyTestResultSerializer(PassiveSerializer):
|
class PolicyTestResultSerializer(PassiveSerializer):
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
"""Invitation Stage API Views"""
|
"""Invitation Stage API Views"""
|
||||||
|
from rest_framework.fields import JSONField
|
||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
|
from authentik.core.api.utils import is_dict
|
||||||
from authentik.flows.api.stages import StageSerializer
|
from authentik.flows.api.stages import StageSerializer
|
||||||
from authentik.stages.invitation.models import Invitation, InvitationStage
|
from authentik.stages.invitation.models import Invitation, InvitationStage
|
||||||
|
|
||||||
|
@ -27,6 +29,8 @@ class InvitationStageViewSet(ModelViewSet):
|
||||||
class InvitationSerializer(ModelSerializer):
|
class InvitationSerializer(ModelSerializer):
|
||||||
"""Invitation Serializer"""
|
"""Invitation Serializer"""
|
||||||
|
|
||||||
|
fixed_data = JSONField(validators=[is_dict])
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
||||||
model = Invitation
|
model = Invitation
|
||||||
|
|
|
@ -142,7 +142,9 @@ class TestInvitationsAPI(APITestCase):
|
||||||
def test_invite_create(self):
|
def test_invite_create(self):
|
||||||
"""Test Invitations creation endpoint"""
|
"""Test Invitations creation endpoint"""
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("authentik_api:invitation-list"), {"identifier": "test-token"}
|
reverse("authentik_api:invitation-list"),
|
||||||
|
{"identifier": "test-token", "fixed_data": {}},
|
||||||
|
format="json"
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 201)
|
self.assertEqual(response.status_code, 201)
|
||||||
self.assertEqual(Invitation.objects.first().created_by, self.user)
|
self.assertEqual(Invitation.objects.first().created_by, self.user)
|
||||||
|
|
Reference in New Issue