mirror of https://github.com/status-im/consul.git
Adds ACL cases for apply.
This commit is contained in:
parent
8222d3f462
commit
d4d866c6d5
|
@ -22,7 +22,7 @@ func TestPreparedQuery_Apply(t *testing.T) {
|
|||
|
||||
// Set up a node and service in the catalog.
|
||||
{
|
||||
arg := structs.RegisterRequest{
|
||||
req := structs.RegisterRequest{
|
||||
Datacenter: "dc1",
|
||||
Node: "foo",
|
||||
Address: "127.0.0.1",
|
||||
|
@ -33,15 +33,14 @@ func TestPreparedQuery_Apply(t *testing.T) {
|
|||
},
|
||||
}
|
||||
var reply struct{}
|
||||
|
||||
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &reply)
|
||||
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &req, &reply)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set up a bare bones query.
|
||||
arg := structs.PreparedQueryRequest{
|
||||
query := structs.PreparedQueryRequest{
|
||||
Datacenter: "dc1",
|
||||
Op: structs.PreparedQueryCreate,
|
||||
Query: &structs.PreparedQuery{
|
||||
|
@ -53,16 +52,16 @@ func TestPreparedQuery_Apply(t *testing.T) {
|
|||
var reply string
|
||||
|
||||
// Set an ID which should fail the create.
|
||||
arg.Query.ID = "nope"
|
||||
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &arg, &reply)
|
||||
query.Query.ID = "nope"
|
||||
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
||||
if err == nil || !strings.Contains(err.Error(), "ID must be empty") {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// Change it to a bogus modify which should also fail.
|
||||
arg.Op = structs.PreparedQueryUpdate
|
||||
arg.Query.ID = generateUUID()
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &arg, &reply)
|
||||
query.Op = structs.PreparedQueryUpdate
|
||||
query.Query.ID = generateUUID()
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
||||
if err == nil || !strings.Contains(err.Error(), "Cannot modify non-existent prepared query") {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
@ -70,34 +69,34 @@ func TestPreparedQuery_Apply(t *testing.T) {
|
|||
// Fix up the ID but invalidate the query itself. This proves we call
|
||||
// parseQuery for a create, but that function is checked in detail as
|
||||
// part of another test.
|
||||
arg.Op = structs.PreparedQueryCreate
|
||||
arg.Query.ID = ""
|
||||
arg.Query.Service.Failover.NearestN = -1
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &arg, &reply)
|
||||
query.Op = structs.PreparedQueryCreate
|
||||
query.Query.ID = ""
|
||||
query.Query.Service.Failover.NearestN = -1
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
||||
if err == nil || !strings.Contains(err.Error(), "Bad NearestN") {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// Fix that and make sure it propagates an error from the Raft apply.
|
||||
arg.Query.Service.Failover.NearestN = 0
|
||||
arg.Query.Service.Service = "nope"
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &arg, &reply)
|
||||
query.Query.Service.Failover.NearestN = 0
|
||||
query.Query.Service.Service = "nope"
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
||||
if err == nil || !strings.Contains(err.Error(), "invalid service") {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// Fix that and make sure the apply goes through.
|
||||
arg.Query.Service.Service = "redis"
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &arg, &reply); err != nil {
|
||||
query.Query.Service.Service = "redis"
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Capture the ID and read the query back to verify.
|
||||
arg.Query.ID = reply
|
||||
query.Query.ID = reply
|
||||
{
|
||||
req := &structs.PreparedQuerySpecificRequest{
|
||||
Datacenter: "dc1",
|
||||
QueryIDOrName: arg.Query.ID,
|
||||
QueryIDOrName: query.Query.ID,
|
||||
}
|
||||
var resp structs.IndexedPreparedQueries
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Lookup", req, &resp); err != nil {
|
||||
|
@ -112,15 +111,15 @@ func TestPreparedQuery_Apply(t *testing.T) {
|
|||
t.Fatalf("bad index: %d", resp.Index)
|
||||
}
|
||||
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
||||
if !reflect.DeepEqual(actual, arg.Query) {
|
||||
if !reflect.DeepEqual(actual, query.Query) {
|
||||
t.Fatalf("bad: %v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Make the op an update. This should go through now that we have an ID.
|
||||
arg.Op = structs.PreparedQueryUpdate
|
||||
arg.Query.Service.Failover.NearestN = 2
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &arg, &reply); err != nil {
|
||||
query.Op = structs.PreparedQueryUpdate
|
||||
query.Query.Service.Failover.NearestN = 2
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
|
@ -128,7 +127,7 @@ func TestPreparedQuery_Apply(t *testing.T) {
|
|||
{
|
||||
req := &structs.PreparedQuerySpecificRequest{
|
||||
Datacenter: "dc1",
|
||||
QueryIDOrName: arg.Query.ID,
|
||||
QueryIDOrName: query.Query.ID,
|
||||
}
|
||||
var resp structs.IndexedPreparedQueries
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Lookup", req, &resp); err != nil {
|
||||
|
@ -143,30 +142,30 @@ func TestPreparedQuery_Apply(t *testing.T) {
|
|||
t.Fatalf("bad index: %d", resp.Index)
|
||||
}
|
||||
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
||||
if !reflect.DeepEqual(actual, arg.Query) {
|
||||
if !reflect.DeepEqual(actual, query.Query) {
|
||||
t.Fatalf("bad: %v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Give a bogus op and make sure it fails.
|
||||
arg.Op = "nope"
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &arg, &reply)
|
||||
query.Op = "nope"
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
||||
if err == nil || !strings.Contains(err.Error(), "Unknown prepared query operation:") {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// Prove that an update also goes through the parseQuery validation.
|
||||
arg.Op = structs.PreparedQueryUpdate
|
||||
arg.Query.Service.Failover.NearestN = -1
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &arg, &reply)
|
||||
query.Op = structs.PreparedQueryUpdate
|
||||
query.Query.Service.Failover.NearestN = -1
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
||||
if err == nil || !strings.Contains(err.Error(), "Bad NearestN") {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// Now change the op to delete; the bad query field should be ignored
|
||||
// because all we care about for a delete op is the ID.
|
||||
arg.Op = structs.PreparedQueryDelete
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &arg, &reply); err != nil {
|
||||
query.Op = structs.PreparedQueryDelete
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
|
@ -174,7 +173,315 @@ func TestPreparedQuery_Apply(t *testing.T) {
|
|||
{
|
||||
req := &structs.PreparedQuerySpecificRequest{
|
||||
Datacenter: "dc1",
|
||||
QueryIDOrName: arg.Query.ID,
|
||||
QueryIDOrName: query.Query.ID,
|
||||
}
|
||||
var resp structs.IndexedPreparedQueries
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Lookup", req, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(resp.Queries) != 0 {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreparedQuery_Apply_ACLDeny(t *testing.T) {
|
||||
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
||||
c.ACLDatacenter = "dc1"
|
||||
c.ACLMasterToken = "root"
|
||||
c.ACLDefaultPolicy = "deny"
|
||||
})
|
||||
defer os.RemoveAll(dir1)
|
||||
defer s1.Shutdown()
|
||||
codec := rpcClient(t, s1)
|
||||
defer codec.Close()
|
||||
|
||||
testutil.WaitForLeader(t, s1.RPC, "dc1")
|
||||
|
||||
// Create two ACLs with read permission to the service.
|
||||
var token1, token2 string
|
||||
{
|
||||
var rules = `
|
||||
service "redis" {
|
||||
policy = "read"
|
||||
}
|
||||
`
|
||||
|
||||
req := structs.ACLRequest{
|
||||
Datacenter: "dc1",
|
||||
Op: structs.ACLSet,
|
||||
ACL: structs.ACL{
|
||||
Name: "User token",
|
||||
Type: structs.ACLTypeClient,
|
||||
Rules: rules,
|
||||
},
|
||||
WriteRequest: structs.WriteRequest{Token: "root"},
|
||||
}
|
||||
var reply string
|
||||
|
||||
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
token1 = reply
|
||||
|
||||
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
token2 = reply
|
||||
}
|
||||
|
||||
// Set up a node and service in the catalog.
|
||||
{
|
||||
req := structs.RegisterRequest{
|
||||
Datacenter: "dc1",
|
||||
Node: "foo",
|
||||
Address: "127.0.0.1",
|
||||
Service: &structs.NodeService{
|
||||
Service: "redis",
|
||||
Tags: []string{"master"},
|
||||
Port: 8000,
|
||||
},
|
||||
WriteRequest: structs.WriteRequest{Token: "root"},
|
||||
}
|
||||
var reply struct{}
|
||||
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &req, &reply)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set up a bare bones query.
|
||||
query := structs.PreparedQueryRequest{
|
||||
Datacenter: "dc1",
|
||||
Op: structs.PreparedQueryCreate,
|
||||
Query: &structs.PreparedQuery{
|
||||
Service: structs.ServiceQuery{
|
||||
Service: "redis",
|
||||
},
|
||||
},
|
||||
}
|
||||
var reply string
|
||||
|
||||
// Creating without a token should fail since the default policy is to
|
||||
// deny.
|
||||
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
||||
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// Now add the token and try again.
|
||||
query.WriteRequest.Token = token1
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Capture the ID and set the token, then read back the query to verify.
|
||||
query.Query.ID = reply
|
||||
query.Query.Token = token1
|
||||
{
|
||||
req := &structs.PreparedQuerySpecificRequest{
|
||||
Datacenter: "dc1",
|
||||
QueryIDOrName: query.Query.ID,
|
||||
QueryOptions: structs.QueryOptions{Token: "root"},
|
||||
}
|
||||
var resp structs.IndexedPreparedQueries
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Lookup", req, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(resp.Queries) != 1 {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
actual := resp.Queries[0]
|
||||
if resp.Index != actual.ModifyIndex {
|
||||
t.Fatalf("bad index: %d", resp.Index)
|
||||
}
|
||||
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
||||
if !reflect.DeepEqual(actual, query.Query) {
|
||||
t.Fatalf("bad: %v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to do an update with a different token that does have access to
|
||||
// the service, but isn't the one that was used to create the query.
|
||||
query.Op = structs.PreparedQueryUpdate
|
||||
query.WriteRequest.Token = token2
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
||||
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// Try again with no token.
|
||||
query.WriteRequest.Token = ""
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
||||
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// Try again with the original token. This should go through.
|
||||
query.WriteRequest.Token = token1
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Try to do a delete with a different token that does have access to
|
||||
// the service, but isn't the one that was used to create the query.
|
||||
query.Op = structs.PreparedQueryDelete
|
||||
query.WriteRequest.Token = token2
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
||||
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// Try again with no token.
|
||||
query.WriteRequest.Token = ""
|
||||
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
||||
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
|
||||
// Try again with the original token. This should go through.
|
||||
query.WriteRequest.Token = token1
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Make sure the query got deleted.
|
||||
{
|
||||
req := &structs.PreparedQuerySpecificRequest{
|
||||
Datacenter: "dc1",
|
||||
QueryIDOrName: query.Query.ID,
|
||||
QueryOptions: structs.QueryOptions{Token: "root"},
|
||||
}
|
||||
var resp structs.IndexedPreparedQueries
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Lookup", req, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(resp.Queries) != 0 {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
// Make the query again.
|
||||
query.Op = structs.PreparedQueryCreate
|
||||
query.Query.ID = ""
|
||||
query.Query.Token = ""
|
||||
query.WriteRequest.Token = token1
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Check that it's there.
|
||||
query.Query.ID = reply
|
||||
query.Query.Token = token1
|
||||
{
|
||||
req := &structs.PreparedQuerySpecificRequest{
|
||||
Datacenter: "dc1",
|
||||
QueryIDOrName: query.Query.ID,
|
||||
QueryOptions: structs.QueryOptions{Token: "root"},
|
||||
}
|
||||
var resp structs.IndexedPreparedQueries
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Lookup", req, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(resp.Queries) != 1 {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
actual := resp.Queries[0]
|
||||
if resp.Index != actual.ModifyIndex {
|
||||
t.Fatalf("bad index: %d", resp.Index)
|
||||
}
|
||||
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
||||
if !reflect.DeepEqual(actual, query.Query) {
|
||||
t.Fatalf("bad: %v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// A management token should be able to update the query no matter what.
|
||||
query.Op = structs.PreparedQueryUpdate
|
||||
query.WriteRequest.Token = "root"
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// That last update should have changed the token to the management one.
|
||||
query.Query.Token = "root"
|
||||
{
|
||||
req := &structs.PreparedQuerySpecificRequest{
|
||||
Datacenter: "dc1",
|
||||
QueryIDOrName: query.Query.ID,
|
||||
QueryOptions: structs.QueryOptions{Token: "root"},
|
||||
}
|
||||
var resp structs.IndexedPreparedQueries
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Lookup", req, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(resp.Queries) != 1 {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
actual := resp.Queries[0]
|
||||
if resp.Index != actual.ModifyIndex {
|
||||
t.Fatalf("bad index: %d", resp.Index)
|
||||
}
|
||||
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
||||
if !reflect.DeepEqual(actual, query.Query) {
|
||||
t.Fatalf("bad: %v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Make another query.
|
||||
query.Op = structs.PreparedQueryCreate
|
||||
query.Query.ID = ""
|
||||
query.Query.Token = ""
|
||||
query.WriteRequest.Token = token1
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Check that it's there.
|
||||
query.Query.ID = reply
|
||||
query.Query.Token = token1
|
||||
{
|
||||
req := &structs.PreparedQuerySpecificRequest{
|
||||
Datacenter: "dc1",
|
||||
QueryIDOrName: query.Query.ID,
|
||||
QueryOptions: structs.QueryOptions{Token: "root"},
|
||||
}
|
||||
var resp structs.IndexedPreparedQueries
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Lookup", req, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(resp.Queries) != 1 {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
actual := resp.Queries[0]
|
||||
if resp.Index != actual.ModifyIndex {
|
||||
t.Fatalf("bad index: %d", resp.Index)
|
||||
}
|
||||
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
||||
if !reflect.DeepEqual(actual, query.Query) {
|
||||
t.Fatalf("bad: %v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// A management token should be able to delete the query no matter what.
|
||||
query.Op = structs.PreparedQueryDelete
|
||||
query.WriteRequest.Token = "root"
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Make sure the query got deleted.
|
||||
{
|
||||
req := &structs.PreparedQuerySpecificRequest{
|
||||
Datacenter: "dc1",
|
||||
QueryIDOrName: query.Query.ID,
|
||||
QueryOptions: structs.QueryOptions{Token: "root"},
|
||||
}
|
||||
var resp structs.IndexedPreparedQueries
|
||||
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Lookup", req, &resp); err != nil {
|
||||
|
|
Loading…
Reference in New Issue