[#1096] object: Make ape middleware fill request with user claim tags

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
This commit is contained in:
Airat Arifullin 2024-04-15 16:51:19 +03:00
parent 6772976657
commit c21d72ac23
7 changed files with 105 additions and 10 deletions

View file

@ -46,6 +46,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/metrics"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
containerClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
frostfsidClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid"
nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
netmap2 "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/netmap"
@ -415,6 +416,8 @@ type shared struct {
cnrClient *containerClient.Client
frostfsidClient *frostfsidClient.Client
respSvc *response.Service
replicator *replicator.Replicator

View file

@ -35,12 +35,13 @@ func initContainerService(_ context.Context, c *cfg) {
frostFSIDClient, err := frostfsid.NewFromMorph(c.cfgMorph.client, c.cfgFrostfsID.scriptHash, 0)
fatalOnErr(err)
c.shared.frostfsidClient = frostFSIDClient
server := containerTransportGRPC.New(
containerService.NewSignService(
&c.key.PrivateKey,
containerService.NewAPEServer(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine, cnrRdr,
newCachedIRFetcher(createInnerRingFetcher(c)), c.netMapSource, frostFSIDClient,
newCachedIRFetcher(createInnerRingFetcher(c)), c.netMapSource, c.shared.frostfsidClient,
containerService.NewExecutionService(containerMorph.NewExecutor(cnrRdr, cnrWrt), c.respSvc),
),
),

View file

@ -444,6 +444,7 @@ func createAPEService(c *cfg, splitSvc *objectService.TransportSplitter) *object
objectAPE.NewChecker(
c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.chainRouter,
objectAPE.NewStorageEngineHeaderProvider(c.cfgObject.cfgLocalStorage.localStorage),
c.shared.frostfsidClient,
),
splitSvc,
)

View file

@ -6,6 +6,7 @@ import (
"fmt"
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
@ -13,19 +14,28 @@ import (
policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
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"
)
type checkerImpl struct {
chainRouter policyengine.ChainRouter
headerProvider HeaderProvider
frostFSIDClient frostfsidSubjectProvider
}
func NewChecker(chainRouter policyengine.ChainRouter, headerProvider HeaderProvider) Checker {
type frostfsidSubjectProvider interface {
GetSubject(util.Uint160) (*client.Subject, error)
}
func NewChecker(chainRouter policyengine.ChainRouter, headerProvider HeaderProvider, frostFSIDClient frostfsidSubjectProvider) Checker {
return &checkerImpl{
chainRouter: chainRouter,
headerProvider: headerProvider,
frostFSIDClient: frostFSIDClient,
}
}

View file

@ -6,6 +6,7 @@ import (
"fmt"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
@ -17,6 +18,7 @@ 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"
)
@ -153,6 +155,41 @@ var (
senderKey = hex.EncodeToString(senderPrivateKey.PublicKey().Bytes())
)
type frostfsIDProviderMock struct {
m map[util.Uint160]*client.Subject
}
var _ frostfsidSubjectProvider = (*frostfsIDProviderMock)(nil)
func newFrostfsIDProviderMock(t *testing.T) *frostfsIDProviderMock {
return &frostfsIDProviderMock{
m: map[util.Uint160]*client.Subject{
scriptHashFromSenderKey(t, senderKey): {
Namespace: "testnamespace",
Name: "test",
KV: map[string]string{
"tag-attr1": "value1",
"tag-attr2": "value2",
},
},
},
}
}
func scriptHashFromSenderKey(t *testing.T, senderKey string) util.Uint160 {
pk, err := keys.NewPublicKeyFromString(senderKey)
require.NoError(t, err)
return pk.GetScriptHash()
}
func (f *frostfsIDProviderMock) GetSubject(key util.Uint160) (*client.Subject, error) {
v, ok := f.m[key]
if !ok {
return nil, fmt.Errorf("%s", subjectNotFoundErrorMessage)
}
return v, nil
}
func TestAPECheck(t *testing.T) {
for _, test := range []struct {
name string
@ -321,6 +358,7 @@ func TestAPECheck(t *testing.T) {
for _, method := range test.methods {
t.Run(method, func(t *testing.T) {
headerProvider := newHeaderProviderMock()
frostfsidProvider := newFrostfsIDProviderMock(t)
cnr := newContainerIDSDK(t, test.container)
obj := newObjectIDSDK(t, test.object)
@ -335,7 +373,7 @@ func TestAPECheck(t *testing.T) {
router := policyengine.NewDefaultChainRouterWithLocalOverrides(ms, ls)
checker := NewChecker(router, headerProvider)
checker := NewChecker(router, headerProvider, frostfsidProvider)
prm := Prm{
Method: method,

View file

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"strconv"
"strings"
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
aperequest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/request"
@ -12,7 +13,13 @@ import (
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
commonschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/common"
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
)
const (
subjectNotFoundErrorMessage = "subject not found"
)
var defaultRequest = aperequest.Request{}
@ -115,15 +122,45 @@ func (c *checkerImpl) newAPERequest(ctx context.Context, prm Prm) (aperequest.Re
}
}
reqProps := map[string]string{
nativeschema.PropertyKeyActorPublicKey: prm.SenderKey,
nativeschema.PropertyKeyActorRole: prm.Role,
}
var err error
reqProps, err = c.fillWithUserClaimTags(reqProps, prm)
if err != nil {
return defaultRequest, err
}
return aperequest.NewRequest(
prm.Method,
aperequest.NewResource(
resourceName(prm.Container, prm.Object, prm.Namespace),
objectProperties(prm.Container, prm.Object, prm.ContainerOwner, header),
),
map[string]string{
nativeschema.PropertyKeyActorPublicKey: prm.SenderKey,
nativeschema.PropertyKeyActorRole: prm.Role,
},
reqProps,
), nil
}
// fillWithUserClaimTags fills ape request properties with user claim tags getting them from frostfsid contract by actor public key.
func (c *checkerImpl) fillWithUserClaimTags(reqProps map[string]string, prm Prm) (map[string]string, error) {
if reqProps == nil {
reqProps = make(map[string]string)
}
pk, err := keys.NewPublicKeyFromString(prm.SenderKey)
if err != nil {
return nil, err
}
subj, err := c.frostFSIDClient.GetSubject(pk.GetScriptHash())
if err != nil {
if !strings.Contains(err.Error(), subjectNotFoundErrorMessage) {
return nil, fmt.Errorf("get subject error: %w", err)
}
return reqProps, nil
}
for k, v := range subj.KV {
properyKey := fmt.Sprintf(commonschema.PropertyKeyFormatFrostFSIDUserClaim, k)
reqProps[properyKey] = v
}
return reqProps, nil
}

View file

@ -11,6 +11,7 @@ import (
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
commonschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/common"
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"github.com/stretchr/testify/require"
)
@ -234,6 +235,7 @@ func TestNewAPERequest(t *testing.T) {
}
headerSource := newHeaderProviderMock()
ffidProvider := newFrostfsIDProviderMock(t)
var headerObjSDK *objectSDK.Object
if test.header.headerObjSDK != nil {
@ -247,7 +249,8 @@ func TestNewAPERequest(t *testing.T) {
}
c := checkerImpl{
headerProvider: headerSource,
headerProvider: headerSource,
frostFSIDClient: ffidProvider,
}
r, err := c.newAPERequest(context.TODO(), prm)
@ -268,8 +271,10 @@ func TestNewAPERequest(t *testing.T) {
return prm.Header
}())),
map[string]string{
nativeschema.PropertyKeyActorPublicKey: prm.SenderKey,
nativeschema.PropertyKeyActorRole: prm.Role,
nativeschema.PropertyKeyActorPublicKey: prm.SenderKey,
nativeschema.PropertyKeyActorRole: prm.Role,
fmt.Sprintf(commonschema.PropertyKeyFormatFrostFSIDUserClaim, "tag-attr1"): "value1",
fmt.Sprintf(commonschema.PropertyKeyFormatFrostFSIDUserClaim, "tag-attr2"): "value2",
},
)