Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion synapse/util/pydantic_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
#
#

from synapse._pydantic_compat import BaseModel, Extra
import re
from typing import Any, Callable, Generator

from synapse._pydantic_compat import BaseModel, Extra, StrictStr
from synapse.types import EventID


class ParseModel(BaseModel):
Expand All @@ -37,3 +41,43 @@ class Config:
extra = Extra.ignore
# By default, don't allow fields to be reassigned after parsing.
allow_mutation = False


class AnyEventId(StrictStr):
"""
A validator for strings that need to be an Event ID.

Accepts any valid grammar of Event ID from any room version.
"""

EVENT_ID_HASH_ROOM_VERSION_3_PLUS = re.compile(
r"^([a-zA-Z0-9-_]{43}|[a-zA-Z0-9+/]{43})$"
)

@classmethod
def __get_validators__(cls) -> Generator[Callable[..., Any], Any, Any]:
yield from super().__get_validators__() # type: ignore
yield cls.validate_event_id

@classmethod
def validate_event_id(cls, value: str) -> str:
if not value.startswith("$"):
raise ValueError("Event ID must start with `$`")

if ":" in value:
# Room versions 1 and 2
EventID.from_string(value) # throws on fail
else:
# Room versions 3+: event ID is $ + a base64 sha256 hash
# Room version 3 is base64, 4+ are base64Url
# In both cases, the base64 is unpadded.
# refs:
# - https://spec.matrix.org/v1.15/rooms/v3/
# - https://spec.matrix.org/v1.15/rooms/v4/
b64_hash = value[1:]
if cls.EVENT_ID_HASH_ROOM_VERSION_3_PLUS.fullmatch(b64_hash) is None:
raise ValueError(
"Event ID must either have a domain part or be a valid hash"
)

return value