Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,9 @@ and preserves the named SQLite volume when stopped or recreated. Set
`DIFFX_SERVER_PORT=7979` to publish another host port. The project is named
`diffx`, so its volume is shared by every checkout. Retrieve
the initial credential with `docker compose cp server:/data/state.db.admin-token ./admin-token`.
To choose the credential instead, put `DIFFX_SERVER_TOKEN=…` (at least 32
characters) in a git-ignored `.env` beside `compose.yaml` before the first start.
To choose the credential instead, put `DIFFX_SERVER_TOKEN=…` in a git-ignored
`.env` beside `compose.yaml` before the first start. Any non-empty token works;
prefer a long random one (`openssl rand -hex 32`) when exposing the server.
It only applies to a new database; a different token for an existing account
fails startup.
See [server development](docs/development.md#server-development) for configuration
Expand Down
4 changes: 2 additions & 2 deletions internal/remoteserver/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,13 +329,13 @@ func TestRetentionDurationValidation(t *testing.T) {
}
}

func TestRemoteRejectsMissingIdentityOrWeakCredentials(t *testing.T) {
func TestRemoteRejectsMissingIdentityOrInvalidCredentials(t *testing.T) {
store, err := reviewstore.Open("")
if err != nil {
t.Fatal(err)
}
defer store.Close()
for _, tc := range []struct{ account, token string }{{"", testToken}, {" ", testToken}, {"account", "short"}, {"account\n", testToken}, {"account", testToken + "\r"}} {
for _, tc := range []struct{ account, token string }{{"", testToken}, {" ", testToken}, {"account\n", testToken}, {"account", testToken + "\r"}} {
if _, _, err := Handler(t.Context(), store, tc.account, tc.token, fstest.MapFS{}); err == nil {
t.Fatalf("invalid remote identity accepted: account=%q", tc.account)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/reviewstore/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func initializeAuthentication(transaction *sql.Tx) error {
}

func validCredentials(account, token string) bool {
return strings.TrimSpace(account) != "" && len(account) <= 256 && !strings.ContainsAny(account, "\r\n:") && len(token) >= 32 && !strings.ContainsAny(token, "\r\n")
return strings.TrimSpace(account) != "" && len(account) <= 256 && !strings.ContainsAny(account, "\r\n:") && token != "" && !strings.ContainsAny(token, "\r\n")
}

func tokenHash(token string) string {
Expand All @@ -41,7 +41,7 @@ func (store *Store) ProvisionUser(account, token string) (User, error) {

func (store *Store) provisionRemoteUser(account, token string, reuse bool) (User, error) {
if !validCredentials(account, token) {
return User{}, errors.New("remote account requires a name and a token of at least 32 bytes")
return User{}, errors.New("remote account requires a name and a non-empty token")
}
transaction, err := store.db.Begin()
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion internal/reviewstore/authentication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,14 @@ func TestRemoteCredentialValidation(t *testing.T) {
}
defer store.Close()
for _, tc := range []struct{ name, token string }{
{"", strings.Repeat("x", 32)}, {" ", strings.Repeat("x", 32)}, {"name", "short"},
{"", strings.Repeat("x", 32)}, {" ", strings.Repeat("x", 32)}, {"name", ""},
{"name\n", strings.Repeat("x", 32)}, {"name", strings.Repeat("x", 32) + "\r"}, {"name:invalid", strings.Repeat("x", 32)},
} {
if _, err := store.ProvisionUser(tc.name, tc.token); err == nil {
t.Fatalf("invalid credential accepted for %q", tc.name)
}
}
if _, err := store.ProvisionUser("name", "helloworld"); err != nil {
t.Fatalf("short token rejected: %v", err)
}
}