[#195] Add PUT and GET default lock configuration

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-02-28 11:29:03 +03:00 committed by Angira Kekteeva
parent b96c3c5a33
commit fe9eb9cedc
5 changed files with 147 additions and 9 deletions

View file

@ -48,6 +48,7 @@ type (
// BucketSettings stores settings such as versioning.
BucketSettings struct {
VersioningEnabled bool `json:"versioning_enabled"`
LockConfiguration *ObjectLockConfiguration `json:"lock_configuration"`
}
// CORSConfiguration stores CORS configuration of a request.

18
api/data/locking.go Normal file
View file

@ -0,0 +1,18 @@
package data
type (
ObjectLockConfiguration struct {
ObjectLockEnabled string `xml:"ObjectLockEnabled" json:"ObjectLockEnabled"`
Rule *ObjectLockRule `xml:"Rule" json:"Rule"`
}
ObjectLockRule struct {
DefaultRetention *DefaultRetention `xml:"DefaultRetention" json:"DefaultRetention"`
}
DefaultRetention struct {
Days int64 `xml:"Days" json:"Days"`
Mode string `xml:"Mode" json:"Mode"`
Years int64 `xml:"Years" json:"Years"`
}
)

123
api/handler/locking.go Normal file
View file

@ -0,0 +1,123 @@
package handler
import (
"encoding/xml"
"fmt"
"net/http"
"github.com/nspcc-dev/neofs-s3-gw/api"
"github.com/nspcc-dev/neofs-s3-gw/api/data"
apiErrors "github.com/nspcc-dev/neofs-s3-gw/api/errors"
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
)
func (h *handler) PutBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
reqInfo := api.GetReqInfo(r.Context())
bktInfo, err := h.obj.GetBucketInfo(r.Context(), reqInfo.BucketName)
if err != nil {
h.logAndSendError(w, "could not get bucket info", reqInfo, err)
return
}
if err = checkOwner(bktInfo, r.Header.Get(api.AmzExpectedBucketOwner)); err != nil {
h.logAndSendError(w, "expected owner doesn't match", reqInfo, err)
return
}
if !bktInfo.ObjectLockEnabled {
h.logAndSendError(w, "couldn't put object locking configuration", reqInfo,
apiErrors.GetAPIError(apiErrors.ErrObjectLockConfigurationNotAllowed))
return
}
lockingConf := &data.ObjectLockConfiguration{}
if err = xml.NewDecoder(r.Body).Decode(lockingConf); err != nil {
h.logAndSendError(w, "couldn't parse locking configuration", reqInfo, err)
return
}
if err = checkLockConfiguration(lockingConf); err != nil {
h.logAndSendError(w, "invalid lock configuration", reqInfo, err)
return
}
settings, err := h.obj.GetBucketSettings(r.Context(), bktInfo)
if err != nil {
h.logAndSendError(w, "couldn't get bucket settings", reqInfo, err)
return
}
settings.LockConfiguration = lockingConf
sp := &layer.PutSettingsParams{
BktInfo: bktInfo,
Settings: settings,
}
if err = h.obj.PutBucketSettings(r.Context(), sp); err != nil {
h.logAndSendError(w, "couldn't put bucket settings", reqInfo, err)
return
}
}
func (h *handler) GetBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
reqInfo := api.GetReqInfo(r.Context())
bktInfo, err := h.obj.GetBucketInfo(r.Context(), reqInfo.BucketName)
if err != nil {
h.logAndSendError(w, "could not get bucket info", reqInfo, err)
return
}
if err = checkOwner(bktInfo, r.Header.Get(api.AmzExpectedBucketOwner)); err != nil {
h.logAndSendError(w, "expected owner doesn't match", reqInfo, err)
return
}
settings, err := h.obj.GetBucketSettings(r.Context(), bktInfo)
if err != nil {
h.logAndSendError(w, "couldn't get bucket settings", reqInfo, err)
return
}
if !bktInfo.ObjectLockEnabled {
h.logAndSendError(w, "object lock disabled", reqInfo,
apiErrors.GetAPIError(apiErrors.ErrObjectLockConfigurationNotFound))
return
}
if settings.LockConfiguration == nil {
settings.LockConfiguration = &data.ObjectLockConfiguration{}
}
if settings.LockConfiguration.ObjectLockEnabled == "" {
settings.LockConfiguration.ObjectLockEnabled = enabledValue
}
if err = api.EncodeToResponse(w, settings.LockConfiguration); err != nil {
h.logAndSendError(w, "something went wrong", reqInfo, err)
}
}
func checkLockConfiguration(conf *data.ObjectLockConfiguration) error {
if conf.ObjectLockEnabled != "" && conf.ObjectLockEnabled != enabledValue {
return fmt.Errorf("invalid ObjectLockEnabled value: %s", conf.ObjectLockEnabled)
}
if conf.Rule == nil || conf.Rule.DefaultRetention == nil {
return nil
}
retention := conf.Rule.DefaultRetention
if retention.Mode != "GOVERNANCE" && retention.Mode != "COMPLIANCE" {
return fmt.Errorf("invalid Mode value: %s", retention.Mode)
}
if retention.Days == 0 && retention.Years == 0 {
return fmt.Errorf("you must specify Days or Years")
}
if retention.Days != 0 && retention.Years != 0 {
return fmt.Errorf("you cannot specify Days and Years at the same time")
}
return nil
}

View file

@ -188,6 +188,10 @@ type Tag struct {
Value string
}
const (
enabledValue = "Enabled"
)
// MarshalXML - StringMap marshals into XML.
func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
tokens := []xml.Token{start}

View file

@ -59,10 +59,6 @@ func (h *handler) DeleteBucketWebsiteHandler(w http.ResponseWriter, r *http.Requ
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
}
func (h *handler) GetBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
}
func (h *handler) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
}
@ -78,7 +74,3 @@ func (h *handler) PutBucketLifecycleHandler(w http.ResponseWriter, r *http.Reque
func (h *handler) PutBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
}
func (h *handler) PutBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
}