From c3f89042f91382b3fb3b571285a45c1484bf4308 Mon Sep 17 00:00:00 2001 From: Viacheslav Evseev Date: Thu, 22 Feb 2024 01:50:03 +0300 Subject: [PATCH] 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() }