mirror of
https://github.com/matrix-org/dendrite.git
synced 2026-01-03 04:03:09 -06:00
Get MSC2946 working for restricted rooms over federation
This commit is contained in:
parent
168140f82d
commit
132ca1ae8d
|
|
@ -206,7 +206,7 @@ type roomVisit struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *walker) walk() util.JSONResponse {
|
func (w *walker) walk() util.JSONResponse {
|
||||||
if !w.authorised(w.rootRoomID, "") {
|
if authorised, _ := w.authorised(w.rootRoomID, ""); !authorised {
|
||||||
if w.caller != nil {
|
if w.caller != nil {
|
||||||
// CS API format
|
// CS API format
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
|
@ -279,23 +279,8 @@ func (w *walker) walk() util.JSONResponse {
|
||||||
|
|
||||||
// If we know about this room and the caller is authorised (joined/world_readable) then pull
|
// If we know about this room and the caller is authorised (joined/world_readable) then pull
|
||||||
// events locally
|
// events locally
|
||||||
if w.roomExists(rv.roomID) && w.authorised(rv.roomID, rv.parentRoomID) {
|
roomExists := w.roomExists(rv.roomID)
|
||||||
// Get all `m.space.child` state events for this room
|
if !roomExists {
|
||||||
events, err := w.childReferences(rv.roomID)
|
|
||||||
if err != nil {
|
|
||||||
util.GetLogger(w.ctx).WithError(err).WithField("room_id", rv.roomID).Error("failed to extract references for room")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
discoveredChildEvents = events
|
|
||||||
|
|
||||||
pubRoom := w.publicRoomsChunk(rv.roomID)
|
|
||||||
|
|
||||||
discoveredRooms = append(discoveredRooms, gomatrixserverlib.MSC2946Room{
|
|
||||||
PublicRoom: *pubRoom,
|
|
||||||
RoomType: roomType,
|
|
||||||
ChildrenState: events,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// attempt to query this room over federation, as either we've never heard of it before
|
// attempt to query this room over federation, as either we've never heard of it before
|
||||||
// or we've left it and hence are not authorised (but info may be exposed regardless)
|
// or we've left it and hence are not authorised (but info may be exposed regardless)
|
||||||
fedRes, err := w.federatedRoomInfo(rv.roomID, rv.vias)
|
fedRes, err := w.federatedRoomInfo(rv.roomID, rv.vias)
|
||||||
|
|
@ -314,6 +299,29 @@ func (w *walker) walk() util.JSONResponse {
|
||||||
// as these children may be rooms we do know about.
|
// as these children may be rooms we do know about.
|
||||||
roomType = ConstCreateEventContentValueSpace
|
roomType = ConstCreateEventContentValueSpace
|
||||||
}
|
}
|
||||||
|
} else if authorised, isJoined := w.authorised(rv.roomID, rv.parentRoomID); authorised {
|
||||||
|
// Get all `m.space.child` state events for this room
|
||||||
|
events, err := w.childReferences(rv.roomID)
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(w.ctx).WithError(err).WithField("room_id", rv.roomID).Error("failed to extract references for room")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
discoveredChildEvents = events
|
||||||
|
|
||||||
|
pubRoom := w.publicRoomsChunk(rv.roomID)
|
||||||
|
|
||||||
|
discoveredRooms = append(discoveredRooms, gomatrixserverlib.MSC2946Room{
|
||||||
|
PublicRoom: *pubRoom,
|
||||||
|
RoomType: roomType,
|
||||||
|
ChildrenState: events,
|
||||||
|
})
|
||||||
|
// don't walk children if the user is not joined to the space
|
||||||
|
if !isJoined {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// room exists but user is not authorised
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't walk the children
|
// don't walk the children
|
||||||
|
|
@ -468,25 +476,29 @@ func (w *walker) roomExists(roomID string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// authorised returns true iff the user is joined this room or the room is world_readable
|
// authorised returns true iff the user is joined this room or the room is world_readable
|
||||||
func (w *walker) authorised(roomID, parentRoomID string) bool {
|
func (w *walker) authorised(roomID, parentRoomID string) (authed, isJoined bool) {
|
||||||
if w.caller != nil {
|
if w.caller != nil {
|
||||||
return w.authorisedUser(roomID, parentRoomID)
|
return w.authorisedUser(roomID, parentRoomID)
|
||||||
}
|
}
|
||||||
return w.authorisedServer(roomID)
|
return w.authorisedServer(roomID), false
|
||||||
}
|
}
|
||||||
|
|
||||||
// authorisedServer returns true iff the server is joined this room or the room is world_readable
|
// authorisedServer returns true iff the server is joined this room or the room is world_readable
|
||||||
func (w *walker) authorisedServer(roomID string) bool {
|
func (w *walker) authorisedServer(roomID string) bool {
|
||||||
// Check history visibility first
|
// Check history visibility / join rules first
|
||||||
hisVisTuple := gomatrixserverlib.StateKeyTuple{
|
hisVisTuple := gomatrixserverlib.StateKeyTuple{
|
||||||
EventType: gomatrixserverlib.MRoomHistoryVisibility,
|
EventType: gomatrixserverlib.MRoomHistoryVisibility,
|
||||||
StateKey: "",
|
StateKey: "",
|
||||||
}
|
}
|
||||||
|
joinRuleTuple := gomatrixserverlib.StateKeyTuple{
|
||||||
|
EventType: gomatrixserverlib.MRoomJoinRules,
|
||||||
|
StateKey: "",
|
||||||
|
}
|
||||||
var queryRoomRes roomserver.QueryCurrentStateResponse
|
var queryRoomRes roomserver.QueryCurrentStateResponse
|
||||||
err := w.rsAPI.QueryCurrentState(w.ctx, &roomserver.QueryCurrentStateRequest{
|
err := w.rsAPI.QueryCurrentState(w.ctx, &roomserver.QueryCurrentStateRequest{
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
StateTuples: []gomatrixserverlib.StateKeyTuple{
|
StateTuples: []gomatrixserverlib.StateKeyTuple{
|
||||||
hisVisTuple,
|
hisVisTuple, joinRuleTuple,
|
||||||
},
|
},
|
||||||
}, &queryRoomRes)
|
}, &queryRoomRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -500,26 +512,38 @@ func (w *walker) authorisedServer(roomID string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check if server is joined to the room
|
|
||||||
var queryRes fs.QueryJoinedHostServerNamesInRoomResponse
|
// check if this room is a restricted room and if so, we need to check if the server is joined to an allowed room ID
|
||||||
err = w.fsAPI.QueryJoinedHostServerNamesInRoom(w.ctx, &fs.QueryJoinedHostServerNamesInRoomRequest{
|
// in addition to the actual room ID (but always do the actual one first as it's quicker in the common case)
|
||||||
RoomID: roomID,
|
allowJoinedToRoomIDs := []string{roomID}
|
||||||
}, &queryRes)
|
joinRuleEv := queryRoomRes.StateEvents[joinRuleTuple]
|
||||||
if err != nil {
|
if joinRuleEv != nil {
|
||||||
util.GetLogger(w.ctx).WithError(err).Error("failed to QueryJoinedHostServerNamesInRoom")
|
allowJoinedToRoomIDs = append(allowJoinedToRoomIDs, w.restrictedJoinRuleAllowedRooms(joinRuleEv, "m.room_membership")...)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
for _, srv := range queryRes.ServerNames {
|
|
||||||
if srv == w.serverName {
|
// check if server is joined to any allowed room
|
||||||
return true
|
for _, allowedRoomID := range allowJoinedToRoomIDs {
|
||||||
|
var queryRes fs.QueryJoinedHostServerNamesInRoomResponse
|
||||||
|
err = w.fsAPI.QueryJoinedHostServerNamesInRoom(w.ctx, &fs.QueryJoinedHostServerNamesInRoomRequest{
|
||||||
|
RoomID: allowedRoomID,
|
||||||
|
}, &queryRes)
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(w.ctx).WithError(err).Error("failed to QueryJoinedHostServerNamesInRoom")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, srv := range queryRes.ServerNames {
|
||||||
|
if srv == w.serverName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// authorisedUser returns true iff the user is invited/joined this room or the room is world_readable.
|
// authorisedUser returns true iff the user is invited/joined this room or the room is world_readable.
|
||||||
// Failing that, if the room has a restricted join rule and belongs to the space parent listed, it will return true.
|
// Failing that, if the room has a restricted join rule and belongs to the space parent listed, it will return true.
|
||||||
func (w *walker) authorisedUser(roomID, parentRoomID string) bool {
|
func (w *walker) authorisedUser(roomID, parentRoomID string) (authed bool, isJoined bool) {
|
||||||
hisVisTuple := gomatrixserverlib.StateKeyTuple{
|
hisVisTuple := gomatrixserverlib.StateKeyTuple{
|
||||||
EventType: gomatrixserverlib.MRoomHistoryVisibility,
|
EventType: gomatrixserverlib.MRoomHistoryVisibility,
|
||||||
StateKey: "",
|
StateKey: "",
|
||||||
|
|
@ -541,58 +565,74 @@ func (w *walker) authorisedUser(roomID, parentRoomID string) bool {
|
||||||
}, &queryRes)
|
}, &queryRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(w.ctx).WithError(err).Error("failed to QueryCurrentState")
|
util.GetLogger(w.ctx).WithError(err).Error("failed to QueryCurrentState")
|
||||||
return false
|
return false, false
|
||||||
}
|
}
|
||||||
memberEv := queryRes.StateEvents[roomMemberTuple]
|
memberEv := queryRes.StateEvents[roomMemberTuple]
|
||||||
if memberEv != nil {
|
if memberEv != nil {
|
||||||
membership, _ := memberEv.Membership()
|
membership, _ := memberEv.Membership()
|
||||||
if membership == gomatrixserverlib.Join || membership == gomatrixserverlib.Invite {
|
if membership == gomatrixserverlib.Join || membership == gomatrixserverlib.Invite {
|
||||||
return true
|
return true, membership == gomatrixserverlib.Join
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hisVisEv := queryRes.StateEvents[hisVisTuple]
|
hisVisEv := queryRes.StateEvents[hisVisTuple]
|
||||||
if hisVisEv != nil {
|
if hisVisEv != nil {
|
||||||
hisVis, _ := hisVisEv.HistoryVisibility()
|
hisVis, _ := hisVisEv.HistoryVisibility()
|
||||||
if hisVis == "world_readable" {
|
if hisVis == "world_readable" {
|
||||||
return true
|
return true, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
joinRuleEv := queryRes.StateEvents[joinRuleTuple]
|
joinRuleEv := queryRes.StateEvents[joinRuleTuple]
|
||||||
if parentRoomID != "" && joinRuleEv != nil {
|
if parentRoomID != "" && joinRuleEv != nil {
|
||||||
rule, _ := joinRuleEv.JoinRule()
|
allowedRoomIDs := w.restrictedJoinRuleAllowedRooms(joinRuleEv, "m.room_membership")
|
||||||
if rule == "restricted" {
|
// check parent is in the allowed set
|
||||||
var jrContent gomatrixserverlib.JoinRuleContent
|
var allowed bool
|
||||||
if err := json.Unmarshal(joinRuleEv.Content(), &jrContent); err != nil {
|
for _, a := range allowedRoomIDs {
|
||||||
util.GetLogger(w.ctx).Warnf("failed to check join_rule on room %s: %s", roomID, err)
|
if parentRoomID == a {
|
||||||
return false
|
allowed = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
// check the allow section
|
}
|
||||||
for _, allow := range jrContent.Allow {
|
if allowed {
|
||||||
if allow.Type == "m.room_membership" && allow.RoomID == parentRoomID {
|
// ensure caller is joined to the parent room
|
||||||
// ensure caller is joined to the parent room
|
var queryRes2 roomserver.QueryCurrentStateResponse
|
||||||
var queryRes2 roomserver.QueryCurrentStateResponse
|
err = w.rsAPI.QueryCurrentState(w.ctx, &roomserver.QueryCurrentStateRequest{
|
||||||
err = w.rsAPI.QueryCurrentState(w.ctx, &roomserver.QueryCurrentStateRequest{
|
RoomID: parentRoomID,
|
||||||
RoomID: parentRoomID,
|
StateTuples: []gomatrixserverlib.StateKeyTuple{
|
||||||
StateTuples: []gomatrixserverlib.StateKeyTuple{
|
roomMemberTuple,
|
||||||
roomMemberTuple,
|
},
|
||||||
},
|
}, &queryRes2)
|
||||||
}, &queryRes2)
|
if err != nil {
|
||||||
if err != nil {
|
util.GetLogger(w.ctx).WithError(err).WithField("parent_room_id", parentRoomID).Warn("failed to check user is joined to parent room")
|
||||||
util.GetLogger(w.ctx).WithError(err).WithField("parent_room_id", parentRoomID).Warn("failed to check user is joined to parent room")
|
} else {
|
||||||
continue
|
memberEv = queryRes2.StateEvents[roomMemberTuple]
|
||||||
}
|
if memberEv != nil {
|
||||||
memberEv = queryRes2.StateEvents[roomMemberTuple]
|
membership, _ := memberEv.Membership()
|
||||||
if memberEv != nil {
|
if membership == gomatrixserverlib.Join {
|
||||||
membership, _ := memberEv.Membership()
|
return true, false
|
||||||
if membership == gomatrixserverlib.Join {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *walker) restrictedJoinRuleAllowedRooms(joinRuleEv *gomatrixserverlib.HeaderedEvent, allowType string) (allows []string) {
|
||||||
|
rule, _ := joinRuleEv.JoinRule()
|
||||||
|
if rule != "restricted" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var jrContent gomatrixserverlib.JoinRuleContent
|
||||||
|
if err := json.Unmarshal(joinRuleEv.Content(), &jrContent); err != nil {
|
||||||
|
util.GetLogger(w.ctx).Warnf("failed to check join_rule on room %s: %s", joinRuleEv.RoomID(), err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, allow := range jrContent.Allow {
|
||||||
|
if allow.Type == allowType {
|
||||||
|
allows = append(allows, allow.RoomID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// references returns all child references pointing to or from this room.
|
// references returns all child references pointing to or from this room.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue