Figure out space vs channel from roomid (#43)

* pass in roomserver API so that we have access to the db

* interface to get db info for spaceid and channelid

* determine space or channel by querying the room db

* Add authorization check to the JOIN endpoint

* fix lint errors
This commit is contained in:
Tak Wai Wong 2022-10-17 14:08:04 -07:00
parent c5a753d6e2
commit 18db428c23
No known key found for this signature in database
GPG key ID: 222E4AF2AA1F467D
7 changed files with 190 additions and 28 deletions

View file

@ -2,15 +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/zion" "github.com/matrix-org/dendrite/zion"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
func NewAuthorization(cfg *config.ClientAPI) authorization.Authorization { func NewAuthorization(cfg *config.ClientAPI, rsAPI roomserver.ClientRoomserverAPI) authorization.Authorization {
// Load authorization manager for Zion // Load authorization manager for Zion
if cfg.PublicKeyAuthentication.Ethereum.EnableAuthz { if cfg.PublicKeyAuthentication.Ethereum.EnableAuthz {
auth, err := zion.NewZionAuthorization() auth, err := zion.NewZionAuthorization(rsAPI)
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

@ -76,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)
authorization := clientApiAuthz.NewAuthorization(cfg) authorization := clientApiAuthz.NewAuthorization(cfg, rsAPI)
_ = authorization // todo: use this in httputil.MakeAuthAPI
unstableFeatures := map[string]bool{ unstableFeatures := map[string]bool{
"org.matrix.e2e_cross_signing": true, "org.matrix.e2e_cross_signing": true,
@ -262,7 +261,12 @@ func Setup(
Permission: authz.PermissionRead, Permission: authz.PermissionRead,
}) })
logrus.Debugf("/join/%s isAllowed = %t", vars["roomIDOrAlias"], isAllowed) if !isAllowed {
return util.JSONResponse{
Code: http.StatusUnauthorized,
JSON: jsonerror.Forbidden(""),
}
}
return JoinRoomByIDOrAlias( return JoinRoomByIDOrAlias(
req, device, rsAPI, userAPI, vars["roomIDOrAlias"], req, device, rsAPI, userAPI, vars["roomIDOrAlias"],
@ -348,6 +352,7 @@ func Setup(
if err != nil { if err != nil {
return util.ErrorResponse(err) return util.ErrorResponse(err)
} }
return SendInvite(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI) return SendInvite(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI)
}), }),
).Methods(http.MethodPost, http.MethodOptions) ).Methods(http.MethodPost, http.MethodOptions)

View file

@ -1,3 +1,5 @@
# Purpose
Additional packaages added for the Zion project, nothing in here should be in the Matrix Dendrite upstream, nor in the herenotthere/dendrite-fork. Additional packaages added for the Zion project, nothing in here should be in the Matrix Dendrite upstream, nor in the herenotthere/dendrite-fork.
The zion_space_manager_(mainnet|rinkeby|localhost).go files are generated as new versions of the smart contracts are build and deployed. The bindings are in this location so they can be built alongside the dendrite server in the build process. The zion_space_manager_(mainnet|rinkeby|localhost).go files are generated as new versions of the smart contracts are build and deployed. The bindings are in this location so they can be built alongside the dendrite server in the build process.

88
zion/store.go Normal file
View file

@ -0,0 +1,88 @@
/*
Convenient function for space info mapping between Matrix room and Space contract
*/
package zion
import (
"context"
"encoding/json"
"strings"
roomserver "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/gomatrixserverlib"
)
type Store struct {
rsAPI roomserver.ClientRoomserverAPI
}
func NewStore(rsAPI roomserver.ClientRoomserverAPI) Store {
return Store{
rsAPI: rsAPI,
}
}
func (s *Store) 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.QueryCurrentStateResponse
err := s.rsAPI.QueryCurrentState(context.Background(), &roomserver.QueryCurrentStateRequest{
RoomID: roomId,
AllowWildcards: true,
StateTuples: []gomatrixserverlib.StateKeyTuple{
createTuple,
spaceParentTuple,
spaceChildTuple,
},
}, &roomEvents)
if err != nil {
return result
}
for _, state := range roomEvents.StateEvents {
switch state.Type() {
case gomatrixserverlib.MRoomCreate:
var creatorEvent CreatorEvent
err := json.Unmarshal(roomEvents.StateEvents[createTuple].Content(), &creatorEvent)
result.IsOwner = strings.HasPrefix(
creatorEvent.Creator,
userId.LocalPart,
)
if err == nil {
result.RoomType = Space
result.SpaceNetworkId = roomId
}
case ConstSpaceChildEventType:
result.RoomType = Space
result.SpaceNetworkId = roomId
case ConstSpaceParentEventType:
result.RoomType = Channel
result.SpaceNetworkId = *state.StateKey()
result.ChannelNetworkId = roomId
}
}
return result
}

42
zion/store_types.go Normal file
View file

@ -0,0 +1,42 @@
/*
Store types
*/
package zion
const (
ConstSpaceChildEventType = "m.space.child"
ConstSpaceParentEventType = "m.space.parent"
)
// Define enum for RoomType
type RoomType int64
const (
Space RoomType = iota
Channel
Unknown
)
func (r RoomType) String() string {
switch r {
case Space:
return "space"
case Channel:
return "channel"
}
return "unknown"
}
type RoomInfo struct {
QueryUserId string // Matrix User ID
SpaceNetworkId string
ChannelNetworkId string
RoomType RoomType
IsOwner bool
}
type CreatorEvent struct {
Creator string `json:"creator"`
Type string `json:"type"`
RoomVersion string `json:"room_version"`
}

View file

@ -1,38 +1,45 @@
package zion package zion
import ( import (
"fmt"
"regexp" "regexp"
"strconv" "strconv"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
var regexpMatrixId = regexp.MustCompile(`^@eip155=3a(?P<ChainId>[0-9]+)=3a(?P<LocalPart>0x[0-9a-fA-F]+):(?P<HomeServer>.*)$`) var regexpMatrixId = regexp.MustCompile(`^@eip155=3a(?P<ChainId>[0-9]+)=3a(?P<Address>0x[0-9a-fA-F]+):(?P<HomeServer>.*)$`)
var chainIdIndex = regexpMatrixId.SubexpIndex("ChainId") var chainIdIndex = regexpMatrixId.SubexpIndex("ChainId")
var localPartIndex = regexpMatrixId.SubexpIndex("LocalPart") var addressIndex = regexpMatrixId.SubexpIndex("Address")
//var homeServerIndex = regexpMatrixId.SubexpIndex("HomeServer") //var homeServerIndex = regexpMatrixId.SubexpIndex("HomeServer")
type UserIdentifier struct { type UserIdentifier struct {
accountAddress common.Address AccountAddress common.Address
chainId int ChainId int
MatrixUserId string
LocalPart string
} }
func CreateUserIdentifier(matrixUserId string) UserIdentifier { func CreateUserIdentifier(matrixUserId string) UserIdentifier {
matches := regexpMatrixId.FindStringSubmatch(matrixUserId) matches := regexpMatrixId.FindStringSubmatch(matrixUserId)
accountAddress := "" address := ""
chainId := -1 chainId := -1
localPart := ""
if chainIdIndex < len(matches) { if chainIdIndex < len(matches) {
chainId, _ = strconv.Atoi(matches[chainIdIndex]) chainId, _ = strconv.Atoi(matches[chainIdIndex])
} }
if localPartIndex < len(matches) { if addressIndex < len(matches) {
accountAddress = matches[localPartIndex] address = matches[addressIndex]
localPart = fmt.Sprintf("@eip155=3a%d=3a%s", chainId, address)
} }
return UserIdentifier{ return UserIdentifier{
accountAddress: common.HexToAddress(accountAddress), AccountAddress: common.HexToAddress(address),
chainId: chainId, ChainId: chainId,
MatrixUserId: matrixUserId,
LocalPart: localPart,
} }
} }

View file

@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/matrix-org/dendrite/authorization" "github.com/matrix-org/dendrite/authorization"
roomserver "github.com/matrix-org/dendrite/roomserver/api"
zion_goerli "github.com/matrix-org/dendrite/zion/contracts/goerli/zion_goerli" zion_goerli "github.com/matrix-org/dendrite/zion/contracts/goerli/zion_goerli"
zion_localhost "github.com/matrix-org/dendrite/zion/contracts/localhost/zion_localhost" zion_localhost "github.com/matrix-org/dendrite/zion/contracts/localhost/zion_localhost"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -24,11 +25,12 @@ var localhostJson []byte
var goerliJson []byte var goerliJson []byte
type ZionAuthorization struct { type ZionAuthorization struct {
store Store
spaceManagerLocalhost *zion_localhost.ZionSpaceManagerLocalhost spaceManagerLocalhost *zion_localhost.ZionSpaceManagerLocalhost
spaceManagerGoerli *zion_goerli.ZionSpaceManagerGoerli spaceManagerGoerli *zion_goerli.ZionSpaceManagerGoerli
} }
func NewZionAuthorization() (authorization.Authorization, error) { func NewZionAuthorization(rsAPI roomserver.ClientRoomserverAPI) (authorization.Authorization, error) {
err := godotenv.Load(".env") err := godotenv.Load(".env")
if err != nil { if err != nil {
log.Errorln("error loading .env file", err) log.Errorln("error loading .env file", err)
@ -36,6 +38,8 @@ func NewZionAuthorization() (authorization.Authorization, error) {
var auth ZionAuthorization var auth ZionAuthorization
auth.store = NewStore(rsAPI)
localhost, err := newZionSpaceManagerLocalhost(os.Getenv(localhostEndpointUrl)) localhost, err := newZionSpaceManagerLocalhost(os.Getenv(localhostEndpointUrl))
if err != nil { if err != nil {
log.Errorln("error instantiating ZionSpaceManagerLocalhost", err) log.Errorln("error instantiating ZionSpaceManagerLocalhost", err)
@ -54,31 +58,40 @@ func NewZionAuthorization() (authorization.Authorization, error) {
func (za *ZionAuthorization) IsAllowed(args authorization.AuthorizationArgs) (bool, error) { func (za *ZionAuthorization) IsAllowed(args authorization.AuthorizationArgs) (bool, error) {
userIdentifier := CreateUserIdentifier(args.UserId) userIdentifier := CreateUserIdentifier(args.UserId)
switch userIdentifier.chainId { // Find out if roomId is a space or a channel.
roomInfo := za.store.GetRoomInfo(args.RoomId, userIdentifier)
// Owner of the space / channel is always allowed to proceed.
if roomInfo.IsOwner {
return true, nil
}
switch userIdentifier.ChainId {
case 1337, 31337: case 1337, 31337:
return za.IsAllowedLocalhost(args.RoomId, userIdentifier.accountAddress, args.Permission) return za.isAllowedLocalhost(roomInfo, userIdentifier.AccountAddress, args.Permission)
case 5: case 5:
return za.IsAllowedGoerli(args.RoomId, userIdentifier.accountAddress, args.Permission) return za.isAllowedGoerli(roomInfo, userIdentifier.AccountAddress, args.Permission)
default: default:
log.Errorf("Unsupported chain id: %d\n", userIdentifier.chainId) log.Errorf("Unsupported chain id: %d\n", userIdentifier.ChainId)
} }
return false, nil return false, nil
} }
func (za *ZionAuthorization) IsAllowedLocalhost(roomId string, user common.Address, permission authorization.Permission) (bool, error) { func (za *ZionAuthorization) isAllowedLocalhost(
roomInfo RoomInfo,
user common.Address,
permission authorization.Permission,
) (bool, error) {
if za.spaceManagerLocalhost != nil { if za.spaceManagerLocalhost != nil {
permission := zion_localhost.DataTypesPermission{ permission := zion_localhost.DataTypesPermission{
Name: permission.String(), Name: permission.String(),
} }
addr := user.Hex()
_ = addr
isEntitled, err := za.spaceManagerLocalhost.IsEntitled( isEntitled, err := za.spaceManagerLocalhost.IsEntitled(
nil, nil,
roomId, roomInfo.SpaceNetworkId,
"", // todo: Support channelId roomInfo.ChannelNetworkId,
user, user,
permission, permission,
) )
@ -93,7 +106,11 @@ func (za *ZionAuthorization) IsAllowedLocalhost(roomId string, user common.Addre
return false, nil return false, nil
} }
func (za *ZionAuthorization) IsAllowedGoerli(roomId string, user common.Address, permission authorization.Permission) (bool, error) { func (za *ZionAuthorization) isAllowedGoerli(
roomInfo RoomInfo,
user common.Address,
permission authorization.Permission,
) (bool, error) {
if za.spaceManagerGoerli != nil { if za.spaceManagerGoerli != nil {
permission := zion_goerli.DataTypesPermission{ permission := zion_goerli.DataTypesPermission{
Name: permission.String(), Name: permission.String(),
@ -101,8 +118,8 @@ func (za *ZionAuthorization) IsAllowedGoerli(roomId string, user common.Address,
isEntitled, err := za.spaceManagerGoerli.IsEntitled( isEntitled, err := za.spaceManagerGoerli.IsEntitled(
nil, nil,
roomId, roomInfo.SpaceNetworkId,
"", // todo: support channelId roomInfo.ChannelNetworkId,
user, user,
permission, permission,
) )