feat: merge auth&tester

This commit is contained in:
Vyacheslav1557 2025-04-22 20:44:52 +05:00
parent 0a2dea6c23
commit 441af4c6a2
72 changed files with 4910 additions and 2378 deletions

13
internal/auth/delivery.go Normal file
View file

@ -0,0 +1,13 @@
package auth
import (
"github.com/gofiber/fiber/v2"
)
type AuthHandlers interface {
ListSessions(c *fiber.Ctx) error
Terminate(c *fiber.Ctx) error
Login(c *fiber.Ctx) error
Logout(c *fiber.Ctx) error
Refresh(c *fiber.Ctx) error
}

View file

@ -0,0 +1,160 @@
package rest
import (
"context"
"encoding/base64"
"git.sch9.ru/new_gate/ms-tester/internal/auth"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/pkg"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v4"
"strings"
"time"
)
type Handlers struct {
authUC auth.UseCase
jwtSecret string
}
func NewHandlers(authUC auth.UseCase, jwtSecret string) *Handlers {
return &Handlers{
authUC: authUC,
jwtSecret: jwtSecret,
}
}
const (
sessionKey = "session"
)
func sessionFromCtx(ctx context.Context) (*models.Session, error) {
const op = "sessionFromCtx"
session, ok := ctx.Value(sessionKey).(*models.Session)
if !ok {
return nil, pkg.Wrap(pkg.ErrUnauthenticated, nil, op, "")
}
return session, nil
}
func (h *Handlers) ListSessions(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNotImplemented)
}
func (h *Handlers) Terminate(c *fiber.Ctx) error {
ctx := c.Context()
session, err := sessionFromCtx(ctx)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
err = h.authUC.Terminate(ctx, session.UserId)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.SendStatus(fiber.StatusOK)
}
func (h *Handlers) Login(c *fiber.Ctx) error {
authHeader := c.Get("Authorization", "")
if authHeader == "" {
return c.SendStatus(fiber.StatusUnauthorized)
}
username, pwd, err := parseBasicAuth(authHeader)
if err != nil {
return c.SendStatus(fiber.StatusUnauthorized)
}
credentials := &models.Credentials{
Username: strings.ToLower(username),
Password: pwd,
}
device := &models.Device{
Ip: c.IP(),
UseAgent: c.Get("User-Agent", ""),
}
ctx := c.Context()
session, err := h.authUC.Login(ctx, credentials, device)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
claims := jwt.NewWithClaims(jwt.SigningMethodHS256, models.JWT{
SessionId: session.Id,
UserId: session.UserId,
Role: session.Role,
IssuedAt: time.Now().Unix(),
})
token, err := claims.SignedString([]byte(h.jwtSecret))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
c.Set("Authorization", "Bearer "+token)
return c.SendStatus(fiber.StatusOK)
}
func (h *Handlers) Logout(c *fiber.Ctx) error {
ctx := c.Context()
session, err := sessionFromCtx(ctx)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
err = h.authUC.Logout(c.Context(), session.Id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.SendStatus(fiber.StatusOK)
}
func (h *Handlers) Refresh(c *fiber.Ctx) error {
ctx := c.Context()
session, err := sessionFromCtx(ctx)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
err = h.authUC.Refresh(c.Context(), session.Id)
if err != nil {
return c.SendStatus(pkg.ToREST(err))
}
return c.SendStatus(fiber.StatusOK)
}
func parseBasicAuth(header string) (string, string, error) {
const (
op = "parseBasicAuth"
msg = "invalid auth header"
)
authParts := strings.Split(header, " ")
if len(authParts) != 2 || strings.ToLower(authParts[0]) != "basic" {
return "", "", pkg.Wrap(pkg.ErrUnauthenticated, nil, op, msg)
}
decodedAuth, err := base64.StdEncoding.DecodeString(authParts[1])
if err != nil {
return "", "", pkg.Wrap(pkg.ErrUnauthenticated, nil, op, msg)
}
authParts = strings.Split(string(decodedAuth), ":")
if len(authParts) != 2 {
return "", "", pkg.Wrap(pkg.ErrUnauthenticated, nil, op, msg)
}
return authParts[0], authParts[1], nil
}

14
internal/auth/usecase.go Normal file
View file

@ -0,0 +1,14 @@
package auth
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
)
type UseCase interface {
Login(ctx context.Context, credentials *models.Credentials, device *models.Device) (*models.Session, error)
Refresh(ctx context.Context, sessionId string) error
Logout(ctx context.Context, sessionId string) error
Terminate(ctx context.Context, userId int32) error
ListSessions(ctx context.Context, userId int32) ([]*models.Session, error)
}

View file

@ -0,0 +1,70 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/internal/sessions"
"git.sch9.ru/new_gate/ms-tester/internal/users"
"git.sch9.ru/new_gate/ms-tester/pkg"
"github.com/google/uuid"
"time"
)
type UseCase struct {
usersUC users.UseCase
sessionsUC sessions.UseCase
}
func NewUseCase(usersUC users.UseCase, sessionsUC sessions.UseCase) *UseCase {
return &UseCase{
usersUC: usersUC,
sessionsUC: sessionsUC,
}
}
func (uc *UseCase) Login(ctx context.Context, credentials *models.Credentials, device *models.Device) (*models.Session, error) {
const op = "UseCase.Login"
user, err := uc.usersUC.ReadUserByUsername(ctx, credentials.Username)
if err != nil {
return nil, err
}
if !user.IsSamePwd(credentials.Password) {
return nil, pkg.Wrap(pkg.ErrNotFound, nil, op, "password mismatch")
}
session := &models.Session{
Id: uuid.NewString(),
UserId: user.Id,
Role: user.Role,
CreatedAt: time.Now(),
ExpiresAt: time.Now().Add(40 * time.Minute),
UserAgent: device.UseAgent,
Ip: device.Ip,
}
err = uc.sessionsUC.CreateSession(ctx, session)
if err != nil {
return nil, err
}
return session, nil
}
func (uc *UseCase) Logout(ctx context.Context, sessionId string) error {
return uc.sessionsUC.DeleteSession(ctx, sessionId)
}
func (uc *UseCase) Refresh(ctx context.Context, sessionId string) error {
return uc.sessionsUC.UpdateSession(ctx, sessionId)
}
func (uc *UseCase) Terminate(ctx context.Context, userId int32) error {
return uc.sessionsUC.DeleteAllSessions(ctx, userId)
}
func (uc *UseCase) ListSessions(ctx context.Context, userId int32) ([]*models.Session, error) {
// TODO: implement me
panic("implement me")
}