package frostfs import ( "context" "errors" "strconv" "strings" "testing" objectv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) func TestCredsObject(t *testing.T) { ctx, bktName, payload, newPayload := context.Background(), "bucket", []byte("payload"), []byte("new-payload") key, err := keys.NewPrivateKey() require.NoError(t, err) var userID user.ID userID.SetScriptHash(key.PublicKey().GetScriptHash()) var token bearer.Token err = token.Sign(key.PrivateKey) require.NoError(t, err) ctx = middleware.SetBox(ctx, &middleware.Box{AccessBox: &accessbox.Box{ Gate: &accessbox.GateData{ BearerToken: &token, }, }}) frostfs := NewAuthmateFrostFS(layer.NewTestFrostFS(key), zaptest.NewLogger(t)) cnrID, err := frostfs.CreateContainer(ctx, authmate.PrmContainerCreate{ FriendlyName: bktName, Owner: userID, }) require.NoError(t, err) t.Run("regular access key", func(t *testing.T) { attr1 := object.NewAttribute() attr1.SetKey("attr1") attr1.SetValue("val1") prm := tokens.PrmObjectCreate{ Container: cnrID, Filepath: "regular-obj", ExpirationEpoch: 10, Payload: payload, CustomAttributes: []object.Attribute{*attr1}, } objID, err := frostfs.CreateObject(ctx, prm) require.NoError(t, err) accessKeyID := cnrID.EncodeToString() + "0" + objID.EncodeToString() obj, err := frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID}) require.NoError(t, err) assertParamsSet(t, prm, obj, userID) t.Run("update existing", func(t *testing.T) { attr2 := object.NewAttribute() attr2.SetKey("attr2") attr2.SetValue("val2") prmNew := tokens.PrmObjectCreate{ Container: cnrID, Filepath: "regular-obj-new", ExpirationEpoch: 11, Payload: newPayload, CustomAttributes: []object.Attribute{*attr2}, NewVersionForAccessKeyID: accessKeyID, } _, err = frostfs.CreateObject(ctx, prmNew) require.NoError(t, err) obj, err = frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID}) require.NoError(t, err) assertParamsSet(t, prmNew, obj, userID) }) t.Run("update not existing", func(t *testing.T) { attr2 := object.NewAttribute() attr2.SetKey("attr2") attr2.SetValue("val2") addr := oidtest.Address() accessKeyIDNotExisting := getAccessKeyID(addr) prmNew := tokens.PrmObjectCreate{ Container: cnrID, Filepath: "regular-obj-new", ExpirationEpoch: 11, Payload: newPayload, CustomAttributes: []object.Attribute{*attr2}, NewVersionForAccessKeyID: accessKeyIDNotExisting, } _, err = frostfs.CreateObject(ctx, prmNew) require.NoError(t, err) obj, err = frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyIDNotExisting}) require.NoError(t, err) assertParamsSet(t, prmNew, obj, userID) }) }) t.Run("custom access key", func(t *testing.T) { attr1 := object.NewAttribute() attr1.SetKey("attr1") attr1.SetValue("val1") accessKeyID := "custom-access-key-id" prm := tokens.PrmObjectCreate{ Container: cnrID, Filepath: "custom-obj", ExpirationEpoch: 10, Payload: payload, CustomAccessKey: accessKeyID, CustomAttributes: []object.Attribute{*attr1}, } _, err = frostfs.CreateObject(ctx, prm) require.NoError(t, err) obj, err := frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID}) require.NoError(t, err) assertParamsSet(t, prm, obj, userID) t.Run("update", func(t *testing.T) { attr2 := object.NewAttribute() attr2.SetKey("attr2") attr2.SetValue("val2") prmNew := tokens.PrmObjectCreate{ Container: cnrID, Filepath: "custom-obj-new", ExpirationEpoch: 11, Payload: newPayload, CustomAttributes: []object.Attribute{*attr2}, NewVersionForAccessKeyID: accessKeyID, } _, err = frostfs.CreateObject(ctx, prmNew) require.NoError(t, err) obj, err = frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID}) require.NoError(t, err) assertParamsSet(t, prmNew, obj, userID) }) t.Run("update not existing", func(t *testing.T) { accessKeyIDNotExisting := "unknown" prmNew := tokens.PrmObjectCreate{ Container: cnrID, Payload: newPayload, NewVersionForAccessKeyID: accessKeyIDNotExisting, } _, err = frostfs.CreateObject(ctx, prmNew) require.Error(t, err) }) }) t.Run("fallback", func(t *testing.T) { t.Run("regular", func(t *testing.T) { prm := tokens.PrmObjectCreate{ Container: cnrID, Filepath: "regular-obj", Payload: payload, } objID, err := frostfs.CreateObject(ctx, prm) require.NoError(t, err) accessKeyID := cnrID.EncodeToString() + "0" + objID.EncodeToString() prmNew := tokens.PrmObjectCreate{ Container: cnrID, Filepath: "regular-obj-new", Payload: newPayload, NewVersionForAccessKeyID: accessKeyID, } objIDNew, err := frostfs.CreateObject(ctx, prmNew) require.NoError(t, err) addr := newAddress(cnrID, objID) prmFallback := tokens.PrmGetCredsObject{ Container: cnrID, AccessKeyID: accessKeyID, FallbackAddress: &addr, } frostfs.frostFS.(*layer.TestFrostFS).SetObjectError(newAddress(cnrID, objIDNew), errors.New("error")) obj, err := frostfs.GetCredsObject(ctx, prmFallback) require.NoError(t, err) assertParamsSet(t, prm, obj, userID) }) t.Run("custom", func(t *testing.T) { prm := tokens.PrmObjectCreate{ Container: cnrID, Filepath: "custom-obj", ExpirationEpoch: 10, Payload: payload, CustomAccessKey: "custom-access-key-id", } objID, err := frostfs.CreateObject(ctx, prm) require.NoError(t, err) addr := newAddress(cnrID, objID) prmFallback := tokens.PrmGetCredsObject{ Container: cnrID, AccessKeyID: "unknown", FallbackAddress: &addr, } obj, err := frostfs.GetCredsObject(ctx, prmFallback) require.NoError(t, err) assertParamsSet(t, prm, obj, userID) }) }) } func newAddress(cnr cid.ID, obj oid.ID) oid.Address { var addr oid.Address addr.SetContainer(cnr) addr.SetObject(obj) return addr } func assertParamsSet(t *testing.T, prm tokens.PrmObjectCreate, obj *object.Object, userID user.ID) { require.Equal(t, prm.Payload, obj.Payload()) require.True(t, userID.Equals(obj.OwnerID()), "owners not matched") require.True(t, containerAttribute(obj.Attributes(), object.AttributeFilePath, prm.Filepath), "missing FilePath") require.True(t, containerAttribute(obj.Attributes(), objectv2.SysAttributeExpEpoch, strconv.FormatUint(prm.ExpirationEpoch, 10)), "missing expiration epoch") var crdtName string if prm.CustomAccessKey != "" { crdtName = prm.CustomAccessKey } else if prm.NewVersionForAccessKeyID != "" { crdtName = prm.NewVersionForAccessKeyID } if crdtName != "" { require.Truef(t, containerAttribute(obj.Attributes(), accessBoxCRDTNameAttr, crdtName), "wrong crdt name '%s'", crdtName) } for _, attr := range prm.CustomAttributes { require.True(t, containerAttribute(obj.Attributes(), attr.Key(), attr.Value()), "missing custom attribute") } } func containerAttribute(attrs []object.Attribute, key, val string) bool { for _, attr := range attrs { if attr.Key() == key && attr.Value() == val { return true } } return false } func getAccessKeyID(addr oid.Address) string { return strings.ReplaceAll(addr.EncodeToString(), "/", "0") }