Kick users on redaction of join events
This commit is contained in:
parent
641bac0ce5
commit
dcd28e3614
|
@ -400,13 +400,14 @@ func (r *Inputer) processRoomEvent(
|
|||
// if storing this event results in it being redacted then do so.
|
||||
// we do this after calculating state for this event as we may need to get power levels
|
||||
var (
|
||||
redactedEventID string
|
||||
redactionEvent gomatrixserverlib.PDU
|
||||
redactedEvent gomatrixserverlib.PDU
|
||||
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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -990,15 +990,15 @@ 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
|
||||
validated bool
|
||||
ignoreRedaction bool
|
||||
redactionEv, redactedEv *types.Event
|
||||
validated bool
|
||||
ignoreRedaction bool
|
||||
)
|
||||
|
||||
wErr := d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
|
@ -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 event’s sender is greater than or equal to the redact level.
|
||||
case sender1Domain != "" && sender2Domain != "" && sender1Domain == sender2Domain:
|
||||
// 2. The domain of the redaction event’s sender matches that of the original event’s 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.
|
||||
|
|
Loading…
Reference in a new issue