219 lines
5.8 KiB
Go
219 lines
5.8 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/sha1"
|
|
"embed"
|
|
"encoding/base64"
|
|
"errors"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/golang-jwt/jwt/v4"
|
|
log "github.com/sirupsen/logrus"
|
|
"io/fs"
|
|
m "k8s-manager/src"
|
|
"k8s-manager/src/controller"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
//go:embed template/* static/*
|
|
var f embed.FS
|
|
|
|
func Cors() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
method := c.Request.Method
|
|
origin := c.Request.Header.Get("Origin")
|
|
if origin != "" {
|
|
c.Header("Access-Control-Allow-Origin", "*")
|
|
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
|
|
c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
|
|
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
|
|
c.Header("Access-Control-Allow-Credentials", "true")
|
|
}
|
|
if method == "OPTIONS" {
|
|
c.AbortWithStatus(http.StatusNoContent)
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
m.InitConfig()
|
|
controller.InitClient()
|
|
|
|
r := gin.Default()
|
|
// r.Use(cors.Default())
|
|
r.Use(Cors())
|
|
r.Use(m.JWTAuthMiddleware)
|
|
|
|
fp, _ := fs.Sub(f, "static")
|
|
r.StaticFS("/static", http.FS(fp))
|
|
|
|
//r.GET("/index", func(c *gin.Context) {
|
|
// c.FileFromFS("/template/app_view.html", http.FS(f))
|
|
//})
|
|
r.GET("/apps", controller.GetAppsController)
|
|
r.POST("/app", controller.CreateUpdateDeleteApp)
|
|
r.PUT("/app", controller.CreateUpdateDeleteApp)
|
|
r.DELETE("/app", controller.CreateUpdateDeleteApp)
|
|
|
|
r.POST("/dep", controller.CreateDeleteDeployment)
|
|
r.DELETE("/dep", controller.CreateDeleteDeployment)
|
|
|
|
r.GET("/pod/:id", controller.GetPodStatus)
|
|
|
|
r.GET("/login", LoginToken)
|
|
r.Any("/test_token", TestToken)
|
|
r.GET("/username", controller.GetUsername)
|
|
|
|
r.GET("/files/:app", controller.GetFiles)
|
|
r.GET("/file/:app/:filename", controller.GetPostOrDeleteFile)
|
|
r.POST("/file/:app/:filename", controller.GetPostOrDeleteFile)
|
|
r.DELETE("/file/:app/:filename", controller.GetPostOrDeleteFile)
|
|
|
|
r.POST("/fork", controller.ForkApp)
|
|
|
|
r.POST("/image", controller.AddImage)
|
|
r.PUT("/image", controller.UpdateImage)
|
|
r.DELETE("/image", controller.DeleteImage)
|
|
r.GET("/images", controller.GetImages)
|
|
|
|
err := r.Run(":8000")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
type CGJWTClaim struct {
|
|
jwt.RegisteredClaims
|
|
Userid string
|
|
}
|
|
|
|
func ParseCGToken(token string) (string, error) {
|
|
token = strings.ReplaceAll(token, "-", "+")
|
|
token = strings.ReplaceAll(token, "_", "/")
|
|
token = token + []string{"", "===", "==", "="}[len(token)%4]
|
|
aesEncrypt, err := base64.StdEncoding.DecodeString(token)
|
|
if err != nil {
|
|
log.WithError(err).Error("Parse CG Token 1")
|
|
return "", err
|
|
}
|
|
|
|
jwtString := AesDecryptECB(aesEncrypt, []byte(m.GetConfig().AseKey))
|
|
|
|
jwtResult, err := jwt.ParseWithClaims(string(jwtString), &CGJWTClaim{}, func(token *jwt.Token) (i interface{}, err error) {
|
|
return []byte(m.GetConfig().JwtKey), nil
|
|
// return hmac.New(sha256.New, []byte(jwtKey)), nil
|
|
})
|
|
if err != nil {
|
|
log.WithError(err).Error("Parse CG Token 2")
|
|
return "", err
|
|
}
|
|
if claims, ok := jwtResult.Claims.(*CGJWTClaim); ok && jwtResult.Valid { // 校验token
|
|
return claims.Userid, nil
|
|
}
|
|
return "", errors.New("jwt parse error")
|
|
}
|
|
|
|
//func generateKey(key []byte) (genKey []byte) {
|
|
// genKey = make([]byte, 16)
|
|
// copy(genKey, key)
|
|
// for i := 16; i < len(key); {
|
|
// for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
|
|
// genKey[j] ^= key[i]
|
|
// }
|
|
// }
|
|
// return genKey
|
|
//}
|
|
|
|
func AesDecryptECB(encrypted []byte, key []byte) (decrypted []byte) {
|
|
sh := sha1.New()
|
|
sh.Write(key)
|
|
realKey := sh.Sum(nil)
|
|
c, err := aes.NewCipher(realKey[:16])
|
|
if err != nil {
|
|
log.WithError(err).Error(AesDecryptECB)
|
|
}
|
|
decrypted = make([]byte, len(encrypted))
|
|
//
|
|
for bs, be := 0, c.BlockSize(); bs < len(encrypted); bs, be = bs+c.BlockSize(), be+c.BlockSize() {
|
|
c.Decrypt(decrypted[bs:be], encrypted[bs:be])
|
|
}
|
|
|
|
trim := 0
|
|
if len(decrypted) > 0 {
|
|
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
|
|
}
|
|
|
|
return decrypted[:trim]
|
|
}
|
|
|
|
//func AesDecrypt(data []byte, key []byte) ([]byte, error) {
|
|
// sh := sha1.New()
|
|
// sh.Write(key)
|
|
// realKey := sh.Sum(nil)
|
|
//
|
|
// fmt.Println(string(realKey))
|
|
// block, err := aes.NewCipher(realKey[:16])
|
|
// if err != nil {
|
|
// log.WithError(err).Error("AesDecrypt 1")
|
|
// return nil, err
|
|
// }
|
|
// blockSize := block.BlockSize()
|
|
// blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
|
|
// crypted := make([]byte, len(data))
|
|
// blockMode.CryptBlocks(crypted, data)
|
|
// if err != nil {
|
|
// log.WithError(err).Error("AesDecrypt 2")
|
|
// return nil, err
|
|
// }
|
|
// return crypted, nil
|
|
//}
|
|
|
|
func LoginToken(c *gin.Context) {
|
|
//username := c.Request.URL.Query().Get("username")
|
|
username, err := ParseCGToken(c.Request.URL.Query().Get("cgtoken"))
|
|
if username == "" || err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"code": http.StatusNotFound,
|
|
"msg": "user name error",
|
|
})
|
|
} else {
|
|
Login(c, username)
|
|
}
|
|
}
|
|
|
|
func Login(c *gin.Context, username string) {
|
|
token, err := m.GenerateToken(username)
|
|
if err != nil {
|
|
log.Error(err)
|
|
c.Abort()
|
|
return
|
|
}
|
|
log.WithField("user name", username).Info("Login success.")
|
|
c.SetCookie("Authorization", "Bearer "+token, 0, "", "", false, false)
|
|
c.Header("Authorization", "Bearer "+token)
|
|
c.Header("Cache-Control", "no-store")
|
|
//redirectUrl := ""
|
|
//if os.Getenv("URL_PREFIX") == "" {
|
|
// redirectUrl = "/index"
|
|
//} else {
|
|
// redirectUrl = fmt.Sprintf("/%s/index", os.Getenv("URL_PREFIX"))
|
|
//}
|
|
c.Redirect(http.StatusTemporaryRedirect, "/admin-page/")
|
|
}
|
|
|
|
func TestToken(c *gin.Context) {
|
|
token := c.Request.URL.Query().Get("cgtoken")
|
|
if token == "" {
|
|
c.JSON(http.StatusForbidden, gin.H{"msg": "No token"})
|
|
return
|
|
}
|
|
username, err := ParseCGToken(token)
|
|
if err != nil {
|
|
c.JSON(http.StatusForbidden, gin.H{"msg": "token error"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"msg": "ok", "username": username})
|
|
}
|