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 tweaksmain
parent
4ff7ac7b65
commit
738b829a23
|
@ -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,7 +684,21 @@ 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{
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
Limit: 20,
|
||||||
// synapse uses the min depth they've ever seen in that room
|
// synapse uses the min depth they've ever seen in that room
|
||||||
MinDepth: minDepth,
|
MinDepth: minDepth,
|
||||||
|
@ -645,7 +706,24 @@ func (t *txnReq) getMissingEvents(ctx context.Context, e gomatrixserverlib.Event
|
||||||
EarliestEvents: latestEvents,
|
EarliestEvents: latestEvents,
|
||||||
// The event IDs to retrieve the previous events for.
|
// The event IDs to retrieve the previous events for.
|
||||||
LatestEvents: []string{e.EventID()},
|
LatestEvents: []string{e.EventID()},
|
||||||
}, roomVersion)
|
}, 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
|
||||||
|
|
|
@ -78,6 +78,7 @@ func (p *testEDUProducer) InputSendToDeviceEvent(
|
||||||
|
|
||||||
type testRoomserverAPI struct {
|
type testRoomserverAPI struct {
|
||||||
inputRoomEvents []api.InputRoomEvent
|
inputRoomEvents []api.InputRoomEvent
|
||||||
|
queryMissingAuthPrevEvents func(*api.QueryMissingAuthPrevEventsRequest) api.QueryMissingAuthPrevEventsResponse
|
||||||
queryStateAfterEvents func(*api.QueryStateAfterEventsRequest) api.QueryStateAfterEventsResponse
|
queryStateAfterEvents func(*api.QueryStateAfterEventsRequest) api.QueryStateAfterEventsResponse
|
||||||
queryEventsByID func(req *api.QueryEventsByIDRequest) api.QueryEventsByIDResponse
|
queryEventsByID func(req *api.QueryEventsByIDRequest) api.QueryEventsByIDResponse
|
||||||
queryLatestEventsAndState func(*api.QueryLatestEventsAndStateRequest) api.QueryLatestEventsAndStateResponse
|
queryLatestEventsAndState func(*api.QueryLatestEventsAndStateRequest) api.QueryLatestEventsAndStateResponse
|
||||||
|
@ -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,
|
||||||
StateEvents: fromStateTuples(req.StateToFetch, nil),
|
MissingAuthEventIDs: []string{},
|
||||||
|
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,
|
||||||
// omit the create event so auth checks fail
|
MissingAuthEventIDs: []string{"create_event"},
|
||||||
StateEvents: fromStateTuples(req.StateToFetch, []gomatrixserverlib.StateKeyTuple{
|
MissingPrevEventIDs: []string{},
|
||||||
{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,
|
||||||
StateEvents: fromStateTuples(req.StateToFetch, nil),
|
MissingAuthEventIDs: []string{},
|
||||||
|
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: ""},
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -249,15 +249,11 @@ 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
|
|
||||||
if errors.As(err, ¬Allowed) {
|
|
||||||
return "", &api.PerformError{
|
return "", &api.PerformError{
|
||||||
Code: api.PerformErrorNotAllowed,
|
Code: api.PerformErrorNotAllowed,
|
||||||
Msg: fmt.Sprintf("InputRoomEvents auth failed: %s", err),
|
Msg: fmt.Sprintf("InputRoomEvents auth failed: %s", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("r.InputRoomEvents: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case eventutil.ErrRoomNoExists:
|
case eventutil.ErrRoomNoExists:
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue