From 18db428c239ee0115b3d33c0a50fc11407b7d280 Mon Sep 17 00:00:00 2001 From: Tak Wai Wong <64229756+tak-hntlabs@users.noreply.github.com> Date: Mon, 17 Oct 2022 14:08:04 -0700 Subject: [PATCH] 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 --- clientapi/authorization/authorization.go | 5 +- clientapi/routing/routing.go | 11 ++- zion/README.md | 2 + zion/store.go | 88 ++++++++++++++++++++++++ zion/store_types.go | 42 +++++++++++ zion/user_identifier.go | 25 ++++--- zion/zion_authorization.go | 45 ++++++++---- 7 files changed, 190 insertions(+), 28 deletions(-) create mode 100644 zion/store.go create mode 100644 zion/store_types.go diff --git a/clientapi/authorization/authorization.go b/clientapi/authorization/authorization.go index 019136d15..f37f4becd 100644 --- a/clientapi/authorization/authorization.go +++ b/clientapi/authorization/authorization.go @@ -2,15 +2,16 @@ package authorization import ( "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/zion" 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 if cfg.PublicKeyAuthentication.Ethereum.EnableAuthz { - auth, err := zion.NewZionAuthorization() + auth, err := zion.NewZionAuthorization(rsAPI) if err != nil { log.Errorln("Failed to initialise Zion authorization manager. Using default.", err) diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index 79033fa48..c640d7765 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -76,8 +76,7 @@ func Setup( rateLimits := httputil.NewRateLimits(&cfg.RateLimiting) userInteractiveAuth := auth.NewUserInteractive(userAPI, userAPI, cfg) - authorization := clientApiAuthz.NewAuthorization(cfg) - _ = authorization // todo: use this in httputil.MakeAuthAPI + authorization := clientApiAuthz.NewAuthorization(cfg, rsAPI) unstableFeatures := map[string]bool{ "org.matrix.e2e_cross_signing": true, @@ -262,7 +261,12 @@ func Setup( 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( req, device, rsAPI, userAPI, vars["roomIDOrAlias"], @@ -348,6 +352,7 @@ func Setup( if err != nil { return util.ErrorResponse(err) } + return SendInvite(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI) }), ).Methods(http.MethodPost, http.MethodOptions) diff --git a/zion/README.md b/zion/README.md index 08c27f64e..c411bbd96 100644 --- a/zion/README.md +++ b/zion/README.md @@ -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. 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. diff --git a/zion/store.go b/zion/store.go new file mode 100644 index 000000000..a36c01585 --- /dev/null +++ b/zion/store.go @@ -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 +} diff --git a/zion/store_types.go b/zion/store_types.go new file mode 100644 index 000000000..58bf1fd29 --- /dev/null +++ b/zion/store_types.go @@ -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"` +} diff --git a/zion/user_identifier.go b/zion/user_identifier.go index 718758d3b..40e8f6d41 100644 --- a/zion/user_identifier.go +++ b/zion/user_identifier.go @@ -1,38 +1,45 @@ package zion import ( + "fmt" "regexp" "strconv" "github.com/ethereum/go-ethereum/common" ) -var regexpMatrixId = regexp.MustCompile(`^@eip155=3a(?P[0-9]+)=3a(?P0x[0-9a-fA-F]+):(?P.*)$`) +var regexpMatrixId = regexp.MustCompile(`^@eip155=3a(?P[0-9]+)=3a(?P
0x[0-9a-fA-F]+):(?P.*)$`) var chainIdIndex = regexpMatrixId.SubexpIndex("ChainId") -var localPartIndex = regexpMatrixId.SubexpIndex("LocalPart") +var addressIndex = regexpMatrixId.SubexpIndex("Address") //var homeServerIndex = regexpMatrixId.SubexpIndex("HomeServer") type UserIdentifier struct { - accountAddress common.Address - chainId int + AccountAddress common.Address + ChainId int + MatrixUserId string + LocalPart string } func CreateUserIdentifier(matrixUserId string) UserIdentifier { matches := regexpMatrixId.FindStringSubmatch(matrixUserId) - accountAddress := "" + address := "" chainId := -1 + localPart := "" if chainIdIndex < len(matches) { chainId, _ = strconv.Atoi(matches[chainIdIndex]) } - if localPartIndex < len(matches) { - accountAddress = matches[localPartIndex] + if addressIndex < len(matches) { + address = matches[addressIndex] + localPart = fmt.Sprintf("@eip155=3a%d=3a%s", chainId, address) } return UserIdentifier{ - accountAddress: common.HexToAddress(accountAddress), - chainId: chainId, + AccountAddress: common.HexToAddress(address), + ChainId: chainId, + MatrixUserId: matrixUserId, + LocalPart: localPart, } } diff --git a/zion/zion_authorization.go b/zion/zion_authorization.go index c363759c8..10b4a1d55 100644 --- a/zion/zion_authorization.go +++ b/zion/zion_authorization.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/joho/godotenv" "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_localhost "github.com/matrix-org/dendrite/zion/contracts/localhost/zion_localhost" log "github.com/sirupsen/logrus" @@ -24,11 +25,12 @@ var localhostJson []byte var goerliJson []byte type ZionAuthorization struct { + store Store spaceManagerLocalhost *zion_localhost.ZionSpaceManagerLocalhost spaceManagerGoerli *zion_goerli.ZionSpaceManagerGoerli } -func NewZionAuthorization() (authorization.Authorization, error) { +func NewZionAuthorization(rsAPI roomserver.ClientRoomserverAPI) (authorization.Authorization, error) { err := godotenv.Load(".env") if err != nil { log.Errorln("error loading .env file", err) @@ -36,6 +38,8 @@ func NewZionAuthorization() (authorization.Authorization, error) { var auth ZionAuthorization + auth.store = NewStore(rsAPI) + localhost, err := newZionSpaceManagerLocalhost(os.Getenv(localhostEndpointUrl)) if err != nil { log.Errorln("error instantiating ZionSpaceManagerLocalhost", err) @@ -54,31 +58,40 @@ func NewZionAuthorization() (authorization.Authorization, error) { func (za *ZionAuthorization) IsAllowed(args authorization.AuthorizationArgs) (bool, error) { 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: - return za.IsAllowedLocalhost(args.RoomId, userIdentifier.accountAddress, args.Permission) + return za.isAllowedLocalhost(roomInfo, userIdentifier.AccountAddress, args.Permission) case 5: - return za.IsAllowedGoerli(args.RoomId, userIdentifier.accountAddress, args.Permission) + return za.isAllowedGoerli(roomInfo, userIdentifier.AccountAddress, args.Permission) default: - log.Errorf("Unsupported chain id: %d\n", userIdentifier.chainId) + log.Errorf("Unsupported chain id: %d\n", userIdentifier.ChainId) } 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 { permission := zion_localhost.DataTypesPermission{ Name: permission.String(), } - addr := user.Hex() - _ = addr - isEntitled, err := za.spaceManagerLocalhost.IsEntitled( nil, - roomId, - "", // todo: Support channelId + roomInfo.SpaceNetworkId, + roomInfo.ChannelNetworkId, user, permission, ) @@ -93,7 +106,11 @@ func (za *ZionAuthorization) IsAllowedLocalhost(roomId string, user common.Addre 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 { permission := zion_goerli.DataTypesPermission{ Name: permission.String(), @@ -101,8 +118,8 @@ func (za *ZionAuthorization) IsAllowedGoerli(roomId string, user common.Address, isEntitled, err := za.spaceManagerGoerli.IsEntitled( nil, - roomId, - "", // todo: support channelId + roomInfo.SpaceNetworkId, + roomInfo.ChannelNetworkId, user, permission, )