services/mgmt/device/impl/* Test to show lack of ACL propagation.

This test shows the necessity of ACL propagation for names under an
application's __debug point: the instantiator of an app instance
cannot control access to the app's __debug information.

Change-Id: I1a3a2603bcf2a3c7e818232287bbc35906c94db1
diff --git a/services/mgmt/device/impl/debug_acls_test.go b/services/mgmt/device/impl/debug_acls_test.go
new file mode 100644
index 0000000..30eb9a6
--- /dev/null
+++ b/services/mgmt/device/impl/debug_acls_test.go
@@ -0,0 +1,112 @@
+package impl_test
+
+import (
+	"syscall"
+	"testing"
+
+	"v.io/v23/context"
+	"v.io/v23/security"
+	"v.io/v23/services/security/access"
+	"v.io/v23/vdl"
+
+	mgmttest "v.io/x/ref/services/mgmt/lib/testutil"
+)
+
+func updateACL(t *testing.T, ctx *context.T, blessing, right string, name ...string) {
+	acl, etag, err := appStub(name...).GetACL(ctx)
+	if err != nil {
+		t.Fatalf("GetACL(%v) failed %v", name, err)
+	}
+	acl.Add(security.BlessingPattern(blessing), right)
+	if err = appStub(name...).SetACL(ctx, acl, etag); err != nil {
+		t.Fatalf("SetACL(%v, %v, %v) failed: %v", name, blessing, right, err)
+	}
+}
+
+func TestDebugACLPropagation(t *testing.T) {
+	cleanup, ctx, sh, envelope, root, helperPath, idp := startupHelper(t)
+	defer cleanup()
+
+	// Set up the device manager.
+	dmh, dms := mgmttest.RunShellCommand(t, sh, nil, deviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	mgmttest.ReadPID(t, dms)
+	claimDevice(t, ctx, "dm", "mydevice", noPairingToken)
+
+	// Create the local server that the app uses to let us know it's ready.
+	pingCh, cleanup := setupPingServer(t, ctx)
+	defer cleanup()
+	resolve(t, ctx, "pingserver", 1)
+
+	// Make some users.
+	selfCtx := ctx
+	bobCtx := ctxWithNewPrincipal(t, selfCtx, idp, "bob")
+	hjCtx := ctxWithNewPrincipal(t, selfCtx, idp, "hackerjoe")
+	aliceCtx := ctxWithNewPrincipal(t, selfCtx, idp, "alice")
+
+	// TODO(rjkroege): Set ACLs here that conflict with the one provided by the device
+	// manager and show that the one set here is overridden.
+	// Create the envelope for the first version of the app.
+	*envelope = envelopeFromShell(sh, nil, appCmd, "google naps", "appV1")
+
+	// Install the app.
+	appID := installApp(t, ctx)
+
+	// Give bob rights to start an app.
+	updateACL(t, selfCtx, "root/bob/$", string(access.Read), appID)
+
+	// Bob starts an instance of the app.
+	bobApp := startApp(t, bobCtx, appID)
+	verifyPingArgs(t, pingCh, userName(t), "default", "")
+
+	// Bob permits Alice to read from his app.
+	updateACL(t, bobCtx, "root/alice/$", string(access.Read), appID, bobApp)
+
+	// Confirm that self can access stats name (i.e. apps proxied from
+	// the __debug space of the app.
+	// TODO(rjkroege): validate each of the services under __debug.
+	v, err := statsStub(appID, bobApp, "stats/system/pid").Value(ctx)
+	if err != nil {
+		t.Fatalf("Value() failed: %v\n", err)
+	}
+	var pid int
+	if err := vdl.Convert(&pid, v); err != nil {
+		t.Fatalf("pid returned from stats interface is not an int: %v", err)
+	}
+
+	// Bob has an issue with his app and tries to use the debug output to figure it out.
+	// TODO(rjkroege): Invert this test when ACLs are correctly propagated.
+	if _, err = statsStub(appID, bobApp, "stats/system/pid").Value(bobCtx); err == nil {
+		t.Fatalf("This ought not to work yet! Something is wrong.")
+	}
+
+	// But Bob can't figure it out and hopes that hackerjoe can debug it.
+	updateACL(t, bobCtx, "root/hackerjoe/$", string(access.Debug), appID, bobApp)
+
+	// But the device manager doesn't support this so hackerjoe can't help.
+	// TODO(rjkroege): Invert this test when ACLs are propagated..
+	if _, err = statsStub(appID, bobApp, "stats/system/pid").Value(hjCtx); err == nil {
+		t.Fatalf("This ought not to work yet! Something is wrong.")
+	}
+
+	// Alice might be able to help but Bob didn't give Alice access to the debug ACLs.
+	if _, err = statsStub(appID, bobApp, "stats/system/pid").Value(hjCtx); err == nil {
+		t.Fatalf("Alice could wrongly access the stats without perms.")
+	}
+
+	// Bob changes the permissions so that Alice can help debug too.
+	updateACL(t, bobCtx, "root/alice/$", string(access.Debug), appID, bobApp)
+
+	// But the device manager doesn't support this so Alice can't help either.
+	// TODO(rjkroege): Invert this test when ACLs are propagated..
+	if _, err = statsStub(appID, bobApp, "stats/system/pid").Value(aliceCtx); err == nil {
+		t.Fatalf("This ought not to work yet! Something is wrong.")
+	}
+
+	// Bob is glum so stops his app.
+	stopApp(t, bobCtx, appID, bobApp)
+
+	// Cleanly shut down the device manager.
+	syscall.Kill(dmh.Pid(), syscall.SIGINT)
+	dms.Expect("dm terminated")
+	dms.ExpectEOF()
+}
diff --git a/services/mgmt/device/impl/instance_reaping_test.go b/services/mgmt/device/impl/instance_reaping_test.go
index 0f52a0b..8ccbedf 100644
--- a/services/mgmt/device/impl/instance_reaping_test.go
+++ b/services/mgmt/device/impl/instance_reaping_test.go
@@ -7,49 +7,17 @@
 	"syscall"
 	"testing"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
-	"v.io/v23/services/mgmt/application"
 	"v.io/v23/services/mgmt/stats"
 	"v.io/v23/vdl"
 
 	"v.io/x/ref/lib/flags/consts"
-	"v.io/x/ref/lib/modules"
-	"v.io/x/ref/lib/testutil"
-	"v.io/x/ref/services/mgmt/device/impl"
 	mgmttest "v.io/x/ref/services/mgmt/lib/testutil"
 )
 
-// TODO(rjkroege): This helper is generally useful. Move to util_test.go
-// and use it to reduce boiler plate across all tests here.
-func startupHelper(t *testing.T) (func(), *context.T, *modules.Shell, *application.Envelope, string, string) {
-	ctx, shutdown := testutil.InitForTest()
-	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
-
-	sh, deferFn := mgmttest.CreateShellAndMountTable(t, ctx, nil)
-
-	// Set up mock application and binary repositories.
-	envelope, envCleanup := startMockRepos(t, ctx)
-
-	root, rootCleanup := mgmttest.SetupRootDir(t, "devicemanager")
-	if err := impl.SaveCreatorInfo(root); err != nil {
-		t.Fatal(err)
-	}
-
-	// Create a script wrapping the test target that implements suidhelper.
-	helperPath := generateSuidHelperScript(t, root)
-
-	return func() {
-		rootCleanup()
-		envCleanup()
-		deferFn()
-		shutdown()
-	}, ctx, sh, envelope, root, helperPath
-}
-
 func TestReaperNoticesAppDeath(t *testing.T) {
-	cleanup, ctx, sh, envelope, root, helperPath := startupHelper(t)
+	cleanup, ctx, sh, envelope, root, helperPath, _ := startupHelper(t)
 	defer cleanup()
 
 	// Set up the device manager.  Since we won't do device manager updates,
@@ -120,7 +88,7 @@
 }
 
 func TestReapReconciliation(t *testing.T) {
-	cleanup, ctx, sh, envelope, root, helperPath := startupHelper(t)
+	cleanup, ctx, sh, envelope, root, helperPath, _ := startupHelper(t)
 	defer cleanup()
 
 	// Start a device manager.
diff --git a/services/mgmt/device/impl/util_test.go b/services/mgmt/device/impl/util_test.go
index 6e8fe15..9d4b7eb 100644
--- a/services/mgmt/device/impl/util_test.go
+++ b/services/mgmt/device/impl/util_test.go
@@ -18,6 +18,7 @@
 	"v.io/v23/security"
 	"v.io/v23/services/mgmt/application"
 	"v.io/v23/services/mgmt/device"
+	"v.io/v23/services/mgmt/stats"
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
 	tsecurity "v.io/x/ref/lib/testutil/security"
@@ -26,6 +27,7 @@
 	"v.io/x/ref/lib/testutil"
 	_ "v.io/x/ref/profiles/roaming"
 	"v.io/x/ref/services/mgmt/device/impl"
+	mgmttest "v.io/x/ref/services/mgmt/lib/testutil"
 )
 
 const (
@@ -181,6 +183,12 @@
 	return device.ApplicationClient(appName)
 }
 
+func statsStub(nameComponents ...string) stats.StatsClientMethods {
+	baseName := "dm/apps"
+	statsName := naming.Join(append([]string{baseName}, nameComponents...)...)
+	return stats.StatsClient(statsName)
+}
+
 func installApp(t *testing.T, ctx *context.T, opt ...interface{}) string {
 	appID, err := appStub().Install(ctx, mockApplicationRepoName, ocfg(opt), opkg(opt))
 	if err != nil {
@@ -383,3 +391,34 @@
 	}
 	return ret
 }
+
+// TODO(rjkroege): This helper is generally useful. Use it to reduce
+// boiler plate across all device manager tests.
+func startupHelper(t *testing.T) (func(), *context.T, *modules.Shell, *application.Envelope, string, string, *tsecurity.IDProvider) {
+	ctx, shutdown := testutil.InitForTest()
+	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
+
+	// Make a new identity context.
+	idp := tsecurity.NewIDProvider("root")
+	ctx = ctxWithNewPrincipal(t, ctx, idp, "self")
+
+	sh, deferFn := mgmttest.CreateShellAndMountTable(t, ctx, nil)
+
+	// Set up mock application and binary repositories.
+	envelope, envCleanup := startMockRepos(t, ctx)
+
+	root, rootCleanup := mgmttest.SetupRootDir(t, "devicemanager")
+	if err := impl.SaveCreatorInfo(root); err != nil {
+		t.Fatal(err)
+	}
+
+	// Create a script wrapping the test target that implements suidhelper.
+	helperPath := generateSuidHelperScript(t, root)
+
+	return func() {
+		rootCleanup()
+		envCleanup()
+		deferFn()
+		shutdown()
+	}, ctx, sh, envelope, root, helperPath, idp
+}