2020-01-20 00:21:44 +01:00
|
|
|
package subsonic
|
2020-01-07 20:56:26 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-03-01 02:01:09 +01:00
|
|
|
"net"
|
2020-01-07 20:56:26 +01:00
|
|
|
"net/http"
|
2020-01-27 21:10:46 +01:00
|
|
|
"net/url"
|
|
|
|
"strings"
|
2020-01-07 20:56:26 +01:00
|
|
|
|
2020-01-24 01:44:08 +01:00
|
|
|
"github.com/deluan/navidrome/engine"
|
|
|
|
"github.com/deluan/navidrome/log"
|
|
|
|
"github.com/deluan/navidrome/model"
|
2020-05-13 22:49:55 +02:00
|
|
|
"github.com/deluan/navidrome/model/request"
|
2020-01-24 01:44:08 +01:00
|
|
|
"github.com/deluan/navidrome/server/subsonic/responses"
|
2020-02-07 00:55:38 +01:00
|
|
|
"github.com/deluan/navidrome/utils"
|
2020-01-07 20:56:26 +01:00
|
|
|
)
|
|
|
|
|
2020-03-01 02:01:09 +01:00
|
|
|
const (
|
|
|
|
cookieExpiry = 365 * 24 * 3600 // One year
|
|
|
|
)
|
|
|
|
|
2020-01-27 21:10:46 +01:00
|
|
|
func postFormToQueryParams(next http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
|
|
|
SendError(w, r, NewError(responses.ErrorGeneric, err.Error()))
|
|
|
|
}
|
|
|
|
var parts []string
|
|
|
|
for key, values := range r.Form {
|
|
|
|
for _, v := range values {
|
|
|
|
parts = append(parts, url.QueryEscape(key)+"="+url.QueryEscape(v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
r.URL.RawQuery = strings.Join(parts, "&")
|
|
|
|
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-01-07 20:56:26 +01:00
|
|
|
func checkRequiredParameters(next http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
requiredParameters := []string{"u", "v", "c"}
|
|
|
|
|
|
|
|
for _, p := range requiredParameters {
|
2020-02-07 00:55:38 +01:00
|
|
|
if utils.ParamString(r, p) == "" {
|
2020-01-07 20:56:26 +01:00
|
|
|
msg := fmt.Sprintf(`Missing required parameter "%s"`, p)
|
2020-01-09 02:45:07 +01:00
|
|
|
log.Warn(r, msg)
|
2020-01-07 20:56:26 +01:00
|
|
|
SendError(w, r, NewError(responses.ErrorMissingParameter, msg))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-13 22:49:55 +02:00
|
|
|
username := utils.ParamString(r, "u")
|
2020-02-07 00:55:38 +01:00
|
|
|
client := utils.ParamString(r, "c")
|
|
|
|
version := utils.ParamString(r, "v")
|
2020-01-07 20:56:26 +01:00
|
|
|
ctx := r.Context()
|
2020-05-13 22:49:55 +02:00
|
|
|
ctx = request.WithUsername(ctx, username)
|
|
|
|
ctx = request.WithClient(ctx, client)
|
|
|
|
ctx = request.WithVersion(ctx, version)
|
|
|
|
log.Debug(ctx, "API: New request "+r.URL.Path, "username", username, "client", client, "version", version)
|
2020-01-07 20:56:26 +01:00
|
|
|
|
2020-01-09 16:35:00 +01:00
|
|
|
r = r.WithContext(ctx)
|
2020-01-07 20:56:26 +01:00
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-01-20 19:35:59 +01:00
|
|
|
func authenticate(users engine.Users) func(next http.Handler) http.Handler {
|
|
|
|
return func(next http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2020-02-07 00:55:38 +01:00
|
|
|
username := utils.ParamString(r, "u")
|
|
|
|
pass := utils.ParamString(r, "p")
|
|
|
|
token := utils.ParamString(r, "t")
|
|
|
|
salt := utils.ParamString(r, "s")
|
|
|
|
jwt := utils.ParamString(r, "jwt")
|
2020-01-07 20:56:26 +01:00
|
|
|
|
2020-02-06 22:48:35 +01:00
|
|
|
usr, err := users.Authenticate(r.Context(), username, pass, token, salt, jwt)
|
2020-01-20 19:35:59 +01:00
|
|
|
if err == model.ErrInvalidAuth {
|
2020-01-21 00:12:17 +01:00
|
|
|
log.Warn(r, "Invalid login", "username", username, err)
|
2020-01-20 19:35:59 +01:00
|
|
|
} else if err != nil {
|
2020-01-21 00:12:17 +01:00
|
|
|
log.Error(r, "Error authenticating username", "username", username, err)
|
2020-01-07 20:56:26 +01:00
|
|
|
}
|
|
|
|
|
2020-01-20 19:35:59 +01:00
|
|
|
if err != nil {
|
2020-01-21 00:12:17 +01:00
|
|
|
log.Warn(r, "Invalid login", "username", username)
|
2020-01-20 19:35:59 +01:00
|
|
|
SendError(w, r, NewError(responses.ErrorAuthenticationFail))
|
|
|
|
return
|
|
|
|
}
|
2020-01-07 20:56:26 +01:00
|
|
|
|
2020-01-21 00:12:17 +01:00
|
|
|
ctx := r.Context()
|
2020-05-13 22:49:55 +02:00
|
|
|
ctx = request.WithUser(ctx, *usr)
|
2020-01-21 00:12:17 +01:00
|
|
|
r = r.WithContext(ctx)
|
|
|
|
|
2020-01-20 19:35:59 +01:00
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
})
|
|
|
|
}
|
2020-01-07 20:56:26 +01:00
|
|
|
}
|
2020-03-01 02:01:09 +01:00
|
|
|
|
|
|
|
func getPlayer(players engine.Players) func(next http.Handler) http.Handler {
|
|
|
|
return func(next http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
2020-05-13 22:49:55 +02:00
|
|
|
userName, _ := request.UsernameFrom(ctx)
|
|
|
|
client, _ := request.ClientFrom(ctx)
|
2020-03-01 02:01:09 +01:00
|
|
|
playerId := playerIDFromCookie(r, userName)
|
|
|
|
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
2020-03-16 17:56:15 +01:00
|
|
|
player, trc, err := players.Register(ctx, playerId, client, r.Header.Get("user-agent"), ip)
|
2020-03-01 02:01:09 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Error("Could not register player", "userName", userName, "client", client)
|
2020-03-12 15:00:37 +01:00
|
|
|
} else {
|
2020-05-13 22:49:55 +02:00
|
|
|
ctx = request.WithPlayer(ctx, *player)
|
2020-03-16 17:56:15 +01:00
|
|
|
if trc != nil {
|
2020-05-13 22:49:55 +02:00
|
|
|
ctx = request.WithTranscoding(ctx, *trc)
|
2020-03-16 17:56:15 +01:00
|
|
|
}
|
2020-03-12 15:00:37 +01:00
|
|
|
r = r.WithContext(ctx)
|
2020-03-01 02:01:09 +01:00
|
|
|
|
2020-04-05 02:26:36 +02:00
|
|
|
cookie := &http.Cookie{
|
|
|
|
Name: playerIDCookieName(userName),
|
|
|
|
Value: player.ID,
|
|
|
|
MaxAge: cookieExpiry,
|
|
|
|
HttpOnly: true,
|
|
|
|
Path: "/",
|
|
|
|
}
|
|
|
|
http.SetCookie(w, cookie)
|
2020-03-01 02:01:09 +01:00
|
|
|
}
|
2020-04-05 02:26:36 +02:00
|
|
|
|
2020-03-01 02:01:09 +01:00
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func playerIDFromCookie(r *http.Request, userName string) string {
|
|
|
|
cookieName := playerIDCookieName(userName)
|
|
|
|
var playerId string
|
|
|
|
if c, err := r.Cookie(cookieName); err == nil {
|
|
|
|
playerId = c.Value
|
|
|
|
log.Trace(r, "playerId found in cookies", "playerId", playerId)
|
|
|
|
}
|
|
|
|
return playerId
|
|
|
|
}
|
|
|
|
|
|
|
|
func playerIDCookieName(userName string) string {
|
|
|
|
cookieName := fmt.Sprintf("nd-player-%x", userName)
|
|
|
|
return cookieName
|
|
|
|
}
|