package policyengine import ( "testing" "github.com/stretchr/testify/require" ) func TestInmemory(t *testing.T) { const ( object = "native::object::abc/xyz" container = "native::object::abc/*" namespace = "Tenant1" namespace2 = "Tenant2" actor1 = "owner1" actor2 = "owner2" ) s := NewInMemory() // Object which was put via S3. res := newResource(object, map[string]string{"FromS3": "true"}) // Request initiating from the trusted subnet and actor. reqGood := newRequest("native::object::put", res, map[string]string{ "SourceIP": "10.1.1.12", "Actor": actor1, }) status, ok := s.IsAllowed(Ingress, namespace, reqGood) require.Equal(t, NoRuleFound, status) require.False(t, ok) s.AddNameSpaceChain(Ingress, namespace, &Chain{ Rules: []Rule{ { // Restrict to remove ANY object from the namespace. Status: AccessDenied, Actions: Actions{Names: []string{"native::object::delete"}}, Resources: Resources{Names: []string{"native::object::*"}}, }, { // Allow to put object only from the trusted subnet AND trusted actor, deny otherwise. Status: AccessDenied, Actions: Actions{Names: []string{"native::object::put"}}, Resources: Resources{Names: []string{"native::object::*"}}, Any: true, Condition: []Condition{ { Op: CondStringNotLike, Object: ObjectRequest, Key: "SourceIP", Value: "10.1.1.*", }, { Op: CondStringNotEquals, Object: ObjectRequest, Key: "Actor", Value: actor1, }, }, }, }, }) s.AddNameSpaceChain(Ingress, namespace2, &Chain{ Rules: []Rule{ { // Deny all expect "native::object::get" for all objects expect "native::object::abc/xyz". Status: AccessDenied, Actions: Actions{Inverted: true, Names: []string{"native::object::get"}}, Resources: Resources{Inverted: true, Names: []string{object}}, }, }, }) s.AddResourceChain(Ingress, container, &Chain{ Rules: []Rule{ { // Allow to actor2 to get objects from the specific container only if they have `Department=HR` attribute. Status: Allow, Actions: Actions{Names: []string{"native::object::get"}}, Resources: Resources{Names: []string{"native::object::abc/*"}}, Condition: []Condition{ { Op: CondStringEquals, Object: ObjectResource, Key: "Department", Value: "HR", }, { Op: CondStringEquals, Object: ObjectRequest, Key: "Actor", Value: actor2, }, }, }, }, }) t.Run("bad subnet, namespace deny", func(t *testing.T) { // Request initiating from the untrusted subnet. reqBadIP := newRequest("native::object::put", res, map[string]string{ "SourceIP": "10.122.1.20", "Actor": actor1, }) status, ok := s.IsAllowed(Ingress, namespace, reqBadIP) require.Equal(t, AccessDenied, status) require.True(t, ok) }) t.Run("bad actor, namespace deny", func(t *testing.T) { // Request initiating from the untrusted actor. reqBadActor := newRequest("native::object::put", res, map[string]string{ "SourceIP": "10.1.1.13", "Actor": actor2, }) status, ok := s.IsAllowed(Ingress, namespace, reqBadActor) require.Equal(t, AccessDenied, status) require.True(t, ok) }) t.Run("bad object, container deny", func(t *testing.T) { objGood := newResource("native::object::abc/id1", map[string]string{"Department": "HR"}) objBadAttr := newResource("native::object::abc/id2", map[string]string{"Department": "Support"}) status, ok := s.IsAllowed(Ingress, namespace, newRequest("native::object::get", objGood, map[string]string{ "SourceIP": "10.1.1.14", "Actor": actor2, })) require.Equal(t, Allow, status) require.True(t, ok) status, ok = s.IsAllowed(Ingress, namespace, newRequest("native::object::get", objBadAttr, map[string]string{ "SourceIP": "10.1.1.14", "Actor": actor2, })) require.Equal(t, NoRuleFound, status) require.False(t, ok) }) t.Run("bad operation, namespace deny", func(t *testing.T) { // Request with the forbidden operation. reqBadOperation := newRequest("native::object::delete", res, map[string]string{ "SourceIP": "10.1.1.12", "Actor": actor1, }) status, ok := s.IsAllowed(Ingress, namespace, reqBadOperation) require.Equal(t, AccessDenied, status) require.True(t, ok) }) t.Run("inverted rules", func(t *testing.T) { req := newRequest("native::object::put", newResource(object, nil), nil) status, ok = s.IsAllowed(Ingress, namespace2, req) require.Equal(t, NoRuleFound, status) require.False(t, ok) req = newRequest("native::object::put", newResource("native::object::cba/def", nil), nil) status, ok = s.IsAllowed(Ingress, namespace2, req) require.Equal(t, AccessDenied, status) require.True(t, ok) req = newRequest("native::object::get", newResource("native::object::cba/def", nil), nil) status, ok = s.IsAllowed(Ingress, namespace2, req) require.Equal(t, NoRuleFound, status) require.False(t, ok) }) t.Run("good", func(t *testing.T) { status, ok = s.IsAllowed(Ingress, namespace, reqGood) require.Equal(t, NoRuleFound, status) require.False(t, ok) t.Run("quota on a different container", func(t *testing.T) { s.AddOverride(Ingress, &Chain{ Rules: []Rule{{ Status: QuotaLimitReached, Actions: Actions{Names: []string{"native::object::put"}}, Resources: Resources{Names: []string{"native::object::cba/*"}}, }}, }) status, ok = s.IsAllowed(Ingress, namespace, reqGood) require.Equal(t, NoRuleFound, status) require.False(t, ok) }) t.Run("quota on the request container", func(t *testing.T) { s.AddOverride(Ingress, &Chain{ Rules: []Rule{{ Status: QuotaLimitReached, Actions: Actions{Names: []string{"native::object::put"}}, Resources: Resources{Names: []string{"native::object::abc/*"}}, }}, }) status, ok = s.IsAllowed(Ingress, namespace, reqGood) require.Equal(t, QuotaLimitReached, status) require.True(t, ok) }) }) }