diff options
author | Kegsay <kegan@matrix.org> | 2020-07-03 17:24:51 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-03 17:24:51 +0100 |
commit | ea9df46c70a1b806c11198a1272aac99dc8b62f4 (patch) | |
tree | a2851f4702bbd58dd8fc9c3ba614bd8a61a8a227 /clientapi | |
parent | 46dbc46f84a7120bb418ec48ddc1d6248ef97979 (diff) |
Implement local redaction (#1182)
* Create redaction events and apply checks (but do not send them)
* Send redactions to the roomserver
* Linting
* Slightly better wording
Diffstat (limited to 'clientapi')
-rw-r--r-- | clientapi/routing/createroom.go | 2 | ||||
-rw-r--r-- | clientapi/routing/membership.go | 6 | ||||
-rw-r--r-- | clientapi/routing/redaction.go | 136 | ||||
-rw-r--r-- | clientapi/routing/routing.go | 9 | ||||
-rw-r--r-- | clientapi/routing/sendevent.go | 4 |
5 files changed, 151 insertions, 6 deletions
diff --git a/clientapi/routing/createroom.go b/clientapi/routing/createroom.go index b6a5d122..027a21e7 100644 --- a/clientapi/routing/createroom.go +++ b/clientapi/routing/createroom.go @@ -382,7 +382,7 @@ func createRoom( continue } // Build some stripped state for the invite. - candidates := append(gomatrixserverlib.UnwrapEventHeaders(builtEvents), *inviteEvent) + candidates := append(gomatrixserverlib.UnwrapEventHeaders(builtEvents), inviteEvent.Event) var strippedState []gomatrixserverlib.InviteV2StrippedState for _, event := range candidates { switch event.Type() { diff --git a/clientapi/routing/membership.go b/clientapi/routing/membership.go index c2145159..a9a8fa00 100644 --- a/clientapi/routing/membership.go +++ b/clientapi/routing/membership.go @@ -77,7 +77,7 @@ func sendMembership(ctx context.Context, accountDB accounts.Database, device *us _, err = roomserverAPI.SendEvents( ctx, rsAPI, - []gomatrixserverlib.HeaderedEvent{event.Headered(roomVer)}, + []gomatrixserverlib.HeaderedEvent{event.Event.Headered(roomVer)}, cfg.Matrix.ServerName, nil, ) @@ -210,7 +210,7 @@ func SendInvite( perr := roomserverAPI.SendInvite( req.Context(), rsAPI, - event.Headered(roomVer), + event.Event.Headered(roomVer), nil, // ask the roomserver to draw up invite room state for us cfg.Matrix.ServerName, nil, @@ -232,7 +232,7 @@ func buildMembershipEvent( membership, roomID string, isDirect bool, cfg *config.Dendrite, evTime time.Time, rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI, -) (*gomatrixserverlib.Event, error) { +) (*gomatrixserverlib.HeaderedEvent, error) { profile, err := loadProfile(ctx, targetUserID, cfg, accountDB, asAPI) if err != nil { return nil, err diff --git a/clientapi/routing/redaction.go b/clientapi/routing/redaction.go new file mode 100644 index 00000000..fd80e0ab --- /dev/null +++ b/clientapi/routing/redaction.go @@ -0,0 +1,136 @@ +// 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 ( + "context" + "net/http" + "time" + + "github.com/matrix-org/dendrite/clientapi/httputil" + "github.com/matrix-org/dendrite/clientapi/jsonerror" + currentstateAPI "github.com/matrix-org/dendrite/currentstateserver/api" + "github.com/matrix-org/dendrite/internal/config" + "github.com/matrix-org/dendrite/internal/eventutil" + "github.com/matrix-org/dendrite/roomserver/api" + roomserverAPI "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" +) + +type redactionContent struct { + Reason string `json:"reason"` +} + +type redactionResponse struct { + EventID string `json:"event_id"` +} + +func SendRedaction( + req *http.Request, device *userapi.Device, roomID, eventID string, cfg *config.Dendrite, + rsAPI roomserverAPI.RoomserverInternalAPI, stateAPI currentstateAPI.CurrentStateInternalAPI, +) util.JSONResponse { + resErr := checkMemberInRoom(req.Context(), stateAPI, device.UserID, roomID) + if resErr != nil { + return *resErr + } + + ev := roomserverAPI.GetEvent(req.Context(), rsAPI, eventID) + if ev == nil { + return util.JSONResponse{ + Code: 400, + JSON: jsonerror.NotFound("unknown event ID"), // TODO: is it ok to leak existence? + } + } + if ev.RoomID() != roomID { + return util.JSONResponse{ + Code: 400, + JSON: jsonerror.NotFound("cannot redact event in another room"), + } + } + + // "Users may redact their own events, and any user with a power level greater than or equal + // to the redact power level of the room may redact events there" + // https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid + allowedToRedact := ev.Sender() == device.UserID + if !allowedToRedact { + plEvent := currentstateAPI.GetEvent(req.Context(), stateAPI, roomID, gomatrixserverlib.StateKeyTuple{ + EventType: gomatrixserverlib.MRoomPowerLevels, + StateKey: "", + }) + if plEvent == nil { + return util.JSONResponse{ + Code: 403, + JSON: jsonerror.Forbidden("You don't have permission to redact this event, no power_levels event in this room."), + } + } + pl, err := plEvent.PowerLevels() + if err != nil { + return util.JSONResponse{ + Code: 403, + JSON: jsonerror.Forbidden( + "You don't have permission to redact this event, the power_levels event for this room is malformed so auth checks cannot be performed.", + ), + } + } + allowedToRedact = pl.UserLevel(device.UserID) >= pl.Redact + } + if !allowedToRedact { + return util.JSONResponse{ + Code: 403, + JSON: jsonerror.Forbidden("You don't have permission to redact this event, power level too low."), + } + } + + var r redactionContent + resErr = httputil.UnmarshalJSONRequest(req, &r) + if resErr != nil { + return *resErr + } + + // create the new event and set all the fields we can + builder := gomatrixserverlib.EventBuilder{ + Sender: device.UserID, + RoomID: roomID, + Type: gomatrixserverlib.MRoomRedaction, + Redacts: eventID, + } + err := builder.SetContent(r) + if err != nil { + util.GetLogger(req.Context()).WithError(err).Error("builder.SetContent failed") + return jsonerror.InternalServerError() + } + + var queryRes api.QueryLatestEventsAndStateResponse + e, err := eventutil.BuildEvent(req.Context(), &builder, cfg, time.Now(), rsAPI, &queryRes) + if err == eventutil.ErrRoomNoExists { + return util.JSONResponse{ + Code: http.StatusNotFound, + JSON: jsonerror.NotFound("Room does not exist"), + } + } + _, err = roomserverAPI.SendEvents(context.Background(), rsAPI, []gomatrixserverlib.HeaderedEvent{*e}, cfg.Matrix.ServerName, nil) + if err != nil { + util.GetLogger(req.Context()).WithError(err).Errorf("failed to SendEvents") + return jsonerror.InternalServerError() + } + return util.JSONResponse{ + Code: 200, + JSON: redactionResponse{ + EventID: e.EventID(), + }, + } +} diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index 754fbca8..fe4f1efa 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -338,6 +338,15 @@ func Setup( return SendTyping(req, device, vars["roomID"], vars["userID"], accountDB, eduAPI, stateAPI) }), ).Methods(http.MethodPut, http.MethodOptions) + r0mux.Handle("/rooms/{roomID}/redact/{eventID}", + httputil.MakeAuthAPI("rooms_redact", 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 SendRedaction(req, device, vars["roomID"], vars["eventID"], cfg, rsAPI, stateAPI) + }), + ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/sendToDevice/{eventType}/{txnID}", httputil.MakeAuthAPI("send_to_device", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { diff --git a/clientapi/routing/sendevent.go b/clientapi/routing/sendevent.go index aba5f0d5..bf32992f 100644 --- a/clientapi/routing/sendevent.go +++ b/clientapi/routing/sendevent.go @@ -180,11 +180,11 @@ func generateSendEvent( stateEvents[i] = &queryRes.StateEvents[i].Event } provider := gomatrixserverlib.NewAuthEvents(stateEvents) - if err = gomatrixserverlib.Allowed(*e, &provider); err != nil { + if err = gomatrixserverlib.Allowed(e.Event, &provider); err != nil { return nil, &util.JSONResponse{ Code: http.StatusForbidden, JSON: jsonerror.Forbidden(err.Error()), // TODO: Is this error string comprehensible to the client? } } - return e, nil + return &e.Event, nil } |