2021-10-04 14:30:38 +00:00
|
|
|
package layer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-10-13 18:50:02 +00:00
|
|
|
"encoding/xml"
|
2022-04-22 07:18:21 +00:00
|
|
|
errorsStd "errors"
|
2022-02-28 08:02:05 +00:00
|
|
|
"fmt"
|
2022-03-04 13:07:27 +00:00
|
|
|
"strconv"
|
|
|
|
"time"
|
2021-10-04 14:30:38 +00:00
|
|
|
|
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/data"
|
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/errors"
|
2022-05-26 13:11:14 +00:00
|
|
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
2021-10-04 14:30:38 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
2022-02-28 13:39:04 +00:00
|
|
|
const (
|
2022-03-17 14:03:06 +00:00
|
|
|
AttributeComplianceMode = ".s3-compliance-mode"
|
|
|
|
AttributeExpirationEpoch = "__NEOFS__EXPIRATION_EPOCH"
|
2022-02-28 13:39:04 +00:00
|
|
|
)
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
func (n *layer) PutLockInfo(ctx context.Context, objVersion *ObjectVersion, newLock *data.ObjectLock) error {
|
|
|
|
cnrID := objVersion.BktInfo.CID
|
|
|
|
versionNode, err := n.getNodeVersion(ctx, objVersion)
|
2021-10-13 18:50:02 +00:00
|
|
|
if err != nil {
|
2022-05-26 13:11:14 +00:00
|
|
|
return err
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
lockInfo, err := n.treeService.GetLock(ctx, &cnrID, versionNode.ID)
|
|
|
|
if err != nil && !errorsStd.Is(err, ErrNodeNotFound) {
|
|
|
|
return err
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
if lockInfo == nil {
|
|
|
|
lockInfo = &data.LockInfo{}
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
if newLock.Retention != nil {
|
|
|
|
if lockInfo.RetentionOID != nil {
|
|
|
|
if lockInfo.IsCompliance {
|
|
|
|
return fmt.Errorf("you cannot change compliance mode")
|
|
|
|
}
|
|
|
|
if !newLock.Retention.ByPassedGovernance {
|
|
|
|
return fmt.Errorf("you cannot bypass governence mode")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(lockInfo.UntilDate) > 0 {
|
|
|
|
parsedTime, err := time.Parse(time.RFC3339, lockInfo.UntilDate)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("couldn't parse time '%s': %w", lockInfo.UntilDate, err)
|
|
|
|
}
|
|
|
|
if parsedTime.After(newLock.Retention.Until) {
|
|
|
|
return fmt.Errorf("you couldn't short the until date")
|
|
|
|
}
|
|
|
|
}
|
2022-05-18 07:48:30 +00:00
|
|
|
}
|
2022-05-26 13:11:14 +00:00
|
|
|
lock := &data.ObjectLock{Retention: newLock.Retention}
|
|
|
|
if lockInfo.RetentionOID, err = n.putLockObject(ctx, objVersion.BktInfo, versionNode.OID, lock); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
lockInfo.IsCompliance = newLock.Retention.IsCompliance
|
|
|
|
lockInfo.UntilDate = newLock.Retention.Until.UTC().Format(time.RFC3339)
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
if newLock.LegalHold != nil {
|
|
|
|
if newLock.LegalHold.Enabled && lockInfo.LegalHoldOID == nil {
|
|
|
|
lock := &data.ObjectLock{LegalHold: newLock.LegalHold}
|
|
|
|
if lockInfo.LegalHoldOID, err = n.putLockObject(ctx, objVersion.BktInfo, versionNode.OID, lock); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if !newLock.LegalHold.Enabled && lockInfo.LegalHoldOID != nil {
|
|
|
|
if err = n.objectDelete(ctx, objVersion.BktInfo, *lockInfo.LegalHoldOID); err != nil {
|
|
|
|
return fmt.Errorf("couldn't delete lock object '%s' to remove legal hold: %w", lockInfo.LegalHoldOID.EncodeToString(), err)
|
|
|
|
}
|
|
|
|
lockInfo.LegalHoldOID = nil
|
|
|
|
}
|
2022-05-17 14:56:05 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
if err = n.treeService.PutLock(ctx, &cnrID, versionNode.ID, lockInfo); err != nil {
|
|
|
|
return fmt.Errorf("couldn't put lock into tree: %w", err)
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
if err = n.systemCache.PutLockInfo(lockObjectKey(objVersion), lockInfo); err != nil {
|
|
|
|
n.log.Error("couldn't cache system object", zap.Error(err))
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
func (n *layer) putLockObject(ctx context.Context, bktInfo *data.BucketInfo, objID oid.ID, lock *data.ObjectLock) (*oid.ID, error) {
|
2022-06-06 11:09:09 +00:00
|
|
|
prm := PrmObjectCreate{
|
2022-05-26 13:11:14 +00:00
|
|
|
Container: bktInfo.CID,
|
|
|
|
Creator: bktInfo.Owner,
|
|
|
|
Locks: []oid.ID{objID},
|
2022-03-01 19:02:24 +00:00
|
|
|
}
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
var err error
|
|
|
|
prm.Attributes, err = n.attributesFromLock(ctx, lock)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-03-05 08:53:01 +00:00
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
id, _, err := n.objectPutAndHash(ctx, prm, bktInfo)
|
|
|
|
return id, err
|
|
|
|
}
|
2022-02-28 13:39:04 +00:00
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
func (n *layer) GetLockInfo(ctx context.Context, objVersion *ObjectVersion) (*data.LockInfo, error) {
|
|
|
|
if lockInfo := n.systemCache.GetLockInfo(lockObjectKey(objVersion)); lockInfo != nil {
|
|
|
|
return lockInfo, nil
|
2022-03-01 19:02:24 +00:00
|
|
|
}
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
versionNode, err := n.getNodeVersion(ctx, objVersion)
|
2021-10-04 14:30:38 +00:00
|
|
|
if err != nil {
|
2022-04-12 06:52:44 +00:00
|
|
|
return nil, err
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
lockInfo, err := n.treeService.GetLock(ctx, &objVersion.BktInfo.CID, versionNode.ID)
|
|
|
|
if err != nil && !errorsStd.Is(err, ErrNodeNotFound) {
|
|
|
|
return nil, err
|
2022-05-17 14:56:05 +00:00
|
|
|
}
|
2022-05-26 13:11:14 +00:00
|
|
|
if lockInfo == nil {
|
|
|
|
lockInfo = &data.LockInfo{}
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
if err = n.systemCache.PutLockInfo(lockObjectKey(objVersion), lockInfo); err != nil {
|
|
|
|
n.log.Error("couldn't cache system object", zap.Error(err))
|
2022-05-25 15:59:36 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
return lockInfo, nil
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
func (n *layer) DeleteSystemObject(ctx context.Context, bktInfo *data.BucketInfo, name string) error {
|
|
|
|
f := &findParams{
|
|
|
|
attr: [2]string{objectSystemAttributeName, name},
|
|
|
|
bkt: bktInfo,
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
2022-05-26 13:11:14 +00:00
|
|
|
ids, err := n.objectSearch(ctx, f)
|
2021-10-04 14:30:38 +00:00
|
|
|
if err != nil {
|
2022-05-26 13:11:14 +00:00
|
|
|
return err
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
n.systemCache.Delete(systemObjectKey(bktInfo, name))
|
|
|
|
for i := range ids {
|
|
|
|
if err = n.objectDelete(ctx, bktInfo, ids[i]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
2022-05-26 13:11:14 +00:00
|
|
|
|
|
|
|
return nil
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
2021-10-13 18:50:02 +00:00
|
|
|
func (n *layer) getCORS(ctx context.Context, bkt *data.BucketInfo, sysName string) (*data.CORSConfiguration, error) {
|
|
|
|
if cors := n.systemCache.GetCORS(systemObjectKey(bkt, sysName)); cors != nil {
|
|
|
|
return cors, nil
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
2022-05-17 13:52:51 +00:00
|
|
|
objID, err := n.treeService.GetBucketCORS(ctx, &bkt.CID)
|
2022-05-12 02:33:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-17 13:52:51 +00:00
|
|
|
|
|
|
|
if objID == nil {
|
2022-05-12 02:33:03 +00:00
|
|
|
return nil, errors.GetAPIError(errors.ErrNoSuchCORSConfiguration)
|
|
|
|
}
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2022-05-17 13:52:51 +00:00
|
|
|
obj, err := n.objectGet(ctx, bkt, *objID)
|
2021-10-04 14:30:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-10-13 18:50:02 +00:00
|
|
|
cors := &data.CORSConfiguration{}
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2021-10-13 18:50:02 +00:00
|
|
|
if err = xml.Unmarshal(obj.Payload(), &cors); err != nil {
|
2022-06-22 19:40:52 +00:00
|
|
|
return nil, fmt.Errorf("unmarshal cors: %w", err)
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
2021-10-13 18:50:02 +00:00
|
|
|
if err = n.systemCache.PutCORS(systemObjectKey(bkt, sysName), cors); err != nil {
|
2022-04-25 09:57:58 +00:00
|
|
|
objID, _ := obj.ID()
|
2021-10-04 14:32:35 +00:00
|
|
|
n.log.Warn("couldn't put system meta to objects cache",
|
2022-04-25 09:57:58 +00:00
|
|
|
zap.Stringer("object id", &objID),
|
2021-10-13 18:50:02 +00:00
|
|
|
zap.Stringer("bucket id", bkt.CID),
|
2021-10-04 14:32:35 +00:00
|
|
|
zap.Error(err))
|
|
|
|
}
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2021-10-13 18:50:02 +00:00
|
|
|
return cors, nil
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (n *layer) headSystemVersions(ctx context.Context, bkt *data.BucketInfo, sysName string) (*objectVersions, error) {
|
2021-11-13 20:35:50 +00:00
|
|
|
f := &findParams{
|
2022-03-01 19:02:24 +00:00
|
|
|
attr: [2]string{objectSystemAttributeName, sysName},
|
2022-06-01 17:35:20 +00:00
|
|
|
bkt: bkt,
|
2021-11-13 20:35:50 +00:00
|
|
|
}
|
|
|
|
ids, err := n.objectSearch(ctx, f)
|
2021-10-04 14:30:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
versions := newObjectVersions(sysName)
|
2022-02-08 16:54:04 +00:00
|
|
|
for i := range ids {
|
2022-06-01 17:35:20 +00:00
|
|
|
meta, err := n.objectHead(ctx, bkt, ids[i])
|
2021-10-04 14:30:38 +00:00
|
|
|
if err != nil {
|
|
|
|
n.log.Warn("couldn't head object",
|
2022-02-08 16:54:04 +00:00
|
|
|
zap.Stringer("object id", &ids[i]),
|
2021-10-04 14:30:38 +00:00
|
|
|
zap.Stringer("bucket id", bkt.CID),
|
|
|
|
zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if oi := objInfoFromMeta(bkt, meta); oi != nil {
|
|
|
|
if !isSystem(oi) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
versions.appendVersion(oi)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lastVersion := versions.getLast()
|
|
|
|
if lastVersion == nil {
|
|
|
|
return nil, errors.GetAPIError(errors.ErrNoSuchKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
return versions, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// systemObjectKey is a key to use in SystemCache.
|
|
|
|
func systemObjectKey(bktInfo *data.BucketInfo, obj string) string {
|
|
|
|
return bktInfo.Name + obj
|
|
|
|
}
|
2022-02-28 08:02:05 +00:00
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
func lockObjectKey(objVersion *ObjectVersion) string {
|
|
|
|
// todo reconsider forming name since versionID can be "null" or ""
|
|
|
|
return ".lock." + objVersion.BktInfo.CID.EncodeToString() + "." + objVersion.ObjectName + "." + objVersion.VersionID
|
|
|
|
}
|
|
|
|
|
2022-02-28 08:02:05 +00:00
|
|
|
func (n *layer) GetBucketSettings(ctx context.Context, bktInfo *data.BucketInfo) (*data.BucketSettings, error) {
|
2022-03-01 15:07:15 +00:00
|
|
|
systemKey := systemObjectKey(bktInfo, bktInfo.SettingsObjectName())
|
|
|
|
if settings := n.systemCache.GetSettings(systemKey); settings != nil {
|
2022-02-28 08:02:05 +00:00
|
|
|
return settings, nil
|
|
|
|
}
|
|
|
|
|
2022-05-17 13:52:51 +00:00
|
|
|
settings, err := n.treeService.GetSettingsNode(ctx, &bktInfo.CID)
|
2022-02-28 08:02:05 +00:00
|
|
|
if err != nil {
|
2022-05-12 01:48:17 +00:00
|
|
|
if !errorsStd.Is(err, ErrNodeNotFound) {
|
2022-05-17 13:03:38 +00:00
|
|
|
return nil, err
|
2022-03-31 16:25:33 +00:00
|
|
|
}
|
2022-04-22 07:18:21 +00:00
|
|
|
settings = &data.BucketSettings{}
|
2022-05-30 09:56:51 +00:00
|
|
|
settings.IsNoneStatus = true
|
2022-02-28 08:02:05 +00:00
|
|
|
}
|
|
|
|
|
2022-03-01 15:07:15 +00:00
|
|
|
if err = n.systemCache.PutSettings(systemKey, settings); err != nil {
|
2022-02-28 08:02:05 +00:00
|
|
|
n.log.Warn("couldn't put system meta to objects cache",
|
|
|
|
zap.Stringer("bucket id", bktInfo.CID),
|
|
|
|
zap.Error(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
return settings, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *layer) PutBucketSettings(ctx context.Context, p *PutSettingsParams) error {
|
2022-05-17 13:52:51 +00:00
|
|
|
if err := n.treeService.PutSettingsNode(ctx, &p.BktInfo.CID, p.Settings); err != nil {
|
2022-04-22 07:18:21 +00:00
|
|
|
return fmt.Errorf("failed to get settings node: %w", err)
|
2022-02-28 08:02:05 +00:00
|
|
|
}
|
|
|
|
|
2022-03-01 15:07:15 +00:00
|
|
|
systemKey := systemObjectKey(p.BktInfo, p.BktInfo.SettingsObjectName())
|
2022-04-22 07:18:21 +00:00
|
|
|
if err := n.systemCache.PutSettings(systemKey, p.Settings); err != nil {
|
2022-02-28 08:02:05 +00:00
|
|
|
n.log.Error("couldn't cache system object", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2022-02-28 13:39:04 +00:00
|
|
|
|
2022-03-05 08:53:01 +00:00
|
|
|
func (n *layer) attributesFromLock(ctx context.Context, lock *data.ObjectLock) ([][2]string, error) {
|
2022-05-26 13:11:14 +00:00
|
|
|
if lock.Retention == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2022-03-05 08:53:01 +00:00
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
_, exp, err := n.neoFS.TimeToEpoch(ctx, lock.Retention.Until)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("fetch time to epoch: %w", err)
|
|
|
|
}
|
2022-03-05 08:53:01 +00:00
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
result := [][2]string{
|
|
|
|
{AttributeExpirationEpoch, strconv.FormatUint(exp, 10)},
|
2022-02-28 13:39:04 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
if lock.Retention.IsCompliance {
|
|
|
|
attrCompliance := [2]string{
|
|
|
|
AttributeComplianceMode, strconv.FormatBool(true),
|
|
|
|
}
|
|
|
|
result = append(result, attrCompliance)
|
|
|
|
}
|
2022-03-05 08:53:01 +00:00
|
|
|
return result, nil
|
2022-02-28 13:39:04 +00:00
|
|
|
}
|