forked from TrueCloudLab/frostfs-node
[#876] cli: Add support for container
in local rules
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
This commit is contained in:
parent
b6fc3321c5
commit
51d1d935ef
5 changed files with 351 additions and 117 deletions
|
@ -13,21 +13,25 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
errInvalidStatementFormat = errors.New("invalid statement format")
|
||||
errInvalidConditionFormat = errors.New("invalid condition format")
|
||||
errUnknownAction = errors.New("action is not recognized")
|
||||
errUnknownOperation = errors.New("operation is not recognized")
|
||||
errUnknownActionDetail = errors.New("action detail is not recognized")
|
||||
errUnknownBinaryOperator = errors.New("binary operator is not recognized")
|
||||
errUnknownCondObjectType = errors.New("condition object type is not recognized")
|
||||
errInvalidStatementFormat = errors.New("invalid statement format")
|
||||
errInvalidConditionFormat = errors.New("invalid condition format")
|
||||
errUnknownStatus = errors.New("status is not recognized")
|
||||
errUnknownStatusDetail = errors.New("status detail is not recognized")
|
||||
errUnknownAction = errors.New("action is not recognized")
|
||||
errUnknownBinaryOperator = errors.New("binary operator is not recognized")
|
||||
errUnknownCondObjectType = errors.New("condition object type is not recognized")
|
||||
errMixedTypesInRule = errors.New("found mixed type of actions and conditions in rule")
|
||||
errNoActionsInRule = errors.New("there are no actions in rule")
|
||||
errUnsupportedResourceFormat = errors.New("unsupported resource format")
|
||||
)
|
||||
|
||||
// PrintHumanReadableAPEChain print APE chain rules.
|
||||
func PrintHumanReadableAPEChain(cmd *cobra.Command, chain *apechain.Chain) {
|
||||
cmd.Println("ChainID: " + string(chain.ID))
|
||||
cmd.Println("Chain ID: " + string(chain.ID))
|
||||
cmd.Printf(" HEX: %x\n", chain.ID)
|
||||
cmd.Println("Rules:")
|
||||
for _, rule := range chain.Rules {
|
||||
cmd.Println("\tStatus: " + rule.Status.String())
|
||||
cmd.Println("\n\tStatus: " + rule.Status.String())
|
||||
cmd.Println("\tAny: " + strconv.FormatBool(rule.Any))
|
||||
cmd.Println("\tConditions:")
|
||||
for _, c := range rule.Condition {
|
||||
|
@ -71,7 +75,7 @@ func ParseAPEChain(chain *apechain.Chain, rules []string) error {
|
|||
}
|
||||
|
||||
// ParseAPERule parses access-policy-engine statement from the following form:
|
||||
// <action>[:action_detail] <operation> [<condition1> ...] <resource>
|
||||
// <status>[:status_detail] <action>... [<condition>...] <resource>...
|
||||
//
|
||||
// Examples:
|
||||
// deny Object.Put *
|
||||
|
@ -99,116 +103,178 @@ func parseRuleLexemes(r *apechain.Rule, lexemes []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
r.Actions, err = parseAction(lexemes[1])
|
||||
if err != nil {
|
||||
return err
|
||||
var isObject *bool
|
||||
for i, lexeme := range lexemes[1:] {
|
||||
var name string
|
||||
var actionType bool
|
||||
name, actionType, err = parseAction(lexeme)
|
||||
if err != nil {
|
||||
condition, errCond := parseCondition(lexeme)
|
||||
if errCond != nil {
|
||||
err = fmt.Errorf("%w:%w", err, errCond)
|
||||
lexemes = lexemes[i+1:]
|
||||
break
|
||||
}
|
||||
actionType = condition.Object == apechain.ObjectResource || condition.Object == apechain.ObjectRequest
|
||||
r.Condition = append(r.Condition, *condition)
|
||||
} else {
|
||||
r.Actions.Names = append(r.Actions.Names, name)
|
||||
}
|
||||
if isObject == nil {
|
||||
isObject = &actionType
|
||||
} else if actionType != *isObject {
|
||||
return errMixedTypesInRule
|
||||
}
|
||||
}
|
||||
if len(r.Actions.Names) == 0 {
|
||||
return fmt.Errorf("%w:%w", err, errNoActionsInRule)
|
||||
}
|
||||
for _, lexeme := range lexemes {
|
||||
resource, errRes := parseResource(lexeme, *isObject)
|
||||
if errRes != nil {
|
||||
return fmt.Errorf("%w:%w", err, errRes)
|
||||
}
|
||||
r.Resources.Names = append(r.Resources.Names, resource)
|
||||
}
|
||||
|
||||
r.Condition, err = parseConditions(lexemes[2 : len(lexemes)-1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Resources, err = parseResource(lexemes[len(lexemes)-1])
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseStatus(lexeme string) (apechain.Status, error) {
|
||||
action, expression, found := strings.Cut(lexeme, ":")
|
||||
switch action = strings.ToLower(action); action {
|
||||
switch strings.ToLower(action) {
|
||||
case "deny":
|
||||
if !found {
|
||||
return apechain.AccessDenied, nil
|
||||
} else if strings.EqualFold(expression, "QuotaLimitReached") {
|
||||
return apechain.QuotaLimitReached, nil
|
||||
} else {
|
||||
return 0, fmt.Errorf("%w: %s", errUnknownActionDetail, expression)
|
||||
return 0, fmt.Errorf("%w: %s", errUnknownStatusDetail, expression)
|
||||
}
|
||||
case "allow":
|
||||
if found {
|
||||
return 0, errUnknownActionDetail
|
||||
return 0, errUnknownStatusDetail
|
||||
}
|
||||
return apechain.Allow, nil
|
||||
default:
|
||||
return 0, errUnknownAction
|
||||
return 0, errUnknownStatus
|
||||
}
|
||||
}
|
||||
|
||||
func parseAction(lexeme string) (apechain.Actions, error) {
|
||||
func parseAction(lexeme string) (string, bool, error) {
|
||||
switch strings.ToLower(lexeme) {
|
||||
case "object.put":
|
||||
return apechain.Actions{Names: []string{nativeschema.MethodPutObject}}, nil
|
||||
return nativeschema.MethodPutObject, true, nil
|
||||
case "object.get":
|
||||
return apechain.Actions{Names: []string{nativeschema.MethodGetObject}}, nil
|
||||
return nativeschema.MethodGetObject, true, nil
|
||||
case "object.head":
|
||||
return apechain.Actions{Names: []string{nativeschema.MethodHeadObject}}, nil
|
||||
return nativeschema.MethodHeadObject, true, nil
|
||||
case "object.delete":
|
||||
return apechain.Actions{Names: []string{nativeschema.MethodDeleteObject}}, nil
|
||||
return nativeschema.MethodDeleteObject, true, nil
|
||||
case "object.search":
|
||||
return apechain.Actions{Names: []string{nativeschema.MethodSearchObject}}, nil
|
||||
return nativeschema.MethodSearchObject, true, nil
|
||||
case "object.range":
|
||||
return apechain.Actions{Names: []string{nativeschema.MethodRangeObject}}, nil
|
||||
return nativeschema.MethodRangeObject, true, nil
|
||||
case "object.hash":
|
||||
return apechain.Actions{Names: []string{nativeschema.MethodHashObject}}, nil
|
||||
return nativeschema.MethodHashObject, true, nil
|
||||
case "container.put":
|
||||
return nativeschema.MethodPutContainer, false, nil
|
||||
case "container.delete":
|
||||
return nativeschema.MethodDeleteContainer, false, nil
|
||||
case "container.get":
|
||||
return nativeschema.MethodGetContainer, false, nil
|
||||
case "container.setcontainereacl":
|
||||
return nativeschema.MethodSetContainerEACL, false, nil
|
||||
case "container.getcontainereacl":
|
||||
return nativeschema.MethodGetContainerEACL, false, nil
|
||||
default:
|
||||
}
|
||||
return apechain.Actions{}, fmt.Errorf("%w: %s", errUnknownOperation, lexeme)
|
||||
return "", false, fmt.Errorf("%w: %s", errUnknownAction, lexeme)
|
||||
}
|
||||
|
||||
func parseResource(lexeme string) (apechain.Resources, error) {
|
||||
if lexeme == "*" {
|
||||
return apechain.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}}, nil
|
||||
func parseResource(lexeme string, isObj bool) (string, error) {
|
||||
if len(lexeme) > 0 && !strings.HasSuffix(lexeme, "/") {
|
||||
if isObj {
|
||||
if lexeme == "*" {
|
||||
return nativeschema.ResourceFormatAllObjects, nil
|
||||
} else if lexeme == "/*" {
|
||||
return nativeschema.ResourceFormatRootObjects, nil
|
||||
} else if strings.HasPrefix(lexeme, "/") {
|
||||
lexeme = lexeme[1:]
|
||||
delimCount := strings.Count(lexeme, "/")
|
||||
if delimCount == 1 && len(lexeme) >= 3 { // container/object
|
||||
return nativeschema.ObjectPrefix + "//" + lexeme, nil
|
||||
}
|
||||
} else {
|
||||
delimCount := strings.Count(lexeme, "/")
|
||||
if delimCount == 1 && len(lexeme) >= 3 ||
|
||||
delimCount == 2 && len(lexeme) >= 5 { // namespace/container/object
|
||||
return nativeschema.ObjectPrefix + "/" + lexeme, nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if lexeme == "*" {
|
||||
return nativeschema.ResourceFormatAllContainers, nil
|
||||
} else if lexeme == "/*" {
|
||||
return nativeschema.ResourceFormatRootContainers, nil
|
||||
} else if strings.HasPrefix(lexeme, "/") && len(lexeme) > 1 {
|
||||
lexeme = lexeme[1:]
|
||||
delimCount := strings.Count(lexeme, "/")
|
||||
if delimCount == 0 {
|
||||
return nativeschema.ContainerPrefix + "//" + lexeme, nil
|
||||
}
|
||||
} else {
|
||||
delimCount := strings.Count(lexeme, "/")
|
||||
if delimCount == 1 && len(lexeme) > 3 { // namespace/container
|
||||
return nativeschema.ContainerPrefix + "/" + lexeme, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return apechain.Resources{Names: []string{fmt.Sprintf(nativeschema.ResourceFormatRootContainerObjects, lexeme)}}, nil
|
||||
return "", errUnsupportedResourceFormat
|
||||
}
|
||||
|
||||
const (
|
||||
ObjectResource = "object.resource"
|
||||
ObjectRequest = "object.request"
|
||||
|
||||
ContainerResource = "container.resource"
|
||||
ContainerRequest = "container.request"
|
||||
)
|
||||
|
||||
var typeToCondObject = map[string]apechain.ObjectType{
|
||||
ObjectResource: apechain.ObjectResource,
|
||||
ObjectRequest: apechain.ObjectRequest,
|
||||
ObjectResource: apechain.ObjectResource,
|
||||
ObjectRequest: apechain.ObjectRequest,
|
||||
ContainerResource: apechain.ContainerResource,
|
||||
ContainerRequest: apechain.ContainerRequest,
|
||||
}
|
||||
|
||||
func parseConditions(lexemes []string) ([]apechain.Condition, error) {
|
||||
conds := make([]apechain.Condition, 0)
|
||||
func parseCondition(lexeme string) (*apechain.Condition, error) {
|
||||
typ, expression, found := strings.Cut(lexeme, ":")
|
||||
typ = strings.ToLower(typ)
|
||||
|
||||
for _, lexeme := range lexemes {
|
||||
typ, expression, found := strings.Cut(lexeme, ":")
|
||||
typ = strings.ToLower(typ)
|
||||
|
||||
objType, ok := typeToCondObject[typ]
|
||||
if ok {
|
||||
if !found {
|
||||
return nil, fmt.Errorf("%w: %s", errInvalidConditionFormat, lexeme)
|
||||
}
|
||||
|
||||
var lhs, rhs string
|
||||
var binExpFound bool
|
||||
|
||||
var cond apechain.Condition
|
||||
cond.Object = objType
|
||||
|
||||
lhs, rhs, binExpFound = strings.Cut(expression, "!=")
|
||||
if !binExpFound {
|
||||
lhs, rhs, binExpFound = strings.Cut(expression, "=")
|
||||
if !binExpFound {
|
||||
return nil, fmt.Errorf("%w: %s", errUnknownBinaryOperator, expression)
|
||||
}
|
||||
cond.Op = apechain.CondStringEquals
|
||||
} else {
|
||||
cond.Op = apechain.CondStringNotEquals
|
||||
}
|
||||
|
||||
cond.Key, cond.Value = lhs, rhs
|
||||
|
||||
conds = append(conds, cond)
|
||||
} else {
|
||||
return nil, fmt.Errorf("%w: %s", errUnknownCondObjectType, typ)
|
||||
objType, ok := typeToCondObject[typ]
|
||||
if ok {
|
||||
if !found {
|
||||
return nil, fmt.Errorf("%w: %s", errInvalidConditionFormat, lexeme)
|
||||
}
|
||||
}
|
||||
|
||||
return conds, nil
|
||||
var cond apechain.Condition
|
||||
cond.Object = objType
|
||||
|
||||
lhs, rhs, binExpFound := strings.Cut(expression, "!=")
|
||||
if !binExpFound {
|
||||
lhs, rhs, binExpFound = strings.Cut(expression, "=")
|
||||
if !binExpFound {
|
||||
return nil, fmt.Errorf("%w: %s", errUnknownBinaryOperator, expression)
|
||||
}
|
||||
cond.Op = apechain.CondStringEquals
|
||||
} else {
|
||||
cond.Op = apechain.CondStringNotEquals
|
||||
}
|
||||
|
||||
cond.Key, cond.Value = lhs, rhs
|
||||
return &cond, nil
|
||||
}
|
||||
return nil, fmt.Errorf("%w: %s", errUnknownCondObjectType, typ)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue