"veyron/tools/principal": Principal Tool
This CL adds a "principal" tool (along the lines of the "identity"
tool) to carry out principal operations. This CL adds support
for the command "blessself", "store.forpeer", "store.default" and
"print"
Change-Id: I6958684c31586377f6529239a90e766ff863531c
diff --git a/tools/principal/main.go b/tools/principal/main.go
new file mode 100644
index 0000000..d8e4320
--- /dev/null
+++ b/tools/principal/main.go
@@ -0,0 +1,212 @@
+package main
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "os/user"
+ "time"
+
+ "veyron.io/veyron/veyron/lib/cmdline"
+ "veyron.io/veyron/veyron/services/identity/util"
+
+ "veyron.io/veyron/veyron2/rt"
+ "veyron.io/veyron/veyron2/security"
+)
+
+var (
+ // Flags for the "blessself" command
+ flagBlessFor time.Duration
+ flagAddForPeer string
+
+ cmdPrint = &cmdline.Command{
+ Name: "print",
+ Short: "Print out information about the provided blessing",
+ Long: `
+Prints out information about the blessing (typically obtained from this tool)
+encoded in the provided file.
+`,
+ 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", len(args))
+ }
+ blessings, err := decodeBlessings(args[0])
+ if err != nil {
+ return fmt.Errorf("failed to decode provided blessings: %v", err)
+ }
+ fmt.Printf("Blessings: %v\n", blessings)
+ fmt.Printf("PublicKey: %v\n", blessings.PublicKey())
+ return nil
+ },
+ }
+
+ cmdBlessSelf = &cmdline.Command{
+ Name: "blessself",
+ Short: "Generate a self-signed blessing",
+ Long: `
+Returns a blessing with name <name> and self-signed by the principal
+specified by the environment (VEYRON_CREDENTIALS) that this tool is
+running in. Optionally, the blessing can be restricted with an expiry
+caveat specified using the --for flag.
+`,
+ ArgsName: "[<name>]",
+ ArgsLong: `
+<name> is the name used to create the self-signed blessing. If not
+specified, a name will be generated based on the hostname of the
+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:
+ name = defaultBlessingName()
+ case 1:
+ name = args[0]
+ default:
+ return fmt.Errorf("requires at most one argument, provided %d", len(args))
+ }
+
+ var caveats []security.Caveat
+ if flagBlessFor != 0 {
+ cav, err := security.ExpiryCaveat(time.Now().Add(flagBlessFor))
+ if err != nil {
+ return fmt.Errorf("failed to create expiration caveat: %v", err)
+ }
+ caveats = append(caveats, cav)
+ }
+ blessing, err := r.Principal().BlessSelf(name, caveats...)
+ if err != nil {
+ return fmt.Errorf("failed to create self-signed blessing for name %q: %v", name, err)
+ }
+
+ return dumpBlessings(blessing)
+ },
+ }
+
+ cmdForPeer = &cmdline.Command{
+ Name: "store.forpeer",
+ Short: "Return blessings marked for the provided peer",
+ Long: `
+Returns blessings that are marked for the provided peer in the
+BlessingStore specified by the environment (VEYRON_CREDENTIALS)
+that this tool is running in.
+`,
+ ArgsName: "[<peer_1> ... <peer_k>]",
+ ArgsLong: `
+<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 least one of these. If no arguments are specified,
+store.forpeer returns the blessings that are marked for all peers (i.e.,
+blessings set on the store with the "..." pattern).
+`,
+ Run: func(cmd *cmdline.Command, args []string) error {
+ return dumpBlessings(rt.R().Principal().BlessingStore().ForPeer(args...))
+ },
+ }
+
+ cmdDefault = &cmdline.Command{
+ Name: "store.default",
+ Short: "Return blessings marked as default",
+ Long: `
+Returns blessings that are marked as default in the BlessingStore
+specified by the environment (VEYRON_CREDENTIALS) that this tool
+is running in.
+`,
+ Run: func(cmd *cmdline.Command, args []string) error {
+ return dumpBlessings(rt.R().Principal().BlessingStore().Default())
+ },
+ }
+)
+
+func main() {
+ if len(os.Getenv("VEYRON_CREDENTIALS")) == 0 {
+ // TODO(ataly, ashankar): Handle this case
+ fmt.Fprintf(os.Stderr, "ERROR: Please set the VEYRON_CREDENTIALS environment variable\n")
+ os.Exit(2)
+ }
+ rt.Init()
+ cmdBlessSelf.Flags.DurationVar(&flagBlessFor, "for", 0*time.Hour, "Expiry time of Blessing (optional)")
+
+ (&cmdline.Command{
+ Name: "principal",
+ Short: "Create and manage veyron principals",
+ Long: `
+The principal tool helps create and manage blessings and the set of trusted
+roots bound to a principal.
+
+All objects are printed using base64-VOM-encoding.
+`,
+ Children: []*cmdline.Command{cmdPrint, cmdBlessSelf, cmdDefault, cmdForPeer},
+ }).Main()
+}
+
+func decodeBlessings(fname string) (security.Blessings, error) {
+ var wire security.WireBlessings
+ if err := decode(fname, &wire); err != nil {
+ return nil, err
+ }
+ return security.NewBlessings(wire)
+}
+
+func dumpBlessings(blessings security.Blessings) error {
+ if blessings == nil {
+ return errors.New("no blessings found")
+ }
+ str, err := util.Base64VomEncode(blessings)
+ if err != nil {
+ return fmt.Errorf("base64-VOM encoding failed: %v", err)
+ }
+ fmt.Println(str)
+ return nil
+}
+
+func read(fname string) (string, error) {
+ if len(fname) == 0 {
+ return "", nil
+ }
+ f := os.Stdin
+ if fname != "-" {
+ var err error
+ if f, err = os.Open(fname); err != nil {
+ return "", fmt.Errorf("failed to open %q: %v", fname, err)
+ }
+ }
+ defer f.Close()
+ var buf bytes.Buffer
+ if _, err := io.Copy(&buf, f); err != nil {
+ return "", fmt.Errorf("failed to read %q: %v", fname, err)
+ }
+ return buf.String(), nil
+}
+
+func decode(fname string, val interface{}) error {
+ str, err := read(fname)
+ if err != nil {
+ return err
+ }
+ if err := util.Base64VomDecode(str, val); err != nil || val == nil {
+ return fmt.Errorf("failed to decode %q: %v", fname, err)
+ }
+ return nil
+}
+
+func defaultBlessingName() string {
+ var name string
+ if user, _ := user.Current(); user != nil && len(user.Username) > 0 {
+ name = user.Username
+ } else {
+ name = "anonymous"
+ }
+ if host, _ := os.Hostname(); len(host) > 0 {
+ name = name + "@" + host
+ }
+ return name
+}