mirror of
https://github.com/nezhahq/nezha.git
synced 2025-01-22 12:48:14 -05:00
🔒️ 增强 ping 历史 API 鉴权
This commit is contained in:
parent
99ac12c9fd
commit
8dd509aa08
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/mygin"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
@ -16,18 +17,28 @@ type apiV1 struct {
|
||||
|
||||
func (v *apiV1) serve() {
|
||||
r := v.r.Group("")
|
||||
// API
|
||||
// 强制认证的 API
|
||||
r.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Member: true,
|
||||
IsPage: false,
|
||||
AllowAPI: true,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
MemberOnly: true,
|
||||
AllowAPI: true,
|
||||
IsPage: false,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
r.GET("/server/list", v.serverList)
|
||||
r.GET("/server/details", v.serverDetails)
|
||||
// 不强制认证的 API
|
||||
mr := v.r.Group("monitor")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
MemberOnly: false,
|
||||
IsPage: false,
|
||||
ValidateViewPassword: true,
|
||||
AllowAPI: true,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
mr.GET("/:id", v.monitorHistoriesById)
|
||||
}
|
||||
|
||||
@ -84,5 +95,15 @@ func (v *apiV1) monitorHistoriesById(c *gin.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
_, isMember := c.Get(model.CtxKeyAuthorizedUser)
|
||||
_, isViewPasswordVerfied := c.Get(model.CtxKeyViewPasswordVerified)
|
||||
authorized := isMember || isViewPasswordVerfied
|
||||
|
||||
if server.HideForGuest && !authorized {
|
||||
c.AbortWithStatusJSON(403, gin.H{"code": 403, "message": "需要认证"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, singleton.MonitorAPI.GetMonitorHistories(map[string]any{"server_id": server.ID}))
|
||||
}
|
||||
|
@ -43,11 +43,12 @@ type commonPage struct {
|
||||
|
||||
func (cp *commonPage) serve() {
|
||||
cr := cp.r.Group("")
|
||||
cr.Use(mygin.Authorize(mygin.AuthorizeOption{}))
|
||||
cr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
ValidateViewPassword: true,
|
||||
}))
|
||||
cr.Use(mygin.PreferredTheme)
|
||||
cr.GET("/terminal/:id", cp.terminal)
|
||||
cr.POST("/view-password", cp.issueViewPassword)
|
||||
cr.Use(cp.checkViewPassword) // 前端查看密码鉴权
|
||||
cr.GET("/", cp.home)
|
||||
cr.GET("/service", cp.service)
|
||||
// TODO: 界面直接跳转使用该接口
|
||||
@ -86,31 +87,6 @@ func (p *commonPage) issueViewPassword(c *gin.Context) {
|
||||
c.Redirect(http.StatusFound, c.Request.Referer())
|
||||
}
|
||||
|
||||
func (p *commonPage) checkViewPassword(c *gin.Context) {
|
||||
if singleton.Conf.Site.ViewPassword == "" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if _, authorized := c.Get(model.CtxKeyAuthorizedUser); authorized {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 验证查看密码
|
||||
viewPassword, _ := c.Cookie(singleton.Conf.Site.CookieName + "-vp")
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(viewPassword), []byte(singleton.Conf.Site.ViewPassword)); err != nil {
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/viewpassword"), mygin.CommonEnvironment(c, gin.H{
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "VerifyPassword"}),
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
}))
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set(model.CtxKeyViewPasswordVerified, true)
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func (p *commonPage) service(c *gin.Context) {
|
||||
res, _, _ := p.requestGroup.Do("servicePage", func() (interface{}, error) {
|
||||
singleton.AlertsLock.RLock()
|
||||
|
@ -19,11 +19,11 @@ type guestPage struct {
|
||||
func (gp *guestPage) serve() {
|
||||
gr := gp.r.Group("")
|
||||
gr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Guest: true,
|
||||
IsPage: true,
|
||||
Msg: "您已登录",
|
||||
Btn: "返回首页",
|
||||
Redirect: "/",
|
||||
GuestOnly: true,
|
||||
IsPage: true,
|
||||
Msg: "您已登录",
|
||||
Btn: "返回首页",
|
||||
Redirect: "/",
|
||||
}))
|
||||
|
||||
gr.GET("/login", gp.login)
|
||||
|
@ -28,11 +28,11 @@ type memberAPI struct {
|
||||
func (ma *memberAPI) serve() {
|
||||
mr := ma.r.Group("")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Member: true,
|
||||
IsPage: false,
|
||||
Msg: "访问此接口需要登录",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
MemberOnly: true,
|
||||
IsPage: false,
|
||||
Msg: "访问此接口需要登录",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
|
||||
mr.GET("/search-server", ma.searchServer)
|
||||
@ -444,10 +444,12 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
||||
err = singleton.DB.Save(&m).Error
|
||||
}
|
||||
}
|
||||
if m.Cover == 0 {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
||||
} else {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
||||
if err == nil {
|
||||
if m.Cover == 0 {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
||||
} else {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
|
@ -17,11 +17,11 @@ type memberPage struct {
|
||||
func (mp *memberPage) serve() {
|
||||
mr := mp.r.Group("")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Member: true,
|
||||
IsPage: true,
|
||||
Msg: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "YouAreNotAuthorized"}),
|
||||
Btn: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}),
|
||||
Redirect: "/login",
|
||||
MemberOnly: true,
|
||||
IsPage: true,
|
||||
Msg: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "YouAreNotAuthorized"}),
|
||||
Btn: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}),
|
||||
Redirect: "/login",
|
||||
}))
|
||||
mr.GET("/server", mp.server)
|
||||
mr.GET("/monitor", mp.monitor)
|
||||
|
@ -6,25 +6,28 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
|
||||
type AuthorizeOption struct {
|
||||
Guest bool
|
||||
Member bool
|
||||
IsPage bool
|
||||
AllowAPI bool
|
||||
Msg string
|
||||
Redirect string
|
||||
Btn string
|
||||
GuestOnly bool
|
||||
MemberOnly bool
|
||||
ValidateViewPassword bool
|
||||
IsPage bool
|
||||
AllowAPI bool
|
||||
Msg string
|
||||
Redirect string
|
||||
Btn string
|
||||
}
|
||||
|
||||
func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var code = http.StatusForbidden
|
||||
if opt.Guest {
|
||||
if opt.GuestOnly {
|
||||
code = http.StatusBadRequest
|
||||
}
|
||||
|
||||
@ -67,15 +70,32 @@ func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 已登录且只能游客访问
|
||||
if isLogin && opt.Guest {
|
||||
if isLogin && opt.GuestOnly {
|
||||
ShowErrorPage(c, commonErr, opt.IsPage)
|
||||
return
|
||||
}
|
||||
|
||||
// 未登录且需要登录
|
||||
if !isLogin && opt.Member {
|
||||
if !isLogin && opt.MemberOnly {
|
||||
ShowErrorPage(c, commonErr, opt.IsPage)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证查看密码
|
||||
if opt.ValidateViewPassword && singleton.Conf.Site.ViewPassword != "" {
|
||||
viewPassword, _ := c.Cookie(singleton.Conf.Site.CookieName + "-vp")
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(viewPassword), []byte(singleton.Conf.Site.ViewPassword)); err != nil {
|
||||
c.HTML(http.StatusOK, GetPreferredTheme(c, "/viewpassword"), CommonEnvironment(c, gin.H{
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "VerifyPassword"}),
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
}))
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set(model.CtxKeyViewPasswordVerified, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user