Improve room hiearchy error handling + comments

This commit is contained in:
Sam Wedgwood 2023-07-06 13:38:10 +01:00
parent 8571b6a01b
commit b98e50f544
6 changed files with 60 additions and 4 deletions

View file

@ -26,6 +26,7 @@ import (
"github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec" "github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
log "github.com/sirupsen/logrus"
) )
type RoomHierarchyPaginationCache struct { type RoomHierarchyPaginationCache struct {
@ -54,6 +55,9 @@ func (c *RoomHierarchyPaginationCache) AddLine(line roomserverAPI.CachedRoomHier
return token return token
} }
// Query the hierarchy of a room/space
//
// Implements /_matrix/client/v1/rooms/{roomID}/hierarchy
func QueryRoomHierarchy(req *http.Request, device *userapi.Device, roomIDStr string, rsAPI roomserverAPI.ClientRoomserverAPI, paginationCache *RoomHierarchyPaginationCache) util.JSONResponse { func QueryRoomHierarchy(req *http.Request, device *userapi.Device, roomIDStr string, rsAPI roomserverAPI.ClientRoomserverAPI, paginationCache *RoomHierarchyPaginationCache) util.JSONResponse {
parsedRoomID, err := spec.NewRoomID(roomIDStr) parsedRoomID, err := spec.NewRoomID(roomIDStr)
if err != nil { if err != nil {
@ -127,7 +131,19 @@ func QueryRoomHierarchy(req *http.Request, device *userapi.Device, roomIDStr str
discoveredRooms, err := walker.NextPage(limit) discoveredRooms, err := walker.NextPage(limit)
if err != nil { if err != nil {
// TODO switch err.(type) {
case roomserverAPI.ErrRoomUnknownOrNotAllowed:
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: spec.Forbidden("room is unknown/forbidden"),
}
default:
log.WithError(err).Errorf("failed to fetch next page of room hierarchy (CS API)")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("internal server error"),
}
}
} }
nextBatch := "" nextBatch := ""

View file

@ -508,6 +508,7 @@ func Setup(
).Methods(http.MethodPut, http.MethodOptions) ).Methods(http.MethodPut, http.MethodOptions)
// Defined outside of handler to persist between calls // Defined outside of handler to persist between calls
// TODO: clear based on some criteria
roomHierarchyPaginationCache := new(RoomHierarchyPaginationCache) roomHierarchyPaginationCache := new(RoomHierarchyPaginationCache)
v1mux.Handle("/rooms/{roomID}/hierarchy", v1mux.Handle("/rooms/{roomID}/hierarchy",
httputil.MakeAuthAPI("spaces", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("spaces", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {

View file

@ -27,6 +27,7 @@ import (
"github.com/matrix-org/gomatrixserverlib/fclient" "github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec" "github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util" "github.com/matrix-org/util"
log "github.com/sirupsen/logrus"
) )
// RoomAliasToID converts the queried alias into a room ID and returns it // RoomAliasToID converts the queried alias into a room ID and returns it
@ -118,6 +119,9 @@ func RoomAliasToID(
} }
} }
// Query the immediate children of a room/space
//
// Implements /_matrix/federation/v1/hierarchy/{roomID}
func QueryRoomHierarchy(httpReq *http.Request, request *fclient.FederationRequest, roomIDStr string, rsAPI roomserverAPI.FederationRoomserverAPI) util.JSONResponse { func QueryRoomHierarchy(httpReq *http.Request, request *fclient.FederationRequest, roomIDStr string, rsAPI roomserverAPI.FederationRoomserverAPI) util.JSONResponse {
parsedRoomID, err := spec.NewRoomID(roomIDStr) parsedRoomID, err := spec.NewRoomID(roomIDStr)
if err != nil { if err != nil {
@ -146,7 +150,19 @@ func QueryRoomHierarchy(httpReq *http.Request, request *fclient.FederationReques
discoveredRooms, err := walker.NextPage(-1) discoveredRooms, err := walker.NextPage(-1)
if err != nil { if err != nil {
// TODO switch err.(type) {
case roomserverAPI.ErrRoomUnknownOrNotAllowed:
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: spec.NotFound("room is unknown/forbidden"),
}
default:
log.WithError(err).Errorf("failed to fetch next page of room hierarchy (SS API)")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.Unknown("internal server error"),
}
}
} }
if len(discoveredRooms) == 0 { if len(discoveredRooms) == 0 {

View file

@ -34,6 +34,17 @@ func (e ErrNotAllowed) Error() string {
return e.Err.Error() return e.Err.Error()
} }
// ErrRoomUnknownOrNotAllowed is an error return if either the provided
// room ID does not exist, or points to a room that the requester does
// not have access to.
type ErrRoomUnknownOrNotAllowed struct {
Err error
}
func (e ErrRoomUnknownOrNotAllowed) Error() string {
return e.Err.Error()
}
type RestrictedJoinAPI interface { type RestrictedJoinAPI interface {
CurrentStateEvent(ctx context.Context, roomID spec.RoomID, eventType string, stateKey string) (gomatrixserverlib.PDU, error) CurrentStateEvent(ctx context.Context, roomID spec.RoomID, eventType string, stateKey string) (gomatrixserverlib.PDU, error)
InvitePending(ctx context.Context, roomID spec.RoomID, senderID spec.SenderID) (bool, error) InvitePending(ctx context.Context, roomID spec.RoomID, senderID spec.SenderID) (bool, error)

View file

@ -512,9 +512,17 @@ type QueryRoomHierarchyRequest struct {
From int `json:"json"` From int `json:"json"`
} }
// An iterator-like interface for walking a room/space hierarchy, returning each rooms information.
//
// Used for implementing space summaries / room hierarchies
type RoomHierarchyWalker interface { type RoomHierarchyWalker interface {
// Walk the room hierarchy to retrieve room information until either
// no room left, or provided limit reached. If limit provided is -1, then this is
// treated as no limit.
NextPage(limit int) ([]fclient.MSC2946Room, error) NextPage(limit int) ([]fclient.MSC2946Room, error)
// Returns true if there are no more rooms left to walk
Done() bool Done() bool
// Returns a stripped down version of the hiearchy walker suitable for pagination caching
GetCached() CachedRoomHierarchyWalker GetCached() CachedRoomHierarchyWalker
} }

View file

@ -17,6 +17,7 @@ package query
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"sort" "sort"
"strings" "strings"
@ -92,9 +93,12 @@ const (
ConstSpaceParentEventType = "m.space.parent" ConstSpaceParentEventType = "m.space.parent"
) )
// Walk the room hierarchy to retrieve room information until either
// no room left, or provided limit reached. If limit provided is -1, then this is
// treated as no limit.
func (w *RoomHierarchyWalker) NextPage(limit int) ([]fclient.MSC2946Room, error) { func (w *RoomHierarchyWalker) NextPage(limit int) ([]fclient.MSC2946Room, error) {
if authorised, _ := w.authorised(w.rootRoomID, ""); !authorised { if authorised, _ := w.authorised(w.rootRoomID, ""); !authorised {
return nil, spec.Forbidden("room is unknown/forbidden") return nil, roomserver.ErrRoomUnknownOrNotAllowed{Err: fmt.Errorf("room is unknown/forbidden")}
} }
var discoveredRooms []fclient.MSC2946Room var discoveredRooms []fclient.MSC2946Room
@ -277,7 +281,7 @@ func (w *RoomHierarchyWalker) federatedRoomInfo(roomID string, vias []string) *f
} }
res, err := w.fsAPI.RoomHierarchies(ctx, w.thisServer, spec.ServerName(serverName), roomID, w.suggestedOnly) res, err := w.fsAPI.RoomHierarchies(ctx, w.thisServer, spec.ServerName(serverName), roomID, w.suggestedOnly)
if err != nil { if err != nil {
util.GetLogger(w.ctx).WithError(err).Warnf("failed to call MSC2946Spaces on server %s", serverName) util.GetLogger(w.ctx).WithError(err).Warnf("failed to call RoomHierarchies on server %s", serverName)
continue continue
} }
// ensure nil slices are empty as we send this to the client sometimes // ensure nil slices are empty as we send this to the client sometimes