mirror of
https://github.com/kemko/nomad.git
synced 2026-01-04 17:35:43 +03:00
add acl validation to Scaling.ListPolicies and Scaling.GetPolicy
This commit is contained in:
@@ -5540,9 +5540,8 @@ func TestJobEndpoint_GetScaleStatus_ACL(t *testing.T) {
|
||||
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs}))
|
||||
get.AuthToken = invalidToken.SecretID
|
||||
var invalidResp structs.JobScaleStatusResponse
|
||||
require.NotNil(err)
|
||||
err = msgpackrpc.CallWithCodec(codec, "Job.ScaleStatus", get, &invalidResp)
|
||||
err = msgpackrpc.CallWithCodec(codec, "Job.ScaleStatus", get, &resp)
|
||||
require.Contains(err.Error(), "Permission denied")
|
||||
|
||||
type testCase struct {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
memdb "github.com/hashicorp/go-memdb"
|
||||
|
||||
"github.com/hashicorp/nomad/acl"
|
||||
"github.com/hashicorp/nomad/nomad/state"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
@@ -26,31 +27,17 @@ func (a *Scaling) ListPolicies(args *structs.ScalingPolicyListRequest,
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "scaling", "list_policies"}, time.Now())
|
||||
|
||||
// Check management level permissions
|
||||
// acl, err := a.srv.ResolveToken(args.AuthToken)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// } 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 {
|
||||
// token, err := a.requestACLToken(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{}{}
|
||||
// }
|
||||
// }
|
||||
// Check for list-job permissions
|
||||
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil {
|
||||
hasListScalingPolicies := aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityListScalingPolicies)
|
||||
hasListAndReadJobs := aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityListJobs) &&
|
||||
aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob)
|
||||
if !(hasListScalingPolicies || hasListAndReadJobs) {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the blocking query
|
||||
opts := blockingOptions{
|
||||
@@ -103,31 +90,17 @@ func (a *Scaling) GetPolicy(args *structs.ScalingPolicySpecificRequest,
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "scaling", "get_policy"}, time.Now())
|
||||
|
||||
// Check management level permissions
|
||||
// acl, err := a.srv.ResolveToken(args.AuthToken)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// } 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 {
|
||||
// token, err := a.requestACLToken(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{}{}
|
||||
// }
|
||||
// }
|
||||
// Check for list-job permissions
|
||||
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil {
|
||||
hasReadScalingPolicy := aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadScalingPolicy)
|
||||
hasListAndReadJobs := aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityListJobs) &&
|
||||
aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob)
|
||||
if !(hasReadScalingPolicy || hasListAndReadJobs) {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the blocking query
|
||||
opts := blockingOptions{
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package nomad
|
||||
|
||||
import (
|
||||
msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/nomad/acl"
|
||||
"github.com/hashicorp/nomad/helper/uuid"
|
||||
"github.com/hashicorp/nomad/nomad/mock"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
|
||||
func TestScalingEndpoint_GetPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
|
||||
s1, cleanupS1 := TestServer(t, nil)
|
||||
@@ -24,7 +22,6 @@ func TestScalingEndpoint_GetPolicy(t *testing.T) {
|
||||
codec := rpcClient(t, s1)
|
||||
testutil.WaitForLeader(t, s1.RPC)
|
||||
|
||||
// Create the register request
|
||||
p1 := mock.ScalingPolicy()
|
||||
p2 := mock.ScalingPolicy()
|
||||
s1.fsm.State().UpsertScalingPolicies(1000, []*structs.ScalingPolicy{p1, p2})
|
||||
@@ -39,7 +36,7 @@ func TestScalingEndpoint_GetPolicy(t *testing.T) {
|
||||
var resp structs.SingleScalingPolicyResponse
|
||||
err := msgpackrpc.CallWithCodec(codec, "Scaling.GetPolicy", get, &resp)
|
||||
require.NoError(err)
|
||||
require.Equal(uint64(1000), resp.Index)
|
||||
require.EqualValues(1000, resp.Index)
|
||||
require.Equal(*p1, *resp.Policy)
|
||||
|
||||
// Lookup non-existing policy
|
||||
@@ -47,25 +44,94 @@ func TestScalingEndpoint_GetPolicy(t *testing.T) {
|
||||
resp = structs.SingleScalingPolicyResponse{}
|
||||
err = msgpackrpc.CallWithCodec(codec, "Scaling.GetPolicy", get, &resp)
|
||||
require.NoError(err)
|
||||
require.Equal(uint64(1000), resp.Index)
|
||||
require.EqualValues(1000, resp.Index)
|
||||
require.Nil(resp.Policy)
|
||||
}
|
||||
|
||||
func TestScalingEndpoint_ListPolicies(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
func TestScalingEndpoint_GetPolicy_ACL(t *testing.T) {
|
||||
t.Parallel()
|
||||
require := require.New(t)
|
||||
|
||||
s1, root, cleanupS1 := TestACLServer(t, nil)
|
||||
defer cleanupS1()
|
||||
codec := rpcClient(t, s1)
|
||||
testutil.WaitForLeader(t, s1.RPC)
|
||||
state := s1.fsm.State()
|
||||
|
||||
p1 := mock.ScalingPolicy()
|
||||
p2 := mock.ScalingPolicy()
|
||||
state.UpsertScalingPolicies(1000, []*structs.ScalingPolicy{p1, p2})
|
||||
|
||||
get := &structs.ScalingPolicySpecificRequest{
|
||||
ID: p1.ID,
|
||||
QueryOptions: structs.QueryOptions{
|
||||
Region: "global",
|
||||
},
|
||||
}
|
||||
|
||||
// lookup without token should fail
|
||||
var resp structs.SingleScalingPolicyResponse
|
||||
err := msgpackrpc.CallWithCodec(codec, "Scaling.GetPolicy", get, &resp)
|
||||
require.Error(err)
|
||||
require.Contains(err.Error(), "Permission denied")
|
||||
|
||||
// Expect failure for request with an invalid token
|
||||
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListScalingPolicies}))
|
||||
get.AuthToken = invalidToken.SecretID
|
||||
err = msgpackrpc.CallWithCodec(codec, "Scaling.GetPolicy", get, &resp)
|
||||
require.Error(err)
|
||||
require.Contains(err.Error(), "Permission denied")
|
||||
type testCase struct {
|
||||
authToken string
|
||||
name string
|
||||
}
|
||||
cases := []testCase{
|
||||
{
|
||||
name: "mgmt token should succeed",
|
||||
authToken: root.SecretID,
|
||||
},
|
||||
{
|
||||
name: "read disposition should succeed",
|
||||
authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-read",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "read", nil)).SecretID,
|
||||
},
|
||||
{
|
||||
name: "write disposition should succeed",
|
||||
authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-write",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "write", nil)).SecretID,
|
||||
},
|
||||
{
|
||||
name: "autoscaler disposition should succeed",
|
||||
authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-autoscaler",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "autoscaler", nil)).SecretID,
|
||||
},
|
||||
{
|
||||
name: "list-jobs+read-job capability should succeed",
|
||||
authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-read-job-scaling",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs, acl.NamespaceCapabilityReadJob})).SecretID,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
get.AuthToken = tc.authToken
|
||||
err = msgpackrpc.CallWithCodec(codec, "Scaling.GetPolicy", get, &resp)
|
||||
require.NoError(err, tc.name)
|
||||
require.EqualValues(1000, resp.Index)
|
||||
require.NotNil(resp.Policy)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestScalingEndpoint_ListPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
require := require.New(t)
|
||||
|
||||
s1, cleanupS1 := TestServer(t, nil)
|
||||
defer cleanupS1()
|
||||
codec := rpcClient(t, s1)
|
||||
testutil.WaitForLeader(t, s1.RPC)
|
||||
|
||||
// Create the register request
|
||||
p1 := mock.ScalingPolicy()
|
||||
p2 := mock.ScalingPolicy()
|
||||
|
||||
s1.fsm.State().UpsertScalingPolicies(1000, []*structs.ScalingPolicy{p1, p2})
|
||||
|
||||
// Lookup the policies
|
||||
get := &structs.ScalingPolicyListRequest{
|
||||
QueryOptions: structs.QueryOptions{
|
||||
@@ -73,16 +139,100 @@ func TestScalingEndpoint_ListPolicies(t *testing.T) {
|
||||
},
|
||||
}
|
||||
var resp structs.ACLPolicyListResponse
|
||||
if err := msgpackrpc.CallWithCodec(codec, "Scaling.ListPolicies", get, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
err := msgpackrpc.CallWithCodec(codec, "Scaling.ListPolicies", get, &resp)
|
||||
require.NoError(err)
|
||||
require.Empty(resp.Policies)
|
||||
|
||||
p1 := mock.ScalingPolicy()
|
||||
p2 := mock.ScalingPolicy()
|
||||
s1.fsm.State().UpsertScalingPolicies(1000, []*structs.ScalingPolicy{p1, p2})
|
||||
|
||||
err = msgpackrpc.CallWithCodec(codec, "Scaling.ListPolicies", get, &resp)
|
||||
require.NoError(err)
|
||||
require.EqualValues(1000, resp.Index)
|
||||
require.Len(resp.Policies, 2)
|
||||
}
|
||||
|
||||
func TestScalingEndpoint_ListPolicies_ACL(t *testing.T) {
|
||||
t.Parallel()
|
||||
require := require.New(t)
|
||||
|
||||
s1, root, cleanupS1 := TestACLServer(t, nil)
|
||||
defer cleanupS1()
|
||||
codec := rpcClient(t, s1)
|
||||
testutil.WaitForLeader(t, s1.RPC)
|
||||
state := s1.fsm.State()
|
||||
|
||||
p1 := mock.ScalingPolicy()
|
||||
p2 := mock.ScalingPolicy()
|
||||
state.UpsertScalingPolicies(1000, []*structs.ScalingPolicy{p1, p2})
|
||||
|
||||
get := &structs.ScalingPolicyListRequest{
|
||||
QueryOptions: structs.QueryOptions{
|
||||
Region: "global",
|
||||
},
|
||||
}
|
||||
|
||||
// lookup without token should fail
|
||||
var resp structs.ACLPolicyListResponse
|
||||
err := msgpackrpc.CallWithCodec(codec, "Scaling.ListPolicies", get, &resp)
|
||||
require.Error(err)
|
||||
require.Contains(err.Error(), "Permission denied")
|
||||
|
||||
// Expect failure for request with an invalid token
|
||||
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListScalingPolicies}))
|
||||
get.AuthToken = invalidToken.SecretID
|
||||
require.Error(err)
|
||||
require.Contains(err.Error(), "Permission denied")
|
||||
|
||||
type testCase struct {
|
||||
authToken string
|
||||
name string
|
||||
}
|
||||
cases := []testCase{
|
||||
{
|
||||
name: "mgmt token should succeed",
|
||||
authToken: root.SecretID,
|
||||
},
|
||||
{
|
||||
name: "read disposition should succeed",
|
||||
authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-read",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "read", nil)).SecretID,
|
||||
},
|
||||
{
|
||||
name: "write disposition should succeed",
|
||||
authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-write",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "write", nil)).SecretID,
|
||||
},
|
||||
{
|
||||
name: "autoscaler disposition should succeed",
|
||||
authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-autoscaler",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "autoscaler", nil)).SecretID,
|
||||
},
|
||||
{
|
||||
name: "list-scaling-policies capability should succeed",
|
||||
authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-list-scaling-policies",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListScalingPolicies})).SecretID,
|
||||
},
|
||||
{
|
||||
name: "list-jobs+read-job capability should succeed",
|
||||
authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-read-job-scaling",
|
||||
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs, acl.NamespaceCapabilityReadJob})).SecretID,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
get.AuthToken = tc.authToken
|
||||
err = msgpackrpc.CallWithCodec(codec, "Scaling.ListPolicies", get, &resp)
|
||||
require.NoError(err, tc.name)
|
||||
require.EqualValues(1000, resp.Index)
|
||||
require.Len(resp.Policies, 2)
|
||||
}
|
||||
assert.EqualValues(1000, resp.Index)
|
||||
assert.Len(resp.Policies, 2)
|
||||
}
|
||||
|
||||
func TestScalingEndpoint_ListPolicies_Blocking(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
|
||||
s1, cleanupS1 := TestServer(t, nil)
|
||||
@@ -120,7 +270,7 @@ func TestScalingEndpoint_ListPolicies_Blocking(t *testing.T) {
|
||||
require.NoError(err)
|
||||
|
||||
require.True(time.Since(start) > 200*time.Millisecond, "should block: %#v", resp)
|
||||
require.Equal(uint64(200), resp.Index, "bad index")
|
||||
require.EqualValues(200, resp.Index, "bad index")
|
||||
require.Len(resp.Policies, 2)
|
||||
require.ElementsMatch([]string{p1.ID, p2.ID}, []string{resp.Policies[0].ID, resp.Policies[1].ID})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user