Commit 253e1a2c23

253e1a2c233388e196f3db55aca93a26605c2ab7

parent: a8ba660761

Verified · cmc ci/build: success ci/test: success

cmc <hello@cleberg.net> · 2026-09-28 08:17 UTC

token: create refuses a zero or negative --ttl

Ref #277

Layout: unified · split

internal/control/token.go +4 −9
@@ -69,21 +69,16 @@ func runTokenCreate(c *Ctx, args []string) int {
69 if err != nil { 69 if err != nil {
70 return c.fail(protocol.ExitUsage, "%v", err) 70 return c.fail(protocol.ExitUsage, "%v", err)
71 } 71 }
72 name, scope, ttl := f.Value("--name"), "read", f.Value("--ttl") 72 name, scope := f.Value("--name"), "read"
73 if f.Has("--scope") { 73 if f.Has("--scope") {
74 scope = f.Value("--scope") 74 scope = f.Value("--scope")
75 } 75 }
76 if name == "" || (scope != "full" && scope != "read") { 76 if name == "" || (scope != "full" && scope != "read") {
77 return c.usage() 77 return c.usage()
78 } 78 }
79 var expires *time.Time 79 expires, code := c.ttlFlag(f)
80 if ttl != "" { 80 if code >= 0 {
81 d, err := parseTTL(ttl) 81 return code
82 if err != nil {
83 return c.failInput(err)
84 }
85 t := time.Now().Add(d)
86 expires = &t
87 } 82 }
88 raw, _, err := store.NewToken() 83 raw, _, err := store.NewToken()
89 if err != nil { 84 if err != nil {
internal/control/token_test.go +14
@@ -102,3 +102,17 @@ func TestTokenCreateDefaultsToReadAndRecordsCreator(t *testing.T) {
102 t.Fatal(`no token named "child"`) 102 t.Fatal(`no token named "child"`)
103 } 103 }
104} 104}
105
106func TestTokenCreateRefusesNonPositiveTTL(t *testing.T) {
107 st, _, uid := newQueueTestRepo(t)
108 for _, ttl := range []string{"0s", "-1h"} {
109 c, _ := pruneCtx(st, t.TempDir(), store.User{ID: uid, Username: "alice"})
110 c.Cfg.Limits.WriteRate = -1
111 if code := Dispatch(c, []string{"token", "create", "--name", "x", "--ttl", ttl}); code != protocol.ExitUsage {
112 t.Errorf("--ttl %s: exit %d", ttl, code)
113 }
114 }
115 if toks, err := st.ListAPITokens(uid); err != nil || len(toks) != 0 {
116 t.Fatalf("tokens: %+v %v", toks, err)
117 }
118}