"veyron/tools/principal": Principal Tool (part 2)

Add the store.setdefault and store.set commands, and associated
tests.

Change-Id: I8222b1847df369032f4028975bf20290ba61e066
diff --git a/tools/principal/main.go b/tools/principal/main.go
index d8e4320..51a9181 100644
--- a/tools/principal/main.go
+++ b/tools/principal/main.go
@@ -63,7 +63,6 @@
 machine and the name of the user running this command.
 `,
 		Run: func(cmd *cmdline.Command, args []string) error {
-			r := rt.R()
 			var name string
 			switch len(args) {
 			case 0:
@@ -78,11 +77,11 @@
 			if flagBlessFor != 0 {
 				cav, err := security.ExpiryCaveat(time.Now().Add(flagBlessFor))
 				if err != nil {
-					return fmt.Errorf("failed to create expiration caveat: %v", err)
+					return fmt.Errorf("failed to create expiration Caveat: %v", err)
 				}
 				caveats = append(caveats, cav)
 			}
-			blessing, err := r.Principal().BlessSelf(name, caveats...)
+			blessing, err := rt.R().Principal().BlessSelf(name, caveats...)
 			if err != nil {
 				return fmt.Errorf("failed to create self-signed blessing for name %q: %v", name, err)
 			}
@@ -124,6 +123,78 @@
 			return dumpBlessings(rt.R().Principal().BlessingStore().Default())
 		},
 	}
+
+	cmdSet = &cmdline.Command{
+		Name:  "store.set",
+		Short: "Set provided blessings for peer",
+		Long: `
+Marks the provided blessings to be shared with the provided
+peers on the BlessingStore specified by the environment
+(VEYRON_CREDENTIALS) that this tool is running in.
+
+'set b pattern' marks the intention to reveal b to peers who
+present blessings of their own matching 'pattern'.
+
+'set nil pattern' can be used to remove the blessings previously
+associated with the pattern (by a prior 'set' command).
+
+It is an error to call 'store.set' with blessings whose public
+key does not match the public key of this principal specified
+by the environment.
+`,
+		ArgsName: "<file> <pattern>",
+		ArgsLong: `
+<file> is the path to a file containing a blessing typically obtained
+from this tool. - is used for STDIN.
+
+<pattern> is the BlessingPattern used to identify peers with whom this
+blessing can be shared with.
+`,
+		Run: func(cmd *cmdline.Command, args []string) error {
+			if len(args) != 2 {
+				return fmt.Errorf("requires exactly two arguments <file>, <pattern>, provided %d", cmd.Name, len(args))
+			}
+			blessings, err := decodeBlessings(args[0])
+			if err != nil {
+				return fmt.Errorf("failed to decode provided blessings: %v", err)
+			}
+			pattern := security.BlessingPattern(args[1])
+			if _, err := rt.R().Principal().BlessingStore().Set(blessings, pattern); err != nil {
+				return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
+			}
+			return nil
+		},
+	}
+
+	cmdSetDefault = &cmdline.Command{
+		Name:  "store.setdefault",
+		Short: "Set provided blessings as default",
+		Long: `
+Sets the provided blessings as default in the BlessingStore specified
+by the environment (VEYRON_CREDENTIALS) that this tool is running in.
+
+It is an error to call 'store.setdefault' with blessings whose public key
+does not match the public key of the principal specified by the environment.
+`,
+		ArgsName: "<file>",
+		ArgsLong: `
+<file> is the path to a file containing a blessing typically obtained from
+this tool. - is used for STDIN.
+`,
+		Run: func(cmd *cmdline.Command, args []string) error {
+			if len(args) != 1 {
+				return fmt.Errorf("requires exactly one argument, <file>, provided %d", cmd.Name, len(args))
+			}
+			blessings, err := decodeBlessings(args[0])
+			if err != nil {
+				return fmt.Errorf("failed to decode provided blessings: %v", err)
+			}
+			if err := rt.R().Principal().BlessingStore().SetDefault(blessings); err != nil {
+				return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
+			}
+			return nil
+		},
+	}
 )
 
 func main() {
@@ -144,7 +215,7 @@
 
 All objects are printed using base64-VOM-encoding.
 `,
-		Children: []*cmdline.Command{cmdPrint, cmdBlessSelf, cmdDefault, cmdForPeer},
+		Children: []*cmdline.Command{cmdPrint, cmdBlessSelf, cmdDefault, cmdForPeer, cmdSetDefault, cmdSet},
 	}).Main()
 }
 
diff --git a/tools/principal/test.sh b/tools/principal/test.sh
index ddf27e9..3041116 100755
--- a/tools/principal/test.sh
+++ b/tools/principal/test.sh
@@ -34,18 +34,64 @@
   if [ "${GOT}" != "${WANT}" ]; then
     shell_test::fail "line ${LINENO}: Got ${GOT}, want ${WANT}"
   fi
-
   local GOT=$(./principal blessself bob | ./principal print - | extractBlessings)
   local WANT="bob(0 caveats)"
   if [ "${GOT}" != "${WANT}" ]; then
     shell_test::fail "line ${LINENO}: Got ${GOT}, want ${WANT}"
   fi
-
   local GOT=$(./principal blessself --for=1h bob| ./principal print - | extractBlessings)
   local WANT="bob(1 caveats)"
   if [ "${GOT}" != "${WANT}" ]; then
     shell_test::fail "line ${LINENO}: Got ${GOT}, want ${WANT}"
   fi
+
+  # Test store.default, store.setdefault
+  ./principal blessself testFile >f || shell_test::fail "line ${LINENO}: blessself testFile failed"
+  ./principal store.setdefault f || shell_test::fail "line ${LINENO}: store.setdefault failed"
+  local GOT=$(./principal store.default | ./principal print - | extractBlessings)
+  local WANT="testFile(0 caveats)"
+  if [ "${GOT}" != "${WANT}" ]; then
+    shell_test::fail "line ${LINENO}: Got ${GOT}, want ${WANT}"
+  fi
+
+  ./principal blessself testStdin | ./principal store.setdefault - || shell_test::fail "line ${LINENO}: blessself testStdin | store.setdefault - failed"
+  local GOT=$(./principal store.default | ./principal print - | extractBlessings)
+  local WANT="testStdin(0 caveats)"
+  if [ "${GOT}" != "${WANT}" ]; then
+    shell_test::fail "line ${LINENO}: Got ${GOT}, want ${WANT}"
+  fi
+
+  # Test store.forpeer, store.set
+  ./principal blessself forAlice >f || shell_test::fail "line ${LINENO}: blessself forAlice failed"
+  ./principal store.set f alice/... || shell_test::fail "line ${LINENO}: store.set failed"
+
+  ./principal blessself forAll | ./principal store.set - ... || shell_test::fail "line ${LINENO}: blessself forAll | store.set - ... failed"
+
+  local GOT=$(./principal store.forpeer | ./principal print - | extractBlessings)
+  local WANT="forAll(0 caveats)"
+  if [ "${GOT}" != "${WANT}" ]; then
+    shell_test::fail "line ${LINENO}: Got ${GOT}, want ${WANT}"
+  fi
+  local GOT=$(./principal store.forpeer bob | ./principal print - | extractBlessings)
+  local WANT="forAll(0 caveats)"
+  if [ "${GOT}" != "${WANT}" ]; then
+    shell_test::fail "line ${LINENO}: Got ${GOT}, want ${WANT}"
+  fi
+  local GOT=$(./principal store.forpeer alice | ./principal print - | extractBlessings)
+  local WANT="forAlice(0 caveats)#forAll(0 caveats)"
+  if [ "${GOT}" != "${WANT}" ]; then
+    shell_test::fail "line ${LINENO}: Got ${GOT}, want ${WANT}"
+  fi
+  local GOT=$(./principal store.forpeer alice/friend | ./principal print - | extractBlessings)
+  local WANT="forAlice(0 caveats)#forAll(0 caveats)"
+  if [ "${GOT}" != "${WANT}" ]; then
+    shell_test::fail "line ${LINENO}: Got ${GOT}, want ${WANT}"
+  fi
+  local GOT=$(./principal store.forpeer alice/friend bob/spouse | ./principal print - | extractBlessings)
+  local WANT="forAlice(0 caveats)#forAll(0 caveats)"
+  if [ "${GOT}" != "${WANT}" ]; then
+    shell_test::fail "line ${LINENO}: Got ${GOT}, want ${WANT}"
+  fi
   shell_test::pass
 }