forked from TrueCloudLab/frostfs-s3-gw
[#505] Handle access denied from tree service
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
80beedf13e
commit
3d08562843
5 changed files with 79 additions and 44 deletions
|
@ -2,6 +2,7 @@ package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
errorsStd "errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -23,7 +24,20 @@ func (h *handler) logAndSendError(w http.ResponseWriter, logText string, reqInfo
|
||||||
fields = append(fields, additional...)
|
fields = append(fields, additional...)
|
||||||
|
|
||||||
h.log.Error(logText, fields...)
|
h.log.Error(logText, fields...)
|
||||||
api.WriteErrorResponse(w, reqInfo, err)
|
api.WriteErrorResponse(w, reqInfo, transformToS3Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
func transformToS3Error(err error) error {
|
||||||
|
if _, ok := err.(errors.Error); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if errorsStd.Is(err, layer.ErrAccessDenied) ||
|
||||||
|
errorsStd.Is(err, layer.ErrNodeAccessDenied) {
|
||||||
|
return errors.GetAPIError(errors.ErrAccessDenied)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.GetAPIError(errors.ErrInternalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) getBucketAndCheckOwner(r *http.Request, bucket string, header ...string) (*data.BucketInfo, error) {
|
func (h *handler) getBucketAndCheckOwner(r *http.Request, bucket string, header ...string) (*data.BucketInfo, error) {
|
||||||
|
|
|
@ -93,7 +93,7 @@ func (n *layer) objectHead(ctx context.Context, bktInfo *data.BucketInfo, idObj
|
||||||
|
|
||||||
res, err := n.neoFS.ReadObject(ctx, prm)
|
res, err := n.neoFS.ReadObject(ctx, prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, n.transformNeofsError(ctx, err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.Head, nil
|
return res.Head, nil
|
||||||
|
@ -113,7 +113,7 @@ func (n *layer) initObjectPayloadReader(ctx context.Context, p getParams) (io.Re
|
||||||
|
|
||||||
res, err := n.neoFS.ReadObject(ctx, prm)
|
res, err := n.neoFS.ReadObject(ctx, prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, n.transformNeofsError(ctx, err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.Payload, nil
|
return res.Payload, nil
|
||||||
|
@ -132,7 +132,7 @@ func (n *layer) objectGet(ctx context.Context, bktInfo *data.BucketInfo, objID o
|
||||||
|
|
||||||
res, err := n.neoFS.ReadObject(ctx, prm)
|
res, err := n.neoFS.ReadObject(ctx, prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, n.transformNeofsError(ctx, err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.Head, nil
|
return res.Head, nil
|
||||||
|
@ -420,7 +420,7 @@ func (n *layer) objectDelete(ctx context.Context, bktInfo *data.BucketInfo, idOb
|
||||||
|
|
||||||
n.objCache.Delete(newAddress(bktInfo.CID, idObj))
|
n.objCache.Delete(newAddress(bktInfo.CID, idObj))
|
||||||
|
|
||||||
return n.transformNeofsError(ctx, n.neoFS.DeleteObject(ctx, prm))
|
return n.neoFS.DeleteObject(ctx, prm)
|
||||||
}
|
}
|
||||||
|
|
||||||
// objectPutAndHash prepare auth parameters and invoke neofs.CreateObject.
|
// objectPutAndHash prepare auth parameters and invoke neofs.CreateObject.
|
||||||
|
@ -432,7 +432,7 @@ func (n *layer) objectPutAndHash(ctx context.Context, prm PrmObjectCreate, bktIn
|
||||||
hash.Write(buf)
|
hash.Write(buf)
|
||||||
})
|
})
|
||||||
id, err := n.neoFS.CreateObject(ctx, prm)
|
id, err := n.neoFS.CreateObject(ctx, prm)
|
||||||
return id, hash.Sum(nil), n.transformNeofsError(ctx, err)
|
return id, hash.Sum(nil), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListObjectsV1 returns objects in a bucket for requests of Version 1.
|
// ListObjectsV1 returns objects in a bucket for requests of Version 1.
|
||||||
|
@ -815,19 +815,6 @@ func tryDirectoryName(node *data.NodeVersion, prefix, delimiter string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *layer) transformNeofsError(ctx context.Context, err error) error {
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.Is(err, ErrAccessDenied) {
|
|
||||||
n.log.Debug("error was transformed", zap.String("request_id", api.GetRequestID(ctx)), zap.Error(err))
|
|
||||||
return apiErrors.GetAPIError(apiErrors.ErrAccessDenied)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrapReader(input io.Reader, bufSize int, f func(buf []byte)) io.Reader {
|
func wrapReader(input io.Reader, bufSize int, f func(buf []byte)) io.Reader {
|
||||||
if input == nil {
|
if input == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -85,6 +85,9 @@ var (
|
||||||
// ErrNodeNotFound is returned from Tree service in case of not found error.
|
// ErrNodeNotFound is returned from Tree service in case of not found error.
|
||||||
ErrNodeNotFound = errors.New("not found")
|
ErrNodeNotFound = errors.New("not found")
|
||||||
|
|
||||||
|
// ErrNodeAccessDenied is returned from Tree service in case of access denied error.
|
||||||
|
ErrNodeAccessDenied = errors.New("access denied")
|
||||||
|
|
||||||
// ErrNoNodeToRemove is returned from Tree service in case of the lack of node with OID to remove.
|
// ErrNoNodeToRemove is returned from Tree service in case of the lack of node with OID to remove.
|
||||||
ErrNoNodeToRemove = errors.New("no node to remove")
|
ErrNoNodeToRemove = errors.New("no node to remove")
|
||||||
)
|
)
|
||||||
|
|
|
@ -441,9 +441,6 @@ func (c *TreeClient) DeleteObjectTagging(ctx context.Context, bktInfo *data.Buck
|
||||||
func (c *TreeClient) GetBucketTagging(ctx context.Context, bktInfo *data.BucketInfo) (map[string]string, error) {
|
func (c *TreeClient) GetBucketTagging(ctx context.Context, bktInfo *data.BucketInfo) (map[string]string, error) {
|
||||||
node, err := c.getSystemNodeWithAllAttributes(ctx, bktInfo, []string{bucketTaggingFilename})
|
node, err := c.getSystemNodeWithAllAttributes(ctx, bktInfo, []string{bucketTaggingFilename})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "not found") {
|
|
||||||
return nil, layer.ErrNodeNotFound
|
|
||||||
}
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,7 +546,7 @@ func (c *TreeClient) GetLatestVersion(ctx context.Context, bktInfo *data.BucketI
|
||||||
}
|
}
|
||||||
nodes, err := c.getNodes(ctx, p)
|
nodes, err := c.getNodes(ctx, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't get nodes: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
|
@ -1107,10 +1104,10 @@ func (c *TreeClient) getVersions(ctx context.Context, bktInfo *data.BucketInfo,
|
||||||
}
|
}
|
||||||
nodes, err := c.getNodes(ctx, p)
|
nodes, err := c.getNodes(ctx, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "not found") {
|
if errors.Is(err, layer.ErrNodeNotFound) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("couldn't get nodes: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([]*data.NodeVersion, 0, len(nodes))
|
result := make([]*data.NodeVersion, 0, len(nodes))
|
||||||
|
@ -1152,10 +1149,7 @@ func (c *TreeClient) getSubTree(ctx context.Context, bktInfo *data.BucketInfo, t
|
||||||
|
|
||||||
cli, err := c.service.GetSubTree(ctx, request)
|
cli, err := c.service.GetSubTree(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "not found") {
|
return nil, handleError("failed to get sub tree client", err)
|
||||||
return nil, layer.ErrNodeNotFound
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("failed to get sub tree client: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var subtree []*tree.GetSubTreeResponse_Body
|
var subtree []*tree.GetSubTreeResponse_Body
|
||||||
|
@ -1164,10 +1158,7 @@ func (c *TreeClient) getSubTree(ctx context.Context, bktInfo *data.BucketInfo, t
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
if strings.Contains(err.Error(), "not found") {
|
return nil, handleError("failed to get sub tree", err)
|
||||||
return nil, layer.ErrNodeNotFound
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("failed to get sub tree: %w", err)
|
|
||||||
}
|
}
|
||||||
subtree = append(subtree, resp.Body)
|
subtree = append(subtree, resp.Body)
|
||||||
}
|
}
|
||||||
|
@ -1213,7 +1204,7 @@ func (c *TreeClient) getNode(ctx context.Context, bktInfo *data.BucketInfo, tree
|
||||||
}
|
}
|
||||||
nodes, err := c.getNodes(ctx, p)
|
nodes, err := c.getNodes(ctx, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't get nodes: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
return nil, layer.ErrNodeNotFound
|
return nil, layer.ErrNodeNotFound
|
||||||
|
@ -1250,15 +1241,21 @@ func (c *TreeClient) getNodes(ctx context.Context, p *getNodesParams) ([]*tree.G
|
||||||
|
|
||||||
resp, err := c.service.GetNodeByPath(ctx, request)
|
resp, err := c.service.GetNodeByPath(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "not found") {
|
return nil, handleError("failed to get node by path", err)
|
||||||
return nil, layer.ErrNodeNotFound
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("failed to get node path: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp.GetBody().GetNodes(), nil
|
return resp.GetBody().GetNodes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleError(msg string, err error) error {
|
||||||
|
if strings.Contains(err.Error(), "not found") {
|
||||||
|
return fmt.Errorf("%w: %s", layer.ErrNodeNotFound, err.Error())
|
||||||
|
} else if strings.Contains(err.Error(), "is denied by") {
|
||||||
|
return fmt.Errorf("%w: %s", layer.ErrNodeAccessDenied, err.Error())
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s: %w", msg, err)
|
||||||
|
}
|
||||||
|
|
||||||
func getBearer(ctx context.Context, bktInfo *data.BucketInfo) []byte {
|
func getBearer(ctx context.Context, bktInfo *data.BucketInfo) []byte {
|
||||||
if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil {
|
if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil {
|
||||||
if bd.Gate.BearerToken != nil {
|
if bd.Gate.BearerToken != nil {
|
||||||
|
@ -1291,7 +1288,7 @@ func (c *TreeClient) addNode(ctx context.Context, bktInfo *data.BucketInfo, tree
|
||||||
|
|
||||||
resp, err := c.service.Add(ctx, request)
|
resp, err := c.service.Add(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, handleError("failed to add node", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp.GetBody().GetNodeId(), nil
|
return resp.GetBody().GetNodeId(), nil
|
||||||
|
@ -1320,7 +1317,7 @@ func (c *TreeClient) addNodeByPath(ctx context.Context, bktInfo *data.BucketInfo
|
||||||
|
|
||||||
resp, err := c.service.AddByPath(ctx, request)
|
resp, err := c.service.AddByPath(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, handleError("failed to add node by path", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
body := resp.GetBody()
|
body := resp.GetBody()
|
||||||
|
@ -1355,8 +1352,11 @@ func (c *TreeClient) moveNode(ctx context.Context, bktInfo *data.BucketInfo, tre
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := c.service.Move(ctx, request)
|
if _, err := c.service.Move(ctx, request); err != nil {
|
||||||
return err
|
return handleError("failed to move node", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TreeClient) removeNode(ctx context.Context, bktInfo *data.BucketInfo, treeID string, nodeID uint64) error {
|
func (c *TreeClient) removeNode(ctx context.Context, bktInfo *data.BucketInfo, treeID string, nodeID uint64) error {
|
||||||
|
@ -1377,8 +1377,11 @@ func (c *TreeClient) removeNode(ctx context.Context, bktInfo *data.BucketInfo, t
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := c.service.Remove(ctx, request)
|
if _, err := c.service.Remove(ctx, request); err != nil {
|
||||||
return err
|
return handleError("failed to remove node", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func metaToKV(meta map[string]string) []*tree.KeyValue {
|
func metaToKV(meta map[string]string) []*tree.KeyValue {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package neofs
|
package neofs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/data"
|
"github.com/nspcc-dev/neofs-s3-gw/api/data"
|
||||||
|
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -93,3 +95,29 @@ func TestLockConfigurationEncoding(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHandleError(t *testing.T) {
|
||||||
|
defaultError := errors.New("default error")
|
||||||
|
for _, tc := range []struct {
|
||||||
|
err error
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
err: defaultError,
|
||||||
|
expectedError: defaultError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
err: errors.New("something not found"),
|
||||||
|
expectedError: layer.ErrNodeNotFound,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
err: errors.New("something is denied by some acl rule"),
|
||||||
|
expectedError: layer.ErrNodeAccessDenied,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
err := handleError("err message", tc.err)
|
||||||
|
require.True(t, errors.Is(err, tc.expectedError))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue