ListPolicies and GetPolicy work w/o management token

This commit is contained in:
Alex Dadgar 2017-10-13 11:30:27 -07:00
parent c559f6652f
commit 377e63dc60
5 changed files with 115 additions and 11 deletions

3
dev/acls/default-ns.hcl Normal file
View file

@ -0,0 +1,3 @@
namespace "default" {
policy = "write"
}

3
dev/acls/node-read.hcl Normal file
View file

@ -0,0 +1,3 @@
node {
policy = "read"
}

View file

@ -121,12 +121,36 @@ func (a *ACL) ListPolicies(args *structs.ACLPolicyListRequest, reply *structs.AC
defer metrics.MeasureSince([]string{"nomad", "acl", "list_policies"}, time.Now())
// Check management level permissions
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
acl, err := a.srv.ResolveToken(args.AuthToken)
if err != nil {
return err
} else if acl == nil || !acl.IsManagement() {
} else if acl == nil {
return structs.ErrPermissionDenied
}
// If it is not a management token determine the policies that may be listed
mgt := acl.IsManagement()
var policies map[string]struct{}
if !mgt {
snap, err := a.srv.fsm.State().Snapshot()
if err != nil {
return err
}
token, err := snap.ACLTokenBySecretID(nil, args.AuthToken)
if err != nil {
return err
}
if token == nil {
return structs.ErrTokenNotFound
}
policies = make(map[string]struct{}, len(token.Policies))
for _, p := range token.Policies {
policies[p] = struct{}{}
}
}
// Setup the blocking query
opts := blockingOptions{
queryOpts: &args.QueryOptions,
@ -152,7 +176,9 @@ func (a *ACL) ListPolicies(args *structs.ACLPolicyListRequest, reply *structs.AC
break
}
policy := raw.(*structs.ACLPolicy)
reply.Policies = append(reply.Policies, policy.Stub())
if _, ok := policies[policy.Name]; ok || mgt {
reply.Policies = append(reply.Policies, policy.Stub())
}
}
// Use the last index that affected the policy table
@ -183,12 +209,42 @@ func (a *ACL) GetPolicy(args *structs.ACLPolicySpecificRequest, reply *structs.S
defer metrics.MeasureSince([]string{"nomad", "acl", "get_policy"}, time.Now())
// Check management level permissions
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {
acl, err := a.srv.ResolveToken(args.AuthToken)
if err != nil {
return err
} else if acl == nil || !acl.IsManagement() {
} else if acl == nil {
return structs.ErrPermissionDenied
}
// If it is not a management token determine if it can get this policy
mgt := acl.IsManagement()
if !mgt {
snap, err := a.srv.fsm.State().Snapshot()
if err != nil {
return err
}
token, err := snap.ACLTokenBySecretID(nil, args.AuthToken)
if err != nil {
return err
}
if token == nil {
return structs.ErrTokenNotFound
}
found := false
for _, p := range token.Policies {
if p == args.Name {
found = true
break
}
}
if !found {
return structs.ErrPermissionDenied
}
}
// Setup the blocking query
opts := blockingOptions{
queryOpts: &args.QueryOptions,

View file

@ -28,6 +28,11 @@ func TestACLEndpoint_GetPolicy(t *testing.T) {
policy := mock.ACLPolicy()
s1.fsm.State().UpsertACLPolicies(1000, []*structs.ACLPolicy{policy})
// Create a token with one the policy
token := mock.ACLToken()
token.Policies = []string{policy.Name}
s1.fsm.State().UpsertACLTokens(1001, []*structs.ACLToken{token})
// Lookup the policy
get := &structs.ACLPolicySpecificRequest{
Name: policy.Name,
@ -50,6 +55,21 @@ func TestACLEndpoint_GetPolicy(t *testing.T) {
}
assert.Equal(t, uint64(1000), resp.Index)
assert.Nil(t, resp.Policy)
// Lookup the policy with the token
get = &structs.ACLPolicySpecificRequest{
Name: policy.Name,
QueryOptions: structs.QueryOptions{
Region: "global",
AuthToken: token.SecretID,
},
}
var resp2 structs.SingleACLPolicyResponse
if err := msgpackrpc.CallWithCodec(codec, "ACL.GetPolicy", get, &resp2); err != nil {
t.Fatalf("err: %v", err)
}
assert.EqualValues(t, 1000, resp2.Index)
assert.Equal(t, policy, resp2.Policy)
}
func TestACLEndpoint_GetPolicy_Blocking(t *testing.T) {
@ -290,6 +310,7 @@ func TestACLEndpoint_GetPolicies_Blocking(t *testing.T) {
}
func TestACLEndpoint_ListPolicies(t *testing.T) {
assert := assert.New(t)
t.Parallel()
s1, root := testACLServer(t, nil)
defer s1.Shutdown()
@ -304,6 +325,11 @@ func TestACLEndpoint_ListPolicies(t *testing.T) {
p2.Name = "aaaabbbb-3350-4b4b-d185-0e1992ed43e9"
s1.fsm.State().UpsertACLPolicies(1000, []*structs.ACLPolicy{p1, p2})
// Create a token with one of those policies
token := mock.ACLToken()
token.Policies = []string{p1.Name}
s1.fsm.State().UpsertACLTokens(1001, []*structs.ACLToken{token})
// Lookup the policies
get := &structs.ACLPolicyListRequest{
QueryOptions: structs.QueryOptions{
@ -315,8 +341,8 @@ func TestACLEndpoint_ListPolicies(t *testing.T) {
if err := msgpackrpc.CallWithCodec(codec, "ACL.ListPolicies", get, &resp); err != nil {
t.Fatalf("err: %v", err)
}
assert.Equal(t, uint64(1000), resp.Index)
assert.Equal(t, 2, len(resp.Policies))
assert.EqualValues(1000, resp.Index)
assert.Len(resp.Policies, 2)
// Lookup the policies by prefix
get = &structs.ACLPolicyListRequest{
@ -330,8 +356,24 @@ func TestACLEndpoint_ListPolicies(t *testing.T) {
if err := msgpackrpc.CallWithCodec(codec, "ACL.ListPolicies", get, &resp2); err != nil {
t.Fatalf("err: %v", err)
}
assert.Equal(t, uint64(1000), resp2.Index)
assert.Equal(t, 1, len(resp2.Policies))
assert.EqualValues(1000, resp2.Index)
assert.Len(resp2.Policies, 1)
// List policies using the created token
get = &structs.ACLPolicyListRequest{
QueryOptions: structs.QueryOptions{
Region: "global",
AuthToken: token.SecretID,
},
}
var resp3 structs.ACLPolicyListResponse
if err := msgpackrpc.CallWithCodec(codec, "ACL.ListPolicies", get, &resp3); err != nil {
t.Fatalf("err: %v", err)
}
assert.EqualValues(1000, resp3.Index)
if assert.Len(resp3.Policies, 1) {
assert.Equal(resp3.Policies[0].Name, p1.Name)
}
}
func TestACLEndpoint_ListPolicies_Blocking(t *testing.T) {

View file

@ -26,7 +26,7 @@ The table below shows this endpoint's support for
| Blocking Queries | Consistency Modes | ACL Required |
| ---------------- | ----------------- | ------------ |
| `YES` | `all` | `management` |
| `YES` | `all` | `management` for all policies.<br>Output when given a non-management token will be limited to the policies on the token itself |
### Sample Request
@ -110,7 +110,7 @@ The table below shows this endpoint's support for
| Blocking Queries | Consistency Modes | ACL Required |
| ---------------- | ----------------- | ------------ |
| `YES` | `all` | `management` |
| `YES` | `all` | `management` or token with access to policy |
### Sample Request