1package vault_test
2
3import (
4	"fmt"
5	"io/ioutil"
6	"os"
7	"path/filepath"
8	"testing"
9	"time"
10
11	"github.com/go-test/deep"
12	"github.com/hashicorp/vault/api"
13	"github.com/hashicorp/vault/builtin/plugin"
14	"github.com/hashicorp/vault/helper/namespace"
15	vaulthttp "github.com/hashicorp/vault/http"
16	"github.com/hashicorp/vault/sdk/helper/consts"
17	"github.com/hashicorp/vault/sdk/helper/pluginutil"
18	"github.com/hashicorp/vault/sdk/logical"
19	lplugin "github.com/hashicorp/vault/sdk/plugin"
20	"github.com/hashicorp/vault/sdk/plugin/mock"
21	"github.com/hashicorp/vault/vault"
22)
23
24const (
25	expectedEnvKey   = "FOO"
26	expectedEnvValue = "BAR"
27)
28
29func TestSystemBackend_Plugin_secret(t *testing.T) {
30	cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical)
31	defer cluster.Cleanup()
32
33	core := cluster.Cores[0]
34
35	// Make a request to lazy load the plugin
36	req := logical.TestRequest(t, logical.ReadOperation, "mock-0/internal")
37	req.ClientToken = core.Client.Token()
38	resp, err := core.HandleRequest(namespace.RootContext(nil), req)
39	if err != nil {
40		t.Fatalf("err: %v", err)
41	}
42	if resp == nil {
43		t.Fatalf("bad: response should not be nil")
44	}
45
46	// Seal the cluster
47	cluster.EnsureCoresSealed(t)
48
49	// Unseal the cluster
50	barrierKeys := cluster.BarrierKeys
51	for _, core := range cluster.Cores {
52		for _, key := range barrierKeys {
53			_, err := core.Unseal(vault.TestKeyCopy(key))
54			if err != nil {
55				t.Fatal(err)
56			}
57		}
58		if core.Sealed() {
59			t.Fatal("should not be sealed")
60		}
61		// Wait for active so post-unseal takes place
62		// If it fails, it means unseal process failed
63		vault.TestWaitActive(t, core.Core)
64	}
65}
66
67func TestSystemBackend_Plugin_auth(t *testing.T) {
68	cluster := testSystemBackendMock(t, 1, 1, logical.TypeCredential)
69	defer cluster.Cleanup()
70
71	core := cluster.Cores[0]
72
73	// Make a request to lazy load the plugin
74	req := logical.TestRequest(t, logical.ReadOperation, "auth/mock-0/internal")
75	req.ClientToken = core.Client.Token()
76	resp, err := core.HandleRequest(namespace.RootContext(nil), req)
77	if err != nil {
78		t.Fatalf("err: %v", err)
79	}
80	if resp == nil {
81		t.Fatalf("bad: response should not be nil")
82	}
83
84	// Seal the cluster
85	cluster.EnsureCoresSealed(t)
86
87	// Unseal the cluster
88	barrierKeys := cluster.BarrierKeys
89	for _, core := range cluster.Cores {
90		for _, key := range barrierKeys {
91			_, err := core.Unseal(vault.TestKeyCopy(key))
92			if err != nil {
93				t.Fatal(err)
94			}
95		}
96		if core.Sealed() {
97			t.Fatal("should not be sealed")
98		}
99		// Wait for active so post-unseal takes place
100		// If it fails, it means unseal process failed
101		vault.TestWaitActive(t, core.Core)
102	}
103}
104
105func TestSystemBackend_Plugin_MismatchType(t *testing.T) {
106	cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical)
107	defer cluster.Cleanup()
108
109	core := cluster.Cores[0]
110
111	// Add a credential backend with the same name
112	vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "TestBackend_PluginMainCredentials", []string{}, "")
113
114	// Make a request to lazy load the now-credential plugin
115	// and expect an error
116	req := logical.TestRequest(t, logical.ReadOperation, "mock-0/internal")
117	req.ClientToken = core.Client.Token()
118	_, err := core.HandleRequest(namespace.RootContext(nil), req)
119	if err != nil {
120		t.Fatalf("adding a same-named plugin of a different type should be no problem: %s", err)
121	}
122
123	// Sleep a bit before cleanup is called
124	time.Sleep(1 * time.Second)
125}
126
127func TestSystemBackend_Plugin_CatalogRemoved(t *testing.T) {
128	t.Run("secret", func(t *testing.T) {
129		testPlugin_CatalogRemoved(t, logical.TypeLogical, false)
130	})
131
132	t.Run("auth", func(t *testing.T) {
133		testPlugin_CatalogRemoved(t, logical.TypeCredential, false)
134	})
135
136	t.Run("secret-mount-existing", func(t *testing.T) {
137		testPlugin_CatalogRemoved(t, logical.TypeLogical, true)
138	})
139
140	t.Run("auth-mount-existing", func(t *testing.T) {
141		testPlugin_CatalogRemoved(t, logical.TypeCredential, true)
142	})
143}
144
145func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMount bool) {
146	cluster := testSystemBackendMock(t, 1, 1, btype)
147	defer cluster.Cleanup()
148
149	core := cluster.Cores[0]
150
151	// Remove the plugin from the catalog
152	req := logical.TestRequest(t, logical.DeleteOperation, "sys/plugins/catalog/database/mock-plugin")
153	req.ClientToken = core.Client.Token()
154	resp, err := core.HandleRequest(namespace.RootContext(nil), req)
155	if err != nil || (resp != nil && resp.IsError()) {
156		t.Fatalf("err:%v resp:%#v", err, resp)
157	}
158
159	// Seal the cluster
160	cluster.EnsureCoresSealed(t)
161
162	// Unseal the cluster
163	barrierKeys := cluster.BarrierKeys
164	for _, core := range cluster.Cores {
165		for _, key := range barrierKeys {
166			_, err := core.Unseal(vault.TestKeyCopy(key))
167			if err != nil {
168				t.Fatal(err)
169			}
170		}
171		if core.Sealed() {
172			t.Fatal("should not be sealed")
173		}
174	}
175
176	// Wait for active so post-unseal takes place
177	// If it fails, it means unseal process failed
178	vault.TestWaitActive(t, core.Core)
179
180	if testMount {
181		// Mount the plugin at the same path after plugin is re-added to the catalog
182		// and expect an error due to existing path.
183		var err error
184		switch btype {
185		case logical.TypeLogical:
186			// Add plugin back to the catalog
187			vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "TestBackend_PluginMainLogical", []string{}, "")
188			_, err = core.Client.Logical().Write("sys/mounts/mock-0", map[string]interface{}{
189				"type": "test",
190			})
191		case logical.TypeCredential:
192			// Add plugin back to the catalog
193			vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "TestBackend_PluginMainCredentials", []string{}, "")
194			_, err = core.Client.Logical().Write("sys/auth/mock-0", map[string]interface{}{
195				"type": "test",
196			})
197		}
198		if err == nil {
199			t.Fatal("expected error when mounting on existing path")
200		}
201	}
202}
203
204func TestSystemBackend_Plugin_continueOnError(t *testing.T) {
205	t.Run("secret", func(t *testing.T) {
206		t.Run("sha256_mismatch", func(t *testing.T) {
207			testPlugin_continueOnError(t, logical.TypeLogical, true, consts.PluginTypeSecrets)
208		})
209
210		t.Run("missing_plugin", func(t *testing.T) {
211			testPlugin_continueOnError(t, logical.TypeLogical, false, consts.PluginTypeSecrets)
212		})
213	})
214
215	t.Run("auth", func(t *testing.T) {
216		t.Run("sha256_mismatch", func(t *testing.T) {
217			testPlugin_continueOnError(t, logical.TypeCredential, true, consts.PluginTypeCredential)
218		})
219
220		t.Run("missing_plugin", func(t *testing.T) {
221			testPlugin_continueOnError(t, logical.TypeCredential, false, consts.PluginTypeCredential)
222		})
223	})
224}
225
226func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatch bool, pluginType consts.PluginType) {
227	cluster := testSystemBackendMock(t, 1, 1, btype)
228	defer cluster.Cleanup()
229
230	core := cluster.Cores[0]
231
232	// Get the registered plugin
233	req := logical.TestRequest(t, logical.ReadOperation, fmt.Sprintf("sys/plugins/catalog/%s/mock-plugin", pluginType))
234	req.ClientToken = core.Client.Token()
235	resp, err := core.HandleRequest(namespace.RootContext(nil), req)
236	if err != nil || resp == nil || (resp != nil && resp.IsError()) {
237		t.Fatalf("err:%v resp:%#v", err, resp)
238	}
239
240	command, ok := resp.Data["command"].(string)
241	if !ok || command == "" {
242		t.Fatal("invalid command")
243	}
244
245	// Trigger a sha256 mismatch or missing plugin error
246	if mismatch {
247		req = logical.TestRequest(t, logical.UpdateOperation, "sys/plugins/catalog/database/mock-plugin")
248		req.Data = map[string]interface{}{
249			"sha256":  "d17bd7334758e53e6fbab15745d2520765c06e296f2ce8e25b7919effa0ac216",
250			"command": filepath.Base(command),
251		}
252		req.ClientToken = core.Client.Token()
253		resp, err = core.HandleRequest(namespace.RootContext(nil), req)
254		if err != nil || (resp != nil && resp.IsError()) {
255			t.Fatalf("err:%v resp:%#v", err, resp)
256		}
257	} else {
258		err := os.Remove(filepath.Join(cluster.TempDir, filepath.Base(command)))
259		if err != nil {
260			t.Fatal(err)
261		}
262	}
263
264	// Seal the cluster
265	cluster.EnsureCoresSealed(t)
266
267	// Unseal the cluster
268	barrierKeys := cluster.BarrierKeys
269	for _, core := range cluster.Cores {
270		for _, key := range barrierKeys {
271			_, err := core.Unseal(vault.TestKeyCopy(key))
272			if err != nil {
273				t.Fatal(err)
274			}
275		}
276		if core.Sealed() {
277			t.Fatal("should not be sealed")
278		}
279	}
280
281	// Wait for active so post-unseal takes place
282	// If it fails, it means unseal process failed
283	vault.TestWaitActive(t, core.Core)
284
285	// Re-add the plugin to the catalog
286	switch btype {
287	case logical.TypeLogical:
288		vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "TestBackend_PluginMainLogical", []string{}, cluster.TempDir)
289	case logical.TypeCredential:
290		vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "TestBackend_PluginMainCredentials", []string{}, cluster.TempDir)
291	}
292
293	// Reload the plugin
294	req = logical.TestRequest(t, logical.UpdateOperation, "sys/plugins/reload/backend")
295	req.Data = map[string]interface{}{
296		"plugin": "mock-plugin",
297	}
298	req.ClientToken = core.Client.Token()
299	resp, err = core.HandleRequest(namespace.RootContext(nil), req)
300	if err != nil || (resp != nil && resp.IsError()) {
301		t.Fatalf("err:%v resp:%#v", err, resp)
302	}
303
304	// Make a request to lazy load the plugin
305	var reqPath string
306	switch btype {
307	case logical.TypeLogical:
308		reqPath = "mock-0/internal"
309	case logical.TypeCredential:
310		reqPath = "auth/mock-0/internal"
311	}
312
313	req = logical.TestRequest(t, logical.ReadOperation, reqPath)
314	req.ClientToken = core.Client.Token()
315	resp, err = core.HandleRequest(namespace.RootContext(nil), req)
316	if err != nil {
317		t.Fatalf("err: %v", err)
318	}
319	if resp == nil {
320		t.Fatalf("bad: response should not be nil")
321	}
322}
323
324func TestSystemBackend_Plugin_autoReload(t *testing.T) {
325	cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical)
326	defer cluster.Cleanup()
327
328	core := cluster.Cores[0]
329
330	// Update internal value
331	req := logical.TestRequest(t, logical.UpdateOperation, "mock-0/internal")
332	req.ClientToken = core.Client.Token()
333	req.Data["value"] = "baz"
334	resp, err := core.HandleRequest(namespace.RootContext(nil), req)
335	if err != nil {
336		t.Fatalf("err: %v", err)
337	}
338	if resp != nil {
339		t.Fatalf("bad: %v", resp)
340	}
341
342	// Call errors/rpc endpoint to trigger reload
343	req = logical.TestRequest(t, logical.ReadOperation, "mock-0/errors/rpc")
344	req.ClientToken = core.Client.Token()
345	resp, err = core.HandleRequest(namespace.RootContext(nil), req)
346	if err == nil {
347		t.Fatalf("expected error from error/rpc request")
348	}
349
350	// Check internal value to make sure it's reset
351	req = logical.TestRequest(t, logical.ReadOperation, "mock-0/internal")
352	req.ClientToken = core.Client.Token()
353	resp, err = core.HandleRequest(namespace.RootContext(nil), req)
354	if err != nil {
355		t.Fatalf("err: %v", err)
356	}
357	if resp == nil {
358		t.Fatalf("bad: response should not be nil")
359	}
360	if resp.Data["value"].(string) == "baz" {
361		t.Fatal("did not expect backend internal value to be 'baz'")
362	}
363}
364
365func TestSystemBackend_Plugin_SealUnseal(t *testing.T) {
366	cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical)
367	defer cluster.Cleanup()
368
369	// Seal the cluster
370	cluster.EnsureCoresSealed(t)
371
372	// Unseal the cluster
373	barrierKeys := cluster.BarrierKeys
374	for _, core := range cluster.Cores {
375		for _, key := range barrierKeys {
376			_, err := core.Unseal(vault.TestKeyCopy(key))
377			if err != nil {
378				t.Fatal(err)
379			}
380		}
381		if core.Sealed() {
382			t.Fatal("should not be sealed")
383		}
384	}
385
386	// Wait for active so post-unseal takes place
387	// If it fails, it means unseal process failed
388	vault.TestWaitActive(t, cluster.Cores[0].Core)
389}
390
391func TestSystemBackend_Plugin_reload(t *testing.T) {
392	data := map[string]interface{}{
393		"plugin": "mock-plugin",
394	}
395	t.Run("plugin", func(t *testing.T) { testSystemBackend_PluginReload(t, data) })
396
397	data = map[string]interface{}{
398		"mounts": "mock-0/,mock-1/",
399	}
400	t.Run("mounts", func(t *testing.T) { testSystemBackend_PluginReload(t, data) })
401}
402
403// Helper func to test different reload methods on plugin reload endpoint
404func testSystemBackend_PluginReload(t *testing.T, reqData map[string]interface{}) {
405	cluster := testSystemBackendMock(t, 1, 2, logical.TypeLogical)
406	defer cluster.Cleanup()
407
408	core := cluster.Cores[0]
409	client := core.Client
410
411	for i := 0; i < 2; i++ {
412		// Update internal value in the backend
413		resp, err := client.Logical().Write(fmt.Sprintf("mock-%d/internal", i), map[string]interface{}{
414			"value": "baz",
415		})
416		if err != nil {
417			t.Fatalf("err: %v", err)
418		}
419		if resp != nil {
420			t.Fatalf("bad: %v", resp)
421		}
422	}
423
424	// Perform plugin reload
425	resp, err := client.Logical().Write("sys/plugins/reload/backend", reqData)
426	if err != nil {
427		t.Fatalf("err: %v", err)
428	}
429	if resp != nil {
430		t.Fatalf("bad: %v", resp)
431	}
432
433	for i := 0; i < 2; i++ {
434		// Ensure internal backed value is reset
435		resp, err := client.Logical().Read(fmt.Sprintf("mock-%d/internal", i))
436		if err != nil {
437			t.Fatalf("err: %v", err)
438		}
439		if resp == nil {
440			t.Fatalf("bad: response should not be nil")
441		}
442		if resp.Data["value"].(string) == "baz" {
443			t.Fatal("did not expect backend internal value to be 'baz'")
444		}
445	}
446}
447
448// testSystemBackendMock returns a systemBackend with the desired number
449// of mounted mock plugin backends. numMounts alternates between different
450// ways of providing the plugin_name.
451//
452// The mounts are mounted at sys/mounts/mock-[numMounts] or sys/auth/mock-[numMounts]
453func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType logical.BackendType) *vault.TestCluster {
454	coreConfig := &vault.CoreConfig{
455		LogicalBackends: map[string]logical.Factory{
456			"plugin": plugin.Factory,
457		},
458		CredentialBackends: map[string]logical.Factory{
459			"plugin": plugin.Factory,
460		},
461	}
462
463	// Create a tempdir, cluster.Cleanup will clean up this directory
464	tempDir, err := ioutil.TempDir("", "vault-test-cluster")
465	if err != nil {
466		t.Fatal(err)
467	}
468
469	cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
470		HandlerFunc:        vaulthttp.Handler,
471		KeepStandbysSealed: true,
472		NumCores:           numCores,
473		TempDir:            tempDir,
474	})
475	cluster.Start()
476
477	core := cluster.Cores[0]
478	vault.TestWaitActive(t, core.Core)
479	client := core.Client
480
481	os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
482
483	switch backendType {
484	case logical.TypeLogical:
485		vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "TestBackend_PluginMainLogical", []string{}, tempDir)
486		for i := 0; i < numMounts; i++ {
487			// Alternate input styles for plugin_name on every other mount
488			options := map[string]interface{}{
489				"type": "mock-plugin",
490			}
491			resp, err := client.Logical().Write(fmt.Sprintf("sys/mounts/mock-%d", i), options)
492			if err != nil {
493				t.Fatalf("err: %v", err)
494			}
495			if resp != nil {
496				t.Fatalf("bad: %v", resp)
497			}
498		}
499	case logical.TypeCredential:
500		vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "TestBackend_PluginMainCredentials", []string{}, tempDir)
501		for i := 0; i < numMounts; i++ {
502			// Alternate input styles for plugin_name on every other mount
503			options := map[string]interface{}{
504				"type": "mock-plugin",
505			}
506			resp, err := client.Logical().Write(fmt.Sprintf("sys/auth/mock-%d", i), options)
507			if err != nil {
508				t.Fatalf("err: %v", err)
509			}
510			if resp != nil {
511				t.Fatalf("bad: %v", resp)
512			}
513		}
514	default:
515		t.Fatal("unknown backend type provided")
516	}
517
518	return cluster
519}
520
521func TestSystemBackend_Plugin_Env(t *testing.T) {
522	kvPair := fmt.Sprintf("%s=%s", expectedEnvKey, expectedEnvValue)
523	cluster := testSystemBackend_SingleCluster_Env(t, []string{kvPair})
524	defer cluster.Cleanup()
525}
526
527// testSystemBackend_SingleCluster_Env is a helper func that returns a single
528// cluster and a single mounted plugin logical backend.
529func testSystemBackend_SingleCluster_Env(t *testing.T, env []string) *vault.TestCluster {
530	coreConfig := &vault.CoreConfig{
531		LogicalBackends: map[string]logical.Factory{
532			"test": plugin.Factory,
533		},
534	}
535
536	// Create a tempdir, cluster.Cleanup will clean up this directory
537	tempDir, err := ioutil.TempDir("", "vault-test-cluster")
538	if err != nil {
539		t.Fatal(err)
540	}
541
542	cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
543		HandlerFunc:        vaulthttp.Handler,
544		KeepStandbysSealed: true,
545		NumCores:           1,
546		TempDir:            tempDir,
547	})
548	cluster.Start()
549
550	core := cluster.Cores[0]
551	vault.TestWaitActive(t, core.Core)
552	client := core.Client
553
554	os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
555
556	vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "TestBackend_PluginMainEnv", env, tempDir)
557	options := map[string]interface{}{
558		"type": "mock-plugin",
559	}
560
561	resp, err := client.Logical().Write("sys/mounts/mock", options)
562	if err != nil {
563		t.Fatalf("err: %v", err)
564	}
565	if resp != nil {
566		t.Fatalf("bad: %v", resp)
567	}
568
569	return cluster
570}
571
572func TestBackend_PluginMainLogical(t *testing.T) {
573	args := []string{}
574	if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" && os.Getenv(pluginutil.PluginMetadataModeEnv) != "true" {
575		return
576	}
577
578	caPEM := os.Getenv(pluginutil.PluginCACertPEMEnv)
579	if caPEM == "" {
580		t.Fatal("CA cert not passed in")
581	}
582	args = append(args, fmt.Sprintf("--ca-cert=%s", caPEM))
583
584	apiClientMeta := &api.PluginAPIClientMeta{}
585	flags := apiClientMeta.FlagSet()
586	flags.Parse(args)
587	tlsConfig := apiClientMeta.GetTLSConfig()
588	tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)
589
590	factoryFunc := mock.FactoryType(logical.TypeLogical)
591
592	err := lplugin.Serve(&lplugin.ServeOpts{
593		BackendFactoryFunc: factoryFunc,
594		TLSProviderFunc:    tlsProviderFunc,
595	})
596	if err != nil {
597		t.Fatal(err)
598	}
599}
600
601func TestBackend_PluginMainCredentials(t *testing.T) {
602	args := []string{}
603	if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" && os.Getenv(pluginutil.PluginMetadataModeEnv) != "true" {
604		return
605	}
606
607	caPEM := os.Getenv(pluginutil.PluginCACertPEMEnv)
608	if caPEM == "" {
609		t.Fatal("CA cert not passed in")
610	}
611	args = append(args, fmt.Sprintf("--ca-cert=%s", caPEM))
612
613	apiClientMeta := &api.PluginAPIClientMeta{}
614	flags := apiClientMeta.FlagSet()
615	flags.Parse(args)
616	tlsConfig := apiClientMeta.GetTLSConfig()
617	tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)
618
619	factoryFunc := mock.FactoryType(logical.TypeCredential)
620
621	err := lplugin.Serve(&lplugin.ServeOpts{
622		BackendFactoryFunc: factoryFunc,
623		TLSProviderFunc:    tlsProviderFunc,
624	})
625	if err != nil {
626		t.Fatal(err)
627	}
628}
629
630// TestBackend_PluginMainEnv is a mock plugin that simply checks for the existence of FOO env var.
631func TestBackend_PluginMainEnv(t *testing.T) {
632	args := []string{}
633	if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" && os.Getenv(pluginutil.PluginMetadataModeEnv) != "true" {
634		return
635	}
636
637	// Check on actual vs expected env var
638	actual := os.Getenv(expectedEnvKey)
639	if actual != expectedEnvValue {
640		t.Fatalf("expected: %q, got: %q", expectedEnvValue, actual)
641	}
642
643	caPEM := os.Getenv(pluginutil.PluginCACertPEMEnv)
644	if caPEM == "" {
645		t.Fatal("CA cert not passed in")
646	}
647	args = append(args, fmt.Sprintf("--ca-cert=%s", caPEM))
648
649	apiClientMeta := &api.PluginAPIClientMeta{}
650	flags := apiClientMeta.FlagSet()
651	flags.Parse(args)
652	tlsConfig := apiClientMeta.GetTLSConfig()
653	tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)
654
655	factoryFunc := mock.FactoryType(logical.TypeLogical)
656
657	err := lplugin.Serve(&lplugin.ServeOpts{
658		BackendFactoryFunc: factoryFunc,
659		TLSProviderFunc:    tlsProviderFunc,
660	})
661	if err != nil {
662		t.Fatal(err)
663	}
664}
665
666func TestSystemBackend_InternalUIResultantACL(t *testing.T) {
667	cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
668		HandlerFunc: vaulthttp.Handler,
669	})
670	cluster.Start()
671	defer cluster.Cleanup()
672	client := cluster.Cores[0].Client
673
674	resp, err := client.Auth().Token().Create(&api.TokenCreateRequest{
675		Policies: []string{"default"},
676	})
677	if err != nil {
678		t.Fatal(err)
679	}
680	if resp == nil {
681		t.Fatal("nil response")
682	}
683	if resp.Auth == nil {
684		t.Fatal("nil auth")
685	}
686	if resp.Auth.ClientToken == "" {
687		t.Fatal("empty client token")
688	}
689
690	client.SetToken(resp.Auth.ClientToken)
691
692	resp, err = client.Logical().Read("sys/internal/ui/resultant-acl")
693	if err != nil {
694		t.Fatal(err)
695	}
696	if resp == nil {
697		t.Fatal("nil response")
698	}
699	if resp.Data == nil {
700		t.Fatal("nil data")
701	}
702
703	exp := map[string]interface{}{
704		"exact_paths": map[string]interface{}{
705			"auth/token/lookup-self": map[string]interface{}{
706				"capabilities": []interface{}{
707					"read",
708				},
709			},
710			"auth/token/renew-self": map[string]interface{}{
711				"capabilities": []interface{}{
712					"update",
713				},
714			},
715			"auth/token/revoke-self": map[string]interface{}{
716				"capabilities": []interface{}{
717					"update",
718				},
719			},
720			"sys/capabilities-self": map[string]interface{}{
721				"capabilities": []interface{}{
722					"update",
723				},
724			},
725			"sys/control-group/request": map[string]interface{}{
726				"capabilities": []interface{}{
727					"update",
728				},
729			},
730			"sys/internal/ui/resultant-acl": map[string]interface{}{
731				"capabilities": []interface{}{
732					"read",
733				},
734			},
735			"sys/leases/lookup": map[string]interface{}{
736				"capabilities": []interface{}{
737					"update",
738				},
739			},
740			"sys/leases/renew": map[string]interface{}{
741				"capabilities": []interface{}{
742					"update",
743				},
744			},
745			"sys/renew": map[string]interface{}{
746				"capabilities": []interface{}{
747					"update",
748				},
749			},
750			"sys/tools/hash": map[string]interface{}{
751				"capabilities": []interface{}{
752					"update",
753				},
754			},
755			"sys/wrapping/lookup": map[string]interface{}{
756				"capabilities": []interface{}{
757					"update",
758				},
759			},
760			"sys/wrapping/unwrap": map[string]interface{}{
761				"capabilities": []interface{}{
762					"update",
763				},
764			},
765			"sys/wrapping/wrap": map[string]interface{}{
766				"capabilities": []interface{}{
767					"update",
768				},
769			},
770		},
771		"glob_paths": map[string]interface{}{
772			"cubbyhole/": map[string]interface{}{
773				"capabilities": []interface{}{
774					"create",
775					"delete",
776					"list",
777					"read",
778					"update",
779				},
780			},
781			"sys/tools/hash/": map[string]interface{}{
782				"capabilities": []interface{}{
783					"update",
784				},
785			},
786		},
787		"root": false,
788	}
789
790	if diff := deep.Equal(resp.Data, exp); diff != nil {
791		t.Fatal(diff)
792	}
793}
794