|
|
|
|
package api
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"tera-clone/services/tera-api/internal/adapters/mock"
|
|
|
|
|
"tera-clone/services/tera-api/internal/domain"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestPortLogClearRequiresAdminExactTargetAndConfirmation(t *testing.T) {
|
|
|
|
|
store := mock.NewStore(fixedClock)
|
|
|
|
|
for _, ch := range []string{"CH01", "CH02"} {
|
|
|
|
|
if _, err := store.RecordPortLog(context.Background(), domain.PortLogEvent{Channel: ch, EventType: "command", Command: "show version", ReturnValue: "ok", Message: "command completed", Actor: "tester"}); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
handler := NewRouter(store)
|
|
|
|
|
for _, tc := range []struct {
|
|
|
|
|
actor, body string
|
|
|
|
|
status int
|
|
|
|
|
}{
|
|
|
|
|
{"mock-port-operator", `{"channel":"CH01","confirmation":"CH01"}`, 403},
|
|
|
|
|
{"mock-admin", `{"channel":"CH01"}`, 400},
|
|
|
|
|
{"mock-admin", `{"channel":"","confirmation":""}`, 400},
|
|
|
|
|
{"mock-admin", `{"channel":"CH01","confirmation":"CH02"}`, 400},
|
|
|
|
|
{"mock-admin", `{"channel":"../audit","confirmation":"../audit"}`, 400},
|
|
|
|
|
{"mock-admin", `{"channel":"CH01","confirmation":"CH01","class":"audit"}`, 400},
|
|
|
|
|
} {
|
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/logs/port/clear", strings.NewReader(tc.body))
|
|
|
|
|
req.Header.Set("X-Mock-Actor", tc.actor)
|
|
|
|
|
res := httptest.NewRecorder()
|
|
|
|
|
handler.ServeHTTP(res, req)
|
|
|
|
|
if res.Code != tc.status {
|
|
|
|
|
t.Fatalf("clear %s = %d: %s", tc.actor, res.Code, res.Body.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result := getJSONAs(t, handler, http.MethodPost, "/api/v1/logs/port/clear", `{"channel":"CH01","password":"mock-admin-secret"}`, "mock-admin")
|
|
|
|
|
if result["deleted"] != float64(1) || result["channel"] != "CH01" {
|
|
|
|
|
t.Fatalf("clear result = %+v", result)
|
|
|
|
|
}
|
|
|
|
|
rows, err := store.PortLogEvents(context.Background(), domain.PortLogFilter{})
|
|
|
|
|
if err != nil || len(rows) != 1 || rows[0].Channel != "CH02" {
|
|
|
|
|
t.Fatalf("remaining logs = %+v, %v", rows, err)
|
|
|
|
|
}
|
|
|
|
|
audit, err := store.AuditEvents(context.Background())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
found := false
|
|
|
|
|
for _, event := range audit {
|
|
|
|
|
if event.Action == "log.port.clear" && event.Target == "CH01" && event.Result == "succeeded" {
|
|
|
|
|
found = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !found {
|
|
|
|
|
t.Fatal("successful deletion is not audited")
|
|
|
|
|
}
|
|
|
|
|
}
|