package middleware import ( "context" "encoding/json" "errors" "net/http" "strconv" "strings" "go.uber.org/zap" "penahub.gitlab.yandexcloud.net/backend/penahub_common/jwt_adapter" "penahub.gitlab.yandexcloud.net/backend/templategen/amo" "penahub.gitlab.yandexcloud.net/backend/templategen/dal" "penahub.gitlab.yandexcloud.net/backend/templategen/dal/model" ) type Middleware struct { dal *dal.MongoDAL amo *amo.ClientApp logger *zap.Logger AmoAccessMap map[string]string // key - endpoint; value - access (visibility, creation, delete) } func InitMiddleware(dal *dal.MongoDAL, amo *amo.ClientApp, logger *zap.Logger, amoAccessMap map[string]string) *Middleware { return &Middleware{dal: dal, amo: amo, logger: logger, AmoAccessMap: amoAccessMap} } func (mw *Middleware) MiddlewareCors(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) } else { next.ServeHTTP(w, r) } }) } func (mw *Middleware) Logger(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.URL.String(), "/assets/") { next.ServeHTTP(w, r) return } userId := getJwtUser(r) amoData := getAmoByJwt(r) if amoData != nil { userId = amoData.UserID } mw.logger.Info("HttpRequest", zap.String("URL", r.URL.String()), zap.String("UserID", userId), ) next.ServeHTTP(w, r) }) } func (mw *Middleware) MiddlewareJwt(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.URL.String(), "/assets/") { next.ServeHTTP(w, r) return } ctx := r.Context() if amoData := getAmoByJwt(r); amoData != nil { ctx = context.WithValue(ctx, "UserID", amoData.UserID) } // Check token in cookies c := r.Header.Get(jwt_adapter.DefaultHeaderKey) jwt, err := jwt_adapter.Decode(c) if err == nil { ctx = context.WithValue(ctx, "PenaUserID", jwt.GetUserID()) } next.ServeHTTP(w, r.WithContext(ctx)) }) } func (mw *Middleware) MiddlewareHeaders(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With, X-Auth-Token") w.Header().Set("Access-Control-Allow-Credentials", "true") w.Header().Set("Access-Control-Expose-Headers", "*") w.Header().Set("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE") //w.Header().Add() next.ServeHTTP(w, r) }) } func (mw *Middleware) MiddlewareAmoJwt(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.URL.String(), "/assets/") { next.ServeHTTP(w, r) return } authMap := map[string]interface{}{ //"/amo": nil, } required := false _, ok := authMap[r.URL.String()] if ok { required = true } token, err := mw.amo.DecodeJwt(r) if err != nil { if required { mw.reportError(w, err, http.StatusUnauthorized) } else { // mw.logger.Error("ErrAmoJwtAccess", zap.Error(err)) next.ServeHTTP(w, r) } return } data, err := mw.dal.Amo.GetByAccountID(r.Context(), strconv.FormatInt(token.AccountId, 10)) if err != nil { mw.reportError(w, err, http.StatusInternalServerError) return } if data == nil { mw.reportError(w, errors.New("amo account not found"), http.StatusUnauthorized) return } // check access rules isAcessGranted := token.IsAdmin // Если права не указаны для эндпоинта, то доступно любому юзеру rule, ok := mw.AmoAccessMap[r.URL.String()] if ok { // Если не админ, то проверяем права пользователя if rule == "visibility" && !isAcessGranted { for _, userId := range data.AccessRules.Visibility { if userId == token.UserId { isAcessGranted = true break } } } if rule == "creation" && !isAcessGranted { for _, userId := range data.AccessRules.Creation { if userId == token.UserId { isAcessGranted = true break } } } if rule == "delete" && !isAcessGranted { for _, userId := range data.AccessRules.Delete { if userId == token.UserId { isAcessGranted = true break } } } } else { isAcessGranted = true } // Если прав нет, прерываемся if !isAcessGranted { mw.reportError(w, errors.New("amoUser forbidden"), http.StatusForbidden) // mb: http.StatusForbidden return } ctx := context.WithValue(r.Context(), "amoData", data) ctx = context.WithValue(ctx, "amoIsAdmin", token.IsAdmin) ctx = context.WithValue(ctx, "amoUserID", token.UserId) next.ServeHTTP(w, r.WithContext(ctx)) }) } func (mw *Middleware) MiddlewareAmoPlug(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.URL.String(), "/assets/") { next.ServeHTTP(w, r) return } ctx := r.Context() if r.Header.Get("TestAuth") == "75bc853ae763ffc36cad97069682fed1" { data, err := mw.dal.Amo.GetByAccountID(r.Context(), "30228997") if err != nil { mw.reportError(w, err, http.StatusInternalServerError) return } if data == nil { mw.reportError(w, errors.New("amo account not found"), http.StatusUnauthorized) return } ctx = context.WithValue(ctx, "amoData", data) } next.ServeHTTP(w, r.WithContext(ctx)) }) } func getJwtUser(r *http.Request) string { user, ok := r.Context().Value("UserID").(string) if ok { return user } return "" } func getAmoByJwt(r *http.Request) *model.Amo { amoData, ok := r.Context().Value("amoData").(*model.Amo) if ok { return amoData } return nil } func (mw *Middleware) reportError(w http.ResponseWriter, err error, status int) error { mw.logger.Error("ErrorMiddleware", zap.Error(err)) w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(status) return json.NewEncoder(w).Encode(err) }