feat(auth): implement user and device authentication

This commit is contained in:
QiuSW
2026-07-26 15:18:48 +08:00
parent c5d3b215ff
commit 49db5b8305
66 changed files with 6216 additions and 271 deletions
@@ -15,6 +15,7 @@ import (
"path/filepath"
"strings"
"testing"
"time"
"cmroubao/backend-api/internal/platform/assetstore"
"cmroubao/backend-api/internal/platform/database"
@@ -210,6 +211,32 @@ func TestAdminAPIAssetAndTaskLifecycle(t *testing.T) {
canceled,
)
}
canceledDetailResponse := performAdminRequest(
t,
router,
http.MethodGet,
"/api/v1/tasks/"+taskID,
"",
nil,
"",
)
var canceledDetail map[string]any
decodeResponse(t, canceledDetailResponse, &canceledDetail)
events, _ := canceledDetail["events"].([]any)
if canceledDetailResponse.Code != http.StatusOK || len(events) != 2 {
t.Fatalf(
"canceled detail status/body = %d / %#v",
canceledDetailResponse.Code,
canceledDetail,
)
}
for _, value := range events {
event, _ := value.(map[string]any)
if event["actor_user_id"] !=
"00000000-0000-4000-8000-000000000099" {
t.Fatalf("event actor = %#v", event)
}
}
secondCancel := performAdminRequest(
t,
@@ -229,7 +256,7 @@ func TestAdminAPIAssetAndTaskLifecycle(t *testing.T) {
}
}
func TestAdminRoutesRejectNonLoopbackRequests(t *testing.T) {
func TestAdminRoutesRejectRequestsWithoutAdminSession(t *testing.T) {
router := newAdminIntegrationRouter(t)
request := httptest.NewRequest(http.MethodGet, "/api/v1/tasks", nil)
request.RemoteAddr = "192.0.2.10:3210"
@@ -237,7 +264,7 @@ func TestAdminRoutesRejectNonLoopbackRequests(t *testing.T) {
router.ServeHTTP(response, request)
if response.Code != http.StatusForbidden {
if response.Code != http.StatusUnauthorized {
t.Fatalf("status = %d, body = %s", response.Code, response.Body.String())
}
var body map[string]any
@@ -275,7 +302,7 @@ func TestAdminAssetUploadRequiresIdempotencyKey(t *testing.T) {
type emptyAdminWeb struct{}
func (emptyAdminWeb) Register(gin.IRoutes) {}
func (emptyAdminWeb) RegisterProtected(gin.IRoutes) {}
func newAdminIntegrationRouter(t *testing.T) http.Handler {
t.Helper()
@@ -292,6 +319,18 @@ func newAdminIntegrationRouter(t *testing.T) http.Handler {
if _, err := runner.Up(ctx); err != nil {
t.Fatalf("migration.Up() error = %v", err)
}
now := time.Now().UTC().Format(time.RFC3339Nano)
if _, err := db.ExecContext(
ctx,
`INSERT INTO users (
id, username, password_hash, role, is_active, created_at, updated_at
) VALUES (?, 'admin', 'test-only-hash', 'ADMIN', 1, ?, ?)`,
"00000000-0000-4000-8000-000000000099",
now,
now,
); err != nil {
t.Fatalf("seed admin user: %v", err)
}
repositories, err := repository.New(db)
if err != nil {
t.Fatalf("repository.New() error = %v", err)
@@ -318,9 +357,11 @@ func newAdminIntegrationRouter(t *testing.T) http.Handler {
t.Fatalf("NewAdminRouteRegistrar() error = %v", err)
}
router, err := NewRouter(RouterDependencies{
Database: db,
RegisterAdminRoutes: registrar,
LogEvent: discardEvent,
Database: db,
RegisterPublicRoutes: discardRoutes,
RegisterAdminRoutes: registrar,
AdminSessions: allowAdminAuthenticator{},
LogEvent: discardEvent,
})
if err != nil {
t.Fatalf("NewRouter() error = %v", err)
@@ -375,7 +416,18 @@ func performAdminRequest(
) *httptest.ResponseRecorder {
t.Helper()
request := httptest.NewRequest(method, target, body)
request.RemoteAddr = "127.0.0.1:3210"
request.AddCookie(&http.Cookie{
Name: "cmroubao_admin_session",
Value: "test-session",
})
if method != http.MethodGet && method != http.MethodHead {
const csrfToken = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE"
request.AddCookie(&http.Cookie{
Name: "cmroubao_admin_csrf",
Value: csrfToken,
})
request.Header.Set("X-CSRF-Token", csrfToken)
}
if contentType != "" {
request.Header.Set("Content-Type", contentType)
}