Various alias fixes (#1934)

* Generate m.room.canonical_alias instead of legacy m.room.aliases

* Add omitempty tags

* Add aliases endpoint to client API

* Check power levels when setting aliases

* Don't return null on /aliases

* Don't return error if the state event fails

* Update sytest-whitelist

* Don't send updated m.room.canonical_alias events

* Don't check PLs after all because for local aliases they are apparently irrelevant

* Fix some bugs

* Allow deleting a local alias with enough PL

* Fix some more bugs

* Update sytest-whitelist

* Fix copyright notices

* Review comments
This commit is contained in:
Neil Alexander 2021-07-21 16:53:50 +01:00 committed by GitHub
parent f0f8c7f055
commit c1447a58e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 174 additions and 159 deletions

View file

@ -0,0 +1,96 @@
// Copyright 2021 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 (
"fmt"
"net/http"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/roomserver/api"
userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
)
// GetAliases implements GET /_matrix/client/r0/rooms/{roomId}/aliases
func GetAliases(
req *http.Request, rsAPI api.RoomserverInternalAPI, device *userapi.Device, roomID string,
) util.JSONResponse {
stateTuple := gomatrixserverlib.StateKeyTuple{
EventType: gomatrixserverlib.MRoomHistoryVisibility,
StateKey: "",
}
stateReq := &api.QueryCurrentStateRequest{
RoomID: roomID,
StateTuples: []gomatrixserverlib.StateKeyTuple{stateTuple},
}
stateRes := &api.QueryCurrentStateResponse{}
if err := rsAPI.QueryCurrentState(req.Context(), stateReq, stateRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryCurrentState failed")
return util.ErrorResponse(fmt.Errorf("rsAPI.QueryCurrentState: %w", err))
}
visibility := "invite"
if historyVisEvent, ok := stateRes.StateEvents[stateTuple]; ok {
var err error
visibility, err = historyVisEvent.HistoryVisibility()
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("historyVisEvent.HistoryVisibility failed")
return util.ErrorResponse(fmt.Errorf("historyVisEvent.HistoryVisibility: %w", err))
}
}
if visibility != gomatrixserverlib.WorldReadable {
queryReq := api.QueryMembershipForUserRequest{
RoomID: roomID,
UserID: device.UserID,
}
var queryRes api.QueryMembershipForUserResponse
if err := rsAPI.QueryMembershipForUser(req.Context(), &queryReq, &queryRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryMembershipsForRoom failed")
return jsonerror.InternalServerError()
}
if !queryRes.IsInRoom {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("You aren't a member of this room."),
}
}
}
aliasesReq := api.GetAliasesForRoomIDRequest{
RoomID: roomID,
}
aliasesRes := api.GetAliasesForRoomIDResponse{}
if err := rsAPI.GetAliasesForRoomID(req.Context(), &aliasesReq, &aliasesRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.GetAliasesForRoomID failed")
return util.ErrorResponse(fmt.Errorf("rsAPI.GetAliasesForRoomID: %w", err))
}
response := struct {
Aliases []string `json:"aliases"`
}{
Aliases: aliasesRes.Aliases,
}
if response.Aliases == nil {
response.Aliases = []string{} // pleases sytest
}
return util.JSONResponse{
Code: 200,
JSON: response,
}
}

View file

@ -383,7 +383,6 @@ func createRoom(
// 10- m.room.topic (opt)
// 11- invite events (opt) - with is_direct flag if applicable TODO
// 12- 3pid invite events (opt) TODO
// 13- m.room.aliases event for HS (if alias specified) TODO
// This differs from Synapse slightly. Synapse would vary the ordering of 3-7
// depending on if those events were in "initial_state" or not. This made it
// harder to reason about, hence sticking to a strict static ordering.
@ -404,7 +403,6 @@ func createRoom(
if aliasEvent != nil {
// TODO: bit of a chicken and egg problem here as the alias doesn't exist and cannot until we have made the room.
// This means we might fail creating the alias but say the canonical alias is something that doesn't exist.
// m.room.aliases is handled when we call roomserver.SetRoomAlias
eventsToMake = append(eventsToMake, *aliasEvent)
}

View file

@ -113,13 +113,12 @@ func DirectoryRoom(
}
// SetLocalAlias implements PUT /directory/room/{roomAlias}
// TODO: Check if the user has the power level to set an alias
func SetLocalAlias(
req *http.Request,
device *api.Device,
alias string,
cfg *config.ClientAPI,
aliasAPI roomserverAPI.RoomserverInternalAPI,
rsAPI roomserverAPI.RoomserverInternalAPI,
) util.JSONResponse {
_, domain, err := gomatrixserverlib.SplitID('#', alias)
if err != nil {
@ -172,7 +171,7 @@ func SetLocalAlias(
Alias: alias,
}
var queryRes roomserverAPI.SetRoomAliasResponse
if err := aliasAPI.SetRoomAlias(req.Context(), &queryReq, &queryRes); err != nil {
if err := rsAPI.SetRoomAlias(req.Context(), &queryReq, &queryRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("aliasAPI.SetRoomAlias failed")
return jsonerror.InternalServerError()
}
@ -195,43 +194,32 @@ func RemoveLocalAlias(
req *http.Request,
device *api.Device,
alias string,
aliasAPI roomserverAPI.RoomserverInternalAPI,
rsAPI roomserverAPI.RoomserverInternalAPI,
) util.JSONResponse {
creatorQueryReq := roomserverAPI.GetCreatorIDForAliasRequest{
Alias: alias,
}
var creatorQueryRes roomserverAPI.GetCreatorIDForAliasResponse
if err := aliasAPI.GetCreatorIDForAlias(req.Context(), &creatorQueryReq, &creatorQueryRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("aliasAPI.GetCreatorIDForAlias failed")
return jsonerror.InternalServerError()
}
if creatorQueryRes.UserID == "" {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: jsonerror.NotFound("Alias does not exist"),
}
}
if creatorQueryRes.UserID != device.UserID {
// TODO: Still allow deletion if user is admin
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("You do not have permission to delete this alias"),
}
}
queryReq := roomserverAPI.RemoveRoomAliasRequest{
Alias: alias,
UserID: device.UserID,
}
var queryRes roomserverAPI.RemoveRoomAliasResponse
if err := aliasAPI.RemoveRoomAlias(req.Context(), &queryReq, &queryRes); err != nil {
if err := rsAPI.RemoveRoomAlias(req.Context(), &queryReq, &queryRes); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("aliasAPI.RemoveRoomAlias failed")
return jsonerror.InternalServerError()
}
if !queryRes.Found {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: jsonerror.NotFound("The alias does not exist."),
}
}
if !queryRes.Removed {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("You do not have permission to remove this alias."),
}
}
return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
@ -294,9 +282,9 @@ func SetVisibility(
return jsonerror.InternalServerError()
}
// NOTSPEC: Check if the user's power is greater than power required to change m.room.aliases event
// NOTSPEC: Check if the user's power is greater than power required to change m.room.canonical_alias event
power, _ := gomatrixserverlib.NewPowerLevelContentFromEvent(queryEventsRes.StateEvents[0].Event)
if power.UserLevel(dev.UserID) < power.EventLevel(gomatrixserverlib.MRoomAliases, true) {
if power.UserLevel(dev.UserID) < power.EventLevel(gomatrixserverlib.MRoomCanonicalAlias, true) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("userID doesn't have power level to change visibility"),

View file

@ -275,6 +275,14 @@ func Setup(
return OnIncomingStateRequest(req.Context(), device, rsAPI, vars["roomID"])
})).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/rooms/{roomID}/aliases", httputil.MakeAuthAPI("aliases", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
if err != nil {
return util.ErrorResponse(err)
}
return GetAliases(req, rsAPI, device, vars["roomID"])
})).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/rooms/{roomID}/state/{type:[^/]+/?}", httputil.MakeAuthAPI("room_state", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
if err != nil {