diff --git a/cmd/frostfs-cli/docs/policy.md b/cmd/frostfs-cli/docs/policy.md index 06c325c62..5e439f650 100644 --- a/cmd/frostfs-cli/docs/policy.md +++ b/cmd/frostfs-cli/docs/policy.md @@ -27,6 +27,7 @@ For container it can be represented as: - `/*` all containers in the `root` namespace Actions is a regular operations upon FrostFS containers/objects. Like `Object.Put`, `Container.Get` etc. +You can use `Object.*`, `Container.*` that implies all actions. In status section it is possible to use `allow`, `deny` or `deny:QuotaLimitReached` actions. diff --git a/cmd/frostfs-cli/modules/util/ape.go b/cmd/frostfs-cli/modules/util/ape.go index 6a7eca948..5234a47c3 100644 --- a/cmd/frostfs-cli/modules/util/ape.go +++ b/cmd/frostfs-cli/modules/util/ape.go @@ -82,6 +82,8 @@ func ParseAPEChain(chain *apechain.Chain, rules []string) error { // deny:QuotaLimitReached Object.Put * // allow Object.Put * // allow Object.Get Object.Resource:Department=HR Object.Request:Actor=ownerA * +// allow Object.* * +// allow Container.* * // //nolint:godot func ParseAPERule(r *apechain.Rule, rule string) error { @@ -92,6 +94,18 @@ func ParseAPERule(r *apechain.Rule, rule string) error { return parseRuleLexemes(r, lexemes) } +func unique(inputSlice []string) []string { + uniqueSlice := make([]string, 0, len(inputSlice)) + seen := make(map[string]bool, len(inputSlice)) + for _, element := range inputSlice { + if !seen[element] { + uniqueSlice = append(uniqueSlice, element) + seen[element] = true + } + } + return uniqueSlice +} + func parseRuleLexemes(r *apechain.Rule, lexemes []string) error { if len(lexemes) < 2 { return errInvalidStatementFormat @@ -105,9 +119,9 @@ func parseRuleLexemes(r *apechain.Rule, lexemes []string) error { var isObject *bool for i, lexeme := range lexemes[1:] { - var name string + var names []string var actionType bool - name, actionType, err = parseAction(lexeme) + names, actionType, err = parseAction(lexeme) if err != nil { condition, errCond := parseCondition(lexeme) if errCond != nil { @@ -118,7 +132,7 @@ func parseRuleLexemes(r *apechain.Rule, lexemes []string) error { actionType = condition.Object == apechain.ObjectResource || condition.Object == apechain.ObjectRequest r.Condition = append(r.Condition, *condition) } else { - r.Actions.Names = append(r.Actions.Names, name) + r.Actions.Names = append(r.Actions.Names, names...) } if isObject == nil { isObject = &actionType @@ -126,6 +140,7 @@ func parseRuleLexemes(r *apechain.Rule, lexemes []string) error { return errMixedTypesInRule } } + r.Actions.Names = unique(r.Actions.Names) if len(r.Actions.Names) == 0 { return fmt.Errorf("%w:%w", err, errNoActionsInRule) } @@ -161,35 +176,55 @@ func parseStatus(lexeme string) (apechain.Status, error) { } } -func parseAction(lexeme string) (string, bool, error) { +func parseAction(lexeme string) ([]string, bool, error) { switch strings.ToLower(lexeme) { case "object.put": - return nativeschema.MethodPutObject, true, nil + return []string{nativeschema.MethodPutObject}, true, nil case "object.get": - return nativeschema.MethodGetObject, true, nil + return []string{nativeschema.MethodGetObject}, true, nil case "object.head": - return nativeschema.MethodHeadObject, true, nil + return []string{nativeschema.MethodHeadObject}, true, nil case "object.delete": - return nativeschema.MethodDeleteObject, true, nil + return []string{nativeschema.MethodDeleteObject}, true, nil case "object.search": - return nativeschema.MethodSearchObject, true, nil + return []string{nativeschema.MethodSearchObject}, true, nil case "object.range": - return nativeschema.MethodRangeObject, true, nil + return []string{nativeschema.MethodRangeObject}, true, nil case "object.hash": - return nativeschema.MethodHashObject, true, nil + return []string{nativeschema.MethodHashObject}, true, nil + case "object.*": + return []string{ + nativeschema.MethodPutObject, + nativeschema.MethodGetObject, + nativeschema.MethodHeadObject, + nativeschema.MethodDeleteObject, + nativeschema.MethodSearchObject, + nativeschema.MethodHashObject, + }, true, nil case "container.put": - return nativeschema.MethodPutContainer, false, nil + return []string{nativeschema.MethodPutContainer}, false, nil case "container.delete": - return nativeschema.MethodDeleteContainer, false, nil + return []string{nativeschema.MethodDeleteContainer}, false, nil case "container.get": - return nativeschema.MethodGetContainer, false, nil + return []string{nativeschema.MethodGetContainer}, false, nil case "container.setcontainereacl": - return nativeschema.MethodSetContainerEACL, false, nil + return []string{nativeschema.MethodSetContainerEACL}, false, nil case "container.getcontainereacl": - return nativeschema.MethodGetContainerEACL, false, nil + return []string{nativeschema.MethodGetContainerEACL}, false, nil + case "container.list": + return []string{nativeschema.MethodListContainers}, false, nil + case "container.*": + return []string{ + nativeschema.MethodPutContainer, + nativeschema.MethodDeleteContainer, + nativeschema.MethodGetContainer, + nativeschema.MethodSetContainerEACL, + nativeschema.MethodGetContainerEACL, + nativeschema.MethodListContainers, + }, false, nil default: } - return "", false, fmt.Errorf("%w: %s", errUnknownAction, lexeme) + return nil, false, fmt.Errorf("%w: %s", errUnknownAction, lexeme) } func parseResource(lexeme string, isObj bool) (string, error) {