feat: merge auth&tester
This commit is contained in:
parent
0a2dea6c23
commit
441af4c6a2
72 changed files with 4910 additions and 2378 deletions
13
internal/auth/delivery.go
Normal file
13
internal/auth/delivery.go
Normal 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
|
||||
}
|
160
internal/auth/delivery/rest/handlers.go
Normal file
160
internal/auth/delivery/rest/handlers.go
Normal 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
14
internal/auth/usecase.go
Normal 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)
|
||||
}
|
70
internal/auth/usecase/usecase.go
Normal file
70
internal/auth/usecase/usecase.go
Normal 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")
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue