forked from TrueCloudLab/frostfs-s3-gw
108 lines
2.3 KiB
Go
108 lines
2.3 KiB
Go
|
package handler
|
||
|
|
||
|
import (
|
||
|
"encoding/xml"
|
||
|
"net/http"
|
||
|
"strings"
|
||
|
"unicode"
|
||
|
|
||
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||
|
"github.com/nspcc-dev/neofs-s3-gw/api/errors"
|
||
|
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
allowedTagChars = "+-=._:/@"
|
||
|
|
||
|
keyTagMaxLength = 128
|
||
|
valueTagMaxLength = 256
|
||
|
)
|
||
|
|
||
|
func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||
|
reqInfo := api.GetReqInfo(r.Context())
|
||
|
|
||
|
tagging := new(Tagging)
|
||
|
if err := xml.NewDecoder(r.Body).Decode(tagging); err != nil {
|
||
|
h.logAndSendError(w, "could not decode body", reqInfo, errors.GetAPIError(errors.ErrMalformedXML))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if err := checkTagSet(tagging.TagSet); err != nil {
|
||
|
h.logAndSendError(w, "some tags are invalid", reqInfo, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
p := &layer.HeadObjectParams{
|
||
|
Bucket: reqInfo.BucketName,
|
||
|
Object: reqInfo.ObjectName,
|
||
|
VersionID: reqInfo.URL.Query().Get("versionId"),
|
||
|
}
|
||
|
|
||
|
objInfo, err := h.obj.GetObjectInfo(r.Context(), p)
|
||
|
if err != nil {
|
||
|
h.logAndSendError(w, "could not get object info", reqInfo, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
tagSet := make(map[string]string, len(tagging.TagSet))
|
||
|
for _, tag := range tagging.TagSet {
|
||
|
tagSet[tag.Key] = tag.Value
|
||
|
}
|
||
|
|
||
|
p2 := &layer.PutTaggingParams{
|
||
|
ObjectInfo: objInfo,
|
||
|
TagSet: tagSet,
|
||
|
}
|
||
|
|
||
|
if err = h.obj.PutObjectTagging(r.Context(), p2); err != nil {
|
||
|
h.logAndSendError(w, "could not put object tagging", reqInfo, err)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func checkTagSet(tagSet []Tag) error {
|
||
|
if len(tagSet) > 10 {
|
||
|
return errors.GetAPIError(errors.ErrInvalidTag)
|
||
|
}
|
||
|
|
||
|
for _, tag := range tagSet {
|
||
|
if err := checkTag(tag); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func checkTag(tag Tag) error {
|
||
|
if len(tag.Key) < 1 || len(tag.Key) > keyTagMaxLength {
|
||
|
return errors.GetAPIError(errors.ErrInvalidTag)
|
||
|
}
|
||
|
if len(tag.Value) < 1 || len(tag.Value) > valueTagMaxLength {
|
||
|
return errors.GetAPIError(errors.ErrInvalidTag)
|
||
|
}
|
||
|
|
||
|
if strings.HasPrefix(tag.Key, "aws:") {
|
||
|
return errors.GetAPIError(errors.ErrInvalidTag)
|
||
|
}
|
||
|
|
||
|
if err := checkCharacters(tag.Key); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err := checkCharacters(tag.Value); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func checkCharacters(str string) error {
|
||
|
for _, r := range str {
|
||
|
if !unicode.IsLetter(r) && !unicode.IsDigit(r) &&
|
||
|
!unicode.IsSpace(r) && !strings.ContainsRune(allowedTagChars, r) {
|
||
|
return errors.GetAPIError(errors.ErrInvalidTag)
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|