ape, adm, container: Form APE requests properly #940
|
@ -151,11 +151,13 @@ func initFrostfsIDCreateNamespaceCmd() {
|
|||
frostfsidCmd.AddCommand(frostfsidCreateNamespaceCmd)
|
||||
frostfsidCreateNamespaceCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||
frostfsidCreateNamespaceCmd.Flags().String(namespaceFlag, "", "Namespace name to create")
|
||||
frostfsidCreateNamespaceCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initFrostfsIDListNamespacesCmd() {
|
||||
frostfsidCmd.AddCommand(frostfsidListNamespacesCmd)
|
||||
frostfsidListNamespacesCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||
frostfsidListNamespacesCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initFrostfsIDCreateSubjectCmd() {
|
||||
|
@ -164,12 +166,14 @@ func initFrostfsIDCreateSubjectCmd() {
|
|||
frostfsidCreateSubjectCmd.Flags().String(namespaceFlag, "", "Namespace where create subject")
|
||||
frostfsidCreateSubjectCmd.Flags().String(subjectNameFlag, "", "Subject name, must be unique in namespace")
|
||||
frostfsidCreateSubjectCmd.Flags().String(subjectKeyFlag, "", "Subject hex-encoded public key")
|
||||
frostfsidCreateSubjectCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initFrostfsIDDeleteSubjectCmd() {
|
||||
frostfsidCmd.AddCommand(frostfsidDeleteSubjectCmd)
|
||||
frostfsidDeleteSubjectCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||
frostfsidDeleteSubjectCmd.Flags().String(subjectAddressFlag, "", "Subject address")
|
||||
frostfsidDeleteSubjectCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initFrostfsIDListSubjectsCmd() {
|
||||
|
@ -177,6 +181,7 @@ func initFrostfsIDListSubjectsCmd() {
|
|||
frostfsidListSubjectsCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||
frostfsidListSubjectsCmd.Flags().String(namespaceFlag, "", "Namespace to list subjects")
|
||||
frostfsidListSubjectsCmd.Flags().Bool(includeNamesFlag, false, "Whether include subject name (require additional requests)")
|
||||
frostfsidListSubjectsCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initFrostfsIDCreateGroupCmd() {
|
||||
|
@ -184,6 +189,7 @@ func initFrostfsIDCreateGroupCmd() {
|
|||
frostfsidCreateGroupCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||
frostfsidCreateGroupCmd.Flags().String(namespaceFlag, "", "Namespace where create group")
|
||||
frostfsidCreateGroupCmd.Flags().String(groupNameFlag, "", "Group name, must be unique in namespace")
|
||||
frostfsidCreateGroupCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initFrostfsIDDeleteGroupCmd() {
|
||||
|
@ -191,12 +197,14 @@ func initFrostfsIDDeleteGroupCmd() {
|
|||
frostfsidDeleteGroupCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||
frostfsidDeleteGroupCmd.Flags().String(namespaceFlag, "", "Namespace to delete group")
|
||||
frostfsidDeleteGroupCmd.Flags().Int64(groupIDFlag, 0, "Group id")
|
||||
frostfsidDeleteGroupCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initFrostfsIDListGroupsCmd() {
|
||||
frostfsidCmd.AddCommand(frostfsidListGroupsCmd)
|
||||
frostfsidListGroupsCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||
frostfsidListGroupsCmd.Flags().String(namespaceFlag, "", "Namespace to list groups")
|
||||
frostfsidListGroupsCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initFrostfsIDAddSubjectToGroupCmd() {
|
||||
|
@ -204,6 +212,7 @@ func initFrostfsIDAddSubjectToGroupCmd() {
|
|||
frostfsidAddSubjectToGroupCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||
frostfsidAddSubjectToGroupCmd.Flags().String(subjectAddressFlag, "", "Subject address")
|
||||
frostfsidAddSubjectToGroupCmd.Flags().Int64(groupIDFlag, 0, "Group id")
|
||||
frostfsidAddSubjectToGroupCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initFrostfsIDRemoveSubjectFromGroupCmd() {
|
||||
|
@ -211,6 +220,7 @@ func initFrostfsIDRemoveSubjectFromGroupCmd() {
|
|||
frostfsidRemoveSubjectFromGroupCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||
frostfsidRemoveSubjectFromGroupCmd.Flags().String(subjectAddressFlag, "", "Subject address")
|
||||
frostfsidRemoveSubjectFromGroupCmd.Flags().Int64(groupIDFlag, 0, "Group id")
|
||||
frostfsidRemoveSubjectFromGroupCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initFrostfsIDListGroupSubjectsCmd() {
|
||||
|
@ -219,6 +229,7 @@ func initFrostfsIDListGroupSubjectsCmd() {
|
|||
frostfsidListGroupSubjectsCmd.Flags().String(namespaceFlag, "", "Namespace name")
|
||||
frostfsidListGroupSubjectsCmd.Flags().Int64(groupIDFlag, 0, "Group id")
|
||||
frostfsidListGroupSubjectsCmd.Flags().Bool(includeNamesFlag, false, "Whether include subject name (require additional requests)")
|
||||
frostfsidListGroupSubjectsCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func frostfsidCreateNamespace(cmd *cobra.Command, _ []string) {
|
||||
|
|
|
@ -449,6 +449,7 @@ type cfg struct {
|
|||
cfgMorph cfgMorph
|
||||
cfgAccounting cfgAccounting
|
||||
cfgContainer cfgContainer
|
||||
cfgFrostfsID cfgFrostfsID
|
||||
cfgNodeInfo cfgNodeInfo
|
||||
cfgNetmap cfgNetmap
|
||||
cfgControlService cfgControlService
|
||||
|
@ -569,6 +570,10 @@ type cfgContainer struct {
|
|||
workerPool util.WorkerPool // pool for asynchronous handlers
|
||||
}
|
||||
|
||||
type cfgFrostfsID struct {
|
||||
scriptHash neogoutil.Uint160
|
||||
}
|
||||
|
||||
type cfgNetmap struct {
|
||||
scriptHash neogoutil.Uint160
|
||||
wrapper *nmClient.Client
|
||||
|
@ -681,6 +686,8 @@ func initCfg(appCfg *config.Config) *cfg {
|
|||
}
|
||||
c.cfgContainer = initContainer(appCfg)
|
||||
|
||||
c.cfgFrostfsID = initFrostfsID(appCfg)
|
||||
|
||||
c.cfgNetmap = initNetmap(appCfg, netState, relayOnly)
|
||||
|
||||
c.cfgGRPC = initCfgGRPC()
|
||||
|
@ -779,6 +786,12 @@ func initContainer(appCfg *config.Config) cfgContainer {
|
|||
}
|
||||
}
|
||||
|
||||
func initFrostfsID(appCfg *config.Config) cfgFrostfsID {
|
||||
return cfgFrostfsID{
|
||||
scriptHash: contractsconfig.FrostfsID(appCfg),
|
||||
}
|
||||
}
|
||||
|
||||
func initCfgGRPC() cfgGRPC {
|
||||
maxChunkSize := uint64(maxMsgSize) * 3 / 4 // 25% to meta, 75% to payload
|
||||
maxAddrAmount := uint64(maxChunkSize) / addressSize // each address is about 72 bytes
|
||||
|
|
|
@ -38,6 +38,10 @@ func Container(c *config.Config) util.Uint160 {
|
|||
return contractAddress(c, "container")
|
||||
}
|
||||
|
||||
func FrostfsID(c *config.Config) util.Uint160 {
|
||||
|
||||
return contractAddress(c, "frostfsid")
|
||||
}
|
||||
|
||||
// Proxy returnsthe value of "proxy" config parameter
|
||||
// from "contracts" section.
|
||||
//
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||
containerCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
||||
cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
|
||||
containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
|
||||
containerTransportGRPC "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network/transport/container/grpc"
|
||||
|
@ -32,11 +33,14 @@ func initContainerService(_ context.Context, c *cfg) {
|
|||
|
||||
cnrRdr, cnrWrt := configureEACLAndContainerSources(c, wrap, cnrSrc)
|
||||
|
||||
frostFSIDClient, err := frostfsid.NewFromMorph(c.cfgMorph.client, c.cfgFrostfsID.scriptHash, 0)
|
||||
fatalOnErr(err)
|
||||
|
||||
server := containerTransportGRPC.New(
|
||||
containerService.NewSignService(
|
||||
&c.key.PrivateKey,
|
||||
containerService.NewAPEServer(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine, cnrRdr,
|
||||
newCachedIRFetcher(createInnerRingFetcher(c)), c.netMapSource,
|
||||
newCachedIRFetcher(createInnerRingFetcher(c)), c.netMapSource, frostFSIDClient,
|
||||
containerService.NewExecutionService(containerMorph.NewExecutor(cnrRdr, cnrWrt), c.respSvc),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -288,6 +288,7 @@ func lookupScriptHashesInNNS(c *cfg) {
|
|||
{&c.cfgNetmap.scriptHash, client.NNSNetmapContractName},
|
||||
{&c.cfgAccounting.scriptHash, client.NNSBalanceContractName},
|
||||
{&c.cfgContainer.scriptHash, client.NNSContainerContractName},
|
||||
{&c.cfgFrostfsID.scriptHash, client.NNSFrostFSIDContractName},
|
||||
{&c.cfgMorph.proxyScriptHash, client.NNSProxyContractName},
|
||||
{&c.cfgObject.cfgAccessPolicyEngine.policyContractHash, client.NNSPolicyContractName},
|
||||
}
|
||||
|
|
2
go.mod
|
@ -6,7 +6,7 @@ require (
|
|||
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240112150928-72885aae835c
|
||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20240115082915-f2a82aa635aa
|
||||
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20231101111734-b3ad3335ff65
|
||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240117145620-110b7e41706e
|
||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240126141009-65b4525b3bf0
|
||||
git.frostfs.info/TrueCloudLab/hrw v1.2.1
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240122104724-06cbfe8691ad
|
||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0
|
||||
|
|
BIN
go.sum
|
@ -115,6 +115,10 @@ func getTargetBucket(tx *bbolt.Tx, name chain.Name, target policyengine.Target)
|
|||
return nil, fmt.Errorf("%w: %w: %c", policyengine.ErrChainNotFound, ErrTargetTypeBucketNotFound, target.Type)
|
||||
}
|
||||
|
||||
if target.Type == policyengine.Namespace && target.Name == "" {
|
||||
target.Name = "root"
|
||||
dkirillov
commented
Such renaming is actual only for local overrides or also for rules in contract? Such renaming is actual only for local overrides or also for rules in contract?
aarifullin
commented
Only for local overrides (this is because boltdb does not allow to create buckets with empty names) I also would like to highlight that listing with empty namespace does not behave the same way as in Policy contract **Only** for local overrides (this is because boltdb does not allow to create buckets with empty names)
I also would like to highlight that listing with empty namespace does not behave the same way as in Policy contract
|
||||
}
|
||||
|
||||
rbucket := typeBucket.Bucket([]byte(target.Name))
|
||||
if rbucket == nil {
|
||||
return nil, fmt.Errorf("%w: %w: %s", policyengine.ErrChainNotFound, ErrTargetNameBucketNotFound, target.Name)
|
||||
|
@ -146,6 +150,10 @@ func getTargetBucketCreateIfEmpty(tx *bbolt.Tx, name chain.Name, target policyen
|
|||
}
|
||||
}
|
||||
|
||||
if target.Type == policyengine.Namespace && target.Name == "" {
|
||||
target.Name = "root"
|
||||
}
|
||||
|
||||
rbucket := typeBucket.Bucket([]byte(target.Name))
|
||||
if rbucket == nil {
|
||||
var err error
|
||||
|
|
|
@ -33,16 +33,17 @@ func (c *Client) GetSubject(addr util.Uint160) (*frostfsidclient.Subject, error)
|
|||
return subj, nil
|
||||
}
|
||||
|
||||
// parseSubject from https://git.frostfs.info/TrueCloudLab/frostfs-contract/src/commit/dd5919348da9731f24504e7bc485516c2ba5f11c/frostfsid/client/client.go#L592
|
||||
func parseSubject(structArr []stackitem.Item) (*frostfsidclient.Subject, error) {
|
||||
aarifullin
commented
The current implementation does not work - I was obliged to slightly fix it The current implementation does not work - I was obliged to slightly fix it
|
||||
if len(structArr) < 5 {
|
||||
return nil, errors.New("invalid response subject struct")
|
||||
func parseSubject(res []stackitem.Item) (*frostfsidclient.Subject, error) {
|
||||
if ln := len(res); ln != 1 {
|
||||
return nil, fmt.Errorf("unexpected stack item count (%s): %d", methodGetSubject, ln)
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
subj frostfsidclient.Subject
|
||||
)
|
||||
structArr, err := client.ArrayFromStackItem(res[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get item array of container (%s): %w", methodGetSubject, err)
|
||||
}
|
||||
|
||||
var subj frostfsidclient.Subject
|
||||
|
||||
subj.PrimaryKey, err = unwrap.PublicKey(makeValidRes(structArr[0]))
|
||||
if err != nil {
|
||||
|
|
|
@ -8,14 +8,17 @@ import (
|
|||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
|
||||
containercore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cnrSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
sessionSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||
|
@ -25,6 +28,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource"
|
||||
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -35,6 +39,7 @@ var (
|
|||
errInvalidSessionTokenOwner = errors.New("malformed request: invalid session token owner")
|
||||
errEmptyBodySignature = errors.New("malformed request: empty body signature")
|
||||
errMissingOwnerID = errors.New("malformed request: missing owner ID")
|
||||
errSubjectNotFound = errors.New("subject not found")
|
||||
|
||||
undefinedContainerID = cid.ID{}
|
||||
)
|
||||
|
@ -47,22 +52,29 @@ type containers interface {
|
|||
Get(cid.ID) (*containercore.Container, error)
|
||||
}
|
||||
|
||||
type frostfsidSubjectProvider interface {
|
||||
GetSubject(util.Uint160) (*client.Subject, error)
|
||||
}
|
||||
|
||||
type apeChecker struct {
|
||||
router policyengine.ChainRouter
|
||||
reader containers
|
||||
ir ir
|
||||
nm netmap.Source
|
||||
|
||||
frostFSIDClient frostfsidSubjectProvider
|
||||
|
||||
next Server
|
||||
}
|
||||
|
||||
func NewAPEServer(router policyengine.ChainRouter, reader containers, ir ir, nm netmap.Source, srv Server) Server {
|
||||
func NewAPEServer(router policyengine.ChainRouter, reader containers, ir ir, nm netmap.Source, frostFSIDClient frostfsidSubjectProvider, srv Server) Server {
|
||||
return &apeChecker{
|
||||
router: router,
|
||||
reader: reader,
|
||||
ir: ir,
|
||||
next: srv,
|
||||
nm: nm,
|
||||
frostFSIDClient: frostFSIDClient,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,9 +137,17 @@ func (ac *apeChecker) List(ctx context.Context, req *container.ListRequest) (*co
|
|||
nativeschema.PropertyKeyActorRole: role,
|
||||
}
|
||||
|
||||
namespace, err := ac.namespaceByOwner(req.GetBody().GetOwnerID())
|
||||
dkirillov
commented
Shouldn't we get namespace from actor instead? Shouldn't we get namespace from actor instead?
aarifullin
commented
Although we discussed this privately with @dkirillov, I am going to describe the idea: This may lead to a situation when resource name can be calculated incorrectly, because an actor can be out of owner's namespace Although we discussed this privately with @dkirillov, I am going to describe the idea:
This may lead to a situation when resource name can be calculated incorrectly, because an actor can be out of owner's namespace
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get owner namespace: %w", err)
|
||||
}
|
||||
if err := ac.validateNamespaceByPublicKey(pk, namespace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request := &apeRequest{
|
||||
resource: &apeResource{
|
||||
name: nativeschema.ResourceFormatRootContainers,
|
||||
name: resourceName(namespace, ""),
|
||||
props: make(map[string]string),
|
||||
},
|
||||
op: nativeschema.MethodListContainers,
|
||||
|
@ -135,7 +155,7 @@ func (ac *apeChecker) List(ctx context.Context, req *container.ListRequest) (*co
|
|||
}
|
||||
|
||||
s, found, err := ac.router.IsAllowed(apechain.Ingress,
|
||||
policyengine.NewRequestTargetWithNamespace(""),
|
||||
policyengine.NewRequestTargetWithNamespace(namespace),
|
||||
request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -162,9 +182,17 @@ func (ac *apeChecker) Put(ctx context.Context, req *container.PutRequest) (*cont
|
|||
nativeschema.PropertyKeyActorRole: role,
|
||||
dkirillov
commented
Should we check that the user is trying to create a container in the namespace it belongs to? Should we check that the user is trying to create a container in the namespace it belongs to?
Otherwise it seems he can bypass rule that restrict any container creation in `namespace1` if he belongs to `namespace2`
aarifullin
commented
I was thinking about this scenario. But now I think this check should be used here because using "from FFsID"-namespace is implicit and an error may seem not obvious I was thinking about this scenario.
If we are putting a conatiner where a namespace cut from `Zone()` differs from a namespace got from FFsID contract (by OwnerID), then the check is performed on "from FFsID"-namespace anyway. When the creation is pushed to the sidechain to be processed by an inner-ring node, it performs this check and denies to create a conatiner because of different containers.
But now I think **this check should be used here** because using "from FFsID"-namespace is implicit and an error may seem not obvious
dkirillov
commented
Actually, now I can create container in Actually, now I can create container in `namespace1.ns` zone if I use key from `namespace2` and have rule that forbids any creation in `namespace1`. But you said that I couldn't do that. Am I missing something?
aarifullin
commented
Have you added a subject into frostfs-id contract? Have you added a subject into frostfs-id contract?
aarifullin
commented
BTW, I have added validation BTW, I have added validation
dkirillov
commented
Yes > Have you added a subject into frostfs-id contract
Yes
aarifullin
commented
Let's take this step by step. First, we need to register Then, when
So, let's go on:
Therefore, you cannot create container in > Actually, now I can create container in namespace1.ns zone if I use key from namespace2 and have rule that forbids any creation in namespace1. But you said that I couldn't do that. Am I missing something?
Let's take this step by step.
First, we need to register `ns`, `namespace2.ns` in NNS contrace (`frostfs-adm` util does not support registring yet, I do it locally in mishmashed manner :)).
Then, when `namespace2` exists, we need to bind an owner with this namespace.
Note `"022bb4041c50d607ff871dec7e4cd7778388e0ea6849d84ccbd9aa8f32e16a8131"` is got from owner in unit-test created above.
```bash
frostfs-adm morph frostfsid create-namespace --namespace namespace2 --rpc-endpoint='http://morph-chain.frostfs.devenv:30333' --alphabet-wallets='frostfs-dev-env/services/ir';
frostfs-adm morph frostfsid create-subject --namespace namespace2 --subject-key "022bb4041c50d607ff871dec7e4cd7778388e0ea6849d84ccbd9aa8f32e16a8131" --subject-name nodeowner --rpc-endpoint='http://morph-chain.frostfs.devenv:30333' --alphabet-wallets='frostfs-dev-env/services/ir';
```
So, let's go on:
```go
t.Run("check restrict create container", func(t *testing.T) {
namespaceForbid := "namespace1"
chainRestrictObjectPut := &chain.Chain{ID: "chainRestrictContainerPut", Rules: []chain.Rule{{
Status: chain.AccessDenied,
Actions: chain.Actions{Names: []string{native.MethodPutContainer}},
Resources: chain.Resources{Names: []string{fmt.Sprintf(native.ResourceFormatNamespaceContainers, namespaceForbid)}},
}}}
err = putPolicy(&controlCli, key, namespaceForbid, chainRestrictObjectPut)
require.NoError(t, err)
_, err := createContainer(ctx, p, ownerID, "test1", "namespace1.ns")
require.Error(t, err)
})
```
Therefore, you cannot create container in `"namespace1.ns"`
|
||||
}
|
||||
|
||||
namespace, err := ac.namespaceByOwner(req.GetBody().GetContainer().GetOwnerID())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get namespace error: %w", err)
|
||||
}
|
||||
if err = validateNamespace(req.GetBody().GetContainer(), namespace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request := &apeRequest{
|
||||
resource: &apeResource{
|
||||
name: nativeschema.ResourceFormatRootContainers,
|
||||
name: resourceName(namespace, ""),
|
||||
props: make(map[string]string),
|
||||
},
|
||||
op: nativeschema.MethodPutContainer,
|
||||
|
@ -172,7 +200,7 @@ func (ac *apeChecker) Put(ctx context.Context, req *container.PutRequest) (*cont
|
|||
}
|
||||
|
||||
s, found, err := ac.router.IsAllowed(apechain.Ingress,
|
||||
policyengine.NewRequestTargetWithNamespace(""),
|
||||
policyengine.NewRequestTargetWithNamespace(namespace),
|
||||
request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -251,9 +279,15 @@ func (ac *apeChecker) validateContainerBoundedOperation(containerID *refs.Contai
|
|||
return err
|
||||
}
|
||||
|
||||
namespace := ""
|
||||
cntNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(cont.Value).Zone(), ".ns")
|
||||
if hasNamespace {
|
||||
namespace = cntNamespace
|
||||
}
|
||||
|
||||
request := &apeRequest{
|
||||
resource: &apeResource{
|
||||
name: fmt.Sprintf(nativeschema.ResourceFormatRootContainer, id.EncodeToString()),
|
||||
name: resourceName(namespace, id.EncodeToString()),
|
||||
props: ac.getContainerProps(cont),
|
||||
},
|
||||
op: op,
|
||||
|
@ -261,7 +295,7 @@ func (ac *apeChecker) validateContainerBoundedOperation(containerID *refs.Contai
|
|||
}
|
||||
|
||||
s, found, err := ac.router.IsAllowed(apechain.Ingress,
|
||||
policyengine.NewRequestTargetWithContainer(id.EncodeToString()),
|
||||
policyengine.NewRequestTarget(cntNamespace, id.EncodeToString()),
|
||||
request)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -326,6 +360,19 @@ func (r *apeResource) Property(key string) string {
|
|||
return r.props[key]
|
||||
}
|
||||
|
||||
func resourceName(namespace string, container string) string {
|
||||
if namespace == "" && container == "" {
|
||||
return nativeschema.ResourceFormatRootContainers
|
||||
}
|
||||
if namespace == "" && container != "" {
|
||||
return fmt.Sprintf(nativeschema.ResourceFormatRootContainer, container)
|
||||
}
|
||||
if namespace != "" && container == "" {
|
||||
return fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainers, namespace)
|
||||
}
|
||||
return fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainer, namespace, container)
|
||||
}
|
||||
|
||||
func (ac *apeChecker) getContainerProps(c *containercore.Container) map[string]string {
|
||||
return map[string]string{
|
||||
nativeschema.PropertyKeyContainerOwnerID: c.Value.Owner().EncodeToString(),
|
||||
|
@ -514,3 +561,70 @@ func isContainerNode(nm *netmapSDK.NetMap, pk, binCnrID []byte, cont *containerc
|
|||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (ac *apeChecker) namespaceByOwner(owner *refs.OwnerID) (string, error) {
|
||||
var ownerSDK user.ID
|
||||
if owner == nil {
|
||||
return "", fmt.Errorf("owner id is not set")
|
||||
}
|
||||
if err := ownerSDK.ReadFromV2(*owner); err != nil {
|
||||
return "", err
|
||||
}
|
||||
addr, err := ownerSDK.ScriptHash()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
namespace := ""
|
||||
subject, err := ac.frostFSIDClient.GetSubject(addr)
|
||||
if err == nil {
|
||||
namespace = subject.Namespace
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), errSubjectNotFound.Error()) {
|
||||
return "", fmt.Errorf("get subject error: %w", err)
|
||||
}
|
||||
}
|
||||
return namespace, nil
|
||||
}
|
||||
|
||||
// validateNamespace validates a namespace set in a container.
|
||||
// If frostfs-id contract stores a namespace N1 for an owner ID and a container within a request
|
||||
// is set with namespace N2 (via Zone() property), then N2 is invalid and the request is denied.
|
||||
func validateNamespace(cnrV2 *container.Container, ownerIDNamespace string) error {
|
||||
if cnrV2 == nil {
|
||||
return nil
|
||||
}
|
||||
var cnr cnrSDK.Container
|
||||
if err := cnr.ReadFromV2(*cnrV2); err != nil {
|
||||
return err
|
||||
dkirillov
commented
This doesn't work if we are trying to create container in This doesn't work if we are trying to create container in `container` zone (aka root namespace)
aarifullin
commented
Let me check Let me check
aarifullin
commented
Sorry, I didn't get your point.
(1) - Sorry, I didn't get your point.
Let's consider all cases
1. `cnr.zone` is empty, i.e. `Zone()` returns `"container"`; `ownerIDNamespace` is empty
2. `cnr.zone` is `"container"`, i.e. `Zone()` returns `"container"`; `ownerIDNamespace` is empty
3. `cnr.zone` is `namespace1.ns`; `ownerIDNamespace` is empty
4. `cnr.zone` is `namespace1.ns`; `ownerIDNamespace` is `namespace1`
5. `cnr.zone` is `namespace1.ns`; `ownerIDNamespace` is `namespace2`
(1) - `hasNamespace = false` -> returns `nil`. Correct
(2) - `hasNamespace = false` -> returns `nil`. Correct
(3) - `hasNamespace = true` -> returns `error`. Correct, because frostfs-id has not bound the owner with the namespace
(4) - `hasNamespace = true` -> returns `nil`. Correct, because frostfs-id bound the owner with the namespace
(5) - `hasNamespace = true` -> returns `error`. Correct, because frostfs-id has not bound the owner with the `namespace1`, only `namespace2`
dkirillov
commented
Why are the cases 1 and 2 correct? We will get no error here and fetch rules in Why are the cases 1 and 2 correct? We will get no error [here](https://git.frostfs.info/aarifullin/frostfs-node/src/commit/7eb90e7c00fc7c2c1b6f164bffc86135fb4732cc/pkg/services/container/ape.go#L186) and fetch rules in `ownerIDnamespace` but if we can have denial rules in root namespace we still create container there (but shouldn't I suppose)
aarifullin
commented
I still do not see any contradiction here. If container is being put without domain and an owner ID is not found in frostfs-id contract, then we suppose a container is created within root namespace
And because it is empty - we fetch rules for root namespace
If denial rules are defined for root namespace, they will work out. > but if we can have denial rules in root namespace we still create container there
I still do not see any contradiction here. If container is being put without domain and an owner ID is not found in frostfs-id contract, then we suppose a container is created within root namespace
> and fetch rules in ownerIDnamespace
And because it is empty - we fetch rules for root namespace
> we can have denial rules in root namespace
If denial rules are defined for root namespace, they will work out.
This validation is rather needed to make sure a client sets correct zone (if he sets it for some reason - although, it is not required)
dkirillov
commented
Well, I want the following test to pass:
Well, I want the following test to pass:
```golang
func TestDenyCreationContainerInRootIfActorInCustomNS(t *testing.T) {
srv := &srvStub{
calls: map[string]int{},
}
router := inmemory.NewInMemory()
contRdr := &containerStub{
c: map[cid.ID]*containercore.Container{},
}
ir := &irStub{
keys: [][]byte{},
}
nm := &netmapStub{}
cnrID, testContainer := initTestContainer(t, false)
contRdr.c[cnrID] = &containercore.Container{Value: testContainer}
nm.currentEpoch = 100
nm.netmaps = map[uint64]*netmap.NetMap{}
_, _, err := router.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(""), &chain.Chain{
Rules: []chain.Rule{{
Status: chain.AccessDenied,
Actions: chain.Actions{Names: []string{nativeschema.MethodPutContainer}},
Resources: chain.Resources{Names: []string{nativeschema.ResourceFormatRootContainers}},
}},
})
require.NoError(t, err)
req := initPutRequest(t, testContainer)
ownerScriptHash := initOwnerIDScriptHash(t, testContainer)
frostfsIDSubjectReader := &frostfsidStub{
subjects: map[util.Uint160]*client.Subject{
ownerScriptHash: {
Namespace: testDomainName,
Name: testDomainName,
},
},
}
apeSrv := NewAPEServer(router, contRdr, ir, nm, frostfsIDSubjectReader, srv)
resp, err := apeSrv.Put(context.Background(), req)
require.Nil(t, resp)
var errAccessDenied *apistatus.ObjectAccessDenied
require.ErrorAs(t, err, &errAccessDenied)
}
```
aarifullin
commented
I have updated PR with more strict validation for namespaces for Put and List. Your test-case above is no longer actual. Please, check this out If owner ID can be found within frostfs-id and no domain params are set in cnr before Put - container won't be created, because a client must set these params by I have updated PR with more strict validation for namespaces for Put and List. Your test-case above is no longer actual. Please, check this out
If owner ID can be found within frostfs-id and no domain params are set in cnr before Put - container won't be created, because a client must set these params by `SetDomain`. Therefore, a resource name for such a container will define namespace (cannot be root so)
|
||||
}
|
||||
cntNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(cnr).Zone(), ".ns")
|
||||
if hasNamespace {
|
||||
if cntNamespace != ownerIDNamespace {
|
||||
if ownerIDNamespace == "" {
|
||||
return fmt.Errorf("invalid domain zone: no namespace is expected")
|
||||
}
|
||||
return fmt.Errorf("invalid domain zone: expected namespace %s, but got %s", ownerIDNamespace, cntNamespace)
|
||||
}
|
||||
} else if ownerIDNamespace != "" {
|
||||
return fmt.Errorf("invalid domain zone: expected namespace %s, but got invalid or empty", ownerIDNamespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateNamespace validates if a namespace of a request actor equals to owner's namespace.
|
||||
// An actor's namespace is calculated by a public key.
|
||||
func (ac *apeChecker) validateNamespaceByPublicKey(pk *keys.PublicKey, ownerIDNamespace string) error {
|
||||
var actor user.ID
|
||||
user.IDFromKey(&actor, (ecdsa.PublicKey)(*pk))
|
||||
actorOwnerID := new(refs.OwnerID)
|
||||
actor.WriteToV2(actorOwnerID)
|
||||
actorNamespace, err := ac.namespaceByOwner(actorOwnerID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get actor namespace: %w", err)
|
||||
}
|
||||
if actorNamespace != ownerIDNamespace {
|
||||
return fmt.Errorf("actor namespace %s differs from owner: %s", actorNamespace, ownerIDNamespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package container
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
@ -9,10 +10,12 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
|
||||
containercore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cnrSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
containertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/test"
|
||||
|
@ -24,16 +27,25 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine/inmemory"
|
||||
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
testDomainName = "testdomainname"
|
||||
testDomainZone = "testdomainname.ns"
|
||||
)
|
||||
|
||||
func TestAPE(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("deny get container for others", testDenyGetContainerForOthers)
|
||||
t.Run("deny set container eACL for IR", testDenySetContainerEACLForIR)
|
||||
t.Run("deny get container eACL for IR with session token", testDenyGetContainerEACLForIRSessionToken)
|
||||
t.Run("deny put container for others with session token", testDenyPutContainerForOthersSessionToken)
|
||||
t.Run("deny put container, read namespace from frostfsID", testDenyPutContainerReadNamespaceFromFrostfsID)
|
||||
t.Run("deny put container with invlaid namespace", testDenyPutContainerInvalidNamespace)
|
||||
t.Run("deny list containers for owner with PK", testDenyListContainersForPK)
|
||||
t.Run("deny list containers by namespace invalidation", testDenyListContainersValidationNamespaceError)
|
||||
}
|
||||
|
||||
func testDenyGetContainerForOthers(t *testing.T) {
|
||||
|
@ -49,7 +61,10 @@ func testDenyGetContainerForOthers(t *testing.T) {
|
|||
keys: [][]byte{},
|
||||
}
|
||||
nm := &netmapStub{}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, srv)
|
||||
frostfsIDSubjectReader := &frostfsidStub{
|
||||
subjects: map[util.Uint160]*client.Subject{},
|
||||
}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, frostfsIDSubjectReader, srv)
|
||||
|
||||
contID := cidtest.ID()
|
||||
testContainer := containertest.Container()
|
||||
|
@ -122,7 +137,10 @@ func testDenySetContainerEACLForIR(t *testing.T) {
|
|||
keys: [][]byte{},
|
||||
}
|
||||
nm := &netmapStub{}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, srv)
|
||||
frostfsIDSubjectReader := &frostfsidStub{
|
||||
subjects: map[util.Uint160]*client.Subject{},
|
||||
}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, frostfsIDSubjectReader, srv)
|
||||
|
||||
contID := cidtest.ID()
|
||||
testContainer := containertest.Container()
|
||||
|
@ -197,7 +215,10 @@ func testDenyGetContainerEACLForIRSessionToken(t *testing.T) {
|
|||
keys: [][]byte{},
|
||||
}
|
||||
nm := &netmapStub{}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, srv)
|
||||
frostfsIDSubjectReader := &frostfsidStub{
|
||||
subjects: map[util.Uint160]*client.Subject{},
|
||||
}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, frostfsIDSubjectReader, srv)
|
||||
|
||||
contID := cidtest.ID()
|
||||
testContainer := containertest.Container()
|
||||
|
@ -283,7 +304,10 @@ func testDenyPutContainerForOthersSessionToken(t *testing.T) {
|
|||
keys: [][]byte{},
|
||||
}
|
||||
nm := &netmapStub{}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, srv)
|
||||
frostfsIDSubjectReader := &frostfsidStub{
|
||||
subjects: map[util.Uint160]*client.Subject{},
|
||||
}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, frostfsIDSubjectReader, srv)
|
||||
|
||||
testContainer := containertest.Container()
|
||||
|
||||
|
@ -317,26 +341,7 @@ func testDenyPutContainerForOthersSessionToken(t *testing.T) {
|
|||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req := &container.PutRequest{}
|
||||
req.SetBody(&container.PutRequestBody{})
|
||||
var reqCont container.Container
|
||||
testContainer.WriteToV2(&reqCont)
|
||||
req.GetBody().SetContainer(&reqCont)
|
||||
|
||||
sessionPK, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
sToken := sessiontest.ContainerSigned()
|
||||
sToken.ApplyOnlyTo(cid.ID{})
|
||||
require.NoError(t, sToken.Sign(sessionPK.PrivateKey))
|
||||
var sTokenV2 session.Token
|
||||
sToken.WriteToV2(&sTokenV2)
|
||||
metaHeader := new(session.RequestMetaHeader)
|
||||
metaHeader.SetSessionToken(&sTokenV2)
|
||||
req.SetMetaHeader(metaHeader)
|
||||
|
||||
pk, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, signature.SignServiceMessage(&pk.PrivateKey, req))
|
||||
req := initPutRequest(t, testContainer)
|
||||
|
||||
resp, err := apeSrv.Put(context.Background(), req)
|
||||
require.Nil(t, resp)
|
||||
|
@ -344,6 +349,139 @@ func testDenyPutContainerForOthersSessionToken(t *testing.T) {
|
|||
require.ErrorAs(t, err, &errAccessDenied)
|
||||
}
|
||||
|
||||
func testDenyPutContainerReadNamespaceFromFrostfsID(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := &srvStub{
|
||||
calls: map[string]int{},
|
||||
}
|
||||
router := inmemory.NewInMemory()
|
||||
contRdr := &containerStub{
|
||||
c: map[cid.ID]*containercore.Container{},
|
||||
}
|
||||
ir := &irStub{
|
||||
keys: [][]byte{},
|
||||
}
|
||||
nm := &netmapStub{}
|
||||
|
||||
cnrID, testContainer := initTestContainer(t, true)
|
||||
contRdr.c[cnrID] = &containercore.Container{Value: testContainer}
|
||||
|
||||
nm.currentEpoch = 100
|
||||
nm.netmaps = map[uint64]*netmap.NetMap{}
|
||||
|
||||
_, _, err := router.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(testDomainName), &chain.Chain{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.AccessDenied,
|
||||
Actions: chain.Actions{
|
||||
Names: []string{
|
||||
nativeschema.MethodPutContainer,
|
||||
},
|
||||
},
|
||||
Resources: chain.Resources{
|
||||
Names: []string{
|
||||
fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainers, testDomainName),
|
||||
},
|
||||
},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Object: chain.ObjectRequest,
|
||||
Key: nativeschema.PropertyKeyActorRole,
|
||||
Value: nativeschema.PropertyValueContainerRoleOthers,
|
||||
Op: chain.CondStringEquals,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req := initPutRequest(t, testContainer)
|
||||
ownerScriptHash := initOwnerIDScriptHash(t, testContainer)
|
||||
|
||||
frostfsIDSubjectReader := &frostfsidStub{
|
||||
subjects: map[util.Uint160]*client.Subject{
|
||||
ownerScriptHash: {
|
||||
Namespace: testDomainName,
|
||||
Name: testDomainName,
|
||||
},
|
||||
},
|
||||
}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, frostfsIDSubjectReader, srv)
|
||||
resp, err := apeSrv.Put(context.Background(), req)
|
||||
require.Nil(t, resp)
|
||||
var errAccessDenied *apistatus.ObjectAccessDenied
|
||||
require.ErrorAs(t, err, &errAccessDenied)
|
||||
}
|
||||
|
||||
func testDenyPutContainerInvalidNamespace(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := &srvStub{
|
||||
calls: map[string]int{},
|
||||
}
|
||||
router := inmemory.NewInMemory()
|
||||
contRdr := &containerStub{
|
||||
c: map[cid.ID]*containercore.Container{},
|
||||
}
|
||||
ir := &irStub{
|
||||
keys: [][]byte{},
|
||||
}
|
||||
nm := &netmapStub{}
|
||||
|
||||
cnrID, testContainer := initTestContainer(t, false)
|
||||
var domain cnrSDK.Domain
|
||||
domain.SetName("incorrect" + testDomainName)
|
||||
domain.SetZone("incorrect" + testDomainZone)
|
||||
cnrSDK.WriteDomain(&testContainer, domain)
|
||||
contRdr.c[cnrID] = &containercore.Container{Value: testContainer}
|
||||
|
||||
nm.currentEpoch = 100
|
||||
nm.netmaps = map[uint64]*netmap.NetMap{}
|
||||
|
||||
_, _, err := router.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(testDomainName), &chain.Chain{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.AccessDenied,
|
||||
Actions: chain.Actions{
|
||||
Names: []string{
|
||||
nativeschema.MethodPutContainer,
|
||||
},
|
||||
},
|
||||
Resources: chain.Resources{
|
||||
Names: []string{
|
||||
fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainers, testDomainName),
|
||||
},
|
||||
},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Object: chain.ObjectRequest,
|
||||
Key: nativeschema.PropertyKeyActorRole,
|
||||
Value: nativeschema.PropertyValueContainerRoleOthers,
|
||||
Op: chain.CondStringEquals,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req := initPutRequest(t, testContainer)
|
||||
ownerScriptHash := initOwnerIDScriptHash(t, testContainer)
|
||||
|
||||
frostfsIDSubjectReader := &frostfsidStub{
|
||||
subjects: map[util.Uint160]*client.Subject{
|
||||
ownerScriptHash: {
|
||||
Namespace: testDomainName,
|
||||
Name: testDomainName,
|
||||
},
|
||||
},
|
||||
}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, frostfsIDSubjectReader, srv)
|
||||
resp, err := apeSrv.Put(context.Background(), req)
|
||||
require.Nil(t, resp)
|
||||
require.ErrorContains(t, err, "invalid domain zone")
|
||||
}
|
||||
|
||||
func testDenyListContainersForPK(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := &srvStub{
|
||||
|
@ -357,7 +495,10 @@ func testDenyListContainersForPK(t *testing.T) {
|
|||
keys: [][]byte{},
|
||||
}
|
||||
nm := &netmapStub{}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, srv)
|
||||
frostfsIDSubjectReader := &frostfsidStub{
|
||||
subjects: map[util.Uint160]*client.Subject{},
|
||||
}
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, frostfsIDSubjectReader, srv)
|
||||
|
||||
nm.currentEpoch = 100
|
||||
nm.netmaps = map[uint64]*netmap.NetMap{}
|
||||
|
@ -409,6 +550,82 @@ func testDenyListContainersForPK(t *testing.T) {
|
|||
require.ErrorAs(t, err, &errAccessDenied)
|
||||
}
|
||||
|
||||
func testDenyListContainersValidationNamespaceError(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv := &srvStub{
|
||||
calls: map[string]int{},
|
||||
}
|
||||
router := inmemory.NewInMemory()
|
||||
contRdr := &containerStub{
|
||||
c: map[cid.ID]*containercore.Container{},
|
||||
}
|
||||
ir := &irStub{
|
||||
keys: [][]byte{},
|
||||
}
|
||||
nm := &netmapStub{}
|
||||
|
||||
actorPK, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
ownerPK, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
actorScriptHash, ownerScriptHash := initActorOwnerScriptHashes(t, actorPK, ownerPK)
|
||||
|
||||
const actorDomain = "actor" + testDomainName
|
||||
|
||||
frostfsIDSubjectReader := &frostfsidStub{
|
||||
subjects: map[util.Uint160]*client.Subject{
|
||||
actorScriptHash: {
|
||||
Namespace: actorDomain,
|
||||
Name: actorDomain,
|
||||
},
|
||||
ownerScriptHash: {
|
||||
Namespace: testDomainName,
|
||||
Name: testDomainName,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
apeSrv := NewAPEServer(router, contRdr, ir, nm, frostfsIDSubjectReader, srv)
|
||||
|
||||
nm.currentEpoch = 100
|
||||
nm.netmaps = map[uint64]*netmap.NetMap{}
|
||||
|
||||
_, _, err = router.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(""), &chain.Chain{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.AccessDenied,
|
||||
Actions: chain.Actions{
|
||||
Names: []string{
|
||||
nativeschema.MethodListContainers,
|
||||
},
|
||||
},
|
||||
Resources: chain.Resources{
|
||||
Names: []string{
|
||||
nativeschema.ResourceFormatRootContainers,
|
||||
},
|
||||
},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Object: chain.ObjectRequest,
|
||||
Key: nativeschema.PropertyKeyActorPublicKey,
|
||||
Value: actorPK.PublicKey().String(),
|
||||
Op: chain.CondStringEquals,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req := initListRequest(t, actorPK, ownerPK)
|
||||
|
||||
resp, err := apeSrv.List(context.Background(), req)
|
||||
require.Nil(t, resp)
|
||||
require.ErrorContains(t, err, "actor namespace "+actorDomain+" differs")
|
||||
}
|
||||
|
||||
type srvStub struct {
|
||||
calls map[string]int
|
||||
}
|
||||
|
@ -489,3 +706,424 @@ func (s *netmapStub) GetNetMapByEpoch(epoch uint64) (*netmap.NetMap, error) {
|
|||
func (s *netmapStub) Epoch() (uint64, error) {
|
||||
return s.currentEpoch, nil
|
||||
}
|
||||
|
||||
type frostfsidStub struct {
|
||||
subjects map[util.Uint160]*client.Subject
|
||||
}
|
||||
|
||||
func (f *frostfsidStub) GetSubject(owner util.Uint160) (*client.Subject, error) {
|
||||
s, ok := f.subjects[owner]
|
||||
if !ok {
|
||||
return nil, errSubjectNotFound
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type testAPEServer struct {
|
||||
engine engine.Engine
|
||||
|
||||
containerReader *containerStub
|
||||
|
||||
ir *irStub
|
||||
|
||||
netmap *netmapStub
|
||||
|
||||
frostfsIDSubjectReader *frostfsidStub
|
||||
|
||||
apeChecker *apeChecker
|
||||
}
|
||||
|
||||
func newTestAPEServer() testAPEServer {
|
||||
srv := &srvStub{
|
||||
calls: map[string]int{},
|
||||
}
|
||||
|
||||
engine := inmemory.NewInMemory()
|
||||
|
||||
containerReader := &containerStub{
|
||||
c: map[cid.ID]*containercore.Container{},
|
||||
}
|
||||
|
||||
ir := &irStub{
|
||||
keys: [][]byte{},
|
||||
}
|
||||
|
||||
netmap := &netmapStub{}
|
||||
|
||||
frostfsIDSubjectReader := &frostfsidStub{
|
||||
subjects: map[util.Uint160]*client.Subject{},
|
||||
}
|
||||
|
||||
apeChecker := &apeChecker{
|
||||
router: engine,
|
||||
reader: containerReader,
|
||||
ir: ir,
|
||||
nm: netmap,
|
||||
frostFSIDClient: frostfsIDSubjectReader,
|
||||
next: srv,
|
||||
}
|
||||
|
||||
return testAPEServer{
|
||||
engine: engine,
|
||||
containerReader: containerReader,
|
||||
ir: ir,
|
||||
netmap: netmap,
|
||||
frostfsIDSubjectReader: frostfsIDSubjectReader,
|
||||
apeChecker: apeChecker,
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateContainerBoundedOperation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("check root-defined container in root-defined container target rule", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
components := newTestAPEServer()
|
||||
contID, testContainer := initTestContainer(t, false)
|
||||
components.containerReader.c[contID] = &containercore.Container{Value: testContainer}
|
||||
initTestNetmap(components.netmap)
|
||||
|
||||
_, _, err := components.engine.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(contID.EncodeToString()), &chain.Chain{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.AccessDenied,
|
||||
Actions: chain.Actions{
|
||||
Names: []string{
|
||||
nativeschema.MethodGetContainer,
|
||||
},
|
||||
},
|
||||
Resources: chain.Resources{
|
||||
Names: []string{
|
||||
fmt.Sprintf(nativeschema.ResourceFormatRootContainer, contID.EncodeToString()),
|
||||
},
|
||||
},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Object: chain.ObjectRequest,
|
||||
Key: nativeschema.PropertyKeyActorRole,
|
||||
Value: nativeschema.PropertyValueContainerRoleOthers,
|
||||
Op: chain.CondStringEquals,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req := initTestGetContainerRequest(t, contID)
|
||||
|
||||
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
|
||||
aErr := apeErr(nativeschema.MethodGetContainer, chain.AccessDenied)
|
||||
require.ErrorContains(t, err, aErr.Error())
|
||||
})
|
||||
|
||||
t.Run("check root-defined container in testdomain-defined container target rule", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
components := newTestAPEServer()
|
||||
contID, testContainer := initTestContainer(t, false)
|
||||
components.containerReader.c[contID] = &containercore.Container{Value: testContainer}
|
||||
initTestNetmap(components.netmap)
|
||||
|
||||
_, _, err := components.engine.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(contID.EncodeToString()), &chain.Chain{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.AccessDenied,
|
||||
Actions: chain.Actions{
|
||||
Names: []string{
|
||||
nativeschema.MethodGetContainer,
|
||||
},
|
||||
},
|
||||
Resources: chain.Resources{
|
||||
Names: []string{
|
||||
fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainer, testDomainName, contID.EncodeToString()),
|
||||
},
|
||||
},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Object: chain.ObjectRequest,
|
||||
Key: nativeschema.PropertyKeyActorRole,
|
||||
Value: nativeschema.PropertyValueContainerRoleOthers,
|
||||
Op: chain.CondStringEquals,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req := initTestGetContainerRequest(t, contID)
|
||||
|
||||
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("check root-defined container in testdomain namespace target rule", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
components := newTestAPEServer()
|
||||
contID, testContainer := initTestContainer(t, false)
|
||||
components.containerReader.c[contID] = &containercore.Container{Value: testContainer}
|
||||
initTestNetmap(components.netmap)
|
||||
|
||||
_, _, err := components.engine.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(testDomainName), &chain.Chain{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.AccessDenied,
|
||||
Actions: chain.Actions{
|
||||
Names: []string{
|
||||
nativeschema.MethodGetContainer,
|
||||
},
|
||||
},
|
||||
Resources: chain.Resources{
|
||||
Names: []string{
|
||||
fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainers, testDomainName),
|
||||
},
|
||||
},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Object: chain.ObjectRequest,
|
||||
Key: nativeschema.PropertyKeyActorRole,
|
||||
Value: nativeschema.PropertyValueContainerRoleOthers,
|
||||
Op: chain.CondStringEquals,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req := initTestGetContainerRequest(t, contID)
|
||||
|
||||
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("check testdomain-defined container in root-defined container target rule", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
components := newTestAPEServer()
|
||||
contID, testContainer := initTestContainer(t, true)
|
||||
components.containerReader.c[contID] = &containercore.Container{Value: testContainer}
|
||||
initTestNetmap(components.netmap)
|
||||
|
||||
_, _, err := components.engine.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(contID.EncodeToString()), &chain.Chain{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.AccessDenied,
|
||||
Actions: chain.Actions{
|
||||
Names: []string{
|
||||
nativeschema.MethodGetContainer,
|
||||
},
|
||||
},
|
||||
Resources: chain.Resources{
|
||||
Names: []string{
|
||||
fmt.Sprintf(nativeschema.ResourceFormatRootContainer, contID.EncodeToString()),
|
||||
},
|
||||
},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Object: chain.ObjectRequest,
|
||||
Key: nativeschema.PropertyKeyActorRole,
|
||||
Value: nativeschema.PropertyValueContainerRoleOthers,
|
||||
Op: chain.CondStringEquals,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req := initTestGetContainerRequest(t, contID)
|
||||
|
||||
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("check testdomain-defined container in testdomain-defined container target rule", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
components := newTestAPEServer()
|
||||
contID, testContainer := initTestContainer(t, true)
|
||||
components.containerReader.c[contID] = &containercore.Container{Value: testContainer}
|
||||
initTestNetmap(components.netmap)
|
||||
|
||||
_, _, err := components.engine.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(contID.EncodeToString()), &chain.Chain{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.AccessDenied,
|
||||
Actions: chain.Actions{
|
||||
Names: []string{
|
||||
nativeschema.MethodGetContainer,
|
||||
},
|
||||
},
|
||||
Resources: chain.Resources{
|
||||
Names: []string{
|
||||
fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainer, testDomainName, contID.EncodeToString()),
|
||||
},
|
||||
},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Object: chain.ObjectRequest,
|
||||
Key: nativeschema.PropertyKeyActorRole,
|
||||
Value: nativeschema.PropertyValueContainerRoleOthers,
|
||||
Op: chain.CondStringEquals,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req := initTestGetContainerRequest(t, contID)
|
||||
|
||||
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
|
||||
aErr := apeErr(nativeschema.MethodGetContainer, chain.AccessDenied)
|
||||
require.ErrorContains(t, err, aErr.Error())
|
||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
These 5 lines create dummy request and repeat in every test, can we move them to a function to make tests a bit more readable? These 5 lines create dummy request and repeat in every test, can we move them to a function to make tests a bit more readable?
fyrchik
commented
It seems private key is used only to sign the request, probably a good idea to return already signed request from this function. It seems private key is used only to sign the request, probably a good idea to return already signed request from this function.
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
These 5 lines create dummy request and repeat in every test, can we move them to a function to make tests a bit more readable? These 5 lines create dummy request and repeat in every test, can we move them to a function to make tests a bit more readable?
aarifullin marked this conversation as resolved
Outdated
fyrchik
commented
These 5 lines create dummy request and repeat in every test, can we move them to a function to make tests a bit more readable? These 5 lines create dummy request and repeat in every test, can we move them to a function to make tests a bit more readable?
aarifullin
commented
Have fixed for all cases :) Have fixed for all cases :)
fyrchik
commented
Hm, either I don't see it or you haven't pushed the update? Hm, either I don't see it or you haven't pushed the update?
aarifullin
commented
Sorry, I forgot to push yesterday. Now it is fine Sorry, I forgot to push yesterday. Now it is fine
|
||||
})
|
||||
|
||||
t.Run("check testdomain-defined container in testdomain namespace target rule", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
components := newTestAPEServer()
|
||||
contID, testContainer := initTestContainer(t, true)
|
||||
components.containerReader.c[contID] = &containercore.Container{Value: testContainer}
|
||||
initTestNetmap(components.netmap)
|
||||
|
||||
_, _, err := components.engine.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(testDomainName), &chain.Chain{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.AccessDenied,
|
||||
Actions: chain.Actions{
|
||||
Names: []string{
|
||||
nativeschema.MethodGetContainer,
|
||||
},
|
||||
},
|
||||
Resources: chain.Resources{
|
||||
Names: []string{
|
||||
fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainers, testDomainName),
|
||||
},
|
||||
},
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Object: chain.ObjectRequest,
|
||||
Key: nativeschema.PropertyKeyActorRole,
|
||||
Value: nativeschema.PropertyValueContainerRoleOthers,
|
||||
Op: chain.CondStringEquals,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req := initTestGetContainerRequest(t, contID)
|
||||
|
||||
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
|
||||
aErr := apeErr(nativeschema.MethodGetContainer, chain.AccessDenied)
|
||||
require.ErrorContains(t, err, aErr.Error())
|
||||
})
|
||||
}
|
||||
|
||||
func initTestGetContainerRequest(t *testing.T, contID cid.ID) *container.GetRequest {
|
||||
req := &container.GetRequest{}
|
||||
req.SetBody(&container.GetRequestBody{})
|
||||
var refContID refs.ContainerID
|
||||
contID.WriteToV2(&refContID)
|
||||
req.GetBody().SetContainerID(&refContID)
|
||||
|
||||
pk, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, signature.SignServiceMessage(&pk.PrivateKey, req))
|
||||
return req
|
||||
}
|
||||
|
||||
func initTestNetmap(netmapStub *netmapStub) {
|
||||
netmapStub.currentEpoch = 100
|
||||
netmapStub.netmaps = map[uint64]*netmap.NetMap{}
|
||||
var testNetmap netmap.NetMap
|
||||
testNetmap.SetEpoch(netmapStub.currentEpoch)
|
||||
testNetmap.SetNodes([]netmap.NodeInfo{{}})
|
||||
netmapStub.netmaps[netmapStub.currentEpoch] = &testNetmap
|
||||
netmapStub.netmaps[netmapStub.currentEpoch-1] = &testNetmap
|
||||
}
|
||||
|
||||
func initTestContainer(t *testing.T, isDomainSet bool) (cid.ID, cnrSDK.Container) {
|
||||
contID := cidtest.ID()
|
||||
testContainer := containertest.Container()
|
||||
pp := netmap.PlacementPolicy{}
|
||||
require.NoError(t, pp.DecodeString("REP 1"))
|
||||
testContainer.SetPlacementPolicy(pp)
|
||||
if isDomainSet {
|
||||
// no domain defined -> container is defined in root namespace
|
||||
var domain cnrSDK.Domain
|
||||
domain.SetName(testDomainName)
|
||||
domain.SetZone(testDomainZone)
|
||||
cnrSDK.WriteDomain(&testContainer, domain)
|
||||
}
|
||||
return contID, testContainer
|
||||
}
|
||||
|
||||
func initPutRequest(t *testing.T, testContainer cnrSDK.Container) *container.PutRequest {
|
||||
req := &container.PutRequest{}
|
||||
req.SetBody(&container.PutRequestBody{})
|
||||
var reqCont container.Container
|
||||
testContainer.WriteToV2(&reqCont)
|
||||
req.GetBody().SetContainer(&reqCont)
|
||||
|
||||
sessionPK, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
sToken := sessiontest.ContainerSigned()
|
||||
sToken.ApplyOnlyTo(cid.ID{})
|
||||
require.NoError(t, sToken.Sign(sessionPK.PrivateKey))
|
||||
var sTokenV2 session.Token
|
||||
sToken.WriteToV2(&sTokenV2)
|
||||
metaHeader := new(session.RequestMetaHeader)
|
||||
metaHeader.SetSessionToken(&sTokenV2)
|
||||
req.SetMetaHeader(metaHeader)
|
||||
|
||||
pk, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, signature.SignServiceMessage(&pk.PrivateKey, req))
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
func initOwnerIDScriptHash(t *testing.T, testContainer cnrSDK.Container) util.Uint160 {
|
||||
var ownerSDK *user.ID
|
||||
owner := testContainer.Owner()
|
||||
ownerSDK = &owner
|
||||
sc, err := ownerSDK.ScriptHash()
|
||||
require.NoError(t, err)
|
||||
return sc
|
||||
}
|
||||
|
||||
func initActorOwnerScriptHashes(t *testing.T, actorPK *keys.PrivateKey, ownerPK *keys.PrivateKey) (actorScriptHash util.Uint160, ownerScriptHash util.Uint160) {
|
||||
var actorUserID user.ID
|
||||
user.IDFromKey(&actorUserID, ecdsa.PublicKey(*actorPK.PublicKey()))
|
||||
var err error
|
||||
actorScriptHash, err = actorUserID.ScriptHash()
|
||||
require.NoError(t, err)
|
||||
|
||||
var ownerUserID user.ID
|
||||
user.IDFromKey(&ownerUserID, ecdsa.PublicKey(*ownerPK.PublicKey()))
|
||||
ownerScriptHash, err = ownerUserID.ScriptHash()
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, ownerScriptHash.String(), actorScriptHash.String())
|
||||
return
|
||||
}
|
||||
|
||||
func initListRequest(t *testing.T, actorPK *keys.PrivateKey, ownerPK *keys.PrivateKey) *container.ListRequest {
|
||||
var ownerUserID user.ID
|
||||
user.IDFromKey(&ownerUserID, ownerPK.PrivateKey.PublicKey)
|
||||
|
||||
req := &container.ListRequest{}
|
||||
req.SetBody(&container.ListRequestBody{})
|
||||
var ownerID refs.OwnerID
|
||||
ownerUserID.WriteToV2(&ownerID)
|
||||
req.GetBody().SetOwnerID(&ownerID)
|
||||
|
||||
require.NoError(t, signature.SignServiceMessage(&actorPK.PrivateKey, req))
|
||||
return req
|
||||
}
|
||||
|
|
|
@ -17,12 +17,7 @@ func apeTarget(chainTarget *control.ChainTarget) (engine.Target, error) {
|
|||
case control.ChainTarget_CONTAINER:
|
||||
return engine.ContainerTarget(chainTarget.GetName()), nil
|
||||
case control.ChainTarget_NAMESPACE:
|
||||
namespace := chainTarget.GetName()
|
||||
// If namespace is empty, we take it for root namespace.
|
||||
if namespace == "" {
|
||||
namespace = "root"
|
||||
}
|
||||
return engine.NamespaceTarget(namespace), nil
|
||||
return engine.NamespaceTarget(chainTarget.GetName()), nil
|
||||
default:
|
||||
}
|
||||
return engine.Target{}, status.Error(codes.InvalidArgument,
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
containerV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
||||
|
@ -14,6 +14,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||
cnrSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
|
@ -744,7 +745,11 @@ func (b Service) findRequestInfo(req MetaWithToken, idCnr cid.ID, op acl.Op) (in
|
|||
info.operation = op
|
||||
info.cnrOwner = cnr.Value.Owner()
|
||||
info.idCnr = idCnr
|
||||
info.cnrNamespace = cnr.Value.Attribute(containerV2.SysAttributeZone)
|
||||
|
||||
cnrNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(cnr.Value).Zone(), ".ns")
|
||||
if hasNamespace {
|
||||
info.cnrNamespace = cnrNamespace
|
||||
}
|
||||
|
||||
// it is assumed that at the moment the key will be valid,
|
||||
// otherwise the request would not pass validation
|
||||
|
|
Please, write a comment similar to other functions.
I wonder why do we need this section -- it has been here since before-NNS times, now everything can be fetched.