2026-04-10 03:25:23 -03:00
|
|
|
package internal
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func issueSessionCookie(t *testing.T, username, jfToken string) *http.Cookie {
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
|
|
origKey := sessionKey
|
|
|
|
|
sessionKey = []byte("pegasus-test-session-key")
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
sessionKey = origKey
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
rr := httptest.NewRecorder()
|
|
|
|
|
if err := SetSession(rr, username, jfToken); err != nil {
|
|
|
|
|
t.Fatalf("SetSession failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res := rr.Result()
|
|
|
|
|
defer res.Body.Close()
|
|
|
|
|
cookies := res.Cookies()
|
|
|
|
|
if len(cookies) != 1 {
|
|
|
|
|
t.Fatalf("expected 1 cookie, got %d", len(cookies))
|
|
|
|
|
}
|
|
|
|
|
return cookies[0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCurrentUserValidSession(t *testing.T) {
|
|
|
|
|
cookie := issueSessionCookie(t, "alice", "jf-token-123")
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/whoami", nil)
|
|
|
|
|
req.AddCookie(cookie)
|
|
|
|
|
|
|
|
|
|
claims, err := CurrentUser(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("CurrentUser returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if claims.Username != "alice" {
|
|
|
|
|
t.Fatalf("unexpected username %q", claims.Username)
|
|
|
|
|
}
|
|
|
|
|
if claims.JFToken != "jf-token-123" {
|
|
|
|
|
t.Fatalf("unexpected jellyfin token %q", claims.JFToken)
|
|
|
|
|
}
|
|
|
|
|
if claims.ExpiresAt == nil || claims.IssuedAt == nil {
|
|
|
|
|
t.Fatalf("expected exp+iat claims to be present")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCurrentUserNoCookie(t *testing.T) {
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
|
|
|
_, err := CurrentUser(req)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("expected unauthorized error when cookie is missing")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCurrentUserInvalidSignature(t *testing.T) {
|
|
|
|
|
origKey := sessionKey
|
|
|
|
|
sessionKey = []byte("runtime-key")
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
sessionKey = origKey
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
tok := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims{
|
|
|
|
|
Username: "alice",
|
|
|
|
|
JFToken: "bad-token",
|
|
|
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
|
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
|
|
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
signed, err := tok.SignedString([]byte("different-key"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("signing token failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
|
|
|
req.AddCookie(&http.Cookie{Name: CookieName, Value: signed})
|
|
|
|
|
|
|
|
|
|
_, err = CurrentUser(req)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("expected signature validation error")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCurrentUserMalformedToken(t *testing.T) {
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
|
|
|
req.AddCookie(&http.Cookie{Name: CookieName, Value: "not-a-jwt"})
|
|
|
|
|
|
|
|
|
|
_, err := CurrentUser(req)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("expected parse error for malformed token")
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-11 00:02:59 -03:00
|
|
|
|
|
|
|
|
func TestCurrentUserInvalidClaimType(t *testing.T) {
|
|
|
|
|
origParse := parseJWT
|
|
|
|
|
defer func() { parseJWT = origParse }()
|
|
|
|
|
|
|
|
|
|
parseJWT = func(string, jwt.Claims, jwt.Keyfunc, ...jwt.ParserOption) (*jwt.Token, error) {
|
|
|
|
|
return &jwt.Token{Valid: true, Claims: jwt.MapClaims{"ok": true}}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
|
|
|
req.AddCookie(&http.Cookie{Name: CookieName, Value: "token"})
|
|
|
|
|
|
|
|
|
|
_, err := CurrentUser(req)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("expected invalid-session error")
|
|
|
|
|
}
|
|
|
|
|
}
|