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.
239 lines
5.7 KiB
239 lines
5.7 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,
|
|
}
|
|
|
|
// эндпоинты требующие авторизации оборачиваются в 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)))
|
|
}
|
|
}
|
|
|