feat: lazysecrets list prints the account, and open and get take it (--account)

This commit is contained in:
keb committed 2026-10-14 21:24:01 +02:00
1 parent 1f330bc619
commit b05d19209c
9 files changed
+118 -43

No files matched your search

+10 -5
View File
@@ -33,8 +33,12 @@ type Entry struct {
Provider string `json:"provider"`
// Path names it: its source's, vault's and own names.
Path []string `json:"path"`
// Account is the az account it's seen through, when there are several.
Account string `json:"account,omitempty"`
// Account is the provider it's listed through: an az account
// (Azure:<user>/<tenant>), Databricks or Repos. lazysecrets open and
// get take it (--account) to use that account when several could.
Account string `json:"account"`
// AccountLabel names the az account, when there are several.
AccountLabel string `json:"accountLabel,omitempty"`
// Error says why a source's vaults or a vault's secrets couldn't be
// listed. The entry is then that source or vault, again, or with an
// account's sources unreadable, the account without a link.
@@ -186,12 +190,12 @@ func (l *lister) objects(p provider.Provider, c provider.Container, objs []provi
continue
}
l.put(p, Entry{Link: r.String(), Kind: strings.ToLower(string(o.Type)), Provider: kindOf(c.Source.Kind),
Path: []string{c.Source.Name, c.Name, o.Name}, Account: accountOf(c.Source)})
Path: []string{c.Source.Name, c.Name, o.Name}, AccountLabel: accountOf(c.Source)})
}
}
func sourceEntry(src provider.Source) Entry {
e := Entry{Kind: "source", Provider: kindOf(src.Kind), Path: []string{src.Name}, Account: accountOf(src)}
e := Entry{Kind: "source", Provider: kindOf(src.Kind), Path: []string{src.Name}, AccountLabel: accountOf(src)}
if r, err := ref.ForSource(src); err == nil {
e.Link = r.String()
}
@@ -199,7 +203,7 @@ func sourceEntry(src provider.Source) Entry {
}
func containerEntry(c provider.Container) Entry {
e := Entry{Kind: "vault", Provider: kindOf(c.Source.Kind), Path: []string{c.Source.Name, c.Name}, Account: accountOf(c.Source)}
e := Entry{Kind: "vault", Provider: kindOf(c.Source.Kind), Path: []string{c.Source.Name, c.Name}, AccountLabel: accountOf(c.Source)}
if r, err := ref.ForContainer(c, provider.TypeSecret); err == nil {
e.Link = r.String()
}
@@ -212,6 +216,7 @@ func (l *lister) put(p provider.Provider, e Entry) {
if (e.Link == "" || !l.wants(e.Kind)) && e.Error == "" {
return
}
e.Account = p.ID()
id := p.ID() + "\x00" + e.Link + "\x00" + e.Error
if l.stopped != nil || l.seen[id] {
return
+4
View File
@@ -56,6 +56,10 @@ type Ref struct {
// Share is a share link's sealed files, as in the link; the other
// fields are empty. Package share reads it.
Share string
// Account, when set, is the provider (its ID) to open or read it
// with when several could, from lazysecrets list. It isn't part of
// the link.
Account string
}
// IsShare reports whether r is a share link.
+18
View File
@@ -218,6 +218,9 @@ func (m *Model) openSource(r ref.Ref) {
}
}
}
if row, ok := preferredRow(r, choices); ok {
choices = []choice{{sourceRow: row}}
}
switch len(choices) {
case 0:
m.flash = flash{text: "open: " + describeRef(r) + " isn't in your sources", isErr: true}
@@ -410,6 +413,10 @@ func (m *Model) handleResolved(msg resolvedMsg) {
for _, row := range append([]string{msg.sourceRow}, msg.others...) {
choices = append(choices, choice{label: "Open with " + m.sourceLabel(row), sourceRow: row})
}
if row, ok := preferredRow(msg.ref, choices); ok {
m.startLinkNav(msg.ref, msg.act, row)
return
}
m.chooseSource("Several accounts can see "+msg.ref.ContainerName(), msg.ref, msg.act, choices, true)
case msg.linked != nil:
m.addLinked(*msg.linked)
@@ -419,6 +426,17 @@ func (m *Model) handleResolved(msg resolvedMsg) {
}
}
// preferredRow is the choice under the account the reference asks for
// (ref.Ref.Account), if it's one of them.
func preferredRow(r ref.Ref, choices []choice) (string, bool) {
for _, c := range choices {
if r.Account != "" && providerOf(c.sourceRow) == r.Account {
return c.sourceRow, true
}
}
return "", false
}
// startLinkNav opens a link: like startNav, ending on its object's
// details.
func (m *Model) startLinkNav(r ref.Ref, act linkAction, sourceRow string) {
+14
View File
@@ -305,6 +305,20 @@ func TestOpenAsksWhichAccount(t *testing.T) {
t.Errorf("j enter picked %q", c.Source.Provider)
}
// lazysecrets open --account: no question, that account.
r, _ := ref.Parse("lazysecrets://azure/" + kv + "/secrets/api-key")
r.Account = "Azure:bob@example.com/t1"
d.send(OpenLink(r))
if d.m.prompt != nil && d.m.prompt.title == "Open link" {
d.keys("o")
}
if strings.Contains(d.screen(), "Several accounts can see") {
t.Fatalf("asked, with an account given:\n%s", d.screen())
}
if c, _ := d.m.containers.Selected(); c.Source.Provider != r.Account {
t.Errorf("--account opened via %q", c.Source.Provider)
}
// Opening into a folded account unfolds it.
d.keys("1", "g") // alice's header
d.keys("enter") // fold it