2023-12-27 14:18:15 +00:00
|
|
|
package ape
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
2024-04-11 09:35:49 +00:00
|
|
|
aperequest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/request"
|
2023-12-27 14:18:15 +00:00
|
|
|
checksumtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum/test"
|
|
|
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
2024-03-15 12:16:52 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
2023-12-27 14:18:15 +00:00
|
|
|
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
2024-04-15 13:51:19 +00:00
|
|
|
commonschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/common"
|
2023-12-27 14:18:15 +00:00
|
|
|
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2024-03-15 12:16:52 +00:00
|
|
|
const (
|
|
|
|
testOwnerID = "FPPtmAi9TCX329"
|
|
|
|
)
|
|
|
|
|
2023-12-27 14:18:15 +00:00
|
|
|
func TestObjectProperties(t *testing.T) {
|
|
|
|
for _, test := range []struct {
|
|
|
|
name string
|
|
|
|
container string
|
|
|
|
object *string
|
|
|
|
header *headerObjectSDKParams
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "fully filled header",
|
|
|
|
container: containerID,
|
|
|
|
object: stringPtr(objectID),
|
|
|
|
header: &headerObjectSDKParams{
|
|
|
|
majorVersion: 1,
|
|
|
|
minorVersion: 1,
|
|
|
|
owner: usertest.ID(),
|
|
|
|
epoch: 3,
|
|
|
|
payloadSize: 1000,
|
|
|
|
typ: objectSDK.TypeRegular,
|
|
|
|
payloadChecksum: checksumtest.Checksum(),
|
|
|
|
payloadHomomorphicHash: checksumtest.Checksum(),
|
|
|
|
attributes: []struct {
|
|
|
|
key string
|
|
|
|
val string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
key: "attr1",
|
|
|
|
val: "val1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: "attr2",
|
|
|
|
val: "val2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "partially filled header",
|
|
|
|
container: containerID,
|
|
|
|
header: &headerObjectSDKParams{
|
|
|
|
majorVersion: 1,
|
|
|
|
minorVersion: 1,
|
|
|
|
owner: usertest.ID(),
|
|
|
|
epoch: 3,
|
|
|
|
attributes: []struct {
|
|
|
|
key string
|
|
|
|
val string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
key: "attr1",
|
|
|
|
val: "val1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "only address paramaters set in header",
|
|
|
|
container: containerID,
|
|
|
|
object: stringPtr(objectID),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "only container set in header",
|
|
|
|
container: containerID,
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
cnr := newContainerIDSDK(t, test.container)
|
|
|
|
obj := newObjectIDSDK(t, test.object)
|
2024-03-01 08:58:17 +00:00
|
|
|
header := newHeaderObjectSDK(cnr, obj, test.header)
|
2023-12-27 14:18:15 +00:00
|
|
|
|
2024-03-15 12:16:52 +00:00
|
|
|
var testCnrOwner user.ID
|
|
|
|
require.NoError(t, testCnrOwner.DecodeString(testOwnerID))
|
|
|
|
|
|
|
|
props := objectProperties(cnr, obj, testCnrOwner, header.ToV2().GetHeader())
|
2023-12-27 14:18:15 +00:00
|
|
|
require.Equal(t, test.container, props[nativeschema.PropertyKeyObjectContainerID])
|
2024-03-15 12:16:52 +00:00
|
|
|
require.Equal(t, testOwnerID, props[nativeschema.PropertyKeyContainerOwnerID])
|
2023-12-27 14:18:15 +00:00
|
|
|
|
|
|
|
if obj != nil {
|
|
|
|
require.Equal(t, *test.object, props[nativeschema.PropertyKeyObjectID])
|
|
|
|
}
|
|
|
|
|
|
|
|
if test.header != nil {
|
|
|
|
require.Equal(t,
|
|
|
|
fmt.Sprintf("v%d.%d", test.header.majorVersion, test.header.minorVersion),
|
|
|
|
props[nativeschema.PropertyKeyObjectVersion],
|
|
|
|
)
|
|
|
|
require.Equal(t, test.header.owner.EncodeToString(), props[nativeschema.PropertyKeyObjectOwnerID])
|
|
|
|
require.Equal(t, fmt.Sprintf("%d", test.header.epoch), props[nativeschema.PropertyKeyObjectCreationEpoch])
|
|
|
|
require.Equal(t, fmt.Sprintf("%d", test.header.payloadSize), props[nativeschema.PropertyKeyObjectPayloadLength])
|
|
|
|
require.Equal(t, test.header.typ.String(), props[nativeschema.PropertyKeyObjectType])
|
|
|
|
require.Equal(t, test.header.payloadChecksum.String(), props[nativeschema.PropertyKeyObjectPayloadHash])
|
|
|
|
require.Equal(t, test.header.payloadHomomorphicHash.String(), props[nativeschema.PropertyKeyObjectHomomorphicHash])
|
|
|
|
|
|
|
|
for _, attr := range test.header.attributes {
|
|
|
|
require.Equal(t, attr.val, props[attr.key])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNewAPERequest(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
methods []string
|
|
|
|
namespace string
|
|
|
|
container string
|
|
|
|
object *string
|
|
|
|
header testHeader
|
|
|
|
expectErr error
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "oid required requests",
|
|
|
|
methods: methodsRequiredOID,
|
|
|
|
namespace: namespace,
|
|
|
|
container: containerID,
|
|
|
|
object: stringPtr(objectID),
|
|
|
|
header: testHeader{
|
|
|
|
headerObjSDK: &headerObjectSDKParams{
|
|
|
|
majorVersion: 1,
|
|
|
|
minorVersion: 1,
|
|
|
|
owner: usertest.ID(),
|
|
|
|
epoch: 3,
|
|
|
|
payloadSize: 1000,
|
|
|
|
typ: objectSDK.TypeRegular,
|
|
|
|
payloadChecksum: checksumtest.Checksum(),
|
|
|
|
payloadHomomorphicHash: checksumtest.Checksum(),
|
|
|
|
},
|
|
|
|
fromHeaderProvider: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "oid required requests but header cannot be found locally",
|
|
|
|
methods: methodsRequiredOID,
|
|
|
|
namespace: namespace,
|
|
|
|
container: containerID,
|
|
|
|
object: stringPtr(objectID),
|
|
|
|
header: testHeader{},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "oid required requests missed oid",
|
|
|
|
methods: methodsRequiredOID,
|
|
|
|
namespace: namespace,
|
|
|
|
container: containerID,
|
|
|
|
object: nil,
|
|
|
|
header: testHeader{},
|
|
|
|
expectErr: errMissingOID,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "response for oid required requests",
|
|
|
|
methods: methodsRequiredOID,
|
|
|
|
namespace: namespace,
|
|
|
|
container: containerID,
|
|
|
|
object: stringPtr(objectID),
|
|
|
|
header: testHeader{
|
|
|
|
headerObjSDK: &headerObjectSDKParams{
|
|
|
|
majorVersion: 1,
|
|
|
|
minorVersion: 1,
|
|
|
|
owner: usertest.ID(),
|
|
|
|
epoch: 3,
|
|
|
|
payloadSize: 1000,
|
|
|
|
typ: objectSDK.TypeRegular,
|
|
|
|
payloadChecksum: checksumtest.Checksum(),
|
|
|
|
payloadHomomorphicHash: checksumtest.Checksum(),
|
|
|
|
},
|
|
|
|
fromRequestResponseHeader: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "oid not required methods request",
|
|
|
|
methods: methodsOptionalOID,
|
|
|
|
namespace: namespace,
|
|
|
|
container: containerID,
|
|
|
|
object: nil,
|
|
|
|
header: testHeader{
|
|
|
|
headerObjSDK: &headerObjectSDKParams{
|
|
|
|
majorVersion: 6,
|
|
|
|
minorVersion: 66,
|
|
|
|
owner: usertest.ID(),
|
|
|
|
epoch: 3,
|
|
|
|
typ: objectSDK.TypeLock,
|
|
|
|
},
|
|
|
|
fromRequestResponseHeader: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "oid not required methods request but no header",
|
|
|
|
methods: methodsOptionalOID,
|
|
|
|
namespace: namespace,
|
|
|
|
container: containerID,
|
|
|
|
object: nil,
|
|
|
|
header: testHeader{},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
for _, method := range test.methods {
|
|
|
|
t.Run(method, func(t *testing.T) {
|
|
|
|
cnr := newContainerIDSDK(t, test.container)
|
|
|
|
obj := newObjectIDSDK(t, test.object)
|
|
|
|
|
2024-03-15 12:16:52 +00:00
|
|
|
var testCnrOwner user.ID
|
|
|
|
require.NoError(t, testCnrOwner.DecodeString(testOwnerID))
|
|
|
|
|
2023-12-27 14:18:15 +00:00
|
|
|
prm := Prm{
|
2024-03-15 12:16:52 +00:00
|
|
|
Namespace: test.namespace,
|
|
|
|
Method: method,
|
|
|
|
Container: cnr,
|
|
|
|
Object: obj,
|
|
|
|
Role: role,
|
|
|
|
SenderKey: senderKey,
|
|
|
|
ContainerOwner: testCnrOwner,
|
2023-12-27 14:18:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
headerSource := newHeaderProviderMock()
|
2024-04-15 13:51:19 +00:00
|
|
|
ffidProvider := newFrostfsIDProviderMock(t)
|
2023-12-27 14:18:15 +00:00
|
|
|
|
|
|
|
var headerObjSDK *objectSDK.Object
|
|
|
|
if test.header.headerObjSDK != nil {
|
2024-03-01 08:58:17 +00:00
|
|
|
headerObjSDK = newHeaderObjectSDK(cnr, obj, test.header.headerObjSDK)
|
2023-12-27 14:18:15 +00:00
|
|
|
if test.header.fromHeaderProvider {
|
|
|
|
require.NotNil(t, obj, "oid is required if a header is expected to be found in header provider")
|
|
|
|
headerSource.addHeader(cnr, *obj, headerObjSDK)
|
|
|
|
} else if test.header.fromRequestResponseHeader {
|
|
|
|
prm.Header = headerObjSDK.ToV2().GetHeader()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c := checkerImpl{
|
2024-04-15 13:51:19 +00:00
|
|
|
headerProvider: headerSource,
|
|
|
|
frostFSIDClient: ffidProvider,
|
2023-12-27 14:18:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
r, err := c.newAPERequest(context.TODO(), prm)
|
|
|
|
if test.expectErr != nil {
|
|
|
|
require.Error(t, err)
|
|
|
|
require.ErrorIs(t, err, test.expectErr)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-11 09:35:49 +00:00
|
|
|
expectedRequest := aperequest.NewRequest(
|
|
|
|
method,
|
|
|
|
aperequest.NewResource(
|
|
|
|
resourceName(cnr, obj, prm.Namespace),
|
|
|
|
objectProperties(cnr, obj, testCnrOwner, func() *objectV2.Header {
|
2023-12-27 14:18:15 +00:00
|
|
|
if headerObjSDK != nil {
|
|
|
|
return headerObjSDK.ToV2().GetHeader()
|
|
|
|
}
|
|
|
|
return prm.Header
|
2024-04-11 09:35:49 +00:00
|
|
|
}())),
|
|
|
|
map[string]string{
|
2024-04-15 13:51:19 +00:00
|
|
|
nativeschema.PropertyKeyActorPublicKey: prm.SenderKey,
|
|
|
|
nativeschema.PropertyKeyActorRole: prm.Role,
|
|
|
|
fmt.Sprintf(commonschema.PropertyKeyFormatFrostFSIDUserClaim, "tag-attr1"): "value1",
|
|
|
|
fmt.Sprintf(commonschema.PropertyKeyFormatFrostFSIDUserClaim, "tag-attr2"): "value2",
|
2024-05-02 18:15:58 +00:00
|
|
|
commonschema.PropertyKeyFrostFSIDGroupID: "1",
|
2023-12-27 14:18:15 +00:00
|
|
|
},
|
2024-04-11 09:35:49 +00:00
|
|
|
)
|
2023-12-27 14:18:15 +00:00
|
|
|
|
2024-04-04 12:42:52 +00:00
|
|
|
require.Equal(t, expectedRequest, r)
|
2023-12-27 14:18:15 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceName(t *testing.T) {
|
|
|
|
for _, test := range []struct {
|
|
|
|
name string
|
|
|
|
namespace string
|
|
|
|
container string
|
|
|
|
object *string
|
|
|
|
expected string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "non-root namespace, CID",
|
|
|
|
namespace: namespace,
|
|
|
|
container: containerID,
|
|
|
|
expected: fmt.Sprintf("native:object/%s/%s/*", namespace, containerID),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "non-root namespace, CID, OID",
|
|
|
|
namespace: namespace,
|
|
|
|
container: containerID,
|
|
|
|
object: stringPtr(objectID),
|
|
|
|
expected: fmt.Sprintf("native:object/%s/%s/%s", namespace, containerID, objectID),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "empty namespace, CID",
|
|
|
|
namespace: "",
|
|
|
|
container: containerID,
|
|
|
|
expected: fmt.Sprintf("native:object//%s/*", containerID),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "empty namespace, CID, OID",
|
|
|
|
namespace: "",
|
|
|
|
container: containerID,
|
|
|
|
object: stringPtr(objectID),
|
|
|
|
expected: fmt.Sprintf("native:object//%s/%s", containerID, objectID),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "root namespace, CID",
|
|
|
|
namespace: "root",
|
|
|
|
container: containerID,
|
|
|
|
expected: fmt.Sprintf("native:object//%s/*", containerID),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "root namespace, CID, OID",
|
|
|
|
namespace: "root",
|
|
|
|
container: containerID,
|
|
|
|
object: stringPtr(objectID),
|
|
|
|
expected: fmt.Sprintf("native:object//%s/%s", containerID, objectID),
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
cnr := newContainerIDSDK(t, test.container)
|
|
|
|
obj := newObjectIDSDK(t, test.object)
|
|
|
|
require.Equal(t, test.expected, resourceName(cnr, obj, test.namespace))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|