From 1a6b50d2932789dd811db507df1bd0890623a5ec Mon Sep 17 00:00:00 2001 From: rxdn <29165304+rxdn@users.noreply.github.com> Date: Thu, 23 Jun 2022 17:10:00 +0100 Subject: [PATCH] Staff override --- app/http/endpoints/api/admin/botstaff/add.go | 23 +++ app/http/endpoints/api/admin/botstaff/list.go | 57 ++++++++ .../endpoints/api/admin/botstaff/remove.go | 23 +++ app/http/endpoints/api/session.go | 9 +- .../api/staffoverride/createoverride.go | 32 +++++ .../api/staffoverride/deleteoverride.go | 18 +++ .../api/staffoverride/getoverride.go | 21 +++ app/http/middleware/adminonly.go | 17 +++ app/http/middleware/authenticateguild.go | 2 + app/http/middleware/logging.go | 2 + app/http/server.go | 17 ++- frontend/src/components/Button.svelte | 90 ++++++------ .../src/components/form/RoleSelect.svelte | 4 +- .../manage/StaffOverrideModal.svelte | 107 ++++++++++++++ frontend/src/includes/AdminSidebar.svelte | 100 +++++++++++++ frontend/src/includes/Sidebar.svelte | 9 ++ frontend/src/layouts/AdminLayout.svelte | 93 ++++++++++++ frontend/src/layouts/IndexLayout.svelte | 4 +- frontend/src/routes.js | 10 ++ frontend/src/views/StaffOverride.svelte | 134 ++++++++++++++++++ frontend/src/views/admin/BotStaff.svelte | 127 +++++++++++++++++ go.mod | 2 +- go.sum | 2 + utils/permissionutils.go | 18 +++ 24 files changed, 863 insertions(+), 58 deletions(-) create mode 100644 app/http/endpoints/api/admin/botstaff/add.go create mode 100644 app/http/endpoints/api/admin/botstaff/list.go create mode 100644 app/http/endpoints/api/admin/botstaff/remove.go create mode 100644 app/http/endpoints/api/staffoverride/createoverride.go create mode 100644 app/http/endpoints/api/staffoverride/deleteoverride.go create mode 100644 app/http/endpoints/api/staffoverride/getoverride.go create mode 100644 app/http/middleware/adminonly.go create mode 100644 frontend/src/components/manage/StaffOverrideModal.svelte create mode 100644 frontend/src/includes/AdminSidebar.svelte create mode 100644 frontend/src/layouts/AdminLayout.svelte create mode 100644 frontend/src/views/StaffOverride.svelte create mode 100644 frontend/src/views/admin/BotStaff.svelte diff --git a/app/http/endpoints/api/admin/botstaff/add.go b/app/http/endpoints/api/admin/botstaff/add.go new file mode 100644 index 0000000..733e6aa --- /dev/null +++ b/app/http/endpoints/api/admin/botstaff/add.go @@ -0,0 +1,23 @@ +package botstaff + +import ( + "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/utils" + "github.com/gin-gonic/gin" + "strconv" +) + +func AddBotStaffHandler(ctx *gin.Context) { + userId, err := strconv.ParseUint(ctx.Param("userid"), 10, 64) + if err != nil { + ctx.JSON(400, utils.ErrorJson(err)) + return + } + + if err := database.Client.BotStaff.Add(userId); err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + ctx.Status(204) +} diff --git a/app/http/endpoints/api/admin/botstaff/list.go b/app/http/endpoints/api/admin/botstaff/list.go new file mode 100644 index 0000000..4badd2a --- /dev/null +++ b/app/http/endpoints/api/admin/botstaff/list.go @@ -0,0 +1,57 @@ +package botstaff + +import ( + "context" + "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/rpc/cache" + "github.com/TicketsBot/GoPanel/utils" + "github.com/gin-gonic/gin" + "github.com/rxdn/gdl/objects/user" + "golang.org/x/sync/errgroup" +) + +type userData struct { + Id uint64 `json:"id,string"` + Username string `json:"username"` + Discriminator user.Discriminator `json:"discriminator"` +} + +func ListBotStaffHandler(ctx *gin.Context) { + staff, err := database.Client.BotStaff.GetAll() + if err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + // Get usernames + group, _ := errgroup.WithContext(context.Background()) + + users := make([]userData, len(staff)) + for i, userId := range staff { + i := i + userId := userId + + group.Go(func() error { + user, ok := cache.Instance.GetUser(userId) + + data := userData{ + Id: userId, + } + + if ok { + data.Username = user.Username + data.Discriminator = user.Discriminator + } else { + data.Username = "Unknown User" + } + + users[i] = data + + return nil + }) + } + + _ = group.Wait() // error not possible + + ctx.JSON(200, users) +} diff --git a/app/http/endpoints/api/admin/botstaff/remove.go b/app/http/endpoints/api/admin/botstaff/remove.go new file mode 100644 index 0000000..f2096a4 --- /dev/null +++ b/app/http/endpoints/api/admin/botstaff/remove.go @@ -0,0 +1,23 @@ +package botstaff + +import ( + "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/utils" + "github.com/gin-gonic/gin" + "strconv" +) + +func RemoveBotStaffHandler(ctx *gin.Context) { + userId, err := strconv.ParseUint(ctx.Param("userid"), 10, 64) + if err != nil { + ctx.JSON(400, utils.ErrorJson(err)) + return + } + + if err := database.Client.BotStaff.Delete(userId); err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + ctx.Status(204) +} diff --git a/app/http/endpoints/api/session.go b/app/http/endpoints/api/session.go index a29d5c6..18e7451 100644 --- a/app/http/endpoints/api/session.go +++ b/app/http/endpoints/api/session.go @@ -33,17 +33,12 @@ func SessionHandler(ctx *gin.Context) { return } - var whitelabelOverride bool - for _, id := range config.Conf.ForceWhitelabel { - if id == userId { - whitelabelOverride = true - break - } - } + whitelabelOverride := utils.Contains(config.Conf.ForceWhitelabel, userId) ctx.JSON(200, gin.H{ "username": store.Name, "avatar": store.Avatar, "whitelabel": tier >= premium.Whitelabel || whitelabelOverride, + "admin": utils.Contains(config.Conf.Admins, userId), }) } diff --git a/app/http/endpoints/api/staffoverride/createoverride.go b/app/http/endpoints/api/staffoverride/createoverride.go new file mode 100644 index 0000000..eb75c1a --- /dev/null +++ b/app/http/endpoints/api/staffoverride/createoverride.go @@ -0,0 +1,32 @@ +package api + +import ( + "fmt" + "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/utils" + "github.com/gin-gonic/gin" + "time" +) + +type createOverrideBody struct { + TimePeriod int `json:"time_period"` +} + +func CreateOverrideHandler(ctx *gin.Context) { + guildId := ctx.Keys["guildid"].(uint64) + + var body createOverrideBody + if err := ctx.BindJSON(&body); err != nil { + ctx.JSON(400, utils.ErrorStr("Invalid request body")) + fmt.Println(err.Error()) + return + } + + expires := time.Now().Add(time.Hour * time.Duration(body.TimePeriod)) + if err := database.Client.StaffOverride.Set(guildId, expires); err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + ctx.Status(204) +} diff --git a/app/http/endpoints/api/staffoverride/deleteoverride.go b/app/http/endpoints/api/staffoverride/deleteoverride.go new file mode 100644 index 0000000..befe418 --- /dev/null +++ b/app/http/endpoints/api/staffoverride/deleteoverride.go @@ -0,0 +1,18 @@ +package api + +import ( + "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/utils" + "github.com/gin-gonic/gin" +) + +func DeleteOverrideHandler(ctx *gin.Context) { + guildId := ctx.Keys["guildid"].(uint64) + + if err := database.Client.StaffOverride.Delete(guildId); err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + ctx.Status(204) +} diff --git a/app/http/endpoints/api/staffoverride/getoverride.go b/app/http/endpoints/api/staffoverride/getoverride.go new file mode 100644 index 0000000..4161762 --- /dev/null +++ b/app/http/endpoints/api/staffoverride/getoverride.go @@ -0,0 +1,21 @@ +package api + +import ( + "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/utils" + "github.com/gin-gonic/gin" +) + +func GetOverrideHandler(ctx *gin.Context) { + guildId := ctx.Keys["guildid"].(uint64) + + hasOverride, err := database.Client.StaffOverride.HasActiveOverride(guildId) + if err != nil { + ctx.JSON(500, utils.ErrorJson(err)) + return + } + + ctx.JSON(200, gin.H{ + "has_override": hasOverride, + }) +} diff --git a/app/http/middleware/adminonly.go b/app/http/middleware/adminonly.go new file mode 100644 index 0000000..d2c1014 --- /dev/null +++ b/app/http/middleware/adminonly.go @@ -0,0 +1,17 @@ +package middleware + +import ( + "github.com/TicketsBot/GoPanel/config" + "github.com/TicketsBot/GoPanel/utils" + "github.com/gin-gonic/gin" +) + +func AdminOnly(ctx *gin.Context) { + userId := ctx.Keys["userid"].(uint64) + + if !utils.Contains(config.Conf.Admins, userId) { + ctx.JSON(401, utils.ErrorStr("Unauthorized")) + ctx.Abort() + return + } +} diff --git a/app/http/middleware/authenticateguild.go b/app/http/middleware/authenticateguild.go index 278f366..17b20d0 100644 --- a/app/http/middleware/authenticateguild.go +++ b/app/http/middleware/authenticateguild.go @@ -42,10 +42,12 @@ func AuthenticateGuild(requiredPermissionLevel permission.PermissionLevel) gin.H if permLevel < requiredPermissionLevel { ctx.JSON(403, utils.ErrorStr("Unauthorized")) ctx.Abort() + return } } else { ctx.JSON(400, utils.ErrorStr("Invalid guild ID")) ctx.Abort() + return } } } diff --git a/app/http/middleware/logging.go b/app/http/middleware/logging.go index 99e2637..0bcec8a 100644 --- a/app/http/middleware/logging.go +++ b/app/http/middleware/logging.go @@ -5,6 +5,7 @@ import ( "github.com/getsentry/sentry-go" "github.com/gin-gonic/gin" "io/ioutil" + "runtime/debug" "strconv" ) @@ -46,6 +47,7 @@ func Logging(minLevel sentry.Level) gin.HandlerFunc { "user_id": ctx.Keys["userid"], "request_body": string(requestBody), "response": string(responseBody), + "stacktrace": string(debug.Stack()), }, Level: level, Message: fmt.Sprintf("HTTP %d on %s %s", statusCode, ctx.Request.Method, ctx.FullPath()), diff --git a/app/http/server.go b/app/http/server.go index 825846d..5d864a4 100644 --- a/app/http/server.go +++ b/app/http/server.go @@ -2,12 +2,14 @@ package http import ( "github.com/TicketsBot/GoPanel/app/http/endpoints/api" + "github.com/TicketsBot/GoPanel/app/http/endpoints/api/admin/botstaff" api_autoclose "github.com/TicketsBot/GoPanel/app/http/endpoints/api/autoclose" api_blacklist "github.com/TicketsBot/GoPanel/app/http/endpoints/api/blacklist" api_customisation "github.com/TicketsBot/GoPanel/app/http/endpoints/api/customisation" api_forms "github.com/TicketsBot/GoPanel/app/http/endpoints/api/forms" api_panels "github.com/TicketsBot/GoPanel/app/http/endpoints/api/panel" api_settings "github.com/TicketsBot/GoPanel/app/http/endpoints/api/settings" + api_override "github.com/TicketsBot/GoPanel/app/http/endpoints/api/staffoverride" api_tags "github.com/TicketsBot/GoPanel/app/http/endpoints/api/tags" api_team "github.com/TicketsBot/GoPanel/app/http/endpoints/api/team" api_ticket "github.com/TicketsBot/GoPanel/app/http/endpoints/api/ticket" @@ -67,8 +69,8 @@ func StartServer() { apiGroup.GET("/session", api.SessionHandler) } - guildAuthApiAdmin := apiGroup.Group("/:id", middleware.AuthenticateGuild(true, permission.Admin)) - guildAuthApiSupport := apiGroup.Group("/:id", middleware.AuthenticateGuild(true, permission.Support)) + guildAuthApiAdmin := apiGroup.Group("/:id", middleware.AuthenticateGuild(permission.Admin)) + guildAuthApiSupport := apiGroup.Group("/:id", middleware.AuthenticateGuild(permission.Support)) guildApiNoAuth := apiGroup.Group("/:id", middleware.ParseGuildId) { guildAuthApiSupport.GET("/channels", api.ChannelsHandler) @@ -150,6 +152,10 @@ func StartServer() { guildAuthApiAdmin.PUT("/team/:teamid/:snowflake", rl(middleware.RateLimitTypeGuild, 5, time.Second*10), api_team.AddMember) guildAuthApiAdmin.DELETE("/team/:teamid", api_team.DeleteTeam) guildAuthApiAdmin.DELETE("/team/:teamid/:snowflake", rl(middleware.RateLimitTypeGuild, 30, time.Minute), api_team.RemoveMember) + + guildAuthApiAdmin.GET("/staff-override", api_override.GetOverrideHandler) + guildAuthApiAdmin.POST("/staff-override", api_override.CreateOverrideHandler) + guildAuthApiAdmin.DELETE("/staff-override", api_override.DeleteOverrideHandler) } userGroup := router.Group("/user", middleware.AuthenticateToken) @@ -173,6 +179,13 @@ func StartServer() { } } + adminGroup := apiGroup.Group("/admin", middleware.AdminOnly) + { + adminGroup.GET("/bot-staff", botstaff.ListBotStaffHandler) + adminGroup.POST("/bot-staff/:userid", botstaff.AddBotStaffHandler) + adminGroup.DELETE("/bot-staff/:userid", botstaff.RemoveBotStaffHandler) + } + if err := router.Run(config.Conf.Server.Host); err != nil { panic(err) } diff --git a/frontend/src/components/Button.svelte b/frontend/src/components/Button.svelte index 37bb5ae..c151a7f 100644 --- a/frontend/src/components/Button.svelte +++ b/frontend/src/components/Button.svelte @@ -3,66 +3,66 @@ {/if} - + \ No newline at end of file diff --git a/frontend/src/components/form/RoleSelect.svelte b/frontend/src/components/form/RoleSelect.svelte index d9f7452..a7b8399 100644 --- a/frontend/src/components/form/RoleSelect.svelte +++ b/frontend/src/components/form/RoleSelect.svelte @@ -1,6 +1,6 @@ -`{#if label !== undefined} +{#if label !== undefined} -{/if}` +{/if}
+ + + + + + + + + + + + {#each staff as user} + + + + + {/each} + +
UsernameRemove
{user.username}#{user.discriminator} ({user.id}) + +
+
+ + + + + + + diff --git a/go.mod b/go.mod index 3c5c114..a73829b 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/BurntSushi/toml v0.3.1 github.com/TicketsBot/archiverclient v0.0.0-20220326163414-558fd52746dc github.com/TicketsBot/common v0.0.0-20220615205931-a6a31e73b52a - github.com/TicketsBot/database v0.0.0-20220621182433-accd1b2b81de + github.com/TicketsBot/database v0.0.0-20220623160906-a56dc9dfda90 github.com/TicketsBot/logarchiver v0.0.0-20220326162808-cdf0310f5e1c github.com/TicketsBot/worker v0.0.0-20220621165800-203b0004b733 github.com/apex/log v1.1.2 diff --git a/go.sum b/go.sum index e855e1e..9583b09 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/TicketsBot/common v0.0.0-20220615205931-a6a31e73b52a h1:SwA18cDURmnXS github.com/TicketsBot/common v0.0.0-20220615205931-a6a31e73b52a/go.mod h1:ZAoYcDD7SQLTsZT7dbo/X0J256+pogVRAReunCGng+U= github.com/TicketsBot/database v0.0.0-20220621182433-accd1b2b81de h1:UsRiB3KIwqIF92huRBFKAnCoGLyT9kBYYUycsapBZk0= github.com/TicketsBot/database v0.0.0-20220621182433-accd1b2b81de/go.mod h1:F57cywrZsnper1cy56Bx0c/HEsxQBLHz3Pl98WXblWw= +github.com/TicketsBot/database v0.0.0-20220623160906-a56dc9dfda90 h1:K0t6IaZdeZzEr2BaYj/NBuWIm/hA31jkqFh2c3nyDrw= +github.com/TicketsBot/database v0.0.0-20220623160906-a56dc9dfda90/go.mod h1:F57cywrZsnper1cy56Bx0c/HEsxQBLHz3Pl98WXblWw= github.com/TicketsBot/logarchiver v0.0.0-20220326162808-cdf0310f5e1c h1:OqGjFH6mbE6gd+NqI2ARJdtH3UUvhiAkD0r0fhGJK2s= github.com/TicketsBot/logarchiver v0.0.0-20220326162808-cdf0310f5e1c/go.mod h1:jgi2OXQKsd5nUnTIRkwvPmeuD/i7OhN68LKMssuQY1c= github.com/TicketsBot/ttlcache v1.6.1-0.20200405150101-acc18e37b261 h1:NHD5GB6cjlkpZFjC76Yli2S63/J2nhr8MuE6KlYJpQM= diff --git a/utils/permissionutils.go b/utils/permissionutils.go index e8e6182..b0e60cb 100644 --- a/utils/permissionutils.go +++ b/utils/permissionutils.go @@ -20,6 +20,24 @@ func GetPermissionLevel(guildId, userId uint64) (permission.PermissionLevel, err return permission.Admin, nil } + // Check staff override + staffOverride, err := dbclient.Client.StaffOverride.HasActiveOverride(guildId) + if err != nil { + return permission.Everyone, err + } + + // If staff override enabled and the user is bot staff, grant admin permissions + if staffOverride { + isBotStaff, err := dbclient.Client.BotStaff.IsStaff(userId) + if err != nil { + return permission.Everyone, err + } + + if isBotStaff { + return permission.Admin, nil + } + } + // get member member, err := botContext.GetGuildMember(guildId, userId) if err != nil {