Added config generate-csrf-key, to store a CSRF key that survives restarts and works across instances
Without server.csrf.tokens.key, each server process signs CSRF tokens with a random key of its own, so tokens are lost on a restart and rejected by other instances. "job-search config generate-csrf-key" stores a new random 32-byte hex key in the configuration file, without printing it. --print only prints a key, for JOBSEARCH_SERVER_CSRF_TOKENS_KEY or another instance. An existing key is only replaced with --force, as that invalidates every issued token, and no key is stored while JOBSEARCH_SERVER_CSRF_TOKENS_KEY is set, as it would override the file.
This commit is contained in:
1 parent
18db462d89
commit
eba6af680f
7 files changed
+241
-6
No files matched your search
@@ -0,0 +1,104 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.avsdev.uk/Go/job-search/internal/cli/cmdtest"
|
||||
)
|
||||
|
||||
// fileCSRFKey returns the CSRF key in config.yaml, or "" if it has none.
|
||||
func fileCSRFKey(t *testing.T) string {
|
||||
t.Helper()
|
||||
key, _ := fileSettings(t, "config.yaml")[csrfKeySetting].(string)
|
||||
return key
|
||||
}
|
||||
|
||||
// checkCSRFKey fails the test unless key is csrfKeyBytes of hex.
|
||||
func checkCSRFKey(t *testing.T, key string) {
|
||||
t.Helper()
|
||||
decoded, err := hex.DecodeString(key)
|
||||
if err != nil || len(decoded) != csrfKeyBytes {
|
||||
t.Fatalf("key %q is not %d bytes of hex", key, csrfKeyBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// The key is stored in the configuration file, without being printed, and
|
||||
// the file's other settings are kept.
|
||||
func TestGenerateCSRFKey(t *testing.T) {
|
||||
cmdtest.Setup(t, appConfig)
|
||||
|
||||
out := mustRun(t, "config", "generate-csrf-key")
|
||||
cmdtest.Contains(t, out, csrfKeySetting+" set in ")
|
||||
|
||||
key := fileCSRFKey(t)
|
||||
checkCSRFKey(t, key)
|
||||
if strings.Contains(out, key) {
|
||||
t.Errorf("the key was printed: %s", out)
|
||||
}
|
||||
if fileSettings(t, "config.yaml")["sources.sleeper.interval"] != "2m" {
|
||||
t.Errorf("other settings were changed: %v", fileSettings(t, "config.yaml"))
|
||||
}
|
||||
}
|
||||
|
||||
// An existing key is only replaced with --force.
|
||||
func TestGenerateCSRFKeyExisting(t *testing.T) {
|
||||
cmdtest.Setup(t, appConfig)
|
||||
mustRun(t, "config", "generate-csrf-key")
|
||||
first := fileCSRFKey(t)
|
||||
|
||||
_, err := run(t, "config", "generate-csrf-key")
|
||||
cmdtest.ErrContains(t, err, "already set: --force replaces it")
|
||||
if fileCSRFKey(t) != first {
|
||||
t.Fatal("the key was replaced without --force")
|
||||
}
|
||||
|
||||
mustRun(t, "config", "generate-csrf-key", "--force")
|
||||
second := fileCSRFKey(t)
|
||||
checkCSRFKey(t, second)
|
||||
if second == first {
|
||||
t.Error("--force kept the same key")
|
||||
}
|
||||
}
|
||||
|
||||
// --print only prints a key, and needs no configuration file.
|
||||
func TestGenerateCSRFKeyPrint(t *testing.T) {
|
||||
cmdtest.Setup(t, "")
|
||||
|
||||
key := strings.TrimSpace(mustRun(t, "config", "generate-csrf-key", "--print"))
|
||||
checkCSRFKey(t, key)
|
||||
|
||||
other := strings.TrimSpace(mustRun(t, "config", "generate-csrf-key", "--print"))
|
||||
if other == key {
|
||||
t.Error("two keys were the same")
|
||||
}
|
||||
}
|
||||
|
||||
// A key in the environment variable would override the file, so none is
|
||||
// stored while it is set.
|
||||
func TestGenerateCSRFKeyEnvironment(t *testing.T) {
|
||||
cmdtest.Setup(t, appConfig)
|
||||
t.Setenv("JOBSEARCH_SERVER_CSRF_TOKENS_KEY", generateCSRFKey())
|
||||
|
||||
_, err := run(t, "config", "generate-csrf-key")
|
||||
cmdtest.ErrContains(t, err, "JOBSEARCH_SERVER_CSRF_TOKENS_KEY is set")
|
||||
if fileCSRFKey(t) != "" {
|
||||
t.Error("a key was stored")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateCSRFKeyWithoutConfigFile(t *testing.T) {
|
||||
cmdtest.Setup(t, "")
|
||||
_, err := run(t, "config", "generate-csrf-key")
|
||||
cmdtest.ErrContains(t, err, "no configuration file exists")
|
||||
}
|
||||
|
||||
func TestGenerateCSRFKeyFlags(t *testing.T) {
|
||||
cmdtest.Setup(t, appConfig)
|
||||
_, err := run(t, "config", "generate-csrf-key", "--print", "--force")
|
||||
cmdtest.ErrContains(t, err, "none of the others")
|
||||
if _, err := run(t, "config", "generate-csrf-key", "extra"); err == nil {
|
||||
t.Error("an argument was accepted")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user