Kick users on redaction of join events

This commit is contained in:
Till Faelligen 2023-07-03 08:54:18 +02:00
parent 641bac0ce5
commit dcd28e3614
No known key found for this signature in database
GPG key ID: ACCDC9606D472758
5 changed files with 111 additions and 36 deletions

View file

@ -403,10 +403,11 @@ func (r *Inputer) processRoomEvent(
redactedEventID string
redactionEvent gomatrixserverlib.PDU
redactedEvent gomatrixserverlib.PDU
originalRedactedEvent gomatrixserverlib.PDU
)
if !isRejected && !isCreateEvent {
resolver := state.NewStateResolution(r.DB, roomInfo, r.Queryer)
redactionEvent, redactedEvent, err = r.DB.MaybeRedactEvent(ctx, roomInfo, eventNID, event, &resolver, r.Queryer)
redactionEvent, redactedEvent, originalRedactedEvent, err = r.DB.MaybeRedactEvent(ctx, roomInfo, eventNID, event, &resolver, r.Queryer)
if err != nil {
return err
}
@ -486,6 +487,12 @@ func (r *Inputer) processRoomEvent(
if err != nil {
return fmt.Errorf("r.WriteOutputEvents (redactions): %w", err)
}
// if we're in a pseudoID room, and we redacted a m.room.member event, also leave/kick the user
if event.Version() == gomatrixserverlib.RoomVersionPseudoIDs && redactedEvent.Type() == spec.MRoomMember {
if err = r.leavePseudoIDRoom(ctx, *validRoomID, originalRedactedEvent, redactionEvent); err != nil {
logrus.WithError(err).Error("failed to leave user after membership event redaction")
}
}
}
// If guest_access changed and is not can_join, kick all guest users.
@ -501,6 +508,73 @@ func (r *Inputer) processRoomEvent(
return nil
}
// leavePseudoIDRoom leaves/kicks a user in the event of a membership event redaction.
// TODO: This doesn't play well with users re-joining rooms, as in this case we have multiple join events with a mxid_mapping.
func (r *Inputer) leavePseudoIDRoom(ctx context.Context, roomID spec.RoomID, originalRedactedEvent, redactionEvent gomatrixserverlib.PDU) error {
var memberContent gomatrixserverlib.MemberContent
if err := json.Unmarshal(originalRedactedEvent.Content(), &memberContent); err != nil {
return err
}
if memberContent.Membership != spec.Join {
return nil
}
// no mxid_mapping, nothing to do
if memberContent.MXIDMapping == nil {
return nil
}
userID, err := r.Queryer.QueryUserIDForSender(ctx, roomID, redactionEvent.SenderID())
if err != nil {
return err
}
// We can only create the leave event on servers the redaction originated on.
// We are going to receive the leave event anyway.
if !r.Cfg.Matrix.IsLocalServerName(userID.Domain()) {
return nil
}
// check that the redacted event is the _current_ membership event
stateKey := originalRedactedEvent.StateKey()
signingIdentity, err := r.SigningIdentity(ctx, roomID, *userID)
if err != nil {
return err
}
fledglingEvent := &gomatrixserverlib.ProtoEvent{
RoomID: originalRedactedEvent.RoomID(),
Type: spec.MRoomMember,
StateKey: stateKey,
SenderID: string(redactionEvent.SenderID()),
}
if fledglingEvent.Content, err = json.Marshal(gomatrixserverlib.MemberContent{
Membership: spec.Leave,
}); err != nil {
return err
}
event, err := eventutil.QueryAndBuildEvent(ctx, fledglingEvent, &signingIdentity, time.Now(), r.Queryer, nil)
if err != nil {
return err
}
inputReq := &api.InputRoomEventsRequest{
InputRoomEvents: []api.InputRoomEvent{{
Kind: api.KindNew,
Event: event,
Origin: userID.Domain(),
SendAsServer: string(userID.Domain()),
}},
Asynchronous: true, // Needs to be async, as we otherwise create a deadlock
}
inputRes := &api.InputRoomEventsResponse{}
r.InputRoomEvents(ctx, inputReq, inputRes)
return nil
}
// handleRemoteRoomUpgrade updates published rooms and room aliases
func (r *Inputer) handleRemoteRoomUpgrade(ctx context.Context, event gomatrixserverlib.PDU) error {
oldRoomID := event.RoomID()

View file

@ -647,7 +647,7 @@ func persistEvents(ctx context.Context, db storage.Database, querier api.QuerySe
resolver := state.NewStateResolution(db, roomInfo, querier)
_, redactedEvent, err := db.MaybeRedactEvent(ctx, roomInfo, eventNID, ev, &resolver, querier)
_, redactedEvent, _, err := db.MaybeRedactEvent(ctx, roomInfo, eventNID, ev, &resolver, querier)
if err != nil {
logrus.WithError(err).WithField("event_id", ev.EventID()).Error("Failed to redact event")
continue

View file

@ -574,7 +574,7 @@ func TestRedaction(t *testing.T) {
err = updater.Commit()
assert.NoError(t, err)
_, redactedEvent, err := db.MaybeRedactEvent(ctx, roomInfo, eventNID, ev.PDU, &plResolver, &FakeQuerier{})
_, redactedEvent, _, err := db.MaybeRedactEvent(ctx, roomInfo, eventNID, ev.PDU, &plResolver, &FakeQuerier{})
assert.NoError(t, err)
if redactedEvent != nil {
assert.Equal(t, ev.Redacts(), redactedEvent.EventID())

View file

@ -190,9 +190,7 @@ type Database interface {
GetRoomVersion(ctx context.Context, roomID string) (gomatrixserverlib.RoomVersion, error)
GetOrCreateEventTypeNID(ctx context.Context, eventType string) (eventTypeNID types.EventTypeNID, err error)
GetOrCreateEventStateKeyNID(ctx context.Context, eventStateKey *string) (types.EventStateKeyNID, error)
MaybeRedactEvent(
ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver, querier api.QuerySenderIDAPI,
) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, error)
MaybeRedactEvent(ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver, querier api.QuerySenderIDAPI) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, gomatrixserverlib.PDU, error)
}
type UserRoomKeys interface {
@ -249,10 +247,8 @@ type EventDatabase interface {
EventIDs(ctx context.Context, eventNIDs []types.EventNID) (map[types.EventNID]string, error)
EventsFromIDs(ctx context.Context, roomInfo *types.RoomInfo, eventIDs []string) ([]types.Event, error)
Events(ctx context.Context, roomVersion gomatrixserverlib.RoomVersion, eventNIDs []types.EventNID) ([]types.Event, error)
// MaybeRedactEvent returns the redaction event and the redacted event if this call resulted in a redaction, else an error
// MaybeRedactEvent returns the redaction event, the redacted event and the event before redaction if this call resulted in a redaction, else an error
// (nil if there was nothing to do)
MaybeRedactEvent(
ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver, querier api.QuerySenderIDAPI,
) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, error)
MaybeRedactEvent(ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver, querier api.QuerySenderIDAPI) (redactionEvent, redactedEvent, originalRedactedEvent gomatrixserverlib.PDU, err error)
StoreEvent(ctx context.Context, event gomatrixserverlib.PDU, roomInfo *types.RoomInfo, eventTypeNID types.EventTypeNID, eventStateKeyNID types.EventStateKeyNID, authEventNIDs []types.EventNID, isRejected bool) (types.EventNID, types.StateAtEvent, error)
}

View file

@ -990,13 +990,13 @@ func extractRoomVersionFromCreateEvent(event gomatrixserverlib.PDU) (
// to cross-reference with other tables when loading.
//
// Returns the redaction event and the redacted event if this call resulted in a redaction.
// nolint: gocylo
func (d *EventDatabase) MaybeRedactEvent(
ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver,
querier api.QuerySenderIDAPI,
) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, error) {
) (redactionEvent, redactedEvent, originalRedactedEvent gomatrixserverlib.PDU, err error) {
var (
redactionEvent, redactedEvent *types.Event
err error
redactionEv, redactedEv *types.Event
validated bool
ignoreRedaction bool
)
@ -1019,42 +1019,42 @@ func (d *EventDatabase) MaybeRedactEvent(
}
}
redactionEvent, redactedEvent, validated, err = d.loadRedactionPair(ctx, txn, roomInfo, eventNID, event)
redactionEv, redactedEv, validated, err = d.loadRedactionPair(ctx, txn, roomInfo, eventNID, event)
switch {
case err != nil:
return fmt.Errorf("d.loadRedactionPair: %w", err)
case validated || redactedEvent == nil || redactionEvent == nil:
case validated || redactedEv == nil || redactionEv == nil:
// we've seen this redaction before or there is nothing to redact
return nil
case redactedEvent.RoomID() != redactionEvent.RoomID():
case redactedEv.RoomID() != redactionEv.RoomID():
// redactions across rooms aren't allowed
ignoreRedaction = true
return nil
}
var validRoomID *spec.RoomID
validRoomID, err = spec.NewRoomID(redactedEvent.RoomID())
validRoomID, err = spec.NewRoomID(redactedEv.RoomID())
if err != nil {
return err
}
sender1Domain := ""
sender1, err1 := querier.QueryUserIDForSender(ctx, *validRoomID, redactedEvent.SenderID())
sender1, err1 := querier.QueryUserIDForSender(ctx, *validRoomID, redactedEv.SenderID())
if err1 == nil {
sender1Domain = string(sender1.Domain())
}
sender2Domain := ""
sender2, err2 := querier.QueryUserIDForSender(ctx, *validRoomID, redactionEvent.SenderID())
sender2, err2 := querier.QueryUserIDForSender(ctx, *validRoomID, redactionEv.SenderID())
if err2 == nil {
sender2Domain = string(sender2.Domain())
}
var powerlevels *gomatrixserverlib.PowerLevelContent
powerlevels, err = plResolver.Resolve(ctx, redactionEvent.EventID())
powerlevels, err = plResolver.Resolve(ctx, redactionEv.EventID())
if err != nil {
return err
}
switch {
case powerlevels.UserLevel(redactionEvent.SenderID()) >= powerlevels.Redact:
case powerlevels.UserLevel(redactionEv.SenderID()) >= powerlevels.Redact:
// 1. The power level of the redaction events sender is greater than or equal to the redact level.
case sender1Domain != "" && sender2Domain != "" && sender1Domain == sender2Domain:
// 2. The domain of the redaction events sender matches that of the original events sender.
@ -1065,42 +1065,47 @@ func (d *EventDatabase) MaybeRedactEvent(
// mark the event as redacted
if redactionsArePermanent {
redactedEvent.Redact()
originalRedactedEvent, err = gomatrixserverlib.MustGetRoomVersion(redactedEv.Version()).
NewEventFromTrustedJSON(redactedEv.JSON(), false)
if err != nil {
return err
}
redactedEv.Redact()
}
err = redactedEvent.SetUnsignedField("redacted_because", redactionEvent)
err = redactedEv.SetUnsignedField("redacted_because", redactionEv)
if err != nil {
return fmt.Errorf("redactedEvent.SetUnsignedField: %w", err)
}
// NOTSPEC: sytest relies on this unspecced field existing :(
err = redactedEvent.SetUnsignedField("redacted_by", redactionEvent.EventID())
err = redactedEv.SetUnsignedField("redacted_by", redactionEv.EventID())
if err != nil {
return fmt.Errorf("redactedEvent.SetUnsignedField: %w", err)
}
// overwrite the eventJSON table
err = d.EventJSONTable.InsertEventJSON(ctx, txn, redactedEvent.EventNID, redactedEvent.JSON())
err = d.EventJSONTable.InsertEventJSON(ctx, txn, redactedEv.EventNID, redactedEv.JSON())
if err != nil {
return fmt.Errorf("d.EventJSONTable.InsertEventJSON: %w", err)
}
err = d.RedactionsTable.MarkRedactionValidated(ctx, txn, redactionEvent.EventID(), true)
err = d.RedactionsTable.MarkRedactionValidated(ctx, txn, redactionEv.EventID(), true)
if err != nil {
return fmt.Errorf("d.RedactionsTable.MarkRedactionValidated: %w", err)
}
// We remove the entry from the cache, as if we just "StoreRoomServerEvent", we can't be
// certain that the cached entry actually is updated, since ristretto is eventual-persistent.
d.Cache.InvalidateRoomServerEvent(redactedEvent.EventNID)
d.Cache.InvalidateRoomServerEvent(redactedEv.EventNID)
return nil
})
if wErr != nil {
return nil, nil, err
return nil, nil, nil, err
}
if ignoreRedaction || redactionEvent == nil || redactedEvent == nil {
return nil, nil, nil
if ignoreRedaction || redactionEv == nil || redactedEv == nil {
return nil, nil, nil, nil
}
return redactionEvent.PDU, redactedEvent.PDU, nil
return redactionEv.PDU, redactedEv.PDU, originalRedactedEvent, nil
}
// loadRedactionPair returns both the redaction event and the redacted event, else nil.