mirror of
https://github.com/matrix-org/dendrite.git
synced 2026-01-19 03:53:09 -06:00
Closes HNT-244. The following PR implements Space,Channel soft deletion using on-chain `disabled` flag scope to space, channel respectively. On message sync, dendrite will now gate disabled rooms by performing a leave on the user attempting to sync unless the user is the owner (more on this later). To re-join, given rooms (spaces,channels) are created by default using `invite` membership state, the owner will need to undo the on-chain `disabled` flag, setting it false then re-invite users that left the room as a side effect of it becoming disabled previously. The owner does not leave the space, channel because if they did then there would be no one left to invite users let alone themselves back in if the action is ever undone. What is not implemented in this PR: 1. **Transitive leaves on channels in a space** - If a space is disabled, users will leave the space but not the channels within the space. To allow for fully disabling a space and all its' channels, the client can offer a view to the owner that iterates over the channels and space to disable all on-chain. Furthermore, we could implement a batch on-chain method that fully disables all channels within a space (plus the space) in one on-chain call to save the owner gas. 2. **Data deletion** - No data is remove from the DAGs or on-chain. Therefore deletion is soft and reversible. 3. **New hook to check if a room is disabled** - the client can leverage existing on-chain public read only methods `getSpaceInfoBySpaceId`, `getChannelInfoByChannelId` to read the state of each in order to remove spaces, channels from a member's view that are disabled.
253 lines
6.4 KiB
Go
253 lines
6.4 KiB
Go
package zion
|
|
|
|
import (
|
|
_ "embed"
|
|
"errors"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/matrix-org/dendrite/authorization"
|
|
roomserver "github.com/matrix-org/dendrite/roomserver/api"
|
|
"github.com/matrix-org/dendrite/setup/config"
|
|
zion_goerli "github.com/matrix-org/dendrite/zion/contracts/zion_goerli"
|
|
zion_localhost "github.com/matrix-org/dendrite/zion/contracts/zion_localhost"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
//go:embed contracts/zion_localhost/space-manager.json
|
|
var localhostJson []byte
|
|
|
|
//go:embed contracts/zion_goerli/space-manager.json
|
|
var goerliJson []byte
|
|
|
|
var ErrSpaceDisabled = errors.New("space disabled")
|
|
var ErrChannelDisabled = errors.New("channel disabled")
|
|
|
|
type ZionAuthorization struct {
|
|
store StoreAPI
|
|
spaceManagerLocalhost *zion_localhost.ZionSpaceManagerLocalhost
|
|
spaceManagerGoerli *zion_goerli.ZionSpaceManagerGoerli
|
|
chainId int
|
|
}
|
|
type ClientRoomserverStruct struct {
|
|
roomserver.ClientRoomserverAPI
|
|
}
|
|
|
|
type SyncRoomserverStruct struct {
|
|
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 == "" {
|
|
log.Errorf("No blockchain network url specified in config\n")
|
|
return nil, nil
|
|
}
|
|
|
|
var auth ZionAuthorization
|
|
|
|
auth.chainId = cfg.PublicKeyAuthentication.Ethereum.GetChainID()
|
|
auth.store = rsAPI.NewRoomserverStore()
|
|
|
|
switch auth.chainId {
|
|
case 1337, 31337:
|
|
localhost, err := newZionSpaceManagerLocalhost(cfg.PublicKeyAuthentication.Ethereum.NetworkUrl)
|
|
if err != nil {
|
|
log.Errorln("error instantiating ZionSpaceManagerLocalhost", err)
|
|
}
|
|
auth.spaceManagerLocalhost = localhost
|
|
|
|
case 5:
|
|
goerli, err := newZionSpaceManagerGoerli(cfg.PublicKeyAuthentication.Ethereum.NetworkUrl)
|
|
if err != nil {
|
|
log.Errorln("error instantiating ZionSpaceManagerGoerli", err)
|
|
}
|
|
auth.spaceManagerGoerli = goerli
|
|
|
|
default:
|
|
log.Errorf("Unsupported chain id: %d\n", auth.chainId)
|
|
}
|
|
|
|
return &auth, nil
|
|
}
|
|
|
|
func (za *ZionAuthorization) IsAllowed(args authorization.AuthorizationArgs) (bool, error) {
|
|
userIdentifier := CreateUserIdentifier(args.UserId)
|
|
|
|
// 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 za.chainId {
|
|
case 1337, 31337:
|
|
// Check if space / channel is disabled.
|
|
disabled, err := za.isSpaceChannelDisabledLocalhost(roomInfo)
|
|
if disabled {
|
|
return false, ErrSpaceDisabled
|
|
} else if err != nil {
|
|
return false, err
|
|
}
|
|
return za.isAllowedLocalhost(roomInfo, userIdentifier.AccountAddress, args.Permission)
|
|
case 5:
|
|
// Check if space / channel is disabled.
|
|
disabled, err := za.isSpaceChannelDisabledGoerli(roomInfo)
|
|
if disabled {
|
|
return false, ErrChannelDisabled
|
|
} else if err != nil {
|
|
return false, err
|
|
}
|
|
return za.isAllowedGoerli(roomInfo, userIdentifier.AccountAddress, args.Permission)
|
|
default:
|
|
log.Errorf("Unsupported chain id: %d", userIdentifier.ChainId)
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
func (za *ZionAuthorization) isSpaceChannelDisabledLocalhost(roomInfo RoomInfo) (bool, error) {
|
|
if za.spaceManagerLocalhost == nil {
|
|
return false, errors.New("error fetching space manager contract")
|
|
}
|
|
switch roomInfo.ChannelNetworkId {
|
|
case "":
|
|
spInfo, err := za.spaceManagerLocalhost.GetSpaceInfoBySpaceId(nil, roomInfo.SpaceNetworkId)
|
|
return spInfo.Disabled, err
|
|
default:
|
|
chInfo, err := za.spaceManagerLocalhost.GetChannelInfoByChannelId(nil, roomInfo.SpaceNetworkId, roomInfo.ChannelNetworkId)
|
|
return chInfo.Disabled, err
|
|
}
|
|
|
|
}
|
|
|
|
func (za *ZionAuthorization) isSpaceChannelDisabledGoerli(roomInfo RoomInfo) (bool, error) {
|
|
if za.spaceManagerGoerli == nil {
|
|
return false, errors.New("error fetching space manager contract")
|
|
}
|
|
switch roomInfo.ChannelNetworkId {
|
|
case "":
|
|
spInfo, err := za.spaceManagerGoerli.GetSpaceInfoBySpaceId(nil, roomInfo.SpaceNetworkId)
|
|
return spInfo.Disabled, err
|
|
default:
|
|
chInfo, err := za.spaceManagerGoerli.GetChannelInfoByChannelId(nil, roomInfo.SpaceNetworkId, roomInfo.ChannelNetworkId)
|
|
return chInfo.Disabled, err
|
|
}
|
|
|
|
}
|
|
|
|
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(),
|
|
}
|
|
|
|
isEntitled, err := za.spaceManagerLocalhost.IsEntitled(
|
|
nil,
|
|
roomInfo.SpaceNetworkId,
|
|
roomInfo.ChannelNetworkId,
|
|
user,
|
|
permission,
|
|
)
|
|
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return isEntitled, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
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(),
|
|
}
|
|
|
|
isEntitled, err := za.spaceManagerGoerli.IsEntitled(
|
|
nil,
|
|
roomInfo.SpaceNetworkId,
|
|
roomInfo.ChannelNetworkId,
|
|
user,
|
|
permission,
|
|
)
|
|
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return isEntitled, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
func newZionSpaceManagerLocalhost(endpointUrl string) (*zion_localhost.ZionSpaceManagerLocalhost, error) {
|
|
addresses, err := loadSpaceManagerAddresses(localhostJson)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
address := common.HexToAddress(addresses.Spacemanager)
|
|
|
|
client, err := GetEthClient(endpointUrl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
spaceManager, err := zion_localhost.NewZionSpaceManagerLocalhost(address, client)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return spaceManager, nil
|
|
}
|
|
|
|
func newZionSpaceManagerGoerli(endpointUrl string) (*zion_goerli.ZionSpaceManagerGoerli, error) {
|
|
addresses, err := loadSpaceManagerAddresses(goerliJson)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
address := common.HexToAddress((addresses.Spacemanager))
|
|
|
|
client, err := GetEthClient(endpointUrl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
spaceManager, err := zion_goerli.NewZionSpaceManagerGoerli(address, client)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return spaceManager, nil
|
|
}
|