diff --git a/api/middleware/policy.go b/api/middleware/policy.go index 325dfb9..9001820 100644 --- a/api/middleware/policy.go +++ b/api/middleware/policy.go @@ -33,7 +33,7 @@ type PolicySettings interface { } type FrostFSIDInformer interface { - GetUserGroupIDs(userHash util.Uint160) ([]string, error) + GetUserGroupIDsAndTags(userHash util.Uint160) ([]string, map[string]string, error) } // BucketResolveFunc is a func to resolve bucket info by name. @@ -119,6 +119,7 @@ func getPolicyRequest(r *http.Request, frostfsid FrostFSIDInformer, reqType ReqT var ( owner string groups []string + tags map[string]string ) ctx := r.Context() @@ -130,7 +131,7 @@ func getPolicyRequest(r *http.Request, frostfsid FrostFSIDInformer, reqType ReqT } owner = pk.Address() - groups, err = frostfsid.GetUserGroupIDs(pk.GetScriptHash()) + groups, tags, err = frostfsid.GetUserGroupIDsAndTags(pk.GetScriptHash()) if err != nil { return nil, fmt.Errorf("get group ids: %w", err) } @@ -145,7 +146,7 @@ func getPolicyRequest(r *http.Request, frostfsid FrostFSIDInformer, reqType ReqT res = fmt.Sprintf(s3.ResourceFormatS3Bucket, bktName) } - properties := determineProperties(ctx, reqType, op, owner, groups) + properties := determineProperties(ctx, reqType, op, owner, groups, tags) reqLogOrDefault(r.Context(), log).Debug(logs.PolicyRequest, zap.String("action", op), zap.String("resource", res), zap.Any("properties", properties)) @@ -376,12 +377,17 @@ func determineGeneralOperation(r *http.Request) string { return "UnmatchedOperation" } -func determineProperties(ctx context.Context, reqType ReqType, op, owner string, groups []string) map[string]string { +func determineProperties(ctx context.Context, reqType ReqType, op, owner string, groups []string, tags map[string]string) map[string]string { res := map[string]string{ s3.PropertyKeyOwner: owner, common.PropertyKeyFrostFSIDGroupID: chain.FormCondSliceContainsValue(groups), } - queries := GetReqInfo(ctx).URL.Query() + reqInfo := GetReqInfo(ctx) + queries := reqInfo.URL.Query() + + for k, v := range tags { + res[fmt.Sprintf(common.PropertyKeyFormatFrostFSIDUserClaim, k)] = v + } if reqType == objectType { if versionID := queries.Get(QueryVersionID); len(versionID) > 0 { diff --git a/api/router_mock_test.go b/api/router_mock_test.go index e8fe457..6eff1d6 100644 --- a/api/router_mock_test.go +++ b/api/router_mock_test.go @@ -82,8 +82,11 @@ func (f *frostFSIDMock) ValidatePublicKey(*keys.PublicKey) error { return nil } -func (f *frostFSIDMock) GetUserGroupIDs(util.Uint160) ([]string, error) { - return []string{}, nil +func (f *frostFSIDMock) GetUserGroupIDsAndTags(util.Uint160) ([]string, map[string]string, error) { + tags := make(map[string]string) + tags["test"] = "user" + tags["tag-test"] = "test" + return []string{}, tags, nil } type handlerMock struct { diff --git a/api/router_test.go b/api/router_test.go index c272208..9760169 100644 --- a/api/router_test.go +++ b/api/router_test.go @@ -250,6 +250,14 @@ func TestDefaultBehaviorPolicyChecker(t *testing.T) { createBucketErr(chiRouter, ns, bktName, apiErrors.ErrAccessDenied) } +func TestDefaultPolicyCheckerWithUserTags(t *testing.T) { + chiRouter := prepareRouter(t) + ns, bktName := "", "bucket" + + // check we can access bucket if rules not found + createBucket(chiRouter, ns, bktName) +} + func TestACLAPE(t *testing.T) { t.Run("acl disabled, ape deny by default", func(t *testing.T) { router := prepareRouter(t) diff --git a/internal/frostfs/frostfsid/frostfsid.go b/internal/frostfs/frostfsid/frostfsid.go index 0d3a1e7..cb5a9eb 100644 --- a/internal/frostfs/frostfsid/frostfsid.go +++ b/internal/frostfs/frostfsid/frostfsid.go @@ -110,13 +110,13 @@ func (f *FrostFSID) GetUserKey(account, name string) (string, error) { return hex.EncodeToString(key.Bytes()), nil } -func (f *FrostFSID) GetUserGroupIDs(userHash util.Uint160) ([]string, error) { +func (f *FrostFSID) GetUserGroupIDsAndTags(userHash util.Uint160) ([]string, map[string]string, error) { subjExt, err := f.cli.GetSubjectExtended(userHash) if err != nil { if strings.Contains(err.Error(), "not found") { - return nil, nil + return nil, nil, err } - return nil, err + return nil, nil, err } res := make([]string, len(subjExt.Groups)) @@ -124,5 +124,12 @@ func (f *FrostFSID) GetUserGroupIDs(userHash util.Uint160) ([]string, error) { res[i] = strconv.FormatInt(group.ID, 10) } - return res, nil + tags := make(map[string]string) + for k, v := range subjExt.KV { + if strings.HasPrefix(k, "tag-") { + tags[k] = v + } + } + + return res, tags, nil }