Fetch missing auth events, implement QueryMissingAuthPrevEvents, try other servers in room for /event and /get_missing_events (#1450)

* Try to ask other servers in the room for missing events if the origin won't provide them

* Logging

* More logging

* Implement QueryMissingAuthPrevEvents

* Try to get missing auth events badly

* Use processEvent

* Logging

* Update QueryMissingAuthPrevEvents

* Try to find missing auth events

* Patchy fix for test

* Logging tweaks

* Send auth events as outliers

* Update check in QueryMissingAuthPrevEvents

* Error responses

* More return codes

* Don't return error on reject/soft-fail since it was ultimately handled

* More tweaks

* More error tweaks
This commit is contained in:
Neil Alexander 2020-09-29 13:40:29 +01:00 committed by GitHub
parent 4ff7ac7b65
commit 738b829a23
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 291 additions and 81 deletions

View file

@ -206,10 +206,10 @@ func (t *txnReq) processTransaction(ctx context.Context) (*gomatrixserverlib.Res
return nil, &jsonErr return nil, &jsonErr
} else { } else {
// Auth errors mean the event is 'rejected' which have to be silent to appease sytest // Auth errors mean the event is 'rejected' which have to be silent to appease sytest
errMsg := ""
_, rejected := err.(*gomatrixserverlib.NotAllowed) _, rejected := err.(*gomatrixserverlib.NotAllowed)
errMsg := err.Error() if !rejected {
if rejected { errMsg = err.Error()
errMsg = ""
} }
util.GetLogger(ctx).WithError(err).WithField("event_id", e.EventID()).WithField("rejected", rejected).Warn( util.GetLogger(ctx).WithError(err).WithField("event_id", e.EventID()).WithField("rejected", rejected).Warn(
"Failed to process incoming federation event, skipping", "Failed to process incoming federation event, skipping",
@ -345,17 +345,17 @@ func (t *txnReq) processDeviceListUpdate(ctx context.Context, e gomatrixserverli
} }
func (t *txnReq) processEvent(ctx context.Context, e gomatrixserverlib.Event, isInboundTxn bool) error { func (t *txnReq) processEvent(ctx context.Context, e gomatrixserverlib.Event, isInboundTxn bool) error {
prevEventIDs := e.PrevEventIDs() logger := util.GetLogger(ctx).WithField("event_id", e.EventID()).WithField("room_id", e.RoomID())
// Fetch the state needed to authenticate the event. // Work out if the roomserver knows everything it needs to know to auth
needed := gomatrixserverlib.StateNeededForAuth([]gomatrixserverlib.Event{e}) // the event.
stateReq := api.QueryStateAfterEventsRequest{ stateReq := api.QueryMissingAuthPrevEventsRequest{
RoomID: e.RoomID(), RoomID: e.RoomID(),
PrevEventIDs: prevEventIDs, AuthEventIDs: e.AuthEventIDs(),
StateToFetch: needed.Tuples(), PrevEventIDs: e.PrevEventIDs(),
} }
var stateResp api.QueryStateAfterEventsResponse var stateResp api.QueryMissingAuthPrevEventsResponse
if err := t.rsAPI.QueryStateAfterEvents(ctx, &stateReq, &stateResp); err != nil { if err := t.rsAPI.QueryMissingAuthPrevEvents(ctx, &stateReq, &stateResp); err != nil {
return err return err
} }
@ -369,7 +369,53 @@ func (t *txnReq) processEvent(ctx context.Context, e gomatrixserverlib.Event, is
return roomNotFoundError{e.RoomID()} return roomNotFoundError{e.RoomID()}
} }
if !stateResp.PrevEventsExist { if len(stateResp.MissingAuthEventIDs) > 0 {
logger.Infof("Event refers to %d unknown auth_events", len(stateResp.MissingAuthEventIDs))
servers := []gomatrixserverlib.ServerName{t.Origin}
serverReq := &api.QueryServerJoinedToRoomRequest{
RoomID: e.RoomID(),
}
serverRes := &api.QueryServerJoinedToRoomResponse{}
if err := t.rsAPI.QueryServerJoinedToRoom(ctx, serverReq, serverRes); err == nil {
servers = append(servers, serverRes.ServerNames...)
logger.Infof("Found %d server(s) to query for missing events", len(servers))
}
getAuthEvent:
for _, missingAuthEventID := range stateResp.MissingAuthEventIDs {
for _, server := range servers {
logger.Infof("Retrieving missing auth event %q from %q", missingAuthEventID, server)
tx, err := t.federation.GetEvent(ctx, server, missingAuthEventID)
if err != nil {
continue // try the next server
}
ev, err := gomatrixserverlib.NewEventFromUntrustedJSON(tx.PDUs[0], stateResp.RoomVersion)
if err != nil {
logger.WithError(err).Errorf("Failed to unmarshal auth event %q", missingAuthEventID)
continue // try the next server
}
if err = api.SendInputRoomEvents(
context.Background(),
t.rsAPI,
[]api.InputRoomEvent{
{
Kind: api.KindOutlier,
Event: ev.Headered(stateResp.RoomVersion),
AuthEventIDs: ev.AuthEventIDs(),
SendAsServer: api.DoNotSendToOtherServers,
},
},
); err != nil {
logger.WithError(err).Errorf("Failed to send auth event %q to roomserver", missingAuthEventID)
continue getAuthEvent // move onto the next event
}
}
}
}
if len(stateResp.MissingPrevEventIDs) > 0 {
logger.Infof("Event refers to %d unknown prev_events", len(stateResp.MissingPrevEventIDs))
return t.processEventWithMissingState(ctx, e, stateResp.RoomVersion, isInboundTxn) return t.processEventWithMissingState(ctx, e, stateResp.RoomVersion, isInboundTxn)
} }
@ -611,6 +657,7 @@ retryAllowedState:
// begin from. Returns an error only if we should terminate the transaction which initiated /get_missing_events // begin from. Returns an error only if we should terminate the transaction which initiated /get_missing_events
// This function recursively calls txnReq.processEvent with the missing events, which will be processed before this function returns. // This function recursively calls txnReq.processEvent with the missing events, which will be processed before this function returns.
// This means that we may recursively call this function, as we spider back up prev_events to the min depth. // This means that we may recursively call this function, as we spider back up prev_events to the min depth.
// nolint:gocyclo
func (t *txnReq) getMissingEvents(ctx context.Context, e gomatrixserverlib.Event, roomVersion gomatrixserverlib.RoomVersion, isInboundTxn bool) (backwardsExtremity *gomatrixserverlib.Event, err error) { func (t *txnReq) getMissingEvents(ctx context.Context, e gomatrixserverlib.Event, roomVersion gomatrixserverlib.RoomVersion, isInboundTxn bool) (backwardsExtremity *gomatrixserverlib.Event, err error) {
if !isInboundTxn { if !isInboundTxn {
// we've recursed here, so just take a state snapshot please! // we've recursed here, so just take a state snapshot please!
@ -637,15 +684,46 @@ func (t *txnReq) getMissingEvents(ctx context.Context, e gomatrixserverlib.Event
if minDepth < 0 { if minDepth < 0 {
minDepth = 0 minDepth = 0
} }
missingResp, err := t.federation.LookupMissingEvents(ctx, t.Origin, e.RoomID(), gomatrixserverlib.MissingEvents{
Limit: 20, servers := []gomatrixserverlib.ServerName{t.Origin}
// synapse uses the min depth they've ever seen in that room serverReq := &api.QueryServerJoinedToRoomRequest{
MinDepth: minDepth, RoomID: e.RoomID(),
// The latest event IDs that the sender already has. These are skipped when retrieving the previous events of latest_events. }
EarliestEvents: latestEvents, serverRes := &api.QueryServerJoinedToRoomResponse{}
// The event IDs to retrieve the previous events for. if err = t.rsAPI.QueryServerJoinedToRoom(ctx, serverReq, serverRes); err == nil {
LatestEvents: []string{e.EventID()}, servers = append(servers, serverRes.ServerNames...)
}, roomVersion) logger.Infof("Found %d server(s) to query for missing events", len(servers))
}
var missingResp *gomatrixserverlib.RespMissingEvents
for _, server := range servers {
var m gomatrixserverlib.RespMissingEvents
if m, err = t.federation.LookupMissingEvents(ctx, server, e.RoomID(), gomatrixserverlib.MissingEvents{
Limit: 20,
// synapse uses the min depth they've ever seen in that room
MinDepth: minDepth,
// The latest event IDs that the sender already has. These are skipped when retrieving the previous events of latest_events.
EarliestEvents: latestEvents,
// The event IDs to retrieve the previous events for.
LatestEvents: []string{e.EventID()},
}, roomVersion); err == nil {
missingResp = &m
break
} else {
logger.WithError(err).Errorf("%s pushed us an event but %q did not respond to /get_missing_events", t.Origin, server)
}
}
if missingResp == nil {
logger.WithError(err).Errorf(
"%s pushed us an event but %d server(s) couldn't give us details about prev_events via /get_missing_events - dropping this event until it can",
t.Origin, len(servers),
)
return nil, missingPrevEventsError{
eventID: e.EventID(),
err: err,
}
}
// security: how we handle failures depends on whether or not this event will become the new forward extremity for the room. // security: how we handle failures depends on whether or not this event will become the new forward extremity for the room.
// There's 2 scenarios to consider: // There's 2 scenarios to consider:
@ -658,16 +736,6 @@ func (t *txnReq) getMissingEvents(ctx context.Context, e gomatrixserverlib.Event
// https://github.com/matrix-org/synapse/pull/3456 // https://github.com/matrix-org/synapse/pull/3456
// https://github.com/matrix-org/synapse/blob/229eb81498b0fe1da81e9b5b333a0285acde9446/synapse/handlers/federation.py#L335 // https://github.com/matrix-org/synapse/blob/229eb81498b0fe1da81e9b5b333a0285acde9446/synapse/handlers/federation.py#L335
// For now, we do not allow Case B, so reject the event. // For now, we do not allow Case B, so reject the event.
if err != nil {
logger.WithError(err).Errorf(
"%s pushed us an event but couldn't give us details about prev_events via /get_missing_events - dropping this event until it can",
t.Origin,
)
return nil, missingPrevEventsError{
eventID: e.EventID(),
err: err,
}
}
logger.Infof("get_missing_events returned %d events", len(missingResp.Events)) logger.Infof("get_missing_events returned %d events", len(missingResp.Events))
// topologically sort and sanity check that we are making forward progress // topologically sort and sanity check that we are making forward progress

View file

@ -77,10 +77,11 @@ func (p *testEDUProducer) InputSendToDeviceEvent(
} }
type testRoomserverAPI struct { type testRoomserverAPI struct {
inputRoomEvents []api.InputRoomEvent inputRoomEvents []api.InputRoomEvent
queryStateAfterEvents func(*api.QueryStateAfterEventsRequest) api.QueryStateAfterEventsResponse queryMissingAuthPrevEvents func(*api.QueryMissingAuthPrevEventsRequest) api.QueryMissingAuthPrevEventsResponse
queryEventsByID func(req *api.QueryEventsByIDRequest) api.QueryEventsByIDResponse queryStateAfterEvents func(*api.QueryStateAfterEventsRequest) api.QueryStateAfterEventsResponse
queryLatestEventsAndState func(*api.QueryLatestEventsAndStateRequest) api.QueryLatestEventsAndStateResponse queryEventsByID func(req *api.QueryEventsByIDRequest) api.QueryEventsByIDResponse
queryLatestEventsAndState func(*api.QueryLatestEventsAndStateRequest) api.QueryLatestEventsAndStateResponse
} }
func (t *testRoomserverAPI) SetFederationSenderAPI(fsAPI fsAPI.FederationSenderInternalAPI) {} func (t *testRoomserverAPI) SetFederationSenderAPI(fsAPI fsAPI.FederationSenderInternalAPI) {}
@ -162,6 +163,20 @@ func (t *testRoomserverAPI) QueryStateAfterEvents(
return nil return nil
} }
// Query the state after a list of events in a room from the room server.
func (t *testRoomserverAPI) QueryMissingAuthPrevEvents(
ctx context.Context,
request *api.QueryMissingAuthPrevEventsRequest,
response *api.QueryMissingAuthPrevEventsResponse,
) error {
response.RoomVersion = testRoomVersion
res := t.queryMissingAuthPrevEvents(request)
response.RoomExists = res.RoomExists
response.MissingAuthEventIDs = res.MissingAuthEventIDs
response.MissingPrevEventIDs = res.MissingPrevEventIDs
return nil
}
// Query a list of events by event ID. // Query a list of events by event ID.
func (t *testRoomserverAPI) QueryEventsByID( func (t *testRoomserverAPI) QueryEventsByID(
ctx context.Context, ctx context.Context,
@ -453,11 +468,11 @@ func assertInputRoomEvents(t *testing.T, got []api.InputRoomEvent, want []gomatr
// to the roomserver. It's the most basic test possible. // to the roomserver. It's the most basic test possible.
func TestBasicTransaction(t *testing.T) { func TestBasicTransaction(t *testing.T) {
rsAPI := &testRoomserverAPI{ rsAPI := &testRoomserverAPI{
queryStateAfterEvents: func(req *api.QueryStateAfterEventsRequest) api.QueryStateAfterEventsResponse { queryMissingAuthPrevEvents: func(req *api.QueryMissingAuthPrevEventsRequest) api.QueryMissingAuthPrevEventsResponse {
return api.QueryStateAfterEventsResponse{ return api.QueryMissingAuthPrevEventsResponse{
PrevEventsExist: true, RoomExists: true,
RoomExists: true, MissingAuthEventIDs: []string{},
StateEvents: fromStateTuples(req.StateToFetch, nil), MissingPrevEventIDs: []string{},
} }
}, },
} }
@ -473,14 +488,11 @@ func TestBasicTransaction(t *testing.T) {
// as it does the auth check. // 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 { queryMissingAuthPrevEvents: func(req *api.QueryMissingAuthPrevEventsRequest) api.QueryMissingAuthPrevEventsResponse {
return api.QueryStateAfterEventsResponse{ return api.QueryMissingAuthPrevEventsResponse{
PrevEventsExist: true, RoomExists: true,
RoomExists: true, MissingAuthEventIDs: []string{"create_event"},
// omit the create event so auth checks fail MissingPrevEventIDs: []string{},
StateEvents: fromStateTuples(req.StateToFetch, []gomatrixserverlib.StateKeyTuple{
{EventType: gomatrixserverlib.MRoomCreate, StateKey: ""},
}),
} }
}, },
} }
@ -504,28 +516,24 @@ func TestTransactionFetchMissingPrevEvents(t *testing.T) {
var rsAPI *testRoomserverAPI // ref here so we can refer to inputRoomEvents inside these functions var rsAPI *testRoomserverAPI // ref here so we can refer to inputRoomEvents inside these functions
rsAPI = &testRoomserverAPI{ rsAPI = &testRoomserverAPI{
queryStateAfterEvents: func(req *api.QueryStateAfterEventsRequest) api.QueryStateAfterEventsResponse { queryMissingAuthPrevEvents: func(req *api.QueryMissingAuthPrevEventsRequest) api.QueryMissingAuthPrevEventsResponse {
// we expect this to be called three times: missingPrevEvent := []string{"missing_prev_event"}
// - first with input event to realise there's a gap
// - second with the prevEvent to realise there is no gap
// - third with the input event to realise there is no longer a gap
prevEventsExist := false
if len(req.PrevEventIDs) == 1 { if len(req.PrevEventIDs) == 1 {
switch req.PrevEventIDs[0] { switch req.PrevEventIDs[0] {
case haveEvent.EventID(): case haveEvent.EventID():
prevEventsExist = true missingPrevEvent = []string{}
case prevEvent.EventID(): case prevEvent.EventID():
// we only have this event if we've been send prevEvent // we only have this event if we've been send prevEvent
if len(rsAPI.inputRoomEvents) == 1 && rsAPI.inputRoomEvents[0].Event.EventID() == prevEvent.EventID() { if len(rsAPI.inputRoomEvents) == 1 && rsAPI.inputRoomEvents[0].Event.EventID() == prevEvent.EventID() {
prevEventsExist = true missingPrevEvent = []string{}
} }
} }
} }
return api.QueryStateAfterEventsResponse{ return api.QueryMissingAuthPrevEventsResponse{
PrevEventsExist: prevEventsExist, RoomExists: true,
RoomExists: true, MissingAuthEventIDs: []string{},
StateEvents: fromStateTuples(req.StateToFetch, nil), MissingPrevEventIDs: missingPrevEvent,
} }
}, },
queryLatestEventsAndState: func(req *api.QueryLatestEventsAndStateRequest) api.QueryLatestEventsAndStateResponse { queryLatestEventsAndState: func(req *api.QueryLatestEventsAndStateRequest) api.QueryLatestEventsAndStateResponse {
@ -626,6 +634,38 @@ func TestTransactionFetchMissingStateByStateIDs(t *testing.T) {
StateEvents: stateEvents, StateEvents: stateEvents,
} }
}, },
queryMissingAuthPrevEvents: func(req *api.QueryMissingAuthPrevEventsRequest) api.QueryMissingAuthPrevEventsResponse {
askingForEvent := req.PrevEventIDs[0]
haveEventB := false
haveEventC := false
for _, ev := range rsAPI.inputRoomEvents {
switch ev.Event.EventID() {
case eventB.EventID():
haveEventB = true
case eventC.EventID():
haveEventC = true
}
}
prevEventExists := false
if askingForEvent == eventC.EventID() {
prevEventExists = haveEventC
} else if askingForEvent == eventB.EventID() {
prevEventExists = haveEventB
}
var missingPrevEvent []string
if !prevEventExists {
missingPrevEvent = []string{"test"}
}
return api.QueryMissingAuthPrevEventsResponse{
RoomExists: true,
MissingAuthEventIDs: []string{},
MissingPrevEventIDs: missingPrevEvent,
}
},
queryLatestEventsAndState: func(req *api.QueryLatestEventsAndStateRequest) api.QueryLatestEventsAndStateResponse { queryLatestEventsAndState: func(req *api.QueryLatestEventsAndStateRequest) api.QueryLatestEventsAndStateResponse {
omitTuples := []gomatrixserverlib.StateKeyTuple{ omitTuples := []gomatrixserverlib.StateKeyTuple{
{EventType: gomatrixserverlib.MRoomPowerLevels, StateKey: ""}, {EventType: gomatrixserverlib.MRoomPowerLevels, StateKey: ""},

View file

@ -68,6 +68,13 @@ type RoomserverInternalAPI interface {
response *QueryStateAfterEventsResponse, response *QueryStateAfterEventsResponse,
) error ) error
// Query whether the roomserver is missing any auth or prev events.
QueryMissingAuthPrevEvents(
ctx context.Context,
request *QueryMissingAuthPrevEventsRequest,
response *QueryMissingAuthPrevEventsResponse,
) error
// Query a list of events by event ID. // Query a list of events by event ID.
QueryEventsByID( QueryEventsByID(
ctx context.Context, ctx context.Context,

View file

@ -104,6 +104,16 @@ func (t *RoomserverInternalAPITrace) QueryStateAfterEvents(
return err return err
} }
func (t *RoomserverInternalAPITrace) QueryMissingAuthPrevEvents(
ctx context.Context,
req *QueryMissingAuthPrevEventsRequest,
res *QueryMissingAuthPrevEventsResponse,
) error {
err := t.Impl.QueryMissingAuthPrevEvents(ctx, req, res)
util.GetLogger(ctx).WithError(err).Infof("QueryMissingAuthPrevEvents req=%+v res=%+v", js(req), js(res))
return err
}
func (t *RoomserverInternalAPITrace) QueryEventsByID( func (t *RoomserverInternalAPITrace) QueryEventsByID(
ctx context.Context, ctx context.Context,
req *QueryEventsByIDRequest, req *QueryEventsByIDRequest,

View file

@ -82,6 +82,27 @@ type QueryStateAfterEventsResponse struct {
StateEvents []gomatrixserverlib.HeaderedEvent `json:"state_events"` StateEvents []gomatrixserverlib.HeaderedEvent `json:"state_events"`
} }
type QueryMissingAuthPrevEventsRequest struct {
// The room ID to query the state in.
RoomID string `json:"room_id"`
// The list of auth events to check the existence of.
AuthEventIDs []string `json:"auth_event_ids"`
// The list of previous events to check the existence of.
PrevEventIDs []string `json:"prev_event_ids"`
}
type QueryMissingAuthPrevEventsResponse struct {
// Does the room exist on this roomserver?
// If the room doesn't exist all other fields will be empty.
RoomExists bool `json:"room_exists"`
// The room version of the room.
RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"`
// The event IDs of the auth events that we don't know locally.
MissingAuthEventIDs []string `json:"missing_auth_event_ids"`
// The event IDs of the previous events that we don't know locally.
MissingPrevEventIDs []string `json:"missing_prev_event_ids"`
}
// QueryEventsByIDRequest is a request to QueryEventsByID // QueryEventsByIDRequest is a request to QueryEventsByID
type QueryEventsByIDRequest struct { type QueryEventsByIDRequest struct {
// The event IDs to look up. // The event IDs to look up.
@ -154,6 +175,8 @@ type QueryServerJoinedToRoomResponse struct {
RoomExists bool `json:"room_exists"` RoomExists bool `json:"room_exists"`
// True if we still believe that we are participating in the room // True if we still believe that we are participating in the room
IsInRoom bool `json:"is_in_room"` IsInRoom bool `json:"is_in_room"`
// List of servers that are also in the room
ServerNames []gomatrixserverlib.ServerName `json:"server_names"`
} }
// QueryServerAllowedToSeeEventRequest is a request to QueryServerAllowedToSeeEvent // QueryServerAllowedToSeeEventRequest is a request to QueryServerAllowedToSeeEvent

View file

@ -83,7 +83,7 @@ func CheckForSoftFail(
// Check if the event is allowed. // Check if the event is allowed.
if err = gomatrixserverlib.Allowed(event.Event, &authEvents); err != nil { if err = gomatrixserverlib.Allowed(event.Event, &authEvents); err != nil {
// return true, nil // return true, nil
return true, fmt.Errorf("gomatrixserverlib.Allowed: %w", err) return true, err
} }
return false, nil return false, nil
} }
@ -99,7 +99,7 @@ func CheckAuthEvents(
// Grab the numeric IDs for the supplied auth state events from the database. // Grab the numeric IDs for the supplied auth state events from the database.
authStateEntries, err := db.StateEntriesForEventIDs(ctx, authEventIDs) authStateEntries, err := db.StateEntriesForEventIDs(ctx, authEventIDs)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("db.StateEntriesForEventIDs: %w", err)
} }
authStateEntries = types.DeduplicateStateEntries(authStateEntries) authStateEntries = types.DeduplicateStateEntries(authStateEntries)
@ -109,7 +109,7 @@ func CheckAuthEvents(
// Load the actual auth events from the database. // Load the actual auth events from the database.
authEvents, err := loadAuthEvents(ctx, db, stateNeeded, authStateEntries) authEvents, err := loadAuthEvents(ctx, db, stateNeeded, authStateEntries)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("loadAuthEvents: %w", err)
} }
// Check if the event is allowed. // Check if the event is allowed.

View file

@ -49,7 +49,7 @@ func (r *Inputer) processRoomEvent(
isRejected := false isRejected := false
authEventNIDs, rejectionErr := helpers.CheckAuthEvents(ctx, r.DB, headered, input.AuthEventIDs) authEventNIDs, rejectionErr := helpers.CheckAuthEvents(ctx, r.DB, headered, input.AuthEventIDs)
if rejectionErr != nil { if rejectionErr != nil {
logrus.WithError(rejectionErr).WithField("event_id", event.EventID()).WithField("auth_event_ids", input.AuthEventIDs).Error("processRoomEvent.checkAuthEvents failed for event, rejecting event") logrus.WithError(rejectionErr).WithField("event_id", event.EventID()).WithField("auth_event_ids", input.AuthEventIDs).Error("helpers.CheckAuthEvents failed for event, rejecting event")
isRejected = true isRejected = true
} }

View file

@ -136,14 +136,10 @@ func (r *Inviter) PerformInvite(
log.WithError(err).WithField("event_id", event.EventID()).WithField("auth_event_ids", event.AuthEventIDs()).Error( log.WithError(err).WithField("event_id", event.EventID()).WithField("auth_event_ids", event.AuthEventIDs()).Error(
"processInviteEvent.checkAuthEvents failed for event", "processInviteEvent.checkAuthEvents failed for event",
) )
if _, ok := err.(*gomatrixserverlib.NotAllowed); ok { res.Error = &api.PerformError{
res.Error = &api.PerformError{ Msg: err.Error(),
Msg: err.Error(), Code: api.PerformErrorNotAllowed,
Code: api.PerformErrorNotAllowed,
}
return nil, nil
} }
return nil, fmt.Errorf("checkAuthEvents: %w", err)
} }
// If the invite originated from us and the target isn't local then we // If the invite originated from us and the target isn't local then we
@ -160,7 +156,7 @@ func (r *Inviter) PerformInvite(
if err = r.FSAPI.PerformInvite(ctx, fsReq, fsRes); err != nil { if err = r.FSAPI.PerformInvite(ctx, fsReq, fsRes); err != nil {
res.Error = &api.PerformError{ res.Error = &api.PerformError{
Msg: err.Error(), Msg: err.Error(),
Code: api.PerformErrorNoOperation, Code: api.PerformErrorNotAllowed,
} }
log.WithError(err).WithField("event_id", event.EventID()).Error("r.FSAPI.PerformInvite failed") log.WithError(err).WithField("event_id", event.EventID()).Error("r.FSAPI.PerformInvite failed")
return nil, nil return nil, nil
@ -185,7 +181,12 @@ func (r *Inviter) PerformInvite(
inputRes := &api.InputRoomEventsResponse{} inputRes := &api.InputRoomEventsResponse{}
r.Inputer.InputRoomEvents(context.Background(), inputReq, inputRes) r.Inputer.InputRoomEvents(context.Background(), inputReq, inputRes)
if err = inputRes.Err(); err != nil { if err = inputRes.Err(); err != nil {
return nil, fmt.Errorf("r.InputRoomEvents: %w", err) res.Error = &api.PerformError{
Msg: fmt.Sprintf("r.InputRoomEvents: %s", err.Error()),
Code: api.PerformErrorNotAllowed,
}
log.WithError(err).WithField("event_id", event.EventID()).Error("r.InputRoomEvents failed")
return nil, nil
} }
} else { } else {
// The invite originated over federation. Process the membership // The invite originated over federation. Process the membership

View file

@ -249,14 +249,10 @@ func (r *Joiner) performJoinRoomByID(
inputRes := api.InputRoomEventsResponse{} inputRes := api.InputRoomEventsResponse{}
r.Inputer.InputRoomEvents(ctx, &inputReq, &inputRes) r.Inputer.InputRoomEvents(ctx, &inputReq, &inputRes)
if err = inputRes.Err(); err != nil { if err = inputRes.Err(); err != nil {
var notAllowed *gomatrixserverlib.NotAllowed return "", &api.PerformError{
if errors.As(err, &notAllowed) { Code: api.PerformErrorNotAllowed,
return "", &api.PerformError{ Msg: fmt.Sprintf("InputRoomEvents auth failed: %s", err),
Code: api.PerformErrorNotAllowed,
Msg: fmt.Sprintf("InputRoomEvents auth failed: %s", err),
}
} }
return "", fmt.Errorf("r.InputRoomEvents: %w", err)
} }
} }

View file

@ -98,6 +98,38 @@ func (r *Queryer) QueryStateAfterEvents(
return nil return nil
} }
// QueryMissingAuthPrevEvents implements api.RoomserverInternalAPI
func (r *Queryer) QueryMissingAuthPrevEvents(
ctx context.Context,
request *api.QueryMissingAuthPrevEventsRequest,
response *api.QueryMissingAuthPrevEventsResponse,
) error {
info, err := r.DB.RoomInfo(ctx, request.RoomID)
if err != nil {
return err
}
if info == nil {
return errors.New("room doesn't exist")
}
response.RoomExists = !info.IsStub
response.RoomVersion = info.RoomVersion
for _, authEventID := range request.AuthEventIDs {
if nids, err := r.DB.EventNIDs(ctx, []string{authEventID}); err != nil || len(nids) == 0 {
response.MissingAuthEventIDs = append(response.MissingAuthEventIDs, authEventID)
}
}
for _, prevEventID := range request.PrevEventIDs {
if nids, err := r.DB.EventNIDs(ctx, []string{prevEventID}); err != nil || len(nids) == 0 {
response.MissingPrevEventIDs = append(response.MissingPrevEventIDs, prevEventID)
}
}
return nil
}
// QueryEventsByID implements api.RoomserverInternalAPI // QueryEventsByID implements api.RoomserverInternalAPI
func (r *Queryer) QueryEventsByID( func (r *Queryer) QueryEventsByID(
ctx context.Context, ctx context.Context,
@ -255,19 +287,24 @@ func (r *Queryer) QueryServerJoinedToRoom(
return fmt.Errorf("r.DB.Events: %w", err) return fmt.Errorf("r.DB.Events: %w", err)
} }
servers := map[gomatrixserverlib.ServerName]struct{}{}
for _, e := range events { for _, e := range events {
if e.Type() == gomatrixserverlib.MRoomMember && e.StateKey() != nil { if e.Type() == gomatrixserverlib.MRoomMember && e.StateKey() != nil {
_, serverName, err := gomatrixserverlib.SplitID('@', *e.StateKey()) _, serverName, err := gomatrixserverlib.SplitID('@', *e.StateKey())
if err != nil { if err != nil {
continue continue
} }
servers[serverName] = struct{}{}
if serverName == request.ServerName { if serverName == request.ServerName {
response.IsInRoom = true response.IsInRoom = true
break
} }
} }
} }
for server := range servers {
response.ServerNames = append(response.ServerNames, server)
}
return nil return nil
} }

View file

@ -35,6 +35,7 @@ const (
// Query operations // Query operations
RoomserverQueryLatestEventsAndStatePath = "/roomserver/queryLatestEventsAndState" RoomserverQueryLatestEventsAndStatePath = "/roomserver/queryLatestEventsAndState"
RoomserverQueryStateAfterEventsPath = "/roomserver/queryStateAfterEvents" RoomserverQueryStateAfterEventsPath = "/roomserver/queryStateAfterEvents"
RoomserverQueryMissingAuthPrevEventsPath = "/roomserver/queryMissingAuthPrevEvents"
RoomserverQueryEventsByIDPath = "/roomserver/queryEventsByID" RoomserverQueryEventsByIDPath = "/roomserver/queryEventsByID"
RoomserverQueryMembershipForUserPath = "/roomserver/queryMembershipForUser" RoomserverQueryMembershipForUserPath = "/roomserver/queryMembershipForUser"
RoomserverQueryMembershipsForRoomPath = "/roomserver/queryMembershipsForRoom" RoomserverQueryMembershipsForRoomPath = "/roomserver/queryMembershipsForRoom"
@ -262,6 +263,19 @@ func (h *httpRoomserverInternalAPI) QueryStateAfterEvents(
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response) return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
} }
// QueryStateAfterEvents implements RoomserverQueryAPI
func (h *httpRoomserverInternalAPI) QueryMissingAuthPrevEvents(
ctx context.Context,
request *api.QueryMissingAuthPrevEventsRequest,
response *api.QueryMissingAuthPrevEventsResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryMissingAuthPrevEvents")
defer span.Finish()
apiURL := h.roomserverURL + RoomserverQueryMissingAuthPrevEventsPath
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
}
// QueryEventsByID implements RoomserverQueryAPI // QueryEventsByID implements RoomserverQueryAPI
func (h *httpRoomserverInternalAPI) QueryEventsByID( func (h *httpRoomserverInternalAPI) QueryEventsByID(
ctx context.Context, ctx context.Context,

View file

@ -125,6 +125,20 @@ func AddRoutes(r api.RoomserverInternalAPI, internalAPIMux *mux.Router) {
return util.JSONResponse{Code: http.StatusOK, JSON: &response} return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}), }),
) )
internalAPIMux.Handle(
RoomserverQueryMissingAuthPrevEventsPath,
httputil.MakeInternalAPI("queryMissingAuthPrevEvents", func(req *http.Request) util.JSONResponse {
var request api.QueryMissingAuthPrevEventsRequest
var response api.QueryMissingAuthPrevEventsResponse
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.ErrorResponse(err)
}
if err := r.QueryMissingAuthPrevEvents(req.Context(), &request, &response); err != nil {
return util.ErrorResponse(err)
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryEventsByIDPath, RoomserverQueryEventsByIDPath,
httputil.MakeInternalAPI("queryEventsByID", func(req *http.Request) util.JSONResponse { httputil.MakeInternalAPI("queryEventsByID", func(req *http.Request) util.JSONResponse {