Kick users on redaction of join events
This commit is contained in:
parent
641bac0ce5
commit
dcd28e3614
|
@ -403,10 +403,11 @@ func (r *Inputer) processRoomEvent(
|
||||||
redactedEventID string
|
redactedEventID string
|
||||||
redactionEvent gomatrixserverlib.PDU
|
redactionEvent gomatrixserverlib.PDU
|
||||||
redactedEvent gomatrixserverlib.PDU
|
redactedEvent gomatrixserverlib.PDU
|
||||||
|
originalRedactedEvent gomatrixserverlib.PDU
|
||||||
)
|
)
|
||||||
if !isRejected && !isCreateEvent {
|
if !isRejected && !isCreateEvent {
|
||||||
resolver := state.NewStateResolution(r.DB, roomInfo, r.Queryer)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -486,6 +487,12 @@ func (r *Inputer) processRoomEvent(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("r.WriteOutputEvents (redactions): %w", err)
|
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.
|
// If guest_access changed and is not can_join, kick all guest users.
|
||||||
|
@ -501,6 +508,73 @@ func (r *Inputer) processRoomEvent(
|
||||||
return nil
|
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
|
// handleRemoteRoomUpgrade updates published rooms and room aliases
|
||||||
func (r *Inputer) handleRemoteRoomUpgrade(ctx context.Context, event gomatrixserverlib.PDU) error {
|
func (r *Inputer) handleRemoteRoomUpgrade(ctx context.Context, event gomatrixserverlib.PDU) error {
|
||||||
oldRoomID := event.RoomID()
|
oldRoomID := event.RoomID()
|
||||||
|
|
|
@ -647,7 +647,7 @@ func persistEvents(ctx context.Context, db storage.Database, querier api.QuerySe
|
||||||
|
|
||||||
resolver := state.NewStateResolution(db, roomInfo, querier)
|
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 {
|
if err != nil {
|
||||||
logrus.WithError(err).WithField("event_id", ev.EventID()).Error("Failed to redact event")
|
logrus.WithError(err).WithField("event_id", ev.EventID()).Error("Failed to redact event")
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -574,7 +574,7 @@ func TestRedaction(t *testing.T) {
|
||||||
err = updater.Commit()
|
err = updater.Commit()
|
||||||
assert.NoError(t, err)
|
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)
|
assert.NoError(t, err)
|
||||||
if redactedEvent != nil {
|
if redactedEvent != nil {
|
||||||
assert.Equal(t, ev.Redacts(), redactedEvent.EventID())
|
assert.Equal(t, ev.Redacts(), redactedEvent.EventID())
|
||||||
|
|
|
@ -190,9 +190,7 @@ type Database interface {
|
||||||
GetRoomVersion(ctx context.Context, roomID string) (gomatrixserverlib.RoomVersion, error)
|
GetRoomVersion(ctx context.Context, roomID string) (gomatrixserverlib.RoomVersion, error)
|
||||||
GetOrCreateEventTypeNID(ctx context.Context, eventType string) (eventTypeNID types.EventTypeNID, err error)
|
GetOrCreateEventTypeNID(ctx context.Context, eventType string) (eventTypeNID types.EventTypeNID, err error)
|
||||||
GetOrCreateEventStateKeyNID(ctx context.Context, eventStateKey *string) (types.EventStateKeyNID, error)
|
GetOrCreateEventStateKeyNID(ctx context.Context, eventStateKey *string) (types.EventStateKeyNID, error)
|
||||||
MaybeRedactEvent(
|
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)
|
||||||
ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver, querier api.QuerySenderIDAPI,
|
|
||||||
) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserRoomKeys interface {
|
type UserRoomKeys interface {
|
||||||
|
@ -249,10 +247,8 @@ type EventDatabase interface {
|
||||||
EventIDs(ctx context.Context, eventNIDs []types.EventNID) (map[types.EventNID]string, error)
|
EventIDs(ctx context.Context, eventNIDs []types.EventNID) (map[types.EventNID]string, error)
|
||||||
EventsFromIDs(ctx context.Context, roomInfo *types.RoomInfo, eventIDs []string) ([]types.Event, 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)
|
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)
|
// (nil if there was nothing to do)
|
||||||
MaybeRedactEvent(
|
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)
|
||||||
ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver, querier api.QuerySenderIDAPI,
|
|
||||||
) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, 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)
|
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,13 +990,13 @@ func extractRoomVersionFromCreateEvent(event gomatrixserverlib.PDU) (
|
||||||
// to cross-reference with other tables when loading.
|
// to cross-reference with other tables when loading.
|
||||||
//
|
//
|
||||||
// Returns the redaction event and the redacted event if this call resulted in a redaction.
|
// Returns the redaction event and the redacted event if this call resulted in a redaction.
|
||||||
|
// nolint: gocylo
|
||||||
func (d *EventDatabase) MaybeRedactEvent(
|
func (d *EventDatabase) MaybeRedactEvent(
|
||||||
ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver,
|
ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver,
|
||||||
querier api.QuerySenderIDAPI,
|
querier api.QuerySenderIDAPI,
|
||||||
) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, error) {
|
) (redactionEvent, redactedEvent, originalRedactedEvent gomatrixserverlib.PDU, err error) {
|
||||||
var (
|
var (
|
||||||
redactionEvent, redactedEvent *types.Event
|
redactionEv, redactedEv *types.Event
|
||||||
err error
|
|
||||||
validated bool
|
validated bool
|
||||||
ignoreRedaction 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 {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return fmt.Errorf("d.loadRedactionPair: %w", err)
|
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
|
// we've seen this redaction before or there is nothing to redact
|
||||||
return nil
|
return nil
|
||||||
case redactedEvent.RoomID() != redactionEvent.RoomID():
|
case redactedEv.RoomID() != redactionEv.RoomID():
|
||||||
// redactions across rooms aren't allowed
|
// redactions across rooms aren't allowed
|
||||||
ignoreRedaction = true
|
ignoreRedaction = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var validRoomID *spec.RoomID
|
var validRoomID *spec.RoomID
|
||||||
validRoomID, err = spec.NewRoomID(redactedEvent.RoomID())
|
validRoomID, err = spec.NewRoomID(redactedEv.RoomID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sender1Domain := ""
|
sender1Domain := ""
|
||||||
sender1, err1 := querier.QueryUserIDForSender(ctx, *validRoomID, redactedEvent.SenderID())
|
sender1, err1 := querier.QueryUserIDForSender(ctx, *validRoomID, redactedEv.SenderID())
|
||||||
if err1 == nil {
|
if err1 == nil {
|
||||||
sender1Domain = string(sender1.Domain())
|
sender1Domain = string(sender1.Domain())
|
||||||
}
|
}
|
||||||
sender2Domain := ""
|
sender2Domain := ""
|
||||||
sender2, err2 := querier.QueryUserIDForSender(ctx, *validRoomID, redactionEvent.SenderID())
|
sender2, err2 := querier.QueryUserIDForSender(ctx, *validRoomID, redactionEv.SenderID())
|
||||||
if err2 == nil {
|
if err2 == nil {
|
||||||
sender2Domain = string(sender2.Domain())
|
sender2Domain = string(sender2.Domain())
|
||||||
}
|
}
|
||||||
var powerlevels *gomatrixserverlib.PowerLevelContent
|
var powerlevels *gomatrixserverlib.PowerLevelContent
|
||||||
powerlevels, err = plResolver.Resolve(ctx, redactionEvent.EventID())
|
powerlevels, err = plResolver.Resolve(ctx, redactionEv.EventID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
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.
|
// 1. The power level of the redaction event’s sender is greater than or equal to the redact level.
|
||||||
case sender1Domain != "" && sender2Domain != "" && sender1Domain == sender2Domain:
|
case sender1Domain != "" && sender2Domain != "" && sender1Domain == sender2Domain:
|
||||||
// 2. The domain of the redaction event’s sender matches that of the original event’s sender.
|
// 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
|
// mark the event as redacted
|
||||||
if redactionsArePermanent {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("redactedEvent.SetUnsignedField: %w", err)
|
return fmt.Errorf("redactedEvent.SetUnsignedField: %w", err)
|
||||||
}
|
}
|
||||||
// NOTSPEC: sytest relies on this unspecced field existing :(
|
// 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 {
|
if err != nil {
|
||||||
return fmt.Errorf("redactedEvent.SetUnsignedField: %w", err)
|
return fmt.Errorf("redactedEvent.SetUnsignedField: %w", err)
|
||||||
}
|
}
|
||||||
// overwrite the eventJSON table
|
// 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 {
|
if err != nil {
|
||||||
return fmt.Errorf("d.EventJSONTable.InsertEventJSON: %w", err)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("d.RedactionsTable.MarkRedactionValidated: %w", err)
|
return fmt.Errorf("d.RedactionsTable.MarkRedactionValidated: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We remove the entry from the cache, as if we just "StoreRoomServerEvent", we can't be
|
// 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.
|
// 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
|
return nil
|
||||||
})
|
})
|
||||||
if wErr != nil {
|
if wErr != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
if ignoreRedaction || redactionEvent == nil || redactedEvent == nil {
|
if ignoreRedaction || redactionEv == nil || redactedEv == nil {
|
||||||
return nil, nil, 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.
|
// loadRedactionPair returns both the redaction event and the redacted event, else nil.
|
||||||
|
|
Loading…
Reference in a new issue