forked from TrueCloudLab/frostfs-s3-gw
Denis Kirillov
f5326b9f04
It's need to fit user expectation on deleting CORS for example. Previously after removing cors (that was uploaded in split manner) we can still get some data (from other node) because deletion worked only for latest node version. Signed-off-by: Denis Kirillov <d.kirillov@yadro.com> Signed-off-by: Alex Vanin <a.vanin@yadro.com>
137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
package layer
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
errorsStd "errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
const wildcard = "*"
|
|
|
|
var supportedMethods = map[string]struct{}{"GET": {}, "HEAD": {}, "POST": {}, "PUT": {}, "DELETE": {}}
|
|
|
|
func (n *Layer) PutBucketCORS(ctx context.Context, p *PutCORSParams) error {
|
|
var (
|
|
buf bytes.Buffer
|
|
tee = io.TeeReader(p.Reader, &buf)
|
|
cors = &data.CORSConfiguration{}
|
|
)
|
|
|
|
if err := p.NewDecoder(tee).Decode(cors); err != nil {
|
|
return fmt.Errorf("xml decode cors: %w", err)
|
|
}
|
|
|
|
if cors.CORSRules == nil {
|
|
return errors.GetAPIError(errors.ErrMalformedXML)
|
|
}
|
|
|
|
if err := checkCORS(cors); err != nil {
|
|
return err
|
|
}
|
|
|
|
prm := PrmObjectCreate{
|
|
Payload: &buf,
|
|
Filepath: p.BktInfo.CORSObjectName(),
|
|
CreationTime: TimeNow(ctx),
|
|
}
|
|
|
|
var corsBkt *data.BucketInfo
|
|
if n.corsCnrInfo == nil {
|
|
corsBkt = p.BktInfo
|
|
prm.CopiesNumber = p.CopiesNumbers
|
|
} else {
|
|
corsBkt = n.corsCnrInfo
|
|
prm.PrmAuth.PrivateKey = &n.gateKey.PrivateKey
|
|
}
|
|
|
|
prm.Container = corsBkt.CID
|
|
|
|
_, objID, _, _, err := n.objectPutAndHash(ctx, prm, corsBkt)
|
|
if err != nil {
|
|
return fmt.Errorf("put cors object: %w", err)
|
|
}
|
|
|
|
objsToDelete, err := n.treeService.PutBucketCORS(ctx, p.BktInfo, newAddress(corsBkt.CID, objID))
|
|
objToDeleteNotFound := errorsStd.Is(err, ErrNoNodeToRemove)
|
|
if err != nil && !objToDeleteNotFound {
|
|
return err
|
|
}
|
|
|
|
if !objToDeleteNotFound {
|
|
for _, addr := range objsToDelete {
|
|
n.deleteCORSObject(ctx, p.BktInfo, addr)
|
|
}
|
|
}
|
|
|
|
n.cache.PutCORS(n.BearerOwner(ctx), p.BktInfo, cors)
|
|
|
|
return nil
|
|
}
|
|
|
|
// deleteCORSObject removes object and logs in case of error.
|
|
func (n *Layer) deleteCORSObject(ctx context.Context, bktInfo *data.BucketInfo, addr oid.Address) {
|
|
var prmAuth PrmAuth
|
|
corsBkt := bktInfo
|
|
if !addr.Container().Equals(bktInfo.CID) && !addr.Container().Equals(cid.ID{}) {
|
|
corsBkt = &data.BucketInfo{CID: addr.Container()}
|
|
prmAuth.PrivateKey = &n.gateKey.PrivateKey
|
|
}
|
|
|
|
if err := n.objectDeleteWithAuth(ctx, corsBkt, addr.Object(), prmAuth); err != nil {
|
|
n.reqLogger(ctx).Error(logs.CouldntDeleteCorsObject, zap.Error(err),
|
|
zap.String("cnrID", corsBkt.CID.EncodeToString()),
|
|
zap.String("objID", addr.Object().EncodeToString()))
|
|
}
|
|
}
|
|
|
|
func (n *Layer) GetBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) (*data.CORSConfiguration, error) {
|
|
cors, err := n.getCORS(ctx, bktInfo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return cors, nil
|
|
}
|
|
|
|
func (n *Layer) DeleteBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) error {
|
|
objs, err := n.treeService.DeleteBucketCORS(ctx, bktInfo)
|
|
objNotFound := errorsStd.Is(err, ErrNoNodeToRemove)
|
|
if err != nil && !objNotFound {
|
|
return err
|
|
}
|
|
|
|
if !objNotFound {
|
|
for _, addr := range objs {
|
|
n.deleteCORSObject(ctx, bktInfo, addr)
|
|
}
|
|
}
|
|
|
|
n.cache.DeleteCORS(bktInfo)
|
|
|
|
return nil
|
|
}
|
|
|
|
func checkCORS(cors *data.CORSConfiguration) error {
|
|
for _, r := range cors.CORSRules {
|
|
for _, m := range r.AllowedMethods {
|
|
if _, ok := supportedMethods[m]; !ok {
|
|
return errors.GetAPIErrorWithError(errors.ErrCORSUnsupportedMethod, fmt.Errorf("unsupported method is %s", m))
|
|
}
|
|
}
|
|
for _, h := range r.ExposeHeaders {
|
|
if h == wildcard {
|
|
return errors.GetAPIError(errors.ErrCORSWildcardExposeHeaders)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|