whitelabel token page

This commit is contained in:
Dot-Rar 2020-05-26 18:04:58 +01:00
parent 951f588ea2
commit 396cb4ae47
13 changed files with 264 additions and 114 deletions

View File

@ -3,9 +3,11 @@ package api
import ( import (
"github.com/TicketsBot/GoPanel/config" "github.com/TicketsBot/GoPanel/config"
dbclient "github.com/TicketsBot/GoPanel/database" dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/rpc"
"github.com/TicketsBot/GoPanel/rpc/cache" "github.com/TicketsBot/GoPanel/rpc/cache"
"github.com/TicketsBot/GoPanel/rpc/ratelimit" "github.com/TicketsBot/GoPanel/rpc/ratelimit"
"github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/common/premium"
"github.com/TicketsBot/database" "github.com/TicketsBot/database"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/rxdn/gdl/objects/channel" "github.com/rxdn/gdl/objects/channel"
@ -32,10 +34,10 @@ func CreatePanel(ctx *gin.Context) {
data.MessageId = 0 data.MessageId = 0
// Check panel quota // Check panel quota
premiumChan := make(chan bool) // TODO: Whitelabel tokens & ratelimiters
go utils.IsPremiumGuild(guildId, premiumChan) premiumTier := rpc.PremiumClient.GetTierByGuildId(guildId, true, config.Conf.Bot.Token, ratelimit.Ratelimiter)
isPremium := <-premiumChan
if !isPremium { if premiumTier == premium.None {
panels, err := dbclient.Client.Panel.GetByGuild(guildId) panels, err := dbclient.Client.Panel.GetByGuild(guildId)
if err != nil { if err != nil {
ctx.AbortWithStatusJSON(500, gin.H{ ctx.AbortWithStatusJSON(500, gin.H{
@ -57,7 +59,7 @@ func CreatePanel(ctx *gin.Context) {
return return
} }
msgId, err := data.sendEmbed(isPremium) msgId, err := data.sendEmbed(premiumTier > premium.None)
if err != nil { if err != nil {
if err == request.ErrForbidden { if err == request.ErrForbidden {
ctx.AbortWithStatusJSON(500, gin.H{ ctx.AbortWithStatusJSON(500, gin.H{

View File

@ -1,17 +1,20 @@
package api package api
import ( import (
"github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/GoPanel/config"
"github.com/TicketsBot/GoPanel/rpc"
"github.com/TicketsBot/GoPanel/rpc/ratelimit"
"github.com/TicketsBot/common/premium"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func PremiumHandler(ctx *gin.Context) { func PremiumHandler(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64) guildId := ctx.Keys["guildid"].(uint64)
isPremium := make(chan bool) // TODO: Whitelabel tokens & ratelimiters
go utils.IsPremiumGuild(guildId, isPremium) premiumTier := rpc.PremiumClient.GetTierByGuildId(guildId, true, config.Conf.Bot.Token, ratelimit.Ratelimiter)
ctx.JSON(200, gin.H{ ctx.JSON(200, gin.H{
"premium": <-isPremium, "premium": premiumTier >= premium.Premium,
}) })
} }

View File

@ -4,9 +4,10 @@ import (
"fmt" "fmt"
"github.com/TicketsBot/GoPanel/config" "github.com/TicketsBot/GoPanel/config"
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/rpc"
"github.com/TicketsBot/GoPanel/rpc/cache" "github.com/TicketsBot/GoPanel/rpc/cache"
"github.com/TicketsBot/GoPanel/rpc/ratelimit" "github.com/TicketsBot/GoPanel/rpc/ratelimit"
"github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/common/premium"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/rxdn/gdl/rest" "github.com/rxdn/gdl/rest"
"github.com/rxdn/gdl/rest/request" "github.com/rxdn/gdl/rest/request"
@ -41,9 +42,9 @@ func SendMessage(ctx *gin.Context) {
} }
// Verify guild is premium // Verify guild is premium
isPremium := make(chan bool) // TODO: Whitelabel tokens & ratelimiters
go utils.IsPremiumGuild(guildId, isPremium) premiumTier := rpc.PremiumClient.GetTierByGuildId(guildId, true, config.Conf.Bot.Token, ratelimit.Ratelimiter)
if !<-isPremium { if premiumTier == premium.None {
ctx.AbortWithStatusJSON(402, gin.H{ ctx.AbortWithStatusJSON(402, gin.H{
"success": false, "success": false,
"error": "Guild is not premium", "error": "Guild is not premium",

View File

@ -0,0 +1,112 @@
package api
import (
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/messagequeue"
"github.com/TicketsBot/GoPanel/rpc"
"github.com/TicketsBot/common/premium"
"github.com/TicketsBot/common/tokenchange"
"github.com/TicketsBot/database"
"github.com/gin-gonic/gin"
"github.com/rxdn/gdl/rest"
)
func WhitelabelHandler(ctx *gin.Context) {
userId := ctx.Keys["userid"].(uint64)
premiumTier := rpc.PremiumClient.GetTierByUser(userId, false)
if premiumTier < premium.Whitelabel {
ctx.JSON(402, gin.H{
"success": false,
"error": "You must have the whitelabel premium tier",
})
return
}
// Get token
var data map[string]interface{}
if err := ctx.BindJSON(&data); err != nil {
ctx.JSON(400, gin.H{
"success": false,
"error": "Missing token",
})
return
}
token, ok := data["token"].(string)
if !ok {
ctx.JSON(400, gin.H{
"success": false,
"error": "Missing token",
})
return
}
// Validate token + get bot ID
bot, err := rest.GetCurrentUser(token, nil)
if err != nil {
ctx.JSON(400, gin.H{
"success": false,
"error": err.Error(),
})
return
}
if !bot.Bot {
ctx.JSON(400, gin.H{
"success": false,
"error": "Token is not of a bot user",
})
return
}
// Check if this is a different token
existing, err := dbclient.Client.Whitelabel.GetByUserId(userId)
if err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
if existing.Token == token {
// Respond with 200 to prevent information disclosure attack
ctx.JSON(200, gin.H{
"success": true,
"bot": bot,
})
return
}
if err = dbclient.Client.Whitelabel.Set(database.WhitelabelBot{
UserId: userId,
BotId: bot.Id,
Token: token,
}); err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
tokenChangeData := tokenchange.TokenChangeData{
Token: token,
NewId: bot.Id,
OldId: existing.BotId,
}
if err := tokenchange.PublishTokenChange(messagequeue.Client.Client, tokenChangeData); err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
ctx.JSON(200, gin.H{
"success": true,
"bot": bot,
})
}

View File

@ -2,8 +2,12 @@ package manage
import ( import (
"fmt" "fmt"
"github.com/TicketsBot/GoPanel/config"
"github.com/TicketsBot/GoPanel/rpc"
"github.com/TicketsBot/GoPanel/rpc/cache" "github.com/TicketsBot/GoPanel/rpc/cache"
"github.com/TicketsBot/GoPanel/rpc/ratelimit"
"github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/common/premium"
"github.com/gin-gonic/contrib/sessions" "github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@ -122,9 +126,9 @@ func WebChatWs(ctx *gin.Context) {
} }
// Verify the guild is premium // Verify the guild is premium
premium := make(chan bool) // TODO: Whitelabel tokens & ratelimiters
go utils.IsPremiumGuild(guildIdParsed, premium) premiumTier := rpc.PremiumClient.GetTierByGuildId(guildIdParsed, true, config.Conf.Bot.Token, ratelimit.Ratelimiter)
if !<-premium { if premiumTier == premium.None {
conn.Close() conn.Close()
return return
} }

View File

@ -0,0 +1,32 @@
package root
import (
"fmt"
"github.com/TicketsBot/GoPanel/config"
"github.com/TicketsBot/GoPanel/rpc"
"github.com/TicketsBot/common/premium"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
)
func WhitelabelHandler(ctx *gin.Context) {
store := sessions.Default(ctx)
if store == nil {
return
}
defer store.Save()
userId := store.Get("userid").(uint64)
premiumTier := rpc.PremiumClient.GetTierByUser(userId, false)
if premiumTier < premium.Whitelabel {
ctx.Redirect(302, fmt.Sprintf("%s/premium", config.Conf.Server.MainSite))
return
}
ctx.HTML(200, "main/whitelabel", gin.H{
"name": store.Get("name").(string),
"baseurl": config.Conf.Server.BaseUrl,
"avatar": store.Get("avatar").(string),
})
}

View File

@ -56,6 +56,7 @@ func StartServer() {
authenticateGuild := authorized.Group("/", middleware.AuthenticateGuild(false)) authenticateGuild := authorized.Group("/", middleware.AuthenticateGuild(false))
authorized.GET("/", root.IndexHandler) authorized.GET("/", root.IndexHandler)
authorized.GET("/whitelabel", root.WhitelabelHandler)
authorized.GET("/logout", root.LogoutHandler) authorized.GET("/logout", root.LogoutHandler)
authenticateGuild.GET("/manage/:id/settings", manage.SettingsHandler) authenticateGuild.GET("/manage/:id/settings", manage.SettingsHandler)
@ -108,6 +109,7 @@ func StartServer() {
userGroup := router.Group("/user", middleware.AuthenticateToken) userGroup := router.Group("/user", middleware.AuthenticateToken)
{ {
userGroup.GET("/guilds", api.GetGuilds) userGroup.GET("/guilds", api.GetGuilds)
userGroup.POST("/whitelabel", api.WhitelabelHandler)
} }
if err := router.Run(config.Conf.Server.Host); err != nil { if err := router.Run(config.Conf.Server.Host); err != nil {
@ -119,6 +121,7 @@ func createRenderer() multitemplate.Renderer {
r := multitemplate.NewRenderer() r := multitemplate.NewRenderer()
r = addMainTemplate(r, "index") r = addMainTemplate(r, "index")
r = addMainTemplate(r, "whitelabel")
r = addManageTemplate(r, "blacklist") r = addManageTemplate(r, "blacklist")
r = addManageTemplate(r, "logs") r = addManageTemplate(r, "logs")

View File

@ -9,10 +9,12 @@ import (
"github.com/TicketsBot/GoPanel/config" "github.com/TicketsBot/GoPanel/config"
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/messagequeue" "github.com/TicketsBot/GoPanel/messagequeue"
"github.com/TicketsBot/GoPanel/rpc"
"github.com/TicketsBot/GoPanel/rpc/cache" "github.com/TicketsBot/GoPanel/rpc/cache"
"github.com/TicketsBot/GoPanel/rpc/ratelimit" "github.com/TicketsBot/GoPanel/rpc/ratelimit"
"github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/archiverclient" "github.com/TicketsBot/archiverclient"
"github.com/TicketsBot/common/premium"
"github.com/apex/log" "github.com/apex/log"
gdlratelimit "github.com/rxdn/gdl/rest/ratelimit" gdlratelimit "github.com/rxdn/gdl/rest/ratelimit"
"math/rand" "math/rand"
@ -33,13 +35,20 @@ func main() {
database.ConnectToDatabase() database.ConnectToDatabase()
cache.Instance = cache.NewCache() cache.Instance = cache.NewCache()
manage.Archiver = archiverclient.NewArchiverClientWithTimeout(config.Conf.Bot.ObjectStore, time.Second * 15) manage.Archiver = archiverclient.NewArchiverClientWithTimeout(config.Conf.Bot.ObjectStore, time.Second*15)
utils.LoadEmoji() utils.LoadEmoji()
messagequeue.Client = messagequeue.NewRedisClient() messagequeue.Client = messagequeue.NewRedisClient()
go Listen(messagequeue.Client) go Listen(messagequeue.Client)
rpc.PremiumClient = premium.NewPremiumLookupClient(
premium.NewPatreonClient(config.Conf.Bot.PremiumLookupProxyUrl, config.Conf.Bot.PremiumLookupProxyKey),
messagequeue.Client.Client,
cache.Instance.PgCache,
database.Client,
)
ratelimit.Ratelimiter = gdlratelimit.NewRateLimiter(gdlratelimit.NewRedisStore(messagequeue.Client.Client, "ratelimit"), 1) // TODO: Use values from config ratelimit.Ratelimiter = gdlratelimit.NewRateLimiter(gdlratelimit.NewRedisStore(messagequeue.Client.Client, "ratelimit"), 1) // TODO: Use values from config
http.StartServer() http.StartServer()

8
go.mod
View File

@ -5,7 +5,8 @@ go 1.14
require ( require (
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
github.com/TicketsBot/archiverclient v0.0.0-20200425115930-0ca198cc8306 github.com/TicketsBot/archiverclient v0.0.0-20200425115930-0ca198cc8306
github.com/TicketsBot/database v0.0.0-20200516170158-fd8a949aec2c github.com/TicketsBot/common v0.0.0-20200526165242-b17694befe05
github.com/TicketsBot/database v0.0.0-20200526163954-2cb21ac72d23
github.com/apex/log v1.1.2 github.com/apex/log v1.1.2
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible
@ -13,15 +14,14 @@ require (
github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2 github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2
github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607 github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607
github.com/gin-gonic/gin v1.6.2 github.com/gin-gonic/gin v1.6.2
github.com/go-redis/redis v6.15.7+incompatible github.com/go-redis/redis v6.15.8+incompatible
github.com/gofrs/uuid v3.3.0+incompatible github.com/gofrs/uuid v3.3.0+incompatible
github.com/gorilla/sessions v1.2.0 // indirect github.com/gorilla/sessions v1.2.0 // indirect
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2
github.com/jackc/pgx/v4 v4.6.0 github.com/jackc/pgx/v4 v4.6.0
github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62 github.com/rxdn/gdl v0.0.0-20200522202912-4ae241eb98c1
github.com/rxdn/gdl v0.0.0-20200511170555-8ab2206d70df
github.com/sirupsen/logrus v1.5.0 github.com/sirupsen/logrus v1.5.0
github.com/ulule/limiter/v3 v3.5.0 github.com/ulule/limiter/v3 v3.5.0
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a

View File

@ -8,6 +8,12 @@
<p>Servers</p> <p>Servers</p>
</a> </a>
</li> </li>
<li class="nav-item sidebar-bottom" style="top: 60px">
<a href="/whitelabel">
<i class="fas fa-robot"></i>
<p>Whitelabel</p>
</a>
</li>
<li class="nav-item sidebar-bottom" style="bottom: 60px"> <li class="nav-item sidebar-bottom" style="bottom: 60px">
<a href="/logout"> <a href="/logout">

View File

@ -0,0 +1,66 @@
{{define "content"}}
<div class="content">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h4 class="card-title">Whitelabel</h4>
</div>
<div class="card-body" id="card">
<form onsubmit="updateSettings(); return false;">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label>Bot Token</label>
<input name="token" type="text" class="form-control"
placeholder="9ViiGeUZlFJKIfSzodnzZT6W.bX8IAh.p9gG0tElMXg1EqwAChqaYz3swFY" id="token">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 ">
<p class="white">Note: You will not be able to view the token after submitting it</p>
</div>
</div>
<div class="row">
<div class="col-md-2 pr-1">
<div class="form-group">
<button class="btn btn-primary btn-fill" type="submit"><i class="fas fa-paper-plane"></i>
Submit
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div aria-live="polite" aria-atomic="true" style="position: relative">
<div style="position: absolute; right: 10px" id="toast-container">
</div>
</div>
</div>
<script>
async function updateSettings() {
const token = document.getElementById('token').value;
const data = {
token: token
};
const res = await axios.post('/user/whitelabel', data);
if (res.status !== 200 || !res.data.success) {
showToast('Error', res.data.error);
return;
}
showToast('Success', `Started tickets whitelabel on ${res.data.bot.username}#${res.data.bot.discriminator}`);
}
</script>
{{end}}

5
rpc/premiumclient.go Normal file
View File

@ -0,0 +1,5 @@
package rpc
import "github.com/TicketsBot/common/premium"
var PremiumClient *premium.PremiumLookupClient

View File

@ -1,93 +0,0 @@
package utils
import (
"encoding/json"
"fmt"
"github.com/TicketsBot/GoPanel/config"
"github.com/TicketsBot/GoPanel/rpc/cache"
"github.com/TicketsBot/GoPanel/rpc/ratelimit"
"github.com/TicketsBot/GoPanel/database"
gocache "github.com/robfig/go-cache"
"github.com/rxdn/gdl/rest"
"io/ioutil"
"net/http"
"strconv"
"time"
)
type ProxyResponse struct {
Premium bool
Tier int
}
// TODO: Use Redis cache
var premiumCache = gocache.New(10 * time.Minute, 10 * time.Minute)
func IsPremiumGuild(guildId uint64, ch chan bool) {
guildIdRaw := strconv.FormatUint(guildId, 10)
if premium, ok := premiumCache.Get(guildIdRaw); ok {
ch<-premium.(bool)
return
}
// First lookup by premium key, then votes, then patreon
// TODO: Lookup Patreon first
// TODO: Handle error
hasPremiumKey, _ := database.Client.PremiumGuilds.IsPremium(guildId)
if hasPremiumKey {
if err := premiumCache.Add(guildIdRaw, true, 10 * time.Minute); err != nil {
fmt.Println(err.Error())
}
ch<-true
} else {
// Get guild object
guild, found := cache.Instance.GetGuild(guildId, false)
if !found {
var err error
guild, err = rest.GetGuild(config.Conf.Bot.Token, ratelimit.Ratelimiter, guildId)
if err == nil { // cache
go cache.Instance.StoreGuild(guild)
}
}
// TODO: Find a way to stop people using votes to exploit panels
// Lookup Patreon
client := &http.Client{
Timeout: time.Second * 3,
}
url := fmt.Sprintf("%s/ispremium?key=%s&id=%d", config.Conf.Bot.PremiumLookupProxyUrl, config.Conf.Bot.PremiumLookupProxyKey, guild.OwnerId)
req, err := http.NewRequest("GET", url, nil)
res, err := client.Do(req); if err != nil {
fmt.Println(err.Error())
ch<-false
return
}
defer res.Body.Close()
content, err := ioutil.ReadAll(res.Body); if err != nil {
fmt.Println(err.Error())
ch<-false
return
}
var proxyResponse ProxyResponse
if err = json.Unmarshal(content, &proxyResponse); err != nil {
fmt.Println(err.Error())
ch<-false
return
}
if err := premiumCache.Add(guildIdRaw, proxyResponse.Premium, 10 * time.Minute); err != nil {
fmt.Println(err.Error())
}
ch <-proxyResponse.Premium
}
}