added jwt for tariffs
This commit is contained in:
parent
0ac712e69d
commit
c8f2b77b93
2
go.mod
2
go.mod
@ -13,6 +13,8 @@ require (
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
|
4
go.sum
4
go.sum
@ -6,6 +6,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
|
||||
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||
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-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"go.uber.org/zap"
|
||||
"hub_admin_backend_service/internal/initialize"
|
||||
"hub_admin_backend_service/internal/models"
|
||||
"hub_admin_backend_service/internal/server/http"
|
||||
"hub_admin_backend_service/pkg/closer"
|
||||
"time"
|
||||
@ -42,11 +43,23 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
||||
internalSrv := http.NewServer(http.ServerConfig{
|
||||
Logger: logger,
|
||||
Controllers: []http.Controller{controllers.PrivilegeInternal, controllers.TariffInternal},
|
||||
JWTConfig: &models.JWTConfiguration{
|
||||
PrivateKey: cfg.PrivateKey,
|
||||
PublicKey: cfg.PublicKey,
|
||||
Issuer: cfg.Issuer,
|
||||
Audience: cfg.Audience,
|
||||
},
|
||||
})
|
||||
|
||||
externalSrv := http.NewServer(http.ServerConfig{
|
||||
Logger: logger,
|
||||
Controllers: []http.Controller{controllers.PrivilegeExternal, controllers.TariffExternal},
|
||||
JWTConfig: &models.JWTConfiguration{
|
||||
PrivateKey: cfg.PrivateKey,
|
||||
PublicKey: cfg.PublicKey,
|
||||
Issuer: cfg.Issuer,
|
||||
Audience: cfg.Audience,
|
||||
},
|
||||
})
|
||||
|
||||
go func() {
|
||||
|
@ -3,15 +3,16 @@ package tariff_external
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
"hub_admin_backend_service/internal/repository/tariff"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
Repo string
|
||||
Repo *tariff.Tariff
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
type TariffExternal struct {
|
||||
repo string
|
||||
repo *tariff.Tariff
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
|
@ -3,15 +3,19 @@ package tariff_internal
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
"hub_admin_backend_service/internal/models"
|
||||
"hub_admin_backend_service/internal/repository/tariff"
|
||||
)
|
||||
|
||||
// todo middleware jwt
|
||||
|
||||
type Deps struct {
|
||||
Repo string
|
||||
Repo *tariff.Tariff
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
type TariffInternal struct {
|
||||
repo string
|
||||
repo *tariff.Tariff
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
@ -23,6 +27,16 @@ func NewTariffInternal(deps Deps) *TariffInternal {
|
||||
}
|
||||
|
||||
func (t *TariffInternal) Get(ctx *fiber.Ctx) error {
|
||||
tariffID := ctx.Params("id")
|
||||
if tariffID == "" {
|
||||
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "url field id don't be empty"})
|
||||
}
|
||||
|
||||
var req models.CreateUpdateTariff
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,10 @@ type Config struct {
|
||||
MongoPassword string `env:"MONGO_PASSWORD" envDefault:"test"`
|
||||
MongoDatabase string `env:"MONGO_DB" envDefault:"admin"`
|
||||
MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"`
|
||||
PrivateKey string `env:"JWT_PRIVATE_KEY"`
|
||||
PublicKey string `env:"JWT_PUBLIC_KEY"`
|
||||
Issuer string `env:"JWT_ISSUER"`
|
||||
Audience string `env:"JWT_AUDIENCE"`
|
||||
}
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
|
@ -32,11 +32,11 @@ func NewControllers(deps ControllerDeps) *Controller {
|
||||
}),
|
||||
TariffInternal: tariff_internal.NewTariffInternal(tariff_internal.Deps{
|
||||
Logger: deps.Logger,
|
||||
Repo: "",
|
||||
Repo: deps.Repos.TariffRepo,
|
||||
}),
|
||||
TariffExternal: tariff_external.NewTariffExternal(tariff_external.Deps{
|
||||
Logger: deps.Logger,
|
||||
Repo: "",
|
||||
Repo: deps.Repos.TariffRepo,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.uber.org/zap"
|
||||
"hub_admin_backend_service/internal/repository/privilege"
|
||||
"hub_admin_backend_service/internal/repository/tariff"
|
||||
)
|
||||
|
||||
type RepositoryDeps struct {
|
||||
@ -13,6 +14,7 @@ type RepositoryDeps struct {
|
||||
|
||||
type Repository struct {
|
||||
PrivilegeRepo *privilege.Privilege
|
||||
TariffRepo *tariff.Tariff
|
||||
}
|
||||
|
||||
func NewRepository(deps RepositoryDeps) *Repository {
|
||||
@ -21,5 +23,9 @@ func NewRepository(deps RepositoryDeps) *Repository {
|
||||
Mdb: deps.Mdb.Collection("privileges"),
|
||||
Logger: deps.Logger,
|
||||
}),
|
||||
TariffRepo: tariff.NewTariffRepo(tariff.Deps{
|
||||
Mdb: deps.Mdb.Collection("tariffs"),
|
||||
Logger: deps.Logger,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
18
internal/models/jwt.go
Normal file
18
internal/models/jwt.go
Normal file
@ -0,0 +1,18 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"time"
|
||||
)
|
||||
|
||||
type JWTConfiguration struct {
|
||||
PrivateKey string
|
||||
PublicKey string
|
||||
Issuer string
|
||||
Audience string
|
||||
Algorithm jwt.SigningMethodRSA
|
||||
ExpiresIn time.Duration
|
||||
}
|
||||
|
||||
const AuthJWTDecodedUserIDKey = "userID"
|
||||
const AuthJWTDecodedAccessTokenKey = "access-token"
|
@ -13,3 +13,18 @@ type CreateUpdateReq struct {
|
||||
type ManyCreateUpdate struct {
|
||||
Privileges []CreateUpdateReq `json:"privileges"`
|
||||
}
|
||||
|
||||
type TariffPagination struct {
|
||||
TotalPages int `json:"totalPages"`
|
||||
Tariffs []Tariff `json:"tariffs"`
|
||||
}
|
||||
|
||||
type CreateUpdateTariff struct {
|
||||
Name string `json:"name"`
|
||||
UserID string `json:"userId"`
|
||||
Description string `json:"description"`
|
||||
Price int `json:"price"`
|
||||
Order int `json:"order"`
|
||||
IsCustom bool `json:"isCustom"`
|
||||
Privileges []Privilege `json:"privileges"`
|
||||
}
|
||||
|
19
internal/models/tariff.go
Normal file
19
internal/models/tariff.go
Normal file
@ -0,0 +1,19 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Tariff struct {
|
||||
ID primitive.ObjectID `json:"_id" bson:"_id"`
|
||||
Name string `json:"name" bson:"name"`
|
||||
UserID string `json:"userID" bson:"userID"`
|
||||
Price int `json:"price" bson:"price"`
|
||||
IsCustom bool `json:"isCustom" bson:"isCustom"`
|
||||
Privileges []Privilege `json:"privileges" bson:"privileges"`
|
||||
IsDeleted bool `json:"isDeleted" bson:"isDeleted"`
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"`
|
||||
DeletedAt time.Time `json:"deletedAt" bson:"deletedAt"`
|
||||
}
|
23
internal/repository/tariff/tariff.go
Normal file
23
internal/repository/tariff/tariff.go
Normal file
@ -0,0 +1,23 @@
|
||||
package tariff
|
||||
|
||||
import (
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
Mdb *mongo.Collection
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
type Tariff struct {
|
||||
mdb *mongo.Collection
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func NewTariffRepo(deps Deps) *Tariff {
|
||||
return &Tariff{
|
||||
mdb: deps.Mdb,
|
||||
logger: deps.Logger,
|
||||
}
|
||||
}
|
@ -5,11 +5,14 @@ import (
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
"hub_admin_backend_service/internal/models"
|
||||
"hub_admin_backend_service/internal/utils"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
Logger *zap.Logger
|
||||
Controllers []Controller
|
||||
JWTConfig *models.JWTConfiguration
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
@ -20,6 +23,13 @@ type Server struct {
|
||||
|
||||
func NewServer(config ServerConfig) *Server {
|
||||
app := fiber.New()
|
||||
|
||||
jwtUtil := utils.NewJWT(config.JWTConfig)
|
||||
app.Use("/tariff", utils.NewAuthenticator(jwtUtil))
|
||||
app.Use("/privilege", func(c *fiber.Ctx) error {
|
||||
return c.Next()
|
||||
})
|
||||
|
||||
s := &Server{
|
||||
Logger: config.Logger,
|
||||
Controllers: config.Controllers,
|
||||
|
59
internal/utils/authenticator.go
Normal file
59
internal/utils/authenticator.go
Normal file
@ -0,0 +1,59 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"hub_admin_backend_service/internal/models"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
prefix = "Bearer "
|
||||
)
|
||||
|
||||
func NewAuthenticator(jwtUtil *JWT) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
if jwtUtil == nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Invalid arguments")
|
||||
}
|
||||
|
||||
err := authenticate(jwtUtil, c)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, err.Error())
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func authenticate(jwtUtil *JWT, c *fiber.Ctx) error {
|
||||
jws, err := parseJWSFromRequest(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userID, validateErr := jwtUtil.Validate(jws)
|
||||
if validateErr != nil {
|
||||
return validateErr
|
||||
}
|
||||
|
||||
c.Locals(models.AuthJWTDecodedUserIDKey, userID)
|
||||
c.Locals(models.AuthJWTDecodedAccessTokenKey, jws)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseJWSFromRequest(c *fiber.Ctx) (string, error) {
|
||||
header := c.Get("Authorization")
|
||||
|
||||
if header != "" && strings.HasPrefix(header, prefix) {
|
||||
return strings.TrimPrefix(header, prefix), nil
|
||||
}
|
||||
|
||||
token := c.Query("Authorization")
|
||||
if token == "" {
|
||||
return "", errors.New("failed to parse jws from request: no valid token found")
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
89
internal/utils/jwt.go
Normal file
89
internal/utils/jwt.go
Normal file
@ -0,0 +1,89 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"hub_admin_backend_service/internal/models"
|
||||
"time"
|
||||
)
|
||||
|
||||
type JWT struct {
|
||||
privateKey []byte
|
||||
publicKey []byte
|
||||
algorithm *jwt.SigningMethodRSA
|
||||
expiresIn time.Duration
|
||||
issuer string
|
||||
audience string
|
||||
}
|
||||
|
||||
func NewJWT(configuration *models.JWTConfiguration) *JWT {
|
||||
return &JWT{
|
||||
privateKey: []byte(configuration.PrivateKey),
|
||||
publicKey: []byte(configuration.PublicKey),
|
||||
algorithm: jwt.SigningMethodRS256,
|
||||
expiresIn: 15 * time.Minute,
|
||||
issuer: configuration.Issuer,
|
||||
audience: configuration.Audience,
|
||||
}
|
||||
}
|
||||
|
||||
func (receiver *JWT) Create(id string) (string, error) {
|
||||
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(receiver.privateKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse private key on <Create> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
||||
claims := jwt.MapClaims{
|
||||
"id": id, // Our userID
|
||||
"exp": now.Add(receiver.expiresIn).Unix(), // The expiration time after which the token must be disregarded.
|
||||
"aud": receiver.audience, // Audience
|
||||
"iss": receiver.issuer, // Issuer
|
||||
}
|
||||
|
||||
token, err := jwt.NewWithClaims(receiver.algorithm, claims).SignedString(privateKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to sing on <Create> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (receiver *JWT) Validate(tokenString string) (string, error) {
|
||||
key, err := jwt.ParseRSAPublicKeyFromPEM(receiver.publicKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse rsa public key on <Validate> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
parseCallback := func(token *jwt.Token) (any, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %s", token.Header["alg"])
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(
|
||||
tokenString,
|
||||
parseCallback,
|
||||
jwt.WithAudience(receiver.audience),
|
||||
jwt.WithIssuer(receiver.issuer),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse jwt token on <Validate> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok || !token.Valid {
|
||||
return "", errors.New("token is invalid on <Validate> of <JWT>")
|
||||
}
|
||||
|
||||
data, ok := claims["id"].(string)
|
||||
if !ok {
|
||||
return "", errors.New("data is empty or not a string on <Validate> of <JWT>")
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user