blob: e643c12f3951089164762e02646c397f97d5aae6 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package perms_test
import (
"io/ioutil"
"syscall"
"testing"
"v.io/v23/context"
"v.io/v23/naming"
"v.io/v23/security"
"v.io/v23/security/access"
"v.io/v23/services/permissions"
"v.io/v23/verror"
"v.io/x/ref/services/device/internal/impl/utiltest"
"v.io/x/ref/services/internal/servicetest"
"v.io/x/ref/test/testutil"
)
func updateAccessList(t *testing.T, ctx *context.T, blessing, right string, name ...string) {
accessStub := permissions.ObjectClient(naming.Join(name...))
perms, version, err := accessStub.GetPermissions(ctx)
if err != nil {
t.Fatalf(testutil.FormatLogLine(2, "GetPermissions(%v) failed %v", name, err))
}
perms.Add(security.BlessingPattern(blessing), right)
if err = accessStub.SetPermissions(ctx, perms, version); err != nil {
t.Fatalf(testutil.FormatLogLine(2, "SetPermissions(%v, %v, %v) failed: %v", name, blessing, right, err))
}
}
func testAccessFail(t *testing.T, expected verror.ID, ctx *context.T, who string, name ...string) {
if _, err := utiltest.StatsStub(name...).Value(ctx); verror.ErrorID(err) != expected {
t.Fatalf(testutil.FormatLogLine(2, "%s got error %v but expected %v", who, err, expected))
}
}
func TestDebugPermissionsPropagation(t *testing.T) {
cleanup, ctx, sh, envelope, root, helperPath, idp := utiltest.StartupHelper(t)
defer cleanup()
// Set up the device manager.
dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
servicetest.ReadPID(t, dmh)
utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
// Create the local server that the app uses to let us know it's ready.
pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
defer cleanup()
utiltest.Resolve(t, ctx, "pingserver", 1, true)
// Make some users.
selfCtx := ctx
bobCtx := utiltest.CtxWithNewPrincipal(t, selfCtx, idp, "bob")
hjCtx := utiltest.CtxWithNewPrincipal(t, selfCtx, idp, "hackerjoe")
aliceCtx := utiltest.CtxWithNewPrincipal(t, selfCtx, idp, "alice")
// TODO(rjkroege): Set AccessLists 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 = utiltest.EnvelopeFromShell(sh, nil, utiltest.App, "google naps", 0, 0, "appV1")
// Install the app.
appID := utiltest.InstallApp(t, ctx)
// Give bob rights to start an app.
updateAccessList(t, selfCtx, "root/bob/$", string(access.Read), "dm/apps", appID)
// Bob starts an instance of the app.
bobApp := utiltest.LaunchApp(t, bobCtx, appID)
pingCh.VerifyPingArgs(t, utiltest.UserName(t), "default", "")
// Bob permits Alice to read from his app.
updateAccessList(t, bobCtx, "root/alice/$", string(access.Read), "dm/apps", appID, bobApp)
// Create some globbing test vectors.
globtests := []utiltest.GlobTestVector{
{naming.Join("dm", "apps", appID, bobApp), "*",
[]string{"logs", "pprof", "stats"},
},
{naming.Join("dm", "apps", appID, bobApp, "stats", "system"),
"start-time*",
[]string{"start-time-rfc1123", "start-time-unix"},
},
{naming.Join("dm", "apps", appID, bobApp, "logs"),
"*",
[]string{
"STDERR-<timestamp>",
"STDOUT-<timestamp>",
"app.INFO",
"app.<*>.INFO.<timestamp>",
},
},
}
appGlobtests := []utiltest.GlobTestVector{
{naming.Join("appV1", "__debug"), "*",
[]string{"logs", "pprof", "stats", "vtrace"},
},
{naming.Join("appV1", "__debug", "stats", "system"),
"start-time*",
[]string{"start-time-rfc1123", "start-time-unix"},
},
{naming.Join("appV1", "__debug", "logs"),
"*",
[]string{
"STDERR-<timestamp>",
"STDOUT-<timestamp>",
"app.INFO",
"app.<*>.INFO.<timestamp>",
},
},
}
globtestminus := globtests[1:]
res := utiltest.NewGlobTestRegexHelper("app")
// Confirm that self can access __debug names.
utiltest.VerifyGlob(t, selfCtx, "app", globtests, res)
utiltest.VerifyStatsValues(t, selfCtx, "dm", "apps", appID, bobApp, "stats/system/start-time*")
utiltest.VerifyLog(t, selfCtx, "dm", "apps", appID, bobApp, "logs", "*")
utiltest.VerifyPProfCmdLine(t, selfCtx, "app", "dm", "apps", appID, bobApp, "pprof")
// Bob started the app so selfCtx can't connect to the app.
utiltest.VerifyFailGlob(t, selfCtx, appGlobtests)
testAccessFail(t, verror.ErrNoAccess.ID, selfCtx, "self", "appV1", "__debug", "stats/system/pid")
// hackerjoe (for example) can't either.
utiltest.VerifyFailGlob(t, hjCtx, appGlobtests)
testAccessFail(t, verror.ErrNoAccess.ID, hjCtx, "hackerjoe", "appV1", "__debug", "stats/system/pid")
// Bob has an issue with his app and tries to use the debug output to figure it out.
utiltest.VerifyGlob(t, bobCtx, "app", globtests, res)
utiltest.VerifyStatsValues(t, bobCtx, "dm", "apps", appID, bobApp, "stats/system/start-time*")
utiltest.VerifyLog(t, bobCtx, "dm", "apps", appID, bobApp, "logs", "*")
utiltest.VerifyPProfCmdLine(t, bobCtx, "app", "dm", "apps", appID, bobApp, "pprof")
// Bob can also connect directly to his app.
utiltest.VerifyGlob(t, bobCtx, "app", appGlobtests, res)
utiltest.VerifyStatsValues(t, bobCtx, "appV1", "__debug", "stats/system/start-time*")
// But Bob can't figure it out and hopes that hackerjoe can debug it.
updateAccessList(t, bobCtx, "root/hackerjoe/$", string(access.Debug), "dm/apps", appID, bobApp)
// Fortunately the device manager permits hackerjoe to access the stats.
// But hackerjoe can't solve Bob's problem.
// Because hackerjoe has Debug, hackerjoe can glob the __debug resources
// of Bob's app but can't glob Bob's app.
utiltest.VerifyGlob(t, hjCtx, "app", globtestminus, res)
utiltest.VerifyFailGlob(t, hjCtx, globtests[0:1])
utiltest.VerifyStatsValues(t, hjCtx, "dm", "apps", appID, bobApp, "stats", "system/start-time*")
utiltest.VerifyLog(t, hjCtx, "dm", "apps", appID, bobApp, "logs", "*")
utiltest.VerifyPProfCmdLine(t, hjCtx, "app", "dm", "apps", appID, bobApp, "pprof")
// Permissions are propagated to the app so hackerjoe can connect
// directly to the app too.
utiltest.VerifyGlob(t, hjCtx, "app", globtestminus, res)
utiltest.VerifyStatsValues(t, hjCtx, "appV1", "__debug", "stats/system/start-time*")
// Alice might be able to help but Bob didn't give Alice access to the debug Permissionss.
testAccessFail(t, verror.ErrNoAccess.ID, aliceCtx, "Alice", "dm", "apps", appID, bobApp, "stats/system/pid")
// Bob forgets that Alice can't read the stats when he can.
utiltest.VerifyGlob(t, bobCtx, "app", globtests, res)
utiltest.VerifyStatsValues(t, bobCtx, "dm", "apps", appID, bobApp, "stats/system/start-time*")
// So Bob changes the permissions so that Alice can help debug too.
updateAccessList(t, bobCtx, "root/alice/$", string(access.Debug), "dm/apps", appID, bobApp)
// Alice can access __debug content.
utiltest.VerifyGlob(t, aliceCtx, "app", globtestminus, res)
utiltest.VerifyFailGlob(t, aliceCtx, globtests[0:1])
utiltest.VerifyStatsValues(t, aliceCtx, "dm", "apps", appID, bobApp, "stats", "system/start-time*")
utiltest.VerifyLog(t, aliceCtx, "dm", "apps", appID, bobApp, "logs", "*")
utiltest.VerifyPProfCmdLine(t, aliceCtx, "app", "dm", "apps", appID, bobApp, "pprof")
// Alice can also now connect directly to the app.
utiltest.VerifyGlob(t, aliceCtx, "app", globtestminus, res)
utiltest.VerifyStatsValues(t, aliceCtx, "appV1", "__debug", "stats/system/start-time*")
// Bob is glum because no one can help him fix his app so he terminates
// it.
utiltest.TerminateApp(t, bobCtx, appID, bobApp)
// Cleanly shut down the device manager.
syscall.Kill(dmh.Pid(), syscall.SIGINT)
dmh.Expect("dm terminated")
dmh.ExpectEOF()
}
func TestClaimSetsDebugPermissions(t *testing.T) {
cleanup, ctx, sh, _, root, helperPath, idp := utiltest.StartupHelper(t)
defer cleanup()
extraLogDir, err := ioutil.TempDir(root, "testlogs")
if err != nil {
t.Fatalf("ioutil.TempDir failed: %v", err)
}
// Set up the device manager.
dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "--log_dir="+extraLogDir, "dm", root, helperPath, "unused", "unused_curr_link")
servicetest.ReadPID(t, dmh)
// Make some users.
selfCtx := ctx
bobCtx := utiltest.CtxWithNewPrincipal(t, selfCtx, idp, "bob")
aliceCtx := utiltest.CtxWithNewPrincipal(t, selfCtx, idp, "alice")
hjCtx := utiltest.CtxWithNewPrincipal(t, selfCtx, idp, "hackerjoe")
// Bob claims the device manager.
utiltest.ClaimDevice(t, bobCtx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
// Create some globbing test vectors.
dmGlobtests := []utiltest.GlobTestVector{
{naming.Join("dm", "__debug"), "*",
[]string{"logs", "pprof", "stats", "vtrace"},
},
{naming.Join("dm", "__debug", "stats", "system"),
"start-time*",
[]string{"start-time-rfc1123", "start-time-unix"},
},
{naming.Join("dm", "__debug", "logs"),
"*",
[]string{
// STDERR and STDOUT are not handled through the log package so
// are not included here.
"perms.test.INFO",
"perms.test.<*>.INFO.<timestamp>",
},
},
}
res := utiltest.NewGlobTestRegexHelper(`perms\.test`)
// Bob claimed the DM so can access it.
utiltest.VerifyGlob(t, bobCtx, "perms.test", dmGlobtests, res)
utiltest.VerifyStatsValues(t, bobCtx, "dm", "__debug", "stats/system/start-time*")
// Without permissions, hackerjoe can't access the device manager.
utiltest.VerifyFailGlob(t, hjCtx, dmGlobtests)
testAccessFail(t, verror.ErrNoAccess.ID, hjCtx, "hackerjoe", "dm", "__debug", "stats/system/pid")
// Bob gives system administrator Alice admin access to the dm and hence Alice
// can access the __debug space.
updateAccessList(t, bobCtx, "root/alice/$", string(access.Admin), "dm", "device")
// Alice is an adminstrator and so can can access device manager __debug
// values.
utiltest.VerifyGlob(t, aliceCtx, "perms.test", dmGlobtests, res)
utiltest.VerifyStatsValues(t, aliceCtx, "dm", "__debug", "stats/system/start-time*")
// Bob gives debug access to the device manager to hackerjoe
updateAccessList(t, bobCtx, "root/hackerjoe/$", string(access.Debug), "dm", "device")
// hackerjoe can now access the device manager
utiltest.VerifyGlob(t, hjCtx, "perms.test", dmGlobtests, res)
utiltest.VerifyStatsValues(t, hjCtx, "dm", "__debug", "stats/system/start-time*")
// Cleanly shut down the device manager.
syscall.Kill(dmh.Pid(), syscall.SIGINT)
dmh.Expect("dm terminated")
dmh.ExpectEOF()
}