blob: edacc19a85a07f8402d2ec46ebd7365ab6a27aaa [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 (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"v.io/x/ref"
"v.io/x/ref/lib/security"
"v.io/x/ref/test/expect"
"v.io/x/ref/test/v23test"
)
func withCreds(dir string, cmd *v23test.Cmd) *v23test.Cmd {
cmd.Vars[ref.EnvCredentials] = dir
return cmd
}
// redirect redirects the stdout of the given command to the file at the given
// path.
func redirect(t *testing.T, cmd *v23test.Cmd, path string) {
if err := ioutil.WriteFile(path, []byte(cmd.Stdout()), 0600); err != nil {
t.Fatalf("WriteFile(%q) failed: %v\n", path, err)
}
}
// removePublicKeys replaces public keys (16 hex bytes, :-separated) with
// XX:.... This substitution enables comparison with golden output even when
// keys are freshly minted by the "principal create" command.
func removePublicKeys(input string) string {
return regexp.MustCompile("([0-9a-f]{2}:){15}[0-9a-f]{2}").ReplaceAllString(input, "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX")
}
func removeCaveats(input string) string {
input = regexp.MustCompile(`0xa64c2d0119fba3348071feeb2f308000\(time\.Time=.*\)`).ReplaceAllString(input, "ExpiryCaveat")
input = regexp.MustCompile(`0x54a676398137187ecdb26d2d69ba0003\(\[]string=.*\)`).ReplaceAllString(input, "MethodCaveat")
input = regexp.MustCompile(`0x00000000000000000000000000000000\(bool=true\)`).ReplaceAllString(input, "Unconstrained")
return input
}
func TestV23BlessSelf(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
outputDir = sh.MakeTempDir()
aliceDir = filepath.Join(outputDir, "alice")
aliceBlessingFile = filepath.Join(outputDir, "aliceself")
)
bin := v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
sh.Cmd(bin, "create", aliceDir, "alice").Run()
redirect(t, withCreds(aliceDir, sh.Cmd(bin, "blessself", "alicereborn")), aliceBlessingFile)
got := removePublicKeys(withCreds(aliceDir, sh.Cmd(bin, "dumpblessings", aliceBlessingFile)).Stdout())
want := `Blessings : alicereborn
PublicKey : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 1
Chain #0 (1 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: alicereborn with 0 caveats
`
if want != got {
t.Fatalf("unexpected output, wanted \n%s, got\n%s", want, got)
}
}
func TestV23Store(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
outputDir = sh.MakeTempDir()
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
aliceDir = filepath.Join(outputDir, "alice")
aliceFriend = filepath.Join(outputDir, "alice.bless")
bobDir = filepath.Join(outputDir, "bob")
bobForPeer = filepath.Join(outputDir, "bob.get.forpeer")
)
// Create two principals: alice and bob.
sh.Cmd(bin, "create", aliceDir, "alice").Run()
sh.Cmd(bin, "create", bobDir, "bob").Run()
// Bless Bob with Alice's principal.
redirect(t, withCreds(aliceDir, sh.Cmd(bin, "bless", "--for=1m", bobDir, "friend")), aliceFriend)
// Run store forpeer on bob.
sh.Cmd(bin, "--v23.credentials="+bobDir, "set", "forpeer", aliceFriend, "alice").Run()
redirect(t, withCreds(aliceDir, sh.Cmd(bin, "--v23.credentials="+bobDir, "get", "forpeer", "alice:server")), bobForPeer)
got := removeCaveats(removePublicKeys(sh.Cmd(bin, "dumpblessings", bobForPeer).Stdout()))
want := `Blessings : bob,alice:friend
PublicKey : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 2
Chain #0 (1 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: bob with 0 caveats
Chain #1 (2 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: alice with 0 caveats
Certificate #1: friend with 1 caveat
(0) ExpiryCaveat
`
if want != got {
t.Errorf("unexpected output, got\n%s, wanted\n%s", got, want)
}
// Test the names flag.
got = withCreds(aliceDir, sh.Cmd(bin, "--v23.credentials="+bobDir, "get", "forpeer", "--names", "alice:server")).Stdout()
want = `bob
alice:friend
`
if got != want {
t.Errorf("unexpected output, got %s, want %s", got, want)
}
// Test the rootkey flag. In particular alice:friend's rootkey should be equal to alice's publickey.
got = withCreds(aliceDir, sh.Cmd(bin, "--v23.credentials="+bobDir, "get", "forpeer", "--rootkey", "alice:friend", "alice:server")).Stdout()
want = withCreds(aliceDir, sh.Cmd(bin, "get", "publickey", "--pretty")).Stdout()
if got != want {
t.Errorf("unexpected output, got %s, want %s", got, want)
}
// Test the caveats flag.
got = withCreds(aliceDir, sh.Cmd(bin, "--v23.credentials="+bobDir, "get", "forpeer", "--caveats", "alice:friend", "alice:server")).Stdout()
want = "Expires at"
if !strings.HasPrefix(got, want) {
t.Errorf("unexpected output, got %s, want %s", got, want)
}
}
func TestV23Dump(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
outputDir = sh.MakeTempDir()
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
aliceDir = filepath.Join(outputDir, "alice")
aliceExpiredDir = filepath.Join(outputDir, "alice-expired")
)
sh.Cmd(bin, "create", aliceDir, "alice").Run()
got := removePublicKeys(withCreds(aliceDir, sh.Cmd(bin, "dump")).Stdout())
want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Default Blessings : alice
---------------- BlessingStore ----------------
Default Blessings alice
Peer pattern Blessings
... alice
---------------- BlessingRoots ----------------
Public key Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX [alice]
`
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
got = withCreds(aliceDir, sh.Cmd(bin, "dump", "-s")).Stdout()
want = "alice\n"
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
sh.Cmd(bin, "--v23.credentials="+aliceDir, "fork", "--for", "-1h", aliceExpiredDir, "expired").Run()
got = removePublicKeys(withCreds(aliceExpiredDir, sh.Cmd(bin, "dump")).Stdout())
want = `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Default Blessings : alice:expired [EXPIRED]
---------------- BlessingStore ----------------
Default Blessings alice:expired
Peer pattern Blessings
... alice:expired
---------------- BlessingRoots ----------------
Public key Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX [alice]
`
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
got = withCreds(aliceExpiredDir, sh.Cmd(bin, "dump", "-s")).Stdout()
want = "alice:expired [EXPIRED]\n"
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
}
func TestV23GetRecognizedRoots(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
outputDir = sh.MakeTempDir()
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
aliceDir = filepath.Join(outputDir, "alice")
)
sh.Cmd(bin, "create", aliceDir, "alice").Run()
got := removePublicKeys(withCreds(aliceDir, sh.Cmd(bin, "get", "recognizedroots")).Stdout())
want := `Public key Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX [alice]
`
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
}
func TestV23GetPeermap(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
outputDir = sh.MakeTempDir()
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
aliceDir = filepath.Join(outputDir, "alice")
)
sh.Cmd(bin, "create", aliceDir, "alice").Run()
got := withCreds(aliceDir, sh.Cmd(bin, "get", "peermap")).Stdout()
want := `Default Blessings alice
Peer pattern Blessings
... alice
`
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
}
// Given an invocation of "principal recvblessings", this function returns the
// arguments to provide to "principal bless" provided by the "recvblessings"
// invocation.
//
// For example,
// principal recvblessings
// would typically print something like:
// principal bless --remote-key=<some_public_key> --remote-token=<some_token> extensionfoo
// as an example of command line to use to send the blessings over.
//
// In that case, this method would return:
// { "--remote-key=<some_public_key>", "--remote-token=<some_token>", "extensionfoo"}
func blessArgsFromRecvBlessings(s *expect.Session) []string {
cmd := s.ExpectSetEventuallyRE("(^principal bless .*$)")[0][0]
return strings.Split(cmd, " ")[2:]
}
func TestV23RecvBlessings(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
outputDir = sh.MakeTempDir()
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
aliceDir = filepath.Join(outputDir, "alice")
bobDir = filepath.Join(outputDir, "bob")
carolDir = filepath.Join(outputDir, "carol")
bobBlessFile = filepath.Join(outputDir, "bobBlessInfo")
)
// Generate principals
sh.Cmd(bin, "create", aliceDir, "alice").Run()
sh.Cmd(bin, "create", bobDir, "bob").Run()
sh.Cmd(bin, "create", carolDir, "carol").Run()
// Run recvblessings on carol, and have alice send blessings over
// (blessings received must be set as default and shareable with all
// peers).
{
cmd := sh.Cmd(bin, "--v23.credentials="+carolDir, "--v23.tcp.address=127.0.0.1:0", "recvblessings")
cmd.Start()
args := append([]string{"bless", "--require-caveats=false"}, blessArgsFromRecvBlessings(cmd.S)...)
// Use the "friend:carol" extension
args = append(args, "friend:carol")
withCreds(aliceDir, sh.Cmd(bin, args...)).Run()
cmd.Wait()
}
// Run recvblessings on carol, and have alice send blessings over
// (blessings received must be set as shareable with peers matching
// 'alice:...'.)
{
cmd := sh.Cmd(bin, "--v23.credentials="+carolDir, "--v23.tcp.address=127.0.0.1:0", "recvblessings", "--for-peer=alice", "--set-default=false")
cmd.Start()
// recvblessings suggests a random extension, find the extension
// and replace it with friend:carol:foralice.
args := append([]string{"bless", "--require-caveats=false"}, blessArgsFromRecvBlessings(cmd.S)...)
args = append(args, "friend:carol:foralice")
withCreds(aliceDir, sh.Cmd(bin, args...)).Run()
cmd.Wait()
}
// Run recvblessings on carol with the --remote-arg-file flag, and have
// bob send blessings over with the --remote-arg-file flag.
{
cmd := sh.Cmd(bin, "--v23.credentials="+carolDir, "--v23.tcp.address=127.0.0.1:0", "recvblessings", "--for-peer=bob", "--set-default=false", "--remote-arg-file="+bobBlessFile)
cmd.Start()
// recvblessings suggests a random extension, use
// friend:carol:forbob instead.
args := append([]string{"bless", "--require-caveats=false"}, blessArgsFromRecvBlessings(cmd.S)...)
args = append(args, "friend:carol:forbob")
withCreds(bobDir, sh.Cmd(bin, args...)).Run()
cmd.Wait()
}
cmd := sh.Cmd(bin, "--v23.credentials="+carolDir, "--v23.tcp.address=127.0.0.1:0", "recvblessings", "--for-peer=alice:...", "--set-default=false", "--vmodule=*=2", "--logtostderr")
cmd.Start()
args := append([]string{"bless", "--require-caveats=false"}, blessArgsFromRecvBlessings(cmd.S)...)
args = append(args, "willfail")
{
// Mucking around with remote-key should fail.
cpy := strings.Split(regexp.MustCompile("remote-key=").ReplaceAllString(strings.Join(args, " "), "remote-key=BAD"), " ")
cmd := withCreds(aliceDir, sh.Cmd(bin, cpy...))
cmd.ExitErrorIsOk = true
_, stderr := cmd.StdoutStderr()
if cmd.Err == nil {
t.Fatalf("%v should have failed, but did not", cpy)
}
if want, got := "key mismatch", stderr; !strings.Contains(got, want) {
t.Fatalf("expected %q to be contained within\n%s\n, but was not", want, got)
}
}
{
// Mucking around with the token should fail.
cpy := strings.Split(regexp.MustCompile("remote-token=").ReplaceAllString(strings.Join(args, " "), "remote-token=BAD"), " ")
cmd := withCreds(aliceDir, sh.Cmd(bin, cpy...))
cmd.ExitErrorIsOk = true
_, stderr := cmd.StdoutStderr()
if cmd.Err == nil {
t.Fatalf("%v should have failed, but did not", cpy)
}
if want, got := "blessings received from unexpected sender", stderr; !strings.Contains(got, want) {
t.Fatalf("expected %q to be contained within\n%s\n, but was not", want, got)
}
}
// carol's recvblessings never completes since alice couldn't get her
// act together. Kill the recvblessings command to release the lock on
// carol's credentials.
cmd.Terminate(os.Kill)
// Dump carol out, the only blessing that survives should be from the
// first "bless" command. (alice:friend:carol).
got := removePublicKeys(sh.Cmd(bin, "--v23.credentials="+carolDir, "dump").Stdout())
want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Default Blessings : alice:friend:carol
---------------- BlessingStore ----------------
Default Blessings alice:friend:carol
Peer pattern Blessings
... alice:friend:carol
alice alice:friend:carol:foralice
bob bob:friend:carol:forbob
---------------- BlessingRoots ----------------
Public key Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX [alice]
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX [bob]
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX [carol]
`
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
}
func TestV23RecvBlessingsInteractive(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
outputDir = sh.MakeTempDir()
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
aliceDir = filepath.Join(outputDir, "alice")
bobDir = filepath.Join(outputDir, "bob")
)
// Generate principals
sh.Cmd(bin, "create", aliceDir, "alice").Run()
sh.Cmd(bin, "create", bobDir, "bob").Run()
// Run recvblessings on bob
cmd := sh.Cmd(bin, "--v23.credentials="+bobDir, "--v23.tcp.address=127.0.0.1:0", "recvblessings")
cmd.Start()
args := blessArgsFromRecvBlessings(cmd.S)
// When running the exact command, must be prompted about caveats.
{
cmd := withCreds(aliceDir, sh.Cmd(bin, append([]string{"bless"}, args...)...))
cmd.SetStdinReader(strings.NewReader("yeah\n"))
cmd.ExitErrorIsOk = true
cmd.Start()
cmd.S.Expect("WARNING: No caveats provided")
// Saying something other than "yes" or "YES"
// should fail.
if cmd.Wait(); cmd.Err == nil {
t.Fatalf("Expected principal bless to fail because the wrong input was provided")
}
}
// When agreeing to have no caveats, must specify an extension
{
cmd := withCreds(aliceDir, sh.Cmd(bin, append([]string{"bless"}, args...)...))
cmd.SetStdinReader(strings.NewReader("yes\n"))
cmd.ExitErrorIsOk = true
cmd.Start()
cmd.S.Expect("WARNING: No caveats provided")
if cmd.Wait(); cmd.Err == nil {
t.Fatalf("Expected principal bless to fail because the wrong input was provided")
}
}
// When providing both, the bless command should succeed.
{
cmd := withCreds(aliceDir, sh.Cmd(bin, append([]string{"bless"}, args...)...))
cmd.SetStdinReader(strings.NewReader("YES\nfriend:bobby\n"))
cmd.Run()
}
got := removePublicKeys(sh.Cmd(bin, "--v23.credentials="+bobDir, "dump").Stdout())
want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Default Blessings : alice:friend:bobby
---------------- BlessingStore ----------------
Default Blessings alice:friend:bobby
Peer pattern Blessings
... alice:friend:bobby
---------------- BlessingRoots ----------------
Public key Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX [alice]
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX [bob]
`
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
}
func TestV23Fork(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
outputDir = sh.MakeTempDir()
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
aliceDir = filepath.Join(outputDir, "alice")
alicePhoneDir = filepath.Join(outputDir, "alice-phone")
alicePhoneCalendarDir = filepath.Join(outputDir, "alice-phone-calendar")
tmpfile = filepath.Join(outputDir, "tmpfile")
)
// Generate principals for alice.
sh.Cmd(bin, "create", aliceDir, "alice").Run()
// Run fork to setup up credentials for alice:phone that are
// blessed by alice under the extension "phone".
sh.Cmd(bin, "--v23.credentials="+aliceDir, "fork", "--for", "1h", alicePhoneDir, "phone").Run()
// Dump alice-phone out, the only blessings it has must be from alice (alice:phone).
{
got := removePublicKeys(sh.Cmd(bin, "--v23.credentials="+alicePhoneDir, "dump").Stdout())
want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Default Blessings : alice:phone
---------------- BlessingStore ----------------
Default Blessings alice:phone
Peer pattern Blessings
... alice:phone
---------------- BlessingRoots ----------------
Public key Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX [alice]
`
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
}
// And it should have an expiry caveat
{
redirect(t, sh.Cmd(bin, "--v23.credentials", alicePhoneDir, "get", "default"), tmpfile)
got := removeCaveats(removePublicKeys(sh.Cmd(bin, "dumpblessings", tmpfile).Stdout()))
want := `Blessings : alice:phone
PublicKey : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 1
Chain #0 (2 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: alice with 0 caveats
Certificate #1: phone with 1 caveat
(0) ExpiryCaveat
`
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
}
// Run fork to setup up credentials for alice:phone:calendar that are
// blessed by alice:phone under the extension "calendar".
sh.Cmd(bin, "--v23.credentials="+alicePhoneDir, "fork", "--for", "1h", alicePhoneCalendarDir, "calendar").Run()
{
got := removePublicKeys(sh.Cmd(bin, "--v23.credentials="+alicePhoneCalendarDir, "dump").Stdout())
want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Default Blessings : alice:phone:calendar
---------------- BlessingStore ----------------
Default Blessings alice:phone:calendar
Peer pattern Blessings
... alice:phone:calendar
---------------- BlessingRoots ----------------
Public key Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX [alice]
`
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
}
{
redirect(t, sh.Cmd(bin, "--v23.credentials", alicePhoneCalendarDir, "get", "default"), tmpfile)
got := removeCaveats(removePublicKeys(sh.Cmd(bin, "dumpblessings", tmpfile).Stdout()))
want := `Blessings : alice:phone:calendar
PublicKey : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 1
Chain #0 (3 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: alice with 0 caveats
Certificate #1: phone with 1 caveat
(0) ExpiryCaveat
Certificate #2: calendar with 1 caveat
(0) ExpiryCaveat
`
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
}
}
}
func TestV23Create(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
outputDir = sh.MakeTempDir()
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
aliceDir = filepath.Join(outputDir, "alice")
)
checkBlessing := func(want string) {
if p, err := security.LoadPersistentPrincipal(aliceDir, nil); err != nil {
t.Fatalf("Failed loading principal: %v", err)
} else {
defaultBlessings, _ := p.BlessingStore().Default()
if got := defaultBlessings.String(); got != want {
t.Fatalf("expected blessings \"%v\", got \"%v\" instead", want, got)
}
}
}
// Creating a principal should succeed the first time.
sh.Cmd(bin, "create", aliceDir, "alice").Run()
checkBlessing("alice")
// The second time should fail (the create command won't override an existing principal).
cmd := sh.Cmd(bin, "create", aliceDir, "alice")
cmd.ExitErrorIsOk = true
if cmd.Run(); cmd.Err == nil {
t.Fatalf("principal creation should have failed, but did not")
}
// If we specify -overwrite, it will.
sh.Cmd(bin, "create", "--overwrite", aliceDir, "alice").Run()
checkBlessing("alice")
// If we create a principal without specifying a blessing name, it will have no blessing.
sh.Cmd(bin, "create", "--overwrite", aliceDir).Run()
checkBlessing("")
}
func TestV23CreateWithPassphrase(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
outputDir = sh.MakeTempDir()
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
aliceDir = filepath.Join(outputDir, "alice")
)
// Create a principal with a passphrase.
principalCmd := sh.Cmd(bin, "create", aliceDir, "alice")
principalCmd.SetStdinReader(strings.NewReader("Rumpelstiltskin"))
principalCmd.Run()
// Loading the principal should fail without a passphrase.
if _, err := security.LoadPersistentPrincipal(aliceDir, nil); err == nil {
t.Fatalf("expected to fail without passprhase, succeeded instead")
}
// Loading the principal should fail with the wrong passphrase.
if _, err := security.LoadPersistentPrincipal(aliceDir, []byte("Tom Tit Tom")); err == nil {
t.Fatalf("expected to fail with incorrect passprhase, succeeded instead")
}
// Loading the principal should succeed with the right passphrase.
if p, err := security.LoadPersistentPrincipal(aliceDir, []byte("Rumpelstiltskin")); err != nil {
t.Fatalf("expected to succeed with correct passprhase, failed instead: %v", err)
} else {
defaultBlessings, _ := p.BlessingStore().Default()
if got, want := defaultBlessings.String(), "alice"; got != want {
t.Fatalf("expected blessings %v, got %v instead", want, got)
}
}
}
func TestV23Caveats(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
outputDir = sh.MakeTempDir()
aliceDir = filepath.Join(outputDir, "alice")
aliceBlessingFile = filepath.Join(outputDir, "aliceself")
)
bin := v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
sh.Cmd(bin, "create", aliceDir, "alice").Run()
args := []string{
"blessself",
"--caveat=\"v.io/v23/security\".MethodCaveat={\"method\"}",
"--caveat={{0x54,0xa6,0x76,0x39,0x81,0x37,0x18,0x7e,0xcd,0xb2,0x6d,0x2d,0x69,0xba,0x0,0x3},typeobject([]string)}={\"method\"}",
"alicereborn",
}
redirect(t, withCreds(aliceDir, sh.Cmd(bin, args...)), aliceBlessingFile)
got := removeCaveats(removePublicKeys(withCreds(aliceDir, sh.Cmd(bin, "dumpblessings", aliceBlessingFile)).Stdout()))
want := `Blessings : alicereborn
PublicKey : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 1
Chain #0 (1 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: alicereborn with 2 caveats
(0) MethodCaveat
(1) MethodCaveat
`
if want != got {
t.Fatalf("unexpected output, wanted \n%s, got\n%s", want, got)
}
}
func TestV23ForkWithoutVDLPATH(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
delete(sh.Vars, "JIRI_ROOT")
delete(sh.Vars, "VDLPATH")
var (
parent = sh.MakeTempDir()
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
)
sh.Cmd(bin, "create", parent, "parent").Run()
sh.Cmd(bin, "--v23.credentials="+parent, "fork", "--for=1s", sh.MakeTempDir(), "child").Run()
}
func TestV23ForkWithoutCaveats(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
parent = sh.MakeTempDir()
child = sh.MakeTempDir()
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
)
sh.Cmd(bin, "create", parent, "parent").Run()
cmd := sh.Cmd(bin, "--v23.credentials", parent, "fork", child, "child")
cmd.ExitErrorIsOk = true
if _, stderr := cmd.StdoutStderr(); cmd.Err == nil {
t.Errorf("fork should have failed without any caveats, but did not")
} else if got, want := stderr, "ERROR: no caveats provided"; !strings.Contains(got, want) {
t.Errorf("fork returned error: %q, expected error to contain %q", got, want)
}
cmd = sh.Cmd(bin, "--v23.credentials", parent, "fork", "--for=0", child, "child")
cmd.ExitErrorIsOk = true
if _, stderr := cmd.StdoutStderr(); cmd.Err == nil {
t.Errorf("fork should have failed without any caveats, but did not")
} else if got, want := stderr, "ERROR: no caveats provided"; !strings.Contains(got, want) {
t.Errorf("fork returned error: %q, expected error to contain %q", got, want)
}
sh.Cmd(bin, "--v23.credentials", parent, "fork", "--require-caveats=false", child, "child").Run()
}
func TestV23Bless(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
dir = sh.MakeTempDir()
aliceDir = filepath.Join(dir, "alice")
bobDir = filepath.Join(dir, "bob")
tmpfile = filepath.Join(dir, "tmpfile")
)
// Create two principals: alice and bob
sh.Cmd(bin, "create", aliceDir, "alice").Run()
sh.Cmd(bin, "create", bobDir, "bob").Run()
// All blessings will be done by "alice"
aliceCmd := func(name string, args ...string) *v23test.Cmd {
return withCreds(aliceDir, sh.Cmd(name, args...))
}
{
// "alice" should fail to bless "bob" without any caveats
cmd := aliceCmd(bin, "bless", bobDir, "friend")
cmd.ExitErrorIsOk = true
if _, stderr := cmd.StdoutStderr(); cmd.Err == nil {
t.Errorf("bless should have failed when no caveats are specified")
} else if got, want := stderr, "ERROR: no caveats provided"; !strings.Contains(got, want) {
t.Errorf("got error %q, expected to match %q", got, want)
}
}
{
// But succeed if --require-caveats=false is specified
redirect(t, aliceCmd(bin, "bless", "--require-caveats=false", bobDir, "friend"), tmpfile)
got := removeCaveats(removePublicKeys(aliceCmd(bin, "dumpblessings", tmpfile).Stdout()))
want := `Blessings : alice:friend
PublicKey : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 1
Chain #0 (2 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: alice with 0 caveats
Certificate #1: friend with 1 caveat
(0) Unconstrained
`
if got != want {
t.Errorf("Got\n%vWant\n%v", got, want)
}
}
{
// And succeed if --for is specified
redirect(t, aliceCmd(bin, "bless", "--for=1m", bobDir, "friend"), tmpfile)
got := removeCaveats(removePublicKeys(aliceCmd(bin, "dumpblessings", tmpfile).Stdout()))
want := `Blessings : alice:friend
PublicKey : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 1
Chain #0 (2 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: alice with 0 caveats
Certificate #1: friend with 1 caveat
(0) ExpiryCaveat
`
if got != want {
t.Errorf("Got\n%vWant\n%v", got, want)
}
}
{
// If the Blessings are expired, dumpBlessings should print so.
redirect(t, aliceCmd(bin, "bless", "--for=-1s", bobDir, "friend"), tmpfile)
got := removeCaveats(removePublicKeys(aliceCmd(bin, "dumpblessings", tmpfile).Stdout()))
want := `Blessings : alice:friend [EXPIRED]
PublicKey : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 1
Chain #0 (2 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: alice with 0 caveats
Certificate #1: friend with 1 caveat
(0) ExpiryCaveat
`
if got != want {
t.Errorf("Got\n%vWant\n%v", got, want)
}
}
{
// But not if --for=0
cmd := aliceCmd(bin, "bless", "--for=0", bobDir, "friend")
cmd.ExitErrorIsOk = true
if _, stderr := cmd.StdoutStderr(); cmd.Err == nil {
t.Errorf("bless should have failed when no caveats are specified")
} else if got, want := stderr, "ERROR: no caveats provided"; !strings.Contains(got, want) {
t.Errorf("got error %q, expected to match %q", got, want)
}
}
{
// And should be able to bless a public key.
keyfile := filepath.Join(dir, "keyfile")
redirect(t, withCreds(bobDir, sh.Cmd(bin, "get", "publickey")), keyfile)
redirect(t, aliceCmd(bin, "bless", "--for=1m", keyfile, "buddy"), tmpfile)
got := removeCaveats(removePublicKeys(sh.Cmd(bin, "dumpblessings", tmpfile).Stdout()))
want := `Blessings : alice:buddy
PublicKey : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 1
Chain #0 (2 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: alice with 0 caveats
Certificate #1: buddy with 1 caveat
(0) ExpiryCaveat
`
if got != want {
t.Errorf("Got\n%vWant\n%v", got, want)
}
}
}
func TestV23AddBlessingsToRoots(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
aliceDir = sh.MakeTempDir()
bobDir = sh.MakeTempDir()
blessingFile = filepath.Join(sh.MakeTempDir(), "bobfile")
// Extract the public key from the first line of output from
// "principal dump", which is formatted as:
// Public key : <the public key>
publicKey = func(dir string) string {
output := sh.Cmd(bin, "--v23.credentials="+dir, "dump").Stdout()
line := strings.SplitN(output, "\n", 2)[0]
fields := strings.Split(line, " ")
return fields[len(fields)-1]
}
)
// Create two principals, "alice" and "bob"
sh.Cmd(bin, "create", aliceDir, "alice").Run()
sh.Cmd(bin, "create", bobDir, "bob").Run()
// Have bob create a "bob/friend" blessing and have alice recognize that.
redirect(t, sh.Cmd(bin, "--v23.credentials="+bobDir, "bless", "--require-caveats=false", aliceDir, "friend"), blessingFile)
sh.Cmd(bin, "--v23.credentials="+aliceDir, "recognize", blessingFile).Run()
want := fmt.Sprintf(`Public key Pattern
%v [alice]
%v [bob]
`, publicKey(aliceDir), publicKey(bobDir))
// Finally view alice's recognized roots, it should have lines corresponding to aliceLine and bobLine.
got := sh.Cmd(bin, "--v23.credentials="+aliceDir, "get", "recognizedroots").Stdout()
if got != want {
t.Fatalf("Got:\n%v\n\nWant:\n%v", got, want)
}
}
func TestV23AddKeyToRoots(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
outputDir = sh.MakeTempDir()
aliceDir = filepath.Join(outputDir, "alice")
bobDir = filepath.Join(outputDir, "bob")
)
sh.Cmd(bin, "create", aliceDir, "alice").Run()
sh.Cmd(bin, "create", bobDir, "bob").Run()
// Get bob's public key and add it to roots for alice
bobKey := strings.TrimSpace(sh.Cmd(bin, "--v23.credentials="+bobDir, "get", "publickey").Stdout())
bobPrettyKey := strings.TrimSpace(sh.Cmd(bin, "--v23.credentials="+bobDir, "get", "publickey", "--pretty").Stdout())
sh.Cmd(bin, "--v23.credentials="+aliceDir, "recognize", "bob", bobKey).Run()
// Verify that it has been added
output := sh.Cmd(bin, "--v23.credentials="+aliceDir, "dump").Stdout()
want := fmt.Sprintf("%v [bob]", bobPrettyKey)
for _, line := range strings.Split(output, "\n") {
if line == want {
return
}
}
t.Errorf("Could not find line:\n%v\nin output:\n%v\n", want, output)
}
func TestV23DumpRoots(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
outputDir = sh.MakeTempDir()
aliceDir = filepath.Join(outputDir, "alice")
bobDir = filepath.Join(outputDir, "bob")
aliceFriend = filepath.Join(outputDir, "alicefriend")
aliceFriendRoot = filepath.Join(outputDir, "alicefriendroot")
aliceDefault = filepath.Join(outputDir, "alicedefault")
)
sh.Cmd(bin, "create", aliceDir, "alice").Run()
sh.Cmd(bin, "create", bobDir, "bob").Run()
redirect(t, sh.Cmd(bin, "--v23.credentials="+aliceDir, "bless", "--require-caveats=false", bobDir, "friend"), aliceFriend)
redirect(t, sh.Cmd(bin, "dumproots", aliceFriend), aliceFriendRoot)
redirect(t, sh.Cmd(bin, "--v23.credentials="+aliceDir, "get", "default"), aliceDefault)
want := sh.Cmd(bin, "dumpblessings", aliceDefault).Stdout()
got := sh.Cmd(bin, "dumpblessings", aliceFriendRoot).Stdout()
if got != want {
t.Errorf("Got:\n%s\nWant:\n%s\n", got, want)
}
}
func TestV23Union(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
var (
bin = v23test.BuildGoPkg(sh, "v.io/x/ref/cmd/principal")
outputDir = sh.MakeTempDir()
aliceDir = filepath.Join(outputDir, "alice")
blessing1 = filepath.Join(outputDir, "blessing1")
blessing2 = filepath.Join(outputDir, "blessing2")
union = filepath.Join(outputDir, "union")
)
sh.Cmd(bin, "create", aliceDir, "alice").Run()
redirect(t, sh.Cmd(bin, "--v23.credentials="+aliceDir, "bless", "--require-caveats=false", aliceDir, "one"), blessing1)
redirect(t, sh.Cmd(bin, "--v23.credentials="+aliceDir, "bless", "--require-caveats=false", aliceDir, "two"), blessing2)
redirect(t, sh.Cmd(bin, "union", blessing1, blessing2), union)
want := `Blessings : alice:one,alice:two
PublicKey : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 2
Chain #0 (2 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: alice with 0 caveats
Certificate #1: one with 1 caveat
(0) 0x00000000000000000000000000000000(bool=true)
Chain #1 (2 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate #0: alice with 0 caveats
Certificate #1: two with 1 caveat
(0) 0x00000000000000000000000000000000(bool=true)
`
got := removePublicKeys(sh.Cmd(bin, "dumpblessings", union).Stdout())
if got != want {
t.Errorf("Got:\n%s\nWant:\n%s\n", got, want)
}
}
func TestMain(m *testing.M) {
v23test.TestMain(m)
}