Let an own secret's manifest declare a desk prompt (issue 789)
A family like mesh-mail's mail-credential-* names only the module and the secret's key at the desk, so the operator cannot tell which account is asked. An own secret (or a family) may now declare a one-line prompt in its manifest, with {member} filled by a family member's suffix; the module check refuses one that is not one line, over 120 characters, or holds another placeholder. desk_secret.go reads it from the controller's own catalogue and sends it to the desk as what, never from the call or the module's runtime; omitted when none is declared, so an old desk or module behaves as today.
This commit is contained in:
@@ -54,6 +54,10 @@ type deskGive struct {
|
||||
trusted func(module string) (bool, error)
|
||||
// ask asks one machine's node-launcher.secret and answers its result, or the holder's refusal.
|
||||
ask func(machine string, args map[string]any) (json.RawMessage, error)
|
||||
// promptFor is the one line the secret's declaration asks for it as (novox/hq issue 789): read from the
|
||||
// controller's own catalogue, never from the call or the module's runtime. Nil, or an empty answer, asks by
|
||||
// name alone, as the desk does today.
|
||||
promptFor func(module, name string) (string, error)
|
||||
// accept seals the value as `secret accept` does, and says whether it lives until the module's start.
|
||||
accept func(value string) (untilStart bool, err error)
|
||||
// record writes the act in the hand-act log.
|
||||
@@ -153,15 +157,28 @@ func (d deskGive) give(node, module, name, desk string) (string, error) {
|
||||
}
|
||||
// By name, never by words: the holder writes the prompt from these, and says the controller asks, which
|
||||
// the bus alone makes true (broker.ControllerOnly). Who asked is the bus's word on the caller, cut to a
|
||||
// name's characters — never an argument of the call.
|
||||
raw, err := d.ask(desk, map[string]any{
|
||||
// name's characters — never an argument of the call. `what` is the catalogue's own words for the secret
|
||||
// (novox/hq issue 789), never a word the caller chose; omitted when the module declares none, so an old
|
||||
// desk and an old module behave as today.
|
||||
args := map[string]any{
|
||||
"module": module,
|
||||
"secret": name,
|
||||
"node": node,
|
||||
"asked_by": askedByName(d.askedBy),
|
||||
"seal_to": public,
|
||||
"timeout_seconds": deskPromptWithin,
|
||||
})
|
||||
}
|
||||
if d.promptFor != nil {
|
||||
what, err := d.promptFor(module, name)
|
||||
if err != nil {
|
||||
outcome = "refused"
|
||||
return "", fmt.Errorf("nobody was asked to type anything: %w", err)
|
||||
}
|
||||
if what != "" {
|
||||
args["what"] = what
|
||||
}
|
||||
}
|
||||
raw, err := d.ask(desk, args)
|
||||
if err != nil {
|
||||
outcome = "refused"
|
||||
return "", fmt.Errorf("the desk on %s could not be asked: %w", desk, err)
|
||||
@@ -247,7 +264,8 @@ func askAtDesk(ctx context.Context, node, module, name, desk string) error {
|
||||
_, err := open.inventory.NodeByName(ctx, machine)
|
||||
return err
|
||||
},
|
||||
trusted: func(module string) (bool, error) { return open.inventory.RunsAsItsOwnAccount(ctx, module) },
|
||||
trusted: func(module string) (bool, error) { return open.inventory.RunsAsItsOwnAccount(ctx, module) },
|
||||
promptFor: func(module, name string) (string, error) { return open.inventory.OwnSecretPrompt(ctx, module, name) },
|
||||
ask: func(machine string, args map[string]any) (json.RawMessage, error) {
|
||||
var result json.RawMessage
|
||||
err := onTheBus(func(conn *nats.Conn) error {
|
||||
|
||||
@@ -456,6 +456,52 @@ func TestThePromptNamesWhoAskedFromTheBussWordAlone(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// The desk's prompt names which account of a family is asked for, from the catalogue's own words (novox/hq
|
||||
// issue 789): a family declaring `prompt` with `{member}` sends `what` with the member's suffix filled in.
|
||||
func TestThePromptNamesWhichAccountOfAFamilyIsAsked(t *testing.T) {
|
||||
d, _, _, asked := aDesk(t, sealedTo(t, typed))
|
||||
d.declares = func(module, name string) error {
|
||||
if module != "mesh-mail" || name != "mail-credential-gmail" {
|
||||
return errors.New(module + " does not declare " + name + " as an own secret")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
d.promptFor = func(module, name string) (string, error) {
|
||||
if module == "mesh-mail" && name == "mail-credential-gmail" {
|
||||
return "the password of the mail account gmail", nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
if _, err := d.give("anchor", "mesh-mail", "mail-credential-gmail", "laptop"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := (*asked)[0]["what"]; got != "the password of the mail account gmail" {
|
||||
t.Errorf(`"what" = %v`, got)
|
||||
}
|
||||
}
|
||||
|
||||
// Without a declared prompt, no `what` is sent: an old module, or one that declares none, is asked by name alone,
|
||||
// as the desk does today (novox/hq issue 789).
|
||||
func TestWithNoDeclaredPromptNoWhatIsSent(t *testing.T) {
|
||||
d, _, _, asked := aDesk(t, sealedTo(t, typed))
|
||||
d.promptFor = func(module, name string) (string, error) { return "", nil }
|
||||
if _, err := d.give("anchor", "telegram", "telegram-token", "laptop"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, there := (*asked)[0]["what"]; there {
|
||||
t.Errorf(`"what" was sent with no declared prompt: %v`, (*asked)[0])
|
||||
}
|
||||
// And with no promptFor wired at all (an older caller of deskGive), same thing: `give` still works, asking
|
||||
// nothing more than it does today.
|
||||
d2, _, _, asked2 := aDesk(t, sealedTo(t, typed))
|
||||
if _, err := d2.give("anchor", "telegram", "telegram-token", "laptop"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, there := (*asked2)[0]["what"]; there {
|
||||
t.Errorf(`"what" was sent with promptFor nil: %v`, (*asked2)[0])
|
||||
}
|
||||
}
|
||||
|
||||
// An ask for a secret is bounded before anybody is asked to type (ADR 0277): the record refuses it, nothing is
|
||||
// asked; and how every ask ends is recorded.
|
||||
func TestASecretAskIsBoundedAndItsEndRecorded(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user