From a7a272ad08611b134f53c482f69d50c85b745a9c Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Tue, 23 Nov 2021 14:15:16 +0300 Subject: [PATCH] [#174] subnet: Add `RemoveUser` method Signed-off-by: Pavel Karpy --- subnet/subnet_contract.go | 117 +++++++++++++++++++++----------------- tests/subnet_test.go | 27 +++++++++ 2 files changed, 93 insertions(+), 51 deletions(-) diff --git a/subnet/subnet_contract.go b/subnet/subnet_contract.go index 969569a..601577f 100644 --- a/subnet/subnet_contract.go +++ b/subnet/subnet_contract.go @@ -31,6 +31,8 @@ const ( ErrClientAdmNotExist = "client admin not found" // ErrNodeNotExist is thrown when node is not found. ErrNodeNotExist = "node not found" + // ErrUserNotExist is thrown when user is not found. + ErrUserNotExist = "user not found" // ErrAccessDenied is thrown when operation is denied for caller. ErrAccessDenied = "access denied" @@ -228,31 +230,18 @@ func AddNode(subnetID []byte, node interop.PublicKey) { ctx := storage.GetContext() stKey := append([]byte{ownerPrefix}, subnetID...) - prefixLen := len(stKey) rawOwner := storage.Get(ctx, stKey) if rawOwner == nil { panic("addNode: " + ErrSubNotExist) } + stKey[0] = nodeAdminPrefix + owner := rawOwner.([]byte) - if !runtime.CheckWitness(owner) { - var hasAccess bool - stKey[0] = nodeAdminPrefix - - iter := storage.Find(ctx, stKey, storage.KeysOnly) - for iterator.Next(iter) { - key := iterator.Value(iter).([]byte) - if runtime.CheckWitness(key[prefixLen:]) { - hasAccess = true - break - } - } - - if !hasAccess { - panic("addNode: " + ErrAccessDenied) - } + if !calledByOwnerOrAdmin(ctx, owner, stKey) { + panic("addNode: " + ErrAccessDenied) } stKey[0] = nodePrefix @@ -275,31 +264,18 @@ func RemoveNode(subnetID []byte, node interop.PublicKey) { ctx := storage.GetContext() stKey := append([]byte{ownerPrefix}, subnetID...) - prefixLen := len(stKey) rawOwner := storage.Get(ctx, stKey) if rawOwner == nil { panic("removeNode: " + ErrSubNotExist) } + stKey[0] = nodeAdminPrefix + owner := rawOwner.([]byte) - if !runtime.CheckWitness(owner) { - var hasAccess bool - stKey[0] = nodeAdminPrefix - - iter := storage.Find(ctx, stKey, storage.KeysOnly) - for iterator.Next(iter) { - key := iterator.Value(iter).([]byte) - if runtime.CheckWitness(key[prefixLen:]) { - hasAccess = true - break - } - } - - if !hasAccess { - panic("removeNode: " + ErrAccessDenied) - } + if !calledByOwnerOrAdmin(ctx, owner, stKey) { + panic("removeNode: " + ErrAccessDenied) } stKey[0] = nodePrefix @@ -413,27 +389,13 @@ func AddUser(subnetID []byte, groupID []byte, userID []byte) { panic("addUser: " + ErrSubNotExist) } + stKey[0] = clientAdminPrefix stKey = append(stKey, groupID...) - prefixLen := len(stKey) owner := rawOwner.([]byte) - if !runtime.CheckWitness(owner) { - var hasAccess bool - stKey[0] = clientAdminPrefix - - iter := storage.Find(ctx, stKey, storage.KeysOnly) - for iterator.Next(iter) { - key := iterator.Value(iter).([]byte) - if runtime.CheckWitness(key[prefixLen:]) { - hasAccess = true - break - } - } - - if !hasAccess { - panic("addUser: " + ErrAccessDenied) - } + if !calledByOwnerOrAdmin(ctx, owner, stKey) { + panic("addUser: " + ErrAccessDenied) } stKey[0] = userPrefix @@ -445,6 +407,41 @@ func AddUser(subnetID []byte, groupID []byte, userID []byte) { putKeyInList(ctx, userID, stKey) } +// RemoveUser removes user from the specified subnetwork and group. +// Must be called by the owner or the group's admin only. +func RemoveUser(subnetID []byte, groupID []byte, userID []byte) { + // V2 format check + if len(userID) != userIDSize { + panic("addUser: " + ErrInvalidUser) + } + + ctx := storage.GetContext() + + stKey := append([]byte{ownerPrefix}, subnetID...) + + rawOwner := storage.Get(ctx, stKey) + if rawOwner == nil { + panic("removeUser: " + ErrSubNotExist) + } + + stKey[0] = clientAdminPrefix + stKey = append(stKey, groupID...) + + owner := rawOwner.([]byte) + + if !calledByOwnerOrAdmin(ctx, owner, stKey) { + panic("removeUser: " + ErrAccessDenied) + } + + stKey[0] = userPrefix + + if !keyInList(ctx, userID, stKey) { + panic("removeUser: " + ErrUserNotExist) + } + + deleteKeyFromList(ctx, userID, stKey) +} + // Version returns version of the contract. func Version() int { return common.Version @@ -461,3 +458,21 @@ func putKeyInList(ctx storage.Context, keyToPut interop.PublicKey, prefix []byte func deleteKeyFromList(ctx storage.Context, keyToDelete interop.PublicKey, prefix []byte) { storage.Delete(ctx, append(prefix, keyToDelete...)) } + +func calledByOwnerOrAdmin(ctx storage.Context, owner []byte, adminPrefix []byte) bool { + if runtime.CheckWitness(owner) { + return true + } + + prefixLen := len(adminPrefix) + + iter := storage.Find(ctx, adminPrefix, storage.KeysOnly) + for iterator.Next(iter) { + key := iterator.Value(iter).([]byte) + if runtime.CheckWitness(key[prefixLen:]) { + return true + } + } + + return false +} diff --git a/tests/subnet_test.go b/tests/subnet_test.go index b259cec..b1a6e52 100644 --- a/tests/subnet_test.go +++ b/tests/subnet_test.go @@ -258,6 +258,33 @@ func TestSubnet_AddUser(t *testing.T) { cOwn.InvokeFail(t, method+errSeparator+"user has already been added", method, id, groupId, user) } +func TestSubnet_RemoveUser(t *testing.T) { + e := newSubnetInvoker(t) + + id, owner := createSubnet(t, e) + + groupId := randomBytes(8) + user := randomBytes(27) + + adm := e.NewAccount(t) + admPub, ok := vm.ParseSignatureContract(adm.Script()) + require.True(t, ok) + + const method = "removeUser" + + cOwn := e.WithSigners(owner) + cOwn.InvokeFail(t, method+errSeparator+subnet.ErrSubNotExist, method, []byte{0, 0, 0, 0}, groupId, user) + + cOwn.InvokeFail(t, method+errSeparator+subnet.ErrUserNotExist, method, id, groupId, user) + cOwn.Invoke(t, stackitem.Null{}, "addUser", id, groupId, user) + cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, user) + + cAdm := cOwn.WithSigners(adm) + + cOwn.Invoke(t, stackitem.Null{}, "addClientAdmin", id, groupId, admPub) + cAdm.InvokeFail(t, method+errSeparator+subnet.ErrUserNotExist, method, id, groupId, user) +} + func createSubnet(t *testing.T, e *neotest.ContractInvoker) (id []byte, owner neotest.Signer) { var ( ok bool