mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
499 lines
9.1 KiB
499 lines
9.1 KiB
// Copyright (c) HashiCorp, Inc. |
|
// SPDX-License-Identifier: BUSL-1.1 |
|
|
|
package acl |
|
|
|
import ( |
|
"fmt" |
|
"testing" |
|
|
|
"github.com/stretchr/testify/require" |
|
) |
|
|
|
func TestACL_Enforce(t *testing.T) { |
|
type testCase struct { |
|
method string |
|
resource Resource |
|
segment string |
|
access string |
|
ret EnforcementDecision |
|
err string |
|
} |
|
|
|
testName := func(t testCase) string { |
|
if t.segment != "" { |
|
return fmt.Sprintf("%s/%s/%s/%s", t.resource, t.segment, t.access, t.ret.String()) |
|
} |
|
return fmt.Sprintf("%s/%s/%s", t.resource, t.access, t.ret.String()) |
|
} |
|
|
|
cases := []testCase{ |
|
{ |
|
method: "ACLRead", |
|
resource: ResourceACL, |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "ACLRead", |
|
resource: ResourceACL, |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "ACLWrite", |
|
resource: ResourceACL, |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "ACLWrite", |
|
resource: ResourceACL, |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
resource: ResourceACL, |
|
access: "list", |
|
ret: Deny, |
|
err: "Invalid access level", |
|
}, |
|
{ |
|
method: "OperatorRead", |
|
resource: ResourceOperator, |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "OperatorRead", |
|
resource: ResourceOperator, |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "OperatorWrite", |
|
resource: ResourceOperator, |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "OperatorWrite", |
|
resource: ResourceOperator, |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
resource: ResourceOperator, |
|
access: "list", |
|
ret: Deny, |
|
err: "Invalid access level", |
|
}, |
|
{ |
|
method: "KeyringRead", |
|
resource: ResourceKeyring, |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "KeyringRead", |
|
resource: ResourceKeyring, |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "KeyringWrite", |
|
resource: ResourceKeyring, |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "KeyringWrite", |
|
resource: ResourceKeyring, |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
resource: ResourceKeyring, |
|
access: "list", |
|
ret: Deny, |
|
err: "Invalid access level", |
|
}, |
|
{ |
|
method: "AgentRead", |
|
resource: ResourceAgent, |
|
segment: "foo", |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "AgentRead", |
|
resource: ResourceAgent, |
|
segment: "foo", |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "AgentWrite", |
|
resource: ResourceAgent, |
|
segment: "foo", |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "AgentWrite", |
|
resource: ResourceAgent, |
|
segment: "foo", |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
resource: ResourceAgent, |
|
segment: "foo", |
|
access: "list", |
|
ret: Deny, |
|
err: "Invalid access level", |
|
}, |
|
{ |
|
method: "EventRead", |
|
resource: ResourceEvent, |
|
segment: "foo", |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "EventRead", |
|
resource: ResourceEvent, |
|
segment: "foo", |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "EventWrite", |
|
resource: ResourceEvent, |
|
segment: "foo", |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "EventWrite", |
|
resource: ResourceEvent, |
|
segment: "foo", |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
resource: ResourceEvent, |
|
segment: "foo", |
|
access: "list", |
|
ret: Deny, |
|
err: "Invalid access level", |
|
}, |
|
{ |
|
method: "IdentityRead", |
|
resource: ResourceIdentity, |
|
segment: "foo", |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "IdentityRead", |
|
resource: ResourceIdentity, |
|
segment: "foo", |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "IdentityWrite", |
|
resource: ResourceIdentity, |
|
segment: "foo", |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "IdentityWrite", |
|
resource: ResourceIdentity, |
|
segment: "foo", |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "IntentionRead", |
|
resource: ResourceIntention, |
|
segment: "foo", |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "IntentionRead", |
|
resource: ResourceIntention, |
|
segment: "foo", |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "IntentionWrite", |
|
resource: ResourceIntention, |
|
segment: "foo", |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "IntentionWrite", |
|
resource: ResourceIntention, |
|
segment: "foo", |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
resource: ResourceIntention, |
|
segment: "foo", |
|
access: "list", |
|
ret: Deny, |
|
err: "Invalid access level", |
|
}, |
|
{ |
|
method: "NodeRead", |
|
resource: ResourceNode, |
|
segment: "foo", |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "NodeRead", |
|
resource: ResourceNode, |
|
segment: "foo", |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "NodeWrite", |
|
resource: ResourceNode, |
|
segment: "foo", |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "NodeWrite", |
|
resource: ResourceNode, |
|
segment: "foo", |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
resource: ResourceNode, |
|
segment: "foo", |
|
access: "list", |
|
ret: Deny, |
|
err: "Invalid access level", |
|
}, |
|
{ |
|
method: "PeeringRead", |
|
resource: ResourcePeering, |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "PeeringRead", |
|
resource: ResourcePeering, |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "PeeringWrite", |
|
resource: ResourcePeering, |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "PeeringWrite", |
|
resource: ResourcePeering, |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "PreparedQueryRead", |
|
resource: ResourceQuery, |
|
segment: "foo", |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "PreparedQueryRead", |
|
resource: ResourceQuery, |
|
segment: "foo", |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "PreparedQueryWrite", |
|
resource: ResourceQuery, |
|
segment: "foo", |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "PreparedQueryWrite", |
|
resource: ResourceQuery, |
|
segment: "foo", |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
resource: ResourceQuery, |
|
segment: "foo", |
|
access: "list", |
|
ret: Deny, |
|
err: "Invalid access level", |
|
}, |
|
{ |
|
method: "ServiceRead", |
|
resource: ResourceService, |
|
segment: "foo", |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "ServiceRead", |
|
resource: ResourceService, |
|
segment: "foo", |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "ServiceWrite", |
|
resource: ResourceService, |
|
segment: "foo", |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "ServiceWrite", |
|
resource: ResourceService, |
|
segment: "foo", |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
resource: ResourceSession, |
|
segment: "foo", |
|
access: "list", |
|
ret: Deny, |
|
err: "Invalid access level", |
|
}, |
|
{ |
|
method: "SessionRead", |
|
resource: ResourceSession, |
|
segment: "foo", |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "SessionRead", |
|
resource: ResourceSession, |
|
segment: "foo", |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "SessionWrite", |
|
resource: ResourceSession, |
|
segment: "foo", |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "SessionWrite", |
|
resource: ResourceSession, |
|
segment: "foo", |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
resource: ResourceSession, |
|
segment: "foo", |
|
access: "list", |
|
ret: Deny, |
|
err: "Invalid access level", |
|
}, |
|
{ |
|
method: "KeyRead", |
|
resource: ResourceKey, |
|
segment: "foo", |
|
access: "read", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "KeyRead", |
|
resource: ResourceKey, |
|
segment: "foo", |
|
access: "read", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "KeyWrite", |
|
resource: ResourceKey, |
|
segment: "foo", |
|
access: "write", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "KeyWrite", |
|
resource: ResourceKey, |
|
segment: "foo", |
|
access: "write", |
|
ret: Allow, |
|
}, |
|
{ |
|
method: "KeyList", |
|
resource: ResourceKey, |
|
segment: "foo", |
|
access: "list", |
|
ret: Deny, |
|
}, |
|
{ |
|
method: "KeyList", |
|
resource: ResourceKey, |
|
segment: "foo", |
|
access: "list", |
|
ret: Allow, |
|
}, |
|
{ |
|
resource: ResourceKey, |
|
segment: "foo", |
|
access: "deny", |
|
ret: Deny, |
|
err: "Invalid access level", |
|
}, |
|
{ |
|
resource: "not-a-real-resource", |
|
access: "read", |
|
ret: Deny, |
|
err: "Invalid ACL resource requested:", |
|
}, |
|
} |
|
|
|
for _, tcase := range cases { |
|
t.Run(testName(tcase), func(t *testing.T) { |
|
m := &MockAuthorizer{} |
|
|
|
if tcase.err == "" { |
|
var nilCtx *AuthorizerContext |
|
if tcase.segment != "" { |
|
m.On(tcase.method, tcase.segment, nilCtx).Return(tcase.ret) |
|
} else { |
|
m.On(tcase.method, nilCtx).Return(tcase.ret) |
|
} |
|
} |
|
|
|
ret, err := Enforce(m, tcase.resource, tcase.segment, tcase.access, nil) |
|
if tcase.err == "" { |
|
require.NoError(t, err) |
|
} else { |
|
require.Error(t, err) |
|
require.Contains(t, err.Error(), tcase.err) |
|
} |
|
require.Equal(t, tcase.ret, ret) |
|
m.AssertExpectations(t) |
|
}) |
|
} |
|
}
|
|
|