mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-02 04:16:38 +08:00
fix: secure adult visibility and telegram bot access
This commit is contained in:
@@ -318,7 +318,11 @@ func embyFallbackUser(id string) gin.H {
|
||||
|
||||
func embyViewsHandler(svc *service.Container) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
out, err := svc.Emby.Views(c.Request.Context())
|
||||
uid := c.Param("userId")
|
||||
if uid == "" {
|
||||
uid = embyUserID(c)
|
||||
}
|
||||
out, err := svc.Emby.Views(c.Request.Context(), uid)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
@@ -335,8 +339,13 @@ func embyVirtualFoldersHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
uid := embyUserID(c)
|
||||
visibility := service.UserDefaultMediaVisibility(c.Request.Context(), svc.Repo, uid)
|
||||
out := make([]gin.H, 0, len(libs))
|
||||
for _, lib := range libs {
|
||||
if !service.LibraryVisibleForUser(c.Request.Context(), svc.Repo, lib, visibility) {
|
||||
continue
|
||||
}
|
||||
collectionType := "movies"
|
||||
switch lib.Type {
|
||||
case "tv", "anime", "variety":
|
||||
@@ -538,7 +547,11 @@ func embyShowEpisodesHandler(svc *service.Container) gin.HandlerFunc {
|
||||
|
||||
func embyPlaybackInfoHandler(svc *service.Container) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
out, err := svc.Emby.PlaybackInfo(c.Request.Context(), c.Param("id"))
|
||||
uid := c.Param("userId")
|
||||
if uid == "" {
|
||||
uid = embyUserID(c)
|
||||
}
|
||||
out, err := svc.Emby.PlaybackInfo(c.Request.Context(), c.Param("id"), uid)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
@@ -555,8 +568,14 @@ func embyPlaybackInfoHandler(svc *service.Container) gin.HandlerFunc {
|
||||
// 直接代理到我们的 /api/stream/{id}(同一个 ServeFile)。
|
||||
func embyVideoStreamHandler(svc *service.Container) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uid := embyUserID(c)
|
||||
item, err := svc.Emby.Item(c.Request.Context(), c.Param("id"), uid)
|
||||
if err != nil || item == nil {
|
||||
c.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
// 直接调用 Stream service 写入 response
|
||||
err := svc.Stream.ServeFile(c.Writer, c.Request, c.Param("id"))
|
||||
err = svc.Stream.ServeFile(c.Writer, c.Request, c.Param("id"))
|
||||
if err != nil {
|
||||
c.Status(http.StatusNotFound)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ type verifyPlayProfilePINReq struct {
|
||||
PIN string `json:"pin"`
|
||||
}
|
||||
|
||||
type deletePlayProfileReq struct {
|
||||
PIN string `json:"pin"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// listPlayProfilesHandler returns only the caller's own profiles.
|
||||
func listPlayProfilesHandler(svc *service.Container) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
@@ -82,7 +87,44 @@ func updatePlayProfileHandler(svc *service.Container) gin.HandlerFunc {
|
||||
func deletePlayProfileHandler(svc *service.Container) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uid, _ := c.Get(middleware.CtxUserID)
|
||||
if err := svc.PlayProfiles.DeleteForUser(c.Request.Context(), c.Param("id"), toString(uid)); errors.Is(err, service.ErrPlayProfileNotFound) {
|
||||
userID := toString(uid)
|
||||
var req deletePlayProfileReq
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
|
||||
profile, err := svc.Repo.PlayProfile.FindByID(c.Request.Context(), c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if profile == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "profile not found"})
|
||||
return
|
||||
}
|
||||
if profile.UserID != userID {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "profile forbidden"})
|
||||
return
|
||||
}
|
||||
verified := false
|
||||
if profile.RequirePIN && req.PIN != "" {
|
||||
if _, err := svc.PlayProfiles.VerifyPIN(c.Request.Context(), profile.ID, userID, req.PIN); err == nil {
|
||||
verified = true
|
||||
}
|
||||
}
|
||||
if !verified && req.Password != "" {
|
||||
if err := svc.Auth.VerifyPassword(c.Request.Context(), userID, req.Password); err == nil {
|
||||
verified = true
|
||||
}
|
||||
}
|
||||
if !verified {
|
||||
if profile.RequirePIN {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "删除此 Profile 需要输入 PIN 或当前账号密码"})
|
||||
} else {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "删除此 Profile 需要输入当前账号密码"})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := svc.PlayProfiles.DeleteForUser(c.Request.Context(), profile.ID, userID); errors.Is(err, service.ErrPlayProfileNotFound) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "profile not found"})
|
||||
return
|
||||
} else if errors.Is(err, service.ErrPlayProfileForbidden) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -18,8 +19,19 @@ func updateProfileHandler(svc *service.Container) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
uid, _ := c.Get(middleware.CtxUserID)
|
||||
u, err := svc.Profile.UpdateProfile(c.Request.Context(), uid.(string), patch)
|
||||
userID := uid.(string)
|
||||
if patch.HideAdult != nil {
|
||||
if err := svc.Auth.VerifyPassword(c.Request.Context(), userID, patch.Password); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "需要输入当前账号密码确认"})
|
||||
return
|
||||
}
|
||||
}
|
||||
u, err := svc.Profile.UpdateProfile(c.Request.Context(), userID, patch)
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrUsernameTaken) {
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "username already taken"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -18,8 +17,10 @@ import (
|
||||
)
|
||||
|
||||
func mediaVisibilityForRequest(c *gin.Context, svc *service.Container) service.MediaVisibility {
|
||||
adultEnabled := settingBool(c, svc, "adult.enabled", false)
|
||||
visibility := service.MediaVisibility{IncludeNSFW: adultEnabled}
|
||||
userID := currentUserID(c)
|
||||
adultEnabled := service.AdultContentEnabled(c.Request.Context(), svc.Repo)
|
||||
userHidesAdult := service.UserHidesAdult(c.Request.Context(), svc.Repo, userID)
|
||||
visibility := service.UserDefaultMediaVisibility(c.Request.Context(), svc.Repo, userID)
|
||||
profile, locked := selectedPlayProfile(c, svc)
|
||||
if locked {
|
||||
return service.MediaVisibility{
|
||||
@@ -30,7 +31,7 @@ func mediaVisibilityForRequest(c *gin.Context, svc *service.Container) service.M
|
||||
if profile == nil {
|
||||
return visibility
|
||||
}
|
||||
visibility.IncludeNSFW = adultEnabled && profile.AllowAdult
|
||||
visibility.IncludeNSFW = adultEnabled && profile.AllowAdult && !userHidesAdult
|
||||
visibility.AllowedLibraryIDs = profileAllowedLibraryIDs(*profile)
|
||||
return visibility
|
||||
}
|
||||
@@ -99,14 +100,7 @@ func currentUserID(c *gin.Context) string {
|
||||
}
|
||||
|
||||
func profileAllowedLibraryIDs(profile model.PlayProfile) []string {
|
||||
if strings.TrimSpace(profile.AllowedLibraryIDs) == "" {
|
||||
return nil
|
||||
}
|
||||
var ids []string
|
||||
if err := json.Unmarshal([]byte(profile.AllowedLibraryIDs), &ids); err != nil {
|
||||
return nil
|
||||
}
|
||||
return ids
|
||||
return service.DecodeAllowedLibraryIDs(profile.AllowedLibraryIDs)
|
||||
}
|
||||
|
||||
func signPlayProfilePINToken(svc *service.Container, userID, profileID string, expiresAt time.Time) string {
|
||||
|
||||
Reference in New Issue
Block a user