Merge pull request 'The controller's check stops waiting on fixtures and fixed intervals (issue 591)' (#279) from fix/591-the-controller-check-runs-in-minutes into main
This commit was merged in pull request #279.
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@@ -23,6 +24,25 @@ import (
|
||||
// access change would blank every module's password if the mesh kept nothing (design 25 §4, and the
|
||||
// migration beside this).
|
||||
|
||||
// busPasswordCostForTest, when set, is the bcrypt cost a bus password is hashed at instead of the
|
||||
// library's default. Only ForTest sets it, to bcrypt's least (novox/hq issue 591): at the default
|
||||
// cost one hash takes most of a second under the race detector, and a hash at the least cost is the
|
||||
// same kind of hash, checked by the same comparison and by the bus the same way.
|
||||
var busPasswordCostForTest atomic.Int32
|
||||
|
||||
// busPasswordCost is the bcrypt cost a bus password is hashed at: the library's default, but in a test.
|
||||
func busPasswordCost() int {
|
||||
if c := busPasswordCostForTest.Load(); c != 0 {
|
||||
return int(c)
|
||||
}
|
||||
return bcrypt.DefaultCost
|
||||
}
|
||||
|
||||
// hashBusPassword is the hash a bus password is kept as, minted, recorded or seeded alike.
|
||||
func hashBusPassword(password string) ([]byte, error) {
|
||||
return bcrypt.GenerateFromPassword([]byte(password), busPasswordCost())
|
||||
}
|
||||
|
||||
// BusUser is one user of the bus, as the mesh records it.
|
||||
type BusUser struct {
|
||||
Username string
|
||||
@@ -67,7 +87,7 @@ func (i *Inventory) RecordBusPassword(ctx context.Context, u BusUser, password s
|
||||
if password == "" {
|
||||
return errors.New("a bus user needs a password")
|
||||
}
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
hash, err := hashBusPassword(password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot hash a bus password: %w", err)
|
||||
}
|
||||
@@ -101,7 +121,7 @@ func (i *Inventory) MintBusPassword(ctx context.Context, u BusUser) (string, err
|
||||
// The cost the server will pay on every connection. Left at the library's default rather than
|
||||
// raised: a node reconnecting after a network blip pays it, and the mesh's own links reconnect
|
||||
// far more often than a person logs in anywhere.
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
hash, err := hashBusPassword(password)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot hash a bus password: %w", err)
|
||||
}
|
||||
@@ -183,7 +203,7 @@ func (i *Inventory) SeedBusUser(ctx context.Context, u BusUser, password string)
|
||||
if u.Username == "" || u.Kind == "" || password == "" {
|
||||
return errors.New("a bus user needs a username, a kind and the credential it is using")
|
||||
}
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
hash, err := hashBusPassword(password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot hash a bus password: %w", err)
|
||||
}
|
||||
|
||||
@@ -158,3 +158,17 @@ func TestACredentialTheMeshDidNotMintIsRecordedOnceAndNotOverwritten(t *testing.
|
||||
t.Fatal("a restart put the bootstrap credential back over a rotated one")
|
||||
}
|
||||
}
|
||||
|
||||
// Outside a test a bus password is hashed at bcrypt's default cost: only ForTest lowers it (novox/hq issue 591).
|
||||
func TestABusPasswordIsHashedAtTheDefaultCostOutsideATest(t *testing.T) {
|
||||
was := busPasswordCostForTest.Load()
|
||||
t.Cleanup(func() { busPasswordCostForTest.Store(was) })
|
||||
busPasswordCostForTest.Store(0)
|
||||
hash, err := hashBusPassword("a password")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, err := bcrypt.Cost(hash); err != nil || got != bcrypt.DefaultCost {
|
||||
t.Fatalf("a bus password is hashed at cost %d (%v), not bcrypt's default %d", got, err, bcrypt.DefaultCost)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/store"
|
||||
)
|
||||
@@ -30,14 +31,14 @@ func ForTest(t *testing.T) *Inventory {
|
||||
name = name[:60]
|
||||
}
|
||||
|
||||
conn, err := pgx.Connect(t.Context(), admin)
|
||||
// Created already migrated, a copy of a template migrated once (novox/hq issue 591); the Migrate
|
||||
// below then finds nothing pending, which it does only if the copy's record matches every migration.
|
||||
migrations, err := Migrations()
|
||||
if err != nil {
|
||||
t.Fatalf("cannot reach the test PostgreSQL: %v", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := conn.Exec(t.Context(), "create database "+name); err != nil {
|
||||
t.Fatalf("cannot create %s: %v", name, err)
|
||||
}
|
||||
conn.Close(t.Context())
|
||||
store.CreateMigratedForTest(t, admin, name, migrations)
|
||||
busPasswordCostForTest.Store(int32(bcrypt.MinCost))
|
||||
|
||||
cut := strings.LastIndex(admin, "/")
|
||||
t.Setenv(store.Variable(Name), admin[:cut]+"/"+name+"?sslmode=disable")
|
||||
@@ -59,10 +60,6 @@ func ForTest(t *testing.T) *Inventory {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
migrations, err := Migrations()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := inv.store.Migrate(t.Context(), migrations); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user