You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

216 lines
5.2 KiB

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,
}
// эндпоинты требующие авторизации
arouter.Get("/api/v1/healthz", server.requireAuth(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) 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(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.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(err.Error()))
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"))
}
}
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.Write([]byte(status.Message()))
return
}
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.Write([]byte(status.Message()))
return
}
// Увеличим счетчик заругистрировашихся пользователей
s.prometheus.IncUsersRegistered()
//
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.Write([]byte(status.Message()))
return
}
// Увеличим счетчик пользователей подтвердивших регистрацию
s.prometheus.IncUsersConfirmed()
//
w.Write([]byte(fmt.Sprintf("Success confirmation for %s", code)))
}
}