diff --git a/cmd/dashboard/controller/alertrule.go b/cmd/dashboard/controller/alertrule.go index 708f9cf..5ebb56d 100644 --- a/cmd/dashboard/controller/alertrule.go +++ b/cmd/dashboard/controller/alertrule.go @@ -50,6 +50,9 @@ func createAlertRule(c *gin.Context) (uint64, error) { return 0, err } + uid := getUid(c) + + r.UserID = uid r.Name = arf.Name r.Rules = arf.Rules r.FailTriggerTasks = arf.FailTriggerTasks @@ -100,6 +103,10 @@ func updateAlertRule(c *gin.Context) (any, error) { return nil, singleton.Localizer.ErrorT("alert id %d does not exist", id) } + if !r.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("permission denied") + } + r.Name = arf.Name r.Rules = arf.Rules r.FailTriggerTasks = arf.FailTriggerTasks @@ -133,12 +140,24 @@ func updateAlertRule(c *gin.Context) (any, error) { // @Success 200 {object} model.CommonResponse[any] // @Router /batch-delete/alert-rule [post] func batchDeleteAlertRule(c *gin.Context) (any, error) { - var ar []uint64 - - if err := c.ShouldBindJSON(&ar); err != nil { + var arr []uint64 + if err := c.ShouldBindJSON(&arr); err != nil { return nil, err } + var ars []model.AlertRule + if err := singleton.DB.Where("id in (?)", arr).Find(&ars).Error; err != nil { + return nil, err + } + + var ar []uint64 + for _, a := range ars { + if !a.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("permission denied") + } + ar = append(ar, a.ID) + } + if err := singleton.DB.Unscoped().Delete(&model.AlertRule{}, "id in (?)", ar).Error; err != nil { return nil, newGormError("%v", err) } diff --git a/cmd/dashboard/controller/cron.go b/cmd/dashboard/controller/cron.go index d8d95de..ebe8f4e 100644 --- a/cmd/dashboard/controller/cron.go +++ b/cmd/dashboard/controller/cron.go @@ -1,7 +1,6 @@ package controller import ( - "fmt" "strconv" "github.com/gin-gonic/gin" @@ -50,6 +49,7 @@ func createCron(c *gin.Context) (uint64, error) { return 0, err } + cr.UserID = getUid(c) cr.TaskType = cf.TaskType cr.Name = cf.Name cr.Scheduler = cf.Scheduler @@ -106,7 +106,11 @@ func updateCron(c *gin.Context) (any, error) { var cr model.Cron if err := singleton.DB.First(&cr, id).Error; err != nil { - return nil, fmt.Errorf("task id %d does not exist", id) + return nil, singleton.Localizer.ErrorT("task id %d does not exist", id) + } + + if !cr.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("permission denied") } cr.TaskType = cf.TaskType @@ -156,12 +160,15 @@ func manualTriggerCron(c *gin.Context) (any, error) { return nil, err } - var cr model.Cron - if err := singleton.DB.First(&cr, id).Error; err != nil { + singleton.CronLock.RLock() + cr, ok := singleton.Crons[id] + if !ok { + singleton.CronLock.RUnlock() return nil, singleton.Localizer.ErrorT("task id %d does not exist", id) } + singleton.CronLock.RUnlock() - singleton.ManualTrigger(&cr) + singleton.ManualTrigger(cr) return nil, nil } @@ -177,12 +184,24 @@ func manualTriggerCron(c *gin.Context) (any, error) { // @Success 200 {object} model.CommonResponse[any] // @Router /batch-delete/cron [post] func batchDeleteCron(c *gin.Context) (any, error) { - var cr []uint64 - - if err := c.ShouldBindJSON(&cr); err != nil { + var crr []uint64 + if err := c.ShouldBindJSON(&crr); err != nil { return nil, err } + var cr []uint64 + singleton.CronLock.RLock() + for _, crID := range crr { + if crn, ok := singleton.Crons[crID]; ok { + if !crn.HasPermission(c) { + singleton.CronLock.RUnlock() + return nil, singleton.Localizer.ErrorT("permission denied") + } + cr = append(cr, crn.ID) + } + } + singleton.CronLock.RUnlock() + if err := singleton.DB.Unscoped().Delete(&model.Cron{}, "id in (?)", cr).Error; err != nil { return nil, newGormError("%v", err) } diff --git a/cmd/dashboard/controller/ddns.go b/cmd/dashboard/controller/ddns.go index b58c28e..a0e52b7 100644 --- a/cmd/dashboard/controller/ddns.go +++ b/cmd/dashboard/controller/ddns.go @@ -56,6 +56,7 @@ func createDDNS(c *gin.Context) (uint64, error) { return 0, singleton.Localizer.ErrorT("the retry count must be an integer between 1 and 10") } + p.UserID = getUid(c) p.Name = df.Name enableIPv4 := df.EnableIPv4 enableIPv6 := df.EnableIPv6 @@ -125,6 +126,10 @@ func updateDDNS(c *gin.Context) (any, error) { return nil, singleton.Localizer.ErrorT("profile id %d does not exist", id) } + if !p.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("permission denied") + } + p.Name = df.Name enableIPv4 := df.EnableIPv4 enableIPv6 := df.EnableIPv6 @@ -172,12 +177,25 @@ func updateDDNS(c *gin.Context) (any, error) { // @Success 200 {object} model.CommonResponse[any] // @Router /batch-delete/ddns [post] func batchDeleteDDNS(c *gin.Context) (any, error) { - var ddnsConfigs []uint64 + var ddnsConfigsr []uint64 - if err := c.ShouldBindJSON(&ddnsConfigs); err != nil { + if err := c.ShouldBindJSON(&ddnsConfigsr); err != nil { return nil, err } + var ddnsConfigs []uint64 + singleton.DDNSCacheLock.RLock() + for _, pid := range ddnsConfigsr { + if p, ok := singleton.DDNSCache[pid]; ok { + if !p.HasPermission(c) { + singleton.DDNSCacheLock.RUnlock() + return nil, singleton.Localizer.ErrorT("permission denied") + } + ddnsConfigs = append(ddnsConfigs, p.ID) + } + } + singleton.DDNSCacheLock.RUnlock() + if err := singleton.DB.Unscoped().Delete(&model.DDNSProfile{}, "id in (?)", ddnsConfigs).Error; err != nil { return nil, newGormError("%v", err) } diff --git a/cmd/dashboard/controller/nat.go b/cmd/dashboard/controller/nat.go index 6a2737e..4a75de7 100644 --- a/cmd/dashboard/controller/nat.go +++ b/cmd/dashboard/controller/nat.go @@ -51,6 +51,9 @@ func createNAT(c *gin.Context) (uint64, error) { return 0, err } + uid := getUid(c) + + n.UserID = uid n.Name = nf.Name n.Domain = nf.Domain n.Host = nf.Host @@ -95,6 +98,10 @@ func updateNAT(c *gin.Context) (any, error) { return nil, singleton.Localizer.ErrorT("profile id %d does not exist", id) } + if !n.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("permission denied") + } + n.Name = nf.Name n.Domain = nf.Domain n.Host = nf.Host @@ -121,12 +128,24 @@ func updateNAT(c *gin.Context) (any, error) { // @Success 200 {object} model.CommonResponse[any] // @Router /batch-delete/nat [post] func batchDeleteNAT(c *gin.Context) (any, error) { - var n []uint64 - - if err := c.ShouldBindJSON(&n); err != nil { + var nr []uint64 + if err := c.ShouldBindJSON(&nr); err != nil { return nil, err } + var n []uint64 + singleton.NATCacheRwLock.RLock() + for _, id := range nr { + if p, ok := singleton.NATCache[singleton.NATIDToDomain[id]]; ok { + if !p.HasPermission(c) { + singleton.NATCacheRwLock.RUnlock() + return nil, singleton.Localizer.ErrorT("permission denied") + } + n = append(n, p.ID) + } + } + singleton.NATCacheRwLock.RUnlock() + if err := singleton.DB.Unscoped().Delete(&model.NAT{}, "id in (?)", n).Error; err != nil { return nil, newGormError("%v", err) } diff --git a/cmd/dashboard/controller/notification.go b/cmd/dashboard/controller/notification.go index 78ac770..7a03937 100644 --- a/cmd/dashboard/controller/notification.go +++ b/cmd/dashboard/controller/notification.go @@ -48,6 +48,7 @@ func createNotification(c *gin.Context) (uint64, error) { } var n model.Notification + n.UserID = getUid(c) n.Name = nf.Name n.RequestMethod = nf.RequestMethod n.RequestType = nf.RequestType @@ -106,6 +107,10 @@ func updateNotification(c *gin.Context) (any, error) { return nil, singleton.Localizer.ErrorT("notification id %d does not exist", id) } + if !n.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("permission denied") + } + n.Name = nf.Name n.RequestMethod = nf.RequestMethod n.RequestType = nf.RequestType @@ -148,12 +153,23 @@ func updateNotification(c *gin.Context) (any, error) { // @Success 200 {object} model.CommonResponse[any] // @Router /batch-delete/notification [post] func batchDeleteNotification(c *gin.Context) (any, error) { - var n []uint64 - - if err := c.ShouldBindJSON(&n); err != nil { + var nr []uint64 + if err := c.ShouldBindJSON(&nr); err != nil { return nil, err } + var n []uint64 + singleton.NotificationsLock.RLock() + for _, nid := range nr { + if ns, ok := singleton.NotificationMap[nid]; ok { + if !ns.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("permission denied") + } + n = append(n, ns.ID) + } + } + singleton.NotificationsLock.RUnlock() + err := singleton.DB.Transaction(func(tx *gorm.DB) error { if err := tx.Unscoped().Delete(&model.Notification{}, "id in (?)", n).Error; err != nil { return err diff --git a/cmd/dashboard/controller/notification_group.go b/cmd/dashboard/controller/notification_group.go index 310e6bf..8e6bd97 100644 --- a/cmd/dashboard/controller/notification_group.go +++ b/cmd/dashboard/controller/notification_group.go @@ -68,8 +68,11 @@ func createNotificationGroup(c *gin.Context) (uint64, error) { } ngf.Notifications = slices.Compact(ngf.Notifications) + uid := getUid(c) + var ng model.NotificationGroup ng.Name = ngf.Name + ng.UserID = uid var count int64 if err := singleton.DB.Model(&model.Notification{}).Where("id in (?)", ngf.Notifications).Count(&count).Error; err != nil { @@ -86,6 +89,9 @@ func createNotificationGroup(c *gin.Context) (uint64, error) { } for _, n := range ngf.Notifications { if err := tx.Create(&model.NotificationGroupNotification{ + Common: model.Common{ + UserID: uid, + }, NotificationGroupID: ng.ID, NotificationID: n, }).Error; err != nil { @@ -131,6 +137,10 @@ func updateNotificationGroup(c *gin.Context) (any, error) { return nil, singleton.Localizer.ErrorT("group id %d does not exist", id) } + if !ngDB.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("permission denied") + } + ngDB.Name = ngf.Name ngf.Notifications = slices.Compact(ngf.Notifications) @@ -142,6 +152,8 @@ func updateNotificationGroup(c *gin.Context) (any, error) { return nil, singleton.Localizer.ErrorT("have invalid notification id") } + uid := getUid(c) + err = singleton.DB.Transaction(func(tx *gorm.DB) error { if err := tx.Save(&ngDB).Error; err != nil { return err @@ -152,6 +164,9 @@ func updateNotificationGroup(c *gin.Context) (any, error) { for _, n := range ngf.Notifications { if err := tx.Create(&model.NotificationGroupNotification{ + Common: model.Common{ + UserID: uid, + }, NotificationGroupID: ngDB.ID, NotificationID: n, }).Error; err != nil { @@ -180,11 +195,24 @@ func updateNotificationGroup(c *gin.Context) (any, error) { // @Success 200 {object} model.CommonResponse[any] // @Router /batch-delete/notification-group [post] func batchDeleteNotificationGroup(c *gin.Context) (any, error) { - var ngn []uint64 - if err := c.ShouldBindJSON(&ngn); err != nil { + var ngnr []uint64 + if err := c.ShouldBindJSON(&ngnr); err != nil { return nil, err } + var ng []model.NotificationGroup + if err := singleton.DB.Where("id in (?)", ng).Find(&ng).Error; err != nil { + return nil, err + } + + var ngn []uint64 + for _, n := range ng { + if !n.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("permission denied") + } + ngn = append(ngn, n.ID) + } + err := singleton.DB.Transaction(func(tx *gorm.DB) error { if err := tx.Unscoped().Delete(&model.NotificationGroup{}, "id in (?)", ngn).Error; err != nil { return err diff --git a/cmd/dashboard/controller/server.go b/cmd/dashboard/controller/server.go index 604b0bb..b7ffb69 100644 --- a/cmd/dashboard/controller/server.go +++ b/cmd/dashboard/controller/server.go @@ -62,7 +62,7 @@ func updateServer(c *gin.Context) (any, error) { } if !s.HasPermission(c) { - return nil, singleton.Localizer.ErrorT("unauthorized") + return nil, singleton.Localizer.ErrorT("permission denied") } s.Name = sf.Name @@ -113,6 +113,7 @@ func batchDeleteServer(c *gin.Context) (any, error) { for _, sid := range serversRaw { if s, ok := singleton.ServerList[sid]; ok { if !s.HasPermission(c) { + singleton.ServerLock.RUnlock() return nil, singleton.Localizer.ErrorT("permission denied") } servers = append(servers, s.ID) diff --git a/cmd/dashboard/controller/server_group.go b/cmd/dashboard/controller/server_group.go index c66bb39..1b4fd4c 100644 --- a/cmd/dashboard/controller/server_group.go +++ b/cmd/dashboard/controller/server_group.go @@ -67,8 +67,11 @@ func createServerGroup(c *gin.Context) (uint64, error) { } sgf.Servers = slices.Compact(sgf.Servers) + uid := getUid(c) + var sg model.ServerGroup sg.Name = sgf.Name + sg.UserID = uid var count int64 if err := singleton.DB.Model(&model.Server{}).Where("id in (?)", sgf.Servers).Count(&count).Error; err != nil { @@ -84,6 +87,9 @@ func createServerGroup(c *gin.Context) (uint64, error) { } for _, s := range sgf.Servers { if err := tx.Create(&model.ServerGroupServer{ + Common: model.Common{ + UserID: uid, + }, ServerGroupId: sg.ID, ServerId: s, }).Error; err != nil { @@ -129,6 +135,11 @@ func updateServerGroup(c *gin.Context) (any, error) { if err := singleton.DB.First(&sgDB, id).Error; err != nil { return nil, singleton.Localizer.ErrorT("group id %d does not exist", id) } + + if !sgDB.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("unauthorized") + } + sgDB.Name = sg.Name var count int64 @@ -139,6 +150,8 @@ func updateServerGroup(c *gin.Context) (any, error) { return nil, singleton.Localizer.ErrorT("have invalid server id") } + uid := getUid(c) + err = singleton.DB.Transaction(func(tx *gorm.DB) error { if err := tx.Save(&sgDB).Error; err != nil { return err @@ -149,6 +162,9 @@ func updateServerGroup(c *gin.Context) (any, error) { for _, s := range sg.Servers { if err := tx.Create(&model.ServerGroupServer{ + Common: model.Common{ + UserID: uid, + }, ServerGroupId: sgDB.ID, ServerId: s, }).Error; err != nil { @@ -176,11 +192,24 @@ func updateServerGroup(c *gin.Context) (any, error) { // @Success 200 {object} model.CommonResponse[any] // @Router /batch-delete/server-group [post] func batchDeleteServerGroup(c *gin.Context) (any, error) { - var sgs []uint64 - if err := c.ShouldBindJSON(&sgs); err != nil { + var sgsr []uint64 + if err := c.ShouldBindJSON(&sgsr); err != nil { return nil, err } + var sg []model.ServerGroup + if err := singleton.DB.Where("id in (?)", sgsr).Find(&sg).Error; err != nil { + return nil, err + } + + var sgs []uint64 + for _, s := range sg { + if !s.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("permission denied") + } + sgs = append(sgs, s.ID) + } + err := singleton.DB.Transaction(func(tx *gorm.DB) error { if err := tx.Unscoped().Delete(&model.ServerGroup{}, "id in (?)", sgs).Error; err != nil { return err diff --git a/cmd/dashboard/controller/service.go b/cmd/dashboard/controller/service.go index ed9e859..78be9a6 100644 --- a/cmd/dashboard/controller/service.go +++ b/cmd/dashboard/controller/service.go @@ -263,6 +263,11 @@ func updateService(c *gin.Context) (any, error) { if err := singleton.DB.First(&m, id).Error; err != nil { return nil, singleton.Localizer.ErrorT("service id %d does not exist", id) } + + if !m.HasPermission(c) { + return nil, singleton.Localizer.ErrorT("permission denied") + } + m.Name = mf.Name m.Target = strings.TrimSpace(mf.Target) m.Type = mf.Type @@ -317,10 +322,24 @@ func updateService(c *gin.Context) (any, error) { // @Success 200 {object} model.CommonResponse[any] // @Router /batch-delete/service [post] func batchDeleteService(c *gin.Context) (any, error) { - var ids []uint64 - if err := c.ShouldBindJSON(&ids); err != nil { + var idsr []uint64 + if err := c.ShouldBindJSON(&idsr); err != nil { return nil, err } + + var ids []uint64 + singleton.ServiceSentinelShared.ServicesLock.RLock() + for _, id := range idsr { + if ss, ok := singleton.ServiceSentinelShared.Services[id]; ok { + if !ss.HasPermission(c) { + singleton.ServiceSentinelShared.ServicesLock.RUnlock() + return nil, singleton.Localizer.ErrorT("permission denied") + } + ids = append(ids, ss.ID) + } + } + singleton.ServiceSentinelShared.ServicesLock.RUnlock() + err := singleton.DB.Transaction(func(tx *gorm.DB) error { if err := tx.Unscoped().Delete(&model.Service{}, "id in (?)", ids).Error; err != nil { return err