1package jwtauth
2
3import (
4	"context"
5	"encoding/json"
6	"reflect"
7	"strings"
8	"testing"
9	"time"
10
11	"github.com/go-test/deep"
12	log "github.com/hashicorp/go-hclog"
13	"github.com/hashicorp/go-sockaddr"
14	"github.com/hashicorp/vault/sdk/helper/logging"
15	"github.com/hashicorp/vault/sdk/helper/tokenutil"
16	"github.com/hashicorp/vault/sdk/logical"
17)
18
19func getBackend(t *testing.T) (logical.Backend, logical.Storage) {
20	defaultLeaseTTLVal := time.Hour * 12
21	maxLeaseTTLVal := time.Hour * 24
22
23	config := &logical.BackendConfig{
24		Logger: logging.NewVaultLogger(log.Trace),
25
26		System: &logical.StaticSystemView{
27			DefaultLeaseTTLVal: defaultLeaseTTLVal,
28			MaxLeaseTTLVal:     maxLeaseTTLVal,
29		},
30		StorageView: &logical.InmemStorage{},
31	}
32	b, err := Factory(context.Background(), config)
33	if err != nil {
34		t.Fatalf("unable to create backend: %v", err)
35	}
36
37	return b, config.StorageView
38}
39
40func TestPath_Create(t *testing.T) {
41	t.Run("happy path", func(t *testing.T) {
42		b, storage := getBackend(t)
43
44		data := map[string]interface{}{
45			"role_type":       "jwt",
46			"bound_subject":   "testsub",
47			"bound_audiences": "vault",
48			"user_claim":      "user",
49			"groups_claim":    "groups",
50			"bound_cidrs":     "127.0.0.1/8",
51			"policies":        "test",
52			"period":          "3s",
53			"ttl":             "1s",
54			"num_uses":        12,
55			"max_ttl":         "5s",
56			"max_age":         "60s",
57		}
58
59		expectedSockAddr, err := sockaddr.NewSockAddr("127.0.0.1/8")
60		if err != nil {
61			t.Fatal(err)
62		}
63
64		expected := &jwtRole{
65			TokenParams: tokenutil.TokenParams{
66				TokenPolicies:   []string{"test"},
67				TokenPeriod:     3 * time.Second,
68				TokenTTL:        1 * time.Second,
69				TokenMaxTTL:     5 * time.Second,
70				TokenNumUses:    12,
71				TokenBoundCIDRs: []*sockaddr.SockAddrMarshaler{{SockAddr: expectedSockAddr}},
72			},
73			RoleType:            "jwt",
74			Policies:            []string{"test"},
75			Period:              3 * time.Second,
76			BoundSubject:        "testsub",
77			BoundAudiences:      []string{"vault"},
78			BoundClaimsType:     "string",
79			UserClaim:           "user",
80			GroupsClaim:         "groups",
81			TTL:                 1 * time.Second,
82			MaxTTL:              5 * time.Second,
83			ExpirationLeeway:    0,
84			NotBeforeLeeway:     0,
85			ClockSkewLeeway:     0,
86			NumUses:             12,
87			BoundCIDRs:          []*sockaddr.SockAddrMarshaler{{SockAddr: expectedSockAddr}},
88			AllowedRedirectURIs: []string(nil),
89			MaxAge:              60 * time.Second,
90		}
91
92		req := &logical.Request{
93			Operation: logical.CreateOperation,
94			Path:      "role/plugin-test",
95			Storage:   storage,
96			Data:      data,
97		}
98
99		resp, err := b.HandleRequest(context.Background(), req)
100		if err != nil || (resp != nil && resp.IsError()) {
101			t.Fatalf("err:%s resp:%#v\n", err, resp)
102		}
103		actual, err := b.(*jwtAuthBackend).role(context.Background(), storage, "plugin-test")
104		if err != nil {
105			t.Fatal(err)
106		}
107
108		if !reflect.DeepEqual(expected, actual) {
109			t.Fatalf("Unexpected role data: expected %#v\n got %#v\n", expected, actual)
110		}
111	})
112
113	t.Run("no user claim", func(t *testing.T) {
114		b, storage := getBackend(t)
115		data := map[string]interface{}{
116			"policies": "test",
117		}
118
119		req := &logical.Request{
120			Operation: logical.CreateOperation,
121			Path:      "role/test2",
122			Storage:   storage,
123			Data:      data,
124		}
125
126		resp, err := b.HandleRequest(context.Background(), req)
127		if err != nil {
128			t.Fatal(err)
129		}
130		if resp != nil && !resp.IsError() {
131			t.Fatalf("expected error")
132		}
133		if resp.Error().Error() != "a user claim must be defined on the role" {
134			t.Fatalf("unexpected err: %v", resp)
135		}
136	})
137
138	t.Run("no binding", func(t *testing.T) {
139		b, storage := getBackend(t)
140		data := map[string]interface{}{
141			"role_type":  "jwt",
142			"user_claim": "user",
143			"policies":   "test",
144		}
145
146		req := &logical.Request{
147			Operation: logical.CreateOperation,
148			Path:      "role/test3",
149			Storage:   storage,
150			Data:      data,
151		}
152
153		resp, err := b.HandleRequest(context.Background(), req)
154		if err != nil {
155			t.Fatal(err)
156		}
157		if resp != nil && !resp.IsError() {
158			t.Fatalf("expected error")
159		}
160		if !strings.HasPrefix(resp.Error().Error(), "must have at least one bound constraint") {
161			t.Fatalf("unexpected err: %v", resp)
162		}
163	})
164
165	t.Run("has bound subject", func(t *testing.T) {
166		b, storage := getBackend(t)
167		data := map[string]interface{}{
168			"role_type":     "jwt",
169			"user_claim":    "user",
170			"policies":      "test",
171			"bound_subject": "testsub",
172		}
173
174		req := &logical.Request{
175			Operation: logical.CreateOperation,
176			Path:      "role/test4",
177			Storage:   storage,
178			Data:      data,
179		}
180
181		resp, err := b.HandleRequest(context.Background(), req)
182		if err != nil {
183			t.Fatal(err)
184		}
185		if resp != nil && resp.IsError() {
186			t.Fatalf("did not expect error")
187		}
188	})
189
190	t.Run("has audience", func(t *testing.T) {
191		b, storage := getBackend(t)
192		// Test has audience
193		data := map[string]interface{}{
194			"role_type":       "jwt",
195			"user_claim":      "user",
196			"policies":        "test",
197			"bound_audiences": "vault",
198		}
199
200		req := &logical.Request{
201			Operation: logical.CreateOperation,
202			Path:      "role/test5",
203			Storage:   storage,
204			Data:      data,
205		}
206
207		resp, err := b.HandleRequest(context.Background(), req)
208		if err != nil {
209			t.Fatal(err)
210		}
211		if resp != nil && resp.IsError() {
212			t.Fatalf("did not expect error")
213		}
214	})
215
216	t.Run("has cidr", func(t *testing.T) {
217		b, storage := getBackend(t)
218		// Test has cidr
219		data := map[string]interface{}{
220			"role_type":   "jwt",
221			"user_claim":  "user",
222			"policies":    "test",
223			"bound_cidrs": "127.0.0.1/8",
224		}
225
226		req := &logical.Request{
227			Operation: logical.CreateOperation,
228			Path:      "role/test6",
229			Storage:   storage,
230			Data:      data,
231		}
232
233		resp, err := b.HandleRequest(context.Background(), req)
234		if err != nil {
235			t.Fatal(err)
236		}
237		if resp != nil && resp.IsError() {
238			t.Fatalf("did not expect error")
239		}
240	})
241
242	t.Run("has bound claims", func(t *testing.T) {
243		b, storage := getBackend(t)
244		data := map[string]interface{}{
245			"role_type":  "jwt",
246			"user_claim": "user",
247			"policies":   "test",
248			"bound_claims": map[string]interface{}{
249				"foo": 10,
250				"bar": "baz",
251			},
252		}
253
254		req := &logical.Request{
255			Operation: logical.CreateOperation,
256			Path:      "role/test7",
257			Storage:   storage,
258			Data:      data,
259		}
260
261		resp, err := b.HandleRequest(context.Background(), req)
262		if err != nil {
263			t.Fatal(err)
264		}
265		if resp != nil && resp.IsError() {
266			t.Fatalf("did not expect error")
267		}
268	})
269
270	t.Run("has expiration, not before custom leeways", func(t *testing.T) {
271		b, storage := getBackend(t)
272		data := map[string]interface{}{
273			"role_type":         "jwt",
274			"user_claim":        "user",
275			"policies":          "test",
276			"expiration_leeway": "5s",
277			"not_before_leeway": "5s",
278			"clock_skew_leeway": "5s",
279			"bound_claims": map[string]interface{}{
280				"foo": 10,
281				"bar": "baz",
282			},
283		}
284
285		req := &logical.Request{
286			Operation: logical.CreateOperation,
287			Path:      "role/test8",
288			Storage:   storage,
289			Data:      data,
290		}
291
292		resp, err := b.HandleRequest(context.Background(), req)
293		if err != nil {
294			t.Fatal(err)
295		}
296		if resp != nil && resp.IsError() {
297			t.Fatalf("did not expect error:%s", resp.Error().Error())
298		}
299
300		actual, err := b.(*jwtAuthBackend).role(context.Background(), storage, "test8")
301		if err != nil {
302			t.Fatal(err)
303		}
304
305		expectedDuration := "5s"
306		if actual.ExpirationLeeway.String() != expectedDuration {
307			t.Fatalf("expiration_leeway - expected: %s, got: %s", expectedDuration, actual.ExpirationLeeway)
308		}
309
310		if actual.NotBeforeLeeway.String() != expectedDuration {
311			t.Fatalf("not_before_leeway - expected: %s, got: %s", expectedDuration, actual.NotBeforeLeeway)
312		}
313
314		if actual.ClockSkewLeeway.String() != expectedDuration {
315			t.Fatalf("clock_skew_leeway - expected: %s, got: %s", expectedDuration, actual.ClockSkewLeeway)
316		}
317	})
318
319	t.Run("storing zero leeways", func(t *testing.T) {
320		b, storage := getBackend(t)
321		data := map[string]interface{}{
322			"role_type":         "jwt",
323			"user_claim":        "user",
324			"policies":          "test",
325			"clock_skew_leeway": "0",
326			"expiration_leeway": "0",
327			"not_before_leeway": "0",
328			"bound_claims": map[string]interface{}{
329				"foo": 10,
330				"bar": "baz",
331			},
332		}
333
334		req := &logical.Request{
335			Operation: logical.CreateOperation,
336			Path:      "role/test9",
337			Storage:   storage,
338			Data:      data,
339		}
340
341		resp, err := b.HandleRequest(context.Background(), req)
342		if err != nil {
343			t.Fatal(err)
344		}
345		if resp != nil && resp.IsError() {
346			t.Fatalf("did not expect error:%s", resp.Error().Error())
347		}
348
349		actual, err := b.(*jwtAuthBackend).role(context.Background(), storage, "test9")
350		if err != nil {
351			t.Fatal(err)
352		}
353
354		if actual.ClockSkewLeeway.Seconds() != 0 {
355			t.Fatalf("clock_skew_leeway - expected: 0, got: %v", actual.ClockSkewLeeway.Seconds())
356		}
357		if actual.ExpirationLeeway.Seconds() != 0 {
358			t.Fatalf("expiration_leeway - expected: 0, got: %v", actual.ExpirationLeeway.Seconds())
359		}
360		if actual.NotBeforeLeeway.Seconds() != 0 {
361			t.Fatalf("not_before_leeway - expected: 0, got: %v", actual.NotBeforeLeeway.Seconds())
362		}
363	})
364
365	t.Run("storing negative leeways", func(t *testing.T) {
366		b, storage := getBackend(t)
367		data := map[string]interface{}{
368			"role_type":         "jwt",
369			"user_claim":        "user",
370			"policies":          "test",
371			"clock_skew_leeway": "-1",
372			"expiration_leeway": "-1",
373			"not_before_leeway": "-1",
374			"bound_claims": map[string]interface{}{
375				"foo": 10,
376				"bar": "baz",
377			},
378		}
379
380		req := &logical.Request{
381			Operation: logical.CreateOperation,
382			Path:      "role/test9",
383			Storage:   storage,
384			Data:      data,
385		}
386
387		resp, err := b.HandleRequest(context.Background(), req)
388		if err != nil {
389			t.Fatal(err)
390		}
391		if resp != nil && resp.IsError() {
392			t.Fatalf("did not expect error:%s", resp.Error().Error())
393		}
394
395		actual, err := b.(*jwtAuthBackend).role(context.Background(), storage, "test9")
396		if err != nil {
397			t.Fatal(err)
398		}
399
400		if actual.ClockSkewLeeway.Seconds() != -1 {
401			t.Fatalf("clock_skew_leeway - expected: -1, got: %v", actual.ClockSkewLeeway.Seconds())
402		}
403		if actual.ExpirationLeeway.Seconds() != -1 {
404			t.Fatalf("expiration_leeway - expected: -1, got: %v", actual.ExpirationLeeway.Seconds())
405		}
406		if actual.NotBeforeLeeway.Seconds() != -1 {
407			t.Fatalf("not_before_leeway - expected: -1, got: %v", actual.NotBeforeLeeway.Seconds())
408		}
409	})
410
411	t.Run("storing an invalid bound_claim_type", func(t *testing.T) {
412		b, storage := getBackend(t)
413		data := map[string]interface{}{
414			"role_type":         "jwt",
415			"user_claim":        "user",
416			"policies":          "test",
417			"clock_skew_leeway": "-1",
418			"expiration_leeway": "-1",
419			"not_before_leeway": "-1",
420			"bound_claims_type": "invalid",
421			"bound_claims": map[string]interface{}{
422				"foo": 10,
423				"bar": "baz",
424			},
425		}
426
427		req := &logical.Request{
428			Operation: logical.CreateOperation,
429			Path:      "role/test10",
430			Storage:   storage,
431			Data:      data,
432		}
433
434		resp, err := b.HandleRequest(context.Background(), req)
435		if err != nil {
436			t.Fatal(err)
437		}
438		if resp != nil && !resp.IsError() {
439			t.Fatalf("expected error")
440		}
441		if resp.Error().Error() != "invalid 'bound_claims_type': invalid" {
442			t.Fatalf("unexpected err: %v", resp)
443		}
444	})
445
446	t.Run("role with invalid glob in claim", func(t *testing.T) {
447		b, storage := getBackend(t)
448		data := map[string]interface{}{
449			"role_type":         "jwt",
450			"user_claim":        "user",
451			"policies":          "test",
452			"clock_skew_leeway": "-1",
453			"expiration_leeway": "-1",
454			"not_before_leeway": "-1",
455			"bound_claims_type": "glob",
456			"bound_claims": map[string]interface{}{
457				"bar": "baz",
458				"foo": 25,
459			},
460		}
461
462		req := &logical.Request{
463			Operation: logical.CreateOperation,
464			Path:      "role/test11",
465			Storage:   storage,
466			Data:      data,
467		}
468
469		resp, err := b.HandleRequest(context.Background(), req)
470		if err != nil {
471			t.Fatal(err)
472		}
473		if resp != nil && !resp.IsError() {
474			t.Fatalf("expected error")
475		}
476		if resp.Error().Error() != "claim is not a string or list: 25" {
477			t.Fatalf("unexpected err: %v", resp)
478		}
479	})
480
481	t.Run("role with invalid glob in claim array", func(t *testing.T) {
482		b, storage := getBackend(t)
483		data := map[string]interface{}{
484			"role_type":         "jwt",
485			"user_claim":        "user",
486			"policies":          "test",
487			"clock_skew_leeway": "-1",
488			"expiration_leeway": "-1",
489			"not_before_leeway": "-1",
490			"bound_claims_type": "glob",
491			"bound_claims": map[string]interface{}{
492				"foo": []interface{}{"baz", 10},
493			},
494		}
495
496		req := &logical.Request{
497			Operation: logical.CreateOperation,
498			Path:      "role/test12",
499			Storage:   storage,
500			Data:      data,
501		}
502
503		resp, err := b.HandleRequest(context.Background(), req)
504		if err != nil {
505			t.Fatal(err)
506		}
507		if resp != nil && !resp.IsError() {
508			t.Fatalf("expected error")
509		}
510		if resp.Error().Error() != "claim is not a string: 10" {
511			t.Fatalf("unexpected err: %v", resp)
512		}
513	})
514}
515
516func TestPath_OIDCCreate(t *testing.T) {
517	t.Run("both explicit and default role_type", func(t *testing.T) {
518		b, storage := getBackend(t)
519
520		data := map[string]interface{}{
521			"bound_audiences": "vault",
522			"bound_claims": map[string]interface{}{
523				"foo": 10,
524				"bar": "baz",
525			},
526			"oidc_scopes":           []string{"email", "profile"},
527			"allowed_redirect_uris": []string{"https://example.com", "http://localhost:8250"},
528			"claim_mappings": map[string]string{
529				"foo": "a",
530				"bar": "b",
531			},
532			"user_claim":        "user",
533			"groups_claim":      "groups",
534			"policies":          "test",
535			"period":            "3s",
536			"ttl":               "1s",
537			"num_uses":          12,
538			"max_ttl":           "5s",
539			"expiration_leeway": "300s",
540			"not_before_leeway": "300s",
541			"clock_skew_leeway": "1s",
542		}
543
544		expected := &jwtRole{
545			TokenParams: tokenutil.TokenParams{
546				TokenPolicies: []string{"test"},
547				TokenPeriod:   3 * time.Second,
548				TokenTTL:      1 * time.Second,
549				TokenMaxTTL:   5 * time.Second,
550				TokenNumUses:  12,
551			},
552			RoleType:        "oidc",
553			Policies:        []string{"test"},
554			Period:          3 * time.Second,
555			BoundAudiences:  []string{"vault"},
556			BoundClaimsType: "string",
557			BoundClaims: map[string]interface{}{
558				"foo": json.Number("10"),
559				"bar": "baz",
560			},
561			AllowedRedirectURIs: []string{"https://example.com", "http://localhost:8250"},
562			ClaimMappings: map[string]string{
563				"foo": "a",
564				"bar": "b",
565			},
566			OIDCScopes:       []string{"email", "profile"},
567			UserClaim:        "user",
568			GroupsClaim:      "groups",
569			TTL:              1 * time.Second,
570			MaxTTL:           5 * time.Second,
571			ExpirationLeeway: 300 * time.Second,
572			NotBeforeLeeway:  300 * time.Second,
573			ClockSkewLeeway:  1 * time.Second,
574			NumUses:          12,
575		}
576
577		for _, roleType := range []string{"", "oidc"} {
578			data["role_type"] = roleType
579			req := &logical.Request{
580				Operation: logical.CreateOperation,
581				Path:      "role/plugin-test",
582				Storage:   storage,
583				Data:      data,
584			}
585
586			resp, err := b.HandleRequest(context.Background(), req)
587			if err != nil || (resp != nil && resp.IsError()) {
588				t.Fatalf("err:%s resp:%#v\n", err, resp)
589			}
590			actual, err := b.(*jwtAuthBackend).role(context.Background(), storage, "plugin-test")
591			if err != nil {
592				t.Fatal(err)
593			}
594
595			if diff := deep.Equal(expected, actual); diff != nil {
596				t.Fatal(diff)
597			}
598		}
599	})
600
601	t.Run("invalid reserved metadata key role", func(t *testing.T) {
602		b, storage := getBackend(t)
603
604		data := map[string]interface{}{
605			"bound_audiences": "vault",
606			"bound_claims": map[string]interface{}{
607				"foo": 10,
608				"bar": "baz",
609			},
610			"oidc_scopes":           []string{"email", "profile"},
611			"allowed_redirect_uris": []string{"https://example.com", "http://localhost:8250"},
612			"claim_mappings": map[string]string{
613				"foo":        "a",
614				"some_claim": "role",
615			},
616			"user_claim":        "user",
617			"groups_claim":      "groups",
618			"policies":          "test",
619			"period":            "3s",
620			"ttl":               "1s",
621			"num_uses":          12,
622			"max_ttl":           "5s",
623			"expiration_leeway": "300s",
624			"not_before_leeway": "300s",
625			"clock_skew_leeway": "1s",
626		}
627
628		req := &logical.Request{
629			Operation: logical.CreateOperation,
630			Path:      "role/test2",
631			Storage:   storage,
632			Data:      data,
633		}
634
635		resp, err := b.HandleRequest(context.Background(), req)
636		if err != nil {
637			t.Fatal(err)
638		}
639		if resp != nil && !resp.IsError() {
640			t.Fatalf("expected error")
641		}
642		if !strings.Contains(resp.Error().Error(), `metadata key "role" is reserved`) {
643			t.Fatalf("unexpected err: %v", resp)
644		}
645	})
646
647	t.Run("invalid duplicate metadata destination", func(t *testing.T) {
648		b, storage := getBackend(t)
649
650		data := map[string]interface{}{
651			"bound_audiences": "vault",
652			"bound_claims": map[string]interface{}{
653				"foo": 10,
654				"bar": "baz",
655			},
656			"oidc_scopes":           []string{"email", "profile"},
657			"allowed_redirect_uris": []string{"https://example.com", "http://localhost:8250"},
658			"claim_mappings": map[string]string{
659				"foo": "a",
660				"bar": "a",
661			},
662			"user_claim":        "user",
663			"groups_claim":      "groups",
664			"policies":          "test",
665			"period":            "3s",
666			"ttl":               "1s",
667			"num_uses":          12,
668			"max_ttl":           "5s",
669			"expiration_leeway": "300s",
670			"not_before_leeway": "300s",
671			"clock_skew_leeway": "1s",
672		}
673
674		req := &logical.Request{
675			Operation: logical.CreateOperation,
676			Path:      "role/test2",
677			Storage:   storage,
678			Data:      data,
679		}
680
681		resp, err := b.HandleRequest(context.Background(), req)
682		if err != nil {
683			t.Fatal(err)
684		}
685		if resp != nil && !resp.IsError() {
686			t.Fatalf("expected error")
687		}
688		if !strings.Contains(resp.Error().Error(), `multiple keys are mapped to metadata key "a"`) {
689			t.Fatalf("unexpected err: %v", resp)
690		}
691	})
692
693	t.Run("custom expiration_leeway and not_before_leeway values", func(t *testing.T) {
694		b, storage := getBackend(t)
695
696		data := map[string]interface{}{
697			"user_claim":        "user",
698			"expiration_leeway": "5s",
699			"not_before_leeway": "5s",
700			"bound_claims": map[string]interface{}{
701				"foo": "a",
702				"bar": "b",
703			},
704			"allowed_redirect_uris": []string{"https://example.com", "http://localhost:8250"},
705		}
706
707		req := &logical.Request{
708			Operation: logical.CreateOperation,
709			Path:      "role/test3",
710			Storage:   storage,
711			Data:      data,
712		}
713
714		resp, err := b.HandleRequest(context.Background(), req)
715		if err != nil {
716			t.Fatal(err)
717		}
718		if resp != nil && resp.IsError() {
719			t.Fatalf("unexpected error: %s", resp.Error().Error())
720		}
721
722		actual, err := b.(*jwtAuthBackend).role(context.Background(), storage, "test3")
723		if err != nil {
724			t.Fatal(err)
725		}
726
727		expectedDuration := "5s"
728		if actual.ExpirationLeeway.String() != expectedDuration {
729			t.Fatalf("expiration_leeway - expected: %s, got: %s", expectedDuration, actual.ExpirationLeeway)
730		}
731
732		if actual.NotBeforeLeeway.String() != expectedDuration {
733			t.Fatalf("not_before_leeway - expected: %s, got: %s", expectedDuration, actual.NotBeforeLeeway)
734		}
735	})
736}
737
738func TestPath_Read(t *testing.T) {
739	b, storage := getBackend(t)
740
741	data := map[string]interface{}{
742		"role_type":             "jwt",
743		"bound_subject":         "testsub",
744		"bound_audiences":       "vault",
745		"allowed_redirect_uris": []string{"http://127.0.0.1"},
746		"oidc_scopes":           []string{"email", "profile"},
747		"user_claim":            "user",
748		"groups_claim":          "groups",
749		"bound_cidrs":           "127.0.0.1/8",
750		"policies":              "test",
751		"period":                "3s",
752		"ttl":                   "1s",
753		"num_uses":              12,
754		"max_ttl":               "5s",
755		"expiration_leeway":     "500s",
756		"not_before_leeway":     "500s",
757		"clock_skew_leeway":     "100s",
758	}
759
760	expected := map[string]interface{}{
761		"role_type":               "jwt",
762		"bound_claims_type":       "string",
763		"bound_claims":            map[string]interface{}(nil),
764		"claim_mappings":          map[string]string(nil),
765		"bound_subject":           "testsub",
766		"bound_audiences":         []string{"vault"},
767		"allowed_redirect_uris":   []string{"http://127.0.0.1"},
768		"oidc_scopes":             []string{"email", "profile"},
769		"user_claim":              "user",
770		"groups_claim":            "groups",
771		"token_policies":          []string{"test"},
772		"policies":                []string{"test"},
773		"token_period":            int64(3),
774		"period":                  int64(3),
775		"token_ttl":               int64(1),
776		"ttl":                     int64(1),
777		"token_num_uses":          12,
778		"num_uses":                12,
779		"token_max_ttl":           int64(5),
780		"max_ttl":                 int64(5),
781		"expiration_leeway":       int64(500),
782		"not_before_leeway":       int64(500),
783		"clock_skew_leeway":       int64(100),
784		"verbose_oidc_logging":    false,
785		"token_type":              logical.TokenTypeDefault.String(),
786		"token_no_default_policy": false,
787		"token_explicit_max_ttl":  int64(0),
788		"max_age":                 int64(0),
789	}
790
791	req := &logical.Request{
792		Operation: logical.CreateOperation,
793		Path:      "role/plugin-test",
794		Storage:   storage,
795		Data:      data,
796	}
797
798	resp, err := b.HandleRequest(context.Background(), req)
799	if err != nil || (resp != nil && resp.IsError()) {
800		t.Fatalf("err:%s resp:%#v\n", err, resp)
801	}
802
803	readTest := func() {
804		req = &logical.Request{
805			Operation: logical.ReadOperation,
806			Path:      "role/plugin-test",
807			Storage:   storage,
808		}
809
810		resp, err = b.HandleRequest(context.Background(), req)
811		if err != nil || (resp != nil && resp.IsError()) {
812			t.Fatalf("err:%s resp:%#v\n", err, resp)
813		}
814
815		if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1/8" {
816			t.Fatal("unexpected bound cidrs")
817		}
818		delete(resp.Data, "bound_cidrs")
819		if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1/8" {
820			t.Fatal("unexpected token bound cidrs")
821		}
822		delete(resp.Data, "token_bound_cidrs")
823		if diff := deep.Equal(expected, resp.Data); diff != nil {
824			t.Fatal(diff)
825		}
826	}
827
828	// Run read test for normal case
829	readTest()
830
831	// Remove the 'role_type' parameter in stored role to simulate a legacy role
832	rolePath := rolePrefix + "plugin-test"
833	raw, err := storage.Get(context.Background(), rolePath)
834
835	var role map[string]interface{}
836	if err := raw.DecodeJSON(&role); err != nil {
837		t.Fatal(err)
838	}
839	delete(role, "role_type")
840	entry, err := logical.StorageEntryJSON(rolePath, role)
841	if err != nil {
842		t.Fatal(err)
843	}
844
845	if err = req.Storage.Put(context.Background(), entry); err != nil {
846		t.Fatal(err)
847	}
848
849	// Run read test for "upgrade" case. The legacy role is not changed in storage, but
850	// reads will populate the `role_type` with "jwt".
851	readTest()
852
853	// Remove the 'bound_claims_type' parameter in stored role to simulate a legacy role
854	raw, err = storage.Get(context.Background(), rolePath)
855
856	if err := raw.DecodeJSON(&role); err != nil {
857		t.Fatal(err)
858	}
859	delete(role, "bound_claims_type")
860	entry, err = logical.StorageEntryJSON(rolePath, role)
861	if err != nil {
862		t.Fatal(err)
863	}
864
865	if err = req.Storage.Put(context.Background(), entry); err != nil {
866		t.Fatal(err)
867	}
868
869	// Run read test for "upgrade" case. The legacy role is not changed in storage, but
870	// reads will populate the `bound_claims_type` with "string".
871	readTest()
872}
873
874func TestPath_Delete(t *testing.T) {
875	b, storage := getBackend(t)
876
877	data := map[string]interface{}{
878		"role_type":         "jwt",
879		"bound_subject":     "testsub",
880		"bound_audiences":   "vault",
881		"user_claim":        "user",
882		"groups_claim":      "groups",
883		"bound_cidrs":       "127.0.0.1/8",
884		"policies":          "test",
885		"period":            "3s",
886		"ttl":               "1s",
887		"num_uses":          12,
888		"max_ttl":           "5s",
889		"expiration_leeway": "300s",
890		"not_before_leeway": "300s",
891	}
892
893	req := &logical.Request{
894		Operation: logical.CreateOperation,
895		Path:      "role/plugin-test",
896		Storage:   storage,
897		Data:      data,
898	}
899
900	resp, err := b.HandleRequest(context.Background(), req)
901	if err != nil || (resp != nil && resp.IsError()) {
902		t.Fatalf("err:%s resp:%#v\n", err, resp)
903	}
904
905	req = &logical.Request{
906		Operation: logical.DeleteOperation,
907		Path:      "role/plugin-test",
908		Storage:   storage,
909	}
910
911	resp, err = b.HandleRequest(context.Background(), req)
912	if err != nil || (resp != nil && resp.IsError()) {
913		t.Fatalf("err:%s resp:%#v\n", err, resp)
914	}
915
916	if resp != nil {
917		t.Fatalf("Unexpected resp data: expected nil got %#v\n", resp.Data)
918	}
919
920	req = &logical.Request{
921		Operation: logical.ReadOperation,
922		Path:      "role/plugin-test",
923		Storage:   storage,
924	}
925
926	resp, err = b.HandleRequest(context.Background(), req)
927	if err != nil || (resp != nil && resp.IsError()) {
928		t.Fatalf("err:%s resp:%#v\n", err, resp)
929	}
930
931	if resp != nil {
932		t.Fatalf("Unexpected resp data: expected nil got %#v\n", resp.Data)
933	}
934}
935