91 lines
2.4 KiB
Go
91 lines
2.4 KiB
Go
package ctrlsubsonic
|
|
|
|
import (
|
|
"context"
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"go.senan.xyz/gonic/server/ctrlsubsonic/params"
|
|
"go.senan.xyz/gonic/server/ctrlsubsonic/spec"
|
|
)
|
|
|
|
func checkCredsToken(password, token, salt string) bool {
|
|
toHash := fmt.Sprintf("%s%s", password, salt)
|
|
hash := md5.Sum([]byte(toHash))
|
|
expToken := hex.EncodeToString(hash[:])
|
|
return token == expToken
|
|
}
|
|
|
|
func checkCredsBasic(password, given string) bool {
|
|
if len(given) >= 4 && given[:4] == "enc:" {
|
|
bytes, _ := hex.DecodeString(given[4:])
|
|
given = string(bytes)
|
|
}
|
|
return password == given
|
|
}
|
|
|
|
func (c *Controller) WithParams(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
params := params.New(r)
|
|
withParams := context.WithValue(r.Context(), CtxParams, params)
|
|
next.ServeHTTP(w, r.WithContext(withParams))
|
|
})
|
|
}
|
|
|
|
func (c *Controller) WithRequiredParams(next http.Handler) http.Handler {
|
|
requiredParameters := []string{
|
|
"u", "v", "c",
|
|
}
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
params := r.Context().Value(CtxParams).(params.Params)
|
|
for _, req := range requiredParameters {
|
|
param := params.Get(req)
|
|
if param != "" {
|
|
continue
|
|
}
|
|
_ = writeResp(w, r, spec.NewError(10,
|
|
"please provide a `%s` parameter", req))
|
|
return
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
func (c *Controller) WithUser(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
params := r.Context().Value(CtxParams).(params.Params)
|
|
username := params.Get("u")
|
|
password := params.Get("p")
|
|
token := params.Get("t")
|
|
salt := params.Get("s")
|
|
//
|
|
passwordAuth := token == "" && salt == ""
|
|
tokenAuth := password == ""
|
|
if tokenAuth == passwordAuth {
|
|
_ = writeResp(w, r, spec.NewError(10,
|
|
"please provide `t` and `s`, or just `p`"))
|
|
return
|
|
}
|
|
user := c.DB.GetUserFromName(username)
|
|
if user == nil {
|
|
_ = writeResp(w, r, spec.NewError(40,
|
|
"invalid username `%s`", username))
|
|
return
|
|
}
|
|
var credsOk bool
|
|
if tokenAuth {
|
|
credsOk = checkCredsToken(user.Password, token, salt)
|
|
} else {
|
|
credsOk = checkCredsBasic(user.Password, password)
|
|
}
|
|
if !credsOk {
|
|
_ = writeResp(w, r, spec.NewError(40, "invalid password"))
|
|
return
|
|
}
|
|
withUser := context.WithValue(r.Context(), CtxUser, user)
|
|
next.ServeHTTP(w, r.WithContext(withUser))
|
|
})
|
|
}
|