621e8895c9
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
105 lines
3.1 KiB
Go
105 lines
3.1 KiB
Go
package infra
|
|
|
|
import (
|
|
"database/sql"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
const wgRevokeTestConfig = `[Interface]
|
|
Address = 10.0.0.1/24
|
|
PrivateKey = SERVERKEY==
|
|
|
|
# DeviceID:device-revoke-001
|
|
[Peer]
|
|
PublicKey = PUBKEYREVOKE001==
|
|
AllowedIPs = 10.0.0.10/32
|
|
|
|
# DeviceID:device-revoke-002
|
|
[Peer]
|
|
PublicKey = PUBKEYREVOKE002==
|
|
AllowedIPs = 10.0.0.11/32
|
|
`
|
|
|
|
func TestWGPeerRevoke(t *testing.T) {
|
|
origSyncConf := wgSyncConfFn
|
|
wgSyncConfFn = func(iface, configPath string) error { return nil }
|
|
defer func() { wgSyncConfFn = origSyncConf }()
|
|
|
|
origBlacklist := wgAppendBlacklistFn
|
|
wgAppendBlacklistFn = func(line string) error { return nil }
|
|
defer func() { wgAppendBlacklistFn = origBlacklist }()
|
|
|
|
t.Run("audit DB contiene registro con this_hash != prev_hash", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
configPath := filepath.Join(dir, "wg0.conf")
|
|
auditDBPath := filepath.Join(dir, "revoked.db")
|
|
|
|
if err := os.WriteFile(configPath, []byte(wgRevokeTestConfig), 0600); err != nil {
|
|
t.Fatalf("write config: %v", err)
|
|
}
|
|
|
|
audit, err := WGPeerRevoke("device-revoke-001", "operator-alice", "dispositivo perdido", configPath, auditDBPath)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if audit.ThisHash == "" {
|
|
t.Error("this_hash is empty")
|
|
}
|
|
// En el primer registro prev_hash es vacio — this_hash debe diferir siempre.
|
|
if audit.ThisHash == audit.PrevHash {
|
|
t.Errorf("this_hash == prev_hash (%q), expected different values", audit.ThisHash)
|
|
}
|
|
if audit.PublicKey != "PUBKEYREVOKE001==" {
|
|
t.Errorf("public_key=%q, want PUBKEYREVOKE001==", audit.PublicKey)
|
|
}
|
|
|
|
// Verificar en la BD directamente.
|
|
db, err := sql.Open("sqlite3", auditDBPath)
|
|
if err != nil {
|
|
t.Fatalf("open audit db: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
var storedHash, storedPubKey string
|
|
if err := db.QueryRow("SELECT this_hash, public_key FROM revoked_peers WHERE device_id = ?",
|
|
"device-revoke-001").Scan(&storedHash, &storedPubKey); err != nil {
|
|
t.Fatalf("query audit record: %v", err)
|
|
}
|
|
if storedHash != audit.ThisHash {
|
|
t.Errorf("stored hash=%q, want %q", storedHash, audit.ThisHash)
|
|
}
|
|
if storedPubKey != "PUBKEYREVOKE001==" {
|
|
t.Errorf("stored public_key=%q, want PUBKEYREVOKE001==", storedPubKey)
|
|
}
|
|
})
|
|
|
|
t.Run("segunda revoke del mismo peer → error already revoked", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
configPath := filepath.Join(dir, "wg0.conf")
|
|
auditDBPath := filepath.Join(dir, "revoked.db")
|
|
|
|
if err := os.WriteFile(configPath, []byte(wgRevokeTestConfig), 0600); err != nil {
|
|
t.Fatalf("write config: %v", err)
|
|
}
|
|
|
|
// Primera revocacion.
|
|
if _, err := WGPeerRevoke("device-revoke-002", "operator-bob", "comprometido", configPath, auditDBPath); err != nil {
|
|
t.Fatalf("first revoke unexpected error: %v", err)
|
|
}
|
|
|
|
// Segunda revocacion del mismo deviceID → debe fallar por audit DB, no por config.
|
|
_, err := WGPeerRevoke("device-revoke-002", "operator-bob", "segundo intento", configPath, auditDBPath)
|
|
if err == nil {
|
|
t.Fatal("expected error on second revoke, got nil")
|
|
}
|
|
if !contains(err.Error(), "already revoked") {
|
|
t.Errorf("expected 'already revoked' error, got: %v", err)
|
|
}
|
|
})
|
|
}
|