diff --git a/consul/acl.go b/consul/acl.go index a6dd2db53..47767830c 100644 --- a/consul/acl.go +++ b/consul/acl.go @@ -114,7 +114,7 @@ func (s *Server) useACLPolicy(id string, cached *aclCacheEntry, p *structs.ACLPo // Check for a cached compiled policy var compiled acl.ACL - raw, ok := s.aclPolicyCache.Get(cached.ETag) + raw, ok := s.aclPolicyCache.Get(p.ETag) if ok { compiled = raw.(acl.ACL) } else { @@ -147,5 +147,5 @@ func (s *Server) useACLPolicy(id string, cached *aclCacheEntry, p *structs.ACLPo cached.Expires = time.Now().Add(p.TTL) } s.aclCache.Add(id, cached) - return acl, nil + return compiled, nil } diff --git a/consul/acl_test.go b/consul/acl_test.go new file mode 100644 index 000000000..bbf3ec051 --- /dev/null +++ b/consul/acl_test.go @@ -0,0 +1,223 @@ +package consul + +import ( + "errors" + "fmt" + "os" + "testing" + + "github.com/hashicorp/consul/consul/structs" + "github.com/hashicorp/consul/testutil" +) + +func TestACL_Disabled(t *testing.T) { + dir1, s1 := testServer(t) + defer os.RemoveAll(dir1) + defer s1.Shutdown() + client := rpcClient(t, s1) + defer client.Close() + + testutil.WaitForLeader(t, client.Call, "dc1") + + acl, err := s1.resolveToken("does not exist") + if err != nil { + t.Fatalf("err: %v", err) + } + if acl != nil { + t.Fatalf("got acl") + } +} + +func TestACL_Authority_NotFound(t *testing.T) { + dir1, s1 := testServerWithConfig(t, func(c *Config) { + c.ACLDatacenter = "dc1" // Enable ACLs! + }) + defer os.RemoveAll(dir1) + defer s1.Shutdown() + client := rpcClient(t, s1) + defer client.Close() + + testutil.WaitForLeader(t, client.Call, "dc1") + + acl, err := s1.resolveToken("does not exist") + if err == nil || err.Error() != aclNotFound { + t.Fatalf("err: %v", err) + } + if acl != nil { + t.Fatalf("got acl") + } +} + +func TestACL_Authority_Found(t *testing.T) { + dir1, s1 := testServerWithConfig(t, func(c *Config) { + c.ACLDatacenter = "dc1" // Enable ACLs! + }) + defer os.RemoveAll(dir1) + defer s1.Shutdown() + client := rpcClient(t, s1) + defer client.Close() + + testutil.WaitForLeader(t, client.Call, "dc1") + + // Create a new token + arg := structs.ACLRequest{ + Datacenter: "dc1", + Op: structs.ACLSet, + ACL: structs.ACL{ + Name: "User token", + Type: structs.ACLTypeClient, + Rules: testACLPolicy, + }, + } + var id string + if err := client.Call("ACL.Apply", &arg, &id); err != nil { + t.Fatalf("err: %v", err) + } + + // Resolve the token + acl, err := s1.resolveToken(id) + if err != nil { + t.Fatalf("err: %v", err) + } + if acl == nil { + t.Fatalf("missing acl") + } + + // Check the policy + if acl.KeyRead("bar") { + t.Fatalf("unexpected read") + } + if !acl.KeyRead("foo/test") { + t.Fatalf("unexpected failed read") + } +} + +func TestACL_NonAuthority_NotFound(t *testing.T) { + dir1, s1 := testServerWithConfig(t, func(c *Config) { + c.ACLDatacenter = "dc1" + }) + defer os.RemoveAll(dir1) + defer s1.Shutdown() + + dir2, s2 := testServerWithConfig(t, func(c *Config) { + c.ACLDatacenter = "dc1" // Enable ACLs! + c.Bootstrap = false // Disable bootstrap + }) + defer os.RemoveAll(dir2) + defer s2.Shutdown() + + // Try to join + addr := fmt.Sprintf("127.0.0.1:%d", + s1.config.SerfLANConfig.MemberlistConfig.BindPort) + if _, err := s2.JoinLAN([]string{addr}); err != nil { + t.Fatalf("err: %v", err) + } + + testutil.WaitForResult(func() (bool, error) { + p1, _ := s1.raftPeers.Peers() + return len(p1) == 2, errors.New(fmt.Sprintf("%v", p1)) + }, func(err error) { + t.Fatalf("should have 2 peers: %v", err) + }) + + client := rpcClient(t, s1) + defer client.Close() + testutil.WaitForLeader(t, client.Call, "dc1") + + // find the non-authoritative server + var nonAuth *Server + if !s1.IsLeader() { + nonAuth = s1 + } else { + nonAuth = s2 + } + + acl, err := nonAuth.resolveToken("does not exist") + if err == nil || err.Error() != aclNotFound { + t.Fatalf("err: %v", err) + } + if acl != nil { + t.Fatalf("got acl") + } +} + +func TestACL_NonAuthority_Found(t *testing.T) { + dir1, s1 := testServerWithConfig(t, func(c *Config) { + c.ACLDatacenter = "dc1" + }) + defer os.RemoveAll(dir1) + defer s1.Shutdown() + client := rpcClient(t, s1) + defer client.Close() + + dir2, s2 := testServerWithConfig(t, func(c *Config) { + c.ACLDatacenter = "dc1" // Enable ACLs! + c.Bootstrap = false // Disable bootstrap + }) + defer os.RemoveAll(dir2) + defer s2.Shutdown() + + // Try to join + addr := fmt.Sprintf("127.0.0.1:%d", + s1.config.SerfLANConfig.MemberlistConfig.BindPort) + if _, err := s2.JoinLAN([]string{addr}); err != nil { + t.Fatalf("err: %v", err) + } + + testutil.WaitForResult(func() (bool, error) { + p1, _ := s1.raftPeers.Peers() + return len(p1) == 2, errors.New(fmt.Sprintf("%v", p1)) + }, func(err error) { + t.Fatalf("should have 2 peers: %v", err) + }) + testutil.WaitForLeader(t, client.Call, "dc1") + + // Create a new token + arg := structs.ACLRequest{ + Datacenter: "dc1", + Op: structs.ACLSet, + ACL: structs.ACL{ + Name: "User token", + Type: structs.ACLTypeClient, + Rules: testACLPolicy, + }, + } + var id string + if err := client.Call("ACL.Apply", &arg, &id); err != nil { + t.Fatalf("err: %v", err) + } + + // find the non-authoritative server + var nonAuth *Server + if !s1.IsLeader() { + nonAuth = s1 + } else { + nonAuth = s2 + } + + // Token should resolve + acl, err := nonAuth.resolveToken(id) + if err != nil { + t.Fatalf("err: %v", err) + } + if acl == nil { + t.Fatalf("missing acl") + } + + // Check the policy + if acl.KeyRead("bar") { + t.Fatalf("unexpected read") + } + if !acl.KeyRead("foo/test") { + t.Fatalf("unexpected failed read") + } +} + +var testACLPolicy = ` +key "" { + policy = "deny" +} +key "foo/" { + policy = "write" +} +` diff --git a/consul/server_test.go b/consul/server_test.go index 70aa5811f..76b7d4ed4 100644 --- a/consul/server_test.go +++ b/consul/server_test.go @@ -101,6 +101,17 @@ func testServerDCExpect(t *testing.T, dc string, expect int) (string, *Server) { return dir, server } +func testServerWithConfig(t *testing.T, cb func(c *Config)) (string, *Server) { + name := fmt.Sprintf("Node %d", getPort()) + dir, config := testServerConfig(t, name) + cb(config) + server, err := NewServer(config) + if err != nil { + t.Fatalf("err: %v", err) + } + return dir, server +} + func TestServer_StartStop(t *testing.T) { dir := tmpDir(t) defer os.RemoveAll(dir)