1package agent
2
3import (
4	"bytes"
5	"encoding/json"
6	"fmt"
7	"io"
8	"net/http"
9	"net/http/httptest"
10	"testing"
11	"time"
12
13	"github.com/hashicorp/consul/acl"
14	"github.com/hashicorp/consul/agent/consul/authmethod/testauth"
15	"github.com/hashicorp/consul/agent/structs"
16	"github.com/hashicorp/consul/internal/go-sso/oidcauth/oidcauthtest"
17	"github.com/hashicorp/consul/sdk/freeport"
18	"github.com/hashicorp/consul/sdk/testutil"
19	"github.com/hashicorp/consul/testrpc"
20	"github.com/hashicorp/go-uuid"
21	"github.com/stretchr/testify/require"
22	"gopkg.in/square/go-jose.v2/jwt"
23)
24
25// NOTE: The tests contained herein are designed to test the HTTP API
26//       They are not intended to thoroughly test the backing RPC
27//       functionality as that will be done with other tests.
28
29func TestACL_Disabled_Response(t *testing.T) {
30	t.Parallel()
31	a := NewTestAgent(t, "")
32	defer a.Shutdown()
33
34	type testCase struct {
35		name string
36		fn   func(resp http.ResponseWriter, req *http.Request) (interface{}, error)
37	}
38
39	tests := []testCase{
40		{"ACLBootstrap", a.srv.ACLBootstrap},
41		{"ACLReplicationStatus", a.srv.ACLReplicationStatus},
42		{"AgentToken", a.srv.AgentToken}, // See TestAgent_Token
43		{"ACLRulesTranslate", a.srv.ACLRulesTranslate},
44		{"ACLRulesTranslateLegacyToken", a.srv.ACLRulesTranslateLegacyToken},
45		{"ACLPolicyList", a.srv.ACLPolicyList},
46		{"ACLPolicyCRUD", a.srv.ACLPolicyCRUD},
47		{"ACLPolicyCreate", a.srv.ACLPolicyCreate},
48		{"ACLTokenList", a.srv.ACLTokenList},
49		{"ACLTokenCreate", a.srv.ACLTokenCreate},
50		{"ACLTokenSelf", a.srv.ACLTokenSelf},
51		{"ACLTokenCRUD", a.srv.ACLTokenCRUD},
52		{"ACLRoleList", a.srv.ACLRoleList},
53		{"ACLRoleCreate", a.srv.ACLRoleCreate},
54		{"ACLRoleCRUD", a.srv.ACLRoleCRUD},
55		{"ACLBindingRuleList", a.srv.ACLBindingRuleList},
56		{"ACLBindingRuleCreate", a.srv.ACLBindingRuleCreate},
57		{"ACLBindingRuleCRUD", a.srv.ACLBindingRuleCRUD},
58		{"ACLAuthMethodList", a.srv.ACLAuthMethodList},
59		{"ACLAuthMethodCreate", a.srv.ACLAuthMethodCreate},
60		{"ACLAuthMethodCRUD", a.srv.ACLAuthMethodCRUD},
61		{"ACLLogin", a.srv.ACLLogin},
62		{"ACLLogout", a.srv.ACLLogout},
63		{"ACLAuthorize", a.srv.ACLAuthorize},
64	}
65	testrpc.WaitForLeader(t, a.RPC, "dc1")
66	for _, tt := range tests {
67		t.Run(tt.name, func(t *testing.T) {
68			req, _ := http.NewRequest("PUT", "/should/not/care", nil)
69			resp := httptest.NewRecorder()
70			obj, err := tt.fn(resp, req)
71			require.NoError(t, err)
72			require.Nil(t, obj)
73			require.Equal(t, http.StatusUnauthorized, resp.Code)
74			require.Contains(t, resp.Body.String(), "ACL support disabled")
75		})
76	}
77}
78
79func jsonBody(v interface{}) io.Reader {
80	body := bytes.NewBuffer(nil)
81	enc := json.NewEncoder(body)
82	enc.Encode(v)
83	return body
84}
85
86func TestACL_Bootstrap(t *testing.T) {
87	t.Parallel()
88	a := NewTestAgent(t, TestACLConfig()+`
89      acl_master_token = ""
90   `)
91	defer a.Shutdown()
92
93	tests := []struct {
94		name   string
95		method string
96		code   int
97		token  bool
98	}{
99		{"bootstrap", "PUT", http.StatusOK, true},
100		{"not again", "PUT", http.StatusForbidden, false},
101	}
102	testrpc.WaitForLeader(t, a.RPC, "dc1")
103	for _, tt := range tests {
104		t.Run(tt.name, func(t *testing.T) {
105			resp := httptest.NewRecorder()
106			req, _ := http.NewRequest(tt.method, "/v1/acl/bootstrap", nil)
107			out, err := a.srv.ACLBootstrap(resp, req)
108			if tt.token && err != nil {
109				t.Fatalf("err: %v", err)
110			}
111			if got, want := resp.Code, tt.code; got != want {
112				t.Fatalf("got %d want %d", got, want)
113			}
114			if tt.token {
115				wrap, ok := out.(*aclBootstrapResponse)
116				if !ok {
117					t.Fatalf("bad: %T", out)
118				}
119				if len(wrap.ID) != len("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") {
120					t.Fatalf("bad: %v", wrap)
121				}
122				if wrap.ID != wrap.SecretID {
123					t.Fatalf("bad: %v", wrap)
124				}
125			} else {
126				if out != nil {
127					t.Fatalf("bad: %T", out)
128				}
129			}
130		})
131	}
132}
133
134func TestACL_HTTP(t *testing.T) {
135	t.Parallel()
136	a := NewTestAgent(t, TestACLConfig())
137	defer a.Shutdown()
138
139	testrpc.WaitForLeader(t, a.RPC, "dc1")
140
141	idMap := make(map[string]string)
142	policyMap := make(map[string]*structs.ACLPolicy)
143	roleMap := make(map[string]*structs.ACLRole)
144	tokenMap := make(map[string]*structs.ACLToken)
145
146	// This is all done as a subtest for a couple reasons
147	// 1. It uses only 1 test agent and these are
148	//    somewhat expensive to bring up and tear down often
149	// 2. Instead of having to bring up a new agent and prime
150	//    the ACL system with some data before running the test
151	//    we can intelligently order these tests so we can still
152	//    test everything with less actual operations and do
153	//    so in a manner that is less prone to being flaky
154	//
155	// This could be accomplished with just blocks of code but I find
156	// the go test output nicer to pinpoint the error if they are grouped.
157	//
158	// NOTE: None of the subtests should be parallelized in order for
159	// any of it to work properly.
160	t.Run("Policy", func(t *testing.T) {
161		t.Run("Create", func(t *testing.T) {
162			policyInput := &structs.ACLPolicy{
163				Name:        "test",
164				Description: "test",
165				Rules:       `acl = "read"`,
166				Datacenters: []string{"dc1"},
167			}
168
169			req, _ := http.NewRequest("PUT", "/v1/acl/policy?token=root", jsonBody(policyInput))
170			resp := httptest.NewRecorder()
171			obj, err := a.srv.ACLPolicyCreate(resp, req)
172			require.NoError(t, err)
173
174			policy, ok := obj.(*structs.ACLPolicy)
175			require.True(t, ok)
176
177			// 36 = length of the string form of uuids
178			require.Len(t, policy.ID, 36)
179			require.Equal(t, policyInput.Name, policy.Name)
180			require.Equal(t, policyInput.Description, policy.Description)
181			require.Equal(t, policyInput.Rules, policy.Rules)
182			require.Equal(t, policyInput.Datacenters, policy.Datacenters)
183			require.True(t, policy.CreateIndex > 0)
184			require.Equal(t, policy.CreateIndex, policy.ModifyIndex)
185			require.NotNil(t, policy.Hash)
186			require.NotEqual(t, policy.Hash, []byte{})
187
188			idMap["policy-test"] = policy.ID
189			policyMap[policy.ID] = policy
190		})
191
192		t.Run("Minimal", func(t *testing.T) {
193			policyInput := &structs.ACLPolicy{
194				Name:  "minimal",
195				Rules: `key_prefix "" { policy = "read" }`,
196			}
197
198			req, _ := http.NewRequest("PUT", "/v1/acl/policy?token=root", jsonBody(policyInput))
199			resp := httptest.NewRecorder()
200			obj, err := a.srv.ACLPolicyCreate(resp, req)
201			require.NoError(t, err)
202
203			policy, ok := obj.(*structs.ACLPolicy)
204			require.True(t, ok)
205
206			// 36 = length of the string form of uuids
207			require.Len(t, policy.ID, 36)
208			require.Equal(t, policyInput.Name, policy.Name)
209			require.Equal(t, policyInput.Description, policy.Description)
210			require.Equal(t, policyInput.Rules, policy.Rules)
211			require.Equal(t, policyInput.Datacenters, policy.Datacenters)
212			require.True(t, policy.CreateIndex > 0)
213			require.Equal(t, policy.CreateIndex, policy.ModifyIndex)
214			require.NotNil(t, policy.Hash)
215			require.NotEqual(t, policy.Hash, []byte{})
216
217			idMap["policy-minimal"] = policy.ID
218			policyMap[policy.ID] = policy
219		})
220
221		t.Run("Name Chars", func(t *testing.T) {
222			policyInput := &structs.ACLPolicy{
223				Name:  "read-all_nodes-012",
224				Rules: `node_prefix "" { policy = "read" }`,
225			}
226
227			req, _ := http.NewRequest("PUT", "/v1/acl/policy?token=root", jsonBody(policyInput))
228			resp := httptest.NewRecorder()
229			obj, err := a.srv.ACLPolicyCreate(resp, req)
230			require.NoError(t, err)
231
232			policy, ok := obj.(*structs.ACLPolicy)
233			require.True(t, ok)
234
235			// 36 = length of the string form of uuids
236			require.Len(t, policy.ID, 36)
237			require.Equal(t, policyInput.Name, policy.Name)
238			require.Equal(t, policyInput.Description, policy.Description)
239			require.Equal(t, policyInput.Rules, policy.Rules)
240			require.Equal(t, policyInput.Datacenters, policy.Datacenters)
241			require.True(t, policy.CreateIndex > 0)
242			require.Equal(t, policy.CreateIndex, policy.ModifyIndex)
243			require.NotNil(t, policy.Hash)
244			require.NotEqual(t, policy.Hash, []byte{})
245
246			idMap["policy-read-all-nodes"] = policy.ID
247			policyMap[policy.ID] = policy
248		})
249
250		t.Run("Update Name ID Mismatch", func(t *testing.T) {
251			policyInput := &structs.ACLPolicy{
252				ID:          "ac7560be-7f11-4d6d-bfcf-15633c2090fd",
253				Name:        "read-all-nodes",
254				Description: "Can read all node information",
255				Rules:       `node_prefix "" { policy = "read" }`,
256				Datacenters: []string{"dc1"},
257			}
258
259			req, _ := http.NewRequest("PUT", "/v1/acl/policy/"+idMap["policy-read-all-nodes"]+"?token=root", jsonBody(policyInput))
260			resp := httptest.NewRecorder()
261			_, err := a.srv.ACLPolicyCRUD(resp, req)
262			require.Error(t, err)
263			_, ok := err.(BadRequestError)
264			require.True(t, ok)
265		})
266
267		t.Run("Policy CRUD Missing ID in URL", func(t *testing.T) {
268			req, _ := http.NewRequest("GET", "/v1/acl/policy/?token=root", nil)
269			resp := httptest.NewRecorder()
270			_, err := a.srv.ACLPolicyCRUD(resp, req)
271			require.Error(t, err)
272			_, ok := err.(BadRequestError)
273			require.True(t, ok)
274		})
275
276		t.Run("Update", func(t *testing.T) {
277			policyInput := &structs.ACLPolicy{
278				Name:        "read-all-nodes",
279				Description: "Can read all node information",
280				Rules:       `node_prefix "" { policy = "read" }`,
281				Datacenters: []string{"dc1"},
282			}
283
284			req, _ := http.NewRequest("PUT", "/v1/acl/policy/"+idMap["policy-read-all-nodes"]+"?token=root", jsonBody(policyInput))
285			resp := httptest.NewRecorder()
286			obj, err := a.srv.ACLPolicyCRUD(resp, req)
287			require.NoError(t, err)
288
289			policy, ok := obj.(*structs.ACLPolicy)
290			require.True(t, ok)
291
292			// 36 = length of the string form of uuids
293			require.Len(t, policy.ID, 36)
294			require.Equal(t, policyInput.Name, policy.Name)
295			require.Equal(t, policyInput.Description, policy.Description)
296			require.Equal(t, policyInput.Rules, policy.Rules)
297			require.Equal(t, policyInput.Datacenters, policy.Datacenters)
298			require.True(t, policy.CreateIndex > 0)
299			require.True(t, policy.CreateIndex < policy.ModifyIndex)
300			require.NotNil(t, policy.Hash)
301			require.NotEqual(t, policy.Hash, []byte{})
302
303			idMap["policy-read-all-nodes"] = policy.ID
304			policyMap[policy.ID] = policy
305		})
306
307		t.Run("ID Supplied", func(t *testing.T) {
308			policyInput := &structs.ACLPolicy{
309				ID:          "12123d01-37f1-47e6-b55b-32328652bd38",
310				Name:        "with-id",
311				Description: "test",
312				Rules:       `acl = "read"`,
313				Datacenters: []string{"dc1"},
314			}
315
316			req, _ := http.NewRequest("PUT", "/v1/acl/policy?token=root", jsonBody(policyInput))
317			resp := httptest.NewRecorder()
318			_, err := a.srv.ACLPolicyCreate(resp, req)
319			require.Error(t, err)
320			_, ok := err.(BadRequestError)
321			require.True(t, ok)
322		})
323
324		t.Run("Invalid payload", func(t *testing.T) {
325			body := bytes.NewBuffer(nil)
326			body.Write([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
327
328			req, _ := http.NewRequest("PUT", "/v1/acl/policy?token=root", body)
329			resp := httptest.NewRecorder()
330			_, err := a.srv.ACLPolicyCreate(resp, req)
331			require.Error(t, err)
332			_, ok := err.(BadRequestError)
333			require.True(t, ok)
334		})
335
336		t.Run("Delete", func(t *testing.T) {
337			req, _ := http.NewRequest("DELETE", "/v1/acl/policy/"+idMap["policy-minimal"]+"?token=root", nil)
338			resp := httptest.NewRecorder()
339			_, err := a.srv.ACLPolicyCRUD(resp, req)
340			require.NoError(t, err)
341			delete(policyMap, idMap["policy-minimal"])
342			delete(idMap, "policy-minimal")
343		})
344
345		t.Run("List", func(t *testing.T) {
346			req, _ := http.NewRequest("GET", "/v1/acl/policies?token=root", nil)
347			resp := httptest.NewRecorder()
348			raw, err := a.srv.ACLPolicyList(resp, req)
349			require.NoError(t, err)
350			policies, ok := raw.(structs.ACLPolicyListStubs)
351			require.True(t, ok)
352
353			// 2 we just created + global management
354			require.Len(t, policies, 3)
355
356			for policyID, expected := range policyMap {
357				found := false
358				for _, actual := range policies {
359					if actual.ID == policyID {
360						require.Equal(t, expected.Name, actual.Name)
361						require.Equal(t, expected.Datacenters, actual.Datacenters)
362						require.Equal(t, expected.Hash, actual.Hash)
363						require.Equal(t, expected.CreateIndex, actual.CreateIndex)
364						require.Equal(t, expected.ModifyIndex, actual.ModifyIndex)
365						found = true
366						break
367					}
368				}
369
370				require.True(t, found)
371			}
372		})
373
374		t.Run("Read", func(t *testing.T) {
375			req, _ := http.NewRequest("GET", "/v1/acl/policy/"+idMap["policy-read-all-nodes"]+"?token=root", nil)
376			resp := httptest.NewRecorder()
377			raw, err := a.srv.ACLPolicyCRUD(resp, req)
378			require.NoError(t, err)
379			policy, ok := raw.(*structs.ACLPolicy)
380			require.True(t, ok)
381			require.Equal(t, policyMap[idMap["policy-read-all-nodes"]], policy)
382		})
383
384		t.Run("Read Name", func(t *testing.T) {
385			policyName := "read-all-nodes"
386			req, _ := http.NewRequest("GET", "/v1/acl/policy/name/"+policyName+"?token=root", nil)
387			resp := httptest.NewRecorder()
388			raw, err := a.srv.ACLPolicyReadByName(resp, req)
389			require.NoError(t, err)
390			policy, ok := raw.(*structs.ACLPolicy)
391			require.True(t, ok)
392			require.Equal(t, policyMap[idMap["policy-"+policyName]], policy)
393		})
394	})
395
396	t.Run("Role", func(t *testing.T) {
397		t.Run("Create", func(t *testing.T) {
398			roleInput := &structs.ACLRole{
399				Name:        "test",
400				Description: "test",
401				Policies: []structs.ACLRolePolicyLink{
402					structs.ACLRolePolicyLink{
403						ID:   idMap["policy-test"],
404						Name: policyMap[idMap["policy-test"]].Name,
405					},
406					structs.ACLRolePolicyLink{
407						ID:   idMap["policy-read-all-nodes"],
408						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
409					},
410				},
411				NodeIdentities: []*structs.ACLNodeIdentity{
412					&structs.ACLNodeIdentity{
413						NodeName:   "web-node",
414						Datacenter: "foo",
415					},
416				},
417			}
418
419			req, _ := http.NewRequest("PUT", "/v1/acl/role?token=root", jsonBody(roleInput))
420			resp := httptest.NewRecorder()
421			obj, err := a.srv.ACLRoleCreate(resp, req)
422			require.NoError(t, err)
423
424			role, ok := obj.(*structs.ACLRole)
425			require.True(t, ok)
426
427			// 36 = length of the string form of uuids
428			require.Len(t, role.ID, 36)
429			require.Equal(t, roleInput.Name, role.Name)
430			require.Equal(t, roleInput.Description, role.Description)
431			require.Equal(t, roleInput.Policies, role.Policies)
432			require.Equal(t, roleInput.NodeIdentities, role.NodeIdentities)
433			require.True(t, role.CreateIndex > 0)
434			require.Equal(t, role.CreateIndex, role.ModifyIndex)
435			require.NotNil(t, role.Hash)
436			require.NotEqual(t, role.Hash, []byte{})
437
438			idMap["role-test"] = role.ID
439			roleMap[role.ID] = role
440		})
441
442		t.Run("Name Chars", func(t *testing.T) {
443			roleInput := &structs.ACLRole{
444				Name: "service-id-web",
445				ServiceIdentities: []*structs.ACLServiceIdentity{
446					&structs.ACLServiceIdentity{
447						ServiceName: "web",
448					},
449				},
450			}
451
452			req, _ := http.NewRequest("PUT", "/v1/acl/role?token=root", jsonBody(roleInput))
453			resp := httptest.NewRecorder()
454			obj, err := a.srv.ACLRoleCreate(resp, req)
455			require.NoError(t, err)
456
457			role, ok := obj.(*structs.ACLRole)
458			require.True(t, ok)
459
460			// 36 = length of the string form of uuids
461			require.Len(t, role.ID, 36)
462			require.Equal(t, roleInput.Name, role.Name)
463			require.Equal(t, roleInput.Description, role.Description)
464			require.Equal(t, roleInput.ServiceIdentities, role.ServiceIdentities)
465			require.True(t, role.CreateIndex > 0)
466			require.Equal(t, role.CreateIndex, role.ModifyIndex)
467			require.NotNil(t, role.Hash)
468			require.NotEqual(t, role.Hash, []byte{})
469
470			idMap["role-service-id-web"] = role.ID
471			roleMap[role.ID] = role
472		})
473
474		t.Run("Update Name ID Mismatch", func(t *testing.T) {
475			roleInput := &structs.ACLRole{
476				ID:          "ac7560be-7f11-4d6d-bfcf-15633c2090fd",
477				Name:        "test",
478				Description: "test",
479				ServiceIdentities: []*structs.ACLServiceIdentity{
480					&structs.ACLServiceIdentity{
481						ServiceName: "db",
482					},
483				},
484			}
485
486			req, _ := http.NewRequest("PUT", "/v1/acl/role/"+idMap["role-test"]+"?token=root", jsonBody(roleInput))
487			resp := httptest.NewRecorder()
488			_, err := a.srv.ACLRoleCRUD(resp, req)
489			require.Error(t, err)
490			_, ok := err.(BadRequestError)
491			require.True(t, ok)
492		})
493
494		t.Run("Role CRUD Missing ID in URL", func(t *testing.T) {
495			req, _ := http.NewRequest("GET", "/v1/acl/role/?token=root", nil)
496			resp := httptest.NewRecorder()
497			_, err := a.srv.ACLRoleCRUD(resp, req)
498			require.Error(t, err)
499			_, ok := err.(BadRequestError)
500			require.True(t, ok)
501		})
502
503		t.Run("Update", func(t *testing.T) {
504			roleInput := &structs.ACLRole{
505				Name:        "test",
506				Description: "test",
507				ServiceIdentities: []*structs.ACLServiceIdentity{
508					&structs.ACLServiceIdentity{
509						ServiceName: "web-indexer",
510					},
511				},
512				NodeIdentities: []*structs.ACLNodeIdentity{
513					&structs.ACLNodeIdentity{
514						NodeName:   "web-node",
515						Datacenter: "foo",
516					},
517				},
518			}
519
520			req, _ := http.NewRequest("PUT", "/v1/acl/role/"+idMap["role-test"]+"?token=root", jsonBody(roleInput))
521			resp := httptest.NewRecorder()
522			obj, err := a.srv.ACLRoleCRUD(resp, req)
523			require.NoError(t, err)
524
525			role, ok := obj.(*structs.ACLRole)
526			require.True(t, ok)
527
528			// 36 = length of the string form of uuids
529			require.Len(t, role.ID, 36)
530			require.Equal(t, roleInput.Name, role.Name)
531			require.Equal(t, roleInput.Description, role.Description)
532			require.Equal(t, roleInput.Policies, role.Policies)
533			require.Equal(t, roleInput.ServiceIdentities, role.ServiceIdentities)
534			require.Equal(t, roleInput.NodeIdentities, role.NodeIdentities)
535			require.True(t, role.CreateIndex > 0)
536			require.True(t, role.CreateIndex < role.ModifyIndex)
537			require.NotNil(t, role.Hash)
538			require.NotEqual(t, role.Hash, []byte{})
539
540			idMap["role-test"] = role.ID
541			roleMap[role.ID] = role
542		})
543
544		t.Run("ID Supplied", func(t *testing.T) {
545			roleInput := &structs.ACLRole{
546				ID:          "12123d01-37f1-47e6-b55b-32328652bd38",
547				Name:        "with-id",
548				Description: "test",
549				ServiceIdentities: []*structs.ACLServiceIdentity{
550					&structs.ACLServiceIdentity{
551						ServiceName: "foobar",
552					},
553				},
554			}
555
556			req, _ := http.NewRequest("PUT", "/v1/acl/role?token=root", jsonBody(roleInput))
557			resp := httptest.NewRecorder()
558			_, err := a.srv.ACLRoleCreate(resp, req)
559			require.Error(t, err)
560			_, ok := err.(BadRequestError)
561			require.True(t, ok)
562		})
563
564		t.Run("Invalid payload", func(t *testing.T) {
565			body := bytes.NewBuffer(nil)
566			body.Write([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
567
568			req, _ := http.NewRequest("PUT", "/v1/acl/role?token=root", body)
569			resp := httptest.NewRecorder()
570			_, err := a.srv.ACLRoleCreate(resp, req)
571			require.Error(t, err)
572			_, ok := err.(BadRequestError)
573			require.True(t, ok)
574		})
575
576		t.Run("Delete", func(t *testing.T) {
577			req, _ := http.NewRequest("DELETE", "/v1/acl/role/"+idMap["role-service-id-web"]+"?token=root", nil)
578			resp := httptest.NewRecorder()
579			_, err := a.srv.ACLRoleCRUD(resp, req)
580			require.NoError(t, err)
581			delete(roleMap, idMap["role-service-id-web"])
582			delete(idMap, "role-service-id-web")
583		})
584
585		t.Run("List", func(t *testing.T) {
586			req, _ := http.NewRequest("GET", "/v1/acl/roles?token=root", nil)
587			resp := httptest.NewRecorder()
588			raw, err := a.srv.ACLRoleList(resp, req)
589			require.NoError(t, err)
590			roles, ok := raw.(structs.ACLRoles)
591			require.True(t, ok)
592
593			// 1 we just created
594			require.Len(t, roles, 1)
595
596			for roleID, expected := range roleMap {
597				found := false
598				for _, actual := range roles {
599					if actual.ID == roleID {
600						require.Equal(t, expected.Name, actual.Name)
601						require.Equal(t, expected.Policies, actual.Policies)
602						require.Equal(t, expected.ServiceIdentities, actual.ServiceIdentities)
603						require.Equal(t, expected.Hash, actual.Hash)
604						require.Equal(t, expected.CreateIndex, actual.CreateIndex)
605						require.Equal(t, expected.ModifyIndex, actual.ModifyIndex)
606						found = true
607						break
608					}
609				}
610
611				require.True(t, found)
612			}
613		})
614
615		t.Run("Read", func(t *testing.T) {
616			req, _ := http.NewRequest("GET", "/v1/acl/role/"+idMap["role-test"]+"?token=root", nil)
617			resp := httptest.NewRecorder()
618			raw, err := a.srv.ACLRoleCRUD(resp, req)
619			require.NoError(t, err)
620			role, ok := raw.(*structs.ACLRole)
621			require.True(t, ok)
622			require.Equal(t, roleMap[idMap["role-test"]], role)
623		})
624	})
625
626	t.Run("Token", func(t *testing.T) {
627		t.Run("Create", func(t *testing.T) {
628			tokenInput := &structs.ACLToken{
629				Description: "test",
630				Policies: []structs.ACLTokenPolicyLink{
631					structs.ACLTokenPolicyLink{
632						ID:   idMap["policy-test"],
633						Name: policyMap[idMap["policy-test"]].Name,
634					},
635					structs.ACLTokenPolicyLink{
636						ID:   idMap["policy-read-all-nodes"],
637						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
638					},
639				},
640				NodeIdentities: []*structs.ACLNodeIdentity{
641					&structs.ACLNodeIdentity{
642						NodeName:   "foo",
643						Datacenter: "bar",
644					},
645				},
646			}
647
648			req, _ := http.NewRequest("PUT", "/v1/acl/token?token=root", jsonBody(tokenInput))
649			resp := httptest.NewRecorder()
650			obj, err := a.srv.ACLTokenCreate(resp, req)
651			require.NoError(t, err)
652
653			token, ok := obj.(*structs.ACLToken)
654			require.True(t, ok)
655
656			// 36 = length of the string form of uuids
657			require.Len(t, token.AccessorID, 36)
658			require.Len(t, token.SecretID, 36)
659			require.Equal(t, tokenInput.Description, token.Description)
660			require.Equal(t, tokenInput.Policies, token.Policies)
661			require.Equal(t, tokenInput.NodeIdentities, token.NodeIdentities)
662			require.True(t, token.CreateIndex > 0)
663			require.Equal(t, token.CreateIndex, token.ModifyIndex)
664			require.NotNil(t, token.Hash)
665			require.NotEqual(t, token.Hash, []byte{})
666
667			idMap["token-test"] = token.AccessorID
668			tokenMap[token.AccessorID] = token
669		})
670		t.Run("Create Local", func(t *testing.T) {
671			tokenInput := &structs.ACLToken{
672				Description: "local",
673				Policies: []structs.ACLTokenPolicyLink{
674					structs.ACLTokenPolicyLink{
675						ID:   idMap["policy-test"],
676						Name: policyMap[idMap["policy-test"]].Name,
677					},
678					structs.ACLTokenPolicyLink{
679						ID:   idMap["policy-read-all-nodes"],
680						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
681					},
682				},
683				Local: true,
684			}
685
686			req, _ := http.NewRequest("PUT", "/v1/acl/token?token=root", jsonBody(tokenInput))
687			resp := httptest.NewRecorder()
688			obj, err := a.srv.ACLTokenCreate(resp, req)
689			require.NoError(t, err)
690
691			token, ok := obj.(*structs.ACLToken)
692			require.True(t, ok)
693
694			// 36 = length of the string form of uuids
695			require.Len(t, token.AccessorID, 36)
696			require.Len(t, token.SecretID, 36)
697			require.Equal(t, tokenInput.Description, token.Description)
698			require.Equal(t, tokenInput.Policies, token.Policies)
699			require.True(t, token.CreateIndex > 0)
700			require.Equal(t, token.CreateIndex, token.ModifyIndex)
701			require.NotNil(t, token.Hash)
702			require.NotEqual(t, token.Hash, []byte{})
703
704			idMap["token-local"] = token.AccessorID
705			tokenMap[token.AccessorID] = token
706		})
707		t.Run("Read", func(t *testing.T) {
708			expected := tokenMap[idMap["token-test"]]
709			req, _ := http.NewRequest("GET", "/v1/acl/token/"+expected.AccessorID+"?token=root", nil)
710			resp := httptest.NewRecorder()
711			obj, err := a.srv.ACLTokenCRUD(resp, req)
712			require.NoError(t, err)
713			token, ok := obj.(*structs.ACLToken)
714			require.True(t, ok)
715			require.Equal(t, expected, token)
716		})
717		t.Run("Self", func(t *testing.T) {
718			expected := tokenMap[idMap["token-test"]]
719			req, _ := http.NewRequest("GET", "/v1/acl/token/self?token="+expected.SecretID, nil)
720			resp := httptest.NewRecorder()
721			obj, err := a.srv.ACLTokenSelf(resp, req)
722			require.NoError(t, err)
723			token, ok := obj.(*structs.ACLToken)
724			require.True(t, ok)
725			require.Equal(t, expected, token)
726		})
727		t.Run("Clone", func(t *testing.T) {
728			tokenInput := &structs.ACLToken{
729				Description: "cloned token",
730			}
731
732			baseToken := tokenMap[idMap["token-test"]]
733
734			req, _ := http.NewRequest("PUT", "/v1/acl/token/"+baseToken.AccessorID+"/clone?token=root", jsonBody(tokenInput))
735			resp := httptest.NewRecorder()
736			obj, err := a.srv.ACLTokenCRUD(resp, req)
737			require.NoError(t, err)
738			token, ok := obj.(*structs.ACLToken)
739			require.True(t, ok)
740
741			require.NotEqual(t, baseToken.AccessorID, token.AccessorID)
742			require.NotEqual(t, baseToken.SecretID, token.SecretID)
743			require.Equal(t, tokenInput.Description, token.Description)
744			require.Equal(t, baseToken.Policies, token.Policies)
745			require.True(t, token.CreateIndex > 0)
746			require.Equal(t, token.CreateIndex, token.ModifyIndex)
747			require.NotNil(t, token.Hash)
748			require.NotEqual(t, token.Hash, []byte{})
749
750			idMap["token-cloned"] = token.AccessorID
751			tokenMap[token.AccessorID] = token
752		})
753		t.Run("Update", func(t *testing.T) {
754			originalToken := tokenMap[idMap["token-cloned"]]
755
756			// Accessor and Secret will be filled in
757			tokenInput := &structs.ACLToken{
758				Description: "Better description for this cloned token",
759				Policies: []structs.ACLTokenPolicyLink{
760					structs.ACLTokenPolicyLink{
761						ID:   idMap["policy-read-all-nodes"],
762						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
763					},
764				},
765				NodeIdentities: []*structs.ACLNodeIdentity{
766					&structs.ACLNodeIdentity{
767						NodeName:   "foo",
768						Datacenter: "bar",
769					},
770				},
771			}
772
773			req, _ := http.NewRequest("PUT", "/v1/acl/token/"+originalToken.AccessorID+"?token=root", jsonBody(tokenInput))
774			resp := httptest.NewRecorder()
775			obj, err := a.srv.ACLTokenCRUD(resp, req)
776			require.NoError(t, err)
777			token, ok := obj.(*structs.ACLToken)
778			require.True(t, ok)
779
780			require.Equal(t, originalToken.AccessorID, token.AccessorID)
781			require.Equal(t, originalToken.SecretID, token.SecretID)
782			require.Equal(t, tokenInput.Description, token.Description)
783			require.Equal(t, tokenInput.Policies, token.Policies)
784			require.Equal(t, tokenInput.NodeIdentities, token.NodeIdentities)
785			require.True(t, token.CreateIndex > 0)
786			require.True(t, token.CreateIndex < token.ModifyIndex)
787			require.NotNil(t, token.Hash)
788			require.NotEqual(t, token.Hash, []byte{})
789			require.NotEqual(t, token.Hash, originalToken.Hash)
790
791			tokenMap[token.AccessorID] = token
792		})
793
794		t.Run("CRUD Missing Token Accessor ID", func(t *testing.T) {
795			req, _ := http.NewRequest("GET", "/v1/acl/token/?token=root", nil)
796			resp := httptest.NewRecorder()
797			obj, err := a.srv.ACLTokenCRUD(resp, req)
798			require.Error(t, err)
799			require.Nil(t, obj)
800			_, ok := err.(BadRequestError)
801			require.True(t, ok)
802		})
803		t.Run("Update Accessor Mismatch", func(t *testing.T) {
804			originalToken := tokenMap[idMap["token-cloned"]]
805
806			// Accessor and Secret will be filled in
807			tokenInput := &structs.ACLToken{
808				AccessorID:  "e8aeb69a-0ace-42b9-b95f-d1d9eafe1561",
809				Description: "Better description for this cloned token",
810				Policies: []structs.ACLTokenPolicyLink{
811					structs.ACLTokenPolicyLink{
812						ID:   idMap["policy-read-all-nodes"],
813						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
814					},
815				},
816			}
817
818			req, _ := http.NewRequest("PUT", "/v1/acl/token/"+originalToken.AccessorID+"?token=root", jsonBody(tokenInput))
819			resp := httptest.NewRecorder()
820			obj, err := a.srv.ACLTokenCRUD(resp, req)
821			require.Error(t, err)
822			require.Nil(t, obj)
823			_, ok := err.(BadRequestError)
824			require.True(t, ok)
825		})
826		t.Run("Delete", func(t *testing.T) {
827			req, _ := http.NewRequest("DELETE", "/v1/acl/token/"+idMap["token-cloned"]+"?token=root", nil)
828			resp := httptest.NewRecorder()
829			_, err := a.srv.ACLTokenCRUD(resp, req)
830			require.NoError(t, err)
831			delete(tokenMap, idMap["token-cloned"])
832			delete(idMap, "token-cloned")
833		})
834		t.Run("List", func(t *testing.T) {
835			req, _ := http.NewRequest("GET", "/v1/acl/tokens?token=root", nil)
836			resp := httptest.NewRecorder()
837			raw, err := a.srv.ACLTokenList(resp, req)
838			require.NoError(t, err)
839			tokens, ok := raw.(structs.ACLTokenListStubs)
840			require.True(t, ok)
841
842			// 3 tokens created but 1 was deleted + master token + anon token
843			require.Len(t, tokens, 4)
844
845			// this loop doesn't verify anything about the master token
846			for tokenID, expected := range tokenMap {
847				found := false
848				for _, actual := range tokens {
849					if actual.AccessorID == tokenID {
850						require.Equal(t, expected.Description, actual.Description)
851						require.Equal(t, expected.Policies, actual.Policies)
852						require.Equal(t, expected.Local, actual.Local)
853						require.Equal(t, expected.CreateTime, actual.CreateTime)
854						require.Equal(t, expected.Hash, actual.Hash)
855						require.Equal(t, expected.CreateIndex, actual.CreateIndex)
856						require.Equal(t, expected.ModifyIndex, actual.ModifyIndex)
857						found = true
858						break
859					}
860				}
861				require.True(t, found)
862			}
863		})
864		t.Run("List by Policy", func(t *testing.T) {
865			req, _ := http.NewRequest("GET", "/v1/acl/tokens?token=root&policy="+structs.ACLPolicyGlobalManagementID, nil)
866			resp := httptest.NewRecorder()
867			raw, err := a.srv.ACLTokenList(resp, req)
868			require.NoError(t, err)
869			tokens, ok := raw.(structs.ACLTokenListStubs)
870			require.True(t, ok)
871			require.Len(t, tokens, 1)
872			token := tokens[0]
873			require.Equal(t, "Master Token", token.Description)
874			require.Len(t, token.Policies, 1)
875			require.Equal(t, structs.ACLPolicyGlobalManagementID, token.Policies[0].ID)
876		})
877		t.Run("Create with Accessor", func(t *testing.T) {
878			tokenInput := &structs.ACLToken{
879				AccessorID:  "56e8e6a3-708b-4a2f-8ab3-b973cce39108",
880				Description: "test",
881				Policies: []structs.ACLTokenPolicyLink{
882					structs.ACLTokenPolicyLink{
883						ID:   idMap["policy-test"],
884						Name: policyMap[idMap["policy-test"]].Name,
885					},
886					structs.ACLTokenPolicyLink{
887						ID:   idMap["policy-read-all-nodes"],
888						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
889					},
890				},
891			}
892
893			req, _ := http.NewRequest("PUT", "/v1/acl/token?token=root", jsonBody(tokenInput))
894			resp := httptest.NewRecorder()
895			obj, err := a.srv.ACLTokenCreate(resp, req)
896			require.NoError(t, err)
897
898			token, ok := obj.(*structs.ACLToken)
899			require.True(t, ok)
900
901			// 36 = length of the string form of uuids
902			require.Equal(t, tokenInput.AccessorID, token.AccessorID)
903			require.Len(t, token.SecretID, 36)
904			require.Equal(t, tokenInput.Description, token.Description)
905			require.Equal(t, tokenInput.Policies, token.Policies)
906			require.True(t, token.CreateIndex > 0)
907			require.Equal(t, token.CreateIndex, token.ModifyIndex)
908			require.NotNil(t, token.Hash)
909			require.NotEqual(t, token.Hash, []byte{})
910
911			idMap["token-test"] = token.AccessorID
912			tokenMap[token.AccessorID] = token
913		})
914
915		t.Run("Create with Secret", func(t *testing.T) {
916			tokenInput := &structs.ACLToken{
917				SecretID:    "4e3efd15-d06c-442e-a7cc-1744f55c8dea",
918				Description: "test",
919				Policies: []structs.ACLTokenPolicyLink{
920					structs.ACLTokenPolicyLink{
921						ID:   idMap["policy-test"],
922						Name: policyMap[idMap["policy-test"]].Name,
923					},
924					structs.ACLTokenPolicyLink{
925						ID:   idMap["policy-read-all-nodes"],
926						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
927					},
928				},
929			}
930
931			req, _ := http.NewRequest("PUT", "/v1/acl/token?token=root", jsonBody(tokenInput))
932			resp := httptest.NewRecorder()
933			obj, err := a.srv.ACLTokenCreate(resp, req)
934			require.NoError(t, err)
935
936			token, ok := obj.(*structs.ACLToken)
937			require.True(t, ok)
938
939			// 36 = length of the string form of uuids
940			require.Equal(t, tokenInput.SecretID, token.SecretID)
941			require.Len(t, token.AccessorID, 36)
942			require.Equal(t, tokenInput.Description, token.Description)
943			require.Equal(t, tokenInput.Policies, token.Policies)
944			require.True(t, token.CreateIndex > 0)
945			require.Equal(t, token.CreateIndex, token.ModifyIndex)
946			require.NotNil(t, token.Hash)
947			require.NotEqual(t, token.Hash, []byte{})
948
949			idMap["token-test"] = token.AccessorID
950			tokenMap[token.AccessorID] = token
951		})
952
953		t.Run("Create with Accessor and Secret", func(t *testing.T) {
954			tokenInput := &structs.ACLToken{
955				AccessorID:  "dee863fa-e548-4c61-a96f-9aa07999249f",
956				SecretID:    "10126ffa-b28f-4137-b9a9-e89ab1e97c5b",
957				Description: "test",
958				Policies: []structs.ACLTokenPolicyLink{
959					structs.ACLTokenPolicyLink{
960						ID:   idMap["policy-test"],
961						Name: policyMap[idMap["policy-test"]].Name,
962					},
963					structs.ACLTokenPolicyLink{
964						ID:   idMap["policy-read-all-nodes"],
965						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
966					},
967				},
968			}
969
970			req, _ := http.NewRequest("PUT", "/v1/acl/token?token=root", jsonBody(tokenInput))
971			resp := httptest.NewRecorder()
972			obj, err := a.srv.ACLTokenCreate(resp, req)
973			require.NoError(t, err)
974
975			token, ok := obj.(*structs.ACLToken)
976			require.True(t, ok)
977
978			// 36 = length of the string form of uuids
979			require.Equal(t, tokenInput.SecretID, token.SecretID)
980			require.Equal(t, tokenInput.AccessorID, token.AccessorID)
981			require.Equal(t, tokenInput.Description, token.Description)
982			require.Equal(t, tokenInput.Policies, token.Policies)
983			require.True(t, token.CreateIndex > 0)
984			require.Equal(t, token.CreateIndex, token.ModifyIndex)
985			require.NotNil(t, token.Hash)
986			require.NotEqual(t, token.Hash, []byte{})
987
988			idMap["token-test"] = token.AccessorID
989			tokenMap[token.AccessorID] = token
990		})
991
992		t.Run("Create with Accessor Dup", func(t *testing.T) {
993			tokenInput := &structs.ACLToken{
994				AccessorID:  "dee863fa-e548-4c61-a96f-9aa07999249f",
995				Description: "test",
996				Policies: []structs.ACLTokenPolicyLink{
997					structs.ACLTokenPolicyLink{
998						ID:   idMap["policy-test"],
999						Name: policyMap[idMap["policy-test"]].Name,
1000					},
1001					structs.ACLTokenPolicyLink{
1002						ID:   idMap["policy-read-all-nodes"],
1003						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
1004					},
1005				},
1006			}
1007
1008			req, _ := http.NewRequest("PUT", "/v1/acl/token?token=root", jsonBody(tokenInput))
1009			resp := httptest.NewRecorder()
1010			_, err := a.srv.ACLTokenCreate(resp, req)
1011			require.Error(t, err)
1012		})
1013
1014		t.Run("Create with Secret as Accessor Dup", func(t *testing.T) {
1015			tokenInput := &structs.ACLToken{
1016				SecretID:    "dee863fa-e548-4c61-a96f-9aa07999249f",
1017				Description: "test",
1018				Policies: []structs.ACLTokenPolicyLink{
1019					structs.ACLTokenPolicyLink{
1020						ID:   idMap["policy-test"],
1021						Name: policyMap[idMap["policy-test"]].Name,
1022					},
1023					structs.ACLTokenPolicyLink{
1024						ID:   idMap["policy-read-all-nodes"],
1025						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
1026					},
1027				},
1028			}
1029
1030			req, _ := http.NewRequest("PUT", "/v1/acl/token?token=root", jsonBody(tokenInput))
1031			resp := httptest.NewRecorder()
1032			_, err := a.srv.ACLTokenCreate(resp, req)
1033			require.Error(t, err)
1034		})
1035
1036		t.Run("Create with Secret Dup", func(t *testing.T) {
1037			tokenInput := &structs.ACLToken{
1038				SecretID:    "10126ffa-b28f-4137-b9a9-e89ab1e97c5b",
1039				Description: "test",
1040				Policies: []structs.ACLTokenPolicyLink{
1041					structs.ACLTokenPolicyLink{
1042						ID:   idMap["policy-test"],
1043						Name: policyMap[idMap["policy-test"]].Name,
1044					},
1045					structs.ACLTokenPolicyLink{
1046						ID:   idMap["policy-read-all-nodes"],
1047						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
1048					},
1049				},
1050			}
1051
1052			req, _ := http.NewRequest("PUT", "/v1/acl/token?token=root", jsonBody(tokenInput))
1053			resp := httptest.NewRecorder()
1054			_, err := a.srv.ACLTokenCreate(resp, req)
1055			require.Error(t, err)
1056		})
1057
1058		t.Run("Create with Accessor as Secret Dup", func(t *testing.T) {
1059			tokenInput := &structs.ACLToken{
1060				AccessorID:  "10126ffa-b28f-4137-b9a9-e89ab1e97c5b",
1061				Description: "test",
1062				Policies: []structs.ACLTokenPolicyLink{
1063					structs.ACLTokenPolicyLink{
1064						ID:   idMap["policy-test"],
1065						Name: policyMap[idMap["policy-test"]].Name,
1066					},
1067					structs.ACLTokenPolicyLink{
1068						ID:   idMap["policy-read-all-nodes"],
1069						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
1070					},
1071				},
1072			}
1073
1074			req, _ := http.NewRequest("PUT", "/v1/acl/token?token=root", jsonBody(tokenInput))
1075			resp := httptest.NewRecorder()
1076			_, err := a.srv.ACLTokenCreate(resp, req)
1077			require.Error(t, err)
1078		})
1079
1080		t.Run("Create with Reserved Accessor", func(t *testing.T) {
1081			tokenInput := &structs.ACLToken{
1082				AccessorID:  "00000000-0000-0000-0000-00000000005b",
1083				Description: "test",
1084				Policies: []structs.ACLTokenPolicyLink{
1085					structs.ACLTokenPolicyLink{
1086						ID:   idMap["policy-test"],
1087						Name: policyMap[idMap["policy-test"]].Name,
1088					},
1089					structs.ACLTokenPolicyLink{
1090						ID:   idMap["policy-read-all-nodes"],
1091						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
1092					},
1093				},
1094			}
1095
1096			req, _ := http.NewRequest("PUT", "/v1/acl/token?token=root", jsonBody(tokenInput))
1097			resp := httptest.NewRecorder()
1098			_, err := a.srv.ACLTokenCreate(resp, req)
1099			require.Error(t, err)
1100		})
1101
1102		t.Run("Create with Reserved Secret", func(t *testing.T) {
1103			tokenInput := &structs.ACLToken{
1104				SecretID:    "00000000-0000-0000-0000-00000000005b",
1105				Description: "test",
1106				Policies: []structs.ACLTokenPolicyLink{
1107					structs.ACLTokenPolicyLink{
1108						ID:   idMap["policy-test"],
1109						Name: policyMap[idMap["policy-test"]].Name,
1110					},
1111					structs.ACLTokenPolicyLink{
1112						ID:   idMap["policy-read-all-nodes"],
1113						Name: policyMap[idMap["policy-read-all-nodes"]].Name,
1114					},
1115				},
1116			}
1117
1118			req, _ := http.NewRequest("PUT", "/v1/acl/token?token=root", jsonBody(tokenInput))
1119			resp := httptest.NewRecorder()
1120			_, err := a.srv.ACLTokenCreate(resp, req)
1121			require.Error(t, err)
1122		})
1123	})
1124}
1125
1126func TestACL_LoginProcedure_HTTP(t *testing.T) {
1127	// This tests AuthMethods, BindingRules, Login, and Logout.
1128	t.Parallel()
1129	a := NewTestAgent(t, TestACLConfig())
1130	defer a.Shutdown()
1131
1132	testrpc.WaitForLeader(t, a.RPC, "dc1")
1133
1134	idMap := make(map[string]string)
1135	methodMap := make(map[string]*structs.ACLAuthMethod)
1136	ruleMap := make(map[string]*structs.ACLBindingRule)
1137	tokenMap := make(map[string]*structs.ACLToken)
1138
1139	testSessionID := testauth.StartSession()
1140	defer testauth.ResetSession(testSessionID)
1141
1142	// This is all done as a subtest for a couple reasons
1143	// 1. It uses only 1 test agent and these are
1144	//    somewhat expensive to bring up and tear down often
1145	// 2. Instead of having to bring up a new agent and prime
1146	//    the ACL system with some data before running the test
1147	//    we can intelligently order these tests so we can still
1148	//    test everything with less actual operations and do
1149	//    so in a manner that is less prone to being flaky
1150	// 3. While this test will be large it should
1151	t.Run("AuthMethod", func(t *testing.T) {
1152		t.Run("Create", func(t *testing.T) {
1153			methodInput := &structs.ACLAuthMethod{
1154				Name:        "test",
1155				Type:        "testing",
1156				Description: "test",
1157				Config: map[string]interface{}{
1158					"SessionID": testSessionID,
1159				},
1160			}
1161
1162			req, _ := http.NewRequest("PUT", "/v1/acl/auth-method?token=root", jsonBody(methodInput))
1163			resp := httptest.NewRecorder()
1164			obj, err := a.srv.ACLAuthMethodCreate(resp, req)
1165			require.NoError(t, err)
1166
1167			method, ok := obj.(*structs.ACLAuthMethod)
1168			require.True(t, ok)
1169
1170			require.Equal(t, methodInput.Name, method.Name)
1171			require.Equal(t, methodInput.Type, method.Type)
1172			require.Equal(t, methodInput.Description, method.Description)
1173			require.Equal(t, methodInput.Config, method.Config)
1174			require.True(t, method.CreateIndex > 0)
1175			require.Equal(t, method.CreateIndex, method.ModifyIndex)
1176
1177			methodMap[method.Name] = method
1178		})
1179
1180		t.Run("Create other", func(t *testing.T) {
1181			methodInput := &structs.ACLAuthMethod{
1182				Name:        "other",
1183				Type:        "testing",
1184				Description: "test",
1185				Config: map[string]interface{}{
1186					"SessionID": testSessionID,
1187				},
1188			}
1189
1190			req, _ := http.NewRequest("PUT", "/v1/acl/auth-method?token=root", jsonBody(methodInput))
1191			resp := httptest.NewRecorder()
1192			obj, err := a.srv.ACLAuthMethodCreate(resp, req)
1193			require.NoError(t, err)
1194
1195			method, ok := obj.(*structs.ACLAuthMethod)
1196			require.True(t, ok)
1197
1198			require.Equal(t, methodInput.Name, method.Name)
1199			require.Equal(t, methodInput.Type, method.Type)
1200			require.Equal(t, methodInput.Description, method.Description)
1201			require.Equal(t, methodInput.Config, method.Config)
1202			require.True(t, method.CreateIndex > 0)
1203			require.Equal(t, method.CreateIndex, method.ModifyIndex)
1204
1205			methodMap[method.Name] = method
1206		})
1207
1208		t.Run("Update Name URL Mismatch", func(t *testing.T) {
1209			methodInput := &structs.ACLAuthMethod{
1210				Name:        "test",
1211				Type:        "testing",
1212				Description: "test",
1213				Config: map[string]interface{}{
1214					"SessionID": testSessionID,
1215				},
1216			}
1217
1218			req, _ := http.NewRequest("PUT", "/v1/acl/auth-method/not-test?token=root", jsonBody(methodInput))
1219			resp := httptest.NewRecorder()
1220			_, err := a.srv.ACLAuthMethodCRUD(resp, req)
1221			require.Error(t, err)
1222			_, ok := err.(BadRequestError)
1223			require.True(t, ok)
1224		})
1225
1226		t.Run("Update", func(t *testing.T) {
1227			methodInput := &structs.ACLAuthMethod{
1228				Name:        "test",
1229				Type:        "testing",
1230				Description: "updated description",
1231				Config: map[string]interface{}{
1232					"SessionID": testSessionID,
1233				},
1234			}
1235
1236			req, _ := http.NewRequest("PUT", "/v1/acl/auth-method/test?token=root", jsonBody(methodInput))
1237			resp := httptest.NewRecorder()
1238			obj, err := a.srv.ACLAuthMethodCRUD(resp, req)
1239			require.NoError(t, err)
1240
1241			method, ok := obj.(*structs.ACLAuthMethod)
1242			require.True(t, ok)
1243
1244			require.Equal(t, methodInput.Name, method.Name)
1245			require.Equal(t, methodInput.Type, method.Type)
1246			require.Equal(t, methodInput.Description, method.Description)
1247			require.Equal(t, methodInput.Config, method.Config)
1248			require.True(t, method.CreateIndex > 0)
1249			require.True(t, method.CreateIndex < method.ModifyIndex)
1250
1251			methodMap[method.Name] = method
1252		})
1253
1254		t.Run("Invalid payload", func(t *testing.T) {
1255			body := bytes.NewBuffer(nil)
1256			body.Write([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
1257
1258			req, _ := http.NewRequest("PUT", "/v1/acl/auth-method?token=root", body)
1259			resp := httptest.NewRecorder()
1260			_, err := a.srv.ACLAuthMethodCreate(resp, req)
1261			require.Error(t, err)
1262			_, ok := err.(BadRequestError)
1263			require.True(t, ok)
1264		})
1265
1266		t.Run("List", func(t *testing.T) {
1267			req, _ := http.NewRequest("GET", "/v1/acl/auth-methods?token=root", nil)
1268			resp := httptest.NewRecorder()
1269			raw, err := a.srv.ACLAuthMethodList(resp, req)
1270			require.NoError(t, err)
1271			methods, ok := raw.(structs.ACLAuthMethodListStubs)
1272			require.True(t, ok)
1273
1274			// 2 we just created
1275			require.Len(t, methods, 2)
1276
1277			for methodName, expected := range methodMap {
1278				found := false
1279				for _, actual := range methods {
1280					if actual.Name == methodName {
1281						require.Equal(t, expected.Name, actual.Name)
1282						require.Equal(t, expected.Type, actual.Type)
1283						require.Equal(t, expected.Description, actual.Description)
1284						require.Equal(t, expected.CreateIndex, actual.CreateIndex)
1285						require.Equal(t, expected.ModifyIndex, actual.ModifyIndex)
1286						found = true
1287						break
1288					}
1289				}
1290
1291				require.True(t, found)
1292			}
1293		})
1294
1295		t.Run("Delete", func(t *testing.T) {
1296			req, _ := http.NewRequest("DELETE", "/v1/acl/auth-method/other?token=root", nil)
1297			resp := httptest.NewRecorder()
1298			_, err := a.srv.ACLAuthMethodCRUD(resp, req)
1299			require.NoError(t, err)
1300			delete(methodMap, "other")
1301		})
1302
1303		t.Run("Read", func(t *testing.T) {
1304			req, _ := http.NewRequest("GET", "/v1/acl/auth-method/test?token=root", nil)
1305			resp := httptest.NewRecorder()
1306			raw, err := a.srv.ACLAuthMethodCRUD(resp, req)
1307			require.NoError(t, err)
1308			method, ok := raw.(*structs.ACLAuthMethod)
1309			require.True(t, ok)
1310			require.Equal(t, methodMap["test"], method)
1311		})
1312	})
1313
1314	t.Run("BindingRule", func(t *testing.T) {
1315		t.Run("Create", func(t *testing.T) {
1316			ruleInput := &structs.ACLBindingRule{
1317				Description: "test",
1318				AuthMethod:  "test",
1319				Selector:    "serviceaccount.namespace==default",
1320				BindType:    structs.BindingRuleBindTypeService,
1321				BindName:    "web",
1322			}
1323
1324			req, _ := http.NewRequest("PUT", "/v1/acl/binding-rule?token=root", jsonBody(ruleInput))
1325			resp := httptest.NewRecorder()
1326			obj, err := a.srv.ACLBindingRuleCreate(resp, req)
1327			require.NoError(t, err)
1328
1329			rule, ok := obj.(*structs.ACLBindingRule)
1330			require.True(t, ok)
1331
1332			// 36 = length of the string form of uuids
1333			require.Len(t, rule.ID, 36)
1334			require.Equal(t, ruleInput.Description, rule.Description)
1335			require.Equal(t, ruleInput.AuthMethod, rule.AuthMethod)
1336			require.Equal(t, ruleInput.Selector, rule.Selector)
1337			require.Equal(t, ruleInput.BindType, rule.BindType)
1338			require.Equal(t, ruleInput.BindName, rule.BindName)
1339			require.True(t, rule.CreateIndex > 0)
1340			require.Equal(t, rule.CreateIndex, rule.ModifyIndex)
1341
1342			idMap["rule-test"] = rule.ID
1343			ruleMap[rule.ID] = rule
1344		})
1345
1346		t.Run("Create other", func(t *testing.T) {
1347			ruleInput := &structs.ACLBindingRule{
1348				Description: "other",
1349				AuthMethod:  "test",
1350				Selector:    "serviceaccount.namespace==default",
1351				BindType:    structs.BindingRuleBindTypeRole,
1352				BindName:    "fancy-role",
1353			}
1354
1355			req, _ := http.NewRequest("PUT", "/v1/acl/binding-rule?token=root", jsonBody(ruleInput))
1356			resp := httptest.NewRecorder()
1357			obj, err := a.srv.ACLBindingRuleCreate(resp, req)
1358			require.NoError(t, err)
1359
1360			rule, ok := obj.(*structs.ACLBindingRule)
1361			require.True(t, ok)
1362
1363			// 36 = length of the string form of uuids
1364			require.Len(t, rule.ID, 36)
1365			require.Equal(t, ruleInput.Description, rule.Description)
1366			require.Equal(t, ruleInput.AuthMethod, rule.AuthMethod)
1367			require.Equal(t, ruleInput.Selector, rule.Selector)
1368			require.Equal(t, ruleInput.BindType, rule.BindType)
1369			require.Equal(t, ruleInput.BindName, rule.BindName)
1370			require.True(t, rule.CreateIndex > 0)
1371			require.Equal(t, rule.CreateIndex, rule.ModifyIndex)
1372
1373			idMap["rule-other"] = rule.ID
1374			ruleMap[rule.ID] = rule
1375		})
1376
1377		t.Run("BindingRule CRUD Missing ID in URL", func(t *testing.T) {
1378			req, _ := http.NewRequest("GET", "/v1/acl/binding-rule/?token=root", nil)
1379			resp := httptest.NewRecorder()
1380			_, err := a.srv.ACLBindingRuleCRUD(resp, req)
1381			require.Error(t, err)
1382			_, ok := err.(BadRequestError)
1383			require.True(t, ok)
1384		})
1385
1386		t.Run("Update", func(t *testing.T) {
1387			ruleInput := &structs.ACLBindingRule{
1388				Description: "updated",
1389				AuthMethod:  "test",
1390				Selector:    "serviceaccount.namespace==default",
1391				BindType:    structs.BindingRuleBindTypeService,
1392				BindName:    "${serviceaccount.name}",
1393			}
1394
1395			req, _ := http.NewRequest("PUT", "/v1/acl/binding-rule/"+idMap["rule-test"]+"?token=root", jsonBody(ruleInput))
1396			resp := httptest.NewRecorder()
1397			obj, err := a.srv.ACLBindingRuleCRUD(resp, req)
1398			require.NoError(t, err)
1399
1400			rule, ok := obj.(*structs.ACLBindingRule)
1401			require.True(t, ok)
1402
1403			// 36 = length of the string form of uuids
1404			require.Len(t, rule.ID, 36)
1405			require.Equal(t, ruleInput.Description, rule.Description)
1406			require.Equal(t, ruleInput.AuthMethod, rule.AuthMethod)
1407			require.Equal(t, ruleInput.Selector, rule.Selector)
1408			require.Equal(t, ruleInput.BindType, rule.BindType)
1409			require.Equal(t, ruleInput.BindName, rule.BindName)
1410			require.True(t, rule.CreateIndex > 0)
1411			require.True(t, rule.CreateIndex < rule.ModifyIndex)
1412
1413			idMap["rule-test"] = rule.ID
1414			ruleMap[rule.ID] = rule
1415		})
1416
1417		t.Run("ID Supplied", func(t *testing.T) {
1418			ruleInput := &structs.ACLBindingRule{
1419				ID:          "12123d01-37f1-47e6-b55b-32328652bd38",
1420				Description: "with-id",
1421				AuthMethod:  "test",
1422				Selector:    "serviceaccount.namespace==default",
1423				BindType:    structs.BindingRuleBindTypeService,
1424				BindName:    "vault",
1425			}
1426
1427			req, _ := http.NewRequest("PUT", "/v1/acl/binding-rule?token=root", jsonBody(ruleInput))
1428			resp := httptest.NewRecorder()
1429			_, err := a.srv.ACLBindingRuleCreate(resp, req)
1430			require.Error(t, err)
1431			_, ok := err.(BadRequestError)
1432			require.True(t, ok)
1433		})
1434
1435		t.Run("Invalid payload", func(t *testing.T) {
1436			body := bytes.NewBuffer(nil)
1437			body.Write([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
1438
1439			req, _ := http.NewRequest("PUT", "/v1/acl/binding-rule?token=root", body)
1440			resp := httptest.NewRecorder()
1441			_, err := a.srv.ACLBindingRuleCreate(resp, req)
1442			require.Error(t, err)
1443			_, ok := err.(BadRequestError)
1444			require.True(t, ok)
1445		})
1446
1447		t.Run("List", func(t *testing.T) {
1448			req, _ := http.NewRequest("GET", "/v1/acl/binding-rules?token=root", nil)
1449			resp := httptest.NewRecorder()
1450			raw, err := a.srv.ACLBindingRuleList(resp, req)
1451			require.NoError(t, err)
1452			rules, ok := raw.(structs.ACLBindingRules)
1453			require.True(t, ok)
1454
1455			// 2 we just created
1456			require.Len(t, rules, 2)
1457
1458			for ruleID, expected := range ruleMap {
1459				found := false
1460				for _, actual := range rules {
1461					if actual.ID == ruleID {
1462						require.Equal(t, expected.Description, actual.Description)
1463						require.Equal(t, expected.AuthMethod, actual.AuthMethod)
1464						require.Equal(t, expected.Selector, actual.Selector)
1465						require.Equal(t, expected.BindType, actual.BindType)
1466						require.Equal(t, expected.BindName, actual.BindName)
1467						require.Equal(t, expected.CreateIndex, actual.CreateIndex)
1468						require.Equal(t, expected.ModifyIndex, actual.ModifyIndex)
1469						found = true
1470						break
1471					}
1472				}
1473
1474				require.True(t, found)
1475			}
1476		})
1477
1478		t.Run("Delete", func(t *testing.T) {
1479			req, _ := http.NewRequest("DELETE", "/v1/acl/binding-rule/"+idMap["rule-other"]+"?token=root", nil)
1480			resp := httptest.NewRecorder()
1481			_, err := a.srv.ACLBindingRuleCRUD(resp, req)
1482			require.NoError(t, err)
1483			delete(ruleMap, idMap["rule-other"])
1484			delete(idMap, "rule-other")
1485		})
1486
1487		t.Run("Read", func(t *testing.T) {
1488			req, _ := http.NewRequest("GET", "/v1/acl/binding-rule/"+idMap["rule-test"]+"?token=root", nil)
1489			resp := httptest.NewRecorder()
1490			raw, err := a.srv.ACLBindingRuleCRUD(resp, req)
1491			require.NoError(t, err)
1492			rule, ok := raw.(*structs.ACLBindingRule)
1493			require.True(t, ok)
1494			require.Equal(t, ruleMap[idMap["rule-test"]], rule)
1495		})
1496	})
1497
1498	testauth.InstallSessionToken(testSessionID, "token1", "default", "demo1", "abc123")
1499	testauth.InstallSessionToken(testSessionID, "token2", "default", "demo2", "def456")
1500
1501	t.Run("Login", func(t *testing.T) {
1502		t.Run("Create Token 1", func(t *testing.T) {
1503			loginInput := &structs.ACLLoginParams{
1504				AuthMethod:  "test",
1505				BearerToken: "token1",
1506				Meta:        map[string]string{"foo": "bar"},
1507			}
1508
1509			req, _ := http.NewRequest("POST", "/v1/acl/login?token=root", jsonBody(loginInput))
1510			resp := httptest.NewRecorder()
1511			obj, err := a.srv.ACLLogin(resp, req)
1512			require.NoError(t, err)
1513
1514			token, ok := obj.(*structs.ACLToken)
1515			require.True(t, ok)
1516
1517			// 36 = length of the string form of uuids
1518			require.Len(t, token.AccessorID, 36)
1519			require.Len(t, token.SecretID, 36)
1520			require.Equal(t, `token created via login: {"foo":"bar"}`, token.Description)
1521			require.True(t, token.Local)
1522			require.Len(t, token.Policies, 0)
1523			require.Len(t, token.Roles, 0)
1524			require.Len(t, token.ServiceIdentities, 1)
1525			require.Equal(t, "demo1", token.ServiceIdentities[0].ServiceName)
1526			require.Len(t, token.ServiceIdentities[0].Datacenters, 0)
1527			require.True(t, token.CreateIndex > 0)
1528			require.Equal(t, token.CreateIndex, token.ModifyIndex)
1529			require.NotNil(t, token.Hash)
1530			require.NotEqual(t, token.Hash, []byte{})
1531
1532			idMap["token-test-1"] = token.AccessorID
1533			tokenMap[token.AccessorID] = token
1534		})
1535		t.Run("Create Token 2", func(t *testing.T) {
1536			loginInput := &structs.ACLLoginParams{
1537				AuthMethod:  "test",
1538				BearerToken: "token2",
1539				Meta:        map[string]string{"blah": "woot"},
1540			}
1541
1542			req, _ := http.NewRequest("POST", "/v1/acl/login?token=root", jsonBody(loginInput))
1543			resp := httptest.NewRecorder()
1544			obj, err := a.srv.ACLLogin(resp, req)
1545			require.NoError(t, err)
1546
1547			token, ok := obj.(*structs.ACLToken)
1548			require.True(t, ok)
1549
1550			// 36 = length of the string form of uuids
1551			require.Len(t, token.AccessorID, 36)
1552			require.Len(t, token.SecretID, 36)
1553			require.Equal(t, `token created via login: {"blah":"woot"}`, token.Description)
1554			require.True(t, token.Local)
1555			require.Len(t, token.Policies, 0)
1556			require.Len(t, token.Roles, 0)
1557			require.Len(t, token.ServiceIdentities, 1)
1558			require.Equal(t, "demo2", token.ServiceIdentities[0].ServiceName)
1559			require.Len(t, token.ServiceIdentities[0].Datacenters, 0)
1560			require.True(t, token.CreateIndex > 0)
1561			require.Equal(t, token.CreateIndex, token.ModifyIndex)
1562			require.NotNil(t, token.Hash)
1563			require.NotEqual(t, token.Hash, []byte{})
1564
1565			idMap["token-test-2"] = token.AccessorID
1566			tokenMap[token.AccessorID] = token
1567		})
1568
1569		t.Run("List Tokens by (incorrect) Method", func(t *testing.T) {
1570			req, _ := http.NewRequest("GET", "/v1/acl/tokens?token=root&authmethod=other", nil)
1571			resp := httptest.NewRecorder()
1572			raw, err := a.srv.ACLTokenList(resp, req)
1573			require.NoError(t, err)
1574			tokens, ok := raw.(structs.ACLTokenListStubs)
1575			require.True(t, ok)
1576			require.Len(t, tokens, 0)
1577		})
1578
1579		t.Run("List Tokens by (correct) Method", func(t *testing.T) {
1580			req, _ := http.NewRequest("GET", "/v1/acl/tokens?token=root&authmethod=test", nil)
1581			resp := httptest.NewRecorder()
1582			raw, err := a.srv.ACLTokenList(resp, req)
1583			require.NoError(t, err)
1584			tokens, ok := raw.(structs.ACLTokenListStubs)
1585			require.True(t, ok)
1586			require.Len(t, tokens, 2)
1587
1588			for tokenID, expected := range tokenMap {
1589				found := false
1590				for _, actual := range tokens {
1591					if actual.AccessorID == tokenID {
1592						require.Equal(t, expected.Description, actual.Description)
1593						require.Equal(t, expected.Policies, actual.Policies)
1594						require.Equal(t, expected.Roles, actual.Roles)
1595						require.Equal(t, expected.ServiceIdentities, actual.ServiceIdentities)
1596						require.Equal(t, expected.Local, actual.Local)
1597						require.Equal(t, expected.CreateTime, actual.CreateTime)
1598						require.Equal(t, expected.Hash, actual.Hash)
1599						require.Equal(t, expected.CreateIndex, actual.CreateIndex)
1600						require.Equal(t, expected.ModifyIndex, actual.ModifyIndex)
1601						found = true
1602						break
1603					}
1604				}
1605				require.True(t, found)
1606			}
1607		})
1608
1609		t.Run("Logout", func(t *testing.T) {
1610			tok := tokenMap[idMap["token-test-1"]]
1611			req, _ := http.NewRequest("POST", "/v1/acl/logout?token="+tok.SecretID, nil)
1612			resp := httptest.NewRecorder()
1613			_, err := a.srv.ACLLogout(resp, req)
1614			require.NoError(t, err)
1615		})
1616
1617		t.Run("Token is gone after Logout", func(t *testing.T) {
1618			req, _ := http.NewRequest("GET", "/v1/acl/token/"+idMap["token-test-1"]+"?token=root", nil)
1619			resp := httptest.NewRecorder()
1620			_, err := a.srv.ACLTokenCRUD(resp, req)
1621			require.Error(t, err)
1622			require.True(t, acl.IsErrNotFound(err), err.Error())
1623		})
1624	})
1625}
1626
1627func TestACLEndpoint_LoginLogout_jwt(t *testing.T) {
1628	t.Parallel()
1629
1630	a := NewTestAgent(t, TestACLConfigWithParams(nil))
1631	defer a.Shutdown()
1632
1633	testrpc.WaitForLeader(t, a.RPC, "dc1")
1634
1635	// spin up a fake oidc server
1636	oidcServer := startSSOTestServer(t)
1637	pubKey, privKey := oidcServer.SigningKeys()
1638
1639	type mConfig = map[string]interface{}
1640	cases := map[string]struct {
1641		f         func(config mConfig)
1642		issuer    string
1643		expectErr string
1644	}{
1645		"success - jwt static keys": {func(config mConfig) {
1646			config["BoundIssuer"] = "https://legit.issuer.internal/"
1647			config["JWTValidationPubKeys"] = []string{pubKey}
1648		},
1649			"https://legit.issuer.internal/",
1650			""},
1651		"success - jwt jwks": {func(config mConfig) {
1652			config["JWKSURL"] = oidcServer.Addr() + "/certs"
1653			config["JWKSCACert"] = oidcServer.CACert()
1654		},
1655			"https://legit.issuer.internal/",
1656			""},
1657		"success - jwt oidc discovery": {func(config mConfig) {
1658			config["OIDCDiscoveryURL"] = oidcServer.Addr()
1659			config["OIDCDiscoveryCACert"] = oidcServer.CACert()
1660		},
1661			oidcServer.Addr(),
1662			""},
1663	}
1664
1665	for name, tc := range cases {
1666		tc := tc
1667		t.Run(name, func(t *testing.T) {
1668			method, err := upsertTestCustomizedAuthMethod(a.RPC, TestDefaultMasterToken, "dc1", func(method *structs.ACLAuthMethod) {
1669				method.Type = "jwt"
1670				method.Config = map[string]interface{}{
1671					"JWTSupportedAlgs": []string{"ES256"},
1672					"ClaimMappings": map[string]string{
1673						"first_name":   "name",
1674						"/org/primary": "primary_org",
1675					},
1676					"ListClaimMappings": map[string]string{
1677						"https://consul.test/groups": "groups",
1678					},
1679					"BoundAudiences": []string{"https://consul.test"},
1680				}
1681				if tc.f != nil {
1682					tc.f(method.Config)
1683				}
1684			})
1685			require.NoError(t, err)
1686
1687			t.Run("invalid bearer token", func(t *testing.T) {
1688				loginInput := &structs.ACLLoginParams{
1689					AuthMethod:  method.Name,
1690					BearerToken: "invalid",
1691				}
1692
1693				req, _ := http.NewRequest("POST", "/v1/acl/login", jsonBody(loginInput))
1694				resp := httptest.NewRecorder()
1695				_, err := a.srv.ACLLogin(resp, req)
1696				require.Error(t, err)
1697			})
1698
1699			cl := jwt.Claims{
1700				Subject:   "r3qXcK2bix9eFECzsU3Sbmh0K16fatW6@clients",
1701				Audience:  jwt.Audience{"https://consul.test"},
1702				Issuer:    tc.issuer,
1703				NotBefore: jwt.NewNumericDate(time.Now().Add(-5 * time.Second)),
1704				Expiry:    jwt.NewNumericDate(time.Now().Add(5 * time.Second)),
1705			}
1706
1707			type orgs struct {
1708				Primary string `json:"primary"`
1709			}
1710
1711			privateCl := struct {
1712				FirstName string   `json:"first_name"`
1713				Org       orgs     `json:"org"`
1714				Groups    []string `json:"https://consul.test/groups"`
1715			}{
1716				FirstName: "jeff2",
1717				Org:       orgs{"engineering"},
1718				Groups:    []string{"foo", "bar"},
1719			}
1720
1721			jwtData, err := oidcauthtest.SignJWT(privKey, cl, privateCl)
1722			require.NoError(t, err)
1723
1724			t.Run("valid bearer token no bindings", func(t *testing.T) {
1725				loginInput := &structs.ACLLoginParams{
1726					AuthMethod:  method.Name,
1727					BearerToken: jwtData,
1728				}
1729
1730				req, _ := http.NewRequest("POST", "/v1/acl/login", jsonBody(loginInput))
1731				resp := httptest.NewRecorder()
1732				_, err := a.srv.ACLLogin(resp, req)
1733
1734				testutil.RequireErrorContains(t, err, "Permission denied")
1735			})
1736
1737			_, err = upsertTestCustomizedBindingRule(a.RPC, TestDefaultMasterToken, "dc1", func(rule *structs.ACLBindingRule) {
1738				rule.AuthMethod = method.Name
1739				rule.BindType = structs.BindingRuleBindTypeService
1740				rule.BindName = "test--${value.name}--${value.primary_org}"
1741				rule.Selector = "value.name == jeff2 and value.primary_org == engineering and foo in list.groups"
1742			})
1743			require.NoError(t, err)
1744
1745			t.Run("valid bearer token 1 service binding", func(t *testing.T) {
1746				loginInput := &structs.ACLLoginParams{
1747					AuthMethod:  method.Name,
1748					BearerToken: jwtData,
1749				}
1750
1751				req, _ := http.NewRequest("POST", "/v1/acl/login", jsonBody(loginInput))
1752				resp := httptest.NewRecorder()
1753				obj, err := a.srv.ACLLogin(resp, req)
1754				require.NoError(t, err)
1755
1756				token, ok := obj.(*structs.ACLToken)
1757				require.True(t, ok)
1758
1759				require.Equal(t, method.Name, token.AuthMethod)
1760				require.Equal(t, `token created via login`, token.Description)
1761				require.True(t, token.Local)
1762				require.Len(t, token.Roles, 0)
1763				require.Len(t, token.ServiceIdentities, 1)
1764				svcid := token.ServiceIdentities[0]
1765				require.Len(t, svcid.Datacenters, 0)
1766				require.Equal(t, "test--jeff2--engineering", svcid.ServiceName)
1767
1768				// and delete it
1769				req, _ = http.NewRequest("GET", "/v1/acl/logout", nil)
1770				req.Header.Add("X-Consul-Token", token.SecretID)
1771				resp = httptest.NewRecorder()
1772				_, err = a.srv.ACLLogout(resp, req)
1773				require.NoError(t, err)
1774
1775				// verify the token was deleted
1776				req, _ = http.NewRequest("GET", "/v1/acl/token/"+token.AccessorID, nil)
1777				req.Header.Add("X-Consul-Token", TestDefaultMasterToken)
1778				resp = httptest.NewRecorder()
1779
1780				// make the request
1781				_, err = a.srv.ACLTokenCRUD(resp, req)
1782				require.Error(t, err)
1783				require.Equal(t, acl.ErrNotFound, err)
1784			})
1785		})
1786	}
1787}
1788
1789func TestACL_Authorize(t *testing.T) {
1790	t.Parallel()
1791	a1 := NewTestAgent(t, TestACLConfigWithParams(nil))
1792	defer a1.Shutdown()
1793
1794	testrpc.WaitForTestAgent(t, a1.RPC, "dc1", testrpc.WithToken(TestDefaultMasterToken))
1795
1796	policyReq := structs.ACLPolicySetRequest{
1797		Policy: structs.ACLPolicy{
1798			Name:  "test",
1799			Rules: `acl = "read" operator = "write" service_prefix "" { policy = "read"} node_prefix "" { policy= "write" } key_prefix "/foo" { policy = "write" } `,
1800		},
1801		Datacenter:   "dc1",
1802		WriteRequest: structs.WriteRequest{Token: TestDefaultMasterToken},
1803	}
1804	var policy structs.ACLPolicy
1805	require.NoError(t, a1.RPC("ACL.PolicySet", &policyReq, &policy))
1806
1807	tokenReq := structs.ACLTokenSetRequest{
1808		ACLToken: structs.ACLToken{
1809			Policies: []structs.ACLTokenPolicyLink{
1810				structs.ACLTokenPolicyLink{
1811					ID: policy.ID,
1812				},
1813			},
1814		},
1815		Datacenter:   "dc1",
1816		WriteRequest: structs.WriteRequest{Token: TestDefaultMasterToken},
1817	}
1818
1819	var token structs.ACLToken
1820	require.NoError(t, a1.RPC("ACL.TokenSet", &tokenReq, &token))
1821
1822	// secondary also needs to setup a replication token to pull tokens and policies
1823	secondaryParams := DefaulTestACLConfigParams()
1824	secondaryParams.ReplicationToken = secondaryParams.MasterToken
1825	secondaryParams.EnableTokenReplication = true
1826
1827	a2 := NewTestAgent(t, `datacenter = "dc2" `+TestACLConfigWithParams(secondaryParams))
1828	defer a2.Shutdown()
1829
1830	addr := fmt.Sprintf("127.0.0.1:%d", a1.Config.SerfPortWAN)
1831	_, err := a2.JoinWAN([]string{addr})
1832	require.NoError(t, err)
1833
1834	testrpc.WaitForTestAgent(t, a2.RPC, "dc2", testrpc.WithToken(TestDefaultMasterToken))
1835	// this actually ensures a few things. First the dcs got connect okay, secondly that the policy we
1836	// are about ready to use in our local token creation exists in the secondary DC
1837	testrpc.WaitForACLReplication(t, a2.RPC, "dc2", structs.ACLReplicateTokens, policy.CreateIndex, 1, 0)
1838
1839	localTokenReq := structs.ACLTokenSetRequest{
1840		ACLToken: structs.ACLToken{
1841			Policies: []structs.ACLTokenPolicyLink{
1842				structs.ACLTokenPolicyLink{
1843					ID: policy.ID,
1844				},
1845			},
1846			Local: true,
1847		},
1848		Datacenter:   "dc2",
1849		WriteRequest: structs.WriteRequest{Token: TestDefaultMasterToken},
1850	}
1851
1852	var localToken structs.ACLToken
1853	require.NoError(t, a2.RPC("ACL.TokenSet", &localTokenReq, &localToken))
1854
1855	t.Run("master-token", func(t *testing.T) {
1856		request := []structs.ACLAuthorizationRequest{
1857			structs.ACLAuthorizationRequest{
1858				Resource: "acl",
1859				Access:   "read",
1860			},
1861			structs.ACLAuthorizationRequest{
1862				Resource: "acl",
1863				Access:   "write",
1864			},
1865			structs.ACLAuthorizationRequest{
1866				Resource: "agent",
1867				Segment:  "foo",
1868				Access:   "read",
1869			},
1870			structs.ACLAuthorizationRequest{
1871				Resource: "agent",
1872				Segment:  "foo",
1873				Access:   "write",
1874			},
1875			structs.ACLAuthorizationRequest{
1876				Resource: "event",
1877				Segment:  "foo",
1878				Access:   "read",
1879			},
1880			structs.ACLAuthorizationRequest{
1881				Resource: "event",
1882				Segment:  "foo",
1883				Access:   "write",
1884			},
1885			structs.ACLAuthorizationRequest{
1886				Resource: "intention",
1887				Segment:  "foo",
1888				Access:   "read",
1889			},
1890			structs.ACLAuthorizationRequest{
1891				Resource: "intention",
1892				Segment:  "foo",
1893				Access:   "write",
1894			},
1895			structs.ACLAuthorizationRequest{
1896				Resource: "key",
1897				Segment:  "foo",
1898				Access:   "read",
1899			},
1900			structs.ACLAuthorizationRequest{
1901				Resource: "key",
1902				Segment:  "foo",
1903				Access:   "list",
1904			},
1905			structs.ACLAuthorizationRequest{
1906				Resource: "key",
1907				Segment:  "foo",
1908				Access:   "write",
1909			},
1910			structs.ACLAuthorizationRequest{
1911				Resource: "keyring",
1912				Access:   "read",
1913			},
1914			structs.ACLAuthorizationRequest{
1915				Resource: "keyring",
1916				Access:   "write",
1917			},
1918			structs.ACLAuthorizationRequest{
1919				Resource: "node",
1920				Segment:  "foo",
1921				Access:   "read",
1922			},
1923			structs.ACLAuthorizationRequest{
1924				Resource: "node",
1925				Segment:  "foo",
1926				Access:   "write",
1927			},
1928			structs.ACLAuthorizationRequest{
1929				Resource: "operator",
1930				Access:   "read",
1931			},
1932			structs.ACLAuthorizationRequest{
1933				Resource: "operator",
1934				Access:   "write",
1935			},
1936			structs.ACLAuthorizationRequest{
1937				Resource: "query",
1938				Segment:  "foo",
1939				Access:   "read",
1940			},
1941			structs.ACLAuthorizationRequest{
1942				Resource: "query",
1943				Segment:  "foo",
1944				Access:   "write",
1945			},
1946			structs.ACLAuthorizationRequest{
1947				Resource: "service",
1948				Segment:  "foo",
1949				Access:   "read",
1950			},
1951			structs.ACLAuthorizationRequest{
1952				Resource: "service",
1953				Segment:  "foo",
1954				Access:   "write",
1955			},
1956			structs.ACLAuthorizationRequest{
1957				Resource: "session",
1958				Segment:  "foo",
1959				Access:   "read",
1960			},
1961			structs.ACLAuthorizationRequest{
1962				Resource: "session",
1963				Segment:  "foo",
1964				Access:   "write",
1965			},
1966		}
1967
1968		for _, dc := range []string{"dc1", "dc2"} {
1969			t.Run(dc, func(t *testing.T) {
1970				req, _ := http.NewRequest("POST", "/v1/internal/acl/authorize?dc="+dc, jsonBody(request))
1971				req.Header.Add("X-Consul-Token", TestDefaultMasterToken)
1972				recorder := httptest.NewRecorder()
1973				raw, err := a1.srv.ACLAuthorize(recorder, req)
1974				require.NoError(t, err)
1975				responses, ok := raw.([]structs.ACLAuthorizationResponse)
1976				require.True(t, ok)
1977				require.Len(t, responses, len(request))
1978
1979				for idx, req := range request {
1980					resp := responses[idx]
1981
1982					require.Equal(t, req, resp.ACLAuthorizationRequest)
1983					require.True(t, resp.Allow, "should have allowed all access for master token")
1984				}
1985			})
1986		}
1987
1988	})
1989
1990	customAuthorizationRequests := []structs.ACLAuthorizationRequest{
1991		structs.ACLAuthorizationRequest{
1992			Resource: "acl",
1993			Access:   "read",
1994		},
1995		structs.ACLAuthorizationRequest{
1996			Resource: "acl",
1997			Access:   "write",
1998		},
1999		structs.ACLAuthorizationRequest{
2000			Resource: "agent",
2001			Segment:  "foo",
2002			Access:   "read",
2003		},
2004		structs.ACLAuthorizationRequest{
2005			Resource: "agent",
2006			Segment:  "foo",
2007			Access:   "write",
2008		},
2009		structs.ACLAuthorizationRequest{
2010			Resource: "event",
2011			Segment:  "foo",
2012			Access:   "read",
2013		},
2014		structs.ACLAuthorizationRequest{
2015			Resource: "event",
2016			Segment:  "foo",
2017			Access:   "write",
2018		},
2019		structs.ACLAuthorizationRequest{
2020			Resource: "intention",
2021			Segment:  "foo",
2022			Access:   "read",
2023		},
2024		structs.ACLAuthorizationRequest{
2025			Resource: "intention",
2026			Segment:  "foo",
2027			Access:   "write",
2028		},
2029		structs.ACLAuthorizationRequest{
2030			Resource: "key",
2031			Segment:  "foo",
2032			Access:   "read",
2033		},
2034		structs.ACLAuthorizationRequest{
2035			Resource: "key",
2036			Segment:  "foo",
2037			Access:   "list",
2038		},
2039		structs.ACLAuthorizationRequest{
2040			Resource: "key",
2041			Segment:  "foo",
2042			Access:   "write",
2043		},
2044		structs.ACLAuthorizationRequest{
2045			Resource: "keyring",
2046			Access:   "read",
2047		},
2048		structs.ACLAuthorizationRequest{
2049			Resource: "keyring",
2050			Access:   "write",
2051		},
2052		structs.ACLAuthorizationRequest{
2053			Resource: "node",
2054			Segment:  "foo",
2055			Access:   "read",
2056		},
2057		structs.ACLAuthorizationRequest{
2058			Resource: "node",
2059			Segment:  "foo",
2060			Access:   "write",
2061		},
2062		structs.ACLAuthorizationRequest{
2063			Resource: "operator",
2064			Access:   "read",
2065		},
2066		structs.ACLAuthorizationRequest{
2067			Resource: "operator",
2068			Access:   "write",
2069		},
2070		structs.ACLAuthorizationRequest{
2071			Resource: "query",
2072			Segment:  "foo",
2073			Access:   "read",
2074		},
2075		structs.ACLAuthorizationRequest{
2076			Resource: "query",
2077			Segment:  "foo",
2078			Access:   "write",
2079		},
2080		structs.ACLAuthorizationRequest{
2081			Resource: "service",
2082			Segment:  "foo",
2083			Access:   "read",
2084		},
2085		structs.ACLAuthorizationRequest{
2086			Resource: "service",
2087			Segment:  "foo",
2088			Access:   "write",
2089		},
2090		structs.ACLAuthorizationRequest{
2091			Resource: "session",
2092			Segment:  "foo",
2093			Access:   "read",
2094		},
2095		structs.ACLAuthorizationRequest{
2096			Resource: "session",
2097			Segment:  "foo",
2098			Access:   "write",
2099		},
2100	}
2101
2102	expectedCustomAuthorizationResponses := []bool{
2103		true,  // acl:read
2104		false, // acl:write
2105		false, // agent:read
2106		false, // agent:write
2107		false, // event:read
2108		false, // event:write
2109		true,  // intention:read
2110		false, // intention:write
2111		false, // key:read
2112		false, // key:list
2113		false, // key:write
2114		false, // keyring:read
2115		false, // keyring:write
2116		true,  // node:read
2117		true,  // node:write
2118		true,  // operator:read
2119		true,  // operator:write
2120		false, // query:read
2121		false, // query:write
2122		true,  // service:read
2123		false, // service:write
2124		false, // session:read
2125		false, // session:write
2126	}
2127
2128	t.Run("custom-token", func(t *testing.T) {
2129		for _, dc := range []string{"dc1", "dc2"} {
2130			t.Run(dc, func(t *testing.T) {
2131				req, _ := http.NewRequest("POST", "/v1/internal/acl/authorize", jsonBody(customAuthorizationRequests))
2132				req.Header.Add("X-Consul-Token", token.SecretID)
2133				recorder := httptest.NewRecorder()
2134				raw, err := a1.srv.ACLAuthorize(recorder, req)
2135				require.NoError(t, err)
2136				responses, ok := raw.([]structs.ACLAuthorizationResponse)
2137				require.True(t, ok)
2138				require.Len(t, responses, len(customAuthorizationRequests))
2139				require.Len(t, responses, len(expectedCustomAuthorizationResponses))
2140
2141				for idx, req := range customAuthorizationRequests {
2142					resp := responses[idx]
2143
2144					require.Equal(t, req, resp.ACLAuthorizationRequest)
2145					require.Equal(t, expectedCustomAuthorizationResponses[idx], resp.Allow, "request %d - %+v returned unexpected response", idx, resp.ACLAuthorizationRequest)
2146				}
2147			})
2148		}
2149	})
2150
2151	t.Run("too-many-requests", func(t *testing.T) {
2152		var request []structs.ACLAuthorizationRequest
2153
2154		for i := 0; i < 100; i++ {
2155			request = append(request, structs.ACLAuthorizationRequest{Resource: "acl", Access: "read"})
2156		}
2157
2158		req, _ := http.NewRequest("POST", "/v1/internal/acl/authorize", jsonBody(request))
2159		req.Header.Add("X-Consul-Token", token.SecretID)
2160		recorder := httptest.NewRecorder()
2161		raw, err := a1.srv.ACLAuthorize(recorder, req)
2162		require.Error(t, err)
2163		require.Contains(t, err.Error(), "Refusing to process more than 64 authorizations at once")
2164		require.Nil(t, raw)
2165	})
2166
2167	t.Run("decode-failure", func(t *testing.T) {
2168		req, _ := http.NewRequest("POST", "/v1/internal/acl/authorize", jsonBody(structs.ACLAuthorizationRequest{Resource: "acl", Access: "read"}))
2169		req.Header.Add("X-Consul-Token", token.SecretID)
2170		recorder := httptest.NewRecorder()
2171		raw, err := a1.srv.ACLAuthorize(recorder, req)
2172		require.Error(t, err)
2173		require.Contains(t, err.Error(), "Failed to decode request body")
2174		require.Nil(t, raw)
2175	})
2176
2177	t.Run("acl-not-found", func(t *testing.T) {
2178		request := []structs.ACLAuthorizationRequest{
2179			structs.ACLAuthorizationRequest{
2180				Resource: "acl",
2181				Access:   "read",
2182			},
2183		}
2184
2185		req, _ := http.NewRequest("POST", "/v1/internal/acl/authorize", jsonBody(request))
2186		req.Header.Add("X-Consul-Token", "d908c0be-22e1-433e-84db-8718e1a019de")
2187		recorder := httptest.NewRecorder()
2188		raw, err := a1.srv.ACLAuthorize(recorder, req)
2189		require.Error(t, err)
2190		require.Equal(t, acl.ErrNotFound, err)
2191		require.Nil(t, raw)
2192	})
2193
2194	t.Run("local-token-in-secondary-dc", func(t *testing.T) {
2195		req, _ := http.NewRequest("POST", "/v1/internal/acl/authorize?dc=dc2", jsonBody(customAuthorizationRequests))
2196		req.Header.Add("X-Consul-Token", localToken.SecretID)
2197		recorder := httptest.NewRecorder()
2198		raw, err := a1.srv.ACLAuthorize(recorder, req)
2199		require.NoError(t, err)
2200		responses, ok := raw.([]structs.ACLAuthorizationResponse)
2201		require.True(t, ok)
2202		require.Len(t, responses, len(customAuthorizationRequests))
2203		require.Len(t, responses, len(expectedCustomAuthorizationResponses))
2204
2205		for idx, req := range customAuthorizationRequests {
2206			resp := responses[idx]
2207
2208			require.Equal(t, req, resp.ACLAuthorizationRequest)
2209			require.Equal(t, expectedCustomAuthorizationResponses[idx], resp.Allow, "request %d - %+v returned unexpected response", idx, resp.ACLAuthorizationRequest)
2210		}
2211	})
2212
2213	t.Run("local-token-wrong-dc", func(t *testing.T) {
2214		request := []structs.ACLAuthorizationRequest{
2215			structs.ACLAuthorizationRequest{
2216				Resource: "acl",
2217				Access:   "read",
2218			},
2219		}
2220
2221		req, _ := http.NewRequest("POST", "/v1/internal/acl/authorize", jsonBody(request))
2222		req.Header.Add("X-Consul-Token", localToken.SecretID)
2223		recorder := httptest.NewRecorder()
2224		raw, err := a1.srv.ACLAuthorize(recorder, req)
2225		require.Error(t, err)
2226		require.Equal(t, acl.ErrNotFound, err)
2227		require.Nil(t, raw)
2228	})
2229}
2230
2231type rpcFn func(string, interface{}, interface{}) error
2232
2233func upsertTestCustomizedAuthMethod(
2234	rpc rpcFn, masterToken string, datacenter string,
2235	modify func(method *structs.ACLAuthMethod),
2236) (*structs.ACLAuthMethod, error) {
2237	name, err := uuid.GenerateUUID()
2238	if err != nil {
2239		return nil, err
2240	}
2241
2242	req := structs.ACLAuthMethodSetRequest{
2243		Datacenter: datacenter,
2244		AuthMethod: structs.ACLAuthMethod{
2245			Name: "test-method-" + name,
2246			Type: "testing",
2247		},
2248		WriteRequest: structs.WriteRequest{Token: masterToken},
2249	}
2250
2251	if modify != nil {
2252		modify(&req.AuthMethod)
2253	}
2254
2255	var out structs.ACLAuthMethod
2256
2257	err = rpc("ACL.AuthMethodSet", &req, &out)
2258	if err != nil {
2259		return nil, err
2260	}
2261
2262	return &out, nil
2263}
2264
2265func upsertTestCustomizedBindingRule(rpc rpcFn, masterToken string, datacenter string, modify func(rule *structs.ACLBindingRule)) (*structs.ACLBindingRule, error) {
2266	req := structs.ACLBindingRuleSetRequest{
2267		Datacenter:   datacenter,
2268		BindingRule:  structs.ACLBindingRule{},
2269		WriteRequest: structs.WriteRequest{Token: masterToken},
2270	}
2271
2272	if modify != nil {
2273		modify(&req.BindingRule)
2274	}
2275
2276	var out structs.ACLBindingRule
2277
2278	err := rpc("ACL.BindingRuleSet", &req, &out)
2279	if err != nil {
2280		return nil, err
2281	}
2282
2283	return &out, nil
2284}
2285
2286func startSSOTestServer(t *testing.T) *oidcauthtest.Server {
2287	ports := freeport.MustTake(1)
2288	return oidcauthtest.Start(t, oidcauthtest.WithPort(
2289		ports[0],
2290		func() { freeport.Return(ports) },
2291	))
2292}
2293