mirror of
https://github.com/MengMengCode/VoCat.git
synced 2026-08-13 03:13:43 +08:00
fix: revoke sessions after updates
This commit is contained in:
@@ -3,11 +3,13 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"vocat/internal/device"
|
||||
"vocat/internal/modem"
|
||||
@@ -354,7 +356,22 @@ func TestHandleUpdateCheckUsesTrustedRepository(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleUpdateApplyInstallsFromTrustedRepository(t *testing.T) {
|
||||
database, err := store.Open(context.Background(), ":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = database.Close() })
|
||||
if err := database.SetAdmin(context.Background(), "admin", []byte("hash")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tokenHash := []byte("active-session")
|
||||
if err := database.CreateSession(
|
||||
context.Background(), 1, tokenHash, []byte("csrf"), time.Now().Add(time.Hour),
|
||||
); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
server := &Server{
|
||||
store: database,
|
||||
logger: regionTestLogger(),
|
||||
updateRepository: update.DefaultRepository,
|
||||
updateApply: func(_ context.Context, _ *slog.Logger, options update.Options, restart bool) (update.CheckResult, error) {
|
||||
@@ -370,9 +387,21 @@ func TestHandleUpdateApplyInstallsFromTrustedRepository(t *testing.T) {
|
||||
t.Fatalf("status = %d, body = %s", recorder.Code, recorder.Body)
|
||||
}
|
||||
data := decodeData(t, recorder)
|
||||
if data["applied"] != true || data["version"] != "9.9.9" {
|
||||
if data["applied"] != true || data["version"] != "9.9.9" || data["reauthentication_required"] != true {
|
||||
t.Fatalf("apply data = %#v", data)
|
||||
}
|
||||
if _, err := database.SessionByTokenHash(context.Background(), tokenHash); !errors.Is(err, store.ErrNotFound) {
|
||||
t.Fatalf("session must be revoked after update, got %v", err)
|
||||
}
|
||||
expired := map[string]bool{}
|
||||
for _, cookie := range recorder.Result().Cookies() {
|
||||
if cookie.MaxAge < 0 {
|
||||
expired[cookie.Name] = true
|
||||
}
|
||||
}
|
||||
if !expired[sessionCookieName] || !expired[csrfCookieName] {
|
||||
t.Fatalf("auth cookies were not expired: %#v", recorder.Result().Cookies())
|
||||
}
|
||||
}
|
||||
|
||||
func TestE911WebsheetFlow(t *testing.T) {
|
||||
|
||||
@@ -421,11 +421,22 @@ func (s *Server) handleUpdateApply(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
return
|
||||
}
|
||||
// A binary update changes the trusted server code underneath every active
|
||||
// browser/API session. Revoke every durable token before scheduling the
|
||||
// restart and expire this client's cookies so all users must authenticate
|
||||
// against the newly installed version.
|
||||
if err := s.store.DeleteAllSessions(r.Context()); err != nil {
|
||||
s.logger.Error("revoke sessions after update failed", "error", err)
|
||||
writeError(w, http.StatusInternalServerError, "update_session_revocation_failed", "The update was installed, but active sessions could not be revoked; restart the service and sign in again.")
|
||||
return
|
||||
}
|
||||
s.clearAuthCookies(w)
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"data": map[string]any{
|
||||
"applied": true,
|
||||
"version": result.Latest,
|
||||
"message": "Update verified and installed; the service is restarting.",
|
||||
"applied": true,
|
||||
"version": result.Latest,
|
||||
"reauthentication_required": true,
|
||||
"message": "Update verified and installed; all sessions were revoked and the service is restarting.",
|
||||
},
|
||||
})
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
|
||||
Reference in New Issue
Block a user