blob: ae2ed6405bc6257ffd21a40fded9522b901fdfdb [file] [log] [blame]
// Copyright 2016 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 wakeuplib
import (
crand "crypto/rand"
"fmt"
"math/rand"
"reflect"
"testing"
"time"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/naming"
"v.io/v23/security"
"v.io/v23/security/access"
"v.io/v23/services/wakeup"
_ "v.io/x/ref/runtime/factories/generic"
"v.io/x/ref/test"
"v.io/x/ref/test/testutil"
)
func newWakeup(t *testing.T, ctx *context.T, wakeup func(*context.T, string, string) error) func() {
// Mint new shared key.
var sharedKey [32]byte
if _, err := crand.Read(sharedKey[:]); err != nil {
t.Fatalf("Failed to create wakeup server: %v", err)
}
stop, err := StartServers(ctx, "wakeup", "", sharedKey, wakeup)
if err != nil {
t.Fatalf("Failed to start wakeup server: %v", err)
}
return stop
}
func newEndpoint(t *testing.T, addr string) string {
ep := naming.Endpoint{Address: addr}
return ep.String()
}
func register(t *testing.T, ctx *context.T, token string) string {
name, err := wakeup.WakeUpClient("wakeup/server").Register(ctx, token)
if err != nil {
t.Fatalf("Register(%s) failed: %v", token, err)
}
return name
}
func mount(t *testing.T, ctx *context.T, name, ep string, shouldSucceed bool) {
if err := v23.GetNamespace(ctx).Mount(ctx, name, ep, 1*time.Minute); err != nil {
if !shouldSucceed {
return
}
t.Fatalf("Mount(%s) failed: %v", name, err)
}
}
func resolve(t *testing.T, ctx *context.T, name string, shouldSucceed bool) string {
entry, err := v23.GetNamespace(ctx).Resolve(ctx, name)
if err != nil {
if !shouldSucceed {
return ""
}
t.Fatalf("Resolve(%s) failed: %v", name, err)
}
if len(entry.Servers) != 1 {
t.Fatalf("Expected a single mounted server, got: %v", entry.Servers)
}
return entry.Servers[0].Server
}
func setPermissions(t *testing.T, ctx *context.T, name string, perms access.Permissions, shouldSucceed bool) {
if err := v23.GetNamespace(ctx).SetPermissions(ctx, name, perms, ""); err != nil {
if shouldSucceed {
t.Fatalf("SetPermissions(%s) failed: %v", name, err)
}
}
if !shouldSucceed {
t.Fatalf("SetPermissions(%s) should have failed", name)
}
}
func getPermissions(t *testing.T, ctx *context.T, name string, want access.Permissions) {
got, _, err := v23.GetNamespace(ctx).GetPermissions(ctx, name)
if err != nil {
t.Fatalf("GetPermissions(%s) failed: %v", name, err)
}
if !reflect.DeepEqual(want, got) {
t.Fatalf("Want permissions %v, got %v", want, got)
}
}
func checkWokenUp(t *testing.T, want string, wakeupCh <-chan string, shouldSucceed bool) {
select {
case got := <-wakeupCh:
if !shouldSucceed {
t.Fatalf("Server wrongfully woken up")
} else if got != want {
t.Fatalf("woken up with invalid token, got %v want %v", got, want)
} else {
select {
case val := <-wakeupCh:
t.Fatalf("Wakeup channel should be empty, found value %v", val)
default:
// OK
}
}
default:
if shouldSucceed {
t.Fatalf("never woken up")
}
}
}
func withNewPrincipal(t *testing.T, ctx *context.T) *context.T {
p := testutil.NewPrincipal()
d, _ := v23.GetPrincipal(ctx).BlessingStore().Default()
b, err := v23.GetPrincipal(ctx).Bless(p.PublicKey(), d, fmt.Sprintf("derived%d", rand.Int()), security.UnconstrainedUse())
if err != nil {
t.Fatalf("Error creating principal: %v", err)
}
if err := security.AddToRoots(p, b); err != nil {
t.Fatalf("Couldn't add %v to principal %v's roots: %v", b, p, err)
}
if err := p.BlessingStore().SetDefault(b); err != nil {
t.Fatalf("Couldn't set default blessing %v for principal %v: %v", b, p, err)
}
if _, err := p.BlessingStore().Set(b, security.AllPrincipals); err != nil {
t.Fatalf("Couldn't set blessing %v for all peers of principal %v: %v", b, p, err)
}
ret, err := v23.WithPrincipal(ctx, p)
if err != nil {
t.Fatalf("Couldn't create new context for principal %v: %v", p, err)
}
return ret
}
func TestWakeup(t *testing.T) {
ctx, shutdown := test.V23InitWithMounttable()
defer shutdown()
wakeupCh := make(chan string, 100)
stop := newWakeup(t, ctx, func(ctx *context.T, token, _ string) error {
wakeupCh <- token
return nil
})
defer stop()
// One.
aRoot := register(t, ctx, "aToken")
aMountName := naming.Join(aRoot, "a/server/aa/aaa")
aWakeupName := naming.Join(aRoot, "a/client/aa/aaa")
aEndpoint := newEndpoint(t, "aAddr:1")
mount(t, ctx, aMountName, aEndpoint, true)
checkWokenUp(t, "aToken", wakeupCh, false)
if want, got := aEndpoint, resolve(t, ctx, aWakeupName, true); want != got {
t.Errorf("Resolve: got %v want %v", got, want)
}
checkWokenUp(t, "aToken", wakeupCh, true)
perms := mtPerms("...")
setPermissions(t, ctx, naming.Join(aRoot, "a/server/aa"), perms, true)
getPermissions(t, ctx, naming.Join(aRoot, "a/client/aa"), perms)
checkWokenUp(t, "aToken", wakeupCh, true)
// Two.
bRoot := register(t, ctx, "bToken")
bMountName := naming.Join(bRoot, "b/server")
bWakeupName := naming.Join(bRoot, "b/client")
bEndpoint := newEndpoint(t, "bAddr:2")
mount(t, ctx, bMountName, bEndpoint, true)
checkWokenUp(t, "bToken", wakeupCh, false)
if want, got := bEndpoint, resolve(t, ctx, bWakeupName, true); want != got {
t.Errorf("Resolve: got %v want %v", got, want)
}
checkWokenUp(t, "bToken", wakeupCh, true)
// Fail.
cRoot := register(t, ctx, "cToken")
cMountName := naming.Join(cRoot, "c/server")
resolve(t, ctx, cMountName, false) // not mounted
checkWokenUp(t, "cToken", wakeupCh, false)
// Restricted set of resolvable names.
resolve(t, ctx, "wakeup/server/foo", false)
resolve(t, ctx, "wakeup/mounts", false)
resolve(t, ctx, "wakeup/mounts/foo/bar", false)
resolve(t, ctx, "wakeup/foo", false)
}
func TestWakeupPermissions(t *testing.T) {
ctx, shutdown := test.V23InitWithMounttable()
defer shutdown()
ctxOne := withNewPrincipal(t, ctx)
ctxTwo := withNewPrincipal(t, ctx)
wakeupCh := make(chan string, 100)
stop := newWakeup(t, ctxOne, func(ctx *context.T, token, _ string) error {
wakeupCh <- token
return nil
})
defer stop()
// Success.
aRoot := register(t, ctxTwo, "aToken")
aMountName := naming.Join(aRoot, "a/server/aa/aaa")
aWakeupName := naming.Join(aRoot, "a/client/aa/aaa")
aEndpoint := newEndpoint(t, "aAddr:1")
mount(t, ctxTwo, aMountName, aEndpoint, true)
checkWokenUp(t, "aToken", wakeupCh, false)
if want, got := aEndpoint, resolve(t, ctxTwo, aWakeupName, true); want != got {
t.Errorf("Resolve: got %v want %v", got, want)
}
checkWokenUp(t, "aToken", wakeupCh, true)
// Failure.
bRoot := register(t, ctxOne, "bToken")
bMountName := naming.Join(bRoot, "b/server")
bWakeupName := naming.Join(bRoot, "b/client")
bEndpoint := newEndpoint(t, "bAddr:2")
mount(t, ctxTwo, bMountName, bEndpoint, false)
mount(t, ctxOne, bMountName, bEndpoint, true)
checkWokenUp(t, "bToken", wakeupCh, false)
if want, got := bEndpoint, resolve(t, ctxTwo, bWakeupName, true); want != got {
t.Errorf("Resolve: got %v want %v", got, want)
}
checkWokenUp(t, "bToken", wakeupCh, true)
cRoot := register(t, ctxOne, "cToken")
cMountName := naming.Join(cRoot, "c/server/cc/ccc")
cWakeupName := naming.Join(cRoot, "c/client/cc/ccc")
cEndpoint := newEndpoint(t, "cAddr:3")
mount(t, ctxTwo, cMountName, cEndpoint, false)
mount(t, ctxOne, cMountName, cEndpoint, true)
if want, got := cEndpoint, resolve(t, ctxTwo, cWakeupName, true); want != got {
t.Errorf("Resolve: got %v want %v", got, want)
}
}