// Copyright (c) [2022] [巴拉迪维 BaratSemet] // [ohUrlShortener] is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. package controller import ( "fmt" "log" "net/http" "strconv" "strings" "ohurlshortener/core" "ohurlshortener/service" "ohurlshortener/storage" "ohurlshortener/utils" "github.com/gin-gonic/gin" ) const ( authorizationHeaderKey = "Authorization" authorizationTypeBearer = "Bearer" ) // APIAuthHandler Authorization for /api func APIAuthHandler() gin.HandlerFunc { return func(ctx *gin.Context) { authHeader := ctx.GetHeader(authorizationHeaderKey) if utils.EmptyString(authHeader) { ctx.AbortWithStatusJSON(http.StatusUnauthorized, core.ResultJsonUnauthorized("Authorization Header is empty")) return } fields := strings.Fields(authHeader) if len(fields) < 2 { ctx.AbortWithStatusJSON(http.StatusUnauthorized, core.ResultJsonUnauthorized("Invalid Authorization Header")) return } if fields[0] != authorizationTypeBearer { ctx.AbortWithStatusJSON(http.StatusUnauthorized, core.ResultJsonUnauthorized("Unsupported Authorization Type")) return } token := fields[1] res, err := validateToken(token) if err != nil { ctx.AbortWithStatusJSON(http.StatusInternalServerError, core.ResultJsonError("Internal error")) return } if !res { ctx.AbortWithStatusJSON(http.StatusUnauthorized, core.ResultJsonUnauthorized("Authorization failed")) return } ctx.Next() } } // AdminCookieValue Generate cookie value for admin user func AdminCookieValue(user core.User) (string, error) { var result string data, err := utils.Sha256Of(user.Account + "a=" + user.Password + "=e" + strconv.Itoa(user.ID)) if err != nil { log.Println(err) return result, err } return utils.Base58Encode(data), nil } // AdminAuthHandler Authorization for /admin func AdminAuthHandler() gin.HandlerFunc { return func(c *gin.Context) { user, err := c.Cookie("ohUrlShortenerAdmin") if err != nil { c.Redirect(http.StatusFound, "/login") c.Next() } cookie, err := c.Cookie("ohUrlShortenerCookie") if err != nil { c.Redirect(http.StatusFound, "/login") c.Next() return } if len(user) <= 0 || len(cookie) <= 0 { c.Redirect(http.StatusFound, "/login") c.Next() return } found, err := service.GetUserByAccountFromRedis(user) if err != nil { c.Redirect(http.StatusFound, "/login") c.Next() return } if found.IsEmpty() { c.Redirect(http.StatusFound, "/login") c.Next() return } cValue, err := AdminCookieValue(found) if err != nil { c.Redirect(http.StatusFound, "/login") c.Next() return } if !strings.EqualFold(cValue, cookie) { c.Redirect(http.StatusFound, "/login") c.Next() return } c.Set("current_account", found.Account) c.Next() } // end of func } // WebLogFormatHandler Customized log format for web func WebLogFormatHandler(server string) gin.HandlerFunc { return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { if !strings.HasPrefix(param.Path, "/assets") { return fmt.Sprintf("[%s | %s] %s %s %d %s \t%s %s %s \n", server, param.TimeStamp.Format("2006/01/02 15:04:05"), param.Method, param.Path, param.StatusCode, param.Latency, param.ClientIP, param.Request.UserAgent(), param.ErrorMessage, ) } // end of if return "" }) // end of formatter } // end of func func validateToken(token string) (bool, error) { users, err := storage.FindAllUsers() if err != nil { return false, err } for _, u := range users { if u.Password == token { return true, nil } } return false, nil }