Merge branch 'master' of https://github.com/matrix-org/dendrite into add-nats-support

This commit is contained in:
Till Faelligen 2021-07-24 11:27:24 +02:00
commit a833f5764a
48 changed files with 700 additions and 298 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

@ -43,7 +43,7 @@ type createRoomRequest struct {
Visibility string `json:"visibility"`
Topic string `json:"topic"`
Preset string `json:"preset"`
CreationContent map[string]interface{} `json:"creation_content"`
CreationContent json.RawMessage `json:"creation_content"`
InitialState []fledglingEvent `json:"initial_state"`
RoomAliasName string `json:"room_alias_name"`
GuestCanJoin bool `json:"guest_can_join"`
@ -177,11 +177,6 @@ func createRoom(
// Clobber keys: creator, room_version
if r.CreationContent == nil {
r.CreationContent = make(map[string]interface{}, 2)
}
r.CreationContent["creator"] = userID
roomVersion := roomserverVersion.DefaultRoomVersion()
if r.RoomVersion != "" {
candidateVersion := gomatrixserverlib.RoomVersion(r.RoomVersion)
@ -194,7 +189,6 @@ func createRoom(
}
roomVersion = candidateVersion
}
r.CreationContent["room_version"] = roomVersion
// TODO: visibility/presets/raw initial state
// TODO: Create room alias association
@ -203,7 +197,7 @@ func createRoom(
logger.WithFields(log.Fields{
"userID": userID,
"roomID": roomID,
"roomVersion": r.CreationContent["room_version"],
"roomVersion": roomVersion,
}).Info("Creating new room")
profile, err := appserviceAPI.RetrieveUserProfile(req.Context(), userID, asAPI, accountDB)
@ -212,6 +206,109 @@ func createRoom(
return jsonerror.InternalServerError()
}
createContent := map[string]interface{}{}
if len(r.CreationContent) > 0 {
if err = json.Unmarshal(r.CreationContent, &createContent); err != nil {
util.GetLogger(req.Context()).WithError(err).Error("json.Unmarshal for creation_content failed")
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("invalid create content"),
}
}
}
createContent["creator"] = userID
createContent["room_version"] = roomVersion
powerLevelContent := eventutil.InitialPowerLevelsContent(userID)
joinRuleContent := gomatrixserverlib.JoinRuleContent{
JoinRule: gomatrixserverlib.Invite,
}
historyVisibilityContent := gomatrixserverlib.HistoryVisibilityContent{
HistoryVisibility: historyVisibilityShared,
}
if r.PowerLevelContentOverride != nil {
// Merge powerLevelContentOverride fields by unmarshalling it atop the defaults
err = json.Unmarshal(r.PowerLevelContentOverride, &powerLevelContent)
if err != nil {
util.GetLogger(req.Context()).WithError(err).Error("json.Unmarshal for power_level_content_override failed")
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("malformed power_level_content_override"),
}
}
}
switch r.Preset {
case presetPrivateChat:
joinRuleContent.JoinRule = gomatrixserverlib.Invite
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
case presetTrustedPrivateChat:
joinRuleContent.JoinRule = gomatrixserverlib.Invite
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
// TODO If trusted_private_chat, all invitees are given the same power level as the room creator.
case presetPublicChat:
joinRuleContent.JoinRule = gomatrixserverlib.Public
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
}
createEvent := fledglingEvent{
Type: gomatrixserverlib.MRoomCreate,
Content: createContent,
}
powerLevelEvent := fledglingEvent{
Type: gomatrixserverlib.MRoomPowerLevels,
Content: powerLevelContent,
}
joinRuleEvent := fledglingEvent{
Type: gomatrixserverlib.MRoomJoinRules,
Content: joinRuleContent,
}
historyVisibilityEvent := fledglingEvent{
Type: gomatrixserverlib.MRoomHistoryVisibility,
Content: historyVisibilityContent,
}
membershipEvent := fledglingEvent{
Type: gomatrixserverlib.MRoomMember,
StateKey: userID,
Content: gomatrixserverlib.MemberContent{
Membership: gomatrixserverlib.Join,
DisplayName: profile.DisplayName,
AvatarURL: profile.AvatarURL,
},
}
var nameEvent *fledglingEvent
var topicEvent *fledglingEvent
var guestAccessEvent *fledglingEvent
var aliasEvent *fledglingEvent
if r.Name != "" {
nameEvent = &fledglingEvent{
Type: gomatrixserverlib.MRoomName,
Content: eventutil.NameContent{
Name: r.Name,
},
}
}
if r.Topic != "" {
topicEvent = &fledglingEvent{
Type: gomatrixserverlib.MRoomTopic,
Content: eventutil.TopicContent{
Topic: r.Topic,
},
}
}
if r.GuestCanJoin {
guestAccessEvent = &fledglingEvent{
Type: gomatrixserverlib.MRoomGuestAccess,
Content: eventutil.GuestAccessContent{
GuestAccess: "can_join",
},
}
}
var roomAlias string
if r.RoomAliasName != "" {
roomAlias = fmt.Sprintf("#%s:%s", r.RoomAliasName, cfg.Matrix.ServerName)
@ -230,44 +327,46 @@ func createRoom(
if aliasResp.RoomID != "" {
return util.MessageResponse(400, "Alias already exists")
}
aliasEvent = &fledglingEvent{
Type: gomatrixserverlib.MRoomCanonicalAlias,
Content: eventutil.CanonicalAlias{
Alias: roomAlias,
},
}
}
membershipContent := gomatrixserverlib.MemberContent{
Membership: gomatrixserverlib.Join,
DisplayName: profile.DisplayName,
AvatarURL: profile.AvatarURL,
}
var initialStateEvents []fledglingEvent
for i := range r.InitialState {
if r.InitialState[i].StateKey != "" {
initialStateEvents = append(initialStateEvents, r.InitialState[i])
continue
}
var joinRules, historyVisibility string
switch r.Preset {
case presetPrivateChat:
joinRules = gomatrixserverlib.Invite
historyVisibility = historyVisibilityShared
case presetTrustedPrivateChat:
joinRules = gomatrixserverlib.Invite
historyVisibility = historyVisibilityShared
// TODO If trusted_private_chat, all invitees are given the same power level as the room creator.
case presetPublicChat:
joinRules = gomatrixserverlib.Public
historyVisibility = historyVisibilityShared
default:
// Default room rules, r.Preset was previously checked for valid values so
// only a request with no preset should end up here.
joinRules = gomatrixserverlib.Invite
historyVisibility = historyVisibilityShared
}
switch r.InitialState[i].Type {
case gomatrixserverlib.MRoomCreate:
continue
var builtEvents []*gomatrixserverlib.HeaderedEvent
case gomatrixserverlib.MRoomPowerLevels:
powerLevelEvent = r.InitialState[i]
powerLevelContent := eventutil.InitialPowerLevelsContent(userID)
if r.PowerLevelContentOverride != nil {
// Merge powerLevelContentOverride fields by unmarshalling it atop the defaults
err = json.Unmarshal(r.PowerLevelContentOverride, &powerLevelContent)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("malformed power_level_content_override"),
}
case gomatrixserverlib.MRoomJoinRules:
joinRuleEvent = r.InitialState[i]
case gomatrixserverlib.MRoomHistoryVisibility:
historyVisibilityEvent = r.InitialState[i]
case gomatrixserverlib.MRoomGuestAccess:
guestAccessEvent = &r.InitialState[i]
case gomatrixserverlib.MRoomName:
nameEvent = &r.InitialState[i]
case gomatrixserverlib.MRoomTopic:
topicEvent = &r.InitialState[i]
default:
initialStateEvents = append(initialStateEvents, r.InitialState[i])
}
}
@ -284,37 +383,33 @@ 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.
// TODO: Synapse has txn/token ID on each event. Do we need to do this here?
eventsToMake := []fledglingEvent{
{"m.room.create", "", r.CreationContent},
{"m.room.member", userID, membershipContent},
{"m.room.power_levels", "", powerLevelContent},
{"m.room.join_rules", "", gomatrixserverlib.JoinRuleContent{JoinRule: joinRules}},
{"m.room.history_visibility", "", eventutil.HistoryVisibilityContent{HistoryVisibility: historyVisibility}},
createEvent, membershipEvent, powerLevelEvent, joinRuleEvent, historyVisibilityEvent,
}
if roomAlias != "" {
if guestAccessEvent != nil {
eventsToMake = append(eventsToMake, *guestAccessEvent)
}
eventsToMake = append(eventsToMake, initialStateEvents...)
if nameEvent != nil {
eventsToMake = append(eventsToMake, *nameEvent)
}
if topicEvent != nil {
eventsToMake = append(eventsToMake, *topicEvent)
}
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, fledglingEvent{"m.room.canonical_alias", "", eventutil.CanonicalAlias{Alias: roomAlias}})
}
if r.GuestCanJoin {
eventsToMake = append(eventsToMake, fledglingEvent{"m.room.guest_access", "", eventutil.GuestAccessContent{GuestAccess: "can_join"}})
}
eventsToMake = append(eventsToMake, r.InitialState...)
if r.Name != "" {
eventsToMake = append(eventsToMake, fledglingEvent{"m.room.name", "", eventutil.NameContent{Name: r.Name}})
}
if r.Topic != "" {
eventsToMake = append(eventsToMake, fledglingEvent{"m.room.topic", "", eventutil.TopicContent{Topic: r.Topic}})
eventsToMake = append(eventsToMake, *aliasEvent)
}
// TODO: invite events
// TODO: 3pid invite events
var builtEvents []*gomatrixserverlib.HeaderedEvent
authEvents := gomatrixserverlib.NewAuthEvents(nil)
for i, e := range eventsToMake {
depth := i + 1 // depth starts at 1
@ -403,7 +498,7 @@ func createRoom(
fallthrough
case gomatrixserverlib.MRoomCanonicalAlias:
fallthrough
case "m.room.encryption": // TODO: move this to gmsl
case gomatrixserverlib.MRoomEncryption:
fallthrough
case gomatrixserverlib.MRoomMember:
fallthrough

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 {