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}) }