package ape import ( "fmt" "testing" policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" "github.com/stretchr/testify/require" ) func TestParseAPERule(t *testing.T) { tests := [...]struct { name string rule string expectErr error expectRule policyengine.Rule }{ { name: "Valid allow rule for all objects", rule: "allow Object.Put *", expectRule: policyengine.Rule{ Status: policyengine.Allow, Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}}, Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatAllObjects}}, }, }, { name: "Valid rule for all objects in implicit root namespace", rule: "allow Object.Put /*", expectRule: policyengine.Rule{ Status: policyengine.Allow, Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}}, Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}}, }, }, { name: "Valid rule for all objects in explicit root namespace", rule: "allow Object.Put root/*", expectRule: policyengine.Rule{ Status: policyengine.Allow, Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}}, Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}}, }, }, { name: "Valid rule for all objects in root namespace and container", rule: "allow Object.Put /cid/*", expectRule: policyengine.Rule{ Status: policyengine.Allow, Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}}, Resources: policyengine.Resources{Names: []string{ fmt.Sprintf(nativeschema.ResourceFormatRootContainerObjects, "cid"), }}, }, }, { name: "Valid rule for object in root namespace and container", rule: "allow Object.Put /cid/oid", expectRule: policyengine.Rule{ Status: policyengine.Allow, Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}}, Resources: policyengine.Resources{Names: []string{ fmt.Sprintf(nativeschema.ResourceFormatRootContainerObject, "cid", "oid"), }}, }, }, { name: "Valid rule for all objects in namespace", rule: "allow Object.Put ns/*", expectRule: policyengine.Rule{ Status: policyengine.Allow, Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}}, Resources: policyengine.Resources{Names: []string{ fmt.Sprintf(nativeschema.ResourceFormatNamespaceObjects, "ns"), }}, }, }, { name: "Valid rule for all objects in namespace and container", rule: "allow Object.Put ns/cid/*", expectRule: policyengine.Rule{ Status: policyengine.Allow, Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}}, Resources: policyengine.Resources{Names: []string{ fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainerObjects, "ns", "cid"), }}, }, }, { name: "Valid rule for object in namespace and container", rule: "allow Object.Put ns/cid/oid", expectRule: policyengine.Rule{ Status: policyengine.Allow, Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}}, Resources: policyengine.Resources{Names: []string{ fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainerObject, "ns", "cid", "oid"), }}, }, }, { name: "Valid deny rule", rule: "deny Object.Put *", expectRule: policyengine.Rule{ Status: policyengine.AccessDenied, Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}}, Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatAllObjects}}, }, }, { name: "Valid deny rule with action detail", rule: "deny:QuotaLimitReached Object.Put *", expectRule: policyengine.Rule{ Status: policyengine.QuotaLimitReached, Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}}, Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatAllObjects}}, }, }, { name: "Valid allow rule with conditions", rule: "allow Object.Get ResourceCondition:Department=HR RequestCondition:Actor!=ownerA *", expectRule: policyengine.Rule{ Status: policyengine.Allow, Actions: policyengine.Actions{Names: []string{nativeschema.MethodGetObject}}, Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatAllObjects}}, Condition: []policyengine.Condition{ { Op: policyengine.CondStringEquals, Kind: policyengine.KindResource, Key: "Department", Value: "HR", }, { Op: policyengine.CondStringNotEquals, Kind: policyengine.KindRequest, Key: "Actor", Value: "ownerA", }, }, }, }, { name: "Valid rule for object with conditions with action detail", rule: "deny:QuotaLimitReached Object.Get ResourceCondition:Department=HR RequestCondition:Actor!=ownerA *", expectRule: policyengine.Rule{ Status: policyengine.QuotaLimitReached, Actions: policyengine.Actions{Names: []string{nativeschema.MethodGetObject}}, Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatAllObjects}}, Condition: []policyengine.Condition{ { Op: policyengine.CondStringEquals, Kind: policyengine.KindResource, Key: "Department", Value: "HR", }, { Op: policyengine.CondStringNotEquals, Kind: policyengine.KindRequest, Key: "Actor", Value: "ownerA", }, }, }, }, { name: "Invalid rule with unknown status", rule: "permit Object.Put *", expectErr: errUnknownStatus, }, { name: "Invalid rule with unknown action", rule: "allow Object.PutOut *", expectErr: errUnknownAction, }, { name: "Invalid rule with unknown status detail", rule: "deny:UnknownActionDetail Object.Put *", expectErr: errUnknownStatusDetail, }, { name: "Invalid rule with unknown condition binary operator", rule: "deny Object.Put ResourceCondition:Department