Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Add type hints to groups code #9393

Merged
merged 11 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion changelog.d/9321.bugfix
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Assert a maximum length for the `client_secret` parameter for spec compliance.
Assert a maximum length for some parameters for spec compliance.
1 change: 1 addition & 0 deletions changelog.d/9393.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Assert a maximum length for some parameters for spec compliance.
1 change: 1 addition & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ files =
synapse/events/validator.py,
synapse/events/spamcheck.py,
synapse/federation,
synapse/groups,
synapse/handlers,
synapse/http/client.py,
synapse/http/federation/matrix_federation_agent.py,
Expand Down
5 changes: 5 additions & 0 deletions synapse/api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
# the maximum length for a user id is 255 characters
MAX_USERID_LENGTH = 255

# The maximum length for a group id is 255 characters
MAX_GROUPID_LENGTH = 255
MAX_GROUP_CATEGORYID_LENGTH = 255
MAX_GROUP_ROLEID_LENGTH = 255


class Membership:

Expand Down
41 changes: 39 additions & 2 deletions synapse/federation/transport/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from typing import Optional, Tuple, Type

import synapse
from synapse.api.constants import MAX_GROUP_CATEGORYID_LENGTH, MAX_GROUP_ROLEID_LENGTH
from synapse.api.errors import Codes, FederationDeniedError, SynapseError
from synapse.api.room_versions import RoomVersions
from synapse.api.urls import (
Expand Down Expand Up @@ -1118,7 +1119,17 @@ async def on_POST(self, origin, content, query, group_id, category_id, room_id):
raise SynapseError(403, "requester_user_id doesn't match origin")

if category_id == "":
raise SynapseError(400, "category_id cannot be empty string")
raise SynapseError(
400, "category_id cannot be empty string", Codes.INVALID_PARAM
)

if len(category_id) > MAX_GROUP_CATEGORYID_LENGTH:
raise SynapseError(
400,
"category_id may not be longer than %s characters"
% (MAX_GROUP_CATEGORYID_LENGTH,),
Codes.INVALID_PARAM,
)

resp = await self.handler.update_group_summary_room(
group_id,
Expand Down Expand Up @@ -1184,6 +1195,14 @@ async def on_POST(self, origin, content, query, group_id, category_id):
if category_id == "":
raise SynapseError(400, "category_id cannot be empty string")

if len(category_id) > MAX_GROUP_CATEGORYID_LENGTH:
raise SynapseError(
400,
"category_id may not be longer than %s characters"
% (MAX_GROUP_CATEGORYID_LENGTH,),
Codes.INVALID_PARAM,
)

resp = await self.handler.upsert_group_category(
group_id, requester_user_id, category_id, content
)
Expand Down Expand Up @@ -1240,7 +1259,17 @@ async def on_POST(self, origin, content, query, group_id, role_id):
raise SynapseError(403, "requester_user_id doesn't match origin")

if role_id == "":
raise SynapseError(400, "role_id cannot be empty string")
raise SynapseError(
400, "role_id cannot be empty string", Codes.INVALID_PARAM
)

if len(role_id) > MAX_GROUP_ROLEID_LENGTH:
raise SynapseError(
400,
"role_id may not be longer than %s characters"
% (MAX_GROUP_ROLEID_LENGTH,),
Codes.INVALID_PARAM,
)

resp = await self.handler.update_group_role(
group_id, requester_user_id, role_id, content
Expand Down Expand Up @@ -1285,6 +1314,14 @@ async def on_POST(self, origin, content, query, group_id, role_id, user_id):
if role_id == "":
raise SynapseError(400, "role_id cannot be empty string")

if len(role_id) > MAX_GROUP_ROLEID_LENGTH:
raise SynapseError(
400,
"role_id may not be longer than %s characters"
% (MAX_GROUP_ROLEID_LENGTH,),
Codes.INVALID_PARAM,
)

resp = await self.handler.update_group_summary_user(
group_id,
requester_user_id,
Expand Down
37 changes: 24 additions & 13 deletions synapse/groups/attestations.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@

import logging
import random
from typing import Tuple
from typing import TYPE_CHECKING, Optional, Tuple

from signedjson.sign import sign_json

from synapse.api.errors import HttpResponseException, RequestSendFailed, SynapseError
from synapse.metrics.background_process_metrics import run_as_background_process
from synapse.types import get_domain_from_id
from synapse.types import JsonDict, get_domain_from_id

if TYPE_CHECKING:
from synapse.app.homeserver import HomeServer

logger = logging.getLogger(__name__)

Expand All @@ -63,15 +66,19 @@
class GroupAttestationSigning:
"""Creates and verifies group attestations."""

def __init__(self, hs):
def __init__(self, hs: "HomeServer"):
self.keyring = hs.get_keyring()
self.clock = hs.get_clock()
self.server_name = hs.hostname
self.signing_key = hs.signing_key

async def verify_attestation(
self, attestation, group_id, user_id, server_name=None
):
self,
attestation: JsonDict,
group_id: str,
user_id: str,
server_name: Optional[str] = None,
) -> None:
"""Verifies that the given attestation matches the given parameters.

An optional server_name can be supplied to explicitly set which server's
Expand Down Expand Up @@ -100,16 +107,18 @@ async def verify_attestation(
if valid_until_ms < now:
raise SynapseError(400, "Attestation expired")

assert server_name is not None
await self.keyring.verify_json_for_server(
server_name, attestation, now, "Group attestation"
)

def create_attestation(self, group_id, user_id):
def create_attestation(self, group_id: str, user_id: str) -> JsonDict:
"""Create an attestation for the group_id and user_id with default
validity length.
"""
validity_period = DEFAULT_ATTESTATION_LENGTH_MS
validity_period *= random.uniform(*DEFAULT_ATTESTATION_JITTER)
validity_period = DEFAULT_ATTESTATION_LENGTH_MS * random.uniform(
*DEFAULT_ATTESTATION_JITTER
)
valid_until_ms = int(self.clock.time_msec() + validity_period)

return sign_json(
Expand All @@ -126,7 +135,7 @@ def create_attestation(self, group_id, user_id):
class GroupAttestionRenewer:
"""Responsible for sending and receiving attestation updates."""

def __init__(self, hs):
def __init__(self, hs: "HomeServer"):
self.clock = hs.get_clock()
self.store = hs.get_datastore()
self.assestations = hs.get_groups_attestation_signing()
Expand All @@ -139,7 +148,9 @@ def __init__(self, hs):
self._start_renew_attestations, 30 * 60 * 1000
)

async def on_renew_attestation(self, group_id, user_id, content):
async def on_renew_attestation(
self, group_id: str, user_id: str, content: JsonDict
) -> JsonDict:
"""When a remote updates an attestation"""
attestation = content["attestation"]

Expand All @@ -154,10 +165,10 @@ async def on_renew_attestation(self, group_id, user_id, content):

return {}

def _start_renew_attestations(self):
def _start_renew_attestations(self) -> None:
return run_as_background_process("renew_attestations", self._renew_attestations)

async def _renew_attestations(self):
async def _renew_attestations(self) -> None:
"""Called periodically to check if we need to update any of our attestations"""

now = self.clock.time_msec()
Expand All @@ -166,7 +177,7 @@ async def _renew_attestations(self):
now + UPDATE_ATTESTATION_TIME_MS
)

async def _renew_attestation(group_user: Tuple[str, str]):
async def _renew_attestation(group_user: Tuple[str, str]) -> None:
group_id, user_id = group_user
try:
if not self.is_mine_id(group_id):
Expand Down
Loading