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 (
|
||||
"context"
|
||||
errorsStd "errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -23,7 +24,20 @@ func (h *handler) logAndSendError(w http.ResponseWriter, logText string, reqInfo
|
|||
fields = append(fields, additional...)
|
||||
|
||||
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) {
|
||||
|
|
|
@ -93,7 +93,7 @@ func (n *layer) objectHead(ctx context.Context, bktInfo *data.BucketInfo, idObj
|
|||
|
||||
res, err := n.neoFS.ReadObject(ctx, prm)
|
||||
if err != nil {
|
||||
return nil, n.transformNeofsError(ctx, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, n.transformNeofsError(ctx, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, n.transformNeofsError(ctx, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
return n.transformNeofsError(ctx, n.neoFS.DeleteObject(ctx, prm))
|
||||
return n.neoFS.DeleteObject(ctx, prm)
|
||||
}
|
||||
|
||||
// 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)
|
||||
})
|
||||
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.
|
||||
|
@ -815,19 +815,6 @@ func tryDirectoryName(node *data.NodeVersion, prefix, delimiter string) string {
|
|||
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 {
|
||||
if input == nil {
|
||||
return nil
|
||||
|
|
|
@ -85,6 +85,9 @@ var (
|
|||
// ErrNodeNotFound is returned from Tree service in case of not found error.
|
||||
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 = 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) {
|
||||
node, err := c.getSystemNodeWithAllAttributes(ctx, bktInfo, []string{bucketTaggingFilename})
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
return nil, layer.ErrNodeNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -549,7 +546,7 @@ func (c *TreeClient) GetLatestVersion(ctx context.Context, bktInfo *data.BucketI
|
|||
}
|
||||
nodes, err := c.getNodes(ctx, p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get nodes: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(nodes) == 0 {
|
||||
|
@ -1107,10 +1104,10 @@ func (c *TreeClient) getVersions(ctx context.Context, bktInfo *data.BucketInfo,
|
|||
}
|
||||
nodes, err := c.getNodes(ctx, p)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
if errors.Is(err, layer.ErrNodeNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("couldn't get nodes: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
return nil, layer.ErrNodeNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get sub tree client: %w", err)
|
||||
return nil, handleError("failed to get sub tree client", err)
|
||||
}
|
||||
|
||||
var subtree []*tree.GetSubTreeResponse_Body
|
||||
|
@ -1164,10 +1158,7 @@ func (c *TreeClient) getSubTree(ctx context.Context, bktInfo *data.BucketInfo, t
|
|||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
return nil, layer.ErrNodeNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get sub tree: %w", err)
|
||||
return nil, handleError("failed to get sub tree", err)
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get nodes: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
if len(nodes) == 0 {
|
||||
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)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
return nil, layer.ErrNodeNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get node path: %w", err)
|
||||
return nil, handleError("failed to get node by path", err)
|
||||
}
|
||||
|
||||
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 {
|
||||
if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != 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)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, handleError("failed to add node", err)
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, handleError("failed to add node by path", err)
|
||||
}
|
||||
|
||||
body := resp.GetBody()
|
||||
|
@ -1355,8 +1352,11 @@ func (c *TreeClient) moveNode(ctx context.Context, bktInfo *data.BucketInfo, tre
|
|||
return err
|
||||
}
|
||||
|
||||
_, err := c.service.Move(ctx, request)
|
||||
return err
|
||||
if _, err := c.service.Move(ctx, request); err != nil {
|
||||
return handleError("failed to move node", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
_, err := c.service.Remove(ctx, request)
|
||||
return err
|
||||
if _, err := c.service.Remove(ctx, request); err != nil {
|
||||
return handleError("failed to remove node", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func metaToKV(meta map[string]string) []*tree.KeyValue {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package neofs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-s3-gw/api/data"
|
||||
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
||||
"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