more
This commit is contained in:
@@ -6,8 +6,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getDiscordUser(token string) User {
|
func GetDiscordUser(token string) User {
|
||||||
req, err := http.NewRequest("GET", "https://discord.com/api/v19/users/@me", nil)
|
req, err := http.NewRequest("GET", "https://discord.com/api/v10/users/@me", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ func getDiscordUser(token string) User {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var respObj = User{}
|
var respObj User
|
||||||
err = json.Unmarshal(respBody, &respObj)
|
err = json.Unmarshal(respBody, &respObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package discord
|
package discord
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
@@ -17,3 +19,43 @@ type User struct {
|
|||||||
Premium_type int `json:"premium_type"`
|
Premium_type int `json:"premium_type"`
|
||||||
Public_flags int `json:"public_flags"`
|
Public_flags int `json:"public_flags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SimpleUser struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Discriminator string `json:"discriminator"`
|
||||||
|
Avatar string `json:"avatar"`
|
||||||
|
Bot bool `json:"bot"`
|
||||||
|
Banner string `json:"banner"`
|
||||||
|
AccentColor int `json:"accent_color"`
|
||||||
|
Verified bool `json:"verified"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u User) Simplify() SimpleUser {
|
||||||
|
return SimpleUser{
|
||||||
|
ID: u.ID,
|
||||||
|
Username: u.Username,
|
||||||
|
Discriminator: u.Discriminator,
|
||||||
|
Avatar: u.Avatar,
|
||||||
|
Bot: u.Bot,
|
||||||
|
Banner: u.Banner,
|
||||||
|
AccentColor: u.Accent_color,
|
||||||
|
Verified: u.Verified,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u User) MarshalBinary() ([]byte, error) {
|
||||||
|
return json.Marshal(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) UnmarshalBinary(data []byte) error {
|
||||||
|
return json.Unmarshal(data, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u SimpleUser) MarshalBinary() ([]byte, error) {
|
||||||
|
return json.Marshal(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SimpleUser) UnmarshalBinary(data []byte) error {
|
||||||
|
return json.Unmarshal(data, u)
|
||||||
|
}
|
||||||
16
endpoints/access.go
Normal file
16
endpoints/access.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package endpoints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.zomo.dev/zomo/discord-retokenizer/storage"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func access(c *gin.Context) {
|
||||||
|
authType, token := getAuthorization(c)
|
||||||
|
if authType != AuthorizationScopeBot {
|
||||||
|
c.AbortWithStatus(401)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, botToken := storage.BotTokenFromToken(token)
|
||||||
|
c.String(200, botToken)
|
||||||
|
}
|
||||||
@@ -23,15 +23,16 @@ func getAuthorization(c *gin.Context) (AuthorizationScope, string) {
|
|||||||
if len(headerSpl) != 2 {
|
if len(headerSpl) != 2 {
|
||||||
return AuthorizationScopeNone, ""
|
return AuthorizationScopeNone, ""
|
||||||
}
|
}
|
||||||
if headerSpl[0] == "Bearer" {
|
prefix := headerSpl[0]
|
||||||
if storage.CheckLoginToken(headerSpl[1], c.ClientIP()) {
|
token := strings.ToLower(headerSpl[1])
|
||||||
return AuthorizationScopeUser, headerSpl[1]
|
if prefix == "Bearer" {
|
||||||
|
if storage.CheckLoginToken(token, c.ClientIP()) {
|
||||||
|
return AuthorizationScopeUser, token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if headerSpl[0] == "Bot" {
|
if prefix == "Bot" {
|
||||||
// TODO check bot token
|
if found, _ := storage.BotTokenFromToken(token); found {
|
||||||
if true {
|
return AuthorizationScopeBot, token
|
||||||
return AuthorizationScopeBot, headerSpl[1]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return AuthorizationScopeNone, ""
|
return AuthorizationScopeNone, ""
|
||||||
|
|||||||
69
endpoints/bots.go
Normal file
69
endpoints/bots.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package endpoints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.zomo.dev/zomo/discord-retokenizer/storage"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SimpleDiscordUser struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Discriminator string `json:"discriminator"`
|
||||||
|
Avatar string `json:"avatar"`
|
||||||
|
Banner string `json:"banner"`
|
||||||
|
Accent_color int `json:"accent_color"`
|
||||||
|
Verified bool `json:"verified"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BotData struct {
|
||||||
|
Token string
|
||||||
|
ID string
|
||||||
|
Loaded SimpleDiscordUser
|
||||||
|
}
|
||||||
|
|
||||||
|
// one array of just bot ids
|
||||||
|
// `bot:(id):token`
|
||||||
|
// `bot:(id):data`
|
||||||
|
// SimpleDiscordUser
|
||||||
|
|
||||||
|
func bots(c *gin.Context) {
|
||||||
|
bots := storage.GetBots()
|
||||||
|
c.JSON(200, bots)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bot(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
bot := storage.GetBot(id)
|
||||||
|
c.JSON(200, bot)
|
||||||
|
}
|
||||||
|
|
||||||
|
type addBotBody struct {
|
||||||
|
Token string `json:"token" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func addBot(c *gin.Context) {
|
||||||
|
var tokenBody addBotBody
|
||||||
|
if err := c.BindJSON(&tokenBody); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
didSet := storage.AddBot(tokenBody.Token)
|
||||||
|
if didSet {
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"success": true,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
c.JSON(400, gin.H{
|
||||||
|
"success": false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeBot(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
storage.RemoveBot(id)
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"success": true,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -10,23 +10,25 @@ func Run() {
|
|||||||
public := r.Group("/")
|
public := r.Group("/")
|
||||||
|
|
||||||
public.POST("/login", login) //web login
|
public.POST("/login", login) //web login
|
||||||
public.POST("/access", func(c *gin.Context) {}) //access token
|
public.GET("/access", access) //access token
|
||||||
|
|
||||||
private := r.Group("/")
|
private := r.Group("/")
|
||||||
private.Use(userIsAuthorized)
|
private.Use(userIsAuthorized)
|
||||||
|
|
||||||
private.PATCH("/login", updateLogin) //change username/password (required before adding bots)
|
private.PATCH("/login", updateLogin) //change username/password (required before adding bots)
|
||||||
|
private.GET("/login/tokens", getLoginTokens) //list of login tokens
|
||||||
|
private.DELETE("/login/tokens", clearLoginTokens) //clears all login tokens
|
||||||
|
|
||||||
private.GET("/bots", func(c *gin.Context) {}) //generalized list of bots
|
private.GET("/bots", bots) //generalized list of bots
|
||||||
private.GET("/bot/:bot", func(c *gin.Context) {}) //specific bot
|
private.GET("/bot/:id", bot) //specific bot
|
||||||
private.POST("/bot/", func(c *gin.Context) {}) //add bot given token
|
private.POST("/bot", addBot) //add bot given token
|
||||||
private.DELETE("/bot/:bot", func(c *gin.Context) {}) //remove bot
|
private.DELETE("/bot/:id", removeBot) //remove bot
|
||||||
|
|
||||||
private.GET("/tokens", func(c *gin.Context) {}) //generalized list of tokens
|
private.GET("/tokens", getTokens) //generalized list of tokens
|
||||||
private.GET("/token/:token", func(c *gin.Context) {}) //specific token
|
private.GET("/token/:id", getToken) //specific token
|
||||||
private.POST("/token/", func(c *gin.Context) {}) //new token given bot (so you cant add a token if theres no bots)
|
private.POST("/token", addToken) //new token given bot (so you cant add a token if theres no bots)
|
||||||
private.DELETE("/token/:token", func(c *gin.Context) {}) //remove token
|
private.DELETE("/token/:id", deleteToken) //remove token
|
||||||
private.PATCH("/token/:token", func(c *gin.Context) {}) //update token given bot
|
private.PATCH("/token/:id", updateToken) //update token given bot
|
||||||
|
|
||||||
r.Run()
|
r.Run()
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,6 @@ type LoginBody struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func login(c *gin.Context) {
|
func login(c *gin.Context) {
|
||||||
|
|
||||||
var loginBody LoginBody
|
var loginBody LoginBody
|
||||||
if err := c.BindJSON(&loginBody); err != nil {
|
if err := c.BindJSON(&loginBody); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
@@ -31,7 +30,6 @@ func login(c *gin.Context) {
|
|||||||
"error": "invalid username or password",
|
"error": "invalid username or password",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLogin(c *gin.Context) {
|
func updateLogin(c *gin.Context) {
|
||||||
@@ -43,3 +41,12 @@ func updateLogin(c *gin.Context) {
|
|||||||
storage.UpdateUsername(updateLogin.Username)
|
storage.UpdateUsername(updateLogin.Username)
|
||||||
storage.UpdatePassword(updateLogin.Password)
|
storage.UpdatePassword(updateLogin.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getLoginTokens(c *gin.Context) {
|
||||||
|
tokens := storage.GetLoginTokensSimple()
|
||||||
|
c.JSON(200, tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearLoginTokens(c *gin.Context) {
|
||||||
|
storage.ClearLoginTokens()
|
||||||
|
}
|
||||||
68
endpoints/tokens.go
Normal file
68
endpoints/tokens.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package endpoints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.zomo.dev/zomo/discord-retokenizer/storage"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTokens(c *gin.Context) {
|
||||||
|
tokens := storage.GetTokens()
|
||||||
|
c.JSON(200, tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getToken(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
token := storage.GetToken(id)
|
||||||
|
c.JSON(200, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenBody struct {
|
||||||
|
BotID string `json:"bot_id" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func addToken(c *gin.Context) {
|
||||||
|
var tokenBody TokenBody
|
||||||
|
if err := c.BindJSON(&tokenBody); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
token := storage.GenerateToken(tokenBody.BotID)
|
||||||
|
if token == "" {
|
||||||
|
c.JSON(400, gin.H{
|
||||||
|
"message": "bot id not found",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"token": token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteToken(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
storage.DeleteToken(id)
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"message": "success",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateToken(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
var tokenBody TokenBody
|
||||||
|
if err := c.BindJSON(&tokenBody); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
didSet := storage.UpdateToken(id, tokenBody.BotID)
|
||||||
|
if !didSet {
|
||||||
|
c.JSON(400, gin.H{
|
||||||
|
"message": "token or bot id not found",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"message": "success",
|
||||||
|
})
|
||||||
|
}
|
||||||
4
go.mod
4
go.mod
@@ -2,7 +2,10 @@ module git.zomo.dev/zomo/discord-retokenizer
|
|||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
|
require github.com/joho/godotenv v1.4.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/bwmarrin/snowflake v0.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
@@ -12,7 +15,6 @@ require (
|
|||||||
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
||||||
github.com/go-redis/redis/v9 v9.0.0-rc.2 // indirect
|
github.com/go-redis/redis/v9 v9.0.0-rc.2 // indirect
|
||||||
github.com/goccy/go-json v0.10.0 // indirect
|
github.com/goccy/go-json v0.10.0 // indirect
|
||||||
github.com/joho/godotenv v1.4.0
|
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
|
||||||
|
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
|||||||
138
storage/bots.go
138
storage/bots.go
@@ -1,27 +1,125 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
type SimpleDiscordUser struct {
|
import (
|
||||||
Username string `json:"username"`
|
"fmt"
|
||||||
Discriminator string `json:"discriminator"`
|
|
||||||
Avatar string `json:"avatar"`
|
"git.zomo.dev/zomo/discord-retokenizer/discord"
|
||||||
Banner string `json:"banner"`
|
"github.com/go-redis/redis/v9"
|
||||||
Accent_color int `json:"accent_color"`
|
)
|
||||||
Verified bool `json:"verified"`
|
|
||||||
|
func BotExists(id string) bool {
|
||||||
|
exist, err := client.SMIsMember(ctx, "bots", id).Result()
|
||||||
|
if err != nil {
|
||||||
|
if err == redis.Nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, b := range exist {
|
||||||
|
if !b {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
type BotData struct {
|
func GetBots() []discord.SimpleUser {
|
||||||
Token string
|
botIDs, err := client.SMembers(ctx, "bots").Result()
|
||||||
ID string
|
if err != nil && err != redis.Nil {
|
||||||
Loaded SimpleDiscordUser
|
panic(err)
|
||||||
|
}
|
||||||
|
bots := make([]discord.SimpleUser, 0)
|
||||||
|
for _, id := range botIDs {
|
||||||
|
bots = append(bots, GetBot(id).Simplify())
|
||||||
|
}
|
||||||
|
return bots
|
||||||
}
|
}
|
||||||
|
|
||||||
// one array of just bot ids
|
func GetBot(id string) discord.User {
|
||||||
// `bot:(id):token`
|
var bot discord.User
|
||||||
// `bot:(id):data`
|
key := fmt.Sprintf("bot:%s:data", id)
|
||||||
// SimpleDiscordUser
|
err := client.Get(ctx, key).Scan(&bot)
|
||||||
|
if err != nil && err != redis.Nil {
|
||||||
func GetBots() {
|
panic(err)
|
||||||
|
}
|
||||||
|
return bot
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExistsBot(id string) bool {
|
||||||
|
exist, err := client.SIsMember(ctx, "bots", id).Result()
|
||||||
|
if err != nil {
|
||||||
|
if err == redis.Nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return exist
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveBot(id string) {
|
||||||
|
err := client.SRem(ctx, "bots", id).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
keyPrefix := fmt.Sprintf("bot:%s:", id)
|
||||||
|
err = client.Del(ctx, keyPrefix + "token").Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = client.Del(ctx, keyPrefix + "data").Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBotToken(id string) string {
|
||||||
|
if !BotExists(id) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
key := fmt.Sprintf("bot:%s:token", id)
|
||||||
|
token, err := client.Get(ctx, key).Result()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
func RefreshBot(id string) discord.User {
|
||||||
|
token := getBotToken(id)
|
||||||
|
bot := discord.GetDiscordUser(token)
|
||||||
|
|
||||||
|
key := fmt.Sprintf("bot:%s:data", id)
|
||||||
|
err := client.Set(ctx, key, bot, 0).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return bot
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddBot(token string) bool {
|
||||||
|
user := discord.GetDiscordUser(token)
|
||||||
|
|
||||||
|
if user.ID == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.SAdd(ctx, "bots", user.ID).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := user.ID
|
||||||
|
keyPrefix := fmt.Sprintf("bot:%s:", userID)
|
||||||
|
|
||||||
|
err = client.Set(ctx, keyPrefix + "token", token, 0).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.Set(ctx, keyPrefix + "data", user, 0).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
@@ -41,8 +41,6 @@ func CheckLogin(username string, password string, ip string) (bool, string) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(user, username)
|
|
||||||
|
|
||||||
if user != username {
|
if user != username {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
@@ -56,11 +54,27 @@ func CheckLogin(username string, password string, ip string) (bool, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LoginToken struct {
|
type LoginToken struct {
|
||||||
Token string `json:"token"`
|
ID string `json:"id"`
|
||||||
|
TokenHash string `json:"token"`
|
||||||
IP string `json:"ip"`
|
IP string `json:"ip"`
|
||||||
End string `json:"end"`
|
End string `json:"end"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type LoginTokenSimple struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
End string `json:"end"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t LoginToken) Simplify() LoginTokenSimple {
|
||||||
|
return LoginTokenSimple{
|
||||||
|
ID: t.ID,
|
||||||
|
IP: t.IP,
|
||||||
|
End: t.End,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t LoginToken) MarshalBinary() ([]byte, error) {
|
func (t LoginToken) MarshalBinary() ([]byte, error) {
|
||||||
return json.Marshal(t)
|
return json.Marshal(t)
|
||||||
}
|
}
|
||||||
@@ -70,7 +84,7 @@ func (t *LoginToken) UnmarshalBinary(data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createLoginToken(ip string) string {
|
func createLoginToken(ip string) string {
|
||||||
token := util.GeneratePassword(32)
|
token := util.GenerateToken()
|
||||||
|
|
||||||
tokenHash, err := bcrypt.GenerateFromPassword([]byte(token), bcrypt.DefaultCost)
|
tokenHash, err := bcrypt.GenerateFromPassword([]byte(token), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -78,9 +92,10 @@ func createLoginToken(ip string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tokenData := LoginToken{
|
tokenData := LoginToken{
|
||||||
Token: string(tokenHash),
|
ID: util.GenerateID(),
|
||||||
|
TokenHash: string(tokenHash),
|
||||||
IP: ip,
|
IP: ip,
|
||||||
End: token[len(token) - 4:],
|
End: util.GetEnd(token),
|
||||||
}
|
}
|
||||||
|
|
||||||
member := redis.Z{
|
member := redis.Z{
|
||||||
@@ -96,8 +111,7 @@ func createLoginToken(ip string) string {
|
|||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckLoginToken(token string, ip string) bool {
|
func getLoginTokens() []LoginToken {
|
||||||
|
|
||||||
expired, err := client.ZRangeByScore(ctx, "loginTokens", &redis.ZRangeBy{
|
expired, err := client.ZRangeByScore(ctx, "loginTokens", &redis.ZRangeBy{
|
||||||
Min: "-inf",
|
Min: "-inf",
|
||||||
Max: fmt.Sprintf("%d", time.Now().Unix()),
|
Max: fmt.Sprintf("%d", time.Now().Unix()),
|
||||||
@@ -118,10 +132,28 @@ func CheckLoginToken(token string, ip string) bool {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%v\n", current)
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLoginTokensSimple() []LoginTokenSimple {
|
||||||
|
simple := make([]LoginTokenSimple, 0)
|
||||||
|
|
||||||
|
for _, c := range getLoginTokens() {
|
||||||
|
simple = append(simple, c.Simplify())
|
||||||
|
}
|
||||||
|
|
||||||
|
return simple
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearLoginTokens() {
|
||||||
|
client.Del(ctx, "loginTokens")
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckLoginToken(token string, ip string) bool {
|
||||||
|
current := getLoginTokens()
|
||||||
|
|
||||||
for _, c := range current {
|
for _, c := range current {
|
||||||
err = bcrypt.CompareHashAndPassword([]byte(c.Token), []byte(token))
|
err := bcrypt.CompareHashAndPassword([]byte(c.TokenHash), []byte(token))
|
||||||
if err == nil && ip == c.IP {
|
if err == nil && ip == c.IP {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
229
storage/tokens.go
Normal file
229
storage/tokens.go
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"git.zomo.dev/zomo/discord-retokenizer/discord"
|
||||||
|
"git.zomo.dev/zomo/discord-retokenizer/util"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenTiny struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
TokenHash string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenBot struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
BotID string `json:"bot_id"`
|
||||||
|
End string `json:"end"`
|
||||||
|
TokenHash string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimpleTokenBot struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
BotID string `json:"bot_id"`
|
||||||
|
End string `json:"end"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TokenBot) Simplify() SimpleTokenBot {
|
||||||
|
return SimpleTokenBot{
|
||||||
|
ID: t.ID,
|
||||||
|
BotID: t.BotID,
|
||||||
|
End: t.End,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TokenTiny) MarshalBinary() ([]byte, error) {
|
||||||
|
return json.Marshal(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TokenTiny) UnmarshalBinary(data []byte) error {
|
||||||
|
return json.Unmarshal(data, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TokenBot) MarshalBinary() ([]byte, error) {
|
||||||
|
return json.Marshal(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TokenBot) UnmarshalBinary(data []byte) error {
|
||||||
|
return json.Unmarshal(data, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t SimpleTokenBot) MarshalBinary() ([]byte, error) {
|
||||||
|
return json.Marshal(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SimpleTokenBot) UnmarshalBinary(data []byte) error {
|
||||||
|
return json.Unmarshal(data, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateToken(botID string) string {
|
||||||
|
if !ExistsBot(botID) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a token
|
||||||
|
token := util.GenerateToken()
|
||||||
|
tokenID := util.GenerateID()
|
||||||
|
tokenHash, err := bcrypt.GenerateFromPassword([]byte(token), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tinyToken := TokenTiny{
|
||||||
|
ID: tokenID,
|
||||||
|
TokenHash: string(tokenHash),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.SAdd(ctx, "tokens", tinyToken).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
botToken := TokenBot{
|
||||||
|
ID: tokenID,
|
||||||
|
BotID: botID,
|
||||||
|
End: util.GetEnd(token),
|
||||||
|
TokenHash: string(tokenHash),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.Set(ctx, "token:" + tokenID, botToken, 0).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTokenBot(token string) (bool, TokenBot) {
|
||||||
|
var tokens []TokenTiny
|
||||||
|
err := client.SMembers(ctx, "tokens").ScanSlice(&tokens)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, t := range tokens {
|
||||||
|
err = bcrypt.CompareHashAndPassword([]byte(t.TokenHash), []byte(token))
|
||||||
|
if err == nil {
|
||||||
|
var botToken TokenBot
|
||||||
|
err := client.Get(ctx, "token:" + t.ID).Scan(&botToken)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return true, botToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, TokenBot{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteToken(token string) {
|
||||||
|
foundToken, botToken := getTokenBot(token)
|
||||||
|
if !foundToken {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := client.SRem(ctx, "tokens", botToken.TokenHash).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = client.Del(ctx, "token:" + botToken.ID).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateToken(token string, botID string) bool {
|
||||||
|
if !ExistsBot(botID) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
foundToken, botToken := getTokenBot(token)
|
||||||
|
if !foundToken {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.Set(ctx, "token:" + botToken.ID, botToken, 0).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenUserBot struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
End string `json:"token_end"`
|
||||||
|
User discord.User `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimpleTokenUserBot struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
End string `json:"token_end"`
|
||||||
|
User discord.SimpleUser `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TokenBot) WithUser() TokenUserBot {
|
||||||
|
return TokenUserBot{
|
||||||
|
ID: t.ID,
|
||||||
|
End: t.End,
|
||||||
|
User: GetBot(t.BotID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TokenUserBot) Simplify() SimpleTokenUserBot {
|
||||||
|
return SimpleTokenUserBot{
|
||||||
|
ID: t.ID,
|
||||||
|
End: t.End,
|
||||||
|
User: t.User.Simplify(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TokenUserBot) MarshalBinary() ([]byte, error) {
|
||||||
|
return json.Marshal(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TokenUserBot) UnmarshalBinary(data []byte) error {
|
||||||
|
return json.Unmarshal(data, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t SimpleTokenUserBot) MarshalBinary() ([]byte, error) {
|
||||||
|
return json.Marshal(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SimpleTokenUserBot) UnmarshalBinary(data []byte) error {
|
||||||
|
return json.Unmarshal(data, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTokens() []SimpleTokenUserBot {
|
||||||
|
var tokens []TokenTiny
|
||||||
|
err := client.SMembers(ctx, "tokens").ScanSlice(&tokens)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleTokens := make([]SimpleTokenUserBot, 0)
|
||||||
|
for _, t := range tokens {
|
||||||
|
var tokenBot TokenBot
|
||||||
|
err := client.Get(ctx, "token:" + t.ID).Scan(&tokenBot)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
simpleTokens = append(simpleTokens, tokenBot.WithUser().Simplify())
|
||||||
|
}
|
||||||
|
return simpleTokens
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetToken(id string) TokenUserBot {
|
||||||
|
var tokenBot TokenBot
|
||||||
|
err := client.Get(ctx, "token:" + id).Scan(&tokenBot)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tokenBot.WithUser()
|
||||||
|
}
|
||||||
|
|
||||||
|
func BotTokenFromToken(token string) (bool, string) {
|
||||||
|
foundBot, tokenBot := getTokenBot(token)
|
||||||
|
if !foundBot {
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
return true, getBotToken(tokenBot.BotID)
|
||||||
|
}
|
||||||
21
util/rand.go
21
util/rand.go
@@ -4,9 +4,12 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/snowflake"
|
||||||
)
|
)
|
||||||
|
|
||||||
var passwordChars = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=_+!@#$%^&*()[]{}|;:,.<>/?")
|
// var passwordChars = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=_+!@#$%^&*()[]{}|;:,.<>/?")
|
||||||
|
var passwordChars = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=_+!@#$%^&*?")
|
||||||
func GeneratePassword(length int) string {
|
func GeneratePassword(length int) string {
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
@@ -27,3 +30,19 @@ func GenerateToken() string {
|
|||||||
}
|
}
|
||||||
return hex.EncodeToString(b)
|
return hex.EncodeToString(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var snowflakeNodeIndex int64 = 0
|
||||||
|
func GenerateID() string {
|
||||||
|
node, err := snowflake.NewNode(snowflakeNodeIndex)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return node.Generate().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEnd(token string) string {
|
||||||
|
if len(token) < 8 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return token[len(token)-8:]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user