Files
mule-image/sidecar/auth.go
dtoro 8f97590d9f sidecar: scoped labels + counts proxy (fixes cross-user label leak)
- New GET /api/sidecar/labels — proxies PP's labels, recalculates
  PhotoCount per user's BasePath via DB query
- New GET /api/sidecar/counts — returns user-scoped sidebar badges
  (all, review, archived, private, photos, videos, favorites)
- Fixed auth middleware to expose userUID and basePath on context
- Fixed ppClient.resolveSession — uses correct endpoint
  (GET /api/v1/session, not /api/v1/session/{token}) and correct
  JSON field names (UID, Name instead of UserUID, UserName)
- Frontend: listLabels now calls /api/sidecar/labels instead of /api/v1/labels
2026-06-06 19:23:22 +02:00

88 lines
2.0 KiB
Go

package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// requireSession is the standard auth shim every mutating handler wears.
// We don't store a shared service credential — the caller's X-Auth-Token
// is the only authority, and we probe PhotoPrism with it before doing any
// destructive work. The handler reads the validated token off the context
// via ctxToken so it can keep forwarding it to PhotoPrism for the actual
// operation. The resolved username is available via ctxUserName.
func requireSession(pp *ppClient) gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("X-Auth-Token")
if token == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "no token"})
return
}
user := pp.resolveSession(c.Request.Context(), token)
if user == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid session"})
return
}
c.Set("token", token)
c.Set("userName", user.UserName)
c.Set("userUID", user.UserUID)
c.Set("basePath", user.BasePath)
c.Next()
}
}
// ctxToken returns the validated X-Auth-Token a previous requireSession
// middleware stored on the request. Handlers MUST run behind that
// middleware; otherwise this returns the empty string.
func ctxToken(c *gin.Context) string {
v, ok := c.Get("token")
if !ok {
return ""
}
s, ok := v.(string)
if !ok {
return ""
}
return s
}
// ctxUserName returns the PhotoPrism username resolved by requireSession.
func ctxUserName(c *gin.Context) string {
v, ok := c.Get("userName")
if !ok {
return ""
}
s, ok := v.(string)
if !ok {
return ""
}
return s
}
// ctxUserUID returns the PhotoPrism user UID resolved by requireSession.
func ctxUserUID(c *gin.Context) string {
v, ok := c.Get("userUID")
if !ok {
return ""
}
s, ok := v.(string)
if !ok {
return ""
}
return s
}
// ctxBasePath returns the PhotoPrism user BasePath resolved by requireSession.
func ctxBasePath(c *gin.Context) string {
v, ok := c.Get("basePath")
if !ok {
return ""
}
s, ok := v.(string)
if !ok {
return ""
}
return s
}