From c74c4790b0cd4edd439ee15b73e7098be96289c6 Mon Sep 17 00:00:00 2001 From: YouROK <8yourok8@mail.ru> Date: Thu, 4 Mar 2021 11:11:23 +0300 Subject: [PATCH] fix auth play --- server/web/api/stream.go | 76 ++++++++++++++++++++++++++++++++++++++++ server/web/auth/auth.go | 66 +++++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/server/web/api/stream.go b/server/web/api/stream.go index 1f56fd0..97de8c6 100644 --- a/server/web/api/stream.go +++ b/server/web/api/stream.go @@ -36,6 +36,17 @@ func stream(c *gin.Context) { _, play := c.GetQuery("play") title := c.Query("title") poster := c.Query("poster") + notAuth := c.GetBool("not_auth") + + if notAuth && play { + streamNoAuth(c) + return + } + if notAuth { + c.Header("WWW-Authenticate", "Basic realm=Authorization Required") + c.AbortWithStatus(http.StatusUnauthorized) + return + } if link == "" { c.AbortWithError(http.StatusBadRequest, errors.New("link should not be empty")) @@ -111,3 +122,68 @@ func stream(c *gin.Context) { return } } + +func streamNoAuth(c *gin.Context) { + link := c.Query("link") + indexStr := c.Query("index") + _, preload := c.GetQuery("preload") + title := c.Query("title") + poster := c.Query("poster") + + // c.Header("WWW-Authenticate", "Basic realm=Authorization Required") + // c.AbortWithStatus(http.StatusUnauthorized) + + if link == "" { + c.AbortWithError(http.StatusBadRequest, errors.New("link should not be empty")) + return + } + + if title == "" { + title = c.Param("fname") + title, _ = url.PathUnescape(title) + title = strings.TrimLeft(title, "/") + } else { + title, _ = url.QueryUnescape(title) + } + + link, _ = url.QueryUnescape(link) + poster, _ = url.QueryUnescape(poster) + + spec, err := utils.ParseLink(link) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + tor := torr.GetTorrent(spec.InfoHash.HexString()) + if tor == nil { + c.AbortWithError(http.StatusNotFound, errors.New("Torrent not found")) + return + } + + if !tor.GotInfo() { + c.AbortWithError(http.StatusInternalServerError, errors.New("timeout connection torrent")) + return + } + + // find file + index := -1 + if len(tor.Files()) == 1 { + index = 1 + } else { + ind, err := strconv.Atoi(indexStr) + if err == nil { + index = ind + } + } + if index == -1 { // if file index not set and play file exec + c.AbortWithError(http.StatusBadRequest, errors.New("\"index\" is empty or wrong")) + return + } + // preload torrent + if preload { + torr.Preload(tor, index) + } + + tor.Stream(index, c.Request, c.Writer) +} diff --git a/server/web/auth/auth.go b/server/web/auth/auth.go index 9a2b588..4aae796 100644 --- a/server/web/auth/auth.go +++ b/server/web/auth/auth.go @@ -1,9 +1,14 @@ package auth import ( + "encoding/base64" "encoding/json" "io/ioutil" + "net/http" "path/filepath" + "reflect" + "strings" + "unsafe" "github.com/gin-gonic/gin" @@ -19,7 +24,7 @@ func SetupAuth(engine *gin.Engine) *gin.RouterGroup { if accs == nil { return nil } - return engine.Group("/", gin.BasicAuth(accs)) + return engine.Group("/", BasicAuth(accs)) } func getAccounts() gin.Accounts { @@ -34,3 +39,62 @@ func getAccounts() gin.Accounts { } return accs } + +type authPair struct { + value string + user string +} +type authPairs []authPair + +func (a authPairs) searchCredential(authValue string) (string, bool) { + if authValue == "" { + return "", false + } + for _, pair := range a { + if pair.value == authValue { + return pair.user, true + } + } + return "", false +} + +func BasicAuth(accounts gin.Accounts) gin.HandlerFunc { + pairs := processAccounts(accounts) + return func(c *gin.Context) { + user, found := pairs.searchCredential(c.Request.Header.Get("Authorization")) + if !found { + if strings.HasPrefix(c.FullPath(), "/stream") { + c.Set("not_auth", true) + return + } + c.Header("WWW-Authenticate", "Basic realm=Authorization Required") + c.AbortWithStatus(http.StatusUnauthorized) + return + } + c.Set(gin.AuthUserKey, user) + } +} + +func processAccounts(accounts gin.Accounts) authPairs { + pairs := make(authPairs, 0, len(accounts)) + for user, password := range accounts { + value := authorizationHeader(user, password) + pairs = append(pairs, authPair{ + value: value, + user: user, + }) + } + return pairs +} + +func authorizationHeader(user, password string) string { + base := user + ":" + password + return "Basic " + base64.StdEncoding.EncodeToString(StringToBytes(base)) +} + +func StringToBytes(s string) (b []byte) { + sh := *(*reflect.StringHeader)(unsafe.Pointer(&s)) + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len + return b +}