core: better API validation for JSON fields (#15236)
* core: better API validation for JSON fields Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix web Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -2,6 +2,7 @@
|
||||
|
||||
from typing import Any
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import Model
|
||||
from drf_spectacular.extensions import OpenApiSerializerFieldExtension
|
||||
from drf_spectacular.plumbing import build_basic_type
|
||||
@ -30,7 +31,27 @@ def is_dict(value: Any):
|
||||
raise ValidationError("Value must be a dictionary, and not have any duplicate keys.")
|
||||
|
||||
|
||||
class JSONDictField(JSONField):
|
||||
"""JSON Field which only allows dictionaries"""
|
||||
|
||||
default_validators = [is_dict]
|
||||
|
||||
|
||||
class JSONExtension(OpenApiSerializerFieldExtension):
|
||||
"""Generate API Schema for JSON fields as"""
|
||||
|
||||
target_class = "authentik.core.api.utils.JSONDictField"
|
||||
|
||||
def map_serializer_field(self, auto_schema, direction):
|
||||
return build_basic_type(OpenApiTypes.OBJECT)
|
||||
|
||||
|
||||
class ModelSerializer(BaseModelSerializer):
|
||||
|
||||
# By default, JSON fields we have are used to store dictionaries
|
||||
serializer_field_mapping = BaseModelSerializer.serializer_field_mapping.copy()
|
||||
serializer_field_mapping[models.JSONField] = JSONDictField
|
||||
|
||||
def create(self, validated_data):
|
||||
instance = super().create(validated_data)
|
||||
|
||||
@ -71,21 +92,6 @@ class ModelSerializer(BaseModelSerializer):
|
||||
return instance
|
||||
|
||||
|
||||
class JSONDictField(JSONField):
|
||||
"""JSON Field which only allows dictionaries"""
|
||||
|
||||
default_validators = [is_dict]
|
||||
|
||||
|
||||
class JSONExtension(OpenApiSerializerFieldExtension):
|
||||
"""Generate API Schema for JSON fields as"""
|
||||
|
||||
target_class = "authentik.core.api.utils.JSONDictField"
|
||||
|
||||
def map_serializer_field(self, auto_schema, direction):
|
||||
return build_basic_type(OpenApiTypes.OBJECT)
|
||||
|
||||
|
||||
class PassiveSerializer(Serializer):
|
||||
"""Base serializer class which doesn't implement create/update methods"""
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Serializer for tenants models"""
|
||||
|
||||
from django_tenants.utils import get_public_schema_name
|
||||
from rest_framework.fields import JSONField
|
||||
from rest_framework.generics import RetrieveUpdateAPIView
|
||||
from rest_framework.permissions import SAFE_METHODS
|
||||
|
||||
@ -12,6 +13,8 @@ from authentik.tenants.models import Tenant
|
||||
class SettingsSerializer(ModelSerializer):
|
||||
"""Settings Serializer"""
|
||||
|
||||
footer_links = JSONField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = Tenant
|
||||
fields = [
|
||||
|
196
schema.yml
196
schema.yml
@ -41338,7 +41338,9 @@ components:
|
||||
app:
|
||||
type: string
|
||||
format: uuid
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- app
|
||||
- name
|
||||
@ -41353,7 +41355,9 @@ components:
|
||||
app:
|
||||
type: string
|
||||
format: uuid
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- app
|
||||
- name
|
||||
@ -41942,7 +41946,9 @@ components:
|
||||
friendly_name:
|
||||
type: string
|
||||
nullable: true
|
||||
credentials: {}
|
||||
credentials:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- component
|
||||
- credentials
|
||||
@ -41972,7 +41978,9 @@ components:
|
||||
type: string
|
||||
nullable: true
|
||||
minLength: 1
|
||||
credentials: {}
|
||||
credentials:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- credentials
|
||||
- name
|
||||
@ -42777,7 +42785,9 @@ components:
|
||||
path:
|
||||
type: string
|
||||
default: ''
|
||||
context: {}
|
||||
context:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
last_applied:
|
||||
type: string
|
||||
format: date-time
|
||||
@ -42797,6 +42807,8 @@ components:
|
||||
type: string
|
||||
readOnly: true
|
||||
metadata:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
readOnly: true
|
||||
content:
|
||||
type: string
|
||||
@ -42818,7 +42830,9 @@ components:
|
||||
path:
|
||||
type: string
|
||||
default: ''
|
||||
context: {}
|
||||
context:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
enabled:
|
||||
type: boolean
|
||||
content:
|
||||
@ -42898,7 +42912,9 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Certificates used for client authentication.
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- brand_uuid
|
||||
- domain
|
||||
@ -42968,7 +42984,9 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Certificates used for client authentication.
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- domain
|
||||
Cache:
|
||||
@ -44609,7 +44627,9 @@ components:
|
||||
$ref: '#/components/schemas/ProtocolEnum'
|
||||
host:
|
||||
type: string
|
||||
settings: {}
|
||||
settings:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
property_mappings:
|
||||
type: array
|
||||
items:
|
||||
@ -44680,7 +44700,9 @@ components:
|
||||
host:
|
||||
type: string
|
||||
minLength: 1
|
||||
settings: {}
|
||||
settings:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
property_mappings:
|
||||
type: array
|
||||
items:
|
||||
@ -44744,12 +44766,16 @@ components:
|
||||
format: uuid
|
||||
readOnly: true
|
||||
title: Event uuid
|
||||
user: {}
|
||||
user:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
action:
|
||||
$ref: '#/components/schemas/EventActions'
|
||||
app:
|
||||
type: string
|
||||
context: {}
|
||||
context:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
client_ip:
|
||||
type: string
|
||||
nullable: true
|
||||
@ -44760,7 +44786,9 @@ components:
|
||||
expires:
|
||||
type: string
|
||||
format: date-time
|
||||
brand: {}
|
||||
brand:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- action
|
||||
- app
|
||||
@ -44905,13 +44933,17 @@ components:
|
||||
type: object
|
||||
description: Event Serializer
|
||||
properties:
|
||||
user: {}
|
||||
user:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
action:
|
||||
$ref: '#/components/schemas/EventActions'
|
||||
app:
|
||||
type: string
|
||||
minLength: 1
|
||||
context: {}
|
||||
context:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
client_ip:
|
||||
type: string
|
||||
nullable: true
|
||||
@ -44919,7 +44951,9 @@ components:
|
||||
expires:
|
||||
type: string
|
||||
format: date-time
|
||||
brand: {}
|
||||
brand:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- action
|
||||
- app
|
||||
@ -45894,7 +45928,9 @@ components:
|
||||
type: string
|
||||
format: email
|
||||
maxLength: 254
|
||||
credentials: {}
|
||||
credentials:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
scopes:
|
||||
type: string
|
||||
exclude_users_service_account:
|
||||
@ -45945,6 +45981,8 @@ components:
|
||||
provider:
|
||||
type: integer
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
readOnly: true
|
||||
required:
|
||||
- attributes
|
||||
@ -46059,7 +46097,9 @@ components:
|
||||
format: email
|
||||
minLength: 1
|
||||
maxLength: 254
|
||||
credentials: {}
|
||||
credentials:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
scopes:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -46104,6 +46144,8 @@ components:
|
||||
provider:
|
||||
type: integer
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
readOnly: true
|
||||
required:
|
||||
- attributes
|
||||
@ -47432,6 +47474,8 @@ components:
|
||||
description: Return internal model name
|
||||
readOnly: true
|
||||
kubeconfig:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
description: Paste your kubeconfig here. authentik will automatically use
|
||||
the currently selected context.
|
||||
verify_ssl:
|
||||
@ -47456,6 +47500,8 @@ components:
|
||||
description: If enabled, use the local connection. Required Docker socket/Kubernetes
|
||||
Integration
|
||||
kubeconfig:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
description: Paste your kubeconfig here. authentik will automatically use
|
||||
the currently selected context.
|
||||
verify_ssl:
|
||||
@ -48392,6 +48438,8 @@ components:
|
||||
provider:
|
||||
type: integer
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
readOnly: true
|
||||
required:
|
||||
- attributes
|
||||
@ -48548,6 +48596,8 @@ components:
|
||||
provider:
|
||||
type: integer
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
readOnly: true
|
||||
required:
|
||||
- attributes
|
||||
@ -49460,7 +49510,9 @@ components:
|
||||
type: string
|
||||
oidc_jwks_url:
|
||||
type: string
|
||||
oidc_jwks: {}
|
||||
oidc_jwks:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
authorization_code_auth_method:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AuthorizationCodeAuthMethodEnum'
|
||||
@ -49634,7 +49686,9 @@ components:
|
||||
type: string
|
||||
oidc_jwks_url:
|
||||
type: string
|
||||
oidc_jwks: {}
|
||||
oidc_jwks:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
authorization_code_auth_method:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AuthorizationCodeAuthMethodEnum'
|
||||
@ -52319,7 +52373,9 @@ components:
|
||||
app:
|
||||
type: string
|
||||
format: uuid
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
PatchedApplicationRequest:
|
||||
type: object
|
||||
description: Application Serializer
|
||||
@ -52471,7 +52527,9 @@ components:
|
||||
type: string
|
||||
nullable: true
|
||||
minLength: 1
|
||||
credentials: {}
|
||||
credentials:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
PatchedAuthenticatorSMSStageRequest:
|
||||
type: object
|
||||
description: AuthenticatorSMSStage Serializer
|
||||
@ -52658,7 +52716,9 @@ components:
|
||||
path:
|
||||
type: string
|
||||
default: ''
|
||||
context: {}
|
||||
context:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
enabled:
|
||||
type: boolean
|
||||
content:
|
||||
@ -52729,7 +52789,9 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Certificates used for client authentication.
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
PatchedCaptchaStageRequest:
|
||||
type: object
|
||||
description: CaptchaStage Serializer
|
||||
@ -53005,7 +53067,9 @@ components:
|
||||
host:
|
||||
type: string
|
||||
minLength: 1
|
||||
settings: {}
|
||||
settings:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
property_mappings:
|
||||
type: array
|
||||
items:
|
||||
@ -53057,13 +53121,17 @@ components:
|
||||
type: object
|
||||
description: Event Serializer
|
||||
properties:
|
||||
user: {}
|
||||
user:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
action:
|
||||
$ref: '#/components/schemas/EventActions'
|
||||
app:
|
||||
type: string
|
||||
minLength: 1
|
||||
context: {}
|
||||
context:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
client_ip:
|
||||
type: string
|
||||
nullable: true
|
||||
@ -53071,7 +53139,9 @@ components:
|
||||
expires:
|
||||
type: string
|
||||
format: date-time
|
||||
brand: {}
|
||||
brand:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
PatchedExpressionPolicyRequest:
|
||||
type: object
|
||||
description: Group Membership Policy Serializer
|
||||
@ -53254,7 +53324,9 @@ components:
|
||||
format: email
|
||||
minLength: 1
|
||||
maxLength: 254
|
||||
credentials: {}
|
||||
credentials:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
scopes:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -53638,6 +53710,8 @@ components:
|
||||
description: If enabled, use the local connection. Required Docker socket/Kubernetes
|
||||
Integration
|
||||
kubeconfig:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
description: Paste your kubeconfig here. authentik will automatically use
|
||||
the currently selected context.
|
||||
verify_ssl:
|
||||
@ -54221,7 +54295,9 @@ components:
|
||||
type: string
|
||||
oidc_jwks_url:
|
||||
type: string
|
||||
oidc_jwks: {}
|
||||
oidc_jwks:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
authorization_code_auth_method:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AuthorizationCodeAuthMethodEnum'
|
||||
@ -54700,7 +54776,9 @@ components:
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
settings: {}
|
||||
settings:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
connection_expiry:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -55157,7 +55235,9 @@ components:
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
PatchedSCIMSourcePropertyMappingRequest:
|
||||
type: object
|
||||
description: SCIMSourcePropertyMapping Serializer
|
||||
@ -55218,7 +55298,9 @@ components:
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
PatchedSMSDeviceRequest:
|
||||
type: object
|
||||
description: Serializer for sms authenticator devices
|
||||
@ -55305,9 +55387,7 @@ components:
|
||||
minimum: 0
|
||||
description: Reputation cannot increase higher than this value. Zero or
|
||||
positive.
|
||||
footer_links:
|
||||
description: The option configures the footer links on the flow executor
|
||||
pages.
|
||||
footer_links: {}
|
||||
gdpr_compliance:
|
||||
type: boolean
|
||||
description: When enabled, all the events caused by a user will be deleted
|
||||
@ -57119,7 +57199,9 @@ components:
|
||||
type: string
|
||||
description: Return internal model name
|
||||
readOnly: true
|
||||
settings: {}
|
||||
settings:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
outpost_set:
|
||||
type: array
|
||||
items:
|
||||
@ -57167,7 +57249,9 @@ components:
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
settings: {}
|
||||
settings:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
connection_expiry:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -57577,8 +57661,12 @@ components:
|
||||
type: string
|
||||
ip:
|
||||
type: string
|
||||
ip_geo_data: {}
|
||||
ip_asn_data: {}
|
||||
ip_geo_data:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
ip_asn_data:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
score:
|
||||
type: integer
|
||||
maximum: 9223372036854775807
|
||||
@ -58651,6 +58739,8 @@ components:
|
||||
provider:
|
||||
type: integer
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
readOnly: true
|
||||
required:
|
||||
- attributes
|
||||
@ -58741,6 +58831,8 @@ components:
|
||||
provider:
|
||||
type: integer
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
readOnly: true
|
||||
required:
|
||||
- attributes
|
||||
@ -58855,7 +58947,9 @@ components:
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- group
|
||||
- group_obj
|
||||
@ -58874,7 +58968,9 @@ components:
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- group
|
||||
- id
|
||||
@ -58993,7 +59089,9 @@ components:
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- id
|
||||
- source
|
||||
@ -59011,7 +59109,9 @@ components:
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
attributes: {}
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- id
|
||||
- source
|
||||
@ -59404,9 +59504,7 @@ components:
|
||||
minimum: 0
|
||||
description: Reputation cannot increase higher than this value. Zero or
|
||||
positive.
|
||||
footer_links:
|
||||
description: The option configures the footer links on the flow executor
|
||||
pages.
|
||||
footer_links: {}
|
||||
gdpr_compliance:
|
||||
type: boolean
|
||||
description: When enabled, all the events caused by a user will be deleted
|
||||
@ -59458,9 +59556,7 @@ components:
|
||||
minimum: 0
|
||||
description: Reputation cannot increase higher than this value. Zero or
|
||||
positive.
|
||||
footer_links:
|
||||
description: The option configures the footer links on the flow executor
|
||||
pages.
|
||||
footer_links: {}
|
||||
gdpr_compliance:
|
||||
type: boolean
|
||||
description: When enabled, all the events caused by a user will be deleted
|
||||
|
@ -92,7 +92,7 @@ export class EventMap extends AKElement {
|
||||
// Re-add them
|
||||
this.events?.results
|
||||
.filter((event) => {
|
||||
if (!Object.hasOwn(event.context, "geo")) {
|
||||
if (!Object.hasOwn(event.context || {}, "geo")) {
|
||||
return false;
|
||||
}
|
||||
const geo = (event as EventWithContext).context.geo;
|
||||
|
Reference in New Issue
Block a user