forked from TrueCloudLab/frostfs-s3-gw
Denis Kirillov
689f7ee818
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>
118 lines
2.9 KiB
Go
118 lines
2.9 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"
|
|
"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{
|
|
Container: p.BktInfo.CID,
|
|
Payload: &buf,
|
|
Filepath: p.BktInfo.CORSObjectName(),
|
|
CreationTime: TimeNow(ctx),
|
|
CopiesNumber: p.CopiesNumbers,
|
|
}
|
|
|
|
_, objID, _, _, err := n.objectPutAndHash(ctx, prm, p.BktInfo)
|
|
if err != nil {
|
|
return fmt.Errorf("put system object: %w", err)
|
|
}
|
|
|
|
objIDsToDelete, err := n.treeService.PutBucketCORS(ctx, p.BktInfo, objID)
|
|
objIDToDeleteNotFound := errorsStd.Is(err, ErrNoNodeToRemove)
|
|
if err != nil && !objIDToDeleteNotFound {
|
|
return err
|
|
}
|
|
|
|
if !objIDToDeleteNotFound {
|
|
for _, id := range objIDsToDelete {
|
|
if err = n.objectDelete(ctx, p.BktInfo, id); err != nil {
|
|
n.reqLogger(ctx).Error(logs.CouldntDeleteCorsObject, zap.Error(err),
|
|
zap.String("cnrID", p.BktInfo.CID.EncodeToString()),
|
|
zap.String("objID", id.EncodeToString()))
|
|
}
|
|
}
|
|
}
|
|
|
|
n.cache.PutCORS(n.BearerOwner(ctx), p.BktInfo, cors)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (n *Layer) GetBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) (*data.CORSConfiguration, error) {
|
|
cors, err := n.getCORS(ctx, bktInfo)
|
|
if err != nil {
|
|
if errorsStd.Is(err, ErrNodeNotFound) {
|
|
return nil, fmt.Errorf("%w: %s", errors.GetAPIError(errors.ErrNoSuchCORSConfiguration), err.Error())
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return cors, nil
|
|
}
|
|
|
|
func (n *Layer) DeleteBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) error {
|
|
objIDs, err := n.treeService.DeleteBucketCORS(ctx, bktInfo)
|
|
objIDNotFound := errorsStd.Is(err, ErrNoNodeToRemove)
|
|
if err != nil && !objIDNotFound {
|
|
return err
|
|
}
|
|
if !objIDNotFound {
|
|
for _, id := range objIDs {
|
|
if err = n.objectDelete(ctx, bktInfo, id); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|