func RestAuthentication()

in backend/server/api/middlewares.go [98:202]


func RestAuthentication(router *gin.Engine, basicRes context.BasicRes) gin.HandlerFunc {
	type ApiBody struct {
		Success bool   `json:"success"`
		Message string `json:"message"`
	}
	db := basicRes.GetDal()
	logger := basicRes.GetLogger()
	if db == nil {
		panic(fmt.Errorf("db is not initialised"))
	}
	apiKeyHelper := apikeyhelper.NewApiKeyHelper(basicRes, logger)
	return func(c *gin.Context) {
		path := c.Request.URL.Path
		// Only open api needs to check api key
		if !strings.HasPrefix(path, "/rest") {
			logger.Info("path %s will continue", path)
			c.Next()
			return
		}
		path = strings.TrimPrefix(path, "/rest")

		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			c.Abort()
			c.JSON(http.StatusUnauthorized, &ApiBody{
				Success: false,
				Message: "token is missing",
			})
			return
		}
		apiKeyStr := strings.TrimPrefix(authHeader, "Bearer ")
		if apiKeyStr == authHeader || apiKeyStr == "" {
			c.Abort()
			c.JSON(http.StatusUnauthorized, &ApiBody{
				Success: false,
				Message: "token is not present or malformed",
			})
			return
		}

		hashedApiKey, err := apiKeyHelper.DigestToken(apiKeyStr)
		if err != nil {
			logger.Error(err, "DigestToken")
			c.Abort()
			c.JSON(http.StatusInternalServerError, &ApiBody{
				Success: false,
				Message: err.Error(),
			})
			return
		}

		apiKey, err := apiKeyHelper.GetApiKey(nil, dal.Where("api_key = ?", hashedApiKey))
		if err != nil {
			c.Abort()
			if db.IsErrorNotFound(err) {
				c.JSON(http.StatusForbidden, &ApiBody{
					Success: false,
					Message: "api key is invalid",
				})
			} else {
				logger.Error(err, "query api key from db")
				c.JSON(http.StatusInternalServerError, &ApiBody{
					Success: false,
					Message: err.Error(),
				})
			}
			return
		}

		if apiKey.ExpiredAt != nil && time.Until(*apiKey.ExpiredAt) < 0 {
			c.Abort()
			c.JSON(http.StatusForbidden, &ApiBody{
				Success: false,
				Message: "api key has expired",
			})
			return
		}
		matched, matchErr := regexp.MatchString(apiKey.AllowedPath, path)
		if matchErr != nil {
			logger.Error(err, "regexp match path error")
			c.Abort()
			c.JSON(http.StatusInternalServerError, &ApiBody{
				Success: false,
				Message: matchErr.Error(),
			})
			return
		}
		if !matched {
			c.JSON(http.StatusForbidden, &ApiBody{
				Success: false,
				Message: "path doesn't match api key's scope",
			})
			return
		}

		logger.Info("redirect path: %s to: %s", c.Request.URL.Path, path)
		c.Request.URL.Path = path
		c.Set(common.USER, &common.User{
			Name:  apiKey.Creator.Creator,
			Email: apiKey.Creator.CreatorEmail,
		})
		router.HandleContext(c)
		c.Abort()
	}
}