matrix room Id does not resolve to channelId or spaceId correctly (#1010)

Issue: matrix room id does not always resolve to spaceId or channelId
correctly.

Root cause: The clientApi routing endpoint and the syncapi routing
endpoint uses different stores to query for the current room states.

One is correct, the other has incomplete events. Fix the issue by using
the correct store in both routing code paths.
This commit is contained in:
Tak Wai Wong 2022-11-30 20:12:00 -08:00 committed by GitHub
parent a8127f9bdc
commit 8f4d1828b4
5 changed files with 31 additions and 132 deletions

View file

@ -2,16 +2,16 @@ package authorization
import ( import (
"github.com/matrix-org/dendrite/authorization" "github.com/matrix-org/dendrite/authorization"
roomserver "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
_ "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/zion" "github.com/matrix-org/dendrite/zion"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
func NewRoomserverAuthorization(cfg *config.ClientAPI, rsAPI zion.RoomserverStoreAPI) authorization.Authorization { func NewRoomserverAuthorization(cfg *config.ClientAPI, roomQueryAPI roomserver.QueryEventsAPI) authorization.Authorization {
// Load authorization manager for Zion // Load authorization manager for Zion
if cfg.PublicKeyAuthentication.Ethereum.GetEnableAuthZ() { if cfg.PublicKeyAuthentication.Ethereum.GetEnableAuthZ() {
auth, err := zion.NewZionAuthorization(cfg, rsAPI) auth, err := zion.NewZionAuthorization(cfg, roomQueryAPI)
if err != nil { if err != nil {
log.Errorln("Failed to initialise Zion authorization manager. Using default.", err) log.Errorln("Failed to initialise Zion authorization manager. Using default.", err)

View file

@ -42,7 +42,6 @@ import (
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/setup/jetstream"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
zion "github.com/matrix-org/dendrite/zion"
) )
var ReleaseVersion string var ReleaseVersion string
@ -77,8 +76,7 @@ func Setup(
rateLimits := httputil.NewRateLimits(&cfg.RateLimiting) rateLimits := httputil.NewRateLimits(&cfg.RateLimiting)
userInteractiveAuth := auth.NewUserInteractive(userAPI, userAPI, cfg) userInteractiveAuth := auth.NewUserInteractive(userAPI, userAPI, cfg)
clientAuthz := zion.ClientRoomserverStruct{ClientRoomserverAPI: rsAPI} authorization := clientApiAuthz.NewRoomserverAuthorization(cfg, rsAPI)
authorization := clientApiAuthz.NewRoomserverAuthorization(cfg, clientAuthz)
unstableFeatures := map[string]bool{ unstableFeatures := map[string]bool{
"org.matrix.e2e_cross_signing": true, "org.matrix.e2e_cross_signing": true,

View file

@ -52,8 +52,7 @@ func Setup(
lazyLoadCache caching.LazyLoadCache, lazyLoadCache caching.LazyLoadCache,
fts *fulltext.Search, fts *fulltext.Search,
) { ) {
syncAuthz := zion.SyncRoomserverStruct{SyncRoomserverAPI: rsAPI} authorization := clientApiAuthz.NewRoomserverAuthorization(clientCfg, crsAPI)
authorization := clientApiAuthz.NewRoomserverAuthorization(clientCfg, syncAuthz)
v1unstablemux := csMux.PathPrefix("/{apiversion:(?:v1|unstable)}/").Subrouter() v1unstablemux := csMux.PathPrefix("/{apiversion:(?:v1|unstable)}/").Subrouter()
v3mux := csMux.PathPrefix("/{apiversion:(?:r0|v3)}/").Subrouter() v3mux := csMux.PathPrefix("/{apiversion:(?:r0|v3)}/").Subrouter()

View file

@ -12,19 +12,17 @@ import (
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
) )
type ClientRoomserverStore struct { type Store struct {
rsAPI roomserver.ClientRoomserverAPI roomQuery roomserver.QueryEventsAPI
} }
type SyncRoomserverStore struct { func NewStore(roomQuery roomserver.QueryEventsAPI) Store {
rsAPI roomserver.SyncRoomserverAPI return Store{
roomQuery: roomQuery,
}
} }
type StoreAPI interface { func (s *Store) GetRoomInfo(roomId string, userId UserIdentifier) RoomInfo {
GetRoomInfo(roomId string, userId UserIdentifier) RoomInfo
}
func (s *ClientRoomserverStore) GetRoomInfo(roomId string, userId UserIdentifier) RoomInfo {
result := RoomInfo{ result := RoomInfo{
QueryUserId: userId.MatrixUserId, QueryUserId: userId.MatrixUserId,
SpaceNetworkId: "", SpaceNetworkId: "",
@ -49,7 +47,7 @@ func (s *ClientRoomserverStore) GetRoomInfo(roomId string, userId UserIdentifier
} }
var roomEvents roomserver.QueryCurrentStateResponse var roomEvents roomserver.QueryCurrentStateResponse
err := s.rsAPI.QueryCurrentState(context.Background(), &roomserver.QueryCurrentStateRequest{ err := s.roomQuery.QueryCurrentState(context.Background(), &roomserver.QueryCurrentStateRequest{
RoomID: roomId, RoomID: roomId,
AllowWildcards: true, AllowWildcards: true,
StateTuples: []gomatrixserverlib.StateKeyTuple{ StateTuples: []gomatrixserverlib.StateKeyTuple{
@ -63,9 +61,11 @@ func (s *ClientRoomserverStore) GetRoomInfo(roomId string, userId UserIdentifier
return result return result
} }
//TODO: replace with HydrateRoomInfoWithStateEvents when you have a practical way to flatten roomEvents map //TODO: replace with HydrateRoomInfoWithStateEvents when you have a practical way to flatten roomEvents map
doneSearching := false
for _, state := range roomEvents.StateEvents { for _, state := range roomEvents.StateEvents {
switch state.Type() { switch state.Type() {
case gomatrixserverlib.MRoomCreate: case gomatrixserverlib.MRoomCreate:
// Space is created with no children yet.
var creatorEvent CreatorEvent var creatorEvent CreatorEvent
err := json.Unmarshal(roomEvents.StateEvents[createTuple].Content(), &creatorEvent) err := json.Unmarshal(roomEvents.StateEvents[createTuple].Content(), &creatorEvent)
result.IsOwner = strings.HasPrefix( result.IsOwner = strings.HasPrefix(
@ -77,96 +77,21 @@ func (s *ClientRoomserverStore) GetRoomInfo(roomId string, userId UserIdentifier
result.SpaceNetworkId = roomId result.SpaceNetworkId = roomId
} }
case ConstSpaceChildEventType: case ConstSpaceChildEventType:
// Space is created and has one or more children.
result.RoomType = Space result.RoomType = Space
result.SpaceNetworkId = roomId result.SpaceNetworkId = roomId
doneSearching = true
case ConstSpaceParentEventType: case ConstSpaceParentEventType:
// Channel is created and has a reference to the parent.
result.RoomType = Channel result.RoomType = Channel
result.SpaceNetworkId = *state.StateKey() result.SpaceNetworkId = *state.StateKey()
result.ChannelNetworkId = roomId result.ChannelNetworkId = roomId
doneSearching = true
}
if doneSearching {
break
} }
} }
return result return result
} }
func (s *SyncRoomserverStore) GetRoomInfo(roomId string, userId UserIdentifier) RoomInfo {
result := RoomInfo{
QueryUserId: userId.MatrixUserId,
SpaceNetworkId: "",
ChannelNetworkId: "",
RoomType: Unknown,
IsOwner: false,
}
createTuple := gomatrixserverlib.StateKeyTuple{
EventType: gomatrixserverlib.MRoomCreate,
StateKey: "",
}
spaceChildTuple := gomatrixserverlib.StateKeyTuple{
EventType: ConstSpaceChildEventType,
StateKey: "*",
}
spaceParentTuple := gomatrixserverlib.StateKeyTuple{
EventType: ConstSpaceParentEventType,
StateKey: "*",
}
var roomEvents roomserver.QueryLatestEventsAndStateResponse
err := s.rsAPI.QueryLatestEventsAndState(context.Background(),
&roomserver.QueryLatestEventsAndStateRequest{
RoomID: roomId,
StateToFetch: []gomatrixserverlib.StateKeyTuple{
createTuple,
spaceParentTuple,
spaceChildTuple,
},
}, &roomEvents)
if err != nil {
return result
}
HydrateRoomInfoWithStateEvents(roomId, userId, &result, roomEvents.StateEvents)
return result
}
func HydrateRoomInfoWithStateEvents(roomId string, userId UserIdentifier, r *RoomInfo, stateEvents []*gomatrixserverlib.HeaderedEvent) {
for _, state := range stateEvents {
switch state.Type() {
case gomatrixserverlib.MRoomCreate:
var creatorEvent CreatorEvent
err := json.Unmarshal(state.Content(), &creatorEvent)
r.IsOwner = strings.HasPrefix(
creatorEvent.Creator,
userId.LocalPart,
)
if err == nil {
r.RoomType = Space
r.SpaceNetworkId = roomId
}
case ConstSpaceChildEventType:
r.RoomType = Space
r.SpaceNetworkId = roomId
case ConstSpaceParentEventType:
r.RoomType = Channel
r.SpaceNetworkId = *state.StateKey()
r.ChannelNetworkId = roomId
}
}
}
func NewClientRoomserverStore(rsAPI roomserver.ClientRoomserverAPI) StoreAPI {
return &ClientRoomserverStore{
rsAPI: rsAPI,
}
}
func NewSyncRoomserverStore(rsAPI roomserver.SyncRoomserverAPI) StoreAPI {
return &SyncRoomserverStore{
rsAPI: rsAPI,
}
}

View file

@ -23,46 +23,22 @@ var ErrSpaceDisabled = errors.New("space disabled")
var ErrChannelDisabled = errors.New("channel disabled") var ErrChannelDisabled = errors.New("channel disabled")
type ZionAuthorization struct { type ZionAuthorization struct {
store StoreAPI chainId int
spaceManagerLocalhost *zion_localhost.ZionSpaceManagerLocalhost spaceManagerLocalhost *zion_localhost.ZionSpaceManagerLocalhost
spaceManagerGoerli *zion_goerli.ZionSpaceManagerGoerli spaceManagerGoerli *zion_goerli.ZionSpaceManagerGoerli
chainId int store Store
}
type ClientRoomserverStruct struct {
roomserver.ClientRoomserverAPI
} }
type SyncRoomserverStruct struct { func NewZionAuthorization(cfg *config.ClientAPI, roomQueryAPI roomserver.QueryEventsAPI) (authorization.Authorization, error) {
roomserver.SyncRoomserverAPI
}
type RoomserverStoreAPI interface {
roomserver.QueryLatestEventsAndStateAPI
NewRoomserverStore() StoreAPI
}
func (c ClientRoomserverStruct) NewRoomserverStore() StoreAPI {
return &ClientRoomserverStore{
rsAPI: c,
}
}
func (c SyncRoomserverStruct) NewRoomserverStore() StoreAPI {
return &SyncRoomserverStore{
rsAPI: c,
}
}
func NewZionAuthorization(cfg *config.ClientAPI, rsAPI RoomserverStoreAPI) (authorization.Authorization, error) {
if cfg.PublicKeyAuthentication.Ethereum.NetworkUrl == "" { if cfg.PublicKeyAuthentication.Ethereum.NetworkUrl == "" {
log.Errorf("No blockchain network url specified in config\n") log.Errorf("No blockchain network url specified in config\n")
return nil, nil return nil, nil
} }
var auth ZionAuthorization auth := ZionAuthorization{
store: NewStore(roomQueryAPI),
auth.chainId = cfg.PublicKeyAuthentication.Ethereum.GetChainID() chainId: cfg.PublicKeyAuthentication.Ethereum.GetChainID(),
auth.store = rsAPI.NewRoomserverStore() }
switch auth.chainId { switch auth.chainId {
case 1337, 31337: case 1337, 31337:
@ -106,7 +82,8 @@ func (za *ZionAuthorization) IsAllowed(args authorization.AuthorizationArgs) (bo
} else if err != nil { } else if err != nil {
return false, err return false, err
} }
return za.isAllowedLocalhost(roomInfo, userIdentifier.AccountAddress, args.Permission) isAllowed, err := za.isAllowedLocalhost(roomInfo, userIdentifier.AccountAddress, args.Permission)
return isAllowed, err
case 5: case 5:
// Check if space / channel is disabled. // Check if space / channel is disabled.
disabled, err := za.isSpaceChannelDisabledGoerli(roomInfo) disabled, err := za.isSpaceChannelDisabledGoerli(roomInfo)