alby-hub/config/config_test.go
Roland 3b3c37dd0c
fix: remove legacy acceptance of empty unlock password check (#2534)
* fix: remove legacy acceptance of empty unlock password check

CheckUnlockPassword previously treated a missing or empty
UnlockPasswordCheck value as a match — a legacy compatibility path from
before the canary was always written. It now requires the stored value
to be present and to equal the expected string.

StartApp checks for the canary up front and, if it is missing, stops
with a message asking the user to restore from a backup rather than
continuing. A new IsUnlockPasswordCheckSet helper reports whether the
value is present.

keys.Init now returns the error from reading NostrSecretKey instead of
ignoring it, so a read failure aborts instead of generating and saving a
new key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: add operation context to unlock password check errors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 14:48:06 +07:00

105 lines
2.9 KiB
Go

// see ./tests/config_test.go for config tests with DB coverage for both SQlite & Postgres
package config
import (
"strconv"
"testing"
"github.com/getAlby/hub/db/migrations"
"github.com/getAlby/hub/logger"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func TestCheckCache_NoEncryptionKey(t *testing.T) {
logger.Init(strconv.Itoa(int(logrus.DebugLevel)))
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
err = migrations.Migrate(db)
require.NoError(t, err)
cfg, err := NewConfig(&AppConfig{
Workdir: ".test",
}, db)
require.NoError(t, err)
err = cfg.SetUpdate("key", "value", "")
require.NoError(t, err)
require.Equal(t, cfg.cache["key"][""], "")
value, err := cfg.Get("key", "")
require.NoError(t, err)
require.Equal(t, "value", value)
require.Equal(t, cfg.cache["key"][""], "value")
// test we can access the cached value without the db
cfg.db = nil
value, err = cfg.Get("key", "")
require.NoError(t, err)
require.Equal(t, "value", value)
}
func TestCheckUnlockPasswordCache(t *testing.T) {
logger.Init(strconv.Itoa(int(logrus.DebugLevel)))
unlockPassword := "123"
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
err = migrations.Migrate(db)
require.NoError(t, err)
cfg, err := NewConfig(&AppConfig{
Workdir: ".test",
}, db)
require.NoError(t, err)
err = cfg.SaveUnlockPasswordCheck(unlockPassword)
require.NoError(t, err)
// check cache
assert.Nil(t, cfg.cache["UnlockPasswordCheck"])
// check password
assert.True(t, cfg.CheckUnlockPassword(unlockPassword))
assert.NotNil(t, cfg.cache["UnlockPasswordCheck"])
// check hash cache
cacheValue, ok := cfg.cache["UnlockPasswordCheck"]
require.True(t, ok)
assert.Equal(t, 1, len(cacheValue))
assert.False(t, cfg.CheckUnlockPassword(unlockPassword+"1"))
// check hash cache - length should not have changed because decrypt failed with invalid password
cacheValue2, ok := cfg.cache["UnlockPasswordCheck"]
require.True(t, ok)
assert.Equal(t, 1, len(cacheValue2))
require.Equal(t, cacheValue, cacheValue2)
// change the password
newUnlockPassword := unlockPassword + "1"
err = cfg.ChangeUnlockPassword(unlockPassword, newUnlockPassword)
require.NoError(t, err)
assert.Equal(t, 0, len(cfg.cache["UnlockPasswordCheck"]))
// test we can access the cached value without the db
assert.True(t, cfg.CheckUnlockPassword(newUnlockPassword))
assert.NotNil(t, cfg.cache["UnlockPasswordCheck"])
cfg.db = nil
assert.True(t, cfg.CheckUnlockPassword(newUnlockPassword))
// should panic when trying to access the db for an uncached value
hitPanic := false
func() {
defer func() {
// ensure the app cannot panic if firing events to Alby API fails
if r := recover(); r != nil {
hitPanic = true
}
}()
assert.False(t, cfg.CheckUnlockPassword(unlockPassword))
}()
assert.True(t, hitPanic)
}