From 662a9b7ffd6e2e0fb88707695de6344bd45dde21 Mon Sep 17 00:00:00 2001 From: Piotr Biernat Date: Wed, 22 Oct 2025 10:53:20 +0200 Subject: [PATCH] Added access checking middleware Added URLAccessRepository Refactor --- src/domain/repository/interface.go | 4 +- src/domain/repository/role_repository.go | 55 ++++++++++++++++++ .../repository/url_access_repository.go | 58 +++++++++++++++++++ src/domain/repository/user_repository.go | 28 ++++----- src/go.mod | 5 +- src/go.sum | 10 +++- src/internal/server/access_handler.go | 25 ++++++++ src/internal/server/middlewares.go | 28 ++++----- src/internal/server/router.go | 3 +- src/internal/service/auth.go | 7 ++- src/internal/service/jwt.go | 3 + 11 files changed, 192 insertions(+), 34 deletions(-) create mode 100644 src/domain/repository/role_repository.go create mode 100644 src/domain/repository/url_access_repository.go create mode 100644 src/internal/server/access_handler.go diff --git a/src/domain/repository/interface.go b/src/domain/repository/interface.go index 48684b9..ab0268d 100644 --- a/src/domain/repository/interface.go +++ b/src/domain/repository/interface.go @@ -2,8 +2,8 @@ package repository import entity "git.ego.freeddns.org/egommerce/api-entities/identity/entity" -type UserRepositoryInterface interface { - GetByID(id string) (*entity.User, error) +type RepositoryInterface interface { + FindByID(id string) (*entity.User, error) Create(user *entity.User) (*entity.User, error) Update(user *entity.User) (*entity.User, error) Delete(id int64) (bool, error) diff --git a/src/domain/repository/role_repository.go b/src/domain/repository/role_repository.go new file mode 100644 index 0000000..d3aeef3 --- /dev/null +++ b/src/domain/repository/role_repository.go @@ -0,0 +1,55 @@ +package repository + +import ( + "context" + "errors" + + entity "git.ego.freeddns.org/egommerce/api-entities/identity/entity" + + "github.com/jackc/pgx/v5/pgxpool" +) + +type RoleRepository struct { + db *pgxpool.Pool +} + +func NewRoleRepository(db *pgxpool.Pool) *RoleRepository { + return &RoleRepository{db} +} + +func (r *RoleRepository) FindByID(id string) (*entity.Role, error) { + var role entity.Role + + sql := `SELECT id, roles, url FROM identity.roles WHERE id=$1 LIMIT 1` + err := r.db.QueryRow(context.Background(), sql, id). + Scan(&role.ID, &role.Roles, &role.URL) + if err != nil { + return nil, errors.New("failed to fetch role from DB: " + err.Error()) + } + + return &role, nil +} + +func (r *RoleRepository) Create(role *entity.Role) (string, error) { + var id string + + // sql := `INSERT INTO identity.users(email, username, password) VALUES($1, $2, $3) LIMIT 1 RETURNING id` + // err := r.db.QueryRow(context.Background(), sql, user.Email, user.Username, user.Password).Scan(&id) + // if err != nil { + // if err = db.IsDuplicatedRow(err); err != nil { + // return "", errors.New("username/email is already taken") + // } + + // return "", errors.New("db error: " + err.Error()) + // } + + return id, nil +} + +func (r *RoleRepository) Update(role *entity.Role) (*entity.Role, error) { + return &entity.Role{}, nil +} + +func (r *RoleRepository) Delete(id int64) (bool, error) { + return true, nil +} diff --git a/src/domain/repository/url_access_repository.go b/src/domain/repository/url_access_repository.go new file mode 100644 index 0000000..656049d --- /dev/null +++ b/src/domain/repository/url_access_repository.go @@ -0,0 +1,58 @@ +package repository + +import ( + "context" + "errors" + + entity "git.ego.freeddns.org/egommerce/api-entities/identity/entity" + + "github.com/jackc/pgx/v5/pgxpool" +) + +type URLAccessRepository struct { + db *pgxpool.Pool +} + +func NewURLAccessRepository(db *pgxpool.Pool) *URLAccessRepository { + return &URLAccessRepository{db} +} + +func (r *URLAccessRepository) FindByID(id string) (*entity.URLAccess, error) { + var urlAccess entity.URLAccess + + sql := `SELECT id, roles, url, service FROM identity.url_access WHERE id=$1 LIMIT 1` + err := r.db.QueryRow(context.Background(), sql, id). + Scan(&urlAccess.ID, &urlAccess.Roles, &urlAccess.URL, &urlAccess.Service) + if err != nil { + return nil, errors.New("failed to fetch url_access from DB: " + err.Error()) + } + + return &urlAccess, nil +} + +func (r *URLAccessRepository) Create(role *entity.URLAccess) (string, error) { + var id string + + return id, nil +} + +func (r *URLAccessRepository) Update(role *entity.URLAccess) (*entity.URLAccess, error) { + return &entity.URLAccess{}, nil +} + +func (r *URLAccessRepository) Delete(id int64) (bool, error) { + return true, nil +} + +func (r *URLAccessRepository) FindByURLAndService(url, service string) (*entity.URLAccess, error) { + var urlAccess entity.URLAccess + + sql := `SELECT id, roles, url FROM identity.url_access WHERE url=$1 AND service=$2 LIMIT 1` + err := r.db.QueryRow(context.Background(), sql, url, service). + Scan(&urlAccess.ID, &urlAccess.Roles, &urlAccess.URL) + if err != nil { + return nil, errors.New("failed to fetch url_access from DB: " + err.Error()) + } + + return &urlAccess, nil +} diff --git a/src/domain/repository/user_repository.go b/src/domain/repository/user_repository.go index f767c1d..4fe59b0 100644 --- a/src/domain/repository/user_repository.go +++ b/src/domain/repository/user_repository.go @@ -18,7 +18,7 @@ func NewUserRepository(db *pgxpool.Pool) *UserRepository { return &UserRepository{db} } -func (r *UserRepository) GetByID(id string) (*entity.User, error) { +func (r *UserRepository) FindByID(id string) (*entity.User, error) { var user entity.User sql := `SELECT id, username, password, email, created_at FROM identity.users WHERE id=$1 LIMIT 1` @@ -31,19 +31,6 @@ func (r *UserRepository) GetByID(id string) (*entity.User, error) { return &user, nil } -func (r *UserRepository) GetByUsername(login string) (*entity.User, error) { - var user entity.User - - sql := `SELECT id, username, password, email, created_at FROM identity.users WHERE username=$1 LIMIT 1` - err := r.db.QueryRow(context.Background(), sql, login). - Scan(&user.ID, &user.Username, &user.Password, &user.Email, &user.CreatedAt) - if err != nil { - return nil, errors.New("failed to fetch user from DB: " + err.Error()) - } - - return &user, nil -} - func (r *UserRepository) Create(user *entity.User) (string, error) { var id string @@ -67,3 +54,16 @@ func (r *UserRepository) Update(user *entity.User) (*entity.User, error) { func (r *UserRepository) Delete(id int64) (bool, error) { return true, nil } + +func (r *UserRepository) FindByUsername(login string) (*entity.User, error) { + var user entity.User + + sql := `SELECT id, username, password, email, created_at FROM identity.users WHERE username=$1 LIMIT 1` + err := r.db.QueryRow(context.Background(), sql, login). + Scan(&user.ID, &user.Username, &user.Password, &user.Email, &user.CreatedAt) + if err != nil { + return nil, errors.New("failed to fetch user from DB: " + err.Error()) + } + + return &user, nil +} diff --git a/src/go.mod b/src/go.mod index 4a13b13..29c468b 100644 --- a/src/go.mod +++ b/src/go.mod @@ -5,11 +5,12 @@ go 1.24.0 toolchain go1.24.1 require ( - git.ego.freeddns.org/egommerce/api-entities v0.3.11 + git.ego.freeddns.org/egommerce/api-entities v0.3.17 git.ego.freeddns.org/egommerce/go-api-pkg v0.4.9 github.com/go-pg/migrations/v8 v8.1.0 github.com/go-pg/pg/v10 v10.15.0 github.com/go-redis/redis/v8 v8.11.5 + github.com/gofiber/contrib/jwt v1.1.2 github.com/gofiber/fiber/v2 v2.52.9 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.6.0 @@ -18,12 +19,14 @@ require ( ) require ( + github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect github.com/andybalholm/brotli v1.2.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/clipperhouse/uax29/v2 v2.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-pg/zerochecker v0.2.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect diff --git a/src/go.sum b/src/go.sum index 24fca44..02beac0 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,9 +1,11 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -git.ego.freeddns.org/egommerce/api-entities v0.3.11 h1:WpBCVZ4ND69oxDRlJOOd0qbX4hN7xDIl9ViVT2OqEsw= -git.ego.freeddns.org/egommerce/api-entities v0.3.11/go.mod h1:IqynARw+06GOm4eZGZuepmbi7bUxWBnOB4jd5cI7jf8= +git.ego.freeddns.org/egommerce/api-entities v0.3.17 h1:AEnTwJseSqd7VwG74/QBNb3dv050RdMCQBeQF62WJHE= +git.ego.freeddns.org/egommerce/api-entities v0.3.17/go.mod h1:IqynARw+06GOm4eZGZuepmbi7bUxWBnOB4jd5cI7jf8= git.ego.freeddns.org/egommerce/go-api-pkg v0.4.9 h1:Y9MisGDhl/ti4gsegl9MC7KoY2aHuyA0LvIESPoiPkE= git.ego.freeddns.org/egommerce/go-api-pkg v0.4.9/go.mod h1:Q4onxocNdFhzD9QnQK3ubd68chbJPexjDraEHoIEN3Y= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k= +github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -34,10 +36,14 @@ github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4 github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/gofiber/contrib/jwt v1.1.2 h1:GmWnOqT4A15EkA8IPXwSpvNUXZR4u5SMj+geBmyLAjs= +github.com/gofiber/contrib/jwt v1.1.2/go.mod h1:CpIwrkUQ3Q6IP8y9n3f0wP9bOnSKx39EDp2fBVgMFVk= github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw= github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= 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.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/src/internal/server/access_handler.go b/src/internal/server/access_handler.go new file mode 100644 index 0000000..6adc0d4 --- /dev/null +++ b/src/internal/server/access_handler.go @@ -0,0 +1,25 @@ +package server + +import ( + domain "git.ego.freeddns.org/egommerce/identity-service/domain/repository" + "git.ego.freeddns.org/egommerce/identity-service/internal/service" + "github.com/gofiber/fiber/v2" +) + +func (s *Server) AccessHandlerFn(c *fiber.Ctx) error { + url, srvName := c.Query("q"), c.Query("srv") + + urlRepo := domain.NewURLAccessRepository(s.GetDatabase()) + + userRepo := domain.NewUserRepository(s.GetDatabase()) + authSrv := service.NewAuthService(userRepo, s.GetCache()) + + authSrv.VerifyToken("asd") + + urlAcc, err := urlRepo.FindByURLAndService(url, srvName) + if err != nil { + return s.Error(c, fiber.StatusBadRequest, "unable to fetch requested url data") + } + + return c.JSON(urlAcc.Roles) +} diff --git a/src/internal/server/middlewares.go b/src/internal/server/middlewares.go index b33c821..0ecbc0b 100644 --- a/src/internal/server/middlewares.go +++ b/src/internal/server/middlewares.go @@ -3,6 +3,8 @@ package server import ( "log" + cnf "git.ego.freeddns.org/egommerce/go-api-pkg/config" + jwt "github.com/gofiber/contrib/jwt" "github.com/gofiber/fiber/v2" "github.com/google/uuid" ) @@ -29,25 +31,25 @@ func LoggingMiddleware() func(c *fiber.Ctx) error { func XRequestIDMiddleware() func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error { - requestID := uuid.New().String() - c.Set("X-Request-ID", requestID) + c.Set("X-Request-ID", uuid.New().String()) return c.Next() } } -// JWTProtected func for specify routes group with JWT authentication. -// See: https://github.com/gofiber/jwt -// func JWTProtected() func(*fiber.Ctx) error { -// // Create config for JWT authentication middleware. -// config := jwt.Config{ -// SigningKey: []byte(baseCnf.GetEnv("JWT_ACCESS_TOKEN_SECRET_KEY", "FallbackAccessTokenSecret")), -// ContextKey: "jwt", // used in private routes -// ErrorHandler: jwtError, -// } +func JWTProtected(s *Server) func(c *fiber.Ctx) error { + secret := []byte(cnf.GetEnv("JWT_ACCESS_TOKEN_SECRET_KEY", "FallbackAccessTokenSecret")) -// return jwt.New(config) -// } + return func(c *fiber.Ctx) error { + return jwt.New(jwt.Config{ + SigningKey: jwt.SigningKey{Key: secret}, + ContextKey: "jwt", + ErrorHandler: func(c *fiber.Ctx, err error) error { + return s.Error(c, fiber.StatusUnauthorized, "unauthorized") + }, + })(c) + } +} // func jwtError(c *fiber.Ctx, err error) error { // // Return status 400 Bad Request and failed authentication error. diff --git a/src/internal/server/router.go b/src/internal/server/router.go index 59be803..33cb02e 100644 --- a/src/internal/server/router.go +++ b/src/internal/server/router.go @@ -22,5 +22,6 @@ func SetupRouter(s *Server) { s.Group("/v1"). Post("/login", s.LoginHandlerFn). Post("/refresh", s.RefreshHandlerFn). - Post("/register", s.RegisterHandlerFn) + Post("/register", s.RegisterHandlerFn). + Get("/access", JWTProtected(s), s.AccessHandlerFn) } diff --git a/src/internal/service/auth.go b/src/internal/service/auth.go index 4f04d8f..622d6c5 100644 --- a/src/internal/service/auth.go +++ b/src/internal/service/auth.go @@ -36,7 +36,7 @@ func NewAuthService(userRepo *domain.UserRepository, cache *redis.Client) *Auth } func (a *Auth) Login(login, passwd string) (string, error) { - user, err := a.userRepo.GetByUsername(login) + user, err := a.userRepo.FindByUsername(login) if err != nil { // if err = database.NoRowsInQuerySet(err); err != nil { // return "", errors.New("no user found") @@ -75,6 +75,11 @@ func (a *Auth) RefreshToken(accessToken string) (string, error) { return newAccessToken, nil } +func (a *Auth) VerifyToken(token string) (string, error) { + + return token, nil +} + func (a *Auth) Register(email, login, passwd string) (string, error) { passwd, _ = passSrv.Hash(passwd) diff --git a/src/internal/service/jwt.go b/src/internal/service/jwt.go index 3b74db3..f526c85 100644 --- a/src/internal/service/jwt.go +++ b/src/internal/service/jwt.go @@ -48,6 +48,9 @@ type JWT struct { } func (s *JWT) CreateAccessToken(id string) (string, error) { + fmt.Println(time.Now().Add(s.accessTokenExpireTime).Unix()) + fmt.Println(s.accessTokenExpireTime) + claims := &jwt.StandardClaims{ Subject: id, IssuedAt: time.Now().Unix(),