Merge branch 'master' into daniel/multipleexamples
This commit is contained in:
commit
071edcd86e
7 changed files with 259 additions and 10 deletions
103
api/client-server/v1/guest_events.yaml
Normal file
103
api/client-server/v1/guest_events.yaml
Normal file
|
@ -0,0 +1,103 @@
|
|||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Sync Guest API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/events":
|
||||
get:
|
||||
summary: Listen on the event stream.
|
||||
description: |-
|
||||
This will listen for new events related to a particular room and return
|
||||
them to the caller. This will block until an event is received, or until
|
||||
the ``timeout`` is reached.
|
||||
|
||||
This API is the same as the non-guest /events endpoint, but can be
|
||||
called by guest users.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: query
|
||||
type: string
|
||||
name: from
|
||||
description: |-
|
||||
The token to stream from. This token is either from a previous
|
||||
request to this API or from the initial sync API.
|
||||
required: false
|
||||
x-example: "s3456_9_0"
|
||||
- in: query
|
||||
type: integer
|
||||
name: timeout
|
||||
description: The maximum time in milliseconds to wait for an event.
|
||||
required: false
|
||||
x-example: "35000"
|
||||
- in: query
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
name: room_id
|
||||
description: |-
|
||||
The room IDs for which events should be returned.
|
||||
x-example:
|
||||
- "!somewhere:over"
|
||||
- "!the:rainbow"
|
||||
responses:
|
||||
200:
|
||||
description: "The events received, which may be none."
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"start": "s3456_9_0",
|
||||
"end": "s3457_9_0",
|
||||
"chunk": [
|
||||
{
|
||||
"age": 32,
|
||||
"content": {
|
||||
"body": "incoming message",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$14328055551tzaee:localhost",
|
||||
"origin_server_ts": 1432804485886,
|
||||
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
|
||||
"type": "m.room.message",
|
||||
"user_id": "@bob:localhost"
|
||||
}
|
||||
]
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
start:
|
||||
type: string
|
||||
description: |-
|
||||
A token which correlates to the first value in ``chunk``. This
|
||||
is usually the same token supplied to ``from=``.
|
||||
end:
|
||||
type: string
|
||||
description: |-
|
||||
A token which correlates to the last value in ``chunk``. This
|
||||
token should be used in the next request to ``/events``.
|
||||
chunk:
|
||||
type: array
|
||||
description: "An array of events."
|
||||
items:
|
||||
type: object
|
||||
title: Event
|
||||
allOf:
|
||||
- "$ref": "core-event-schema/room_event.json"
|
||||
400:
|
||||
description: "Bad pagination ``from`` parameter."
|
12
event-schemas/examples/v1/m.room.guest_access
Normal file
12
event-schemas/examples/v1/m.room.guest_access
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"age": 242353,
|
||||
"content": {
|
||||
"guest_access": "can_join"
|
||||
},
|
||||
"state_key": "",
|
||||
"origin_server_ts": 1431961217938,
|
||||
"event_id": "$WLGTSEFSEG:localhost",
|
||||
"type": "m.room.guest_access",
|
||||
"room_id": "!Cuyf34gef24u:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
30
event-schemas/schema/v1/m.room.guest_access
Normal file
30
event-schemas/schema/v1/m.room.guest_access
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"type": "object",
|
||||
"title": "Controls whether guest users are allowed to join rooms.",
|
||||
"description": "This event controls whether guest users are allowed to join rooms. If this event is absent, servers should act as if it is present and has the guest_access value \"forbidden\".",
|
||||
"allOf": [{
|
||||
"$ref": "core-event-schema/state_event.json"
|
||||
}],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"guest_access": {
|
||||
"type": "string",
|
||||
"description": "Whether guests can join the room.",
|
||||
"enum": ["can_join", "forbidden"]
|
||||
}
|
||||
},
|
||||
"required": ["guest_access"]
|
||||
},
|
||||
"state_key": {
|
||||
"type": "string",
|
||||
"description": "A zero-length string.",
|
||||
"pattern": "^$"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["m.room.guest_access"]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,9 +25,11 @@ import (
|
|||
var (
|
||||
port = flag.Int("port", 8000, "Port on which to serve HTTP")
|
||||
|
||||
toServe atomic.Value // Always contains valid []byte to serve. May be stale unless wg is zero.
|
||||
wg sync.WaitGroup // Indicates how many updates are pending.
|
||||
mu sync.Mutex // Prevent multiple updates in parallel.
|
||||
mu sync.Mutex // Prevent multiple updates in parallel.
|
||||
toServe atomic.Value // Always contains valid []byte to serve. May be stale unless wg is zero.
|
||||
|
||||
wgMu sync.Mutex // Prevent multiple calls to wg.Wait() or wg.Add(positive number) in parallel.
|
||||
wg sync.WaitGroup // Indicates how many updates are pending.
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -111,9 +113,17 @@ func filter(e fsnotify.Event) bool {
|
|||
}
|
||||
|
||||
func serve(w http.ResponseWriter, req *http.Request) {
|
||||
wgMu.Lock()
|
||||
wg.Wait()
|
||||
b := toServe.Load().([]byte)
|
||||
w.Write(b)
|
||||
wgMu.Unlock()
|
||||
b := toServe.Load().(bytesOrErr)
|
||||
if b.err != nil {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write([]byte(b.err.Error()))
|
||||
} else {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.Write([]byte(b.bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func populateOnce(dir string) {
|
||||
|
@ -126,15 +136,15 @@ func populateOnce(dir string) {
|
|||
cmd.Stderr = &b
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
toServe.Store([]byte(fmt.Errorf("error generating spec: %v\nOutput from gendoc:\n%v", err, b.String()).Error()))
|
||||
toServe.Store(bytesOrErr{nil, fmt.Errorf("error generating spec: %v\nOutput from gendoc:\n%v", err, b.String())})
|
||||
return
|
||||
}
|
||||
specBytes, err := ioutil.ReadFile(path.Join(dir, "scripts", "gen", "specification.html"))
|
||||
if err != nil {
|
||||
toServe.Store([]byte(fmt.Errorf("error reading spec: %v", err).Error()))
|
||||
toServe.Store(bytesOrErr{nil, fmt.Errorf("error reading spec: %v", err)})
|
||||
return
|
||||
}
|
||||
toServe.Store(specBytes)
|
||||
toServe.Store(bytesOrErr{specBytes, nil})
|
||||
}
|
||||
|
||||
func doPopulate(ch chan struct{}, dir string) {
|
||||
|
@ -143,7 +153,9 @@ func doPopulate(ch chan struct{}, dir string) {
|
|||
select {
|
||||
case <-ch:
|
||||
if pending == 0 {
|
||||
wgMu.Lock()
|
||||
wg.Add(1)
|
||||
wgMu.Unlock()
|
||||
}
|
||||
pending++
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
|
@ -159,3 +171,8 @@ func exists(path string) bool {
|
|||
_, err := os.Stat(path)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
type bytesOrErr struct {
|
||||
bytes []byte
|
||||
err error
|
||||
}
|
||||
|
|
81
specification/modules/guest_access.rst
Normal file
81
specification/modules/guest_access.rst
Normal file
|
@ -0,0 +1,81 @@
|
|||
Guest access
|
||||
============
|
||||
|
||||
.. _module:guest-access:
|
||||
|
||||
There are times when it is desirable for clients to be able to interact with
|
||||
rooms without having to fully register for an account on a homeserver or join
|
||||
the room. This module specifies how these clients should interact with servers
|
||||
in order to participate in rooms as guests.
|
||||
|
||||
Guest users retrieve access tokens from a homeserver using the ordinary
|
||||
`register endpoint <#post-matrix-client-api-v2-alpha-register>`_, specifying
|
||||
the ``kind`` parameter as ``guest``. They may then interact with the
|
||||
client-server API as any other user would, but will only have access to a subset
|
||||
of the API as described the Client behaviour subsection below.
|
||||
Homeservers may choose not to allow this access at all to their local users, but
|
||||
have no information about whether users on other homeservers are guests or not.
|
||||
|
||||
This module does not fully factor in federation; it relies on individual
|
||||
homeservers properly adhering to the rules set out in this module, rather than
|
||||
allowing all homeservers to enforce the rules on each other.
|
||||
|
||||
Events
|
||||
------
|
||||
{{m_room_guest_access_event}}
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
The following API endpoints are allowed to be accessed by guest accounts for
|
||||
retrieving events:
|
||||
|
||||
* `GET /rooms/:room_id/state <#get-matrix-client-api-v1-rooms-roomid-state>`_
|
||||
* `GET /rooms/:room_id/state/:event_type/:state_key <#get-matrix-client-api-v1-rooms-roomid-state-eventtype-statekey>`_
|
||||
* `GET /rooms/:room_id/messages <#get-matrix-client-api-v1-rooms-roomid-messages>`_
|
||||
|
||||
There is also a special version of the
|
||||
`GET /events <#get-matrix-client-api-v1-events>`_ endpoint:
|
||||
|
||||
{{guest_events_http_api}}
|
||||
|
||||
They will only return events which happened while the room state had the
|
||||
``m.room.history_visibility`` state event present with ``history_visibility``
|
||||
value ``world_readable``. Guest clients do not need to join rooms in order to
|
||||
receive events for them.
|
||||
|
||||
The following API endpoints are allowed to be accessed by guest accounts for
|
||||
sending events:
|
||||
|
||||
* `POST /rooms/:room_id/join <#post-matrix-client-api-v1-rooms-roomid-join>`_
|
||||
* `PUT /rooms/:room_id/send/m.room.message/:txn_id <#put-matrix-client-api-v1-rooms-roomid-send-eventtype-txnid>`_
|
||||
|
||||
Guest clients *do* need to join rooms in order to send events to them.
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
Servers are required to only return events to guest accounts for rooms where
|
||||
the room state at the event had the ``m.room.history_visibility`` state event
|
||||
present with ``history_visibility`` value ``world_readable``. These events may
|
||||
be returned even if the anonymous user is not joined to the room.
|
||||
|
||||
Servers MUST only allow guest users to join rooms if the ``m.room.guest_access``
|
||||
state event is present on the room, and has the ``guest_access`` value
|
||||
``can_join``. If the ``m.room.guest_access`` event is changed to stop this from
|
||||
being the case, the server MUST set those users' ``m.room.member`` state to
|
||||
``leave``.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
Each homeserver manages its own guest accounts itself, and whether an account
|
||||
is a guest account or not is not information passed from server to server.
|
||||
Accordingly, any server participating in a room is trusted to properly enforce
|
||||
the permissions outlined in this section.
|
||||
|
||||
Clients may wish to display to their users that rooms which are
|
||||
``world_readable`` *may* be showing messages to non-joined users. There is no
|
||||
way using this module to find out whether any non-joined guest users *do* see
|
||||
events in the room, or to list or count any guest users.
|
||||
|
||||
Homeservers may want to enable protections such as captchas for guest
|
||||
registration to prevent spam, denial of service, and similar attacks.
|
||||
|
|
@ -25,6 +25,7 @@ groups: # reusable blobs of files when prefixed with 'group:'
|
|||
- modules/push.rst
|
||||
- modules/third_party_invites.rst
|
||||
- modules/search.rst
|
||||
- modules/guest_access.rst
|
||||
|
||||
|
||||
title_styles: ["=", "-", "~", "+", "^", "`"]
|
||||
|
|
|
@ -369,7 +369,7 @@ class MatrixUnits(Units):
|
|||
]
|
||||
if len(params_missing_examples) == 0:
|
||||
path_template = api.get("basePath", "").rstrip("/") + path
|
||||
qps = {}
|
||||
qps = []
|
||||
body = ""
|
||||
for param in single_api.get("parameters", []):
|
||||
if param["in"] == "path":
|
||||
|
@ -381,7 +381,12 @@ class MatrixUnits(Units):
|
|||
elif param["in"] == "body":
|
||||
body = param["schema"]["example"]
|
||||
elif param["in"] == "query":
|
||||
qps[param["name"]] = param["x-example"]
|
||||
example = param["x-example"]
|
||||
if type(example) == list:
|
||||
for value in example:
|
||||
qps.append((param["name"], value))
|
||||
else:
|
||||
qps.append((param["name"], example))
|
||||
query_string = "" if len(qps) == 0 else "?"+urllib.urlencode(qps)
|
||||
if body:
|
||||
endpoint["example"]["req"] = "%s %s%s HTTP/1.1\nContent-Type: application/json\n\n%s" % (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue