2021-05-15 14:03:10 +00:00
|
|
|
|
package middleware
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
|
|
|
|
import (
|
2021-05-15 14:03:10 +00:00
|
|
|
|
"bitbucket.org/skeris/profile/dal"
|
|
|
|
|
"bitbucket.org/skeris/profile/errors"
|
2021-04-11 09:48:15 +00:00
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
2021-05-15 14:03:10 +00:00
|
|
|
|
errors2 "github.com/pkg/errors"
|
|
|
|
|
"github.com/themakers/hlog"
|
2021-09-05 15:24:13 +00:00
|
|
|
|
"heruvym/jwt_adapter"
|
2021-04-11 09:48:15 +00:00
|
|
|
|
"net/http"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
// My MiddleWare with gorilla/mux
|
|
|
|
|
|
|
|
|
|
type Middleware struct {
|
|
|
|
|
logger hlog.Logger
|
|
|
|
|
mongo dal.LayerMongoDb
|
|
|
|
|
allowedOrigins string
|
|
|
|
|
allowedRoles map[string]string // key - path, value - roles
|
2021-04-11 09:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
func NewMiddleware(
|
|
|
|
|
logger hlog.Logger,
|
|
|
|
|
mongo dal.LayerMongoDb,
|
|
|
|
|
allowedOrigins string,
|
|
|
|
|
allowedRoles map[string]string,
|
|
|
|
|
) *Middleware {
|
|
|
|
|
return &Middleware{
|
|
|
|
|
logger: logger,
|
|
|
|
|
mongo: mongo,
|
|
|
|
|
allowedOrigins: allowedOrigins,
|
|
|
|
|
allowedRoles: allowedRoles,
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
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)
|
|
|
|
|
})
|
|
|
|
|
}
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
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)
|
|
|
|
|
})
|
|
|
|
|
}
|
2021-05-01 10:05:45 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
func recFn(rec interface{}) (int, string) {
|
|
|
|
|
var (
|
|
|
|
|
code int
|
|
|
|
|
message string
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if err, ok := rec.(error); ok {
|
|
|
|
|
if v, ok := errors.IsForbidden(err); ok {
|
|
|
|
|
code = http.StatusForbidden
|
|
|
|
|
message = v.Error()
|
|
|
|
|
} else if v, ok := errors.IsUnauthenticated(err); ok {
|
|
|
|
|
code = http.StatusUnauthorized
|
|
|
|
|
message = v.Error()
|
2021-05-01 10:05:45 +00:00
|
|
|
|
} else {
|
2021-05-15 14:03:10 +00:00
|
|
|
|
code = http.StatusInternalServerError
|
|
|
|
|
message = err.Error()
|
2021-05-01 10:05:45 +00:00
|
|
|
|
}
|
2021-05-15 14:03:10 +00:00
|
|
|
|
} else {
|
|
|
|
|
code = http.StatusInternalServerError
|
|
|
|
|
message = fmt.Sprintf("%v", rec)
|
|
|
|
|
}
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
return code, message
|
|
|
|
|
}
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
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,
|
|
|
|
|
})
|
2021-04-11 09:48:15 +00:00
|
|
|
|
}
|
2021-05-15 14:03:10 +00:00
|
|
|
|
}()
|
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
|
})
|
|
|
|
|
}
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
func (mw *Middleware) MiddlewareJwt(next http.Handler) http.Handler {
|
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-09-05 15:24:13 +00:00
|
|
|
|
var token string
|
|
|
|
|
tokenCookie, err := r.Cookie(jwt_adapter.DefaultHeaderKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Escape GET requests
|
|
|
|
|
if r.Method == http.MethodGet {
|
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if len(r.Header[jwt_adapter.DefaultHeaderKey]) <= 0 {
|
2021-05-15 14:03:10 +00:00
|
|
|
|
mw.logger.Emit(ErrorJwtAccess{Err: errors2.New(jwt_adapter.DefaultHeaderKey + "header missing")})
|
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
|
return
|
2021-09-05 15:24:13 +00:00
|
|
|
|
}
|
|
|
|
|
token = r.Header[jwt_adapter.DefaultHeaderKey][0]
|
|
|
|
|
token = strings.Replace(token, "Bearer ", "", -1)
|
|
|
|
|
} else {
|
|
|
|
|
token = tokenCookie.Value
|
2021-04-11 09:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
adapter, err := jwt_adapter.Decode(token)
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
mw.logger.Emit(ErrorJwtAccess{Err: err})
|
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
|
return
|
2021-04-11 09:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
err = setJwtHeader(adapter, w, mw.logger)
|
|
|
|
|
if err != nil {
|
|
|
|
|
mw.logger.Emit(ErrorJwtAccess{Err: err})
|
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-09-05 15:24:13 +00:00
|
|
|
|
ctx := context.WithValue(r.Context(), jwt_adapter.DefaultHeaderKey, adapter)
|
2021-05-15 14:03:10 +00:00
|
|
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
|
|
|
})
|
2021-04-11 09:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
func getJwtUserId(r *http.Request) (string, error) {
|
2021-09-05 15:24:13 +00:00
|
|
|
|
if jwtAdapter, ok := r.Context().Value(jwt_adapter.DefaultHeaderKey).(*jwt_adapter.JwtAdapter); ok {
|
2021-05-15 14:03:10 +00:00
|
|
|
|
return jwtAdapter.User, nil
|
2021-04-11 09:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
return "", errors2.New("no token in context")
|
2021-04-11 09:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
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 {
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
// Если роли не указаны
|
|
|
|
|
if allowedRoles == "" {
|
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
|
return
|
2021-04-11 09:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
id, err := getJwtUserId(r)
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
mw.logger.Emit(ErrorRoleAccess{Err: err})
|
|
|
|
|
http.Error(w, "internal server error", http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
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
|
2021-04-11 09:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
// Если у пользователя не задана роль - блокируем доступ
|
|
|
|
|
if role == "" {
|
|
|
|
|
err = errors.UserHaveNoRole("User have no role")
|
|
|
|
|
mw.logger.Emit(ErrorRoleAccess{err})
|
|
|
|
|
http.Error(w, err.Error(), http.StatusForbidden)
|
|
|
|
|
return
|
2021-04-11 09:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
// Если указан астериск - доступ имеет любая роль
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
|
})
|
|
|
|
|
}
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
// 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)
|
2021-04-11 09:48:15 +00:00
|
|
|
|
|
2021-05-15 14:03:10 +00:00
|
|
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
|
|
|
})
|
2021-04-11 09:48:15 +00:00
|
|
|
|
}
|