Files
sub2api-cn-relay-manager/internal/app/user_key_operation_metrics_test.go
phamnazage-jpg 4e2ee087fd feat(vNext.4): implement trusted-subject security chain for portal user key self-service
- Add portal_auth.go: Portal user session auth with HMAC-signed cookies
- Add /api/portal/session/{login,logout,state} endpoints
- Update nginx config template: cookie-to-header trusted proxy pattern
- Update frontend: sync CRM session on login/logout
- Add TRUSTED_SUBJECT_DEPLOY_GUIDE.md with remote43 deployment steps
- Update EXECUTION_BOARD.md: mark trusted-subject blocking issue as resolved

This implements the secure chain:
  Browser → Portal → nginx (cookie→header) → CRM (verify proxy secret)

Required remote43 actions:
1. Generate 64-char hex secret
2. Update .env.crm with TRUSTED_* config
3. Update nginx with cookie map and header injection
4. Restart services

Fixes EXECUTION_BOARD.md 2026-06-08 blocking issue
2026-06-09 07:48:03 +08:00

56 lines
1.8 KiB
Go

package app
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
"sub2api-cn-relay-manager/internal/metrics"
)
func TestUserKeyCreateResolveHostErrorRecordsMetric(t *testing.T) {
t.Parallel()
store := openAppTestStore(t)
defer closeAppTestStore(t, store)
handler := NewAPIHandler("t", ActionSet{
UserKeyHandler: buildUserKeyHandler(appTestDSN(t, store), testUserKeyAuthConfig()),
})
req := makeCreateRequest(t, http.MethodPost, "/api/keys", makeCreateBody("missing-group", "portal key", []string{"gpt-5.4"}))
applyTrustedProxyAuthHeaders(req, "portal-user")
resp := httptestRecorder(handler, req)
if resp.code != http.StatusInternalServerError {
t.Fatalf("status code = %d, want 500 body=%s", resp.code, resp.Body().String())
}
metricsReq := httptest.NewRequest(http.MethodGet, "/metrics", nil)
metricsResp := httptest.NewRecorder()
metrics.Handler().ServeHTTP(metricsResp, metricsReq)
body := metricsResp.Body.String()
if !strings.Contains(body, `user_key_operations_total{operation="create",result="resolve_host_error"}`) {
t.Fatalf("metrics body missing create resolve_host_error metric: %s", body)
}
}
func TestUserKeyDeleteGetKeyErrorRecordsMetric(t *testing.T) {
t.Parallel()
store := openAppTestStore(t)
defer closeAppTestStore(t, store)
handler := buildUserKeyHandler(appTestDSN(t, store))
if err := handler.deleteFn(context.Background(), "key_missing", "portal-user"); err == nil {
t.Fatal("expected deleteFn to fail for missing key")
}
metricsReq := httptest.NewRequest(http.MethodGet, "/metrics", nil)
metricsResp := httptest.NewRecorder()
metrics.Handler().ServeHTTP(metricsResp, metricsReq)
body := metricsResp.Body.String()
if !strings.Contains(body, `user_key_operations_total{operation="delete",result="get_key_error"}`) {
t.Fatalf("metrics body missing delete get_key_error metric: %s", body)
}
}