Mint an assignment's account once, whoever finds it missing first (issue 1945, ADR 0543, review)
The assign act and the assignments' watch both checked whether an account was minted and then minted it, and minting overwrites the hash: interleaved, the bus kept one caller's hash and the node was sealed the other's password, the crash loop this branch fixes. Both now mint through one statement that inserts the row only when none stands; the caller whose insert wins seals, the other seals nothing. issueMissingProcessAccounts mints the same way. A watch opened again after one ended handed the standing assignments as the start's look, so one that came to stand in the gap got no bus composition or grant when its account already stood: only the process's first watch is the start's look now. The start's look reads the catalogue once and ends with one composition of the bus's node and the grants; a controller not holding the lease is told so and tries again rather than dropping the key. Tests folded to stay within the budget: the assignment tests are three table tests, the process account and mayIssue tests folded into one of them, and the concurrent mint is a case of the inventory's rotation test. Research 072: the grants piece (memberships and credentials heard from placement).
This commit is contained in:
@@ -114,6 +114,37 @@ func (i *Inventory) writeBusUser(ctx context.Context, u BusUser, hash string) er
|
||||
return nil
|
||||
}
|
||||
|
||||
// MintBusPasswordIfAbsent mints u's password only when no row names u yet, in one statement: of two callers that both
|
||||
// found the account missing, exactly one wins, and the other is told it lost (won false, no password) and must seal
|
||||
// nothing, so the hash the bus holds is always the password the node is sealed (novox/hq ADR 0543, the review of
|
||||
// issue 1945: the assign act and the assignments' watch once each minted, the later hash beside the earlier seal).
|
||||
func (i *Inventory) MintBusPasswordIfAbsent(ctx context.Context, u BusUser) (string, bool, error) {
|
||||
if u.Username == "" || u.Kind == "" {
|
||||
return "", false, errors.New("a bus user needs a username and a kind")
|
||||
}
|
||||
raw := make([]byte, 32)
|
||||
if _, err := rand.Read(raw); err != nil {
|
||||
return "", false, fmt.Errorf("cannot generate a bus password: %w", err)
|
||||
}
|
||||
password := base64.RawURLEncoding.EncodeToString(raw)
|
||||
hash, err := hashBusPassword(password)
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("cannot hash a bus password: %w", err)
|
||||
}
|
||||
tag, err := i.store.Pool().Exec(ctx,
|
||||
`insert into bus_user (username, kind, node, module, password_hash)
|
||||
values ($1, $2, $3, $4, $5)
|
||||
on conflict (username) do nothing`,
|
||||
u.Username, u.Kind, u.Node, u.Module, string(hash))
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("cannot record the bus user %s: %w", u.Username, err)
|
||||
}
|
||||
if tag.RowsAffected() == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
return password, true, nil
|
||||
}
|
||||
|
||||
func (i *Inventory) MintBusPassword(ctx context.Context, u BusUser) (string, error) {
|
||||
if u.Username == "" || u.Kind == "" {
|
||||
return "", errors.New("a bus user needs a username and a kind")
|
||||
|
||||
Reference in New Issue
Block a user