fix(auth): repair persistence layer and stop storing plaintext keys
- Store SHA-256 digests (key_hash + key_preview) instead of raw keys, in both the in-memory store and Postgres; migrate legacy plaintext rows in place and drop the plaintext column. - Fix TEXT[] scans that failed at runtime (missing pq.Array) in GetKey/ListKeys/LoadAllKeys. - Load persisted keys at startup (InitializeFromDatabase was never called) and fall back to the database on cache miss, so issued keys survive restarts. - Make HybridKeyStore.CreateKey genuinely write-through: cache is only updated after a successful DB write. - Guard nil database backend (auth enabled without DB previously panicked on key creation) and degrade to in-memory operation. - Persist bootstrap admin keys when a database is configured. - Record real audit-log details as JSON and log audit failures instead of silently dropping them; add DB pool limits and ping timeout. - Sentinel errors matched with errors.Is; unit tests for hashing, write-through ordering, DB fallback, and nil-DB operation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+10
-18
@@ -101,26 +101,18 @@ func RegisterAdminKeyInMemory(keyStore *APIKeyStore, adminKey string, logger *lo
|
||||
adminRoles := []string{"admin", "notify-email", "notify-slack", "notify-ntfy"}
|
||||
now := time.Now().UTC()
|
||||
apiKey := &APIKey{
|
||||
Key: adminKey,
|
||||
ClientID: "admin-bootstrap",
|
||||
Roles: adminRoles,
|
||||
CreatedAt: now,
|
||||
IsActive: true,
|
||||
RateLimit: 0, // Unlimited
|
||||
Name: fmt.Sprintf("admin-bootstrap-%d", now.Unix()),
|
||||
Key: adminKey,
|
||||
KeyHash: HashKey(adminKey),
|
||||
KeyPreview: keyPreview(adminKey),
|
||||
ClientID: "admin-bootstrap",
|
||||
Roles: adminRoles,
|
||||
CreatedAt: now,
|
||||
IsActive: true,
|
||||
RateLimit: 0, // Unlimited
|
||||
Name: fmt.Sprintf("admin-bootstrap-%d", now.Unix()),
|
||||
}
|
||||
|
||||
// Add to keystore
|
||||
keyStore.mu.Lock()
|
||||
defer keyStore.mu.Unlock()
|
||||
|
||||
keyStore.keys[adminKey] = apiKey
|
||||
keyStore.rateLimits[adminKey] = &RateLimiter{
|
||||
maxRequests: 0, // Unlimited
|
||||
window: time.Minute,
|
||||
resetTime: time.Now().Add(time.Minute),
|
||||
count: 0,
|
||||
}
|
||||
keyStore.RegisterKey(apiKey)
|
||||
|
||||
logger.Infof("Registered existing admin key from Kubernetes secret")
|
||||
return apiKey, nil
|
||||
|
||||
Reference in New Issue
Block a user