[#49] Add basic ACL translation

Implement functions:
GetBucketACL, PutBucketACL, GetObjectACL,
PutObjectACL, GetBucketPolicy, PutBucketPolicy

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2021-07-21 14:59:46 +03:00
parent b1c6629b10
commit efe11c271f
10 changed files with 2046 additions and 100 deletions

View file

@ -2,27 +2,24 @@ package handler
import (
"encoding/xml"
"fmt"
"net"
"net/http"
"strconv"
"strings"
"github.com/nspcc-dev/neofs-s3-gw/api/errors"
"github.com/nspcc-dev/neofs-api-go/pkg/acl"
"github.com/nspcc-dev/neofs-node/pkg/policy"
"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"
"go.uber.org/zap"
)
// keywords of predefined basic ACL values.
const (
basicACLPrivate = "private"
basicACLReadOnly = "public-read"
basicACLPublic = "public-read-write"
defaultPolicy = "REP 3"
basicACLPrivate = "private"
basicACLReadOnly = "public-read"
basicACLPublic = "public-read-write"
cannedACLAuthRead = "authenticated-read"
defaultPolicy = "REP 3"
publicBasicRule = 0x0FFFFFFF
)
@ -39,6 +36,50 @@ func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
reqInfo = api.GetReqInfo(r.Context())
)
objectACL, err := parseACLHeaders(r)
if err != nil {
h.logAndSendError(w, "could not parse object acl", reqInfo, err)
return
}
objectACL.Resource = reqInfo.BucketName + "/" + reqInfo.ObjectName
bktPolicy, err := aclToPolicy(objectACL)
if err != nil {
h.logAndSendError(w, "could not translate object acl to bucket policy", reqInfo, err)
return
}
astChild, err := policyToAst(bktPolicy)
if err != nil {
h.logAndSendError(w, "could not translate policy to ast", reqInfo, err)
return
}
bacl, err := h.obj.GetBucketACL(r.Context(), reqInfo.BucketName)
if err != nil {
h.logAndSendError(w, "could not get bucket eacl", reqInfo, err)
return
}
if err = checkOwner(bacl.Info, r.Header.Get(api.AmzExpectedBucketOwner)); err != nil {
h.logAndSendError(w, "expected owner doesn't match", reqInfo, err)
return
}
parentAst := tableToAst(bacl.EACL, reqInfo.BucketName)
for _, resource := range parentAst.Resources {
if resource.Name == bacl.Info.CID.String() {
resource.Name = reqInfo.BucketName
}
}
resAst, updated := mergeAst(parentAst, astChild)
table, err := astToTable(resAst, reqInfo.BucketName)
if err != nil {
h.logAndSendError(w, "could not translate ast to table", reqInfo, err)
return
}
metadata := parseMetadata(r)
if contentType := r.Header.Get(api.ContentType); len(contentType) > 0 {
metadata[api.ContentType] = contentType
@ -57,6 +98,18 @@ func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
return
}
if updated {
p := &layer.PutBucketACLParams{
Name: reqInfo.BucketName,
EACL: table,
}
if err = h.obj.PutBucketACL(r.Context(), p); err != nil {
h.logAndSendError(w, "could not put bucket acl", reqInfo, err)
return
}
}
w.Header().Set(api.ETag, info.HashSum)
api.WriteSuccessResponseHeadersOnly(w)
}
@ -74,24 +127,25 @@ func parseMetadata(r *http.Request) map[string]string {
func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
var (
err error
reqInfo = api.GetReqInfo(r.Context())
p = layer.CreateBucketParams{Name: reqInfo.BucketName}
p = layer.CreateBucketParams{Name: reqInfo.BucketName, ACL: publicBasicRule}
)
if err = checkBucketName(reqInfo.BucketName); err != nil {
if err := checkBucketName(reqInfo.BucketName); err != nil {
h.logAndSendError(w, "invalid bucket name", reqInfo, err)
return
}
if val, ok := r.Header["X-Amz-Acl"]; ok {
p.ACL, err = parseBasicACL(val[0])
} else {
p.ACL = publicBasicRule
}
bktACL, err := parseACLHeaders(r)
if err != nil {
h.logAndSendError(w, "could not parse basic ACL", reqInfo, err)
h.logAndSendError(w, "could not parse bucket acl", reqInfo, err)
return
}
bktACL.IsBucket = true
p.EACL, err = bucketACLToTable(bktACL)
if err != nil {
h.logAndSendError(w, "could translate bucket acl to eacl", reqInfo, err)
return
}
@ -180,23 +234,3 @@ func parseLocationConstraint(r *http.Request) (*createBucketParams, error) {
}
return params, nil
}
func parseBasicACL(basicACL string) (uint32, error) {
switch basicACL {
case basicACLPublic:
return acl.PublicBasicRule, nil
case basicACLPrivate:
return acl.PrivateBasicRule, nil
case basicACLReadOnly:
return acl.ReadOnlyBasicRule, nil
default:
basicACL = strings.Trim(strings.ToLower(basicACL), "0x")
value, err := strconv.ParseUint(basicACL, 16, 32)
if err != nil {
return 0, fmt.Errorf("can't parse basic ACL: %s", basicACL)
}
return uint32(value), nil
}
}