2022-07-28 15:00:43 +00:00
|
|
|
package middleware
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-08-10 13:53:34 +00:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2022-09-15 13:53:55 +00:00
|
|
|
"github.com/Pena-Co-Ltd/amocrm_templategen_back/amo"
|
|
|
|
"github.com/Pena-Co-Ltd/amocrm_templategen_back/dal"
|
2022-10-11 10:07:29 +00:00
|
|
|
"github.com/Pena-Co-Ltd/amocrm_templategen_back/dal/model"
|
2022-07-28 15:00:43 +00:00
|
|
|
"github.com/dgrijalva/jwt-go"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"net/http"
|
2022-08-10 13:53:34 +00:00
|
|
|
"strconv"
|
2022-07-28 15:00:43 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
JwtSecret = []byte("my-secret-123")
|
|
|
|
)
|
|
|
|
|
|
|
|
type Middleware struct {
|
2022-08-22 04:23:47 +00:00
|
|
|
dal *dal.MongoDAL
|
|
|
|
amo *amo.ClientApp
|
|
|
|
logger *zap.Logger
|
|
|
|
AmoAccessMap map[string]string // key - endpoint; value - access (visibility, creation, delete)
|
2022-07-28 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type UserClaims struct {
|
2022-08-10 13:53:34 +00:00
|
|
|
UserID string
|
|
|
|
FullName string
|
|
|
|
Email string
|
|
|
|
IsActivated bool
|
2022-07-28 15:00:43 +00:00
|
|
|
jwt.StandardClaims
|
|
|
|
}
|
|
|
|
|
2022-08-22 04:23:47 +00:00
|
|
|
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}
|
2022-08-10 13:53:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
})
|
2022-07-28 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 := ""
|
|
|
|
user := getJwtUser(r)
|
|
|
|
if user != nil {
|
|
|
|
userId = user.UserID
|
|
|
|
}
|
2022-10-11 10:07:29 +00:00
|
|
|
|
|
|
|
amoData := getAmoByJwt(r)
|
|
|
|
if amoData != nil {
|
|
|
|
userId = amoData.UserID
|
|
|
|
}
|
|
|
|
|
2022-07-28 15:00:43 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
authMap := map[string]interface{}{
|
|
|
|
//"/amo": nil,
|
|
|
|
}
|
|
|
|
|
|
|
|
required := false
|
|
|
|
_, ok := authMap[r.URL.String()]
|
|
|
|
if ok {
|
|
|
|
required = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check token in headers
|
|
|
|
tokenHeader := r.Header.Get("Authorization")
|
|
|
|
if tokenHeader == "" && required {
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
tokenHeader = strings.Replace(tokenHeader, "Bearer ", "", -1)
|
|
|
|
|
|
|
|
if tokenHeader != "" {
|
|
|
|
token, err := jwt.ParseWithClaims(tokenHeader, &UserClaims{}, func(tk *jwt.Token) (interface{}, error) {
|
|
|
|
return JwtSecret, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
mw.logger.Error("ErrJwtAccess", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
if claims, ok := token.Claims.(*UserClaims); ok && token.Valid {
|
|
|
|
ctx := context.WithValue(r.Context(), "JWT", claims)
|
|
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check token in cookies
|
|
|
|
c, err := r.Cookie("Authorization")
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if !required {
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
} else {
|
2022-08-10 13:53:34 +00:00
|
|
|
mw.logger.Error("ErrJwtAccess", zap.Error(err))
|
2022-07-28 15:00:43 +00:00
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
tokenCookie := strings.Replace(c.Value, "Bearer ", "", -1)
|
|
|
|
token, err := jwt.ParseWithClaims(tokenCookie, &UserClaims{}, func(tk *jwt.Token) (interface{}, error) {
|
|
|
|
return JwtSecret, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
mw.logger.Error("ErrJwtAccess", zap.Error(err))
|
|
|
|
if !required {
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
} else {
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
claims, ok := token.Claims.(*UserClaims)
|
|
|
|
if !ok || !token.Valid {
|
|
|
|
mw.logger.Error("ErrJwtAccess", zap.Error(err))
|
|
|
|
if !required {
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
} else {
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := context.WithValue(r.Context(), "JWT", claims)
|
|
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-10 13:53:34 +00:00
|
|
|
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) {
|
2022-10-11 10:07:29 +00:00
|
|
|
if strings.Contains(r.URL.String(), "/assets/") {
|
2022-08-10 13:53:34 +00:00
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-10-11 10:07:29 +00:00
|
|
|
authMap := map[string]interface{}{
|
|
|
|
//"/amo": nil,
|
|
|
|
}
|
|
|
|
|
|
|
|
required := false
|
|
|
|
_, ok := authMap[r.URL.String()]
|
|
|
|
if ok {
|
|
|
|
required = true
|
|
|
|
}
|
|
|
|
|
2022-08-10 13:53:34 +00:00
|
|
|
token, err := mw.amo.DecodeJwt(r)
|
|
|
|
if err != nil {
|
2022-10-11 10:07:29 +00:00
|
|
|
if required {
|
|
|
|
mw.reportError(w, err, http.StatusUnauthorized)
|
|
|
|
} else {
|
|
|
|
mw.logger.Error("ErrAmoJwtAccess", zap.Error(err))
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
}
|
2022-08-10 13:53:34 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-08-22 04:23:47 +00:00
|
|
|
// 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.StatusUnauthorized) // 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) {
|
2022-10-11 10:07:29 +00:00
|
|
|
data, err := mw.dal.Amo.GetByID(r.Context(), "63226edec2259da3375fe2a4")
|
2022-08-22 04:23:47 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-08-10 13:53:34 +00:00
|
|
|
ctx := context.WithValue(r.Context(), "amoData", data)
|
|
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-07-28 15:00:43 +00:00
|
|
|
func getJwtUser(r *http.Request) *UserClaims {
|
|
|
|
user, ok := r.Context().Value("JWT").(*UserClaims)
|
|
|
|
if ok {
|
|
|
|
return user
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2022-10-11 10:07:29 +00:00
|
|
|
func getAmoByJwt(r *http.Request) *model.Amo {
|
|
|
|
amoData, ok := r.Context().Value("amoData").(*model.Amo)
|
|
|
|
if ok {
|
|
|
|
return amoData
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2022-08-10 13:53:34 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|