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 (
"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)

View file

@ -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)

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.
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
import (
"fmt"
"regexp"
"strconv"
"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 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,
}
}

View file

@ -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,
)