From 61621906c06a97e96c2991355a6155d3a7d41581 Mon Sep 17 00:00:00 2001 From: rxdn <29165304+rxdn@users.noreply.github.com> Date: Thu, 24 Dec 2020 20:42:26 +0000 Subject: [PATCH] Whitelabel slash commands --- app/http/endpoints/api/getpermissionlevel.go | 17 +- app/http/endpoints/api/guilds.go | 15 +- .../whitelabelcreateinteractions.go | 72 +++++ .../api/whitelabel/whitelabelgetpublickey.go | 53 ++++ .../api/whitelabel/whitelabelpostpublickey.go | 65 +++++ app/http/endpoints/manage/logsview.go | 8 +- app/http/endpoints/manage/modmaillogsview.go | 8 +- app/http/endpoints/manage/webchatws.go | 13 +- app/http/middleware/authenticateguild.go | 9 +- app/http/server.go | 13 +- botcontext/botcontext.go | 6 +- go.mod | 11 +- public/templates/includes/navbar.tmpl | 5 +- public/templates/layouts/main.tmpl | 2 + public/templates/views/whitelabel.tmpl | 252 +++++++++--------- utils/permissionutils.go | 11 +- 16 files changed, 397 insertions(+), 163 deletions(-) create mode 100644 app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go create mode 100644 app/http/endpoints/api/whitelabel/whitelabelgetpublickey.go create mode 100644 app/http/endpoints/api/whitelabel/whitelabelpostpublickey.go diff --git a/app/http/endpoints/api/getpermissionlevel.go b/app/http/endpoints/api/getpermissionlevel.go index 1c6ab78..4fd5bf9 100644 --- a/app/http/endpoints/api/getpermissionlevel.go +++ b/app/http/endpoints/api/getpermissionlevel.go @@ -1,10 +1,12 @@ package api import ( + "context" "fmt" "github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/common/permission" "github.com/gin-gonic/gin" + "golang.org/x/sync/errgroup" "strconv" "strings" ) @@ -21,10 +23,11 @@ func GetPermissionLevel(ctx *gin.Context) { return } - // TODO: Check whether the bot is in the guild to prevent us getting maliciously 429'd + // TODO: This is insanely inefficient levels := make(map[string]permission.PermissionLevel) + group, _ := errgroup.WithContext(context.Background()) for _, raw := range guilds { guildId, err := strconv.ParseUint(raw, 10, 64) if err != nil { @@ -35,8 +38,16 @@ func GetPermissionLevel(ctx *gin.Context) { return } - level := utils.GetPermissionLevel(guildId, userId) - levels[strconv.FormatUint(guildId, 10)] = level + group.Go(func() error { + level, err := utils.GetPermissionLevel(guildId, userId) + levels[strconv.FormatUint(guildId, 10)] = level + return err + }) + } + + if err := group.Wait(); err != nil { + ctx.JSON(500, utils.ErrorToResponse(err)) + return } ctx.JSON(200, gin.H{ diff --git a/app/http/endpoints/api/guilds.go b/app/http/endpoints/api/guilds.go index 4c6d333..f9eb858 100644 --- a/app/http/endpoints/api/guilds.go +++ b/app/http/endpoints/api/guilds.go @@ -3,6 +3,7 @@ package api import ( "context" "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/rpc/cache" "github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/common/permission" "github.com/gin-gonic/gin" @@ -39,6 +40,12 @@ func GetGuilds(ctx *gin.Context) { g := g group.Go(func() error { + // verify bot is in guild + _, ok := cache.Instance.GetGuild(g.GuildId, false) + if !ok { + return nil + } + fakeGuild := guild.Guild{ Id: g.GuildId, Owner: g.Owner, @@ -49,7 +56,12 @@ func GetGuilds(ctx *gin.Context) { fakeGuild.OwnerId = userId } - if utils.GetPermissionLevel(g.GuildId, userId) >= permission.Support { + permLevel, err := utils.GetPermissionLevel(g.GuildId, userId) + if err != nil { + return err + } + + if permLevel >= permission.Support { lock.Lock() adminGuilds = append(adminGuilds, wrappedGuild{ Id: g.GuildId, @@ -58,6 +70,7 @@ func GetGuilds(ctx *gin.Context) { }) lock.Unlock() } + return nil }) } diff --git a/app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go b/app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go new file mode 100644 index 0000000..088f4cf --- /dev/null +++ b/app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go @@ -0,0 +1,72 @@ +package api + +import ( + "github.com/TicketsBot/GoPanel/botcontext" + "github.com/TicketsBot/GoPanel/database" + command "github.com/TicketsBot/worker/bot/command/impl" + "github.com/gin-gonic/gin" + "github.com/rxdn/gdl/rest" + "time" +) + +func WhitelabelCreateInteractions(ctx *gin.Context) { + userId := ctx.Keys["userid"].(uint64) + + // Get bot + bot, err := database.Client.Whitelabel.GetByUserId(userId) + if err != nil { + ctx.JSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + // Ensure bot exists + if bot.BotId == 0 { + ctx.JSON(404, gin.H{ + "success": false, + "error": "No bot found", + }) + return + } + + botContext, err := botcontext.ContextForGuild(0) + if err != nil { + ctx.JSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + for _, cmd := range command.Commands { + properties := cmd.Properties() + + if properties.MessageOnly || properties.AdminOnly || properties.HelperOnly { + continue + } + + option := command.BuildOption(cmd) + + data := rest.CreateCommandData{ + Name: option.Name, + Description: option.Description, + Options: option.Options, + } + + if _, err := rest.CreateGlobalCommand(bot.Token, botContext.RateLimiter, bot.BotId, data); err != nil { + ctx.JSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + time.Sleep(time.Second) + } + + ctx.JSON(200, gin.H{ + "success": true, + }) +} diff --git a/app/http/endpoints/api/whitelabel/whitelabelgetpublickey.go b/app/http/endpoints/api/whitelabel/whitelabelgetpublickey.go new file mode 100644 index 0000000..c95ef61 --- /dev/null +++ b/app/http/endpoints/api/whitelabel/whitelabelgetpublickey.go @@ -0,0 +1,53 @@ +package api + +import ( + "github.com/TicketsBot/GoPanel/database" + "github.com/gin-gonic/gin" +) + +func WhitelabelGetPublicKey(ctx *gin.Context) { + type data struct { + PublicKey string `json:"public_key"` + } + + userId := ctx.Keys["userid"].(uint64) + + // Get bot + bot, err := database.Client.Whitelabel.GetByUserId(userId) + if err != nil { + ctx.JSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + // Ensure bot exists + if bot.BotId == 0 { + ctx.JSON(404, gin.H{ + "success": false, + "error": "No bot found", + }) + return + } + + key, err := database.Client.WhitelabelKeys.Get(bot.BotId) + if err != nil { + ctx.JSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + if key == "" { + ctx.JSON(404, gin.H{ + "success": false, + }) + } else { + ctx.JSON(200, gin.H{ + "success": true, + "key": key, + }) + } +} diff --git a/app/http/endpoints/api/whitelabel/whitelabelpostpublickey.go b/app/http/endpoints/api/whitelabel/whitelabelpostpublickey.go new file mode 100644 index 0000000..773d5ff --- /dev/null +++ b/app/http/endpoints/api/whitelabel/whitelabelpostpublickey.go @@ -0,0 +1,65 @@ +package api + +import ( + "encoding/hex" + "github.com/TicketsBot/GoPanel/database" + "github.com/gin-gonic/gin" +) + +func WhitelabelPostPublicKey(ctx *gin.Context) { + type data struct { + PublicKey string `json:"public_key"` + } + + userId := ctx.Keys["userid"].(uint64) + + // Get bot + bot, err := database.Client.Whitelabel.GetByUserId(userId) + if err != nil { + ctx.JSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + // Ensure bot exists + if bot.BotId == 0 { + ctx.JSON(404, gin.H{ + "success": false, + "error": "No bot found", + }) + return + } + + // Parse status + var body data + if err := ctx.BindJSON(&body); err != nil { + ctx.JSON(400, gin.H{ + "success": false, + "error": "No public key provided", + }) + return + } + + bytes, err := hex.DecodeString(body.PublicKey) + if err != nil || len(bytes) != 32 { + ctx.JSON(400, gin.H{ + "success": false, + "error": "Invalid public key", + }) + return + } + + if err := database.Client.WhitelabelKeys.Set(bot.BotId, body.PublicKey); err != nil { + ctx.JSON(500, gin.H{ + "success": false, + "error": err.Error(), + }) + return + } + + ctx.JSON(200, gin.H{ + "success": true, + }) +} diff --git a/app/http/endpoints/manage/logsview.go b/app/http/endpoints/manage/logsview.go index c4b4e6e..53af068 100644 --- a/app/http/endpoints/manage/logsview.go +++ b/app/http/endpoints/manage/logsview.go @@ -59,7 +59,13 @@ func LogViewHandler(ctx *gin.Context) { } // Verify the user has permissions to be here - if utils.GetPermissionLevel(guildId, userId) < permission.Support && ticket.UserId != userId { + permLevel, err := utils.GetPermissionLevel(guildId, userId) + if err != nil { + ctx.JSON(500, utils.ErrorToResponse(err)) + return + } + + if permLevel < permission.Support && ticket.UserId != userId { ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page return } diff --git a/app/http/endpoints/manage/modmaillogsview.go b/app/http/endpoints/manage/modmaillogsview.go index 0d63733..ea5c607 100644 --- a/app/http/endpoints/manage/modmaillogsview.go +++ b/app/http/endpoints/manage/modmaillogsview.go @@ -56,7 +56,13 @@ func ModmailLogViewHandler(ctx *gin.Context) { } // Verify the user has permissions to be here - if utils.GetPermissionLevel(guildId, userId) < permission.Support && archive.UserId != userId { + permLevel, err := utils.GetPermissionLevel(guildId, userId) + if err != nil { + ctx.JSON(500, utils.ErrorToResponse(err)) + return + } + + if permLevel < permission.Support && archive.UserId != userId { utils.ErrorPage(ctx, 403, "You do not have permission to view this archive") return } diff --git a/app/http/endpoints/manage/webchatws.go b/app/http/endpoints/manage/webchatws.go index 73075ff..1cdf81a 100644 --- a/app/http/endpoints/manage/webchatws.go +++ b/app/http/endpoints/manage/webchatws.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/TicketsBot/GoPanel/botcontext" "github.com/TicketsBot/GoPanel/rpc" - "github.com/TicketsBot/GoPanel/rpc/cache" "github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/common/permission" "github.com/TicketsBot/common/premium" @@ -113,11 +112,15 @@ func WebChatWs(ctx *gin.Context) { return } - // Get object for selected guild - guild, _ := cache.Instance.GetGuild(guildIdParsed, false) - // Verify the user has permissions to be here - if utils.GetPermissionLevel(guild.Id, userId) < permission.Admin { + permLevel, err := utils.GetPermissionLevel(guildIdParsed, userId) + if err != nil { + fmt.Println(err.Error()) + conn.Close() + return + } + + if permLevel < permission.Admin { fmt.Println(err.Error()) conn.Close() return diff --git a/app/http/middleware/authenticateguild.go b/app/http/middleware/authenticateguild.go index 2609dce..feabaea 100644 --- a/app/http/middleware/authenticateguild.go +++ b/app/http/middleware/authenticateguild.go @@ -46,7 +46,14 @@ func AuthenticateGuild(isApiMethod bool, requiredPermissionLevel permission.Perm // Verify the user has permissions to be here userId := ctx.Keys["userid"].(uint64) - if utils.GetPermissionLevel(guild.Id, userId) < requiredPermissionLevel { + + permLevel, err := utils.GetPermissionLevel(guild.Id, userId) + if err != nil { + ctx.JSON(500, utils.ErrorToResponse(err)) + return + } + + if permLevel < requiredPermissionLevel { if isApiMethod { ctx.AbortWithStatusJSON(403, gin.H{ "success": false, diff --git a/app/http/server.go b/app/http/server.go index db9c4c9..dfa9c87 100644 --- a/app/http/server.go +++ b/app/http/server.go @@ -47,7 +47,7 @@ func StartServer() { router.Use(static.Serve("/assets/", static.LocalFile("./public/static", false))) router.Use(gin.Recovery()) - router.Use(createLimiter(600, time.Minute * 10)) + router.Use(createLimiter(600, time.Minute*10)) // Register templates router.HTMLRender = createRenderer() @@ -55,8 +55,7 @@ func StartServer() { router.GET("/login", root.LoginHandler) router.GET("/callback", root.CallbackHandler) - router.GET("/manage/:id/logs/view/:ticket", manage.LogViewHandler) // we check in the actual handler bc of a custom redirect - router.GET("/manage/:id/logs/modmail/view/:uuid", manage.ModmailLogViewHandler) // we check in the actual handler bc of a custom redirect + router.GET("/manage/:id/logs/view/:ticket", manage.LogViewHandler) // we check in the actual handler bc of a custom redirect authorized := router.Group("/", middleware.AuthenticateCookie) { @@ -71,7 +70,6 @@ func StartServer() { authenticateGuildAdmin.GET("/manage/:id/settings", manage.SettingsHandler) authenticateGuildSupport.GET("/manage/:id/logs", manage.LogsHandler) - authenticateGuildSupport.GET("/manage/:id/logs/modmail", manage.ModmailLogsHandler) authenticateGuildSupport.GET("/manage/:id/blacklist", manage.BlacklistHandler) authenticateGuildAdmin.GET("/manage/:id/panels", manage.PanelHandler) authenticateGuildSupport.GET("/manage/:id/tags", manage.TagsHandler) @@ -139,10 +137,12 @@ func StartServer() { whitelabelGroup.GET("/", api_whitelabel.WhitelabelGet) whitelabelApiGroup.GET("/errors", api_whitelabel.WhitelabelGetErrors) whitelabelApiGroup.GET("/guilds", api_whitelabel.WhitelabelGetGuilds) - whitelabelApiGroup.POST("/modmail", api_whitelabel.WhitelabelModmailPost) + whitelabelApiGroup.GET("/public-key", api_whitelabel.WhitelabelGetPublicKey) + whitelabelApiGroup.POST("/public-key", api_whitelabel.WhitelabelPostPublicKey) + whitelabelApiGroup.POST("/create-interactions", api_whitelabel.WhitelabelCreateInteractions) whitelabelApiGroup.Group("/").Use(createLimiter(10, time.Minute)).POST("/", api_whitelabel.WhitelabelPost) - whitelabelApiGroup.Group("/").Use(createLimiter(1, time.Second * 5)).POST("/status", api_whitelabel.WhitelabelStatusPost) + whitelabelApiGroup.Group("/").Use(createLimiter(1, time.Second*5)).POST("/status", api_whitelabel.WhitelabelStatusPost) } } @@ -177,6 +177,7 @@ func addMainTemplate(renderer multitemplate.Renderer, name string, extra ...stri "./public/templates/includes/head.tmpl", "./public/templates/includes/sidebar.tmpl", "./public/templates/includes/loadingscreen.tmpl", + "./public/templates/includes/notifymodal.tmpl", fmt.Sprintf("./public/templates/views/%s.tmpl", name), } diff --git a/botcontext/botcontext.go b/botcontext/botcontext.go index a3847a1..ec45b01 100644 --- a/botcontext/botcontext.go +++ b/botcontext/botcontext.go @@ -5,8 +5,8 @@ import ( dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/messagequeue" "github.com/TicketsBot/GoPanel/rpc/cache" + "github.com/TicketsBot/common/permission" "github.com/TicketsBot/database" - "github.com/go-redis/redis" "github.com/rxdn/gdl/objects/channel" "github.com/rxdn/gdl/objects/guild" "github.com/rxdn/gdl/objects/member" @@ -23,8 +23,8 @@ func (ctx BotContext) Db() *database.Database { return dbclient.Client } -func (ctx BotContext) Redis() *redis.Client { - return messagequeue.Client.Client +func (ctx BotContext) Cache() permission.PermissionCache { + return permission.NewRedisCache(messagequeue.Client.Client) } func (ctx BotContext) IsBotAdmin(userId uint64) bool { diff --git a/go.mod b/go.mod index d9de6d7..1834301 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.14 require ( github.com/BurntSushi/toml v0.3.1 github.com/TicketsBot/archiverclient v0.0.0-20200704164621-09d42dd941e0 - github.com/TicketsBot/common v0.0.0-20200925115036-a1bbe85f45bb - github.com/TicketsBot/database v0.0.0-20200723134637-72f4cd31eef6 - github.com/TicketsBot/logarchiver v0.0.0-20200425163447-199b93429026 // indirect + github.com/TicketsBot/common v0.0.0-20201222195753-3dd751ebabf8 + github.com/TicketsBot/database v0.0.0-20201224193659-c89391f44b57 + github.com/TicketsBot/worker v0.0.0-20201224203453-0c8f9a415306 github.com/apex/log v1.1.2 github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible @@ -19,11 +19,10 @@ require ( github.com/gofrs/uuid v3.3.0+incompatible github.com/gorilla/sessions v1.2.0 // indirect github.com/gorilla/websocket v1.4.2 - github.com/jackc/pgx/v4 v4.6.0 - github.com/klauspost/compress v1.10.10 // indirect + github.com/jackc/pgx/v4 v4.7.1 github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c github.com/pkg/errors v0.9.1 - github.com/rxdn/gdl v0.0.0-20201123164345-0469e0a3cea3 + github.com/rxdn/gdl v0.0.0-20201214225805-4ae598a98327 github.com/sirupsen/logrus v1.5.0 github.com/ulule/limiter/v3 v3.5.0 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a diff --git a/public/templates/includes/navbar.tmpl b/public/templates/includes/navbar.tmpl index 24d2455..48a3be2 100644 --- a/public/templates/includes/navbar.tmpl +++ b/public/templates/includes/navbar.tmpl @@ -5,10 +5,7 @@ Settings