*: 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:
parent
0b3d91aa27
commit
ccfd45774e
|
@ -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=[],
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
Reference in New Issue