forked from TrueCloudLab/frostfs-s3-gw
[#196] Add bucket tagging
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
16da1aba64
commit
987185b9e1
10 changed files with 229 additions and 76 deletions
|
@ -2,7 +2,9 @@ package handler
|
|||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
|
@ -14,6 +16,7 @@ import (
|
|||
const (
|
||||
allowedTagChars = "+-=._:/@"
|
||||
|
||||
maxTags = 10
|
||||
keyTagMaxLength = 128
|
||||
valueTagMaxLength = 256
|
||||
)
|
||||
|
@ -21,14 +24,9 @@ const (
|
|||
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)
|
||||
tagSet, err := readTagSet(r.Body)
|
||||
if err != nil {
|
||||
h.logAndSendError(w, "could not read tag set", reqInfo, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -44,11 +42,6 @@ func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request
|
|||
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,
|
||||
|
@ -81,31 +74,12 @@ func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request
|
|||
return
|
||||
}
|
||||
|
||||
tagging := &Tagging{}
|
||||
for k, v := range tagSet {
|
||||
tagging.TagSet = append(tagging.TagSet, Tag{Key: k, Value: v})
|
||||
}
|
||||
|
||||
w.Header().Set(api.AmzVersionID, objInfo.Version())
|
||||
if err = api.EncodeToResponse(w, tagging); err != nil {
|
||||
if err = api.EncodeToResponse(w, encodeTagging(tagSet)); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
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 (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
|
||||
|
@ -128,34 +102,114 @@ func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Requ
|
|||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
|
||||
tagSet, err := readTagSet(r.Body)
|
||||
if err != nil {
|
||||
h.logAndSendError(w, "could not read tag set", reqInfo, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.obj.PutBucketTagging(r.Context(), reqInfo.BucketName, tagSet); err != nil {
|
||||
h.logAndSendError(w, "could not put object tagging", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
|
||||
tagSet, err := h.obj.GetBucketTagging(r.Context(), reqInfo.BucketName)
|
||||
if err != nil {
|
||||
h.logAndSendError(w, "could not get object tagging", reqInfo, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = api.EncodeToResponse(w, encodeTagging(tagSet)); err != nil {
|
||||
h.logAndSendError(w, "something went wrong", reqInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
if err := h.obj.DeleteBucketTagging(r.Context(), reqInfo.BucketName); err != nil {
|
||||
h.logAndSendError(w, "could not delete object tagging", reqInfo, err)
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func readTagSet(reader io.Reader) (map[string]string, error) {
|
||||
tagging := new(Tagging)
|
||||
if err := xml.NewDecoder(reader).Decode(tagging); err != nil {
|
||||
return nil, errors.GetAPIError(errors.ErrMalformedXML)
|
||||
}
|
||||
|
||||
if err := checkTagSet(tagging.TagSet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tagSet := make(map[string]string, len(tagging.TagSet))
|
||||
for _, tag := range tagging.TagSet {
|
||||
tagSet[tag.Key] = tag.Value
|
||||
}
|
||||
|
||||
return tagSet, nil
|
||||
}
|
||||
|
||||
func encodeTagging(tagSet map[string]string) *Tagging {
|
||||
tagging := &Tagging{}
|
||||
for k, v := range tagSet {
|
||||
tagging.TagSet = append(tagging.TagSet, Tag{Key: k, Value: v})
|
||||
}
|
||||
sort.Slice(tagging.TagSet, func(i, j int) bool {
|
||||
return tagging.TagSet[i].Key < tagging.TagSet[j].Key
|
||||
})
|
||||
|
||||
return tagging
|
||||
}
|
||||
|
||||
func checkTagSet(tagSet []Tag) error {
|
||||
if len(tagSet) > maxTags {
|
||||
return errors.GetAPIError(errors.ErrInvalidTagsSizeExceed)
|
||||
}
|
||||
|
||||
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)
|
||||
return errors.GetAPIError(errors.ErrInvalidTagKey)
|
||||
}
|
||||
if len(tag.Value) < 1 || len(tag.Value) > valueTagMaxLength {
|
||||
return errors.GetAPIError(errors.ErrInvalidTag)
|
||||
if len(tag.Value) > valueTagMaxLength {
|
||||
return errors.GetAPIError(errors.ErrInvalidTagValue)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(tag.Key, "aws:") {
|
||||
return errors.GetAPIError(errors.ErrInvalidTag)
|
||||
return errors.GetAPIError(errors.ErrInvalidTagKey)
|
||||
}
|
||||
|
||||
if err := checkCharacters(tag.Key); err != nil {
|
||||
return err
|
||||
if !isValidTag(tag.Key) {
|
||||
return errors.GetAPIError(errors.ErrInvalidTagKey)
|
||||
}
|
||||
if err := checkCharacters(tag.Value); err != nil {
|
||||
return err
|
||||
if !isValidTag(tag.Value) {
|
||||
return errors.GetAPIError(errors.ErrInvalidTagValue)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkCharacters(str string) error {
|
||||
func isValidTag(str string) bool {
|
||||
for _, r := range str {
|
||||
if !unicode.IsLetter(r) && !unicode.IsDigit(r) &&
|
||||
!unicode.IsSpace(r) && !strings.ContainsRune(allowedTagChars, r) {
|
||||
return errors.GetAPIError(errors.ErrInvalidTag)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return true
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue