forked from TrueCloudLab/frostfs-s3-gw
[#535] Support public access block operations
Signed-off-by: Marina Biryukova <m.biryukova@yadro.com>
This commit is contained in:
parent
4f0f2ca7bd
commit
a7ce40d745
23 changed files with 940 additions and 87 deletions
311
api/handler/access_block_test.go
Normal file
311
api/handler/access_block_test.go
Normal file
|
@ -0,0 +1,311 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
apierr "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/iam"
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetPublicAccessBlock(t *testing.T) {
|
||||
hc := prepareHandlerContext(t)
|
||||
bktName := "bucket-get-public-access-block"
|
||||
|
||||
createBucket(hc, bktName)
|
||||
deletePublicAccessBlock(hc, bktName)
|
||||
getPublicAccessBlockErr(hc, bktName, apierr.GetAPIError(apierr.ErrNoSuchPublicAccessBlockConfiguration))
|
||||
|
||||
putPublicAccessBlock(hc, bktName, &data.PublicAccessBlockConfiguration{
|
||||
BlockPublicAcls: true,
|
||||
BlockPublicPolicy: true,
|
||||
IgnorePublicAcls: true,
|
||||
RestrictPublicBuckets: true,
|
||||
}, nil)
|
||||
|
||||
cfg := getPublicAccessBlock(hc, bktName)
|
||||
require.True(t, cfg.BlockPublicAcls)
|
||||
require.True(t, cfg.BlockPublicPolicy)
|
||||
require.True(t, cfg.IgnorePublicAcls)
|
||||
require.True(t, cfg.RestrictPublicBuckets)
|
||||
|
||||
deletePublicAccessBlock(hc, bktName)
|
||||
getPublicAccessBlockErr(hc, bktName, apierr.GetAPIError(apierr.ErrNoSuchPublicAccessBlockConfiguration))
|
||||
}
|
||||
|
||||
func TestPutPublicAccessBlock(t *testing.T) {
|
||||
hc := prepareHandlerContext(t)
|
||||
bktName := "bucket-put-public-access-block"
|
||||
|
||||
createBucket(hc, bktName)
|
||||
|
||||
cfg := &data.PublicAccessBlockConfiguration{
|
||||
BlockPublicAcls: true,
|
||||
BlockPublicPolicy: true,
|
||||
IgnorePublicAcls: true,
|
||||
RestrictPublicBuckets: true,
|
||||
}
|
||||
body, err := xml.Marshal(cfg)
|
||||
require.NoError(t, err)
|
||||
contentMD5, err := getContentMD5(bytes.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
|
||||
putPublicAccessBlockErr(hc, bktName, cfg, map[string]string{api.AmzExpectedBucketOwner: "owner"}, apierr.GetAPIError(apierr.ErrAccessDenied))
|
||||
putPublicAccessBlockErr(hc, bktName, cfg, map[string]string{api.ContentMD5: "invalid"}, apierr.GetAPIError(apierr.ErrInvalidDigest))
|
||||
putPublicAccessBlockErr(hc, bktName, cfg, map[string]string{api.ContentMD5: base64.StdEncoding.EncodeToString([]byte{})}, apierr.GetAPIError(apierr.ErrInvalidDigest))
|
||||
putPublicAccessBlock(hc, bktName, cfg, map[string]string{api.ContentMD5: base64.StdEncoding.EncodeToString(contentMD5)})
|
||||
|
||||
w, r := prepareTestRequest(hc, bktName, "", &data.LifecycleConfiguration{})
|
||||
hc.Handler().PutPublicAccessBlockHandler(w, r)
|
||||
assertS3Error(t, w, apierr.GetAPIError(apierr.ErrMalformedXML))
|
||||
}
|
||||
|
||||
func TestBlockPublicAcls(t *testing.T) {
|
||||
hc := prepareHandlerContext(t)
|
||||
bktName := "bucket-block-public-acls"
|
||||
|
||||
info := createBucket(hc, bktName)
|
||||
putPublicAccessBlock(hc, bktName, &data.PublicAccessBlockConfiguration{
|
||||
BlockPublicAcls: true,
|
||||
}, nil)
|
||||
|
||||
putBucketACLErr(hc, bktName, info.Box, map[string]string{api.AmzACL: basicACLPublic}, apierr.GetAPIError(apierr.ErrAccessDenied))
|
||||
putBucketACLErr(hc, bktName, info.Box, map[string]string{api.AmzACL: basicACLReadOnly}, apierr.GetAPIError(apierr.ErrAccessDenied))
|
||||
putBucketACL(hc, bktName, info.Box, map[string]string{api.AmzACL: basicACLPrivate})
|
||||
}
|
||||
|
||||
func TestBlockPublicPolicy(t *testing.T) {
|
||||
hc := prepareHandlerContext(t)
|
||||
bktName := "bucket-block-public-policy"
|
||||
|
||||
createBucket(hc, bktName)
|
||||
putPublicAccessBlock(hc, bktName, &data.PublicAccessBlockConfiguration{
|
||||
BlockPublicPolicy: true,
|
||||
}, nil)
|
||||
|
||||
putBucketPolicy(hc, bktName, iam.Policy{ // public policy
|
||||
Version: "2012-10-17",
|
||||
Statement: []iam.Statement{
|
||||
{
|
||||
Principal: map[iam.PrincipalType][]string{iam.Wildcard: {}},
|
||||
Effect: "Allow",
|
||||
Action: []string{"s3:*"},
|
||||
Resource: []string{fmt.Sprintf("arn:aws:s3:::%s/*", bktName)},
|
||||
},
|
||||
},
|
||||
}, apierr.ErrAccessDenied)
|
||||
|
||||
key, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
hc.Handler().frostfsid.(*frostfsidMock).data["devenv"] = key.PublicKey()
|
||||
putBucketPolicy(hc, bktName, iam.Policy{ // non-public policy
|
||||
Version: "2012-10-17",
|
||||
Statement: []iam.Statement{
|
||||
{
|
||||
Principal: map[iam.PrincipalType][]string{iam.AWSPrincipalType: {"arn:aws:iam:::user/devenv"}},
|
||||
Effect: "Allow",
|
||||
Action: []string{"s3:*"},
|
||||
Resource: []string{fmt.Sprintf("arn:aws:s3:::%s/*", bktName)},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestIgnorePublicAcls(t *testing.T) {
|
||||
hc := prepareHandlerContext(t)
|
||||
bktName := "bucket-ignore-public-acls"
|
||||
|
||||
info := createBucket(hc, bktName)
|
||||
chains := hc.h.policyEngine.APE.(*apeMock).chainMap[engine.ContainerTarget(info.BktInfo.CID.EncodeToString())]
|
||||
require.Len(t, chains, 2)
|
||||
require.Len(t, chains[0].Rules, 0)
|
||||
require.Len(t, chains[1].Rules, 0)
|
||||
|
||||
putPublicAccessBlock(hc, bktName, &data.PublicAccessBlockConfiguration{
|
||||
IgnorePublicAcls: true,
|
||||
}, nil)
|
||||
chains = hc.h.policyEngine.APE.(*apeMock).chainMap[engine.ContainerTarget(info.BktInfo.CID.EncodeToString())]
|
||||
require.Len(t, chains, 2)
|
||||
require.Len(t, chains[0].Rules, 0)
|
||||
require.Len(t, chains[1].Rules, 0)
|
||||
|
||||
putBucketACL(hc, bktName, info.Box, map[string]string{api.AmzACL: basicACLPrivate})
|
||||
chains = hc.h.policyEngine.APE.(*apeMock).chainMap[engine.ContainerTarget(info.BktInfo.CID.EncodeToString())]
|
||||
require.Len(t, chains, 2)
|
||||
require.Len(t, chains[0].Rules, 0)
|
||||
require.Len(t, chains[1].Rules, 0)
|
||||
|
||||
putBucketACL(hc, bktName, info.Box, map[string]string{api.AmzACL: basicACLPublic})
|
||||
chains = hc.h.policyEngine.APE.(*apeMock).chainMap[engine.ContainerTarget(info.BktInfo.CID.EncodeToString())]
|
||||
require.Len(t, chains, 2)
|
||||
require.Len(t, chains[0].Rules, 0)
|
||||
require.Len(t, chains[1].Rules, 0)
|
||||
|
||||
putBucketACL(hc, bktName, info.Box, map[string]string{api.AmzACL: basicACLReadOnly})
|
||||
chains = hc.h.policyEngine.APE.(*apeMock).chainMap[engine.ContainerTarget(info.BktInfo.CID.EncodeToString())]
|
||||
require.Len(t, chains, 2)
|
||||
require.Len(t, chains[0].Rules, 0)
|
||||
require.Len(t, chains[1].Rules, 0)
|
||||
|
||||
putPublicAccessBlock(hc, bktName, &data.PublicAccessBlockConfiguration{
|
||||
IgnorePublicAcls: false,
|
||||
}, nil)
|
||||
chains = hc.h.policyEngine.APE.(*apeMock).chainMap[engine.ContainerTarget(info.BktInfo.CID.EncodeToString())]
|
||||
require.Len(t, chains, 2)
|
||||
require.Len(t, chains[0].Rules, 1)
|
||||
require.Len(t, chains[1].Rules, 1)
|
||||
|
||||
putBucketACL(hc, bktName, info.Box, map[string]string{api.AmzACL: basicACLPrivate})
|
||||
chains = hc.h.policyEngine.APE.(*apeMock).chainMap[engine.ContainerTarget(info.BktInfo.CID.EncodeToString())]
|
||||
require.Len(t, chains, 2)
|
||||
require.Len(t, chains[0].Rules, 0)
|
||||
require.Len(t, chains[1].Rules, 0)
|
||||
|
||||
putBucketACL(hc, bktName, info.Box, map[string]string{api.AmzACL: basicACLPublic})
|
||||
chains = hc.h.policyEngine.APE.(*apeMock).chainMap[engine.ContainerTarget(info.BktInfo.CID.EncodeToString())]
|
||||
require.Len(t, chains, 2)
|
||||
require.Len(t, chains[0].Rules, 1)
|
||||
require.Len(t, chains[1].Rules, 1)
|
||||
|
||||
putPublicAccessBlock(hc, bktName, &data.PublicAccessBlockConfiguration{
|
||||
IgnorePublicAcls: true,
|
||||
}, nil)
|
||||
require.Len(t, hc.h.policyEngine.APE.(*apeMock).chainMap[engine.ContainerTarget(info.BktInfo.CID.EncodeToString())], 0)
|
||||
|
||||
deletePublicAccessBlock(hc, bktName)
|
||||
chains = hc.h.policyEngine.APE.(*apeMock).chainMap[engine.ContainerTarget(info.BktInfo.CID.EncodeToString())]
|
||||
require.Len(t, chains, 2)
|
||||
require.Len(t, chains[0].Rules, 1)
|
||||
require.Len(t, chains[1].Rules, 1)
|
||||
}
|
||||
|
||||
func TestCheckRestrictPublicBuckets(t *testing.T) {
|
||||
hc := prepareHandlerContext(t)
|
||||
bktName := "bucket-restrict-public-buckets"
|
||||
|
||||
createBucket(hc, bktName)
|
||||
putPublicAccessBlock(hc, bktName, &data.PublicAccessBlockConfiguration{
|
||||
RestrictPublicBuckets: true,
|
||||
}, nil)
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = middleware.SetReqInfo(ctx, &middleware.ReqInfo{
|
||||
BucketName: bktName,
|
||||
})
|
||||
err := hc.Handler().CheckRestrictPublicBuckets(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
key, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
hc.Handler().frostfsid.(*frostfsidMock).data["devenv"] = key.PublicKey()
|
||||
putBucketPolicy(hc, bktName, iam.Policy{ // non-public policy
|
||||
Version: "2012-10-17",
|
||||
Statement: []iam.Statement{
|
||||
{
|
||||
Principal: map[iam.PrincipalType][]string{iam.AWSPrincipalType: {"arn:aws:iam:::user/devenv"}},
|
||||
Effect: "Allow",
|
||||
Action: []string{"s3:*"},
|
||||
Resource: []string{fmt.Sprintf("arn:aws:s3:::%s/*", bktName)},
|
||||
},
|
||||
},
|
||||
})
|
||||
err = hc.Handler().CheckRestrictPublicBuckets(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
putBucketPolicy(hc, bktName, iam.Policy{ // public policy
|
||||
Version: "2012-10-17",
|
||||
Statement: []iam.Statement{
|
||||
{
|
||||
Principal: map[iam.PrincipalType][]string{iam.Wildcard: {}},
|
||||
Effect: "Allow",
|
||||
Action: []string{"s3:*"},
|
||||
Resource: []string{fmt.Sprintf("arn:aws:s3:::%s/*", bktName)},
|
||||
},
|
||||
},
|
||||
})
|
||||
err = hc.Handler().CheckRestrictPublicBuckets(ctx)
|
||||
require.ErrorIs(t, err, apierr.GetAPIError(apierr.ErrAccessDenied))
|
||||
|
||||
ctx = middleware.SetReqInfo(ctx, &middleware.ReqInfo{
|
||||
BucketName: bktName,
|
||||
UserNamespace: ptr("namespace"),
|
||||
})
|
||||
err = hc.Handler().CheckRestrictPublicBuckets(ctx)
|
||||
require.ErrorIs(t, err, apierr.GetAPIError(apierr.ErrAccessDenied))
|
||||
|
||||
ctx = middleware.SetReqInfo(ctx, &middleware.ReqInfo{
|
||||
BucketName: bktName,
|
||||
UserNamespace: ptr(""),
|
||||
})
|
||||
err = hc.Handler().CheckRestrictPublicBuckets(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
deletePublicAccessBlock(hc, bktName)
|
||||
ctx = middleware.SetReqInfo(ctx, &middleware.ReqInfo{
|
||||
BucketName: bktName,
|
||||
})
|
||||
err = hc.Handler().CheckRestrictPublicBuckets(ctx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func putPublicAccessBlock(hc *handlerContext, bktName string, cfg *data.PublicAccessBlockConfiguration, headers map[string]string) {
|
||||
w := putPublicAccessBlockBase(hc, bktName, cfg, headers)
|
||||
assertStatus(hc.t, w, http.StatusOK)
|
||||
}
|
||||
|
||||
func putPublicAccessBlockErr(hc *handlerContext, bktName string, cfg *data.PublicAccessBlockConfiguration, headers map[string]string, err apierr.Error) {
|
||||
w := putPublicAccessBlockBase(hc, bktName, cfg, headers)
|
||||
assertS3Error(hc.t, w, err)
|
||||
}
|
||||
|
||||
func putPublicAccessBlockBase(hc *handlerContext, bktName string, cfg *data.PublicAccessBlockConfiguration, headers map[string]string) *httptest.ResponseRecorder {
|
||||
w, r := prepareTestRequest(hc, bktName, "", cfg)
|
||||
for k, v := range headers {
|
||||
r.Header.Set(k, v)
|
||||
}
|
||||
|
||||
hc.Handler().PutPublicAccessBlockHandler(w, r)
|
||||
return w
|
||||
}
|
||||
|
||||
func getPublicAccessBlock(hc *handlerContext, bktName string) *data.PublicAccessBlockConfiguration {
|
||||
w := getPublicAccessBlockBase(hc, bktName)
|
||||
assertStatus(hc.t, w, http.StatusOK)
|
||||
res := &data.PublicAccessBlockConfiguration{}
|
||||
parseTestResponse(hc.t, w, res)
|
||||
return res
|
||||
}
|
||||
|
||||
func getPublicAccessBlockErr(hc *handlerContext, bktName string, err apierr.Error) {
|
||||
w := getPublicAccessBlockBase(hc, bktName)
|
||||
assertS3Error(hc.t, w, err)
|
||||
}
|
||||
|
||||
func getPublicAccessBlockBase(hc *handlerContext, bktName string) *httptest.ResponseRecorder {
|
||||
w, r := prepareTestRequest(hc, bktName, "", nil)
|
||||
hc.Handler().GetPublicAccessBlockHandler(w, r)
|
||||
return w
|
||||
}
|
||||
|
||||
func deletePublicAccessBlock(hc *handlerContext, bktName string) {
|
||||
w := deletePublicAccessBlockBase(hc, bktName)
|
||||
assertStatus(hc.t, w, http.StatusNoContent)
|
||||
}
|
||||
|
||||
func deletePublicAccessBlockBase(hc *handlerContext, bktName string) *httptest.ResponseRecorder {
|
||||
w, r := prepareTestRequest(hc, bktName, "", nil)
|
||||
hc.Handler().DeletePublicAccessBlockHandler(w, r)
|
||||
return w
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue