44807b612c
- New credentialStore type in credentials.go loads/saves map[id]localCred to a JSON file. Thread-safe with sync.RWMutex, writes to disk on every mutation. - PasskeyHandle replaces sync.RWMutex+map with *credentialStore. Inline save/lookip/update closures delegate to store methods. - newPasskeyHandle now takes a storePath parameter and returns an error; callers updated. - New -passkey-file flag (default ./passkeys.json) configures the credential store path in main.go. - Tests use os.CreateTemp in t.TempDir() so each test gets an isolated, auto-cleaned store file.
78 lines
1.7 KiB
Go
78 lines
1.7 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}
|
|
return cs.persist()
|
|
}
|
|
|
|
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()
|
|
cred, ok := cs.creds[id]
|
|
if !ok {
|
|
return fmt.Errorf("credential not found")
|
|
}
|
|
cred.SignCount = count
|
|
cs.creds[id] = cred
|
|
return cs.persist()
|
|
}
|