diff --git a/api/handler/acl.go b/api/handler/acl.go index 36932ea..876f460 100644 --- a/api/handler/acl.go +++ b/api/handler/acl.go @@ -681,7 +681,7 @@ func (h *handler) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Reque return } - if err = h.ape.DeleteBucketPolicy(reqInfo.Namespace, bktInfo.CID, getBucketChainID(bktInfo)); err != nil { + if err = h.ape.DeleteBucketPolicy(reqInfo.Namespace, bktInfo.CID, getBucketChainID(chain.S3, bktInfo)); err != nil { h.logAndSendError(w, "failed to delete policy from storage", reqInfo, err) return } @@ -721,15 +721,13 @@ func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) return } - s3Chain, err := engineiam.ConvertToS3Chain(bktPolicy, h.frostfsid) - if err != nil { - h.logAndSendError(w, "could not convert s3 policy to chain policy", reqInfo, err) - return - } - s3Chain.ID = getBucketChainID(bktInfo) + for _, stat := range bktPolicy.Statement { + if len(stat.NotResource) != 0 { + h.logAndSendError(w, "policy resource mismatched bucket", reqInfo, errors.GetAPIError(errors.ErrMalformedPolicy)) + return + } - for _, rule := range s3Chain.Rules { - for _, resource := range rule.Resources.Names { + for _, resource := range stat.Resource { if reqInfo.BucketName != strings.Split(strings.TrimPrefix(resource, arnAwsPrefix), "/")[0] { h.logAndSendError(w, "policy resource mismatched bucket", reqInfo, errors.GetAPIError(errors.ErrMalformedPolicy)) return @@ -737,14 +735,50 @@ func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) } } - if err = h.ape.PutBucketPolicy(reqInfo.Namespace, bktInfo.CID, jsonPolicy, s3Chain); err != nil { + nativeChain, err := engineiam.ConvertToNativeChain(bktPolicy, h.nativeResolver(reqInfo.Namespace, bktInfo)) + if err != nil { + h.logAndSendError(w, "could not convert s3 policy to native chain policy", reqInfo, err) + return + } + nativeChain.ID = getBucketChainID(chain.Ingress, bktInfo) + + s3Chain, err := engineiam.ConvertToS3Chain(bktPolicy, h.frostfsid) + if err != nil { + h.logAndSendError(w, "could not convert s3 policy to chain policy", reqInfo, err) + return + } + s3Chain.ID = getBucketChainID(chain.S3, bktInfo) + + if err = h.ape.PutBucketPolicy(reqInfo.Namespace, bktInfo.CID, jsonPolicy, []*chain.Chain{s3Chain, nativeChain}); err != nil { h.logAndSendError(w, "failed to update policy in contract", reqInfo, err) return } } -func getBucketChainID(bktInfo *data.BucketInfo) chain.ID { - return chain.ID(string(chain.S3) + ":bkt" + string(bktInfo.CID[:])) +type nativeResolver struct { + FrostFSID + namespace string + bktInfo *data.BucketInfo +} + +func (n *nativeResolver) GetBucketInfo(bucket string) (*engineiam.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 +} + +func (h *handler) nativeResolver(ns string, bktInfo *data.BucketInfo) engineiam.NativeResolver { + return &nativeResolver{ + FrostFSID: h.frostfsid, + namespace: ns, + bktInfo: bktInfo, + } +} + +func getBucketChainID(prefix chain.Name, bktInfo *data.BucketInfo) chain.ID { + return chain.ID(string(prefix) + ":bkt" + string(bktInfo.CID[:])) } func parseACLHeaders(header http.Header, key *keys.PublicKey) (*AccessControlPolicy, error) { diff --git a/api/handler/api.go b/api/handler/api.go index b7752a9..f058b83 100644 --- a/api/handler/api.go +++ b/api/handler/api.go @@ -51,11 +51,12 @@ type ( FrostFSID interface { GetUserAddress(account, user string) (string, error) + GetUserKey(account, name string) (string, error) } // 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, s3Chain *chain.Chain) error + PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, chains []*chain.Chain) error DeleteBucketPolicy(ns string, cnrID cid.ID, chainID chain.ID) error GetBucketPolicy(ns string, cnrID cid.ID) ([]byte, error) SaveACLChains(ns string, chains []*chain.Chain) error @@ -101,6 +102,10 @@ func (f frostfsIDDisabled) GetUserAddress(_, _ string) (string, error) { return "", errors.New("frostfsid disabled") } +func (f frostfsIDDisabled) GetUserKey(account, name string) (string, error) { + return "", errors.New("frostfsid disabled") +} + // pickCopiesNumbers chooses the return values following this logic: // 1) array of copies numbers sent in request's header has the highest priority. // 2) array of copies numbers with corresponding location constraint provided in the config file. diff --git a/api/handler/handlers_test.go b/api/handler/handlers_test.go index f3bd5cc..259f787 100644 --- a/api/handler/handlers_test.go +++ b/api/handler/handlers_test.go @@ -261,11 +261,18 @@ func (a *apeMock) DeletePolicy(namespace string, cnrID cid.ID) error { return nil } -func (a *apeMock) PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, s3Chain *chain.Chain) error { +func (a *apeMock) PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, chain []*chain.Chain) error { if err := a.PutPolicy(ns, cnrID, policy); err != nil { return err } - return a.AddChain(engine.NamespaceTarget(ns), s3Chain) + + for i := range chain { + if err := a.AddChain(engine.NamespaceTarget(ns), chain[i]); err != nil { + return err + } + } + + return nil } func (a *apeMock) DeleteBucketPolicy(ns string, cnrID cid.ID, chainID chain.ID) error { diff --git a/internal/frostfs/frostfsid/frostfsid.go b/internal/frostfs/frostfsid/frostfsid.go index b32060b..0d3a1e7 100644 --- a/internal/frostfs/frostfsid/frostfsid.go +++ b/internal/frostfs/frostfsid/frostfsid.go @@ -2,6 +2,7 @@ package frostfsid import ( "context" + "encoding/hex" "fmt" "strconv" "strings" @@ -100,6 +101,15 @@ func (f *FrostFSID) GetUserAddress(namespace, name string) (string, error) { return key.Address(), nil } +func (f *FrostFSID) GetUserKey(account, name string) (string, error) { + key, err := f.cli.GetSubjectKeyByName(account, name) + if err != nil { + return "", err + } + + return hex.EncodeToString(key.Bytes()), nil +} + func (f *FrostFSID) GetUserGroupIDs(userHash util.Uint160) ([]string, error) { subjExt, err := f.cli.GetSubjectExtended(userHash) if err != nil { diff --git a/internal/frostfs/policy/morph_rule_chain_storage.go b/internal/frostfs/policy/morph_rule_chain_storage.go index f1ec9c4..f53210b 100644 --- a/internal/frostfs/policy/morph_rule_chain_storage.go +++ b/internal/frostfs/policy/morph_rule_chain_storage.go @@ -73,13 +73,16 @@ func (c *MorphRuleChainStorage) ListMorphRuleChains(name chain.Name, target engi return list, nil } -func (c *MorphRuleChainStorage) PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, s3Chain *chain.Chain) error { +func (c *MorphRuleChainStorage) PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, chains []*chain.Chain) error { c.cache.Delete(cache.MorphPolicyCacheKey{Target: engine.NamespaceTarget(ns), Name: chain.S3}) tx := c.contract.StartTx() - tx.AddChain(policycontract.Namespace, ns, s3Chain.ID, s3Chain.Bytes()) tx.AddChain(policycontract.IAM, ns, getBucketPolicyName(cnrID), policy) + for i := range chains { + tx.AddChain(policycontract.Namespace, ns, chains[i].ID, chains[i].Bytes()) + } + return c.contract.SendTx(tx) } diff --git a/internal/frostfs/policy/storage.go b/internal/frostfs/policy/storage.go index 4426fde..54f4b16 100644 --- a/internal/frostfs/policy/storage.go +++ b/internal/frostfs/policy/storage.go @@ -66,8 +66,8 @@ func (s *Storage) LocalStorage() engine.LocalOverrideStorage { return s.local } -func (s *Storage) PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, s3Chain *chain.Chain) error { - return s.morph.PutBucketPolicy(ns, cnrID, policy, s3Chain) +func (s *Storage) PutBucketPolicy(ns string, cnrID cid.ID, policy []byte, policyChains []*chain.Chain) error { + return s.morph.PutBucketPolicy(ns, cnrID, policy, policyChains) } func (s *Storage) DeleteBucketPolicy(ns string, cnrID cid.ID, chainID chain.ID) error { @@ -81,8 +81,3 @@ func (s *Storage) GetBucketPolicy(ns string, cnrID cid.ID) ([]byte, error) { func (s *Storage) SaveACLChains(ns string, chains []*chain.Chain) error { return s.morph.SaveACLChains(ns, chains) } - -// -//func (s *Storage) ListChains(target engine.Target) ([]*chain.Chain, error) { -// return s.morph.ListMorphRuleChains(target) -//}