fix(subsonic): getUser answers an admin's lookup with the requested user (#6292)

An admin asking for another username got their own name and roles back.
The handler now looks the user up by exact username and answers with
that user's payload; an unknown username is ErrDataNotFound. A
non-admin asking about someone else is still ErrNotAuthorized.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 13:58:09 -04:00
co-authored by Claude Opus 5.5
parent 466912e202
commit 09e78962b2
3 changed files with 82 additions and 14 deletions
+30 -11
View File
@@ -1,9 +1,13 @@
package subsonic
import (
"context"
"encoding/xml"
"errors"
"net/http"
"github.com/jackc/pgx/v5"
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
)
@@ -38,26 +42,41 @@ type User struct {
VideoConversionRole bool `json:"videoConversionRole" xml:"videoConversionRole,attr"`
}
// handleGetUser returns the authenticated user when no username is provided
// or when the requested username matches; admins may look up any user. Per
// spec a non-admin asking about another user gets ErrNotAuthorized.
func handleGetUser(w http.ResponseWriter, r *http.Request) {
// userHandlers serves getUser. lookup finds a user by exact username; it is
// a field so tests can answer without a database.
type userHandlers struct {
lookup func(ctx context.Context, username string) (dbq.User, error)
}
// getUser returns the authenticated user when no username is provided or
// when the requested username matches; admins may look up any user, and an
// unknown username is ErrDataNotFound. Per spec a non-admin asking about
// another user gets ErrNotAuthorized.
func (h *userHandlers) getUser(w http.ResponseWriter, r *http.Request) {
caller, ok := UserFromContext(r.Context())
if !ok {
WriteFail(w, r, ErrNotAuthorized, "Not authenticated")
return
}
requested := r.URL.Query().Get("username")
if requested != "" && requested != caller.Username && !caller.IsAdmin {
if requested == "" || requested == caller.Username {
Write(w, r, UserResponse{Envelope: NewEnvelope("ok"), User: userPayload(caller)})
return
}
if !caller.IsAdmin {
WriteFail(w, r, ErrNotAuthorized, "Not authorized to view other users")
return
}
// No per-user lookup: an admin asking for another username is answered
// with the caller's own roles.
Write(w, r, UserResponse{
Envelope: NewEnvelope("ok"),
User: userPayload(caller),
})
target, err := h.lookup(r.Context(), requested)
if errors.Is(err, pgx.ErrNoRows) {
WriteFail(w, r, ErrDataNotFound, "User not found")
return
}
if err != nil {
WriteFail(w, r, ErrGeneric, "Failed to look up user")
return
}
Write(w, r, UserResponse{Envelope: NewEnvelope("ok"), User: userPayload(target)})
}
func userPayload(u dbq.User) User {