forked from TrueCloudLab/frostfs-s3-gw
[#680] Move policy engine converter to s3-gw
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
e788bb6ec9
commit
0ba6989197
21 changed files with 4325 additions and 50 deletions
|
@ -19,6 +19,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
|
||||
s3common "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/policy-engine/common"
|
||||
engineiam "git.frostfs.info/TrueCloudLab/policy-engine/iam"
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
|
@ -177,7 +178,7 @@ func (h *handler) putBucketACLAPEHandler(w http.ResponseWriter, r *http.Request,
|
|||
}
|
||||
|
||||
chainRules := bucketCannedACLToAPERules(cannedACL, reqInfo, bktInfo.CID)
|
||||
if err = h.ape.SaveACLChains(bktInfo.CID.EncodeToString(), chainRules); err != nil {
|
||||
if err = h.policyEngine.APE.SaveACLChains(bktInfo.CID.EncodeToString(), chainRules); err != nil {
|
||||
h.logAndSendError(ctx, w, "failed to add morph rule chains", reqInfo, err)
|
||||
return
|
||||
}
|
||||
|
@ -248,7 +249,7 @@ func (h *handler) GetBucketPolicyStatusHandler(w http.ResponseWriter, r *http.Re
|
|||
return
|
||||
}
|
||||
|
||||
jsonPolicy, err := h.ape.GetBucketPolicy(reqInfo.Namespace, bktInfo.CID)
|
||||
jsonPolicy, err := h.policyEngine.APE.GetBucketPolicy(reqInfo.Namespace, bktInfo.CID)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
err = fmt.Errorf("%w: %s", errors.GetAPIError(errors.ErrNoSuchBucketPolicy), err.Error())
|
||||
|
@ -293,7 +294,7 @@ func (h *handler) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
jsonPolicy, err := h.ape.GetBucketPolicy(reqInfo.Namespace, bktInfo.CID)
|
||||
jsonPolicy, err := h.policyEngine.APE.GetBucketPolicy(reqInfo.Namespace, bktInfo.CID)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
err = fmt.Errorf("%w: %s", errors.GetAPIError(errors.ErrNoSuchBucketPolicy), err.Error())
|
||||
|
@ -323,7 +324,7 @@ func (h *handler) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Reque
|
|||
}
|
||||
|
||||
chainIDs := []chain.ID{getBucketChainID(chain.S3, bktInfo), getBucketChainID(chain.Ingress, bktInfo)}
|
||||
if err = h.ape.DeleteBucketPolicy(reqInfo.Namespace, bktInfo.CID, chainIDs); err != nil {
|
||||
if err = h.policyEngine.APE.DeleteBucketPolicy(reqInfo.Namespace, bktInfo.CID, chainIDs); err != nil {
|
||||
h.logAndSendError(ctx, w, "failed to delete policy from storage", reqInfo, err)
|
||||
return
|
||||
}
|
||||
|
@ -360,7 +361,7 @@ func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
var bktPolicy engineiam.Policy
|
||||
var bktPolicy s3common.Policy
|
||||
if err = json.Unmarshal(jsonPolicy, &bktPolicy); err != nil {
|
||||
h.logAndSendError(ctx, w, "could not parse bucket policy", reqInfo, err)
|
||||
return
|
||||
|
@ -372,7 +373,7 @@ func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
if len(stat.NotPrincipal) != 0 && stat.Effect == engineiam.AllowEffect {
|
||||
if len(stat.NotPrincipal) != 0 && stat.Effect == s3common.AllowEffect {
|
||||
h.logAndSendError(ctx, w, "invalid NotPrincipal", reqInfo, errors.GetAPIError(errors.ErrMalformedPolicyNotPrincipal))
|
||||
return
|
||||
}
|
||||
|
@ -385,14 +386,14 @@ func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
}
|
||||
|
||||
s3Chain, err := engineiam.ConvertToS3Chain(bktPolicy, h.frostfsid)
|
||||
s3Chain, err := h.policyEngine.Converter.ToS3Chain(bktPolicy, h.frostfsid)
|
||||
if err != nil {
|
||||
h.logAndSendError(ctx, w, "could not convert s3 policy to chain policy", reqInfo, err)
|
||||
return
|
||||
}
|
||||
s3Chain.ID = getBucketChainID(chain.S3, bktInfo)
|
||||
|
||||
nativeChain, err := engineiam.ConvertToNativeChain(bktPolicy, h.nativeResolver(reqInfo.Namespace, bktInfo))
|
||||
nativeChain, err := h.policyEngine.Converter.ToNativeChain(bktPolicy, h.nativeResolver(reqInfo.Namespace, bktInfo))
|
||||
if err == nil {
|
||||
nativeChain.ID = getBucketChainID(chain.Ingress, bktInfo)
|
||||
} else if !stderrors.Is(err, engineiam.ErrActionsNotApplicable) {
|
||||
|
@ -407,7 +408,7 @@ func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request)
|
|||
chainsToSave = append(chainsToSave, nativeChain)
|
||||
}
|
||||
|
||||
if err = h.ape.PutBucketPolicy(reqInfo.Namespace, bktInfo.CID, jsonPolicy, chainsToSave); err != nil {
|
||||
if err = h.policyEngine.APE.PutBucketPolicy(reqInfo.Namespace, bktInfo.CID, jsonPolicy, chainsToSave); err != nil {
|
||||
h.logAndSendError(ctx, w, "failed to update policy in contract", reqInfo, err)
|
||||
return
|
||||
}
|
||||
|
@ -419,15 +420,15 @@ type nativeResolver struct {
|
|||
bktInfo *data.BucketInfo
|
||||
}
|
||||
|
||||
func (n *nativeResolver) GetBucketInfo(bucket string) (*engineiam.BucketInfo, error) {
|
||||
func (n *nativeResolver) GetBucketInfo(bucket string) (*s3common.BucketInfo, error) {
|
||||
if n.bktInfo.Name != bucket {
|
||||
return nil, fmt.Errorf("invalid bucket %s: %w", bucket, errors.GetAPIError(errors.ErrMalformedPolicy))
|
||||
}
|
||||
|
||||
return &engineiam.BucketInfo{Namespace: n.namespace, Container: n.bktInfo.CID.EncodeToString()}, nil
|
||||
return &s3common.BucketInfo{Namespace: n.namespace, Container: n.bktInfo.CID.EncodeToString()}, nil
|
||||
}
|
||||
|
||||
func (h *handler) nativeResolver(ns string, bktInfo *data.BucketInfo) engineiam.NativeResolver {
|
||||
func (h *handler) nativeResolver(ns string, bktInfo *data.BucketInfo) s3common.NativeResolver {
|
||||
return &nativeResolver{
|
||||
FrostFSID: h.frostfsid,
|
||||
namespace: ns,
|
||||
|
|
|
@ -189,14 +189,14 @@ func TestDeleteBucketWithPolicy(t *testing.T) {
|
|||
|
||||
putBucketPolicy(hc, bktName, newPolicy)
|
||||
|
||||
require.Len(t, hc.h.ape.(*apeMock).policyMap, 1)
|
||||
require.Len(t, hc.h.ape.(*apeMock).chainMap[engine.ContainerTarget(bi.CID.EncodeToString())], 4)
|
||||
require.Len(t, hc.h.policyEngine.APE.(*apeMock).policyMap, 1)
|
||||
require.Len(t, hc.h.policyEngine.APE.(*apeMock).chainMap[engine.ContainerTarget(bi.CID.EncodeToString())], 4)
|
||||
|
||||
hc.owner = bi.Owner
|
||||
deleteBucket(t, hc, bktName, http.StatusNoContent)
|
||||
|
||||
require.Empty(t, hc.h.ape.(*apeMock).policyMap)
|
||||
chains, err := hc.h.ape.(*apeMock).ListChains(engine.ContainerTarget(bi.CID.EncodeToString()))
|
||||
require.Empty(t, hc.h.policyEngine.APE.(*apeMock).policyMap)
|
||||
chains, err := hc.h.policyEngine.APE.(*apeMock).ListChains(engine.ContainerTarget(bi.CID.EncodeToString()))
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, chains)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-mfa/mfa"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
policyengine "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/policy-engine"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||
|
@ -20,12 +21,12 @@ import (
|
|||
|
||||
type (
|
||||
handler struct {
|
||||
log *zap.Logger
|
||||
obj *layer.Layer
|
||||
cfg Config
|
||||
ape APE
|
||||
frostfsid FrostFSID
|
||||
mfa *mfa.Manager
|
||||
log *zap.Logger
|
||||
obj *layer.Layer
|
||||
cfg Config
|
||||
policyEngine PolicyEngine
|
||||
frostfsid FrostFSID
|
||||
mfa *mfa.Manager
|
||||
}
|
||||
|
||||
// Config contains data which handler needs to keep.
|
||||
|
@ -54,6 +55,11 @@ type (
|
|||
GetUserKey(account, name string) (string, error)
|
||||
}
|
||||
|
||||
PolicyEngine struct {
|
||||
APE APE
|
||||
Converter *policyengine.Converter
|
||||
}
|
||||
|
||||
// APE is Access Policy Engine that needs to save policy and acl info to different places.
|
||||
APE interface {
|
||||
PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, chains []*chain.Chain) error
|
||||
|
@ -73,14 +79,14 @@ const (
|
|||
var _ api.Handler = (*handler)(nil)
|
||||
|
||||
// New creates new api.Handler using given logger and client.
|
||||
func New(log *zap.Logger, obj *layer.Layer, cfg Config, storage APE, ffsid FrostFSID, mfaMgr *mfa.Manager) (api.Handler, error) {
|
||||
func New(log *zap.Logger, obj *layer.Layer, cfg Config, policyEngine PolicyEngine, ffsid FrostFSID, mfaMgr *mfa.Manager) (api.Handler, error) {
|
||||
switch {
|
||||
case obj == nil:
|
||||
return nil, errors.New("empty FrostFS Object Layer")
|
||||
case log == nil:
|
||||
return nil, errors.New("empty logger")
|
||||
case storage == nil:
|
||||
return nil, errors.New("empty policy storage")
|
||||
case policyEngine.APE == nil || policyEngine.Converter == nil:
|
||||
return nil, errors.New("empty policy engine")
|
||||
case ffsid == nil:
|
||||
return nil, errors.New("empty frostfsid")
|
||||
case mfaMgr == nil:
|
||||
|
@ -88,12 +94,12 @@ func New(log *zap.Logger, obj *layer.Layer, cfg Config, storage APE, ffsid Frost
|
|||
}
|
||||
|
||||
return &handler{
|
||||
log: log,
|
||||
obj: obj,
|
||||
cfg: cfg,
|
||||
ape: storage,
|
||||
frostfsid: ffsid,
|
||||
mfa: mfaMgr,
|
||||
log: log,
|
||||
obj: obj,
|
||||
cfg: cfg,
|
||||
policyEngine: policyEngine,
|
||||
frostfsid: ffsid,
|
||||
mfa: mfaMgr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -345,7 +345,7 @@ func (h *handler) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
|
|||
getBucketCannedChainID(chain.S3, bktInfo.CID),
|
||||
getBucketCannedChainID(chain.Ingress, bktInfo.CID),
|
||||
}
|
||||
if err = h.ape.DeleteBucketPolicy(reqInfo.Namespace, bktInfo.CID, chainIDs); err != nil {
|
||||
if err = h.policyEngine.APE.DeleteBucketPolicy(reqInfo.Namespace, bktInfo.CID, chainIDs); err != nil {
|
||||
h.logAndSendError(ctx, w, "failed to delete policy from storage", reqInfo, err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/resolver"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
intmfa "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/mfa"
|
||||
policyengine "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/policy-engine"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/service/tree"
|
||||
bearertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer/test"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
|
@ -287,10 +288,15 @@ func prepareHandlerContextBase(config *handlerConfig, log *zap.Logger) (*handler
|
|||
placementPolicies: make(map[string]netmap.PlacementPolicy),
|
||||
}
|
||||
h := &handler{
|
||||
log: log,
|
||||
obj: layer.NewLayer(ctx, log, tp, layerCfg),
|
||||
cfg: cfg,
|
||||
ape: newAPEMock(),
|
||||
log: log,
|
||||
obj: layer.NewLayer(ctx, log, tp, layerCfg),
|
||||
cfg: cfg,
|
||||
policyEngine: PolicyEngine{
|
||||
APE: newAPEMock(),
|
||||
Converter: policyengine.NewConverter(policyengine.Config{
|
||||
VersionFetcher: apeConverterMock{version: policyengine.V1},
|
||||
}),
|
||||
},
|
||||
frostfsid: newFrostfsIDMock(),
|
||||
}
|
||||
|
||||
|
@ -377,6 +383,14 @@ func getMinCacheConfig(logger *zap.Logger) *layer.CachesConfig {
|
|||
}
|
||||
}
|
||||
|
||||
type apeConverterMock struct {
|
||||
version policyengine.ConverterVersion
|
||||
}
|
||||
|
||||
func (a apeConverterMock) ConverterVersion() policyengine.ConverterVersion {
|
||||
return a.version
|
||||
}
|
||||
|
||||
type apeMock struct {
|
||||
chainMap map[engine.Target][]*chain.Chain
|
||||
policyMap map[string][]byte
|
||||
|
|
|
@ -860,7 +860,7 @@ func (h *handler) createBucketHandlerPolicy(w http.ResponseWriter, r *http.Reque
|
|||
zap.Stringer("container_id", bktInfo.CID), logs.TagField(logs.TagExternalStorage))
|
||||
|
||||
chains := bucketCannedACLToAPERules(cannedACL, reqInfo, bktInfo.CID)
|
||||
if err = h.ape.SaveACLChains(bktInfo.CID.EncodeToString(), chains); err != nil {
|
||||
if err = h.policyEngine.APE.SaveACLChains(bktInfo.CID.EncodeToString(), chains); err != nil {
|
||||
cleanErr := h.cleanupBucketCreation(ctx, reqInfo, bktInfo, boxData, chains)
|
||||
h.logAndSendError(ctx, w, "failed to add morph rule chain", reqInfo, err, zap.NamedError("cleanup_error", cleanErr))
|
||||
return
|
||||
|
@ -913,7 +913,7 @@ func (h *handler) cleanupBucketCreation(ctx context.Context, reqInfo *middleware
|
|||
chainIDs[i] = c.ID
|
||||
}
|
||||
|
||||
if err := h.ape.DeleteBucketPolicy(reqInfo.Namespace, bktInfo.CID, chainIDs); err != nil {
|
||||
if err := h.policyEngine.APE.DeleteBucketPolicy(reqInfo.Namespace, bktInfo.CID, chainIDs); err != nil {
|
||||
return fmt.Errorf("delete bucket acl policy: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -1006,7 +1006,7 @@ func TestCreateBucketWithoutPermissions(t *testing.T) {
|
|||
hc := prepareHandlerContext(t)
|
||||
bktName := "bkt-name"
|
||||
|
||||
hc.h.ape.(*apeMock).err = errors.New("no permissions")
|
||||
hc.h.policyEngine.APE.(*apeMock).err = errors.New("no permissions")
|
||||
|
||||
box, _ := createAccessBox(t)
|
||||
createBucketAssertS3Error(hc, bktName, box, apierr.ErrInternalError)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue