mirror of
https://github.com/matrix-org/dendrite
synced 2024-12-12 21:53:19 +01:00
Implement rejected events (#1426)
* WIP Event rejection * Still send back errors for rejected events Instead, discard them at the federationapi /send layer rather than re-implementing checks at the clientapi/PerformJoin layer. * Implement rejected events Critically, rejected events CAN cause state resolution to happen as it can merge forks in the DAG. This is fine, _provided_ we do not add the rejected event when performing state resolution, which is what this PR does. It also fixes the error handling when NotAllowed happens, as we were checking too early and needlessly handling NotAllowed in more than one place. * Update test to match reality * Modify InputRoomEvents to no longer return an error Errors do not serialise across HTTP boundaries in polylith mode, so instead set fields on the InputRoomEventsResponse. Add `Err()` function to make the API shape basically the same. * Remove redundant returns; linting * Update blacklist
This commit is contained in:
parent
ba6c7c4a5c
commit
18231f25b4
27 changed files with 114 additions and 74 deletions
|
@ -215,7 +215,8 @@ func writeToRoomServer(input []string, roomserverURL string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return x.InputRoomEvents(context.Background(), &request, &response)
|
x.InputRoomEvents(context.Background(), &request, &response)
|
||||||
|
return response.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// testRoomserver is used to run integration tests against a single roomserver.
|
// testRoomserver is used to run integration tests against a single roomserver.
|
||||||
|
|
|
@ -372,12 +372,9 @@ func (t *txnReq) processEvent(ctx context.Context, e gomatrixserverlib.Event, is
|
||||||
return t.processEventWithMissingState(ctx, e, stateResp.RoomVersion, isInboundTxn)
|
return t.processEventWithMissingState(ctx, e, stateResp.RoomVersion, isInboundTxn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the event is allowed by the state at the event.
|
// pass the event to the roomserver which will do auth checks
|
||||||
if err := checkAllowedByState(e, gomatrixserverlib.UnwrapEventHeaders(stateResp.StateEvents)); err != nil {
|
// If the event fail auth checks, gmsl.NotAllowed error will be returned which we be silently
|
||||||
return err
|
// discarded by the caller of this function
|
||||||
}
|
|
||||||
|
|
||||||
// pass the event to the roomserver
|
|
||||||
return api.SendEvents(
|
return api.SendEvents(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
t.rsAPI,
|
t.rsAPI,
|
||||||
|
|
|
@ -89,12 +89,11 @@ func (t *testRoomserverAPI) InputRoomEvents(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.InputRoomEventsRequest,
|
request *api.InputRoomEventsRequest,
|
||||||
response *api.InputRoomEventsResponse,
|
response *api.InputRoomEventsResponse,
|
||||||
) error {
|
) {
|
||||||
t.inputRoomEvents = append(t.inputRoomEvents, request.InputRoomEvents...)
|
t.inputRoomEvents = append(t.inputRoomEvents, request.InputRoomEvents...)
|
||||||
for _, ire := range request.InputRoomEvents {
|
for _, ire := range request.InputRoomEvents {
|
||||||
fmt.Println("InputRoomEvents: ", ire.Event.EventID())
|
fmt.Println("InputRoomEvents: ", ire.Event.EventID())
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testRoomserverAPI) PerformInvite(
|
func (t *testRoomserverAPI) PerformInvite(
|
||||||
|
@ -461,7 +460,8 @@ func TestBasicTransaction(t *testing.T) {
|
||||||
assertInputRoomEvents(t, rsAPI.inputRoomEvents, []gomatrixserverlib.HeaderedEvent{testEvents[len(testEvents)-1]})
|
assertInputRoomEvents(t, rsAPI.inputRoomEvents, []gomatrixserverlib.HeaderedEvent{testEvents[len(testEvents)-1]})
|
||||||
}
|
}
|
||||||
|
|
||||||
// The purpose of this test is to check that if the event received fails auth checks the transaction is failed.
|
// The purpose of this test is to check that if the event received fails auth checks the event is still sent to the roomserver
|
||||||
|
// as it does the auth check.
|
||||||
func TestTransactionFailAuthChecks(t *testing.T) {
|
func TestTransactionFailAuthChecks(t *testing.T) {
|
||||||
rsAPI := &testRoomserverAPI{
|
rsAPI := &testRoomserverAPI{
|
||||||
queryStateAfterEvents: func(req *api.QueryStateAfterEventsRequest) api.QueryStateAfterEventsResponse {
|
queryStateAfterEvents: func(req *api.QueryStateAfterEventsRequest) api.QueryStateAfterEventsResponse {
|
||||||
|
@ -479,11 +479,9 @@ func TestTransactionFailAuthChecks(t *testing.T) {
|
||||||
testData[len(testData)-1], // a message event
|
testData[len(testData)-1], // a message event
|
||||||
}
|
}
|
||||||
txn := mustCreateTransaction(rsAPI, &txnFedClient{}, pdus)
|
txn := mustCreateTransaction(rsAPI, &txnFedClient{}, pdus)
|
||||||
mustProcessTransaction(t, txn, []string{
|
mustProcessTransaction(t, txn, []string{})
|
||||||
// expect the event to have an error
|
// expect message to be sent to the roomserver
|
||||||
testEvents[len(testEvents)-1].EventID(),
|
assertInputRoomEvents(t, rsAPI.inputRoomEvents, []gomatrixserverlib.HeaderedEvent{testEvents[len(testEvents)-1]})
|
||||||
})
|
|
||||||
assertInputRoomEvents(t, rsAPI.inputRoomEvents, nil) // expect no messages to be sent to the roomserver
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The purpose of this test is to make sure that when an event is received for which we do not know the prev_events,
|
// The purpose of this test is to make sure that when an event is received for which we do not know the prev_events,
|
||||||
|
|
|
@ -16,7 +16,7 @@ type RoomserverInternalAPI interface {
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *InputRoomEventsRequest,
|
request *InputRoomEventsRequest,
|
||||||
response *InputRoomEventsResponse,
|
response *InputRoomEventsResponse,
|
||||||
) error
|
)
|
||||||
|
|
||||||
PerformInvite(
|
PerformInvite(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
|
@ -23,10 +23,9 @@ func (t *RoomserverInternalAPITrace) InputRoomEvents(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *InputRoomEventsRequest,
|
req *InputRoomEventsRequest,
|
||||||
res *InputRoomEventsResponse,
|
res *InputRoomEventsResponse,
|
||||||
) error {
|
) {
|
||||||
err := t.Impl.InputRoomEvents(ctx, req, res)
|
t.Impl.InputRoomEvents(ctx, req, res)
|
||||||
util.GetLogger(ctx).WithError(err).Infof("InputRoomEvents req=%+v res=%+v", js(req), js(res))
|
util.GetLogger(ctx).Infof("InputRoomEvents req=%+v res=%+v", js(req), js(res))
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RoomserverInternalAPITrace) PerformInvite(
|
func (t *RoomserverInternalAPITrace) PerformInvite(
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -87,4 +89,18 @@ type InputRoomEventsRequest struct {
|
||||||
|
|
||||||
// InputRoomEventsResponse is a response to InputRoomEvents
|
// InputRoomEventsResponse is a response to InputRoomEvents
|
||||||
type InputRoomEventsResponse struct {
|
type InputRoomEventsResponse struct {
|
||||||
|
ErrMsg string // set if there was any error
|
||||||
|
NotAllowed bool // true if an event in the input was not allowed.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InputRoomEventsResponse) Err() error {
|
||||||
|
if r.ErrMsg == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if r.NotAllowed {
|
||||||
|
return &gomatrixserverlib.NotAllowed{
|
||||||
|
Message: r.ErrMsg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("InputRoomEventsResponse: %s", r.ErrMsg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,7 +187,8 @@ func SendInputRoomEvents(
|
||||||
) error {
|
) error {
|
||||||
request := InputRoomEventsRequest{InputRoomEvents: ires}
|
request := InputRoomEventsRequest{InputRoomEvents: ires}
|
||||||
var response InputRoomEventsResponse
|
var response InputRoomEventsResponse
|
||||||
return rsAPI.InputRoomEvents(ctx, &request, &response)
|
rsAPI.InputRoomEvents(ctx, &request, &response)
|
||||||
|
return response.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendInvite event to the roomserver.
|
// SendInvite event to the roomserver.
|
||||||
|
|
|
@ -271,5 +271,6 @@ func (r *RoomserverInternalAPI) sendUpdatedAliasesEvent(
|
||||||
var inputRes api.InputRoomEventsResponse
|
var inputRes api.InputRoomEventsResponse
|
||||||
|
|
||||||
// Send the request
|
// Send the request
|
||||||
return r.InputRoomEvents(ctx, &inputReq, &inputRes)
|
r.InputRoomEvents(ctx, &inputReq, &inputRes)
|
||||||
|
return inputRes.Err()
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ func (r *Inputer) InputRoomEvents(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.InputRoomEventsRequest,
|
request *api.InputRoomEventsRequest,
|
||||||
response *api.InputRoomEventsResponse,
|
response *api.InputRoomEventsResponse,
|
||||||
) error {
|
) {
|
||||||
// Create a wait group. Each task that we dispatch will call Done on
|
// Create a wait group. Each task that we dispatch will call Done on
|
||||||
// this wait group so that we know when all of our events have been
|
// this wait group so that we know when all of our events have been
|
||||||
// processed.
|
// processed.
|
||||||
|
@ -156,8 +156,10 @@ func (r *Inputer) InputRoomEvents(
|
||||||
// that back to the caller.
|
// that back to the caller.
|
||||||
for _, task := range tasks {
|
for _, task := range tasks {
|
||||||
if task.err != nil {
|
if task.err != nil {
|
||||||
return task.err
|
response.ErrMsg = task.err.Error()
|
||||||
|
_, rejected := task.err.(*gomatrixserverlib.NotAllowed)
|
||||||
|
response.NotAllowed = rejected
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,10 +46,11 @@ func (r *Inputer) processRoomEvent(
|
||||||
|
|
||||||
// Check that the event passes authentication checks and work out
|
// Check that the event passes authentication checks and work out
|
||||||
// the numeric IDs for the auth events.
|
// the numeric IDs for the auth events.
|
||||||
authEventNIDs, err := helpers.CheckAuthEvents(ctx, r.DB, headered, input.AuthEventIDs)
|
isRejected := false
|
||||||
if err != nil {
|
authEventNIDs, rejectionErr := helpers.CheckAuthEvents(ctx, r.DB, headered, input.AuthEventIDs)
|
||||||
logrus.WithError(err).WithField("event_id", event.EventID()).WithField("auth_event_ids", input.AuthEventIDs).Error("processRoomEvent.checkAuthEvents failed for event")
|
if rejectionErr != nil {
|
||||||
return
|
logrus.WithError(rejectionErr).WithField("event_id", event.EventID()).WithField("auth_event_ids", input.AuthEventIDs).Error("processRoomEvent.checkAuthEvents failed for event, rejecting event")
|
||||||
|
isRejected = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we don't have a transaction ID then get one.
|
// If we don't have a transaction ID then get one.
|
||||||
|
@ -65,12 +66,13 @@ func (r *Inputer) processRoomEvent(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the event.
|
// Store the event.
|
||||||
_, stateAtEvent, redactionEvent, redactedEventID, err := r.DB.StoreEvent(ctx, event, input.TransactionID, authEventNIDs)
|
_, stateAtEvent, redactionEvent, redactedEventID, err := r.DB.StoreEvent(ctx, event, input.TransactionID, authEventNIDs, isRejected)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("r.DB.StoreEvent: %w", err)
|
return "", fmt.Errorf("r.DB.StoreEvent: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if storing this event results in it being redacted then do so.
|
// if storing this event results in it being redacted then do so.
|
||||||
if redactedEventID == event.EventID() {
|
if !isRejected && redactedEventID == event.EventID() {
|
||||||
r, rerr := eventutil.RedactEvent(redactionEvent, &event)
|
r, rerr := eventutil.RedactEvent(redactionEvent, &event)
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
return "", fmt.Errorf("eventutil.RedactEvent: %w", rerr)
|
return "", fmt.Errorf("eventutil.RedactEvent: %w", rerr)
|
||||||
|
@ -101,12 +103,22 @@ func (r *Inputer) processRoomEvent(
|
||||||
if stateAtEvent.BeforeStateSnapshotNID == 0 {
|
if stateAtEvent.BeforeStateSnapshotNID == 0 {
|
||||||
// We haven't calculated a state for this event yet.
|
// We haven't calculated a state for this event yet.
|
||||||
// Lets calculate one.
|
// Lets calculate one.
|
||||||
err = r.calculateAndSetState(ctx, input, *roomInfo, &stateAtEvent, event)
|
err = r.calculateAndSetState(ctx, input, *roomInfo, &stateAtEvent, event, isRejected)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("r.calculateAndSetState: %w", err)
|
return "", fmt.Errorf("r.calculateAndSetState: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We stop here if the event is rejected: We've stored it but won't update forward extremities or notify anyone about it.
|
||||||
|
if isRejected {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"event_id": event.EventID(),
|
||||||
|
"type": event.Type(),
|
||||||
|
"room": event.RoomID(),
|
||||||
|
}).Debug("Stored rejected event")
|
||||||
|
return event.EventID(), rejectionErr
|
||||||
|
}
|
||||||
|
|
||||||
if input.Kind == api.KindRewrite {
|
if input.Kind == api.KindRewrite {
|
||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{
|
||||||
"event_id": event.EventID(),
|
"event_id": event.EventID(),
|
||||||
|
@ -157,11 +169,12 @@ func (r *Inputer) calculateAndSetState(
|
||||||
roomInfo types.RoomInfo,
|
roomInfo types.RoomInfo,
|
||||||
stateAtEvent *types.StateAtEvent,
|
stateAtEvent *types.StateAtEvent,
|
||||||
event gomatrixserverlib.Event,
|
event gomatrixserverlib.Event,
|
||||||
|
isRejected bool,
|
||||||
) error {
|
) error {
|
||||||
var err error
|
var err error
|
||||||
roomState := state.NewStateResolution(r.DB, roomInfo)
|
roomState := state.NewStateResolution(r.DB, roomInfo)
|
||||||
|
|
||||||
if input.HasState {
|
if input.HasState && !isRejected {
|
||||||
// Check here if we think we're in the room already.
|
// Check here if we think we're in the room already.
|
||||||
stateAtEvent.Overwrite = true
|
stateAtEvent.Overwrite = true
|
||||||
var joinEventNIDs []types.EventNID
|
var joinEventNIDs []types.EventNID
|
||||||
|
@ -188,7 +201,7 @@ func (r *Inputer) calculateAndSetState(
|
||||||
stateAtEvent.Overwrite = false
|
stateAtEvent.Overwrite = false
|
||||||
|
|
||||||
// We haven't been told what the state at the event is so we need to calculate it from the prev_events
|
// We haven't been told what the state at the event is so we need to calculate it from the prev_events
|
||||||
if stateAtEvent.BeforeStateSnapshotNID, err = roomState.CalculateAndStoreStateBeforeEvent(ctx, event); err != nil {
|
if stateAtEvent.BeforeStateSnapshotNID, err = roomState.CalculateAndStoreStateBeforeEvent(ctx, event, isRejected); err != nil {
|
||||||
return fmt.Errorf("roomState.CalculateAndStoreStateBeforeEvent: %w", err)
|
return fmt.Errorf("roomState.CalculateAndStoreStateBeforeEvent: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -535,7 +535,7 @@ func persistEvents(ctx context.Context, db storage.Database, events []gomatrixse
|
||||||
var stateAtEvent types.StateAtEvent
|
var stateAtEvent types.StateAtEvent
|
||||||
var redactedEventID string
|
var redactedEventID string
|
||||||
var redactionEvent *gomatrixserverlib.Event
|
var redactionEvent *gomatrixserverlib.Event
|
||||||
roomNID, stateAtEvent, redactionEvent, redactedEventID, err = db.StoreEvent(ctx, ev.Unwrap(), nil, authNids)
|
roomNID, stateAtEvent, redactionEvent, redactedEventID, err = db.StoreEvent(ctx, ev.Unwrap(), nil, authNids, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).WithField("event_id", ev.EventID()).Error("Failed to persist event")
|
logrus.WithError(err).WithField("event_id", ev.EventID()).Error("Failed to persist event")
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -183,7 +183,8 @@ func (r *Inviter) PerformInvite(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
inputRes := &api.InputRoomEventsResponse{}
|
inputRes := &api.InputRoomEventsResponse{}
|
||||||
if err = r.Inputer.InputRoomEvents(context.Background(), inputReq, inputRes); err != nil {
|
r.Inputer.InputRoomEvents(context.Background(), inputReq, inputRes)
|
||||||
|
if err = inputRes.Err(); err != nil {
|
||||||
return nil, fmt.Errorf("r.InputRoomEvents: %w", err)
|
return nil, fmt.Errorf("r.InputRoomEvents: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -247,7 +247,8 @@ func (r *Joiner) performJoinRoomByID(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
inputRes := api.InputRoomEventsResponse{}
|
inputRes := api.InputRoomEventsResponse{}
|
||||||
if err = r.Inputer.InputRoomEvents(ctx, &inputReq, &inputRes); err != nil {
|
r.Inputer.InputRoomEvents(ctx, &inputReq, &inputRes)
|
||||||
|
if err = inputRes.Err(); err != nil {
|
||||||
var notAllowed *gomatrixserverlib.NotAllowed
|
var notAllowed *gomatrixserverlib.NotAllowed
|
||||||
if errors.As(err, ¬Allowed) {
|
if errors.As(err, ¬Allowed) {
|
||||||
return "", &api.PerformError{
|
return "", &api.PerformError{
|
||||||
|
|
|
@ -139,7 +139,8 @@ func (r *Leaver) performLeaveRoomByID(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
inputRes := api.InputRoomEventsResponse{}
|
inputRes := api.InputRoomEventsResponse{}
|
||||||
if err = r.Inputer.InputRoomEvents(ctx, &inputReq, &inputRes); err != nil {
|
r.Inputer.InputRoomEvents(ctx, &inputReq, &inputRes)
|
||||||
|
if err = inputRes.Err(); err != nil {
|
||||||
return nil, fmt.Errorf("r.InputRoomEvents: %w", err)
|
return nil, fmt.Errorf("r.InputRoomEvents: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ func (r *Queryer) QueryStateAfterEvents(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case types.MissingEventError:
|
case types.MissingEventError:
|
||||||
|
util.GetLogger(ctx).Errorf("QueryStateAfterEvents: MissingEventError: %s", err)
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -149,12 +149,15 @@ func (h *httpRoomserverInternalAPI) InputRoomEvents(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *api.InputRoomEventsRequest,
|
request *api.InputRoomEventsRequest,
|
||||||
response *api.InputRoomEventsResponse,
|
response *api.InputRoomEventsResponse,
|
||||||
) error {
|
) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "InputRoomEvents")
|
span, ctx := opentracing.StartSpanFromContext(ctx, "InputRoomEvents")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
||||||
apiURL := h.roomserverURL + RoomserverInputRoomEventsPath
|
apiURL := h.roomserverURL + RoomserverInputRoomEventsPath
|
||||||
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
||||||
|
if err != nil {
|
||||||
|
response.ErrMsg = err.Error()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpRoomserverInternalAPI) PerformInvite(
|
func (h *httpRoomserverInternalAPI) PerformInvite(
|
||||||
|
|
|
@ -20,9 +20,7 @@ func AddRoutes(r api.RoomserverInternalAPI, internalAPIMux *mux.Router) {
|
||||||
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
if err := r.InputRoomEvents(req.Context(), &request, &response); err != nil {
|
r.InputRoomEvents(req.Context(), &request, &response)
|
||||||
return util.ErrorResponse(err)
|
|
||||||
}
|
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
@ -140,14 +140,6 @@ func mustCreateEvents(t *testing.T, roomVer gomatrixserverlib.RoomVersion, event
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func eventsJSON(events []gomatrixserverlib.Event) []json.RawMessage {
|
|
||||||
result := make([]json.RawMessage, len(events))
|
|
||||||
for i := range events {
|
|
||||||
result[i] = events[i].JSON()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustLoadRawEvents(t *testing.T, ver gomatrixserverlib.RoomVersion, events []json.RawMessage) []gomatrixserverlib.HeaderedEvent {
|
func mustLoadRawEvents(t *testing.T, ver gomatrixserverlib.RoomVersion, events []json.RawMessage) []gomatrixserverlib.HeaderedEvent {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
hs := make([]gomatrixserverlib.HeaderedEvent, len(events))
|
hs := make([]gomatrixserverlib.HeaderedEvent, len(events))
|
||||||
|
|
|
@ -159,7 +159,7 @@ func (v StateResolution) LoadCombinedStateAfterEvents(
|
||||||
}
|
}
|
||||||
fullState = append(fullState, entries...)
|
fullState = append(fullState, entries...)
|
||||||
}
|
}
|
||||||
if prevState.IsStateEvent() {
|
if prevState.IsStateEvent() && !prevState.IsRejected {
|
||||||
// If the prev event was a state event then add an entry for the event itself
|
// If the prev event was a state event then add an entry for the event itself
|
||||||
// so that we get the state after the event rather than the state before.
|
// so that we get the state after the event rather than the state before.
|
||||||
fullState = append(fullState, prevState.StateEntry)
|
fullState = append(fullState, prevState.StateEntry)
|
||||||
|
@ -523,6 +523,7 @@ func init() {
|
||||||
func (v StateResolution) CalculateAndStoreStateBeforeEvent(
|
func (v StateResolution) CalculateAndStoreStateBeforeEvent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
event gomatrixserverlib.Event,
|
event gomatrixserverlib.Event,
|
||||||
|
isRejected bool,
|
||||||
) (types.StateSnapshotNID, error) {
|
) (types.StateSnapshotNID, error) {
|
||||||
// Load the state at the prev events.
|
// Load the state at the prev events.
|
||||||
prevEventRefs := event.PrevEvents()
|
prevEventRefs := event.PrevEvents()
|
||||||
|
@ -561,7 +562,7 @@ func (v StateResolution) CalculateAndStoreStateAfterEvents(
|
||||||
|
|
||||||
if len(prevStates) == 1 {
|
if len(prevStates) == 1 {
|
||||||
prevState := prevStates[0]
|
prevState := prevStates[0]
|
||||||
if prevState.EventStateKeyNID == 0 {
|
if prevState.EventStateKeyNID == 0 || prevState.IsRejected {
|
||||||
// 3) None of the previous events were state events and they all
|
// 3) None of the previous events were state events and they all
|
||||||
// have the same state, so this event has exactly the same state
|
// have the same state, so this event has exactly the same state
|
||||||
// as the previous events.
|
// as the previous events.
|
||||||
|
|
|
@ -70,6 +70,7 @@ type Database interface {
|
||||||
// Stores a matrix room event in the database. Returns the room NID, the state snapshot and the redacted event ID if any, or an error.
|
// Stores a matrix room event in the database. Returns the room NID, the state snapshot and the redacted event ID if any, or an error.
|
||||||
StoreEvent(
|
StoreEvent(
|
||||||
ctx context.Context, event gomatrixserverlib.Event, txnAndSessionID *api.TransactionID, authEventNIDs []types.EventNID,
|
ctx context.Context, event gomatrixserverlib.Event, txnAndSessionID *api.TransactionID, authEventNIDs []types.EventNID,
|
||||||
|
isRejected bool,
|
||||||
) (types.RoomNID, types.StateAtEvent, *gomatrixserverlib.Event, string, error)
|
) (types.RoomNID, types.StateAtEvent, *gomatrixserverlib.Event, string, error)
|
||||||
// Look up the state entries for a list of string event IDs
|
// Look up the state entries for a list of string event IDs
|
||||||
// Returns an error if the there is an error talking to the database
|
// Returns an error if the there is an error talking to the database
|
||||||
|
|
|
@ -65,13 +65,14 @@ CREATE TABLE IF NOT EXISTS roomserver_events (
|
||||||
-- Needed for setting reference hashes when sending new events.
|
-- Needed for setting reference hashes when sending new events.
|
||||||
reference_sha256 BYTEA NOT NULL,
|
reference_sha256 BYTEA NOT NULL,
|
||||||
-- A list of numeric IDs for events that can authenticate this event.
|
-- A list of numeric IDs for events that can authenticate this event.
|
||||||
auth_event_nids BIGINT[] NOT NULL
|
auth_event_nids BIGINT[] NOT NULL,
|
||||||
|
is_rejected BOOLEAN NOT NULL DEFAULT FALSE
|
||||||
);
|
);
|
||||||
`
|
`
|
||||||
|
|
||||||
const insertEventSQL = "" +
|
const insertEventSQL = "" +
|
||||||
"INSERT INTO roomserver_events (room_nid, event_type_nid, event_state_key_nid, event_id, reference_sha256, auth_event_nids, depth)" +
|
"INSERT INTO roomserver_events (room_nid, event_type_nid, event_state_key_nid, event_id, reference_sha256, auth_event_nids, depth, is_rejected)" +
|
||||||
" VALUES ($1, $2, $3, $4, $5, $6, $7)" +
|
" VALUES ($1, $2, $3, $4, $5, $6, $7, $8)" +
|
||||||
" ON CONFLICT ON CONSTRAINT roomserver_event_id_unique" +
|
" ON CONFLICT ON CONSTRAINT roomserver_event_id_unique" +
|
||||||
" DO NOTHING" +
|
" DO NOTHING" +
|
||||||
" RETURNING event_nid, state_snapshot_nid"
|
" RETURNING event_nid, state_snapshot_nid"
|
||||||
|
@ -88,7 +89,7 @@ const bulkSelectStateEventByIDSQL = "" +
|
||||||
" ORDER BY event_type_nid, event_state_key_nid ASC"
|
" ORDER BY event_type_nid, event_state_key_nid ASC"
|
||||||
|
|
||||||
const bulkSelectStateAtEventByIDSQL = "" +
|
const bulkSelectStateAtEventByIDSQL = "" +
|
||||||
"SELECT event_type_nid, event_state_key_nid, event_nid, state_snapshot_nid FROM roomserver_events" +
|
"SELECT event_type_nid, event_state_key_nid, event_nid, state_snapshot_nid, is_rejected FROM roomserver_events" +
|
||||||
" WHERE event_id = ANY($1)"
|
" WHERE event_id = ANY($1)"
|
||||||
|
|
||||||
const updateEventStateSQL = "" +
|
const updateEventStateSQL = "" +
|
||||||
|
@ -174,12 +175,14 @@ func (s *eventStatements) InsertEvent(
|
||||||
referenceSHA256 []byte,
|
referenceSHA256 []byte,
|
||||||
authEventNIDs []types.EventNID,
|
authEventNIDs []types.EventNID,
|
||||||
depth int64,
|
depth int64,
|
||||||
|
isRejected bool,
|
||||||
) (types.EventNID, types.StateSnapshotNID, error) {
|
) (types.EventNID, types.StateSnapshotNID, error) {
|
||||||
var eventNID int64
|
var eventNID int64
|
||||||
var stateNID int64
|
var stateNID int64
|
||||||
err := s.insertEventStmt.QueryRowContext(
|
err := s.insertEventStmt.QueryRowContext(
|
||||||
ctx, int64(roomNID), int64(eventTypeNID), int64(eventStateKeyNID),
|
ctx, int64(roomNID), int64(eventTypeNID), int64(eventStateKeyNID),
|
||||||
eventID, referenceSHA256, eventNIDsAsArray(authEventNIDs), depth,
|
eventID, referenceSHA256, eventNIDsAsArray(authEventNIDs), depth,
|
||||||
|
isRejected,
|
||||||
).Scan(&eventNID, &stateNID)
|
).Scan(&eventNID, &stateNID)
|
||||||
return types.EventNID(eventNID), types.StateSnapshotNID(stateNID), err
|
return types.EventNID(eventNID), types.StateSnapshotNID(stateNID), err
|
||||||
}
|
}
|
||||||
|
@ -255,6 +258,7 @@ func (s *eventStatements) BulkSelectStateAtEventByID(
|
||||||
&result.EventStateKeyNID,
|
&result.EventStateKeyNID,
|
||||||
&result.EventNID,
|
&result.EventNID,
|
||||||
&result.BeforeStateSnapshotNID,
|
&result.BeforeStateSnapshotNID,
|
||||||
|
&result.IsRejected,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -382,7 +382,7 @@ func (d *Database) GetLatestEventsForUpdate(
|
||||||
// nolint:gocyclo
|
// nolint:gocyclo
|
||||||
func (d *Database) StoreEvent(
|
func (d *Database) StoreEvent(
|
||||||
ctx context.Context, event gomatrixserverlib.Event,
|
ctx context.Context, event gomatrixserverlib.Event,
|
||||||
txnAndSessionID *api.TransactionID, authEventNIDs []types.EventNID,
|
txnAndSessionID *api.TransactionID, authEventNIDs []types.EventNID, isRejected bool,
|
||||||
) (types.RoomNID, types.StateAtEvent, *gomatrixserverlib.Event, string, error) {
|
) (types.RoomNID, types.StateAtEvent, *gomatrixserverlib.Event, string, error) {
|
||||||
var (
|
var (
|
||||||
roomNID types.RoomNID
|
roomNID types.RoomNID
|
||||||
|
@ -446,6 +446,7 @@ func (d *Database) StoreEvent(
|
||||||
event.EventReference().EventSHA256,
|
event.EventReference().EventSHA256,
|
||||||
authEventNIDs,
|
authEventNIDs,
|
||||||
event.Depth(),
|
event.Depth(),
|
||||||
|
isRejected,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
// We've already inserted the event so select the numeric event ID
|
// We've already inserted the event so select the numeric event ID
|
||||||
|
@ -459,7 +460,9 @@ func (d *Database) StoreEvent(
|
||||||
if err = d.EventJSONTable.InsertEventJSON(ctx, txn, eventNID, event.JSON()); err != nil {
|
if err = d.EventJSONTable.InsertEventJSON(ctx, txn, eventNID, event.JSON()); err != nil {
|
||||||
return fmt.Errorf("d.EventJSONTable.InsertEventJSON: %w", err)
|
return fmt.Errorf("d.EventJSONTable.InsertEventJSON: %w", err)
|
||||||
}
|
}
|
||||||
redactionEvent, redactedEventID, err = d.handleRedactions(ctx, txn, eventNID, event)
|
if !isRejected { // ignore rejected redaction events
|
||||||
|
redactionEvent, redactedEventID, err = d.handleRedactions(ctx, txn, eventNID, event)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -41,13 +41,14 @@ const eventsSchema = `
|
||||||
depth INTEGER NOT NULL,
|
depth INTEGER NOT NULL,
|
||||||
event_id TEXT NOT NULL UNIQUE,
|
event_id TEXT NOT NULL UNIQUE,
|
||||||
reference_sha256 BLOB NOT NULL,
|
reference_sha256 BLOB NOT NULL,
|
||||||
auth_event_nids TEXT NOT NULL DEFAULT '[]'
|
auth_event_nids TEXT NOT NULL DEFAULT '[]',
|
||||||
|
is_rejected BOOLEAN NOT NULL DEFAULT FALSE
|
||||||
);
|
);
|
||||||
`
|
`
|
||||||
|
|
||||||
const insertEventSQL = `
|
const insertEventSQL = `
|
||||||
INSERT INTO roomserver_events (room_nid, event_type_nid, event_state_key_nid, event_id, reference_sha256, auth_event_nids, depth)
|
INSERT INTO roomserver_events (room_nid, event_type_nid, event_state_key_nid, event_id, reference_sha256, auth_event_nids, depth, is_rejected)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||||
ON CONFLICT DO NOTHING;
|
ON CONFLICT DO NOTHING;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ const bulkSelectStateEventByIDSQL = "" +
|
||||||
" ORDER BY event_type_nid, event_state_key_nid ASC"
|
" ORDER BY event_type_nid, event_state_key_nid ASC"
|
||||||
|
|
||||||
const bulkSelectStateAtEventByIDSQL = "" +
|
const bulkSelectStateAtEventByIDSQL = "" +
|
||||||
"SELECT event_type_nid, event_state_key_nid, event_nid, state_snapshot_nid FROM roomserver_events" +
|
"SELECT event_type_nid, event_state_key_nid, event_nid, state_snapshot_nid, is_rejected FROM roomserver_events" +
|
||||||
" WHERE event_id IN ($1)"
|
" WHERE event_id IN ($1)"
|
||||||
|
|
||||||
const updateEventStateSQL = "" +
|
const updateEventStateSQL = "" +
|
||||||
|
@ -150,13 +151,14 @@ func (s *eventStatements) InsertEvent(
|
||||||
referenceSHA256 []byte,
|
referenceSHA256 []byte,
|
||||||
authEventNIDs []types.EventNID,
|
authEventNIDs []types.EventNID,
|
||||||
depth int64,
|
depth int64,
|
||||||
|
isRejected bool,
|
||||||
) (types.EventNID, types.StateSnapshotNID, error) {
|
) (types.EventNID, types.StateSnapshotNID, error) {
|
||||||
// attempt to insert: the last_row_id is the event NID
|
// attempt to insert: the last_row_id is the event NID
|
||||||
var eventNID int64
|
var eventNID int64
|
||||||
insertStmt := sqlutil.TxStmt(txn, s.insertEventStmt)
|
insertStmt := sqlutil.TxStmt(txn, s.insertEventStmt)
|
||||||
result, err := insertStmt.ExecContext(
|
result, err := insertStmt.ExecContext(
|
||||||
ctx, int64(roomNID), int64(eventTypeNID), int64(eventStateKeyNID),
|
ctx, int64(roomNID), int64(eventTypeNID), int64(eventStateKeyNID),
|
||||||
eventID, referenceSHA256, eventNIDsAsArray(authEventNIDs), depth,
|
eventID, referenceSHA256, eventNIDsAsArray(authEventNIDs), depth, isRejected,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
|
@ -261,6 +263,7 @@ func (s *eventStatements) BulkSelectStateAtEventByID(
|
||||||
&result.EventStateKeyNID,
|
&result.EventStateKeyNID,
|
||||||
&result.EventNID,
|
&result.EventNID,
|
||||||
&result.BeforeStateSnapshotNID,
|
&result.BeforeStateSnapshotNID,
|
||||||
|
&result.IsRejected,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,10 @@ type EventStateKeys interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Events interface {
|
type Events interface {
|
||||||
InsertEvent(c context.Context, txn *sql.Tx, i types.RoomNID, j types.EventTypeNID, k types.EventStateKeyNID, eventID string, referenceSHA256 []byte, authEventNIDs []types.EventNID, depth int64) (types.EventNID, types.StateSnapshotNID, error)
|
InsertEvent(
|
||||||
|
ctx context.Context, txn *sql.Tx, i types.RoomNID, j types.EventTypeNID, k types.EventStateKeyNID, eventID string,
|
||||||
|
referenceSHA256 []byte, authEventNIDs []types.EventNID, depth int64, isRejected bool,
|
||||||
|
) (types.EventNID, types.StateSnapshotNID, error)
|
||||||
SelectEvent(ctx context.Context, txn *sql.Tx, eventID string) (types.EventNID, types.StateSnapshotNID, error)
|
SelectEvent(ctx context.Context, txn *sql.Tx, eventID string) (types.EventNID, types.StateSnapshotNID, error)
|
||||||
// bulkSelectStateEventByID lookups a list of state events by event ID.
|
// bulkSelectStateEventByID lookups a list of state events by event ID.
|
||||||
// If any of the requested events are missing from the database it returns a types.MissingEventError
|
// If any of the requested events are missing from the database it returns a types.MissingEventError
|
||||||
|
|
|
@ -101,6 +101,9 @@ type StateAtEvent struct {
|
||||||
Overwrite bool
|
Overwrite bool
|
||||||
// The state before the event.
|
// The state before the event.
|
||||||
BeforeStateSnapshotNID StateSnapshotNID
|
BeforeStateSnapshotNID StateSnapshotNID
|
||||||
|
// True if this StateEntry is rejected. State resolution should then treat this
|
||||||
|
// StateEntry as being a message event (not a state event).
|
||||||
|
IsRejected bool
|
||||||
// The state entry for the event itself, allows us to calculate the state after the event.
|
// The state entry for the event itself, allows us to calculate the state after the event.
|
||||||
StateEntry
|
StateEntry
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,11 +40,6 @@ Ignore invite in incremental sync
|
||||||
New room members see their own join event
|
New room members see their own join event
|
||||||
Existing members see new members' join events
|
Existing members see new members' join events
|
||||||
|
|
||||||
# Blacklisted because the federation work for these hasn't been finished yet.
|
|
||||||
Can recv device messages over federation
|
|
||||||
Device messages over federation wake up /sync
|
|
||||||
Wildcard device messages over federation wake up /sync
|
|
||||||
|
|
||||||
# See https://github.com/matrix-org/sytest/pull/901
|
# See https://github.com/matrix-org/sytest/pull/901
|
||||||
Remote invited user can see room metadata
|
Remote invited user can see room metadata
|
||||||
|
|
||||||
|
@ -56,8 +51,8 @@ Inbound federation accepts a second soft-failed event
|
||||||
# Caused by https://github.com/matrix-org/sytest/pull/911
|
# Caused by https://github.com/matrix-org/sytest/pull/911
|
||||||
Outbound federation requests missing prev_events and then asks for /state_ids and resolves the state
|
Outbound federation requests missing prev_events and then asks for /state_ids and resolves the state
|
||||||
|
|
||||||
# We don't implement device lists yet
|
|
||||||
Device list doesn't change if remote server is down
|
|
||||||
|
|
||||||
# We don't implement lazy membership loading yet.
|
# We don't implement lazy membership loading yet.
|
||||||
The only membership state included in a gapped incremental sync is for senders in the timeline
|
The only membership state included in a gapped incremental sync is for senders in the timeline
|
||||||
|
|
||||||
|
# flakey since implementing rejected events
|
||||||
|
Inbound federation correctly soft fails events
|
|
@ -470,4 +470,6 @@ We can't peek into rooms with shared history_visibility
|
||||||
We can't peek into rooms with invited history_visibility
|
We can't peek into rooms with invited history_visibility
|
||||||
We can't peek into rooms with joined history_visibility
|
We can't peek into rooms with joined history_visibility
|
||||||
Local users can peek by room alias
|
Local users can peek by room alias
|
||||||
Peeked rooms only turn up in the sync for the device who peeked them
|
Peeked rooms only turn up in the sync for the device who peeked them
|
||||||
|
Room state at a rejected message event is the same as its predecessor
|
||||||
|
Room state at a rejected state event is the same as its predecessor
|
Loading…
Reference in a new issue