forked from ebhomengo/niki
feat(niki): add admin authorization service.
This commit is contained in:
parent
17b7c4beb2
commit
28ee6babd3
|
@ -24,4 +24,3 @@ bin
|
|||
*.env
|
||||
|
||||
logs/
|
||||
mise.log
|
4
Makefile
4
Makefile
|
@ -19,7 +19,3 @@ format:
|
|||
|
||||
build:
|
||||
go build main.go
|
||||
|
||||
run-dev:
|
||||
sudo docker compose up
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package adminhandler
|
|||
|
||||
import (
|
||||
adminservice "git.gocasts.ir/ebhomengo/niki/service/admin/admin"
|
||||
adminauthorizationservice "git.gocasts.ir/ebhomengo/niki/service/admin/authorization"
|
||||
adminauthservice "git.gocasts.ir/ebhomengo/niki/service/auth"
|
||||
adminvalidator "git.gocasts.ir/ebhomengo/niki/validator/admin/admin"
|
||||
)
|
||||
|
@ -11,15 +12,18 @@ type Handler struct {
|
|||
authSvc adminauthservice.Service
|
||||
adminSvc adminservice.Service
|
||||
adminVld adminvalidator.Validator
|
||||
adminAuthorizeSvc adminauthorizationservice.Service
|
||||
}
|
||||
|
||||
func New(authConfig adminauthservice.Config, authSvc adminauthservice.Service,
|
||||
adminSvc adminservice.Service, adminVld adminvalidator.Validator,
|
||||
adminAuthorizeSvc adminauthorizationservice.Service,
|
||||
) Handler {
|
||||
return Handler{
|
||||
authConfig: authConfig,
|
||||
authSvc: authSvc,
|
||||
adminSvc: adminSvc,
|
||||
adminVld: adminVld,
|
||||
adminAuthorizeSvc: adminAuthorizeSvc,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package adminhandler
|
|||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/delivery/http_server/middleware"
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
|
@ -10,9 +11,8 @@ func (h Handler) SetRoutes(e *echo.Echo) {
|
|||
|
||||
//nolint:gocritic
|
||||
//r.POST("/", h.Add).Name = "admin-addkindboxreq"
|
||||
r.POST("/register", h.Register)
|
||||
r.POST("/register", h.Register, middleware.Auth(h.authSvc, h.authConfig), middleware.AdminAuthorization(h.adminAuthorizeSvc, entity.AdminAdminRegisterPermission))
|
||||
r.POST("/login-by-phone", h.LoginByPhoneNumber)
|
||||
r.GET("/test", h.LoginByPhoneNumber, middleware.Auth(h.authSvc, h.authConfig))
|
||||
//nolint:gocritic
|
||||
//r.PATCH("/:id", h.Update).Name = "admin-updatekindboxreq"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package adminkindboxhandler
|
||||
|
||||
import (
|
||||
adminauthorizationservice "git.gocasts.ir/ebhomengo/niki/service/admin/authorization"
|
||||
adminkindboxservice "git.gocasts.ir/ebhomengo/niki/service/admin/kind_box"
|
||||
authservice "git.gocasts.ir/ebhomengo/niki/service/auth"
|
||||
adminkindboxvalidator "git.gocasts.ir/ebhomengo/niki/validator/admin/kind_box"
|
||||
|
@ -11,15 +12,18 @@ type Handler struct {
|
|||
authSvc authservice.Service
|
||||
adminKindBoxSvc adminkindboxservice.Service
|
||||
adminKindBoxVld adminkindboxvalidator.Validator
|
||||
adminAuthorizeSvc adminauthorizationservice.Service
|
||||
}
|
||||
|
||||
func New(authConfig authservice.Config, authSvc authservice.Service,
|
||||
adminKindBoxSvc adminkindboxservice.Service, adminKindBoxVld adminkindboxvalidator.Validator,
|
||||
adminAuthorizeSvc adminauthorizationservice.Service,
|
||||
) Handler {
|
||||
return Handler{
|
||||
authConfig: authConfig,
|
||||
authSvc: authSvc,
|
||||
adminKindBoxSvc: adminKindBoxSvc,
|
||||
adminKindBoxVld: adminKindBoxVld,
|
||||
adminAuthorizeSvc: adminAuthorizeSvc,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package adminkindboxreqhandler
|
||||
|
||||
import (
|
||||
adminauthorizationservice "git.gocasts.ir/ebhomengo/niki/service/admin/authorization"
|
||||
adminkindboxreqservice "git.gocasts.ir/ebhomengo/niki/service/admin/kind_box_req"
|
||||
authservice "git.gocasts.ir/ebhomengo/niki/service/auth"
|
||||
adminkindboxreqvalidator "git.gocasts.ir/ebhomengo/niki/validator/admin/kind_box_req"
|
||||
|
@ -11,15 +12,18 @@ type Handler struct {
|
|||
authSvc authservice.Service
|
||||
adminKindBoxReqSvc adminkindboxreqservice.Service
|
||||
adminKindBoxReqVld adminkindboxreqvalidator.Validator
|
||||
adminAuthorizeSvc adminauthorizationservice.Service
|
||||
}
|
||||
|
||||
func New(authConfig authservice.Config, authSvc authservice.Service,
|
||||
adminKindBoxReqSvc adminkindboxreqservice.Service, adminKindBoxReqVld adminkindboxreqvalidator.Validator,
|
||||
adminAuthorizeSvc adminauthorizationservice.Service,
|
||||
) Handler {
|
||||
return Handler{
|
||||
authConfig: authConfig,
|
||||
authSvc: authSvc,
|
||||
adminKindBoxReqSvc: adminKindBoxReqSvc,
|
||||
adminKindBoxReqVld: adminKindBoxReqVld,
|
||||
adminAuthorizeSvc: adminAuthorizeSvc,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package adminkindboxreqhandler
|
|||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/delivery/http_server/middleware"
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
echo "github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
|
@ -9,9 +10,9 @@ func (h Handler) SetRoutes(e *echo.Echo) {
|
|||
r := e.Group("/admin/kindboxreqs")
|
||||
|
||||
// todo - add acl
|
||||
r.PATCH("/accept-kind-box-req/:id", h.Accept)
|
||||
r.PATCH("/reject-kind-box-req/:id", h.Reject)
|
||||
r.PATCH("/accept-kind-box-req/:id", h.Accept, middleware.Auth(h.authSvc, h.authConfig), middleware.AdminAuthorization(h.adminAuthorizeSvc, entity.AdminKindBoxReqAcceptPermission))
|
||||
r.PATCH("/reject-kind-box-req/:id", h.Reject, middleware.Auth(h.authSvc, h.authConfig), middleware.AdminAuthorization(h.adminAuthorizeSvc, entity.AdminKindBoxReqRejectPermission))
|
||||
r.PATCH("/deliver-kind-box-req/:id", h.Deliver)
|
||||
r.PATCH("/assign-sender-agent/:id", h.AssignSenderAgent)
|
||||
r.GET("/", h.GetAll, middleware.Auth(h.authSvc, h.authConfig))
|
||||
r.GET("/", h.GetAll, middleware.Auth(h.authSvc, h.authConfig), middleware.AdminAuthorization(h.adminAuthorizeSvc, entity.AdminKindBoxReqGetAllPermission))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
"git.gocasts.ir/ebhomengo/niki/pkg/claim"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
adminauthorizationservice "git.gocasts.ir/ebhomengo/niki/service/admin/authorization"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func AdminAuthorization(service adminauthorizationservice.Service,
|
||||
permissions ...entity.AdminPermission,
|
||||
) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) (err error) {
|
||||
claims := claim.GetClaimsFromEchoContext(c)
|
||||
|
||||
isAllowed, err := service.CheckAccess(claims.UserID, entity.MapToAdminRole(claims.Role), permissions...)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, echo.Map{
|
||||
"message": errmsg.ErrorMsgSomethingWentWrong,
|
||||
})
|
||||
}
|
||||
|
||||
if !isAllowed {
|
||||
return c.JSON(http.StatusForbidden, echo.Map{
|
||||
"message": errmsg.ErrorMsgUserNotAllowed,
|
||||
})
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import (
|
|||
benefactorhandler "git.gocasts.ir/ebhomengo/niki/delivery/http_server/benefactor/benefactor"
|
||||
benefactorkindboxreqhandler "git.gocasts.ir/ebhomengo/niki/delivery/http_server/benefactor/kind_box_req"
|
||||
adminservice "git.gocasts.ir/ebhomengo/niki/service/admin/admin"
|
||||
adminauthorizationservice "git.gocasts.ir/ebhomengo/niki/service/admin/authorization"
|
||||
adminkindboxreqservice "git.gocasts.ir/ebhomengo/niki/service/admin/kind_box_req"
|
||||
authservice "git.gocasts.ir/ebhomengo/niki/service/auth"
|
||||
benefactoraddressservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/address"
|
||||
|
@ -48,6 +49,7 @@ func New(
|
|||
adminAuthSvc authservice.Service,
|
||||
adminKinBoxReqSvc adminkindboxreqservice.Service,
|
||||
adminKinBoxReqVld adminkindboxreqvalidator.Validator,
|
||||
adminAuthorizeSvc adminauthorizationservice.Service,
|
||||
) Server {
|
||||
return Server{
|
||||
Router: echo.New(),
|
||||
|
@ -55,8 +57,8 @@ func New(
|
|||
benefactorHandler: benefactorhandler.New(cfg.Auth, benefactorAuthSvc, benefactorSvc, benefactorVld),
|
||||
benefactorKindBoxReqHandler: benefactorkindboxreqhandler.New(cfg.Auth, benefactorAuthSvc, benefactorKindBoxReqSvc, benefactorKindBoxReqVld),
|
||||
benefactorAddressHandler: benefactoraddresshandler.New(cfg.Auth, benefactorAuthSvc, benefactorAddressSvc, benefactorAddressVld),
|
||||
adminHandler: adminhandler.New(cfg.AdminAuth, adminAuthSvc, adminSvc, adminVld),
|
||||
adminKindBoxReqHandler: adminkindboxreqhandler.New(cfg.Auth, adminAuthSvc, adminKinBoxReqSvc, adminKinBoxReqVld),
|
||||
adminHandler: adminhandler.New(cfg.AdminAuth, adminAuthSvc, adminSvc, adminVld, adminAuthorizeSvc),
|
||||
adminKindBoxReqHandler: adminkindboxreqhandler.New(cfg.Auth, adminAuthSvc, adminKinBoxReqSvc, adminKinBoxReqVld, adminAuthorizeSvc),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package initial
|
||||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
adminauthorizationservice "git.gocasts.ir/ebhomengo/niki/service/admin/authorization"
|
||||
)
|
||||
|
||||
type AdminAuthorization struct {
|
||||
AdminAuthorizationSvc adminauthorizationservice.Service
|
||||
}
|
||||
|
||||
func InitAdminAuthorizationService(db *mysql.DB) adminauthorizationservice.Service {
|
||||
return adminauthorizationservice.New(InitAdminMysql(db))
|
||||
}
|
6
main.go
6
main.go
|
@ -17,6 +17,7 @@ type Dependencies struct {
|
|||
initial.Databases
|
||||
initial.Validators
|
||||
initial.Services
|
||||
initial.AdminAuthorization
|
||||
}
|
||||
|
||||
func parseFlags() bool {
|
||||
|
@ -64,6 +65,9 @@ func initDependencies(cfg config.Config, redisAdapter redis.Adapter, db *mysql.D
|
|||
AdminKindBoxReqSvc: initial.InitAdminKindBoxReqService(db),
|
||||
AdminSvc: initial.InitAdminService(cfg, db),
|
||||
},
|
||||
initial.AdminAuthorization{
|
||||
AdminAuthorizationSvc: initial.InitAdminAuthorizationService(db),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,7 +77,7 @@ func initAndRunServer(cfg config.Config, dependencies *Dependencies) {
|
|||
dependencies.BenefactorKindBoxReqSvc, dependencies.BenefactorKindBoxReqVld,
|
||||
dependencies.BenefactorAddressSvc, dependencies.BenefactorAddressVld,
|
||||
dependencies.AdminSvc, dependencies.AdminVld, dependencies.AdminAuthSvc,
|
||||
dependencies.AdminKindBoxReqSvc, dependencies.AdminKindBoxReqVld)
|
||||
dependencies.AdminKindBoxReqSvc, dependencies.AdminKindBoxReqVld, dependencies.AdminAuthorizationSvc)
|
||||
|
||||
server.Serve()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package errmsg
|
||||
|
||||
const (
|
||||
ErrorMsgAdminNotAllowed = "admin is not allowed"
|
||||
ErrorMsgNotFound = "record not found"
|
||||
ErrorMsgSomethingWentWrong = "something went wrong"
|
||||
ErrorMsgInvalidInput = "invalid input"
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package slice
|
||||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
)
|
||||
|
||||
func DoesExist(list []entity.AdminPermission, value entity.AdminPermission) bool {
|
||||
for _, item := range list {
|
||||
if item == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package mysqladmin
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
"git.gocasts.ir/ebhomengo/niki/pkg/slice"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
)
|
||||
|
||||
func (d *DB) GetAdminPermissions(adminID uint, role entity.AdminRole) ([]entity.AdminPermission, error) {
|
||||
const op = "mysqladmin.GetAdminPermissions"
|
||||
|
||||
// get adminRoleACL
|
||||
adminRoleACL := make([]entity.AdminAccessControl, 0)
|
||||
|
||||
adminRoleRows, err := d.conn.Conn().Query(`select * from admin_access_controls where actor_type = ? and actor_id = ?`,
|
||||
entity.AdminRoleActorType, role)
|
||||
if err != nil {
|
||||
return nil, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgSomethingWentWrong).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
defer adminRoleRows.Close()
|
||||
|
||||
for adminRoleRows.Next() {
|
||||
acl, err := scanAccessControl(adminRoleRows)
|
||||
if err != nil {
|
||||
return nil, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgSomethingWentWrong).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
adminRoleACL = append(adminRoleACL, acl)
|
||||
}
|
||||
|
||||
if err := adminRoleRows.Err(); err != nil {
|
||||
return nil, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgSomethingWentWrong).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
// get adminACL
|
||||
adminACL := make([]entity.AdminAccessControl, 0)
|
||||
|
||||
adminRows, err := d.conn.Conn().Query(`select * from admin_access_controls where actor_type = ? and actor_id = ?`,
|
||||
entity.AdminAdminActorType, adminID)
|
||||
if err != nil {
|
||||
return nil, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgSomethingWentWrong).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
defer adminRows.Close()
|
||||
|
||||
for adminRows.Next() {
|
||||
acl, err := scanAccessControl(adminRows)
|
||||
if err != nil {
|
||||
return nil, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgSomethingWentWrong).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
adminACL = append(adminACL, acl)
|
||||
}
|
||||
|
||||
if err := adminRows.Err(); err != nil {
|
||||
return nil, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgSomethingWentWrong).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
// merge ACLs by permission
|
||||
adminPermissions := make([]entity.AdminPermission, 0)
|
||||
for _, r := range adminRoleACL {
|
||||
if !slice.DoesExist(adminPermissions, r.Permission) {
|
||||
adminPermissions = append(adminPermissions, r.Permission)
|
||||
}
|
||||
}
|
||||
for _, a := range adminACL {
|
||||
if !slice.DoesExist(adminPermissions, a.Permission) {
|
||||
adminPermissions = append(adminPermissions, a.Permission)
|
||||
}
|
||||
}
|
||||
if len(adminPermissions) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return adminPermissions, nil
|
||||
}
|
||||
|
||||
func scanAccessControl(scanner mysql.Scanner) (entity.AdminAccessControl, error) {
|
||||
var (
|
||||
createdAt time.Time
|
||||
acl entity.AdminAccessControl
|
||||
)
|
||||
|
||||
err := scanner.Scan(&acl.ID, &acl.ActorID, &acl.ActorType, &acl.Permission, &createdAt)
|
||||
|
||||
return acl, err
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package adminauthorizationservice
|
||||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
GetAdminPermissions(adminID uint, role entity.AdminRole) ([]entity.AdminPermission, error)
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
repo Repository
|
||||
}
|
||||
|
||||
func New(repo Repository) Service {
|
||||
return Service{repo: repo}
|
||||
}
|
||||
|
||||
func (s Service) CheckAccess(adminID uint, role entity.AdminRole, permissions ...entity.AdminPermission) (bool, error) {
|
||||
const op = "adminauthorizationservice.CheckAccess"
|
||||
|
||||
AdminPermissions, err := s.repo.GetAdminPermissions(adminID, role)
|
||||
if err != nil {
|
||||
return false, richerror.New(op).WithErr(err)
|
||||
}
|
||||
|
||||
for _, p := range permissions {
|
||||
for _, ap := range AdminPermissions {
|
||||
if p == ap {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
|
@ -2,11 +2,11 @@ package benefactorkindboxreqservice
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
param "git.gocasts.ir/ebhomengo/niki/param/benefactor/kind_box_req"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (s Service) Add(ctx context.Context, req param.KindBoxReqAddRequest) (param.KindBoxReqAddResponse, error) {
|
||||
|
|
Loading…
Reference in New Issue