Skip to content
File

Blob: cmd/client/token.go

go130 lines
1package client
2 
3import (
4 "context"
5 "fmt"
6 "net"
7 "time"
8 
9 "go.miragespace.co/specter/tun/client"
10 "go.miragespace.co/specter/tun/client/dialer"
11 
12 "github.com/quic-go/quic-go"
13 "github.com/urfave/cli/v3"
14 "go.uber.org/zap"
15)
16 
17func tokenCommand() *cli.Command {
18 config := func() cli.Flag {
19 return &cli.StringFlag{
20 Name: "config",
21 Aliases: []string{"c"},
22 Required: true,
23 Usage: "owner configuration file; stop the owner client before using these commands",
24 }
25 }
26 return &cli.Command{
27 Name: "token",
28 Usage: "Manage domain tokens with a stopped owner client",
29 Commands: []*cli.Command{
30 {
31 Name: "mint",
32 Usage: "Mint a bearer token for a registered hostname",
33 ArgsUsage: "<hostname>",
34 Flags: []cli.Flag{config(), &cli.StringFlag{
35 Name: "expires-at",
36 Usage: "optional expiry in RFC3339 format (default: no expiry)",
37 }},
38 Action: cmdToken,
39 },
40 {
41 Name: "list",
42 Usage: "List grants, including incomplete grants",
43 ArgsUsage: " ",
44 Flags: []cli.Flag{config()},
45 Action: cmdToken,
46 },
47 {
48 Name: "revoke",
49 Usage: "Revoke a grant by its ID",
50 ArgsUsage: "<id>",
51 Flags: []cli.Flag{config()},
52 Action: cmdToken,
53 },
54 },
55 }
56}
57 
58func cmdToken(ctx context.Context, cmd *cli.Command) error {
59 expected := 1
60 if cmd.Name == "list" {
61 expected = 0
62 }
63 if cmd.Args().Len() != expected {
64 return fmt.Errorf("token %s expects %d argument(s)", cmd.Name, expected)
65 }
66 var expiry time.Time
67 if cmd.String("expires-at") != "" {
68 parsed, err := time.Parse(time.RFC3339, cmd.String("expires-at"))
69 if err != nil {
70 return fmt.Errorf("expires-at must be RFC3339: %w", err)
71 }
72 expiry = parsed
73 }
74 cfg, err := client.NewConfig(cmd.String("config"))
75 if err != nil {
76 return err
77 }
78 apex, err := dialer.ParseApex(cfg.Apex)
79 if err != nil {
80 return err
81 }
82 listener, err := net.ListenPacket("udp", ":0")
83 if err != nil {
84 return err
85 }
86 defer listener.Close()
87 quicTransport := &quic.Transport{Conn: listener}
88 defer quicTransport.Close()
89 logger := cmd.Root().Metadata["logger"].(*zap.Logger)
90 _, tp := createTransport(cmd, transportCfg{
91 logger: logger,
92 quicTp: quicTransport,
93 apex: apex,
94 })
95 defer tp.Stop()
96 c, err := client.NewClient(ctx, client.ClientConfig{
97 Logger: logger,
98 Configuration: cfg,
99 ServerTransport: tp,
100 })
101 if err != nil {
102 return err
103 }
104 defer c.Close()
105 if err := c.Register(ctx); err != nil {
106 return err
107 }
108 switch cmd.Name {
109 case "mint":
110 resp, err := c.MintDelegation(ctx, cmd.Args().First(), expiry)
111 if err != nil {
112 return err
113 }
114 return c.FormatMintedDelegation(resp, cmd.Root().Writer)
115 case "list":
116 resp, err := c.ListDelegations(ctx)
117 if err != nil {
118 return err
119 }
120 return c.FormatDelegations(resp, cmd.Root().Writer)
121 case "revoke":
122 resp, err := c.RevokeDelegation(ctx, cmd.Args().First())
123 if err != nil {
124 return err
125 }
126 return c.FormatRevokedDelegation(resp, cmd.Root().Writer)
127 }
128 return nil
129}