From af14dbbeb870d3e2d76cb2c6e396e5d6726c8a85 Mon Sep 17 00:00:00 2001 From: Viacheslav Evseev Date: Thu, 22 Feb 2024 01:47:50 +0300 Subject: [PATCH 1/7] Fix docker check env flags Checking for the value of an environment variable according to README.md, not for its existing. --- docker-entrypoint.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index ac67b7a..4bf7262 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,9 +1,9 @@ #!/bin/sh FLAGS="--path $TS_CONF_PATH --logpath $TS_LOG_PATH --port $TS_PORT --torrentsdir $TS_TORR_DIR" -if [[ -n "$TS_HTTPAUTH" ]]; then FLAGS="${FLAGS} --httpauth"; fi -if [[ -n "$TS_RDB" ]]; then FLAGS="${FLAGS} --rdb"; fi -if [[ -n "$TS_DONTKILL" ]]; then FLAGS="${FLAGS} --dontkill"; fi +if [[ "$TS_HTTPAUTH" -eq 1 ]]; then FLAGS="${FLAGS} --httpauth"; fi +if [[ "$TS_RDB" -eq 1 ]]; then FLAGS="${FLAGS} --rdb"; fi +if [[ "$TS_DONTKILL" -eq 1 ]]; then FLAGS="${FLAGS} --dontkill"; fi if [ ! -d $TS_CONF_PATH ]; then mkdir -p $TS_CONF_PATH From c3f89042f91382b3fb3b571285a45c1484bf4308 Mon Sep 17 00:00:00 2001 From: Viacheslav Evseev Date: Thu, 22 Feb 2024 01:50:03 +0300 Subject: [PATCH 2/7] Refactor auth code --- server/docs/docs.go | 17 ++--------- server/docs/swagger.json | 17 ++--------- server/docs/swagger.yaml | 13 ++------- server/web/api/play.go | 3 +- server/web/api/route.go | 26 ++++++++++------- server/web/api/stream.go | 3 +- server/web/auth/auth.go | 47 +++++++++++++++--------------- server/web/msx/msx.go | 11 ++++--- server/web/pages/route.go | 11 ++++--- server/web/pages/template/route.go | 3 +- server/web/server.go | 15 ++++------ 11 files changed, 68 insertions(+), 98 deletions(-) diff --git a/server/docs/docs.go b/server/docs/docs.go index b2c4f5b..ad7ff29 100644 --- a/server/docs/docs.go +++ b/server/docs/docs.go @@ -241,12 +241,6 @@ const docTemplate = `{ "name": "id", "in": "path", "required": true - }, - { - "type": "boolean", - "description": "Not authenticated", - "name": "not_auth", - "in": "query" } ], "responses": { @@ -458,7 +452,7 @@ const docTemplate = `{ }, { "type": "string", - "description": "Get m3u from last play", + "description": "Get M3U from last played file", "name": "fromlast", "in": "query" }, @@ -475,17 +469,10 @@ const docTemplate = `{ "in": "query", "required": true }, - { - "type": "string", - "description": "File index in torrent", - "name": "poster", - "in": "query", - "required": true - }, { "type": "string", "description": "Set poster link of torrent", - "name": "not_auth", + "name": "poster", "in": "query", "required": true } diff --git a/server/docs/swagger.json b/server/docs/swagger.json index 52ef964..454c1e7 100644 --- a/server/docs/swagger.json +++ b/server/docs/swagger.json @@ -234,12 +234,6 @@ "name": "id", "in": "path", "required": true - }, - { - "type": "boolean", - "description": "Not authenticated", - "name": "not_auth", - "in": "query" } ], "responses": { @@ -451,7 +445,7 @@ }, { "type": "string", - "description": "Get m3u from last play", + "description": "Get M3U from last played file", "name": "fromlast", "in": "query" }, @@ -468,17 +462,10 @@ "in": "query", "required": true }, - { - "type": "string", - "description": "File index in torrent", - "name": "poster", - "in": "query", - "required": true - }, { "type": "string", "description": "Set poster link of torrent", - "name": "not_auth", + "name": "poster", "in": "query", "required": true } diff --git a/server/docs/swagger.yaml b/server/docs/swagger.yaml index fe094f3..79174d1 100644 --- a/server/docs/swagger.yaml +++ b/server/docs/swagger.yaml @@ -452,10 +452,6 @@ paths: name: id required: true type: string - - description: Not authenticated - in: query - name: not_auth - type: boolean produces: - application/octet-stream responses: @@ -592,7 +588,7 @@ paths: in: query name: m3u type: string - - description: Get m3u from last play + - description: Get M3U from last played file in: query name: fromlast type: string @@ -605,14 +601,9 @@ paths: name: title required: true type: string - - description: File index in torrent - in: query - name: poster - required: true - type: string - description: Set poster link of torrent in: query - name: not_auth + name: poster required: true type: string produces: diff --git a/server/web/api/play.go b/server/web/api/play.go index 3a4c946..48d4180 100644 --- a/server/web/api/play.go +++ b/server/web/api/play.go @@ -21,7 +21,6 @@ import ( // // @Param hash path string true "Torrent hash" // @Param id path string true "File index in torrent" -// @Param not_auth query bool false "Not authenticated" // // @Produce application/octet-stream // @Success 200 "Torrent data" @@ -29,7 +28,7 @@ import ( func play(c *gin.Context) { hash := c.Param("hash") indexStr := c.Param("id") - notAuth := c.GetBool("not_auth") + notAuth := c.GetBool("auth_required") && c.GetString(gin.AuthUserKey) == "" if hash == "" || indexStr == "" { c.AbortWithError(http.StatusNotFound, errors.New("link should not be empty")) diff --git a/server/web/api/route.go b/server/web/api/route.go index 53c8ebe..94002d4 100644 --- a/server/web/api/route.go +++ b/server/web/api/route.go @@ -1,6 +1,8 @@ package api import ( + "server/web/auth" + "github.com/gin-gonic/gin" ) @@ -8,15 +10,17 @@ type requestI struct { Action string `json:"action,omitempty"` } -func SetupRoute(route *gin.RouterGroup) { - route.GET("/shutdown", shutdown) +func SetupRoute(route gin.IRouter) { + authorized := route.Group("/", auth.CheckAuth()) - route.POST("/settings", settings) + authorized.GET("/shutdown", shutdown) - route.POST("/torrents", torrents) - route.POST("/torrent/upload", torrentUpload) + authorized.POST("/settings", settings) - route.POST("/cache", cache) + authorized.POST("/torrents", torrents) + authorized.POST("/torrent/upload", torrentUpload) + + authorized.POST("/cache", cache) route.HEAD("/stream", stream) route.HEAD("/stream/*fname", stream) @@ -27,15 +31,15 @@ func SetupRoute(route *gin.RouterGroup) { route.HEAD("/play/:hash/:id", play) route.GET("/play/:hash/:id", play) - route.POST("/viewed", viewed) + authorized.POST("/viewed", viewed) - route.GET("/playlistall/all.m3u", allPlayList) + authorized.GET("/playlistall/all.m3u", allPlayList) route.GET("/playlist", playList) route.GET("/playlist/*fname", playList) // Is this endpoint still needed ? `fname` is never used in handler - route.GET("/download/:size", download) + authorized.GET("/download/:size", download) - route.GET("/search/*query", rutorSearch) + authorized.GET("/search/*query", rutorSearch) - route.GET("/ffp/:hash/:id", ffp) + authorized.GET("/ffp/:hash/:id", ffp) } diff --git a/server/web/api/stream.go b/server/web/api/stream.go index 9e16916..6f9ab57 100644 --- a/server/web/api/stream.go +++ b/server/web/api/stream.go @@ -45,7 +45,6 @@ import ( // @Param play query string false "Start stream torrent" // @Param title query string true "Set title of torrent" // @Param poster query string true "Set poster link of torrent" -// @Param not_auth query string true "Stream / playlist without authentication" // // @Produce application/octet-stream // @Success 200 "Data returned according to query" @@ -62,7 +61,7 @@ func stream(c *gin.Context) { title := c.Query("title") poster := c.Query("poster") data := "" - notAuth := c.GetBool("not_auth") + notAuth := c.GetBool("auth_required") && c.GetString(gin.AuthUserKey) == "" if notAuth && (play || m3u) { streamNoAuth(c) diff --git a/server/web/auth/auth.go b/server/web/auth/auth.go index cb5eb08..bcc60f5 100644 --- a/server/web/auth/auth.go +++ b/server/web/auth/auth.go @@ -6,8 +6,6 @@ import ( "net/http" "os" "path/filepath" - "reflect" - "strings" "unsafe" "github.com/gin-gonic/gin" @@ -16,15 +14,15 @@ import ( "server/settings" ) -func SetupAuth(engine *gin.Engine) *gin.RouterGroup { +func SetupAuth(engine *gin.Engine) { if !settings.HttpAuth { - return nil + return } accs := getAccounts() if accs == nil { - return nil + return } - return engine.Group("/", BasicAuth(accs)) + engine.Use(BasicAuth(accs)) } func getAccounts() gin.Accounts { @@ -60,22 +58,28 @@ func (a authPairs) searchCredential(authValue string) (string, bool) { func BasicAuth(accounts gin.Accounts) gin.HandlerFunc { pairs := processAccounts(accounts) - return func(c *gin.Context) { + return func(c *gin.Context) { + c.Set("auth_required", true) + user, found := pairs.searchCredential(c.Request.Header.Get("Authorization")) - if !found { // always accessible - if strings.HasPrefix(c.FullPath(), "/stream") || - c.FullPath() == "/site.webmanifest" || - // https://github.com/YouROK/TorrServer/issues/172 - (strings.HasPrefix(c.FullPath(), "/play") && c.FullPath() != "/playlistall/all.m3u") || - (settings.SearchWA && strings.HasPrefix(c.FullPath(), "/search")) { - c.Set("not_auth", true) - return - } - c.Header("WWW-Authenticate", "Basic realm=Authorization Required") - c.AbortWithStatus(http.StatusUnauthorized) + if found { + c.Set(gin.AuthUserKey, user) + } + } +} + +func CheckAuth() gin.HandlerFunc { + return func(c *gin.Context) { + if !settings.HttpAuth { return } - c.Set(gin.AuthUserKey, user) + + if _, ok := c.Get(gin.AuthUserKey); ok { + return + } + + c.Header("WWW-Authenticate", "Basic realm=Authorization Required") + c.AbortWithStatus(http.StatusUnauthorized) } } @@ -97,8 +101,5 @@ func authorizationHeader(user, password string) string { } 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 + return unsafe.Slice(unsafe.StringData(s), len(s)) } diff --git a/server/web/msx/msx.go b/server/web/msx/msx.go index d5a74e2..d87f321 100644 --- a/server/web/msx/msx.go +++ b/server/web/msx/msx.go @@ -7,6 +7,7 @@ import ( "sync" "server/version" + "server/web/auth" "github.com/gin-gonic/gin" ) @@ -30,10 +31,12 @@ func asset(c *gin.Context, t string, d []byte) { c.Data(200, t+"; charset=UTF-8", d) } -func SetupRoute(r *gin.RouterGroup) { - r.GET("/msx/:pth", msxPTH) - r.GET("/msx/imdb", msxIMDB) - r.GET("/msx/imdb/:id", msxIMDBID) +func SetupRoute(r gin.IRouter) { + authorized := r.Group("/", auth.CheckAuth()) + + authorized.GET("/msx/:pth", msxPTH) + authorized.GET("/msx/imdb", msxIMDB) + authorized.GET("/msx/imdb/:id", msxIMDBID) } // msxPTH godoc diff --git a/server/web/pages/route.go b/server/web/pages/route.go index 90a5090..96a100f 100644 --- a/server/web/pages/route.go +++ b/server/web/pages/route.go @@ -6,13 +6,16 @@ import ( "server/settings" "server/torr" + "server/web/auth" "server/web/pages/template" ) -func SetupRoute(route *gin.RouterGroup) { - template.RouteWebPages(route) - route.GET("/stat", statPage) - route.GET("/magnets", getTorrents) +func SetupRoute(route gin.IRouter) { + authorized := route.Group("/", auth.CheckAuth()) + + template.RouteWebPages(authorized) + authorized.GET("/stat", statPage) + authorized.GET("/magnets", getTorrents) } // stat godoc diff --git a/server/web/pages/template/route.go b/server/web/pages/template/route.go index e1af681..c87594c 100644 --- a/server/web/pages/template/route.go +++ b/server/web/pages/template/route.go @@ -3,10 +3,11 @@ package template import ( "crypto/md5" "fmt" + "github.com/gin-gonic/gin" ) -func RouteWebPages(route *gin.RouterGroup) { +func RouteWebPages(route gin.IRouter) { route.GET("/", func(c *gin.Context) { etag := fmt.Sprintf("%x", md5.Sum(Indexhtml)) c.Header("Cache-Control", "public, max-age=31536000") diff --git a/server/web/server.go b/server/web/server.go index 69aa3f9..873e5b6 100644 --- a/server/web/server.go +++ b/server/web/server.go @@ -71,19 +71,14 @@ func Start() { route := gin.New() route.Use(log.WebLogger(), blocker.Blocker(), gin.Recovery(), cors.New(corsCfg), location.Default()) + auth.SetupAuth(route) route.GET("/echo", echo) - routeAuth := auth.SetupAuth(route) - if routeAuth != nil { - api.SetupRoute(routeAuth) - msx.SetupRoute(routeAuth) - pages.SetupRoute(routeAuth) - } else { - api.SetupRoute(&route.RouterGroup) - msx.SetupRoute(&route.RouterGroup) - pages.SetupRoute(&route.RouterGroup) - } + api.SetupRoute(route) + msx.SetupRoute(route) + pages.SetupRoute(route) + if settings.BTsets.EnableDLNA { dlna.Start() } From 3695797f74abcd27a6e2c9a155db31965cf9d68b Mon Sep 17 00:00:00 2001 From: Viacheslav Evseev Date: Thu, 22 Feb 2024 02:40:54 +0300 Subject: [PATCH 3/7] return missing "/search" and "/site.webmanifest" router --- gen_web.go | 2 +- server/web/api/route.go | 9 +++++++-- server/web/auth/auth.go | 7 ++++++- server/web/pages/route.go | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/gen_web.go b/gen_web.go index 8ae0d4a..1f9e64e 100644 --- a/gen_web.go +++ b/gen_web.go @@ -87,7 +87,7 @@ import ( "github.com/gin-gonic/gin" ) -func RouteWebPages(route *gin.RouterGroup) { +func RouteWebPages(route gin.IRouter) { route.GET("/", func(c *gin.Context) { etag := fmt.Sprintf("%x", md5.Sum(Indexhtml)) c.Header("Cache-Control", "public, max-age=31536000") diff --git a/server/web/api/route.go b/server/web/api/route.go index 94002d4..7c1d263 100644 --- a/server/web/api/route.go +++ b/server/web/api/route.go @@ -1,6 +1,7 @@ package api import ( + config "server/settings" "server/web/auth" "github.com/gin-gonic/gin" @@ -39,7 +40,11 @@ func SetupRoute(route gin.IRouter) { authorized.GET("/download/:size", download) - authorized.GET("/search/*query", rutorSearch) - + if config.SearchWA { + route.GET("/search/*query", rutorSearch) + } else { + authorized.GET("/search/*query", rutorSearch) + } + authorized.GET("/ffp/:hash/:id", ffp) } diff --git a/server/web/auth/auth.go b/server/web/auth/auth.go index bcc60f5..b125f39 100644 --- a/server/web/auth/auth.go +++ b/server/web/auth/auth.go @@ -6,6 +6,7 @@ import ( "net/http" "os" "path/filepath" + "slices" "unsafe" "github.com/gin-gonic/gin" @@ -68,12 +69,16 @@ func BasicAuth(accounts gin.Accounts) gin.HandlerFunc { } } -func CheckAuth() gin.HandlerFunc { +func CheckAuth(exclude ...string) gin.HandlerFunc { return func(c *gin.Context) { if !settings.HttpAuth { return } + if slices.Contains(exclude, c.FullPath()) { + return + } + if _, ok := c.Get(gin.AuthUserKey); ok { return } diff --git a/server/web/pages/route.go b/server/web/pages/route.go index 96a100f..4649ad8 100644 --- a/server/web/pages/route.go +++ b/server/web/pages/route.go @@ -11,7 +11,7 @@ import ( ) func SetupRoute(route gin.IRouter) { - authorized := route.Group("/", auth.CheckAuth()) + authorized := route.Group("/", auth.CheckAuth("/site.webmanifest")) template.RouteWebPages(authorized) authorized.GET("/stat", statPage) From b50e5a381e5fe4b53a693bb86aa36a2f80f48841 Mon Sep 17 00:00:00 2001 From: Viacheslav Evseev Date: Thu, 22 Feb 2024 05:53:55 +0300 Subject: [PATCH 4/7] isolate web pages auth logic --- server/web/auth/auth.go | 7 +------ server/web/pages/route.go | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/server/web/auth/auth.go b/server/web/auth/auth.go index b125f39..3a03650 100644 --- a/server/web/auth/auth.go +++ b/server/web/auth/auth.go @@ -6,7 +6,6 @@ import ( "net/http" "os" "path/filepath" - "slices" "unsafe" "github.com/gin-gonic/gin" @@ -69,15 +68,11 @@ func BasicAuth(accounts gin.Accounts) gin.HandlerFunc { } } -func CheckAuth(exclude ...string) gin.HandlerFunc { +func CheckAuth() gin.HandlerFunc { return func(c *gin.Context) { if !settings.HttpAuth { return } - - if slices.Contains(exclude, c.FullPath()) { - return - } if _, ok := c.Get(gin.AuthUserKey); ok { return diff --git a/server/web/pages/route.go b/server/web/pages/route.go index 4649ad8..d0dfc53 100644 --- a/server/web/pages/route.go +++ b/server/web/pages/route.go @@ -1,6 +1,8 @@ package pages import ( + "slices" + "github.com/anacrolix/torrent/metainfo" "github.com/gin-gonic/gin" @@ -11,9 +13,18 @@ import ( ) func SetupRoute(route gin.IRouter) { - authorized := route.Group("/", auth.CheckAuth("/site.webmanifest")) + authorized := route.Group("/", auth.CheckAuth()) - template.RouteWebPages(authorized) + webPagesAuth := route.Group("/", func() gin.HandlerFunc { + return func(c *gin.Context) { + if slices.Contains([]string{"/site.webmanifest"}, c.FullPath()) { + return + } + auth.CheckAuth()(c) + } + }()) + + template.RouteWebPages(webPagesAuth) authorized.GET("/stat", statPage) authorized.GET("/magnets", getTorrents) } From c774bbd69ae3737c7dcb1a7ffe8a349c04b10e50 Mon Sep 17 00:00:00 2001 From: Viacheslav Evseev Date: Thu, 22 Feb 2024 06:01:01 +0300 Subject: [PATCH 5/7] remove `not_auth` from swagger docs --- server/docs/docs.go | 7 ------- server/docs/swagger.json | 7 ------- server/docs/swagger.yaml | 5 ----- 3 files changed, 19 deletions(-) diff --git a/server/docs/docs.go b/server/docs/docs.go index 1b10d1d..ad7ff29 100644 --- a/server/docs/docs.go +++ b/server/docs/docs.go @@ -475,13 +475,6 @@ const docTemplate = `{ "name": "poster", "in": "query", "required": true - }, - { - "type": "string", - "description": "Stream / playlist without authentication", - "name": "not_auth", - "in": "query", - "required": true } ], "responses": { diff --git a/server/docs/swagger.json b/server/docs/swagger.json index ca920d0..454c1e7 100644 --- a/server/docs/swagger.json +++ b/server/docs/swagger.json @@ -468,13 +468,6 @@ "name": "poster", "in": "query", "required": true - }, - { - "type": "string", - "description": "Stream / playlist without authentication", - "name": "not_auth", - "in": "query", - "required": true } ], "responses": { diff --git a/server/docs/swagger.yaml b/server/docs/swagger.yaml index 61c0876..79174d1 100644 --- a/server/docs/swagger.yaml +++ b/server/docs/swagger.yaml @@ -606,11 +606,6 @@ paths: name: poster required: true type: string - - description: Stream / playlist without authentication - in: query - name: poster - required: true - type: string produces: - application/octet-stream responses: From c3218b0ea1510223482708e335e2c93ae448da5e Mon Sep 17 00:00:00 2001 From: nikk gitanes Date: Thu, 22 Feb 2024 06:12:33 +0300 Subject: [PATCH 6/7] go1.20 compat slices --- server/web/pages/route.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/web/pages/route.go b/server/web/pages/route.go index d0dfc53..3c2ad8f 100644 --- a/server/web/pages/route.go +++ b/server/web/pages/route.go @@ -1,8 +1,6 @@ package pages import ( - "slices" - "github.com/anacrolix/torrent/metainfo" "github.com/gin-gonic/gin" @@ -10,6 +8,8 @@ import ( "server/torr" "server/web/auth" "server/web/pages/template" + + "golang.org/x/exp/slices" ) func SetupRoute(route gin.IRouter) { From a0b8b6f43ed6b0af611974ff081190db4e096ce0 Mon Sep 17 00:00:00 2001 From: nikk gitanes Date: Thu, 22 Feb 2024 06:29:58 +0300 Subject: [PATCH 7/7] bump version --- server/version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/version/version.go b/server/version/version.go index 3f676b1..e2c37fa 100644 --- a/server/version/version.go +++ b/server/version/version.go @@ -6,7 +6,7 @@ import ( // "github.com/anacrolix/torrent" ) -const Version = "MatriX.129.4" +const Version = "MatriX.129.5" func GetTorrentVersion() string { bi, ok := debug.ReadBuildInfo()