From b544169cf9ae176c153597aa6f9ae69f469014bf Mon Sep 17 00:00:00 2001 From: Till Faelligen Date: Tue, 1 Mar 2022 13:27:14 +0100 Subject: [PATCH] Add canonical support --- clientapi/jsonerror/jsonerror.go | 5 +++ clientapi/routing/sendevent.go | 36 +++++++++++++++++ roomserver/api/alias.go | 26 ++++++++++++ roomserver/internal/alias.go | 68 ++++++++++++++++++++++++++++++-- sytest-whitelist | 5 ++- 5 files changed, 136 insertions(+), 4 deletions(-) diff --git a/clientapi/jsonerror/jsonerror.go b/clientapi/jsonerror/jsonerror.go index 97c597030..1fc1c0c01 100644 --- a/clientapi/jsonerror/jsonerror.go +++ b/clientapi/jsonerror/jsonerror.go @@ -58,6 +58,11 @@ func BadJSON(msg string) *MatrixError { return &MatrixError{"M_BAD_JSON", msg} } +// BadAlias is an error when the client supplies a bad alias. +func BadAlias(msg string) *MatrixError { + return &MatrixError{"M_BAD_ALIAS", msg} +} + // NotJSON is an error when the client supplies something that is not JSON // to a JSON endpoint. func NotJSON(msg string) *MatrixError { diff --git a/clientapi/routing/sendevent.go b/clientapi/routing/sendevent.go index 23935b5d9..359446aa8 100644 --- a/clientapi/routing/sendevent.go +++ b/clientapi/routing/sendevent.go @@ -16,6 +16,8 @@ package routing import ( "context" + "encoding/json" + "fmt" "net/http" "sync" "time" @@ -120,6 +122,40 @@ func SendEvent( } timeToGenerateEvent := time.Since(startedGeneratingEvent) + // validate that the aliases exists + if eventType == gomatrixserverlib.MRoomCanonicalAlias { + aliasReq := api.AliasEvent{} + if err = json.Unmarshal(e.Content(), &aliasReq); err != nil { + return util.ErrorResponse(fmt.Errorf("unable to parse alias event: %w", err)) + } + if !aliasReq.Valid() { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.InvalidParam("Request contains invalid aliases."), + } + } + aliasRes := &api.GetAliasesForRoomIDResponse{} + if err = rsAPI.GetAliasesForRoomID(req.Context(), &api.GetAliasesForRoomIDRequest{RoomID: roomID}, aliasRes); err != nil { + return jsonerror.InternalServerError() + } + var found int + requestAliases := append(aliasReq.AltAliases, aliasReq.Alias) + for _, alias := range aliasRes.Aliases { + for _, altAlias := range requestAliases { + if altAlias == alias { + found++ + } + } + } + // check that we found at least the same amount of existing aliases as are in the request + if aliasReq.Alias != "" && found < len(requestAliases) { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.BadAlias("No matching alias found."), + } + } + } + var txnAndSessionID *api.TransactionID if txnID != nil { txnAndSessionID = &api.TransactionID{ diff --git a/roomserver/api/alias.go b/roomserver/api/alias.go index df69e5b4d..49f05053e 100644 --- a/roomserver/api/alias.go +++ b/roomserver/api/alias.go @@ -14,6 +14,8 @@ package api +import "regexp" + // SetRoomAliasRequest is a request to SetRoomAlias type SetRoomAliasRequest struct { // ID of the user setting the alias @@ -84,3 +86,27 @@ type RemoveRoomAliasResponse struct { // Did we remove it? Removed bool `json:"removed"` } + +type AliasEvent struct { + Alias string `json:"alias"` + AltAliases []string `json:"alt_aliases"` +} + +var validateAliasRegex = regexp.MustCompile("^#.*:.+$") + +func (a AliasEvent) Valid() bool { + // alias is set to be removed + if a.Alias == "" { + return true + } + if !validateAliasRegex.MatchString(a.Alias) { + return false + } + for _, alias := range a.AltAliases { + if !validateAliasRegex.MatchString(alias) { + return false + } + } + return true +} + diff --git a/roomserver/internal/alias.go b/roomserver/internal/alias.go index 7995279d2..91ae85b3a 100644 --- a/roomserver/internal/alias.go +++ b/roomserver/internal/alias.go @@ -16,12 +16,15 @@ package internal import ( "context" + "database/sql" "fmt" - - "github.com/matrix-org/dendrite/roomserver/api" - "github.com/matrix-org/gomatrixserverlib" + "time" asAPI "github.com/matrix-org/dendrite/appservice/api" + "github.com/matrix-org/dendrite/roomserver/api" + "github.com/matrix-org/gomatrixserverlib" + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" ) // RoomserverInternalAPIDatabase has the storage APIs needed to implement the alias API. @@ -183,6 +186,65 @@ func (r *RoomserverInternalAPI) RemoveRoomAlias( } } + ev, err := r.DB.GetStateEvent(ctx, roomID, gomatrixserverlib.MRoomCanonicalAlias, "") + if err != nil && err != sql.ErrNoRows { + return err + } + + stateAlias := gjson.GetBytes(ev.Content(), "alias").Str + // the alias to remove is currently set as the canonical alias, remove it + if stateAlias == request.Alias { + res, err := sjson.DeleteBytes(ev.Content(), "alias") + if err != nil { + return err + } + + stateRes := &api.QueryLatestEventsAndStateResponse{} + + if err = r.QueryLatestEventsAndState(ctx, &api.QueryLatestEventsAndStateRequest{RoomID: roomID}, stateRes); err != nil { + return err + } + + stateEventIDs := make([]string, len(stateRes.StateEvents)) + for i, ev := range stateRes.StateEvents { + stateEventIDs[i] = ev.EventID() + } + + sender := request.UserID + if request.UserID != ev.Sender() { + sender = ev.Sender() + } + builder := &gomatrixserverlib.EventBuilder{ + Sender: sender, + RoomID: ev.RoomID(), + Type: ev.Type(), + StateKey: ev.StateKey(), + PrevEvents: []string{ev.EventID()}, + AuthEvents: stateEventIDs, + Content: res, + } + + newEvent, err := builder.Build(time.Now(), r.ServerName, r.Cfg.Matrix.KeyID, r.Cfg.Matrix.PrivateKey, ev.RoomVersion) + if err != nil { + return err + } + + inputEvent := api.InputRoomEvent{ + Kind: api.KindNew, + Event: newEvent.Headered(ev.RoomVersion), + Origin: r.ServerName, + StateEventIDs: stateEventIDs, + HasState: true, + SendAsServer: string(r.ServerName), + } + + respInput := &api.InputRoomEventsResponse{} + r.InputRoomEvents(ctx, &api.InputRoomEventsRequest{InputRoomEvents: []api.InputRoomEvent{inputEvent}}, respInput) + if respInput.NotAllowed || respInput.ErrMsg != "" { + return fmt.Errorf(respInput.ErrMsg) + } + } + // Remove the alias from the database if err := r.DB.RemoveRoomAlias(ctx, request.Alias); err != nil { return err diff --git a/sytest-whitelist b/sytest-whitelist index 12522cfb3..dc83b9e9b 100644 --- a/sytest-whitelist +++ b/sytest-whitelist @@ -604,4 +604,7 @@ Remote banned user is kicked and may not rejoin until unbanned registration remembers parameters registration accepts non-ascii passwords registration with inhibit_login inhibits login - +Canonical alias can be set +Canonical alias can include alt_aliases +Can delete canonical alias +Multiple calls to /sync should not cause 500 errors