|
11 | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12 | 12 | # See the License for the specific language governing permissions and
|
13 | 13 | # limitations under the License.
|
14 |
| - |
| 14 | +import collections.abc |
15 | 15 | from typing import Union
|
16 | 16 |
|
| 17 | +import jsonschema |
| 18 | + |
17 | 19 | from synapse.api.constants import MAX_ALIAS_LENGTH, EventTypes, Membership
|
18 | 20 | from synapse.api.errors import Codes, SynapseError
|
19 | 21 | from synapse.api.room_versions import EventFormatVersions
|
20 | 22 | from synapse.config.homeserver import HomeServerConfig
|
21 | 23 | from synapse.events import EventBase
|
22 | 24 | from synapse.events.builder import EventBuilder
|
23 |
| -from synapse.events.utils import validate_canonicaljson |
| 25 | +from synapse.events.utils import ( |
| 26 | + CANONICALJSON_MAX_INT, |
| 27 | + CANONICALJSON_MIN_INT, |
| 28 | + validate_canonicaljson, |
| 29 | +) |
24 | 30 | from synapse.federation.federation_server import server_matches_acl_event
|
25 | 31 | from synapse.types import EventID, RoomID, UserID
|
26 | 32 |
|
@@ -87,6 +93,29 @@ def validate_new(self, event: EventBase, config: HomeServerConfig):
|
87 | 93 | 400, "Can't create an ACL event that denies the local server"
|
88 | 94 | )
|
89 | 95 |
|
| 96 | + if event.type == EventTypes.PowerLevels: |
| 97 | + try: |
| 98 | + jsonschema.validate( |
| 99 | + instance=event.content, |
| 100 | + schema=POWER_LEVELS_SCHEMA, |
| 101 | + cls=plValidator, |
| 102 | + ) |
| 103 | + except jsonschema.ValidationError as e: |
| 104 | + if e.path: |
| 105 | + # example: "users_default": '0' is not of type 'integer' |
| 106 | + message = '"' + e.path[-1] + '": ' + e.message # noqa: B306 |
| 107 | + # jsonschema.ValidationError.message is a valid attribute |
| 108 | + else: |
| 109 | + # example: '0' is not of type 'integer' |
| 110 | + message = e.message # noqa: B306 |
| 111 | + # jsonschema.ValidationError.message is a valid attribute |
| 112 | + |
| 113 | + raise SynapseError( |
| 114 | + code=400, |
| 115 | + msg=message, |
| 116 | + errcode=Codes.BAD_JSON, |
| 117 | + ) |
| 118 | + |
90 | 119 | def _validate_retention(self, event: EventBase):
|
91 | 120 | """Checks that an event that defines the retention policy for a room respects the
|
92 | 121 | format enforced by the spec.
|
@@ -185,3 +214,47 @@ def _ensure_strings(self, d, keys):
|
185 | 214 | def _ensure_state_event(self, event):
|
186 | 215 | if not event.is_state():
|
187 | 216 | raise SynapseError(400, "'%s' must be state events" % (event.type,))
|
| 217 | + |
| 218 | + |
| 219 | +POWER_LEVELS_SCHEMA = { |
| 220 | + "type": "object", |
| 221 | + "properties": { |
| 222 | + "ban": {"$ref": "#/definitions/int"}, |
| 223 | + "events": {"$ref": "#/definitions/objectOfInts"}, |
| 224 | + "events_default": {"$ref": "#/definitions/int"}, |
| 225 | + "invite": {"$ref": "#/definitions/int"}, |
| 226 | + "kick": {"$ref": "#/definitions/int"}, |
| 227 | + "notifications": {"$ref": "#/definitions/objectOfInts"}, |
| 228 | + "redact": {"$ref": "#/definitions/int"}, |
| 229 | + "state_default": {"$ref": "#/definitions/int"}, |
| 230 | + "users": {"$ref": "#/definitions/objectOfInts"}, |
| 231 | + "users_default": {"$ref": "#/definitions/int"}, |
| 232 | + }, |
| 233 | + "definitions": { |
| 234 | + "int": { |
| 235 | + "type": "integer", |
| 236 | + "minimum": CANONICALJSON_MIN_INT, |
| 237 | + "maximum": CANONICALJSON_MAX_INT, |
| 238 | + }, |
| 239 | + "objectOfInts": { |
| 240 | + "type": "object", |
| 241 | + "additionalProperties": {"$ref": "#/definitions/int"}, |
| 242 | + }, |
| 243 | + }, |
| 244 | +} |
| 245 | + |
| 246 | + |
| 247 | +def _create_power_level_validator(): |
| 248 | + validator = jsonschema.validators.validator_for(POWER_LEVELS_SCHEMA) |
| 249 | + |
| 250 | + # by default jsonschema does not consider a frozendict to be an object so |
| 251 | + # we need to use a custom type checker |
| 252 | + # https://python-jsonschema.readthedocs.io/en/stable/validate/?highlight=object#validating-with-additional-types |
| 253 | + type_checker = validator.TYPE_CHECKER.redefine( |
| 254 | + "object", lambda checker, thing: isinstance(thing, collections.abc.Mapping) |
| 255 | + ) |
| 256 | + |
| 257 | + return jsonschema.validators.extend(validator, type_checker=type_checker) |
| 258 | + |
| 259 | + |
| 260 | +plValidator = _create_power_level_validator() |
0 commit comments