diff --git a/api/check_examples.py b/api/check_examples.py index be0676bb..fb9430b6 100755 --- a/api/check_examples.py +++ b/api/check_examples.py @@ -56,7 +56,8 @@ def check_parameter(filepath, request, parameter): # Setting the 'id' tells jsonschema where the file is so that it # can correctly resolve relative $ref references in the schema schema['id'] = fileurl - resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_yaml}) + example = resolve_references(filepath, example) + resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_file}) jsonschema.validate(example, schema, resolver=resolver) except Exception as e: raise ValueError("Error validating JSON schema for %r" % ( @@ -76,7 +77,8 @@ def check_response(filepath, request, code, response): # Setting the 'id' tells jsonschema where the file is so that it # can correctly resolve relative $ref references in the schema schema['id'] = fileurl - resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_yaml}) + example = resolve_references(filepath, example) + resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_file}) jsonschema.validate(example, schema, resolver=resolver) except Exception as e: raise ValueError("Error validating JSON schema for %r %r" % ( @@ -104,6 +106,27 @@ def check_swagger_file(filepath): check_response(filepath, request, code, response) +def resolve_references(path, schema): + if isinstance(schema, dict): + # do $ref first + if '$ref' in schema: + value = schema['$ref'] + path = os.path.abspath(os.path.join(os.path.dirname(path), value)) + ref = load_file("file://" + path) + result = resolve_references(path, ref) + del schema['$ref'] + else: + result = {} + + for key, value in schema.items(): + result[key] = resolve_references(path, value) + return result + elif isinstance(schema, list): + return [resolve_references(path, value) for value in schema] + else: + return schema + + def load_yaml(path): if not path.startswith("file:///"): raise Exception("Bad ref: %s" % (path,)) @@ -112,6 +135,24 @@ def load_yaml(path): return yaml.load(f) +def load_json(path): + if not path.startswith("file:///"): + raise Exception("Bad ref: %s" % (path,)) + path = path[len("file://"):] + with open(path, "r") as f: + return json.load(f) + + +def load_file(path): + print("Loading reference: %s" % path) + if path.endswith(".json"): + return load_json(path) + else: + # We have to assume it's YAML because some of the YAML examples + # do not have file extensions. + return load_yaml(path) + + if __name__ == '__main__': paths = sys.argv[1:] if not paths: diff --git a/api/server-server/definitions/invite_event.yaml b/api/server-server/definitions/invite_event.yaml new file mode 100644 index 00000000..9cef8061 --- /dev/null +++ b/api/server-server/definitions/invite_event.yaml @@ -0,0 +1,88 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +title: Invite Event +description: An invite event +allOf: + - $ref: "pdu.yaml" + - type: object + properties: + # Note: we override a bunch of parameters to change their descriptions + sender: + type: string + # TODO: Verify/clarify this - it doesn't seem right, given this is a 'regular' invite + description: |- + The matrix ID of the user who sent the original ``m.room.third_party_invite``. + example: "@someone:example.org" + origin: + type: string + description: The name of the inviting homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the inviting homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the invited member. + example: "@joe:elsewhere.com" + content: + type: object + title: Membership Event Content + description: |- + The content of the event, matching what is available in the + `Client-Server API`_. + example: {"membership": "invite"} + properties: + membership: + type: string + description: The value ``invite``. + example: "invite" + required: ['membership'] + auth_events: + type: array + description: |- + An event reference list containing the authorization events that would + allow the member to be invited to the room. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "abase64encodedsha256hashshouldbe43byteslong" + } + properties: + sha256: + type: string + description: The event hash. + example: abase64encodedsha256hashshouldbe43byteslong + required: ['sha256'] + redacts: + type: string + description: Not used. + required: + # Every other field is already flagged as required by the $ref + - state_key + - unsigned # TODO: apparently this is required? Verify. diff --git a/api/server-server/examples/unsigned_pdu.json b/api/server-server/examples/unsigned_pdu.json index 6ed4b79d..0b585a63 100644 --- a/api/server-server/examples/unsigned_pdu.json +++ b/api/server-server/examples/unsigned_pdu.json @@ -4,6 +4,13 @@ "origin": "example.com", "event_id": "$a4ecee13e2accdadf56c1025:example.com", "origin_server_ts": 1404838188000, + "depth": 12, + "auth_events": [ + [ + "$af232176:example.org", + {"sha256": "abase64encodedsha256hashshouldbe43byteslong"} + ] + ], "type": "m.room.message", "prev_events": [ [ diff --git a/api/server-server/invites.yaml b/api/server-server/invites.yaml new file mode 100644 index 00000000..a5d21938 --- /dev/null +++ b/api/server-server/invites.yaml @@ -0,0 +1,88 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Invite User To Room API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +produces: + - application/json +paths: + "/invite/{roomId}/{eventId}": + put: + summary: Invites a user to a room + description: |- + Invites a remote user to a room. Once the event has been + signed by both the inviting homeserver and the invited + homeserver, it can be sent to all of the users in the room. + operationId: sendInvite + parameters: + - in: path + name: roomId + type: string + description: The room ID that the user is being invited to. + required: true + x-example: "!abc123:matrix.org" + - in: path + name: eventId + type: string + description: The event ID for the invite event. + required: true + x-example: "$abc123:example.org" + - in: body + name: body + type: object + required: true + schema: + $ref: "definitions/invite_event.yaml" + example: { + "$ref": "examples/pdu.json", + "type": "m.room.member", + "state_key": "@someone:example.org", + "content": { + "membership": "invite" + }, + "unsigned": {} + } + responses: + 200: + description: |- + The event with the invited server's signature added. All other fields of the events + should remain untouched. + schema: + type: array + minItems: 2 + maxItems: 2 + items: + - type: integer + description: The value ``200``. + example: 200 + - $ref: "definitions/invite_event.yaml" + examples: + application/json: [ + 200, + { + "$ref": "examples/pdu.json", + "type": "m.room.member", + "state_key": "@someone:example.org", + "content": { + "membership": "invite" + }, + "unsigned": {} + } + ] diff --git a/api/server-server/joins.yaml b/api/server-server/joins.yaml new file mode 100644 index 00000000..eaf14e71 --- /dev/null +++ b/api/server-server/joins.yaml @@ -0,0 +1,276 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Join Room API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +produces: + - application/json +paths: + "/make_join/{roomId}/{userId}": + get: + summary: Get information required to make a join event for a room + description: |- + Asks the receiving server to return information that the sending + server will need to prepare a join event to get into the room. + This is part of the `Joining Rooms`_ handshake. + operationId: makeJoin + parameters: + - in: path + name: roomId + type: string + description: The room ID that is about to be joined. + required: true + x-example: "!abc123:matrix.org" + - in: path + name: userId + type: string + description: The user ID the join event will be for. + required: true + x-example: "@someone:example.org" + responses: + 200: + description: |- + An unsigned event that the server may now use as a template + for the rest of the `Joining Rooms`_ handshake. + schema: + allOf: + - $ref: "definitions/unsigned_pdu.yaml" + - type: object + properties: + # Note: we override a bunch of parameters to change their descriptions + sender: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + origin: + type: string + description: The name of the resident homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the resident homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + content: + type: object + title: Membership Event Content + description: The content of the event. + example: {"membership": "join"} + properties: + membership: + type: string + description: The value ``join``. + example: "join" + required: ['membership'] + depth: + type: integer + description: This field must be present but is ignored; it may be 0. + example: 12 + auth_events: + type: array + description: |- + An event reference list containing the authorization events that would + allow the member to join the room. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "abase64encodedsha256hashshouldbe43byteslong" + } + properties: + sha256: + type: string + description: The event hash. + example: abase64encodedsha256hashshouldbe43byteslong + required: ['sha256'] + redacts: + type: string + description: Not used. + required: + # Every other field is already flagged as required by the $ref + - state_key + examples: + application/json: { + "$ref": "examples/unsigned_pdu.json", + "type": "m.room.member", + "state_key": "@someone:example.org", + "content": { + "membership": "join" + } + } + "/send_join/{roomId}/{eventId}": + put: + summary: Submit a signed join event to a resident server + description: |- + Submits a signed join event to the resident server for it + to accept it into the room's graph. + operationId: sendJoin + parameters: + - in: path + name: roomId + type: string + description: The room ID that is about to be joined. + required: true + x-example: "!abc123:matrix.org" + - in: path + name: eventId + type: string + description: The event ID for the join event. + required: true + x-example: "$abc123:example.org" + - in: body + name: body + type: object + required: true + schema: + allOf: + - $ref: "definitions/pdu.yaml" + - type: object + properties: + # Note: we override a bunch of parameters to change their descriptions + sender: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + origin: + type: string + description: The name of the joining homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the joining homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + content: + type: object + title: Membership Event Content + description: The content of the event. + example: {"membership": "join"} + properties: + membership: + type: string + description: The value ``join``. + example: "join" + required: ['membership'] + depth: + type: integer + description: This field must be present but is ignored; it may be 0. + example: 12 + auth_events: + type: array + description: |- + An event reference list containing the authorization events that would + allow the member to join the room. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "abase64encodedsha256hashshouldbe43byteslong" + } + properties: + sha256: + type: string + description: The event hash. + example: abase64encodedsha256hashshouldbe43byteslong + required: ['sha256'] + redacts: + type: string + description: Not used. + required: + # Every other field is already flagged as required by the $ref + - state_key + example: { + "$ref": "examples/pdu.json", + "type": "m.room.member", + "state_key": "@someone:example.org", + "content": { + "membership": "join" + } + } + responses: + 200: + description: |- + The full state for the room, having accepted the join event. + schema: + type: array + minItems: 2 + maxItems: 2 + items: + - type: integer + description: The value ``200``. + example: 200 + - type: object + title: Room State + description: The state for the room. + properties: + auth_chain: + type: array + description: The auth chain. + items: + type: object + properties: {} + # TODO: Verify schema + state: + type: array + description: The room state. + items: + type: object + properties: {} + # TODO: Verify schema + required: ["auth_chain", "state"] + examples: + application/json: [ + 200, + { + # TODO: Use the appropriate refs (see TODOs in schema) + "auth_chain": [], + "state": [] + } + ] diff --git a/specification/server_server_api.rst b/specification/server_server_api.rst index 6f980045..f1825f27 100644 --- a/specification/server_server_api.rst +++ b/specification/server_server_api.rst @@ -689,16 +689,7 @@ All these URLs are name-spaced within a prefix of:: {{query_general_ss_http_api}} -To join a room:: - - GET .../make_join// - Response: JSON encoding of a join proto-event - - PUT .../send_join// - Response: JSON encoding of the state of the room at the time of the event - -Performs the room join handshake. For more information, see "Joining Rooms" -below. +{{joins_ss_http_api}} Joining Rooms ------------- @@ -888,65 +879,7 @@ that requested by the requester in the ``v`` parameter). Inviting to a room ------------------ -When a user wishes to invite another user to a local room and the other user is -on a different server, the inviting server will send a request to the invited -server:: - - PUT .../invite/{roomId}/{eventId} - -The required fields in the JSON body are: - -======================== ============ ========================================= - Key Type Description -======================== ============ ========================================= -``room_id`` String The room ID of the room. Must be the same - as the room ID specified in the path. -``event_id`` String The ID of the event. Must be the same as - the event ID specified in the path. -``type`` String The value ``m.room.member``. -``auth_events`` List An event-reference list containing the - IDs of the authorization events that - would allow this member to be invited in - the room. -``content`` Object The content of the event. -``depth`` Integer The depth of the event. -``origin`` String The name of the inviting homeserver. -``origin_server_ts`` Integer A timestamp added by the inviting - homeserver. -``prev_events`` List An event-reference list containing the - IDs of the immediate predecessor events. -``sender`` String The Matrix ID of the user who sent the - original ``m.room.third_party_invite``. -``state_key`` String The Matrix ID of the invited user. -``signatures`` Object The signature of the event from the - origin server. -``unsigned`` Object An object containing the properties that - aren't part of the signature's - computation. -======================== ============ ========================================= - -Where the ``content`` key contains the content for the ``m.room.member`` event -specified in the `Client-Server API`_. Note that the ``membership`` property of -the content must be ``invite``. - -Upon receiving this request, the invited homeserver will append its signature to -the event and respond to the request with the following JSON body:: - - [ - 200, - "event": {...} - ] - -Where ``event`` contains the event signed by both homeservers, using the same -JSON keys as the initial request on ``/invite/{roomId}/{eventId}``. Note that, -except for the ``signatures`` object (which now contains an additional signature), -all of the event's keys remain the same as in the event initially provided. - -This response format is due to a typo in Synapse, the first implementation of -Matrix's APIs, and is preserved to maintain compatibility. - -Now that the event has been signed by both the inviting homeserver and the -invited homeserver, it can be sent to all of the users in the room. +{{invites_ss_http_api}} Third-party invites -------------------