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" prometheus "git.slaventius.ru/test3k/auth/internal/transport/prometheus" "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 prometheus *prometheus.Prometheus Router *mux.Mux config *config.Config logger *logger.Logger ctx context.Context } func NewServer(ctx context.Context, config *config.Config) *AuthServer { arouter := mux.NewMux() alogger := logger.NewLogger("test3k:authService", config.Sentry.DSN) server := &AuthServer{ db: db.NewDBClient(ctx, config), prometheus: prometheus.NewService(), Router: arouter, config: config, logger: alogger, ctx: ctx, } // эндпоинты требующие авторизации оборачиваются в server.requireAuth // arouter.Get("/api/v1/healthz", server.requireAuth(healthz(server))) // arouter.Get("/api/v1/healthz", healthz(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 } func (s *AuthServer) GracefulStop() error { return s.db.Close() } // Парсинг токена func (s *AuthServer) parseToken(token string) (*jwt.Token, error) { return 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 }) } // Проверка наличия токена func (s *AuthServer) verifyValueToken(tokenHeader string) (string, error) { authorizationHeaderAttributes := strings.Split(tokenHeader, " ") if tokenHeader == "" || len(authorizationHeaderAttributes) < 2 { return "", errors.New("token is empty") } return authorizationHeaderAttributes[1], nil } // Проверка срока жизни токена func (s *AuthServer) verifyExpirationToken(token *jwt.Token) error { now := float64(time.Now().Unix()) expiration := token.Claims.(jwt.MapClaims)["exp"].(float64) if expiration < now { return errors.New("the token has expired") } return nil } func (s *AuthServer) requireAuth(next http.Handler) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // ctx := r.Context() // token, err := s.verifyValueToken(r.Header.Get(HeaderAuthToken)) if err != nil { s.logger.Error(err.Error()) // w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(err.Error())) return } // tokenParsed, err := s.parseToken(token) if err != nil { s.logger.Error(err.Error()) // w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(err.Error())) return } // erf := s.verifyExpirationToken(tokenParsed) if erf != nil { s.logger.Error(erf.Error()) // w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(erf.Error())) return } // // next.ServeHTTP(w, r.WithContext(ctx)) next.ServeHTTP(w, r) }) } 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")) } } func login(s *AuthServer) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { uid := r.FormValue("uid") password := r.FormValue("password") err := s.db.Login(uid, password) if err != nil { status := status.Convert(err) // s.logger.Error(status.Message()) // w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(status.Message())) return } w.WriteHeader(http.StatusOK) w.Write([]byte(fmt.Sprintf("Success login for %s", uid))) } } func registration(s *AuthServer) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { uid := r.FormValue("uid") email := r.FormValue("email") password := r.FormValue("password") _, err := s.db.Registration(uid, email, password) if err != nil { status := status.Convert(err) // s.logger.Error(status.Message()) // w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(status.Message())) return } // Увеличим счетчик заругистрировашихся пользователей s.prometheus.IncUsersRegistered() // w.WriteHeader(http.StatusOK) w.Write([]byte(fmt.Sprintf("Success registration for %s", uid))) } } func confirmation(s *AuthServer) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { code := r.FormValue("code") err := s.db.Confirmation(code) if err != nil { status := status.Convert(err) // s.logger.Error(status.Message()) // w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(status.Message())) return } // Увеличим счетчик пользователей подтвердивших регистрацию s.prometheus.IncUsersConfirmed() // w.WriteHeader(http.StatusOK) w.Write([]byte(fmt.Sprintf("Success confirmation for %s", code))) } }