github.com/grailbio/base@v0.0.11/cmd/grail-role/main.go (about)

     1  // The following enables go generate to generate the doc.go file.
     2  //go:generate go run v.io/x/lib/cmdline/gendoc "--build-cmd=go install" --copyright-notice= . -help
     3  
     4  package main
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/base64"
     9  	"fmt"
    10  	"time"
    11  
    12  	_ "github.com/grailbio/base/cmdutil/interactive"
    13  	"github.com/grailbio/base/security/ticket"
    14  	_ "github.com/grailbio/v23/factories/grail" // Needed to initialize v23
    15  	"v.io/v23"
    16  	"v.io/v23/context"
    17  	"v.io/v23/security"
    18  	"v.io/v23/vom"
    19  	"v.io/x/lib/cmdline"
    20  	"v.io/x/lib/vlog"
    21  	libsecurity "v.io/x/ref/lib/security"
    22  	"v.io/x/ref/lib/v23cmd"
    23  )
    24  
    25  const blessingSuffix = "_role"
    26  
    27  var (
    28  	durationFlag time.Duration
    29  	timeoutFlag  time.Duration
    30  )
    31  
    32  func newCmdRoot() *cmdline.Command {
    33  	cmd := &cmdline.Command{
    34  		Runner: v23cmd.RunnerFunc(run),
    35  		Name:   "role",
    36  		Short:  "Creates credentials for a role account",
    37  		Long: `
    38  Command role creates Vanadium principals for a Vanadium role account. This is
    39  accomplished by fetching a VanadiumTicket from the ticket-server. The
    40  ticket-server will bless the principal presented by the client so
    41  the blessing presented to the ticket-server is required to have a ':_role'
    42  prefix to prevent the accidental reuse of the original private key of the
    43  client.
    44  
    45  Example:
    46  
    47    grail role tickets/roles/lims-server /tmp/lims-server
    48  `,
    49  		ArgsName: "<ticket> <directory>",
    50  	}
    51  	cmd.Flags.DurationVar(&durationFlag, "duration", 1*time.Hour, "Duration for the blessing.")
    52  	cmd.Flags.DurationVar(&timeoutFlag, "timeout", 10*time.Second, "The timeout of the requests to the server.")
    53  	return cmd
    54  }
    55  
    56  func run(ctx *context.T, env *cmdline.Env, args []string) error {
    57  	if len(args) != 2 {
    58  		return fmt.Errorf("Exactly two arguments are required: <ticket> <directory>")
    59  	}
    60  	ticketPath, dir := args[0], args[1]
    61  
    62  	principal, err := libsecurity.CreatePersistentPrincipal(dir, nil)
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	name, err := bless(ctx, principal, blessingSuffix)
    68  	if err != nil {
    69  		return err
    70  	}
    71  
    72  	if err := principal.BlessingStore().SetDefault(name); err != nil {
    73  		return err
    74  	}
    75  	if _, err := principal.BlessingStore().Set(name, security.AllPrincipals); err != nil {
    76  		return err
    77  	}
    78  	if err := security.AddToRoots(principal, name); err != nil {
    79  		return err
    80  	}
    81  
    82  	vlog.Infof("\n%s", principal.BlessingStore().DebugString())
    83  
    84  	roleCtx, err := v23.WithPrincipal(ctx, principal)
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	client := ticket.TicketServiceClient(ticketPath)
    90  	_, cancel := context.WithTimeout(roleCtx, timeoutFlag)
    91  	defer cancel()
    92  
    93  	t, err := client.Get(roleCtx)
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	vlog.VI(1).Infof("%#v\n", t)
    99  
   100  	vanadiumTicket, ok := t.(ticket.TicketVanadiumTicket)
   101  	if !ok {
   102  		return fmt.Errorf("Not a VanadiumTicket: %#v", t)
   103  	}
   104  
   105  	var blessings security.Blessings
   106  	if err := base64urlVomDecode(vanadiumTicket.Value.Blessing, &blessings); err != nil {
   107  		return err
   108  	}
   109  
   110  	vlog.Info(blessings)
   111  
   112  	if err := principal.BlessingStore().SetDefault(blessings); err != nil {
   113  		return err
   114  	}
   115  	if _, err := principal.BlessingStore().Set(blessings, "..."); err != nil {
   116  		return err
   117  	}
   118  	if err := security.AddToRoots(principal, blessings); err != nil {
   119  		return fmt.Errorf("failed to add blessings to recognized roots: %v", err)
   120  	}
   121  
   122  	fmt.Printf("Public key: %s\n", principal.PublicKey())
   123  	fmt.Println("---------------- BlessingStore ----------------")
   124  	fmt.Print(principal.BlessingStore().DebugString())
   125  	fmt.Println("---------------- BlessingRoots ----------------")
   126  	fmt.Print(principal.Roots().DebugString())
   127  
   128  	return nil
   129  }
   130  
   131  func bless(ctx *context.T, p security.Principal, name string) (security.Blessings, error) {
   132  	caveat, err := security.NewExpiryCaveat(time.Now().Add(durationFlag))
   133  	if err != nil {
   134  		ctx.Errorf("Couldn't create caveat")
   135  		return security.Blessings{}, err
   136  	}
   137  	rp := v23.GetPrincipal(ctx)
   138  	rblessing, _ := rp.BlessingStore().Default()
   139  	return rp.Bless(p.PublicKey(), rblessing, name, caveat)
   140  }
   141  
   142  func base64urlVomDecode(s string, i interface{}) error {
   143  	b, err := base64.URLEncoding.DecodeString(s)
   144  	if err != nil {
   145  		return err
   146  	}
   147  	dec := vom.NewDecoder(bytes.NewBuffer(b))
   148  	return dec.Decode(i)
   149  }
   150  
   151  func main() {
   152  	cmdline.HideGlobalFlagsExcept()
   153  	cmdline.Main(newCmdRoot())
   154  }