package httpapi import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "time" "git.nianxx.cn/wangxuming/NianAIGC/backend/internal/identity" ) type passwordChangerStub struct { command identity.PasswordChangeCommand session identity.Session err error } func (s *passwordChangerStub) Change(_ context.Context, command identity.PasswordChangeCommand) (identity.Session, error) { s.command = command return s.session, s.err } func TestAuthPasswordChangeReplacesSignedSessionCookie(t *testing.T) { secret := "password-change-secret-with-enough-entropy" now := time.Now().UTC().Truncate(time.Second) version := 4 changer := &passwordChangerStub{session: identity.Session{Version: 1, AuthMode: identity.AuthModeUser, IssuedAt: now.Unix(), ExpiresAt: now.Add(time.Hour).Unix(), SessionVersion: &version, User: identity.User{ID: "u", Subject: "u", Phone: "13800138000", DisplayName: "User", ClientID: "platform", Role: "user", Status: "active", Authorities: []string{"ROLE_USER"}, Scope: []string{}}}} resolver := &platformSessionResolverStub{outcome: "authenticated", session: identity.Session{AuthMode: identity.AuthModeUser, User: identity.User{ID: "u", Role: "user"}}} authorizer, _ := NewPlatformAuthorizer(AuthState{Required: true, Configured: true}, resolver) handler, err := NewAuthPasswordChangeHandler(PasswordChangeConfig{SessionSecret: secret}, authorizer, changer) if err != nil { t.Fatal(err) } r := httptest.NewRequest(http.MethodPost, "https://app.test/api/auth/password/change", strings.NewReader(`{"currentPassword":" current-password ","newPassword":"next-password","confirmPassword":"next-password"}`)) r.AddCookie(&http.Cookie{Name: identity.SessionCookieName, Value: "existing"}) w := httptest.NewRecorder() handler.ServeHTTP(w, r) if w.Code != http.StatusOK || changer.command.AccountID != "u" || changer.command.CurrentPassword != "current-password" { t.Fatalf("status=%d command=%+v body=%q", w.Code, changer.command, w.Body.String()) } cookies := w.Result().Cookies() if len(cookies) != identity.CookieMaxChunks { t.Fatalf("cookies=%d", len(cookies)) } signed := cookies[0].Value parsed, err := identity.Parse(signed, secret, now) if err != nil || parsed.SessionVersion == nil || *parsed.SessionVersion != 4 { t.Fatalf("parse=%+v err=%v", parsed, err) } var body map[string]any if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil || body["ok"] != true { t.Fatalf("body=%q err=%v", w.Body.String(), err) } } func TestAuthPasswordChangeValidatesConfirmation(t *testing.T) { resolver := &platformSessionResolverStub{outcome: "authenticated", session: identity.Session{AuthMode: identity.AuthModeUser, User: identity.User{ID: "u", Role: "user"}}} authorizer, _ := NewPlatformAuthorizer(AuthState{Required: true, Configured: true}, resolver) changer := &passwordChangerStub{} handler, _ := NewAuthPasswordChangeHandler(PasswordChangeConfig{SessionSecret: "secret"}, authorizer, changer) r := httptest.NewRequest(http.MethodPost, "/api/auth/password/change", strings.NewReader(`{"currentPassword":"old-password","newPassword":"new-password","confirmPassword":"different"}`)) r.AddCookie(&http.Cookie{Name: identity.SessionCookieName, Value: "cookie"}) w := httptest.NewRecorder() handler.ServeHTTP(w, r) if w.Code != http.StatusBadRequest || changer.command.AccountID != "" { t.Fatalf("status=%d command=%+v", w.Code, changer.command) } }