Implement room version capabilities in CS API (#866)

* Add wiring for querying the roomserver for the default room version

* Try to implement /capabilities for room versions

* Update copyright notices

* Update sytests, add /capabilities endpoint into CS API

* Update sytest-whitelist

* Add GetDefaultRoomVersion

* Fix cases where state package was shadowed

* Fix version formatting

* Update Dockerfile to Go 1.13.6

* oh yes types I remember

* And fix the default too
This commit is contained in:
Neil Alexander 2020-02-05 18:06:39 +00:00 committed by GitHub
parent 880d8ae024
commit c20109a573
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 208 additions and 19 deletions

View File

@ -0,0 +1,51 @@
// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// 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.
package routing
import (
"net/http"
"github.com/matrix-org/dendrite/clientapi/httputil"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/util"
)
// SendMembership implements PUT /rooms/{roomID}/(join|kick|ban|unban|leave|invite)
// by building a m.room.member event then sending it to the room server
func GetCapabilities(
req *http.Request, queryAPI roomserverAPI.RoomserverQueryAPI,
) util.JSONResponse {
roomVersionsQueryReq := roomserverAPI.QueryRoomVersionCapabilitiesRequest{}
var roomVersionsQueryRes roomserverAPI.QueryRoomVersionCapabilitiesResponse
if err := queryAPI.QueryRoomVersionCapabilities(
req.Context(),
&roomVersionsQueryReq,
&roomVersionsQueryRes,
); err != nil {
return httputil.LogThenError(req, err)
}
response := map[string]interface{}{
"capabilities": map[string]interface{}{
"m.room_versions": roomVersionsQueryRes,
},
}
return util.JSONResponse{
Code: http.StatusOK,
JSON: response,
}
}

View File

@ -551,4 +551,10 @@ func Setup(
return DeleteTag(req, accountDB, device, vars["userId"], vars["roomId"], vars["tag"], syncProducer) return DeleteTag(req, accountDB, device, vars["userId"], vars["roomId"], vars["tag"], syncProducer)
}), }),
).Methods(http.MethodDelete, http.MethodOptions) ).Methods(http.MethodDelete, http.MethodOptions)
r0mux.Handle("/capabilities",
common.MakeAuthAPI("capabilities", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
return GetCapabilities(req, queryAPI)
}),
).Methods(http.MethodGet)
} }

View File

@ -1,4 +1,4 @@
FROM docker.io/golang:1.12.5-alpine3.9 FROM docker.io/golang:1.13.6-alpine
RUN mkdir /build RUN mkdir /build

View File

@ -1,4 +1,6 @@
// Copyright 2017 Vector Creations Ltd // Copyright 2017 Vector Creations Ltd
// Copyright 2018 New Vector Ltd
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -244,6 +246,15 @@ type QueryServersInRoomAtEventResponse struct {
Servers []gomatrixserverlib.ServerName `json:"servers"` Servers []gomatrixserverlib.ServerName `json:"servers"`
} }
// QueryRoomVersionCapabilities asks for the default room version
type QueryRoomVersionCapabilitiesRequest struct{}
// QueryRoomVersionCapabilitiesResponse is a response to QueryServersInRoomAtEventResponse
type QueryRoomVersionCapabilitiesResponse struct {
DefaultRoomVersion string `json:"default"`
AvailableRoomVersions map[string]string `json:"available"`
}
// RoomserverQueryAPI is used to query information from the room server. // RoomserverQueryAPI is used to query information from the room server.
type RoomserverQueryAPI interface { type RoomserverQueryAPI interface {
// Query the latest events and state for a room from the room server. // Query the latest events and state for a room from the room server.
@ -323,6 +334,13 @@ type RoomserverQueryAPI interface {
request *QueryServersInRoomAtEventRequest, request *QueryServersInRoomAtEventRequest,
response *QueryServersInRoomAtEventResponse, response *QueryServersInRoomAtEventResponse,
) error ) error
// Asks for the default room version as preferred by the server.
QueryRoomVersionCapabilities(
ctx context.Context,
request *QueryRoomVersionCapabilitiesRequest,
response *QueryRoomVersionCapabilitiesResponse,
) error
} }
// RoomserverQueryLatestEventsAndStatePath is the HTTP path for the QueryLatestEventsAndState API. // RoomserverQueryLatestEventsAndStatePath is the HTTP path for the QueryLatestEventsAndState API.
@ -358,6 +376,9 @@ const RoomserverQueryBackfillPath = "/api/roomserver/queryBackfill"
// RoomserverQueryServersInRoomAtEventPath is the HTTP path for the QueryServersInRoomAtEvent API // RoomserverQueryServersInRoomAtEventPath is the HTTP path for the QueryServersInRoomAtEvent API
const RoomserverQueryServersInRoomAtEventPath = "/api/roomserver/queryServersInRoomAtEvents" const RoomserverQueryServersInRoomAtEventPath = "/api/roomserver/queryServersInRoomAtEvents"
// RoomserverQueryRoomVersionCapabilitiesPath is the HTTP path for the QueryRoomVersionCapabilities API
const RoomserverQueryRoomVersionCapabilitiesPath = "/api/roomserver/queryRoomVersionCapabilities"
// NewRoomserverQueryAPIHTTP creates a RoomserverQueryAPI implemented by talking to a HTTP POST API. // NewRoomserverQueryAPIHTTP creates a RoomserverQueryAPI implemented by talking to a HTTP POST API.
// If httpClient is nil then it uses the http.DefaultClient // If httpClient is nil then it uses the http.DefaultClient
func NewRoomserverQueryAPIHTTP(roomserverURL string, httpClient *http.Client) RoomserverQueryAPI { func NewRoomserverQueryAPIHTTP(roomserverURL string, httpClient *http.Client) RoomserverQueryAPI {
@ -514,3 +535,16 @@ func (h *httpRoomserverQueryAPI) QueryServersInRoomAtEvent(
apiURL := h.roomserverURL + RoomserverQueryServersInRoomAtEventPath apiURL := h.roomserverURL + RoomserverQueryServersInRoomAtEventPath
return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response) return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
} }
// QueryServersInRoomAtEvent implements RoomServerQueryAPI
func (h *httpRoomserverQueryAPI) QueryRoomVersionCapabilities(
ctx context.Context,
request *QueryRoomVersionCapabilitiesRequest,
response *QueryRoomVersionCapabilitiesResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryRoomVersionCapabilities")
defer span.Finish()
apiURL := h.roomserverURL + RoomserverQueryRoomVersionCapabilitiesPath
return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
}

View File

@ -1,4 +1,6 @@
// Copyright 2017 Vector Creations Ltd // Copyright 2017 Vector Creations Ltd
// Copyright 2018 New Vector Ltd
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -151,7 +153,7 @@ func calculateAndSetState(
event gomatrixserverlib.Event, event gomatrixserverlib.Event,
) error { ) error {
// TODO: get the correct room version // TODO: get the correct room version
state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, db) roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, db)
if err != nil { if err != nil {
return err return err
} }
@ -169,7 +171,7 @@ func calculateAndSetState(
} }
} else { } else {
// We haven't been told what the state at the event is so we need to calculate it from the prev_events // We haven't been told what the state at the event is so we need to calculate it from the prev_events
if stateAtEvent.BeforeStateSnapshotNID, err = state.CalculateAndStoreStateBeforeEvent(ctx, event, roomNID); err != nil { if stateAtEvent.BeforeStateSnapshotNID, err = roomState.CalculateAndStoreStateBeforeEvent(ctx, event, roomNID); err != nil {
return err return err
} }
} }

View File

@ -1,4 +1,6 @@
// Copyright 2017 Vector Creations Ltd // Copyright 2017 Vector Creations Ltd
// Copyright 2018 New Vector Ltd
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -172,7 +174,7 @@ func (u *latestEventsUpdater) doUpdateLatestEvents() error {
func (u *latestEventsUpdater) latestState() error { func (u *latestEventsUpdater) latestState() error {
var err error var err error
// TODO: get the correct room version // TODO: get the correct room version
state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, u.db) roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, u.db)
if err != nil { if err != nil {
return err return err
} }
@ -181,21 +183,21 @@ func (u *latestEventsUpdater) latestState() error {
for i := range u.latest { for i := range u.latest {
latestStateAtEvents[i] = u.latest[i].StateAtEvent latestStateAtEvents[i] = u.latest[i].StateAtEvent
} }
u.newStateNID, err = state.CalculateAndStoreStateAfterEvents( u.newStateNID, err = roomState.CalculateAndStoreStateAfterEvents(
u.ctx, u.roomNID, latestStateAtEvents, u.ctx, u.roomNID, latestStateAtEvents,
) )
if err != nil { if err != nil {
return err return err
} }
u.removed, u.added, err = state.DifferenceBetweeenStateSnapshots( u.removed, u.added, err = roomState.DifferenceBetweeenStateSnapshots(
u.ctx, u.oldStateNID, u.newStateNID, u.ctx, u.oldStateNID, u.newStateNID,
) )
if err != nil { if err != nil {
return err return err
} }
u.stateBeforeEventRemoves, u.stateBeforeEventAdds, err = state.DifferenceBetweeenStateSnapshots( u.stateBeforeEventRemoves, u.stateBeforeEventAdds, err = roomState.DifferenceBetweeenStateSnapshots(
u.ctx, u.newStateNID, u.stateAtEvent.BeforeStateSnapshotNID, u.ctx, u.newStateNID, u.stateAtEvent.BeforeStateSnapshotNID,
) )
return err return err

View File

@ -1,4 +1,6 @@
// Copyright 2017 Vector Creations Ltd // Copyright 2017 Vector Creations Ltd
// Copyright 2018 New Vector Ltd
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -18,6 +20,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"strconv"
"github.com/matrix-org/dendrite/common" "github.com/matrix-org/dendrite/common"
"github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/api"
@ -25,6 +28,7 @@ import (
"github.com/matrix-org/dendrite/roomserver/state" "github.com/matrix-org/dendrite/roomserver/state"
"github.com/matrix-org/dendrite/roomserver/state/database" "github.com/matrix-org/dendrite/roomserver/state/database"
"github.com/matrix-org/dendrite/roomserver/types" "github.com/matrix-org/dendrite/roomserver/types"
"github.com/matrix-org/dendrite/roomserver/version"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -100,7 +104,7 @@ func (r *RoomserverQueryAPI) QueryLatestEventsAndState(
response *api.QueryLatestEventsAndStateResponse, response *api.QueryLatestEventsAndStateResponse,
) error { ) error {
// TODO: get the correct room version // TODO: get the correct room version
state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB) roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
if err != nil { if err != nil {
return err return err
} }
@ -121,7 +125,7 @@ func (r *RoomserverQueryAPI) QueryLatestEventsAndState(
} }
// Look up the currrent state for the requested tuples. // Look up the currrent state for the requested tuples.
stateEntries, err := state.LoadStateAtSnapshotForStringTuples( stateEntries, err := roomState.LoadStateAtSnapshotForStringTuples(
ctx, currentStateSnapshotNID, request.StateToFetch, ctx, currentStateSnapshotNID, request.StateToFetch,
) )
if err != nil { if err != nil {
@ -144,7 +148,7 @@ func (r *RoomserverQueryAPI) QueryStateAfterEvents(
response *api.QueryStateAfterEventsResponse, response *api.QueryStateAfterEventsResponse,
) error { ) error {
// TODO: get the correct room version // TODO: get the correct room version
state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB) roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
if err != nil { if err != nil {
return err return err
} }
@ -170,7 +174,7 @@ func (r *RoomserverQueryAPI) QueryStateAfterEvents(
response.PrevEventsExist = true response.PrevEventsExist = true
// Look up the currrent state for the requested tuples. // Look up the currrent state for the requested tuples.
stateEntries, err := state.LoadStateAfterEventsForStringTuples( stateEntries, err := roomState.LoadStateAfterEventsForStringTuples(
ctx, prevStates, request.StateToFetch, ctx, prevStates, request.StateToFetch,
) )
if err != nil { if err != nil {
@ -327,7 +331,7 @@ func (r *RoomserverQueryAPI) getMembershipsBeforeEventNID(
ctx context.Context, eventNID types.EventNID, joinedOnly bool, ctx context.Context, eventNID types.EventNID, joinedOnly bool,
) ([]types.Event, error) { ) ([]types.Event, error) {
// TODO: get the correct room version // TODO: get the correct room version
state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB) roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
if err != nil { if err != nil {
return []types.Event{}, err return []types.Event{}, err
} }
@ -345,7 +349,7 @@ func (r *RoomserverQueryAPI) getMembershipsBeforeEventNID(
} }
// Fetch the state as it was when this event was fired // Fetch the state as it was when this event was fired
stateEntries, err := state.LoadCombinedStateAfterEvents(ctx, prevState) stateEntries, err := roomState.LoadCombinedStateAfterEvents(ctx, prevState)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -433,12 +437,12 @@ func (r *RoomserverQueryAPI) checkServerAllowedToSeeEvent(
ctx context.Context, eventID string, serverName gomatrixserverlib.ServerName, ctx context.Context, eventID string, serverName gomatrixserverlib.ServerName,
) (bool, error) { ) (bool, error) {
// TODO: get the correct room version // TODO: get the correct room version
state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB) roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
if err != nil { if err != nil {
return false, err return false, err
} }
stateEntries, err := state.LoadStateAtEvent(ctx, eventID) stateEntries, err := roomState.LoadStateAtEvent(ctx, eventID)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -593,7 +597,7 @@ func (r *RoomserverQueryAPI) QueryStateAndAuthChain(
response *api.QueryStateAndAuthChainResponse, response *api.QueryStateAndAuthChainResponse,
) error { ) error {
// TODO: get the correct room version // TODO: get the correct room version
state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB) roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
if err != nil { if err != nil {
return err return err
} }
@ -620,7 +624,7 @@ func (r *RoomserverQueryAPI) QueryStateAndAuthChain(
response.PrevEventsExist = true response.PrevEventsExist = true
// Look up the currrent state for the requested tuples. // Look up the currrent state for the requested tuples.
stateEntries, err := state.LoadCombinedStateAfterEvents( stateEntries, err := roomState.LoadCombinedStateAfterEvents(
ctx, prevStates, ctx, prevStates,
) )
if err != nil { if err != nil {
@ -723,6 +727,25 @@ func (r *RoomserverQueryAPI) QueryServersInRoomAtEvent(
return nil return nil
} }
// QueryRoomVersionCapabilities implements api.RoomserverQueryAPI
func (r *RoomserverQueryAPI) QueryRoomVersionCapabilities(
ctx context.Context,
request *api.QueryRoomVersionCapabilitiesRequest,
response *api.QueryRoomVersionCapabilitiesResponse,
) error {
response.DefaultRoomVersion = strconv.Itoa(int(version.GetDefaultRoomVersion()))
response.AvailableRoomVersions = make(map[string]string)
for v, desc := range version.GetSupportedRoomVersions() {
sv := strconv.Itoa(int(v))
if desc.Stable {
response.AvailableRoomVersions[sv] = "stable"
} else {
response.AvailableRoomVersions[sv] = "unstable"
}
}
return nil
}
// SetupHTTP adds the RoomserverQueryAPI handlers to the http.ServeMux. // SetupHTTP adds the RoomserverQueryAPI handlers to the http.ServeMux.
// nolint: gocyclo // nolint: gocyclo
func (r *RoomserverQueryAPI) SetupHTTP(servMux *http.ServeMux) { func (r *RoomserverQueryAPI) SetupHTTP(servMux *http.ServeMux) {
@ -880,4 +903,18 @@ func (r *RoomserverQueryAPI) SetupHTTP(servMux *http.ServeMux) {
return util.JSONResponse{Code: http.StatusOK, JSON: &response} return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}), }),
) )
servMux.Handle(
api.RoomserverQueryRoomVersionCapabilitiesPath,
common.MakeInternalAPI("QueryRoomVersionCapabilities", func(req *http.Request) util.JSONResponse {
var request api.QueryRoomVersionCapabilitiesRequest
var response api.QueryRoomVersionCapabilitiesResponse
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.ErrorResponse(err)
}
if err := r.QueryRoomVersionCapabilities(req.Context(), &request, &response); err != nil {
return util.ErrorResponse(err)
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
} }

View File

@ -1,3 +1,19 @@
// Copyright 2017 Vector Creations Ltd
// Copyright 2018 New Vector Ltd
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
//
// 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.
package database package database
import ( import (

View File

@ -1,3 +1,19 @@
// Copyright 2017 Vector Creations Ltd
// Copyright 2018 New Vector Ltd
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
//
// 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.
package state package state
import ( import (

View File

@ -1,4 +1,6 @@
// Copyright 2017 Vector Creations Ltd // Copyright 2017 Vector Creations Ltd
// Copyright 2018 New Vector Ltd
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.

View File

@ -55,7 +55,6 @@ type Database interface {
GetMembershipEventNIDsForRoom(ctx context.Context, roomNID types.RoomNID, joinOnly bool) ([]types.EventNID, error) GetMembershipEventNIDsForRoom(ctx context.Context, roomNID types.RoomNID, joinOnly bool) ([]types.EventNID, error)
EventsFromIDs(ctx context.Context, eventIDs []string) ([]types.Event, error) EventsFromIDs(ctx context.Context, eventIDs []string) ([]types.Event, error)
GetRoomVersionForRoom(ctx context.Context, roomNID types.RoomNID) (int64, error) GetRoomVersionForRoom(ctx context.Context, roomNID types.RoomNID) (int64, error)
//GetRoomVersionForEvent(ctx context.Context, eventNID types.EventNID) int64
} }
// NewPublicRoomsServerDatabase opens a database connection. // NewPublicRoomsServerDatabase opens a database connection.

View File

@ -1,3 +1,17 @@
// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// 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.
package version package version
import ( import (
@ -69,6 +83,10 @@ var roomVersions = map[RoomVersionID]RoomVersionDescription{
}, },
} }
func GetDefaultRoomVersion() RoomVersionID {
return RoomVersionV1
}
func GetRoomVersions() map[RoomVersionID]RoomVersionDescription { func GetRoomVersions() map[RoomVersionID]RoomVersionDescription {
return roomVersions return roomVersions
} }

View File

@ -19,3 +19,6 @@ Alias creators can delete alias with no ops
# Blacklisted because matrix-org/dendrite#847 might have broken it but we're not # Blacklisted because matrix-org/dendrite#847 might have broken it but we're not
# really sure and we need it pretty badly anyway # really sure and we need it pretty badly anyway
Real non-joined users can get individual state for world_readable rooms after leaving Real non-joined users can get individual state for world_readable rooms after leaving
# Blacklisted until matrix-org/dendrite#862 is reverted due to Riot bug
Latest account data appears in v2 /sync

View File

@ -112,7 +112,7 @@ User can invite local user to room with version 4
Should reject keys claiming to belong to a different user Should reject keys claiming to belong to a different user
Can add account data Can add account data
Can add account data to room Can add account data to room
Latest account data appears in v2 /sync #Latest account data appears in v2 /sync
New account data appears in incremental v2 /sync New account data appears in incremental v2 /sync
Checking local federation server Checking local federation server
Inbound federation can query profile data Inbound federation can query profile data
@ -227,3 +227,6 @@ Guest users can sync from world_readable guest_access rooms if joined
Guest users can sync from default guest_access rooms if joined Guest users can sync from default guest_access rooms if joined
Real non-joined users cannot room initalSync for non-world_readable rooms Real non-joined users cannot room initalSync for non-world_readable rooms
Push rules come down in an initial /sync Push rules come down in an initial /sync
Regular users can add and delete aliases in the default room configuration
Regular users can add and delete aliases when m.room.aliases is restricted
GET /r0/capabilities is not public