|
|
|
@ -2,8 +2,11 @@ package auth |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"context" |
|
|
|
|
"errors" |
|
|
|
|
"fmt" |
|
|
|
|
"net/http" |
|
|
|
|
"strings" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
"git.slaventius.ru/test3k/auth/internal/config" |
|
|
|
|
db "git.slaventius.ru/test3k/auth/internal/transport/grpc" |
|
|
|
@ -11,9 +14,19 @@ import ( |
|
|
|
|
"git.slaventius.ru/test3k/umate/pkg/logger" |
|
|
|
|
|
|
|
|
|
mux "github.com/go-chi/chi/v5" |
|
|
|
|
"github.com/golang-jwt/jwt" |
|
|
|
|
"github.com/prometheus/client_golang/prometheus/promhttp" |
|
|
|
|
"google.golang.org/grpc/status" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
HeaderAuthDevice string = "X-Device" // Заголовок типа устройства
|
|
|
|
|
HeaderAuthChecksum string = "X-Checksum" // Заголовок ключа "антиспам"
|
|
|
|
|
HeaderAuthToken string = "Authorization" //
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type ContextKey string |
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
type AuthServer struct { |
|
|
|
|
db *db.AuthDBClient |
|
|
|
@ -29,20 +42,23 @@ func NewServer(ctx context.Context, config *config.Config) *AuthServer { |
|
|
|
|
alogger := logger.NewLogger("test3k:authService", config.Sentry.DSN) |
|
|
|
|
server := &AuthServer{ |
|
|
|
|
db: db.NewDBClient(ctx, config), |
|
|
|
|
prometheus: prometheus.NewService(arouter, "/metrics"), |
|
|
|
|
prometheus: prometheus.NewService(), |
|
|
|
|
Router: arouter, |
|
|
|
|
config: config, |
|
|
|
|
logger: alogger, |
|
|
|
|
ctx: ctx, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
server.Router.Get("/api/v1/healthz", healthz(server)) |
|
|
|
|
// эндпоинты требующие авторизации
|
|
|
|
|
arouter.Get("/api/v1/healthz", server.requireAuth(healthz(server))) |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
server.Router.Post("/api/v1/login", login(server)) |
|
|
|
|
server.Router.Post("/api/v1/registration", registration(server)) |
|
|
|
|
server.Router.Post("/api/v1/confirmation", confirmation(server)) |
|
|
|
|
arouter.Post("/api/v1/login", login(server)) |
|
|
|
|
arouter.Post("/api/v1/registration", registration(server)) |
|
|
|
|
arouter.Post("/api/v1/confirmation", confirmation(server)) |
|
|
|
|
|
|
|
|
|
// middleware?
|
|
|
|
|
arouter.Handle("/metrics", promhttp.Handler()) |
|
|
|
|
|
|
|
|
|
return server |
|
|
|
|
} |
|
|
|
@ -51,20 +67,77 @@ func (s *AuthServer) GracefulStop() error { |
|
|
|
|
return s.db.Close() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func healthz(s *AuthServer) http.HandlerFunc { |
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
// err := s.db.Check()
|
|
|
|
|
// if err != nil {
|
|
|
|
|
// status := status.Convert(err)
|
|
|
|
|
func (s *AuthServer) requireAuth(next http.Handler) http.HandlerFunc { |
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
ctx := r.Context() |
|
|
|
|
|
|
|
|
|
// Получим и проверим идентификатор сессии
|
|
|
|
|
authorizationHeader := r.Header.Get(HeaderAuthToken) |
|
|
|
|
authorizationHeaderAttributes := strings.Split(authorizationHeader, " ") |
|
|
|
|
if authorizationHeader == "" || len(authorizationHeaderAttributes) < 2 { |
|
|
|
|
err := errors.New("token is empty") |
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// s.logger.Error(status.Message())
|
|
|
|
|
//
|
|
|
|
|
s.logger.Error(err.Error()) |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
w.WriteHeader(http.StatusUnauthorized) |
|
|
|
|
w.Write([]byte(err.Error())) |
|
|
|
|
|
|
|
|
|
return |
|
|
|
|
} else { |
|
|
|
|
token := authorizationHeaderAttributes[1] |
|
|
|
|
tokenParsed, err := jwt.Parse(token, func(atoken *jwt.Token) (interface{}, error) { |
|
|
|
|
if _, ok := atoken.Method.(*jwt.SigningMethodHMAC); !ok { |
|
|
|
|
return nil, errors.New("there was an error in parsing") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return []byte(s.config.Auth.SecretKey), nil |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
s.logger.Error(err.Error()) |
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// w.Write([]byte(status.Message()))
|
|
|
|
|
//
|
|
|
|
|
w.WriteHeader(http.StatusUnauthorized) |
|
|
|
|
w.Write([]byte(err.Error())) |
|
|
|
|
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
now := float64(time.Now().Unix()) |
|
|
|
|
expiration := tokenParsed.Claims.(jwt.MapClaims)["exp"].(float64) |
|
|
|
|
|
|
|
|
|
if expiration < now { |
|
|
|
|
erf := errors.New("the token has expired") |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
s.logger.Error(erf.Error()) |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
w.WriteHeader(http.StatusUnauthorized) |
|
|
|
|
w.Write([]byte(erf.Error())) |
|
|
|
|
|
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
ctx = context.WithValue(ctx, ContextKey("email"), tokenParsed.Claims.(jwt.MapClaims)["email"]) |
|
|
|
|
ctx = context.WithValue(ctx, ContextKey(HeaderAuthToken), token) |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
next.ServeHTTP(w, r.WithContext(ctx)) |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func healthz(s *AuthServer) http.HandlerFunc { |
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
ctx := r.Context() |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
s.logger.Info(ctx.Value(ContextKey(HeaderAuthToken))) |
|
|
|
|
s.logger.Info(ctx.Value(ContextKey("email"))) |
|
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK) |
|
|
|
|
w.Write([]byte("ok")) |
|
|
|
|