2023-12-27 14:18:15 +00:00
|
|
|
package ape
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
2024-04-15 13:51:19 +00:00
|
|
|
"strings"
|
2023-12-27 14:18:15 +00:00
|
|
|
|
|
|
|
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
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
|
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
|
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
2024-03-15 12:16:52 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
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"
|
2024-04-15 13:51:19 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
subjectNotFoundErrorMessage = "subject not found"
|
2023-12-27 14:18:15 +00:00
|
|
|
)
|
|
|
|
|
2024-04-11 09:35:49 +00:00
|
|
|
var defaultRequest = aperequest.Request{}
|
2023-12-27 14:18:15 +00:00
|
|
|
|
|
|
|
func nativeSchemaRole(role acl.Role) string {
|
|
|
|
switch role {
|
|
|
|
case acl.RoleOwner:
|
|
|
|
return nativeschema.PropertyValueContainerRoleOwner
|
|
|
|
case acl.RoleContainer:
|
|
|
|
return nativeschema.PropertyValueContainerRoleContainer
|
|
|
|
case acl.RoleInnerRing:
|
|
|
|
return nativeschema.PropertyValueContainerRoleIR
|
|
|
|
case acl.RoleOthers:
|
|
|
|
return nativeschema.PropertyValueContainerRoleOthers
|
|
|
|
default:
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceName(cid cid.ID, oid *oid.ID, namespace string) string {
|
|
|
|
if namespace == "root" || namespace == "" {
|
|
|
|
if oid != nil {
|
|
|
|
return fmt.Sprintf(nativeschema.ResourceFormatRootContainerObject, cid.EncodeToString(), oid.EncodeToString())
|
|
|
|
}
|
|
|
|
return fmt.Sprintf(nativeschema.ResourceFormatRootContainerObjects, cid.EncodeToString())
|
|
|
|
}
|
|
|
|
if oid != nil {
|
|
|
|
return fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainerObject, namespace, cid.EncodeToString(), oid.EncodeToString())
|
|
|
|
}
|
|
|
|
return fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainerObjects, namespace, cid.EncodeToString())
|
|
|
|
}
|
|
|
|
|
|
|
|
// objectProperties collects object properties from address parameters and a header if it is passed.
|
2024-03-15 12:16:52 +00:00
|
|
|
func objectProperties(cnr cid.ID, oid *oid.ID, cnrOwner user.ID, header *objectV2.Header) map[string]string {
|
2023-12-27 14:18:15 +00:00
|
|
|
objectProps := map[string]string{
|
|
|
|
nativeschema.PropertyKeyObjectContainerID: cnr.EncodeToString(),
|
|
|
|
}
|
|
|
|
|
2024-03-15 12:16:52 +00:00
|
|
|
objectProps[nativeschema.PropertyKeyContainerOwnerID] = cnrOwner.EncodeToString()
|
|
|
|
|
2023-12-27 14:18:15 +00:00
|
|
|
if oid != nil {
|
|
|
|
objectProps[nativeschema.PropertyKeyObjectID] = oid.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
if header == nil {
|
|
|
|
return objectProps
|
|
|
|
}
|
|
|
|
|
|
|
|
objV2 := new(objectV2.Object)
|
|
|
|
objV2.SetHeader(header)
|
|
|
|
objSDK := objectSDK.NewFromV2(objV2)
|
|
|
|
|
|
|
|
objectProps[nativeschema.PropertyKeyObjectVersion] = objSDK.Version().String()
|
|
|
|
objectProps[nativeschema.PropertyKeyObjectOwnerID] = objSDK.OwnerID().EncodeToString()
|
|
|
|
objectProps[nativeschema.PropertyKeyObjectCreationEpoch] = strconv.Itoa(int(objSDK.CreationEpoch()))
|
|
|
|
objectProps[nativeschema.PropertyKeyObjectPayloadLength] = strconv.Itoa(int(objSDK.PayloadSize()))
|
|
|
|
objectProps[nativeschema.PropertyKeyObjectType] = objSDK.Type().String()
|
|
|
|
|
|
|
|
pcs, isSet := objSDK.PayloadChecksum()
|
|
|
|
if isSet {
|
|
|
|
objectProps[nativeschema.PropertyKeyObjectPayloadHash] = pcs.String()
|
|
|
|
}
|
|
|
|
hcs, isSet := objSDK.PayloadHomomorphicHash()
|
|
|
|
if isSet {
|
|
|
|
objectProps[nativeschema.PropertyKeyObjectHomomorphicHash] = hcs.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, attr := range header.GetAttributes() {
|
|
|
|
objectProps[attr.GetKey()] = attr.GetValue()
|
|
|
|
}
|
|
|
|
|
|
|
|
return objectProps
|
|
|
|
}
|
|
|
|
|
|
|
|
// newAPERequest creates an APE request to be passed to a chain router. It collects resource properties from
|
|
|
|
// header provided by headerProvider. If it cannot be found in headerProvider, then properties are
|
|
|
|
// initialized from header given in prm (if it is set). Otherwise, just CID and OID are set to properties.
|
2024-04-11 09:35:49 +00:00
|
|
|
func (c *checkerImpl) newAPERequest(ctx context.Context, prm Prm) (aperequest.Request, error) {
|
2023-12-27 14:18:15 +00:00
|
|
|
switch prm.Method {
|
|
|
|
case nativeschema.MethodGetObject,
|
|
|
|
nativeschema.MethodHeadObject,
|
|
|
|
nativeschema.MethodRangeObject,
|
|
|
|
nativeschema.MethodHashObject,
|
|
|
|
nativeschema.MethodDeleteObject:
|
|
|
|
if prm.Object == nil {
|
2024-04-04 12:42:52 +00:00
|
|
|
return defaultRequest, fmt.Errorf("method %s: %w", prm.Method, errMissingOID)
|
2023-12-27 14:18:15 +00:00
|
|
|
}
|
|
|
|
case nativeschema.MethodSearchObject, nativeschema.MethodPutObject:
|
|
|
|
default:
|
2024-04-04 12:42:52 +00:00
|
|
|
return defaultRequest, fmt.Errorf("unknown method: %s", prm.Method)
|
2023-12-27 14:18:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var header *objectV2.Header
|
|
|
|
if prm.Header != nil {
|
|
|
|
header = prm.Header
|
2024-04-04 11:55:38 +00:00
|
|
|
} else if prm.Object != nil && !prm.WithoutHeaderRequest {
|
2023-12-27 14:18:15 +00:00
|
|
|
headerObjSDK, err := c.headerProvider.GetHeader(ctx, prm.Container, *prm.Object)
|
|
|
|
if err == nil {
|
|
|
|
header = headerObjSDK.ToV2().GetHeader()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-15 13:51:19 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-04-11 09:35:49 +00:00
|
|
|
return aperequest.NewRequest(
|
|
|
|
prm.Method,
|
|
|
|
aperequest.NewResource(
|
|
|
|
resourceName(prm.Container, prm.Object, prm.Namespace),
|
|
|
|
objectProperties(prm.Container, prm.Object, prm.ContainerOwner, header),
|
|
|
|
),
|
2024-04-15 13:51:19 +00:00
|
|
|
reqProps,
|
2024-04-11 09:35:49 +00:00
|
|
|
), nil
|
2023-12-27 14:18:15 +00:00
|
|
|
}
|
2024-04-15 13:51:19 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|