mirror of
https://github.com/element-hq/synapse.git
synced 2026-11-04 06:57:20 +00:00
Validate local events once, against precompiled schemas
`jsonschema.validate` checks the schema against the metaschema on every call. For power levels that cost more than validating the event: about 9 ms per `/createRoom`. Check the power levels and mentions schemas once at import and validate with bound validator instances, reporting the same best-match error. `create_event` also ran `validate_new` a second time on the event that `create_new_client_event` had just validated. Drop the repeat. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVLsNh6akt4ifEizY4D1ta
This commit is contained in:
1 parent
4dd87ff58a
commit
2a407a8953
4 files changed
+39
-19
No files matched your search
+36
-16
@@ -106,25 +106,14 @@ class EventValidator:
|
||||
)
|
||||
|
||||
elif event.type == EventTypes.PowerLevels:
|
||||
try:
|
||||
jsonschema.validate(
|
||||
instance=event.content,
|
||||
schema=POWER_LEVELS_SCHEMA,
|
||||
cls=POWER_LEVELS_VALIDATOR,
|
||||
)
|
||||
except jsonschema.ValidationError as e:
|
||||
raise _validation_error_to_api_error(e)
|
||||
_validate_against_schema(_POWER_LEVELS_SCHEMA_VALIDATOR, event.content)
|
||||
|
||||
# If the event contains a mentions key, validate it.
|
||||
if EventContentFields.MENTIONS in event.content:
|
||||
try:
|
||||
jsonschema.validate(
|
||||
instance=event.content[EventContentFields.MENTIONS],
|
||||
schema=MENTIONS_SCHEMA,
|
||||
cls=MENTIONS_VALIDATOR,
|
||||
)
|
||||
except jsonschema.ValidationError as e:
|
||||
raise _validation_error_to_api_error(e)
|
||||
_validate_against_schema(
|
||||
_MENTIONS_SCHEMA_VALIDATOR,
|
||||
event.content[EventContentFields.MENTIONS],
|
||||
)
|
||||
|
||||
def _validate_retention(self, event: EventBase) -> None:
|
||||
"""Checks that an event that defines the retention policy for a room respects the
|
||||
@@ -329,3 +318,34 @@ def _validation_error_to_api_error(err: jsonschema.ValidationError) -> SynapseEr
|
||||
POWER_LEVELS_VALIDATOR = _create_validator(POWER_LEVELS_SCHEMA)
|
||||
|
||||
MENTIONS_VALIDATOR = _create_validator(MENTIONS_SCHEMA)
|
||||
|
||||
|
||||
def _compile_validator(
|
||||
cls: type[jsonschema.Draft7Validator], schema: JsonDict
|
||||
) -> jsonschema.Draft7Validator:
|
||||
"""Check `schema` once and return a validator instance bound to it.
|
||||
|
||||
`jsonschema.validate` checks the schema against the metaschema on every
|
||||
call, which costs more than validating a power levels event.
|
||||
"""
|
||||
cls.check_schema(schema)
|
||||
return cls(schema)
|
||||
|
||||
|
||||
_POWER_LEVELS_SCHEMA_VALIDATOR = _compile_validator(
|
||||
POWER_LEVELS_VALIDATOR, POWER_LEVELS_SCHEMA
|
||||
)
|
||||
|
||||
_MENTIONS_SCHEMA_VALIDATOR = _compile_validator(MENTIONS_VALIDATOR, MENTIONS_SCHEMA)
|
||||
|
||||
|
||||
def _validate_against_schema(
|
||||
validator: jsonschema.Draft7Validator, instance: object
|
||||
) -> None:
|
||||
"""Validate `instance`, raising a 400 `SynapseError` if it does not match.
|
||||
|
||||
Reports the same error as `jsonschema.validate` would.
|
||||
"""
|
||||
error = jsonschema.exceptions.best_match(validator.iter_errors(instance))
|
||||
if error is not None:
|
||||
raise _validation_error_to_api_error(error)
|
||||
@@ -800,7 +800,7 @@ class EventCreationHandler:
|
||||
403, "You must be in the room to create an alias for it"
|
||||
)
|
||||
|
||||
self.validator.validate_new(event, self.config)
|
||||
# `create_new_client_event` has already run `validate_new` on the event.
|
||||
return event, unpersisted_context
|
||||
|
||||
async def _is_exempt_from_privacy_policy(
|
||||
|
||||
@@ -126,7 +126,7 @@ class TestBulkPushRuleEvaluator(HomeserverTestCase):
|
||||
# some of this validation was missing in the past.
|
||||
with (
|
||||
patch("synapse.events.validator.validate_canonicaljson"),
|
||||
patch("synapse.events.validator.jsonschema.validate"),
|
||||
patch("synapse.events.validator._validate_against_schema"),
|
||||
patch("synapse.handlers.event_auth.check_state_dependent_auth_rules"),
|
||||
):
|
||||
pl_event_id = self.helper.send_state(
|
||||
|
||||
@@ -229,7 +229,7 @@ class UpgradeRoomTest(unittest.HomeserverTestCase):
|
||||
|
||||
# Synapse refuses to accept new stringy power level events. Bypass this by
|
||||
# neutering the validation.
|
||||
with patch("synapse.events.validator.jsonschema.validate"):
|
||||
with patch("synapse.events.validator._validate_against_schema"):
|
||||
# Note: https://github.com/matrix-org/matrix-spec/issues/853 plans to forbid
|
||||
# string power levels in new rooms. For this test to have a clean
|
||||
# conscience, we ought to ensure it's upgrading from a sufficiently old
|
||||
|
||||
Reference in new issue
Block a user