package middleware import ( "context" "fmt" "heruvym/jwt_adapter" "net/http" "strings" "time" errors2 "github.com/pkg/errors" "github.com/rs/xid" "gitea.pena/PenaSide/hlog" ) // My MiddleWare with gorilla/mux type Middleware struct { logger hlog.Logger // mongo dal.LayerMongoDb allowedOrigins string allowedRoles map[string]string // key - path, value - roles } const sessionKey = "Sess" const HostKey = "host" func NewMiddleware( logger hlog.Logger, //mongo dal.LayerMongoDb, allowedOrigins string, allowedRoles map[string]string, ) *Middleware { return &Middleware{ logger: logger, allowedOrigins: allowedOrigins, allowedRoles: allowedRoles, } } func (mw *Middleware) MiddlewareLogger(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mw.logger.Emit(DebugHttpRequest{Url: r.URL.String()}) next.ServeHTTP(w, r) }) } func (mw *Middleware) MiddlewareOriginAccess(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if len(r.Header["Origin"]) > 0 { if mw.allowedOrigins != "*" && !strings.Contains(mw.allowedOrigins, r.Header["Origin"][0]) { mw.logger.Emit(ErrorOriginAccess{Origin: r.Header["Origin"][0], Url: r.URL.String()}) return } } next.ServeHTTP(w, r) }) } func recFn(rec interface{}) (int, string) { var ( code int message string ) if err, ok := rec.(error); ok { code = http.StatusInternalServerError message = err.Error() } else { code = http.StatusInternalServerError message = fmt.Sprintf("%v", rec) } return code, message } func (mw *Middleware) MiddlewareRecovery(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if rec := recover(); rec != nil { code, message := recFn(rec) w.WriteHeader(code) if _, err := fmt.Fprint(w, message); err != nil { mw.logger.Emit(ErrorWritingPanicResponse{Err: err}) } mw.logger.Emit(ErrorPanicInHttpHandler{ Code: code, Message: message, Recovered: rec, }) } }() next.ServeHTTP(w, r) }) } func (mw *Middleware) MiddlewareJwt(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var ( token, role string adapter *jwt_adapter.JwtAdapter ) switch r.Header["Referer"][0] { case "sadmin.pena": role = "admin" case "admin.pena": role = "admin" default: role = "user" } ctx := context.WithValue(r.Context(), jwt_adapter.RoleKey, role) tokenCookie, err := r.Cookie(jwt_adapter.DefaultHeaderKey) fmt.Println("MW1", err) if err != nil { // Escape GET requests if r.Method == http.MethodGet { next.ServeHTTP(w, r.WithContext(ctx)) return } fmt.Println("MW2", jwt_adapter.DefaultHeaderKey, r.Header[jwt_adapter.DefaultHeaderKey]) if len(r.Header[jwt_adapter.DefaultHeaderKey]) <= 0 || !func(hdrs []string) bool { if len(hdrs) == 0 {return false} fmt.Println("SS", hdrs[0]) if hdrs[0] == "Bearer" || hdrs[0] == "Bearer " { return false } return true }(r.Header[jwt_adapter.DefaultHeaderKey]) { fmt.Println("MW3", r.Header[sessionKey], sessionKey, r.Header) if len(r.Header[sessionKey]) == 0 { if sessCookie, err := r.Cookie(sessionKey); err != nil { id := xid.New().String() adapter = &jwt_adapter.JwtAdapter{Id: id} http.SetCookie(w, &http.Cookie{ Name: sessionKey, Value: id, Expires: time.Now().Add(time.Hour * 24 * 30), SameSite: http.SameSiteNoneMode, Secure: true, }) fmt.Println("SSS", sessCookie, err) } else { fmt.Println("SSS1", sessCookie.Value, err) adapter = &jwt_adapter.JwtAdapter{Id: sessCookie.Value} } } else { adapter = &jwt_adapter.JwtAdapter{Id: r.Header[sessionKey][0]} } } else { token = r.Header[jwt_adapter.DefaultHeaderKey][0] token = strings.Replace(token, "Bearer ", "", -1) } } else { token = tokenCookie.Value } if adapter == nil { adapter, err = jwt_adapter.Decode(token) if err != nil { mw.logger.Emit(ErrorJwtAccess{Err: err}) w.WriteHeader(http.StatusUnauthorized) return } } err = setJwtHeader(adapter, w, mw.logger) if err != nil { mw.logger.Emit(ErrorJwtAccess{Err: err}) w.WriteHeader(http.StatusUnauthorized) return } ctx = context.WithValue(ctx, jwt_adapter.DefaultHeaderKey, adapter) next.ServeHTTP(w, r.WithContext(ctx)) }) } func getJwtUserId(r *http.Request) (string, error) { if jwtAdapter, ok := r.Context().Value(jwt_adapter.DefaultHeaderKey).(*jwt_adapter.JwtAdapter); ok { return jwtAdapter.Id, nil } return "", errors2.New("no token in context") } func (mw *Middleware) MiddlewareRoleAccess(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Если доступ по роли задан if allowedRoles, ok := mw.allowedRoles[r.URL.Path]; ok { // Если роли не указаны if allowedRoles == "" { next.ServeHTTP(w, r) return } /* id, err := getJwtUserId(r) if err != nil { mw.logger.Emit(ErrorRoleAccess{Err: err}) http.Error(w, "internal server error", http.StatusInternalServerError) return }*/ /* role, err := mw.mongo.GetProfileRole(r.Context(), id) if err != nil { mw.logger.Emit(ErrorRoleAccess{Err: err}) http.Error(w, "internal server error", http.StatusInternalServerError) return } */ // Если у пользователя не задана роль - блокируем доступ /* if role == "" { err = errors.UserHaveNoRole("User have no role") mw.logger.Emit(ErrorRoleAccess{err}) http.Error(w, err.Error(), http.StatusForbidden) return } // Если указан астериск - доступ имеет любая роль if !(allowedRoles == "*" || strings.Contains(allowedRoles, role)) { err = errors.UserHaveNoRole("User role not allowed") mw.logger.Emit(ErrorRoleAccess{err}) http.Error(w, err.Error(), http.StatusForbidden) return }*/ } next.ServeHTTP(w, r) }) } // MiddlewareJwtPlug jwt заглушка для отладки кода, удалить в релизе /* func (mw *Middleware) MiddlewareJwtPlug(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { adapter := jwt_adapter.JwtAdapter{ID: "604b79aced1d431b9e911f56"} adapter.Init() adapter.SetUserID("604b79aced1d431b9e911f56") ctx := context.WithValue(r.Context(), "JWT", &adapter) next.ServeHTTP(w, r.WithContext(ctx)) }) } */ func (mw *Middleware) ExtractHostMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { host := r.Header["Referer"][0] ctx := context.WithValue(r.Context(), HostKey, host) next.ServeHTTP(w, r.WithContext(ctx)) }) }