Files
Maven/cmd/mavweb/credentials.go
T
kami 8d823000d1 fix: code-review findings on overnight-jul5
- digest queue no longer dropped on failed dispatch (retry next tick)
- collapsed reminders marked fired/rescheduled only after digest delivers
- /tools step-up gate skipped when WebAuthn is not configured (was 403 forever)
- passkey credential store rolls back memory on persist failure
- auth_test fake updated for TickTrace (branch build break)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASstMtsZWLSRcD1Tq8T68Q
2026-07-05 17:50:21 +04:00

90 lines
2.0 KiB
Go

package main
import (
"encoding/json"
"fmt"
"os"
"sync"
)
type credentialStore struct {
mu sync.RWMutex
path string
creds map[string]localCred
}
func newCredentialStore(path string) (*credentialStore, error) {
cs := &credentialStore{
path: path,
creds: make(map[string]localCred),
}
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return cs, nil
}
return nil, fmt.Errorf("read %s: %w", path, err)
}
if len(data) > 0 {
if err := json.Unmarshal(data, &cs.creds); err != nil {
return nil, fmt.Errorf("parse %s: %w", path, err)
}
}
return cs, nil
}
func (cs *credentialStore) persist() error {
data, err := json.MarshalIndent(cs.creds, "", " ")
if err != nil {
return fmt.Errorf("marshal: %w", err)
}
if err := os.WriteFile(cs.path, data, 0600); err != nil {
return fmt.Errorf("write %s: %w", cs.path, err)
}
return nil
}
func (cs *credentialStore) Save(id string, publicKey []byte) error {
cs.mu.Lock()
defer cs.mu.Unlock()
if _, exists := cs.creds[id]; exists {
return fmt.Errorf("credential already exists")
}
cs.creds[id] = localCred{PublicKey: publicKey}
if err := cs.persist(); err != nil {
// roll back so memory matches disk — otherwise the credential looks
// registered until restart, then silently vanishes, and a retry hits
// "already exists".
delete(cs.creds, id)
return err
}
return nil
}
func (cs *credentialStore) Lookup(id string) (publicKey []byte, signCount int64, err error) {
cs.mu.RLock()
defer cs.mu.RUnlock()
cred, ok := cs.creds[id]
if !ok {
return nil, 0, fmt.Errorf("credential not found")
}
return cred.PublicKey, cred.SignCount, nil
}
func (cs *credentialStore) UpdateSignCount(id string, count int64) error {
cs.mu.Lock()
defer cs.mu.Unlock()
prev, ok := cs.creds[id]
if !ok {
return fmt.Errorf("credential not found")
}
cred := prev
cred.SignCount = count
cs.creds[id] = cred
if err := cs.persist(); err != nil {
cs.creds[id] = prev // keep memory consistent with disk
return err
}
return nil
}