[#52] policy: Add admin

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
This commit is contained in:
Evgenii Stratonikov 2023-11-20 16:10:52 +03:00
parent 20f86e96b2
commit e56e8d02ee
3 changed files with 88 additions and 8 deletions

View file

@ -1,2 +1,6 @@
name: "APE" name: "APE"
safemethods: ["listChains","getChain","listChainsByPrefix"] safemethods:
- "getAdmin"
- "listChains"
- "getChain"
- "listChainsByPrefix"

View file

@ -2,7 +2,9 @@ package policy
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common" "git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
) )
@ -16,8 +18,65 @@ const (
IAM = 'i' IAM = 'i'
) )
const (
ownerKeyPrefix = 'o'
)
const (
// ErrNotAutorized is returned when the none of the transaction signers
// belongs to the list of autorized keys.
ErrNotAutorized = "none of the signers is not autorized to change the contract"
)
// _deploy function sets up initial list of inner ring public keys. // _deploy function sets up initial list of inner ring public keys.
func _deploy(data any, isUpdate bool) { func _deploy(data any, isUpdate bool) {
if isUpdate {
return
}
args := data.(struct {
Admin interop.Hash160
})
ctx := storage.GetContext()
if args.Admin != nil {
if len(args.Admin) != 20 {
panic("invaliad admin hash length")
}
storage.Put(ctx, ownerKey(args.Admin), []byte{1})
}
}
func ownerKey(sender interop.Hash160) []byte {
return append([]byte{ownerKeyPrefix}, sender...)
}
func checkAuthorization(ctx storage.Context) {
if runtime.CheckWitness(common.AlphabetAddress()) {
return
}
admin := getAdmin(ctx)
if admin != nil && runtime.CheckWitness(admin) {
return
}
panic(ErrNotAutorized)
}
func SetAdmin(addr interop.Hash160) {
common.CheckAlphabetWitness()
ctx := storage.GetContext()
storage.Put(ctx, []byte{ownerKeyPrefix}, addr)
}
func GetAdmin() interop.Hash160 {
ctx := storage.GetReadOnlyContext()
return getAdmin(ctx)
}
func getAdmin(ctx storage.Context) interop.Hash160 {
return storage.Get(ctx, []byte{ownerKeyPrefix}).(interop.Hash160)
} }
func storageKey(prefix Kind, entityName, name string) []byte { func storageKey(prefix Kind, entityName, name string) []byte {
@ -28,9 +87,9 @@ func storageKey(prefix Kind, entityName, name string) []byte {
} }
func AddChain(entity Kind, entityName, name string, chain []byte) { func AddChain(entity Kind, entityName, name string, chain []byte) {
common.CheckAlphabetWitness() // TODO: Allow to work with chain directly for everyone?
ctx := storage.GetContext() ctx := storage.GetContext()
checkAuthorization(ctx)
key := storageKey(entity, entityName, name) key := storageKey(entity, entityName, name)
storage.Put(ctx, key, chain) storage.Put(ctx, key, chain)
} }
@ -47,17 +106,17 @@ func GetChain(entity Kind, entityName, name string) []byte {
} }
func RemoveChain(entity Kind, entityName string, name string) { func RemoveChain(entity Kind, entityName string, name string) {
common.CheckAlphabetWitness()
ctx := storage.GetContext() ctx := storage.GetContext()
checkAuthorization(ctx)
key := storageKey(entity, entityName, name) key := storageKey(entity, entityName, name)
storage.Delete(ctx, key) storage.Delete(ctx, key)
} }
func RemoveChainsByPrefix(entity Kind, entityName string, name string) { func RemoveChainsByPrefix(entity Kind, entityName string, name string) {
common.CheckAlphabetWitness()
ctx := storage.GetContext() ctx := storage.GetContext()
checkAuthorization(ctx)
key := storageKey(entity, entityName, name) key := storageKey(entity, entityName, name)
it := storage.Find(ctx, key, storage.KeysOnly) it := storage.Find(ctx, key, storage.KeysOnly)
for iterator.Next(it) { for iterator.Next(it) {

View file

@ -18,7 +18,7 @@ const policyPath = "../policy"
func deployPolicyContract(t *testing.T, e *neotest.Executor) util.Uint160 { func deployPolicyContract(t *testing.T, e *neotest.Executor) util.Uint160 {
cfgPath := path.Join(policyPath, "config.yml") cfgPath := path.Join(policyPath, "config.yml")
c := neotest.CompileFile(t, e.CommitteeHash, policyPath, cfgPath) c := neotest.CompileFile(t, e.CommitteeHash, policyPath, cfgPath)
e.DeployContract(t, c, nil) e.DeployContract(t, c, []any{nil})
return c.Hash return c.Hash
} }
@ -72,6 +72,23 @@ func TestPolicy(t *testing.T) {
}) })
} }
func TestAutorization(t *testing.T) {
e := newPolicyInvoker(t)
e.Invoke(t, stackitem.Null{}, "getAdmin")
s := e.NewAccount(t, 1_0000_0000)
c := e.WithSigners(s)
args := []any{policy.Container, "cnr1", "ingress:myrule3", []byte("opaque")}
c.InvokeFail(t, policy.ErrNotAutorized, "addChain", args...)
e.Invoke(t, stackitem.Null{}, "setAdmin", s.ScriptHash())
e.Invoke(t, stackitem.NewBuffer(s.ScriptHash().BytesBE()), "getAdmin")
c.Invoke(t, stackitem.Null{}, "addChain", args...)
}
func checkChains(t *testing.T, e *neotest.ContractInvoker, namespace, container, name string, expected [][]byte) { func checkChains(t *testing.T, e *neotest.ContractInvoker, namespace, container, name string, expected [][]byte) {
s, err := e.TestInvoke(t, "listChains", namespace, container, name) s, err := e.TestInvoke(t, "listChains", namespace, container, name)
require.NoError(t, err) require.NoError(t, err)