ref/cmd/principal: Some principal getter additions.
As per https://github.com/veyron/release-issues/issues/991.
This CL adds:
(1): 'principal get recognizedroots' prints the recognized public keys and
patterns.
(2): 'principal get peermap' prints the blessings to present to peers.
(3): --name and --rootkey flags to the 'get forpeer' and
'get default' commands that prints either the name or the rootkey
of the blessing.
Change-Id: Ib56161f66a2a5e7e736b0234f25a84555602bc31
diff --git a/cmd/principal/doc.go b/cmd/principal/doc.go
index 0e03614..40aea3c 100644
--- a/cmd/principal/doc.go
+++ b/cmd/principal/doc.go
@@ -208,7 +208,7 @@
-remote-arg-file=
If non-empty, the remote key, remote token, and principal will be written to
the specified file in a JSON object. This can be provided to 'principal bless
- --remote-arg-file FILE EXTENSION'.
+ --remote-arg-file FILE EXTENSION'
-set-default=true
If true, the blessings received will be set as the default blessing in the
store
@@ -299,7 +299,7 @@
-remote-arg-file=
File containing bless arguments written by 'principal recvblessings
-remote-arg-file FILE EXTENSION' command. This can be provided to bless in
- place of --remote-key, --remote-token, and <principal>.
+ place of --remote-key, --remote-token, and <principal>
-remote-key=
Public key of the remote principal to bless (obtained from the
'recvblessings' command run by the remote principal
@@ -383,8 +383,11 @@
principal get <command>
The principal get commands are:
- default Return blessings marked as default
- forpeer Return blessings marked for the provided peer
+ default Return blessings marked as default
+ forpeer Return blessings marked for the provided peer
+ recognizedroots Return recognized blessings, and their associated public key.
+ peermap Shows the map from peer pattern to which blessing name to
+ present.
Principal Get Default
@@ -392,7 +395,14 @@
the environment that this tool is running in.
Usage:
- principal get default
+ principal get default [flags]
+
+The principal get default flags are:
+ -name=false
+ If true, shows the value of the blessing name to be presented to the peer
+ -rootkey=false
+ If true, shows the value of the root key of the certificate chain to be
+ presented to the peer
Principal Get Forpeer
@@ -400,7 +410,7 @@
specified by the environment that this tool is running in.
Usage:
- principal get forpeer [<peer_1> ... <peer_k>]
+ principal get forpeer [flags] [<peer_1> ... <peer_k>]
<peer_1> ... <peer_k> are the (human-readable string) blessings bound to the
peer. The returned blessings are marked with a pattern that is matched by at
@@ -408,6 +418,33 @@
blessings that are marked for all peers (i.e., blessings set on the store with
the "..." pattern).
+The principal get forpeer flags are:
+ -name=false
+ If true, shows the value of the blessing name to be presented to the peer
+ -rootkey=false
+ If true, shows the value of the root key of the certificate chain to be
+ presented to the peer
+
+Principal Get Recognizedroots
+
+Shows list of blessing names that the principal recognizes, and their associated
+public key. If the principal is operating as a client, contacted servers must
+appear on this list. If the principal is operating as a server, clients must
+present blessings derived from this list.
+
+Usage:
+ principal get recognizedroots
+
+Principal Get Peermap
+
+Shows the map from peer pattern to which blessing name to present. If the
+principal operates as a server, it presents its default blessing to all peers.
+If the principal operates as a client, it presents the map value associated with
+the peer it contacts.
+
+Usage:
+ principal get peermap
+
Principal Addtoroots
Adds an identity provider to the set of recognized roots public keys for this
diff --git a/cmd/principal/main.go b/cmd/principal/main.go
index 41845d9..d706292 100644
--- a/cmd/principal/main.go
+++ b/cmd/principal/main.go
@@ -65,6 +65,10 @@
flagRecvBlessingsSetDefault bool
flagRecvBlessingsForPeer string
+ // Flags for the commands the get blessings
+ flagBlessingsName bool
+ flagBlessingsRootKey bool
+
errNoCaveats = fmt.Errorf("no caveats provided: it is generally dangerous to bless another principal without any caveats as that gives them almost unrestricted access to the blesser's credentials. If you really want to do this, set --require-caveats=false")
cmdDump = &cmdline.Command{
Name: "dump",
@@ -275,6 +279,40 @@
},
}
+ cmdGetTrustedRoots = &cmdline.Command{
+ Name: "recognizedroots",
+ Short: "Return recognized blessings, and their associated public key.",
+ Long: `
+Shows list of blessing names that the principal recognizes, and their associated
+public key. If the principal is operating as a client, contacted servers must
+appear on this list. If the principal is operating as a server, clients must
+present blessings derived from this list.
+`,
+ Run: func(cmd *cmdline.Command, args []string) error {
+ ctx, shutdown := v23.Init()
+ defer shutdown()
+ fmt.Printf(v23.GetPrincipal(ctx).Roots().DebugString())
+ return nil
+ },
+ }
+
+ cmdGetPeerMap = &cmdline.Command{
+ Name: "peermap",
+ Short: "Shows the map from peer pattern to which blessing name to present.",
+ Long: `
+Shows the map from peer pattern to which blessing name to present.
+If the principal operates as a server, it presents its default blessing to all peers.
+If the principal operates as a client, it presents the map value associated with
+the peer it contacts.
+`,
+ Run: func(cmd *cmdline.Command, args []string) error {
+ ctx, shutdown := v23.Init()
+ defer shutdown()
+ fmt.Printf(v23.GetPrincipal(ctx).BlessingStore().DebugString())
+ return nil
+ },
+ }
+
cmdGetForPeer = &cmdline.Command{
Name: "forpeer",
Short: "Return blessings marked for the provided peer",
@@ -294,8 +332,7 @@
Run: func(cmd *cmdline.Command, args []string) error {
ctx, shutdown := v23.Init()
defer shutdown()
- principal := v23.GetPrincipal(ctx)
- return dumpBlessings(principal.BlessingStore().ForPeer(args...))
+ return printBlessingsInfo(v23.GetPrincipal(ctx).BlessingStore().ForPeer(args...))
},
}
@@ -309,8 +346,7 @@
Run: func(cmd *cmdline.Command, args []string) error {
ctx, shutdown := v23.Init()
defer shutdown()
- principal := v23.GetPrincipal(ctx)
- return dumpBlessings(principal.BlessingStore().Default())
+ return printBlessingsInfo(v23.GetPrincipal(ctx).BlessingStore().Default())
},
}
@@ -815,7 +851,7 @@
cmdBless.Flags.StringVar(&flagBlessWith, "with", "", "Path to file containing blessing to extend")
cmdBless.Flags.StringVar(&flagBlessRemoteKey, "remote-key", "", "Public key of the remote principal to bless (obtained from the 'recvblessings' command run by the remote principal")
cmdBless.Flags.StringVar(&flagBlessRemoteToken, "remote-token", "", "Token provided by principal running the 'recvblessings' command")
- cmdBless.Flags.StringVar(&flagRemoteArgFile, "remote-arg-file", "", "File containing bless arguments written by 'principal recvblessings -remote-arg-file FILE EXTENSION' command. This can be provided to bless in place of --remote-key, --remote-token, and <principal>.")
+ cmdBless.Flags.StringVar(&flagRemoteArgFile, "remote-arg-file", "", "File containing bless arguments written by 'principal recvblessings -remote-arg-file FILE EXTENSION' command. This can be provided to bless in place of --remote-key, --remote-token, and <principal>")
cmdSeekBlessings.Flags.StringVar(&flagSeekBlessingsFrom, "from", "https://dev.v.io/auth/google", "URL to use to begin the seek blessings process")
cmdSeekBlessings.Flags.BoolVar(&flagSeekBlessingsSetDefault, "set-default", true, "If true, the blessings obtained will be set as the default blessing in the store")
@@ -831,7 +867,13 @@
cmdRecvBlessings.Flags.BoolVar(&flagRecvBlessingsSetDefault, "set-default", true, "If true, the blessings received will be set as the default blessing in the store")
cmdRecvBlessings.Flags.StringVar(&flagRecvBlessingsForPeer, "for-peer", string(security.AllPrincipals), "If non-empty, the blessings received will be marked for peers matching this pattern in the store")
- cmdRecvBlessings.Flags.StringVar(&flagRemoteArgFile, "remote-arg-file", "", "If non-empty, the remote key, remote token, and principal will be written to the specified file in a JSON object. This can be provided to 'principal bless --remote-arg-file FILE EXTENSION'.")
+ cmdRecvBlessings.Flags.StringVar(&flagRemoteArgFile, "remote-arg-file", "", "If non-empty, the remote key, remote token, and principal will be written to the specified file in a JSON object. This can be provided to 'principal bless --remote-arg-file FILE EXTENSION'")
+
+ cmdGetForPeer.Flags.BoolVar(&flagBlessingsName, "name", false, "If true, shows the value of the blessing name to be presented to the peer")
+ cmdGetForPeer.Flags.BoolVar(&flagBlessingsRootKey, "rootkey", false, "If true, shows the value of the root key of the certificate chain to be presented to the peer")
+
+ cmdGetDefault.Flags.BoolVar(&flagBlessingsName, "name", false, "If true, shows the value of the blessing name to be presented to the peer")
+ cmdGetDefault.Flags.BoolVar(&flagBlessingsRootKey, "rootkey", false, "If true, shows the value of the root key of the certificate chain to be presented to the peer")
cmdSet := &cmdline.Command{
Name: "set",
@@ -853,7 +895,7 @@
All blessings are printed to stdout using base64-VOM-encoding.
`,
- Children: []*cmdline.Command{cmdGetDefault, cmdGetForPeer},
+ Children: []*cmdline.Command{cmdGetDefault, cmdGetForPeer, cmdGetTrustedRoots, cmdGetPeerMap},
}
root := &cmdline.Command{
@@ -887,6 +929,24 @@
return nil
}
+func printBlessingsInfo(blessings security.Blessings) error {
+ if blessings.IsZero() {
+ return fmt.Errorf("no blessings found")
+ }
+ if flagBlessingsName {
+ fmt.Println(blessings)
+ return nil
+ } else if flagBlessingsRootKey {
+ wire, err := blessings2wire(blessings)
+ if err != nil {
+ return err
+ }
+ fmt.Println(rootkey(wire.CertificateChains[0]))
+ return nil
+ }
+ return dumpBlessings(blessings)
+}
+
func read(fname string) (string, error) {
if len(fname) == 0 {
return "", nil
diff --git a/cmd/principal/principal_v23_test.go b/cmd/principal/principal_v23_test.go
index 952bec0..1f3cef1 100644
--- a/cmd/principal/principal_v23_test.go
+++ b/cmd/principal/principal_v23_test.go
@@ -116,12 +116,51 @@
got := removePublicKeys(bin.WithEnv(blessEnv).Start("dump").Output())
want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
-Default blessings: alice
-Peer pattern : Blessings
-... : alice
+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]
+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 V23TestGetRecognizedRoots(t *v23tests.T) {
+ var (
+ outputDir = t.NewTempDir()
+ bin = t.BuildGoPkg("v.io/x/ref/cmd/principal")
+ aliceDir = filepath.Join(outputDir, "alice")
+ )
+
+ bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
+
+ blessEnv := credEnv(aliceDir)
+ got := removePublicKeys(bin.WithEnv(blessEnv).Start("get", "recognizedroots").Output())
+ 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 V23TestGetPeermap(t *v23tests.T) {
+ var (
+ outputDir = t.NewTempDir()
+ bin = t.BuildGoPkg("v.io/x/ref/cmd/principal")
+ aliceDir = filepath.Join(outputDir, "alice")
+ )
+
+ bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
+
+ blessEnv := credEnv(aliceDir)
+ got := removePublicKeys(bin.WithEnv(blessEnv).Start("get", "peermap").Output())
+ want := `Default Blessings alice
+Peer pattern Blessings
+... alice
`
if want != got {
t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
@@ -225,16 +264,16 @@
got := removePublicKeys(bin.Start("--v23.credentials="+carolDir, "dump").Output())
want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
-Default blessings: alice/friend/carol
-Peer pattern : Blessings
-... : alice/friend/carol
-alice : alice/friend/carol/foralice
-bob : bob/friend/carol/forbob
+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]
+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)
@@ -263,12 +302,12 @@
got := removePublicKeys(bin.Start("--v23.credentials="+alicePhoneDir, "dump").Output())
want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
-Default blessings: alice/phone
-Peer pattern : Blessings
-... : alice/phone
+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]
+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)
@@ -298,12 +337,12 @@
got := removePublicKeys(bin.Start("--v23.credentials="+alicePhoneCalendarDir, "dump").Output())
want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
-Default blessings: alice/phone/calendar
-Peer pattern : Blessings
-... : alice/phone/calendar
+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]
+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)
@@ -507,24 +546,16 @@
// Have bob create a "bob/friend" blessing and have alice recognize that.
redirect(t, bin.Start("--v23.credentials="+bobDir, "bless", "--require-caveats=false", aliceDir, "friend"), blessingFile)
bin.Start("--v23.credentials="+aliceDir, "addtoroots", blessingFile).WaitOrDie(os.Stdout, os.Stderr)
- var (
- // blessing roots lines that should match the keys
- aliceLine = fmt.Sprintf("%v : [alice]", publicKey(aliceDir))
- bobLine = fmt.Sprintf("%v : [bob]", publicKey(bobDir))
- foundAlice, foundBob bool
- )
- // Finally dump alice's principal, it should have lines corresponding to aliceLine and bobLine.
- output := bin.Start("--v23.credentials="+aliceDir, "dump").Output()
- for _, line := range strings.Split(output, "\n") {
- if line == aliceLine {
- foundAlice = true
- } else if line == bobLine {
- foundBob = true
- }
- }
- if !foundAlice || !foundBob {
- t.Fatalf("Got:\n%v\n\nExpected Blessing Roots to include:\n%s\n%s", output, aliceLine, bobLine)
+ 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 := bin.Start("--v23.credentials="+aliceDir, "get", "recognizedroots").Output()
+ if got != want {
+ t.Fatalf("Got:\n%v\n\nWant:\n%v", got, want)
}
}
@@ -545,7 +576,7 @@
bin.Start("--v23.credentials="+aliceDir, "addtoroots", "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9iRjaFDoGJI9tarUwWqIW31ti72krThkYByn1v9Lf89D9VA0Mg2oUL7FDDM7qxjZcVM1ktM_W4tBfMVuRZmVCA==", "some_other_provider").WaitOrDie(os.Stdout, os.Stderr)
// "foo" should appear in the set of BlessingRoots
output := bin.Start("--v23.credentials="+aliceDir, "dump").Output()
- want := fmt.Sprintf("41:26:f6:aa:54:f9:31:d4:9d:1f:d2:69:c6:c5:50:70 : [some_other_provider]")
+ want := fmt.Sprintf("41:26:f6:aa:54:f9:31:d4:9d:1f:d2:69:c6:c5:50:70 [some_other_provider]")
for _, line := range strings.Split(output, "\n") {
if line == want {
return
diff --git a/cmd/principal/v23_test.go b/cmd/principal/v23_test.go
index c0d9525..dddc89a 100644
--- a/cmd/principal/v23_test.go
+++ b/cmd/principal/v23_test.go
@@ -32,6 +32,14 @@
v23tests.RunTest(t, V23TestDump)
}
+func TestV23GetRecognizedRoots(t *testing.T) {
+ v23tests.RunTest(t, V23TestGetRecognizedRoots)
+}
+
+func TestV23GetPeermap(t *testing.T) {
+ v23tests.RunTest(t, V23TestGetPeermap)
+}
+
func TestV23RecvBlessings(t *testing.T) {
v23tests.RunTest(t, V23TestRecvBlessings)
}
diff --git a/lib/security/blessingroots.go b/lib/security/blessingroots.go
index 009df29..6ecd0ba 100644
--- a/lib/security/blessingroots.go
+++ b/lib/security/blessingroots.go
@@ -74,12 +74,12 @@
// DebugString encodes all roots into a string in the following
// format
//
-// Public key : Pattern
-// <public key> : <patterns>
+// Public key Pattern
+// <public key> <patterns>
// ...
-// <public key> : <patterns>
+// <public key> <patterns>
func (br *blessingRoots) DebugString() string {
- const format = "%-47s : %s\n"
+ const format = "%-47s %s\n"
b := bytes.NewBufferString(fmt.Sprintf(format, "Public key", "Pattern"))
var s rootSorter
for keyBytes, patterns := range br.store {
diff --git a/lib/security/blessingstore.go b/lib/security/blessingstore.go
index bebf3ef..381ec87 100644
--- a/lib/security/blessingstore.go
+++ b/lib/security/blessingstore.go
@@ -150,15 +150,14 @@
// DebugString return a human-readable string encoding of the store
// in the following format
-// Default blessing : <Default blessing of the store>
-//
-// Peer pattern : Blessings
-// <pattern> : <blessings>
+// Default Blessings <blessings>
+// Peer pattern Blessings
+// <pattern> <blessings>
// ...
-// <pattern> : <blessings>
+// <pattern> <blessings>
func (bs *blessingStore) DebugString() string {
- const format = "%-30s : %s\n"
- b := bytes.NewBufferString(fmt.Sprintf("Default blessings: %v\n", bs.state.Default.Blessings()))
+ const format = "%-30s %s\n"
+ b := bytes.NewBufferString(fmt.Sprintf(format, "Default Blessings", bs.state.Default.Blessings()))
b.WriteString(fmt.Sprintf(format, "Peer pattern", "Blessings"))
diff --git a/services/device/internal/impl/impl_test.go b/services/device/internal/impl/impl_test.go
index ed8ce4f..fd22cce 100644
--- a/services/device/internal/impl/impl_test.go
+++ b/services/device/internal/impl/impl_test.go
@@ -685,7 +685,8 @@
}
instanceDebug := debug(t, ctx, appID, instance1ID)
- if !strings.Contains(instanceDebug, fmt.Sprintf("Blessing Store: Default blessings: %s/forapp", test.TestBlessing)) {
+ // Verify the apps default blessings.
+ if !strings.Contains(instanceDebug, fmt.Sprintf("Default Blessings %s/forapp", test.TestBlessing)) {
t.Fatalf("debug response doesn't contain expected info: %v", instanceDebug)
}
diff --git a/services/device/mgmt_v23_test.go b/services/device/mgmt_v23_test.go
index 7fc2c4f..cead99a 100644
--- a/services/device/mgmt_v23_test.go
+++ b/services/device/mgmt_v23_test.go
@@ -177,7 +177,7 @@
// Verify the device's default blessing is as expected.
inv := debugBin.Start("stats", "read", mtName+"/devmgr/__debug/stats/security/principal/*/blessingstore")
- inv.ExpectRE(".*Default blessings: root/alice/myworkstation$", -1)
+ inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+root/alice/myworkstation$")
// Get the device's profile, which should be set to non-empty string
inv = deviceBin.Start("describe", mtName+"/devmgr/device")
@@ -268,7 +268,7 @@
// Verify the app's default blessing.
inv = debugBin.Start("stats", "read", instanceName+"/stats/security/principal/*/blessingstore")
- inv.ExpectRE(".*Default blessings: root/alice/myapp$", -1)
+ inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+root/alice/myapp$")
// Stop the instance
deviceBin.Run("stop", instanceName)