diff --git a/.vscode/launch.json b/.vscode/launch.json index 039a2ac..b87b93c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,6 +15,7 @@ "DB_PORT": "9995", "APP_PORT": "9994", // "SENTRY_DSN": "https://3f4b31dbbd9a4a6b8a71f9881d962f25@o4504654569799680.ingest.sentry.io/4504654572683264" + "SECRET_KEY":"T7vnDGlNwjc7pxY5agvksbj0adpKYfnbsbvqsnozA6YZWzohhJO1Dl95HTgMt1cN", }, "args": [] } diff --git a/go.mod b/go.mod index 86cec85..9864d01 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( git.slaventius.ru/test3k/umate v0.0.0-20230228085343-523fb50446c9 github.com/go-chi/chi/v5 v5.0.8 + github.com/golang-jwt/jwt v3.2.2+incompatible github.com/kelseyhightower/envconfig v1.4.0 github.com/prometheus/client_golang v1.14.0 google.golang.org/grpc v1.52.3 diff --git a/go.sum b/go.sum index 7c4403d..77c80e4 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -git.slaventius.ru/test3k/umate v0.0.0-20230224071336-c256dd02b0f7 h1:Eo+tW2vVUvhz3mMR04QCzxTx51M0rQfQKV4M6Y/3dBM= -git.slaventius.ru/test3k/umate v0.0.0-20230224071336-c256dd02b0f7/go.mod h1:xE7ik2EnLB+CNDJA5+HbRIwAk+V7sgUjS0HPeXS5Ka0= git.slaventius.ru/test3k/umate v0.0.0-20230228085343-523fb50446c9 h1:OJryx+ovm7tjWGxq/E/u6jX9HSDr2PCBuEuCkVz+WWk= git.slaventius.ru/test3k/umate v0.0.0-20230228085343-523fb50446c9/go.mod h1:xE7ik2EnLB+CNDJA5+HbRIwAk+V7sgUjS0HPeXS5Ka0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -83,6 +81,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= diff --git a/internal/auth.go b/internal/auth.go index 38d2a75..bd2d70c 100644 --- a/internal/auth.go +++ b/internal/auth.go @@ -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")) diff --git a/internal/config/config.go b/internal/config/config.go index 1313c5e..34c4763 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -15,6 +15,10 @@ type appConfig struct { Port int `envconfig:"APP_PORT"` } +type authConfig struct { + SecretKey string `envconfig:"SECRET_KEY"` +} + type sentryConfig struct { DSN string `envconfig:"SENTRY_DSN"` } @@ -23,6 +27,7 @@ type sentryConfig struct { type Config struct { Db dbConfig App appConfig + Auth authConfig Sentry sentryConfig } diff --git a/internal/transport/prometheus/prometheus.go b/internal/transport/prometheus/prometheus.go index 71de1cf..31e50fd 100644 --- a/internal/transport/prometheus/prometheus.go +++ b/internal/transport/prometheus/prometheus.go @@ -1,18 +1,15 @@ package pro import ( - mux "github.com/go-chi/chi/v5" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" ) type Prometheus struct { - router *mux.Mux usersConfirmed prometheus.Counter // Количество пользователей подтвердивших регистрацию usersRegistered prometheus.Counter // Количество зарегистрировавшихся пользователей } -func NewService(router *mux.Mux, pattern string) *Prometheus { +func NewService() *Prometheus { // Количество пользователей подтвердивших регистрацию usersConfirmed := prometheus.NewCounter( prometheus.CounterOpts{ @@ -27,11 +24,7 @@ func NewService(router *mux.Mux, pattern string) *Prometheus { }) prometheus.MustRegister(usersRegistered) - // middleware? - router.Handle(pattern, promhttp.Handler()) - return &Prometheus{ - router: router, usersConfirmed: usersConfirmed, usersRegistered: usersRegistered, }