forked from TrueCloudLab/frostfs-contract
[#52] policy: Add admin
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
This commit is contained in:
parent
20f86e96b2
commit
e56e8d02ee
3 changed files with 88 additions and 8 deletions
|
@ -1,2 +1,6 @@
|
||||||
name: "APE"
|
name: "APE"
|
||||||
safemethods: ["listChains","getChain","listChainsByPrefix"]
|
safemethods:
|
||||||
|
- "getAdmin"
|
||||||
|
- "listChains"
|
||||||
|
- "getChain"
|
||||||
|
- "listChainsByPrefix"
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue