*: fix api errors raised in general validate() to specify a field (#6663)

* *: fix api errors raised in general validate() to specify a field

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove required flag for tls server name for ldap provider

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* attempt to make timing test less flaky

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L 2023-08-29 14:41:48 +02:00 committed by GitHub
parent 0b3d91aa27
commit ccfd45774e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 22 additions and 13 deletions

View File

@ -9,6 +9,7 @@ from drf_spectacular.plumbing import (
) )
from drf_spectacular.settings import spectacular_settings from drf_spectacular.settings import spectacular_settings
from drf_spectacular.types import OpenApiTypes from drf_spectacular.types import OpenApiTypes
from rest_framework.settings import api_settings
from authentik.api.pagination import PAGINATION_COMPONENT_NAME, PAGINATION_SCHEMA from authentik.api.pagination import PAGINATION_COMPONENT_NAME, PAGINATION_SCHEMA
@ -31,7 +32,7 @@ GENERIC_ERROR = build_object_type(
VALIDATION_ERROR = build_object_type( VALIDATION_ERROR = build_object_type(
description=_("Validation Error"), description=_("Validation Error"),
properties={ properties={
"non_field_errors": build_array_type(build_standard_type(OpenApiTypes.STR)), api_settings.NON_FIELD_ERRORS_KEY: build_array_type(build_standard_type(OpenApiTypes.STR)),
"code": build_standard_type(OpenApiTypes.STR), "code": build_standard_type(OpenApiTypes.STR),
}, },
required=[], required=[],

View File

@ -31,7 +31,7 @@ class ApplyBlueprintMetaSerializer(PassiveSerializer):
required = attrs["required"] required = attrs["required"]
instance = BlueprintInstance.objects.filter(**identifiers).first() instance = BlueprintInstance.objects.filter(**identifiers).first()
if not instance and required: if not instance and required:
raise ValidationError("Required blueprint does not exist") raise ValidationError({"identifiers": "Required blueprint does not exist"})
self.blueprint_instance = instance self.blueprint_instance = instance
return super().validate(attrs) return super().validate(attrs)

View File

@ -47,7 +47,7 @@ class TokenSerializer(ManagedSerializer, ModelSerializer):
attrs.setdefault("user", request.user) attrs.setdefault("user", request.user)
attrs.setdefault("intent", TokenIntents.INTENT_API) attrs.setdefault("intent", TokenIntents.INTENT_API)
if attrs.get("intent") not in [TokenIntents.INTENT_API, TokenIntents.INTENT_APP_PASSWORD]: if attrs.get("intent") not in [TokenIntents.INTENT_API, TokenIntents.INTENT_APP_PASSWORD]:
raise ValidationError(f"Invalid intent {attrs.get('intent')}") raise ValidationError({"intent": f"Invalid intent {attrs.get('intent')}"})
return attrs return attrs
class Meta: class Meta:

View File

@ -39,7 +39,7 @@ class NotificationTransportSerializer(ModelSerializer):
mode = attrs.get("mode") mode = attrs.get("mode")
if mode in [TransportMode.WEBHOOK, TransportMode.WEBHOOK_SLACK]: if mode in [TransportMode.WEBHOOK, TransportMode.WEBHOOK_SLACK]:
if "webhook_url" not in attrs or attrs.get("webhook_url", "") == "": if "webhook_url" not in attrs or attrs.get("webhook_url", "") == "":
raise ValidationError("Webhook URL may not be empty.") raise ValidationError({"webhook_url": "Webhook URL may not be empty."})
return attrs return attrs
class Meta: class Meta:

View File

@ -59,7 +59,9 @@ class ProxyProviderSerializer(ProviderSerializer):
attrs.get("mode", ProxyMode.PROXY) == ProxyMode.PROXY attrs.get("mode", ProxyMode.PROXY) == ProxyMode.PROXY
and attrs.get("internal_host", "") == "" and attrs.get("internal_host", "") == ""
): ):
raise ValidationError(_("Internal host cannot be empty when forward auth is disabled.")) raise ValidationError(
{"internal_host": _("Internal host cannot be empty when forward auth is disabled.")}
)
return attrs return attrs
def create(self, validated_data: dict): def create(self, validated_data: dict):

View File

@ -69,7 +69,7 @@ class ProxyProviderTests(APITestCase):
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
self.assertJSONEqual( self.assertJSONEqual(
response.content.decode(), response.content.decode(),
{"non_field_errors": ["Internal host cannot be empty when forward auth is disabled."]}, {"internal_host": ["Internal host cannot be empty when forward auth is disabled."]},
) )
def test_create_defaults(self): def test_create_defaults(self):

View File

@ -44,8 +44,12 @@ class LDAPSourceSerializer(SourceSerializer):
sources = sources.exclude(pk=self.instance.pk) sources = sources.exclude(pk=self.instance.pk)
if sources.exists(): if sources.exists():
raise ValidationError( raise ValidationError(
{
"sync_users_password": (
"Only a single LDAP Source with password synchronization is allowed" "Only a single LDAP Source with password synchronization is allowed"
) )
}
)
return super().validate(attrs) return super().validate(attrs)
class Meta: class Meta:

View File

@ -63,7 +63,7 @@ class OAuthSourceSerializer(SourceSerializer):
well_known_config.raise_for_status() well_known_config.raise_for_status()
except RequestException as exc: except RequestException as exc:
text = exc.response.text if exc.response else str(exc) text = exc.response.text if exc.response else str(exc)
raise ValidationError(text) raise ValidationError({"oidc_well_known_url": text})
config = well_known_config.json() config = well_known_config.json()
try: try:
attrs["authorization_url"] = config["authorization_endpoint"] attrs["authorization_url"] = config["authorization_endpoint"]
@ -71,7 +71,9 @@ class OAuthSourceSerializer(SourceSerializer):
attrs["profile_url"] = config["userinfo_endpoint"] attrs["profile_url"] = config["userinfo_endpoint"]
attrs["oidc_jwks_url"] = config["jwks_uri"] attrs["oidc_jwks_url"] = config["jwks_uri"]
except (IndexError, KeyError) as exc: except (IndexError, KeyError) as exc:
raise ValidationError(f"Invalid well-known configuration: {exc}") raise ValidationError(
{"oidc_well_known_url": f"Invalid well-known configuration: {exc}"}
)
jwks_url = attrs.get("oidc_jwks_url") jwks_url = attrs.get("oidc_jwks_url")
if jwks_url and jwks_url != "": if jwks_url and jwks_url != "":
@ -80,7 +82,7 @@ class OAuthSourceSerializer(SourceSerializer):
jwks_config.raise_for_status() jwks_config.raise_for_status()
except RequestException as exc: except RequestException as exc:
text = exc.response.text if exc.response else str(exc) text = exc.response.text if exc.response else str(exc)
raise ValidationError(text) raise ValidationError({"jwks_url": text})
config = jwks_config.json() config = jwks_config.json()
attrs["oidc_jwks"] = config attrs["oidc_jwks"] = config

View File

@ -99,6 +99,7 @@ class TestUserLoginStage(FlowTestCase):
session[SESSION_KEY_PLAN] = plan session[SESSION_KEY_PLAN] = plan
session.save() session.save()
before_request = now()
response = self.client.get( response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
) )
@ -108,7 +109,7 @@ class TestUserLoginStage(FlowTestCase):
session_key = self.client.session.session_key session_key = self.client.session.session_key
session = AuthenticatedSession.objects.filter(session_key=session_key).first() session = AuthenticatedSession.objects.filter(session_key=session_key).first()
self.assertAlmostEqual( self.assertAlmostEqual(
session.expires.timestamp() - now().timestamp(), session.expires.timestamp() - before_request.timestamp(),
timedelta_from_string(self.stage.session_duration).total_seconds(), timedelta_from_string(self.stage.session_duration).total_seconds(),
delta=1, delta=1,
) )

View File

@ -36,7 +36,7 @@ class TenantSerializer(ModelSerializer):
if self.instance: if self.instance:
tenants = tenants.exclude(pk=self.instance.pk) tenants = tenants.exclude(pk=self.instance.pk)
if tenants.exists(): if tenants.exists():
raise ValidationError("Only a single Tenant can be set as default.") raise ValidationError({"default": "Only a single Tenant can be set as default."})
return super().validate(attrs) return super().validate(attrs)
class Meta: class Meta:

View File

@ -217,7 +217,6 @@ export class LDAPProviderFormPage extends ModelForm<LDAPProvider, number> {
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${msg("TLS Server name")} label=${msg("TLS Server name")}
?required=${true}
name="tlsServerName" name="tlsServerName"
> >
<input <input