fix: scope marks, labels, and subjects to the authenticated user
Marks (ratings/color labels) were stored without a user column — every user saw every other user's marks. Labels and subjects from PhotoPrism's global endpoints leaked across users because those endpoints ignore BasePath ACL. Sidecar: - Add UserName as composite primary key on Mark (photo_uid, user_name) - Replace validateSession with resolveSession that fetches the user identity from PhotoPrism's session endpoint - Filter all mark queries by user_name Frontend: - Filter listLabels/listSubjects through a BasePath-aware existence check — each label/subject is kept only if the user has at least one matching photo (single count=1 probe per item, batched at concurrency 8) - Skip filtering for admin users with empty BasePath (single-user compat) Also documents USER_BASEPATHS in .env.example — the env var that drives per-user library isolation via PhotoPrism's auth_users.base_path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ import (
|
||||
// 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.
|
||||
// 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")
|
||||
@@ -19,11 +19,13 @@ func requireSession(pp *ppClient) gin.HandlerFunc {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "no token"})
|
||||
return
|
||||
}
|
||||
if !pp.validateSession(c.Request.Context(), token) {
|
||||
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.Next()
|
||||
}
|
||||
}
|
||||
@@ -42,3 +44,16 @@ func ctxToken(c *gin.Context) string {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user