2020-07-03 11:24:51 -05:00
// 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"
2023-05-16 19:33:27 -05:00
"errors"
2020-07-03 11:24:51 -05:00
"net/http"
"time"
2022-10-17 07:48:35 -05:00
"github.com/matrix-org/gomatrixserverlib"
2023-04-19 09:50:33 -05:00
"github.com/matrix-org/gomatrixserverlib/spec"
2022-10-17 07:48:35 -05:00
"github.com/matrix-org/util"
2020-07-03 11:24:51 -05:00
"github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/internal/eventutil"
2022-04-26 03:28:41 -05:00
"github.com/matrix-org/dendrite/internal/transactions"
2020-07-03 11:24:51 -05:00
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
2023-04-27 06:54:20 -05:00
"github.com/matrix-org/dendrite/roomserver/types"
2020-12-02 11:41:00 -06:00
"github.com/matrix-org/dendrite/setup/config"
2020-07-03 11:24:51 -05:00
userapi "github.com/matrix-org/dendrite/userapi/api"
)
type redactionContent struct {
2023-09-27 01:27:08 -05:00
Reason string ` json:"reason" `
Redacts string ` json:"redacts" `
2020-07-03 11:24:51 -05:00
}
type redactionResponse struct {
EventID string ` json:"event_id" `
}
func SendRedaction (
2020-08-10 08:18:04 -05:00
req * http . Request , device * userapi . Device , roomID , eventID string , cfg * config . ClientAPI ,
2022-05-05 07:17:38 -05:00
rsAPI roomserverAPI . ClientRoomserverAPI ,
2022-04-26 03:28:41 -05:00
txnID * string ,
txnCache * transactions . Cache ,
2020-07-03 11:24:51 -05:00
) util . JSONResponse {
2023-06-12 06:19:25 -05:00
deviceUserID , userIDErr := spec . NewUserID ( device . UserID , true )
if userIDErr != nil {
return util . JSONResponse {
Code : http . StatusForbidden ,
JSON : spec . Forbidden ( "userID doesn't have power level to redact" ) ,
}
}
2023-06-14 09:23:46 -05:00
validRoomID , err := spec . NewRoomID ( roomID )
if err != nil {
return util . JSONResponse {
Code : http . StatusBadRequest ,
JSON : spec . BadJSON ( "RoomID is invalid" ) ,
}
}
senderID , queryErr := rsAPI . QuerySenderIDForUser ( req . Context ( ) , * validRoomID , * deviceUserID )
2023-06-12 06:19:25 -05:00
if queryErr != nil {
return util . JSONResponse {
Code : http . StatusForbidden ,
JSON : spec . Forbidden ( "userID doesn't have power level to redact" ) ,
}
}
resErr := checkMemberInRoom ( req . Context ( ) , rsAPI , * deviceUserID , roomID )
2020-07-03 11:24:51 -05:00
if resErr != nil {
return * resErr
}
2023-08-02 05:12:14 -05:00
// if user is member of room, and sender ID is nil, then this user doesn't have a pseudo ID for some reason,
// which is unexpected.
if senderID == nil {
util . GetLogger ( req . Context ( ) ) . WithField ( "userID" , * deviceUserID ) . WithField ( "roomID" , roomID ) . Error ( "missing sender ID for user, despite having membership" )
return util . JSONResponse {
Code : http . StatusInternalServerError ,
JSON : spec . Unknown ( "internal server error" ) ,
}
}
2022-04-26 03:28:41 -05:00
if txnID != nil {
// Try to fetch response from transactionsCache
2022-10-17 07:48:35 -05:00
if res , ok := txnCache . FetchTransaction ( device . AccessToken , * txnID , req . URL ) ; ok {
2022-04-26 03:28:41 -05:00
return * res
}
}
2023-03-01 10:06:47 -06:00
ev := roomserverAPI . GetEvent ( req . Context ( ) , rsAPI , roomID , eventID )
2020-07-03 11:24:51 -05:00
if ev == nil {
return util . JSONResponse {
Code : 400 ,
2023-05-09 17:46:49 -05:00
JSON : spec . NotFound ( "unknown event ID" ) , // TODO: is it ok to leak existence?
2020-07-03 11:24:51 -05:00
}
}
2023-09-15 09:39:06 -05:00
if ev . RoomID ( ) . String ( ) != roomID {
2020-07-03 11:24:51 -05:00
return util . JSONResponse {
Code : 400 ,
2023-05-09 17:46:49 -05:00
JSON : spec . NotFound ( "cannot redact event in another room" ) ,
2020-07-03 11:24:51 -05:00
}
}
// "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
2023-08-02 05:12:14 -05:00
allowedToRedact := ev . SenderID ( ) == * senderID
2020-07-03 11:24:51 -05:00
if ! allowedToRedact {
2020-09-04 06:30:56 -05:00
plEvent := roomserverAPI . GetStateEvent ( req . Context ( ) , rsAPI , roomID , gomatrixserverlib . StateKeyTuple {
2023-04-19 09:50:33 -05:00
EventType : spec . MRoomPowerLevels ,
2020-07-03 11:24:51 -05:00
StateKey : "" ,
} )
if plEvent == nil {
return util . JSONResponse {
Code : 403 ,
2023-05-09 17:46:49 -05:00
JSON : spec . Forbidden ( "You don't have permission to redact this event, no power_levels event in this room." ) ,
2020-07-03 11:24:51 -05:00
}
}
2023-06-14 09:23:46 -05:00
pl , plErr := plEvent . PowerLevels ( )
if plErr != nil {
2020-07-03 11:24:51 -05:00
return util . JSONResponse {
Code : 403 ,
2023-05-09 17:46:49 -05:00
JSON : spec . Forbidden (
2020-07-03 11:24:51 -05:00
"You don't have permission to redact this event, the power_levels event for this room is malformed so auth checks cannot be performed." ,
) ,
}
}
2023-08-02 05:12:14 -05:00
allowedToRedact = pl . UserLevel ( * senderID ) >= pl . Redact
2020-07-03 11:24:51 -05:00
}
if ! allowedToRedact {
return util . JSONResponse {
Code : 403 ,
2023-05-09 17:46:49 -05:00
JSON : spec . Forbidden ( "You don't have permission to redact this event, power level too low." ) ,
2020-07-03 11:24:51 -05:00
}
}
var r redactionContent
resErr = httputil . UnmarshalJSONRequest ( req , & r )
if resErr != nil {
return * resErr
}
// create the new event and set all the fields we can
2023-05-04 05:17:42 -05:00
proto := gomatrixserverlib . ProtoEvent {
2023-08-02 05:12:14 -05:00
SenderID : string ( * senderID ) ,
2023-06-07 12:14:35 -05:00
RoomID : roomID ,
Type : spec . MRoomRedaction ,
Redacts : eventID ,
2020-07-03 11:24:51 -05:00
}
2023-09-27 01:27:08 -05:00
// Room version 11 expects the "redacts" field on the
// content field, so add it here as well
r . Redacts = eventID
2023-06-14 09:23:46 -05:00
err = proto . SetContent ( r )
2020-07-03 11:24:51 -05:00
if err != nil {
2023-05-04 05:17:42 -05:00
util . GetLogger ( req . Context ( ) ) . WithError ( err ) . Error ( "proto.SetContent failed" )
2023-05-16 19:33:27 -05:00
return util . JSONResponse {
Code : http . StatusInternalServerError ,
JSON : spec . InternalServerError { } ,
}
2020-07-03 11:24:51 -05:00
}
2023-06-28 13:29:49 -05:00
identity , err := rsAPI . SigningIdentityFor ( req . Context ( ) , * validRoomID , * deviceUserID )
2022-11-15 09:05:23 -06:00
if err != nil {
2023-05-16 19:33:27 -05:00
return util . JSONResponse {
Code : http . StatusInternalServerError ,
JSON : spec . InternalServerError { } ,
}
2022-11-15 09:05:23 -06:00
}
2021-09-08 11:31:03 -05:00
var queryRes roomserverAPI . QueryLatestEventsAndStateResponse
2023-06-28 13:29:49 -05:00
e , err := eventutil . QueryAndBuildEvent ( req . Context ( ) , & proto , & identity , time . Now ( ) , rsAPI , & queryRes )
2023-05-16 19:33:27 -05:00
if errors . Is ( err , eventutil . ErrRoomNoExists { } ) {
2020-07-03 11:24:51 -05:00
return util . JSONResponse {
Code : http . StatusNotFound ,
2023-05-09 17:46:49 -05:00
JSON : spec . NotFound ( "Room does not exist" ) ,
2020-07-03 11:24:51 -05:00
}
}
2022-10-26 06:59:19 -05:00
domain := device . UserDomain ( )
2023-04-27 06:54:20 -05:00
if err = roomserverAPI . SendEvents ( context . Background ( ) , rsAPI , roomserverAPI . KindNew , [ ] * types . HeaderedEvent { e } , device . UserDomain ( ) , domain , domain , nil , false ) ; err != nil {
2020-07-03 11:24:51 -05:00
util . GetLogger ( req . Context ( ) ) . WithError ( err ) . Errorf ( "failed to SendEvents" )
2023-05-16 19:33:27 -05:00
return util . JSONResponse {
Code : http . StatusInternalServerError ,
JSON : spec . InternalServerError { } ,
}
2020-07-03 11:24:51 -05:00
}
2022-04-26 03:28:41 -05:00
res := util . JSONResponse {
2020-07-03 11:24:51 -05:00
Code : 200 ,
JSON : redactionResponse {
EventID : e . EventID ( ) ,
} ,
}
2022-04-26 03:28:41 -05:00
// Add response to transactionsCache
if txnID != nil {
2022-10-17 07:48:35 -05:00
txnCache . AddTransaction ( device . AccessToken , * txnID , req . URL , & res )
2022-04-26 03:28:41 -05:00
}
return res
2020-07-03 11:24:51 -05:00
}