blob: 6b19ba25117b30ac4319b23e3f9f3bb6e012c7e5 [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 main_test
import (
"bytes"
"reflect"
"regexp"
"strings"
"testing"
"v.io/v23"
"v.io/v23/security"
"v.io/v23/security/access"
"v.io/v23/verror"
"v.io/x/lib/cmdline"
"v.io/x/ref/lib/v23cmd"
"v.io/x/ref/test"
cmd_device "v.io/x/ref/services/device/device"
"v.io/x/ref/services/internal/servicetest"
)
const pkgPath = "v.io/x/ref/services/device/main"
var (
errOops = verror.Register(pkgPath+".errOops", verror.NoRetry, "oops!")
)
func TestAccessListGetCommand(t *testing.T) {
ctx, shutdown := test.V23Init()
defer shutdown()
tapes := servicetest.NewTapeMap()
ctx, server, err := v23.WithNewDispatchingServer(ctx, "", newDispatcher(t, tapes))
if err != nil {
t.Fatalf("NewServer failed: %v", err)
}
// Setup the command-line.
cmd := cmd_device.CmdRoot
var stdout, stderr bytes.Buffer
env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
deviceName := server.Status().Endpoints[0].Name()
// Test the 'get' command.
rootTape := tapes.ForSuffix("")
rootTape.SetResponses(GetPermissionsResponse{
perms: access.Permissions{
"Admin": access.AccessList{
In: []security.BlessingPattern{"self"},
NotIn: []string{"self/bad"},
},
"Read": access.AccessList{
In: []security.BlessingPattern{"other", "self"},
},
},
version: "aVersionForToday",
err: nil,
})
if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"acl", "get", deviceName}); err != nil {
t.Fatalf("error: %v", err)
}
if expected, got := strings.TrimSpace(`
other Read
self Admin,Read
self/bad !Admin
`), strings.TrimSpace(stdout.String()); got != expected {
t.Errorf("Unexpected output from get. Got %q, expected %q", got, expected)
}
if got, expected := rootTape.Play(), []interface{}{"GetPermissions"}; !reflect.DeepEqual(expected, got) {
t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
}
func TestAccessListSetCommand(t *testing.T) {
ctx, shutdown := test.V23Init()
defer shutdown()
tapes := servicetest.NewTapeMap()
ctx, server, err := v23.WithNewDispatchingServer(ctx, "", newDispatcher(t, tapes))
if err != nil {
t.Fatalf("NewServer failed: %v", err)
}
// Setup the command-line.
cmd := cmd_device.CmdRoot
var stdout, stderr bytes.Buffer
env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
deviceName := server.Status().Endpoints[0].Name()
// Some tests to validate parse.
if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"acl", "set", deviceName}); err == nil {
t.Fatalf("failed to correctly detect insufficient parameters")
}
if expected, got := "ERROR: set: incorrect number of arguments 1, must be 1 + 2n", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
t.Fatalf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
}
stderr.Reset()
stdout.Reset()
if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"acl", "set", deviceName, "foo"}); err == nil {
t.Fatalf("failed to correctly detect insufficient parameters")
}
if expected, got := "ERROR: set: incorrect number of arguments 2, must be 1 + 2n", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
t.Fatalf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
}
stderr.Reset()
stdout.Reset()
if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"acl", "set", deviceName, "foo", "bar", "ohno"}); err == nil {
t.Fatalf("failed to correctly detect insufficient parameters")
}
if expected, got := "ERROR: set: incorrect number of arguments 4, must be 1 + 2n", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
t.Fatalf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
}
stderr.Reset()
stdout.Reset()
if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"acl", "set", deviceName, "foo", "!"}); err == nil {
t.Fatalf("failed to detect invalid parameter")
}
if expected, got := "ERROR: failed to parse access tags for \"foo\": empty access tag", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
t.Errorf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
}
// Correct operation in the absence of errors.
stderr.Reset()
stdout.Reset()
rootTape := tapes.ForSuffix("")
rootTape.SetResponses(
GetPermissionsResponse{
perms: access.Permissions{
"Admin": access.AccessList{
In: []security.BlessingPattern{"self"},
},
"Read": access.AccessList{
In: []security.BlessingPattern{"other", "self"},
NotIn: []string{"other/bob"},
},
},
version: "aVersionForToday",
err: nil,
},
verror.NewErrBadVersion(nil),
GetPermissionsResponse{
perms: access.Permissions{
"Admin": access.AccessList{
In: []security.BlessingPattern{"self"},
},
"Read": access.AccessList{
In: []security.BlessingPattern{"other", "self"},
NotIn: []string{"other/bob/baddevice"},
},
},
version: "aVersionForTomorrow",
err: nil,
},
nil,
)
// set command that:
// - Adds entry for "friends" to "Write" & "Admin"
// - Adds a blacklist entry for "friend/alice" for "Admin"
// - Edits existing entry for "self" (adding "Write" access)
// - Removes entry for "other/bob/baddevice"
if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{
"acl",
"set",
deviceName,
"friends", "Admin,Write",
"friends/alice", "!Admin,Write",
"self", "Admin,Write,Read",
"other/bob/baddevice", "^",
}); err != nil {
t.Fatalf("SetPermissions failed: %v", err)
}
if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
}
if expected, got := "WARNING: trying again because of asynchronous change", strings.TrimSpace(stderr.String()); got != expected {
t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
}
expected := []interface{}{
"GetPermissions",
SetPermissionsStimulus{
fun: "SetPermissions",
perms: access.Permissions{
"Admin": access.AccessList{
In: []security.BlessingPattern{"friends", "self"},
NotIn: []string{"friends/alice"},
},
"Read": access.AccessList{
In: []security.BlessingPattern{"other", "self"},
NotIn: []string{"other/bob"},
},
"Write": access.AccessList{
In: []security.BlessingPattern{"friends", "friends/alice", "self"},
NotIn: []string(nil),
},
},
version: "aVersionForToday",
},
"GetPermissions",
SetPermissionsStimulus{
fun: "SetPermissions",
perms: access.Permissions{
"Admin": access.AccessList{
In: []security.BlessingPattern{"friends", "self"},
NotIn: []string{"friends/alice"},
},
"Read": access.AccessList{
In: []security.BlessingPattern{"other", "self"},
NotIn: []string(nil),
},
"Write": access.AccessList{
In: []security.BlessingPattern{"friends", "friends/alice", "self"},
NotIn: []string(nil),
},
},
version: "aVersionForTomorrow",
},
}
if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
rootTape.Rewind()
stdout.Reset()
stderr.Reset()
// GetPermissions fails.
rootTape.SetResponses(GetPermissionsResponse{
perms: access.Permissions{},
version: "aVersionForToday",
err: verror.New(errOops, nil),
})
if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"acl", "set", deviceName, "vana/bad", "Read"}); err == nil {
t.Fatalf("GetPermissions RPC inside perms set command failed but error wrongly not detected")
} else if expected, got := `^GetPermissions\(`+deviceName+`\) failed:.*oops!`, err.Error(); !regexp.MustCompile(expected).MatchString(got) {
t.Fatalf("Unexpected output from list. Got %q, regexp %q", got, expected)
}
if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
}
expected = []interface{}{
"GetPermissions",
}
if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
rootTape.Rewind()
stdout.Reset()
stderr.Reset()
// SetPermissions fails with something other than a bad version failure.
rootTape.SetResponses(
GetPermissionsResponse{
perms: access.Permissions{
"Read": access.AccessList{
In: []security.BlessingPattern{"other", "self"},
},
},
version: "aVersionForToday",
err: nil,
},
verror.New(errOops, nil),
)
if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"acl", "set", deviceName, "friend", "Read"}); err == nil {
t.Fatalf("SetPermissions should have failed: %v", err)
} else if expected, got := `^SetPermissions\(`+deviceName+`\) failed:.*oops!`, err.Error(); !regexp.MustCompile(expected).MatchString(got) {
t.Fatalf("Unexpected output from list. Got %q, regexp %q", got, expected)
}
if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
}
expected = []interface{}{
"GetPermissions",
SetPermissionsStimulus{
fun: "SetPermissions",
perms: access.Permissions{
"Read": access.AccessList{
In: []security.BlessingPattern{"friend", "other", "self"},
NotIn: []string(nil),
},
},
version: "aVersionForToday",
},
}
if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
}