Commit 2ddf2389c0
Verified · cmc
Layout: unified · split
internal/control/token.go +2 −10
| @@ -122,19 +122,11 @@ func runTokenList(c *Ctx, args []string) int { | |||
| 122 | for _, t := range tokens { | 122 | for _, t := range tokens { |
| 123 | ds = append(ds, out{t.Name, t.Scope, t.CreatedAt, t.ExpiresAt, t.LastUsedAt, t.CreatedBy}) | 123 | ds = append(ds, out{t.Name, t.Scope, t.CreatedAt, t.ExpiresAt, t.LastUsedAt, t.CreatedBy}) |
| 124 | } | 124 | } |
| 125 | now := time.Now() | ||
| 125 | return c.emit(ds, func(w io.Writer) { | 126 | return c.emit(ds, func(w io.Writer) { |
| 126 | tb := c.table(w, "NAME", "SCOPE", "EXPIRES") | 127 | tb := c.table(w, "NAME", "SCOPE", "EXPIRES") |
| 127 | for _, d := range ds { | 128 | for _, d := range ds { |
| 128 | exp := "never expires" | 129 | tb.row(cRef(d.Name), cState(d.Scope), cText(expiresText(d.ExpiresAt, now))) |
| 129 | if d.ExpiresAt != nil { | ||
| 130 | ts := d.ExpiresAt.UTC().Format(time.RFC3339Nano) | ||
| 131 | if c.Term.Cols == 0 { | ||
| 132 | exp = "expires " + stamp(ts) | ||
| 133 | } else { | ||
| 134 | exp = "expires " + relAge(ts, termNow()) | ||
| 135 | } | ||
| 136 | } | ||
| 137 | tb.row(cRef(d.Name), cState(d.Scope), cText(exp)) | ||
| 138 | } | 130 | } |
| 139 | tb.flush() | 131 | tb.flush() |
| 140 | }) | 132 | }) |
internal/control/token_test.go +24
| @@ -45,6 +45,30 @@ func TestExpiringCredentialCannotMint(t *testing.T) { | |||
| 45 | } | 45 | } |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | // #286: at a terminal, a token expiring in the future must not render | ||
| 49 | // through relAge, which clamps a future time to zero and prints | ||
| 50 | // "expires just now". | ||
| 51 | func TestTokenListFutureExpiryAtTerminal(t *testing.T) { | ||
| 52 | st, _, uid := newQueueTestRepo(t) | ||
| 53 | exp := time.Now().Add(90 * 24 * time.Hour) | ||
| 54 | if err := st.CreateAPIToken(uid, "laptop", "h-laptop", "read", &exp, 0); err != nil { | ||
| 55 | t.Fatal(err) | ||
| 56 | } | ||
| 57 | c, errOut := pruneCtx(st, t.TempDir(), store.User{ID: uid, Username: "alice"}) | ||
| 58 | c.Term = Term{Cols: 80} | ||
| 59 | var out bytes.Buffer | ||
| 60 | c.Stdout = &out | ||
| 61 | if code := Dispatch(c, []string{"token", "list"}); code != protocol.ExitOK { | ||
| 62 | t.Fatalf("exit %d: %s", code, errOut) | ||
| 63 | } | ||
| 64 | if strings.Contains(out.String(), "just now") { | ||
| 65 | t.Fatalf("token list at a terminal printed \"just now\" for a future expiry:\n%s", out.String()) | ||
| 66 | } | ||
| 67 | if !strings.Contains(out.String(), "expires ") { | ||
| 68 | t.Fatalf("token list:\n%s", out.String()) | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 48 | func TestTokenCreateDefaultsToReadAndRecordsCreator(t *testing.T) { | 72 | func TestTokenCreateDefaultsToReadAndRecordsCreator(t *testing.T) { |
| 49 | st, _, uid := newQueueTestRepo(t) | 73 | st, _, uid := newQueueTestRepo(t) |
| 50 | if err := st.CreateAPIToken(uid, "parent", "h-parent", "full", nil, 0); err != nil { | 74 | if err := st.CreateAPIToken(uid, "parent", "h-parent", "full", nil, 0); err != nil { |