0.2.45 - Advanced analytics, logout old versions
This commit is contained in:
@@ -126,7 +126,7 @@ func (s *Server) Routes() http.Handler {
|
||||
// once, without the gate ever touching health checks or the admin page.
|
||||
v1 := http.NewServeMux()
|
||||
|
||||
v1.HandleFunc("POST /v1/auth/login", s.handleLogin)
|
||||
v1.HandleFunc("POST /v1/auth/login", s.requireSupportedClient(s.handleLogin))
|
||||
v1.Handle("POST /v1/auth/logout", s.authed(s.handleLogout))
|
||||
v1.Handle("GET /v1/auth/session", s.authed(s.handleSession))
|
||||
v1.Handle("GET /v1/auth/devices", s.authed(s.handleDevices))
|
||||
@@ -196,6 +196,7 @@ func (s *Server) Routes() http.Handler {
|
||||
|
||||
v1.Handle("POST /v1/playback/{phase}", s.authed(s.handlePlaybackReport))
|
||||
v1.Handle("POST /v1/analytics/rows", s.authed(s.handleRowAnalytics))
|
||||
v1.Handle("POST /v1/analytics/events", s.authed(s.handleJourneyAnalytics))
|
||||
|
||||
v1.Handle("GET /v1/images/{itemId}/{imageType}", s.authed(s.handleImage))
|
||||
|
||||
@@ -204,8 +205,8 @@ func (s *Server) Routes() http.Handler {
|
||||
mux.HandleFunc("GET /readyz", s.handleReady)
|
||||
// Update policy is app-scoped, not user-scoped. Keep it outside authentication and
|
||||
// maintenance so a fresh install, a signed-out TV, and a retired build can all learn
|
||||
// whether the server requires an update without touching a viewer session.
|
||||
mux.HandleFunc("GET /v1/update", s.handleUpdate)
|
||||
// whether the server requires an update. A valid session enriches only its log context.
|
||||
mux.Handle("GET /v1/update", s.identifyOptionalSession(http.HandlerFunc(s.handleUpdate)))
|
||||
// Exact route outside the maintenance gate: signed-in clients poll this lightweight
|
||||
// status even while every normal /v1 operation is deliberately unavailable.
|
||||
mux.Handle("GET /v1/status", s.authed(s.handleServiceStatus))
|
||||
@@ -230,6 +231,21 @@ func (s *Server) Routes() http.Handler {
|
||||
|
||||
type authedFunc func(http.ResponseWriter, *http.Request, store.Session)
|
||||
|
||||
// identifyOptionalSession gives public routes the viewer and television attached to a
|
||||
// valid bearer token without turning authentication into a condition of access. The
|
||||
// update check must remain reachable before sign-in, but an offer made to a signed-in
|
||||
// client should still say whose session is affected in the logs.
|
||||
func (s *Server) identifyOptionalSession(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if token := bearerToken(r); token != "" {
|
||||
if sess, err := s.sessionFor(r.Context(), token); err == nil {
|
||||
identify(r.Context(), sess)
|
||||
}
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// authed resolves the bearer token to a session before running h.
|
||||
//
|
||||
// Images are also accepted with a `t=` query parameter: Coil builds plain URLs from the
|
||||
@@ -253,6 +269,26 @@ func (s *Server) authed(h authedFunc) http.Handler {
|
||||
}
|
||||
sess = s.captureClientIdentity(r, sess)
|
||||
identify(r.Context(), sess)
|
||||
decision := s.updateDecision(r)
|
||||
if mustRetireForUpdate(decision, clientVersion(r)) {
|
||||
// Mirror an ordinary sign-out closely enough that this token cannot be restored
|
||||
// from either database or Redis. The 401 is intentional: every supported client
|
||||
// treats it as authoritative and removes the rejected local profile.
|
||||
if err := s.store.DeleteSession(r.Context(), sess.TokenHash); err != nil {
|
||||
s.loggerFor(r.Context()).Error("required-update session delete failed", "error", err)
|
||||
}
|
||||
_ = s.cache.Delete(r.Context(), cache.SessionKey(hexHash(sess.TokenHash)))
|
||||
_ = s.cache.InvalidateUser(r.Context(), sess.EmbyUserID)
|
||||
s.loggerFor(r.Context()).Info("signed out for required update",
|
||||
"device_id", sess.DeviceID,
|
||||
"from", clientLogValue(clientVersion(r)),
|
||||
"minimum", forcedUpdateFloor,
|
||||
"to", decision.Version,
|
||||
)
|
||||
w.Header().Set("X-Memby-Update-Required", decision.Version)
|
||||
writeError(w, http.StatusUnauthorized, "Memby must be updated before signing in again")
|
||||
return
|
||||
}
|
||||
h(w, r, sess)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user