[#174] subnet: Add RemoveUser method

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
Pavel Karpy 2021-11-23 14:15:16 +03:00 committed by LeL
parent 9a05e213eb
commit a7a272ad08
2 changed files with 93 additions and 51 deletions

View file

@ -31,6 +31,8 @@ const (
ErrClientAdmNotExist = "client admin not found" ErrClientAdmNotExist = "client admin not found"
// ErrNodeNotExist is thrown when node is not found. // ErrNodeNotExist is thrown when node is not found.
ErrNodeNotExist = "node 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 is thrown when operation is denied for caller.
ErrAccessDenied = "access denied" ErrAccessDenied = "access denied"
@ -228,32 +230,19 @@ func AddNode(subnetID []byte, node interop.PublicKey) {
ctx := storage.GetContext() ctx := storage.GetContext()
stKey := append([]byte{ownerPrefix}, subnetID...) stKey := append([]byte{ownerPrefix}, subnetID...)
prefixLen := len(stKey)
rawOwner := storage.Get(ctx, stKey) rawOwner := storage.Get(ctx, stKey)
if rawOwner == nil { if rawOwner == nil {
panic("addNode: " + ErrSubNotExist) panic("addNode: " + ErrSubNotExist)
} }
owner := rawOwner.([]byte)
if !runtime.CheckWitness(owner) {
var hasAccess bool
stKey[0] = nodeAdminPrefix stKey[0] = nodeAdminPrefix
iter := storage.Find(ctx, stKey, storage.KeysOnly) owner := rawOwner.([]byte)
for iterator.Next(iter) {
key := iterator.Value(iter).([]byte)
if runtime.CheckWitness(key[prefixLen:]) {
hasAccess = true
break
}
}
if !hasAccess { if !calledByOwnerOrAdmin(ctx, owner, stKey) {
panic("addNode: " + ErrAccessDenied) panic("addNode: " + ErrAccessDenied)
} }
}
stKey[0] = nodePrefix stKey[0] = nodePrefix
@ -275,32 +264,19 @@ func RemoveNode(subnetID []byte, node interop.PublicKey) {
ctx := storage.GetContext() ctx := storage.GetContext()
stKey := append([]byte{ownerPrefix}, subnetID...) stKey := append([]byte{ownerPrefix}, subnetID...)
prefixLen := len(stKey)
rawOwner := storage.Get(ctx, stKey) rawOwner := storage.Get(ctx, stKey)
if rawOwner == nil { if rawOwner == nil {
panic("removeNode: " + ErrSubNotExist) panic("removeNode: " + ErrSubNotExist)
} }
owner := rawOwner.([]byte)
if !runtime.CheckWitness(owner) {
var hasAccess bool
stKey[0] = nodeAdminPrefix stKey[0] = nodeAdminPrefix
iter := storage.Find(ctx, stKey, storage.KeysOnly) owner := rawOwner.([]byte)
for iterator.Next(iter) {
key := iterator.Value(iter).([]byte)
if runtime.CheckWitness(key[prefixLen:]) {
hasAccess = true
break
}
}
if !hasAccess { if !calledByOwnerOrAdmin(ctx, owner, stKey) {
panic("removeNode: " + ErrAccessDenied) panic("removeNode: " + ErrAccessDenied)
} }
}
stKey[0] = nodePrefix stKey[0] = nodePrefix
@ -413,28 +389,14 @@ func AddUser(subnetID []byte, groupID []byte, userID []byte) {
panic("addUser: " + ErrSubNotExist) panic("addUser: " + ErrSubNotExist)
} }
stKey[0] = clientAdminPrefix
stKey = append(stKey, groupID...) stKey = append(stKey, groupID...)
prefixLen := len(stKey)
owner := rawOwner.([]byte) owner := rawOwner.([]byte)
if !runtime.CheckWitness(owner) {
var hasAccess bool
stKey[0] = clientAdminPrefix if !calledByOwnerOrAdmin(ctx, owner, stKey) {
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) panic("addUser: " + ErrAccessDenied)
} }
}
stKey[0] = userPrefix stKey[0] = userPrefix
@ -445,6 +407,41 @@ func AddUser(subnetID []byte, groupID []byte, userID []byte) {
putKeyInList(ctx, userID, stKey) 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. // Version returns version of the contract.
func Version() int { func Version() int {
return common.Version 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) { func deleteKeyFromList(ctx storage.Context, keyToDelete interop.PublicKey, prefix []byte) {
storage.Delete(ctx, append(prefix, keyToDelete...)) 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
}

View file

@ -258,6 +258,33 @@ func TestSubnet_AddUser(t *testing.T) {
cOwn.InvokeFail(t, method+errSeparator+"user has already been added", method, id, groupId, user) 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) { func createSubnet(t *testing.T, e *neotest.ContractInvoker) (id []byte, owner neotest.Signer) {
var ( var (
ok bool ok bool