Add user Roles
This commit is contained in:
parent
9e95e8ee5e
commit
001d02c587
@ -43,14 +43,16 @@ func (a *App) Initialize(cfg *config.Config) error {
|
|||||||
middlewares := a.initMiddleware(services)
|
middlewares := a.initMiddleware(services)
|
||||||
healthHandler := handler.NewHealthHandler()
|
healthHandler := handler.NewHealthHandler()
|
||||||
fileHandler := handler.NewFileHandler(services.fileService)
|
fileHandler := handler.NewFileHandler(services.fileService)
|
||||||
|
rbacHandler := handler.NewRBACHandler(services.rbacService)
|
||||||
|
|
||||||
a.router = router.NewRouter(
|
a.router = router.NewRouter(
|
||||||
cfg,
|
cfg,
|
||||||
handler.NewAuthHandler(services.authService),
|
handler.NewAuthHandler(services.authService),
|
||||||
middlewares.authMiddleware,
|
middlewares.authMiddleware,
|
||||||
healthHandler,
|
healthHandler,
|
||||||
handler.NewUserHandler(services.userService, &validator.UserValidatorImpl{}),
|
handler.NewUserHandler(services.userService, validator.NewUserValidator()),
|
||||||
fileHandler,
|
fileHandler,
|
||||||
|
rbacHandler,
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -99,6 +101,7 @@ type repositories struct {
|
|||||||
userRepo *repository.UserRepositoryImpl
|
userRepo *repository.UserRepositoryImpl
|
||||||
userProfileRepo *repository.UserProfileRepository
|
userProfileRepo *repository.UserProfileRepository
|
||||||
titleRepo *repository.TitleRepository
|
titleRepo *repository.TitleRepository
|
||||||
|
rbacRepo *repository.RBACRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initRepositories() *repositories {
|
func (a *App) initRepositories() *repositories {
|
||||||
@ -106,6 +109,7 @@ func (a *App) initRepositories() *repositories {
|
|||||||
userRepo: repository.NewUserRepository(a.db),
|
userRepo: repository.NewUserRepository(a.db),
|
||||||
userProfileRepo: repository.NewUserProfileRepository(a.db),
|
userProfileRepo: repository.NewUserProfileRepository(a.db),
|
||||||
titleRepo: repository.NewTitleRepository(a.db),
|
titleRepo: repository.NewTitleRepository(a.db),
|
||||||
|
rbacRepo: repository.NewRBACRepository(a.db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,6 +127,7 @@ type services struct {
|
|||||||
userService *service.UserServiceImpl
|
userService *service.UserServiceImpl
|
||||||
authService *service.AuthServiceImpl
|
authService *service.AuthServiceImpl
|
||||||
fileService *service.FileServiceImpl
|
fileService *service.FileServiceImpl
|
||||||
|
rbacService *service.RBACServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services {
|
func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services {
|
||||||
@ -137,10 +142,13 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
|
|||||||
s3Client := client.NewFileClient(fileCfg)
|
s3Client := client.NewFileClient(fileCfg)
|
||||||
fileSvc := service.NewFileService(s3Client, processors.userProcessor, "profile", "documents")
|
fileSvc := service.NewFileService(s3Client, processors.userProcessor, "profile", "documents")
|
||||||
|
|
||||||
|
rbacSvc := service.NewRBACService(repos.rbacRepo)
|
||||||
|
|
||||||
return &services{
|
return &services{
|
||||||
userService: userSvc,
|
userService: userSvc,
|
||||||
authService: authService,
|
authService: authService,
|
||||||
fileService: fileSvc,
|
fileService: fileSvc,
|
||||||
|
rbacService: rbacSvc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
57
internal/contract/rbac_contract.go
Normal file
57
internal/contract/rbac_contract.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PermissionResponse struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreatePermissionRequest struct {
|
||||||
|
Code string `json:"code"` // unique
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdatePermissionRequest struct {
|
||||||
|
Code *string `json:"code,omitempty"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListPermissionsResponse struct {
|
||||||
|
Permissions []PermissionResponse `json:"permissions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoleWithPermissionsResponse struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
Permissions []PermissionResponse `json:"permissions"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateRoleRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
PermissionCodes []string `json:"permission_codes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateRoleRequest struct {
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
Code *string `json:"code,omitempty"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
PermissionCodes *[]string `json:"permission_codes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListRolesResponse struct {
|
||||||
|
Roles []RoleWithPermissionsResponse `json:"roles"`
|
||||||
|
}
|
||||||
10
internal/entities/role_permission.go
Normal file
10
internal/entities/role_permission.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
import "github.com/google/uuid"
|
||||||
|
|
||||||
|
type RolePermission struct {
|
||||||
|
RoleID uuid.UUID `gorm:"type:uuid;primaryKey" json:"role_id"`
|
||||||
|
PermissionID uuid.UUID `gorm:"type:uuid;primaryKey" json:"permission_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (RolePermission) TableName() string { return "role_permissions" }
|
||||||
137
internal/handler/rbac_handler.go
Normal file
137
internal/handler/rbac_handler.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"eslogad-be/internal/contract"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RBACService interface {
|
||||||
|
CreatePermission(ctx context.Context, req *contract.CreatePermissionRequest) (*contract.PermissionResponse, error)
|
||||||
|
UpdatePermission(ctx context.Context, id uuid.UUID, req *contract.UpdatePermissionRequest) (*contract.PermissionResponse, error)
|
||||||
|
DeletePermission(ctx context.Context, id uuid.UUID) error
|
||||||
|
ListPermissions(ctx context.Context) (*contract.ListPermissionsResponse, error)
|
||||||
|
|
||||||
|
CreateRole(ctx context.Context, req *contract.CreateRoleRequest) (*contract.RoleWithPermissionsResponse, error)
|
||||||
|
UpdateRole(ctx context.Context, id uuid.UUID, req *contract.UpdateRoleRequest) (*contract.RoleWithPermissionsResponse, error)
|
||||||
|
DeleteRole(ctx context.Context, id uuid.UUID) error
|
||||||
|
ListRoles(ctx context.Context) (*contract.ListRolesResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RBACHandler struct{ svc RBACService }
|
||||||
|
|
||||||
|
func NewRBACHandler(svc RBACService) *RBACHandler { return &RBACHandler{svc: svc} }
|
||||||
|
|
||||||
|
func (h *RBACHandler) CreatePermission(c *gin.Context) {
|
||||||
|
var req contract.CreatePermissionRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := h.svc.CreatePermission(c.Request.Context(), &req)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RBACHandler) UpdatePermission(c *gin.Context) {
|
||||||
|
id, err := uuid.Parse(c.Param("id"))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req contract.UpdatePermissionRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := h.svc.UpdatePermission(c.Request.Context(), id, &req)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RBACHandler) DeletePermission(c *gin.Context) {
|
||||||
|
id, err := uuid.Parse(c.Param("id"))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := h.svc.DeletePermission(c.Request.Context(), id); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "deleted"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RBACHandler) ListPermissions(c *gin.Context) {
|
||||||
|
resp, err := h.svc.ListPermissions(c.Request.Context())
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RBACHandler) CreateRole(c *gin.Context) {
|
||||||
|
var req contract.CreateRoleRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := h.svc.CreateRole(c.Request.Context(), &req)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RBACHandler) UpdateRole(c *gin.Context) {
|
||||||
|
id, err := uuid.Parse(c.Param("id"))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req contract.UpdateRoleRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := h.svc.UpdateRole(c.Request.Context(), id, &req)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RBACHandler) DeleteRole(c *gin.Context) {
|
||||||
|
id, err := uuid.Parse(c.Param("id"))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := h.svc.DeleteRole(c.Request.Context(), id); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "deleted"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RBACHandler) ListRoles(c *gin.Context) {
|
||||||
|
resp, err := h.svc.ListRoles(c.Request.Context())
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||||
|
}
|
||||||
97
internal/repository/rbac_repository.go
Normal file
97
internal/repository/rbac_repository.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"eslogad-be/internal/entities"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RBACRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRBACRepository(db *gorm.DB) *RBACRepository { return &RBACRepository{db: db} }
|
||||||
|
|
||||||
|
// Permissions
|
||||||
|
func (r *RBACRepository) CreatePermission(ctx context.Context, p *entities.Permission) error {
|
||||||
|
return r.db.WithContext(ctx).Create(p).Error
|
||||||
|
}
|
||||||
|
func (r *RBACRepository) UpdatePermission(ctx context.Context, p *entities.Permission) error {
|
||||||
|
return r.db.WithContext(ctx).Model(&entities.Permission{}).Where("id = ?", p.ID).Updates(p).Error
|
||||||
|
}
|
||||||
|
func (r *RBACRepository) DeletePermission(ctx context.Context, id uuid.UUID) error {
|
||||||
|
return r.db.WithContext(ctx).Delete(&entities.Permission{}, "id = ?", id).Error
|
||||||
|
}
|
||||||
|
func (r *RBACRepository) ListPermissions(ctx context.Context) ([]entities.Permission, error) {
|
||||||
|
var perms []entities.Permission
|
||||||
|
if err := r.db.WithContext(ctx).Order("code ASC").Find(&perms).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return perms, nil
|
||||||
|
}
|
||||||
|
func (r *RBACRepository) GetPermissionByCode(ctx context.Context, code string) (*entities.Permission, error) {
|
||||||
|
var p entities.Permission
|
||||||
|
if err := r.db.WithContext(ctx).First(&p, "code = ?", code).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roles
|
||||||
|
func (r *RBACRepository) CreateRole(ctx context.Context, role *entities.Role) error {
|
||||||
|
return r.db.WithContext(ctx).Create(role).Error
|
||||||
|
}
|
||||||
|
func (r *RBACRepository) UpdateRole(ctx context.Context, role *entities.Role) error {
|
||||||
|
return r.db.WithContext(ctx).Model(&entities.Role{}).Where("id = ?", role.ID).Updates(role).Error
|
||||||
|
}
|
||||||
|
func (r *RBACRepository) DeleteRole(ctx context.Context, id uuid.UUID) error {
|
||||||
|
return r.db.WithContext(ctx).Delete(&entities.Role{}, "id = ?", id).Error
|
||||||
|
}
|
||||||
|
func (r *RBACRepository) ListRoles(ctx context.Context) ([]entities.Role, error) {
|
||||||
|
var roles []entities.Role
|
||||||
|
if err := r.db.WithContext(ctx).Order("name ASC").Find(&roles).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return roles, nil
|
||||||
|
}
|
||||||
|
func (r *RBACRepository) GetRoleByCode(ctx context.Context, code string) (*entities.Role, error) {
|
||||||
|
var role entities.Role
|
||||||
|
if err := r.db.WithContext(ctx).First(&role, "code = ?", code).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &role, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RBACRepository) SetRolePermissionsByCodes(ctx context.Context, roleID uuid.UUID, permCodes []string) error {
|
||||||
|
if err := r.db.WithContext(ctx).Where("role_id = ?", roleID).Delete(&entities.RolePermission{}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(permCodes) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var perms []entities.Permission
|
||||||
|
if err := r.db.WithContext(ctx).Where("code IN ?", permCodes).Find(&perms).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pairs := make([]entities.RolePermission, 0, len(perms))
|
||||||
|
for _, p := range perms {
|
||||||
|
pairs = append(pairs, entities.RolePermission{RoleID: roleID, PermissionID: p.ID})
|
||||||
|
}
|
||||||
|
return r.db.WithContext(ctx).Create(&pairs).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RBACRepository) GetPermissionsByRoleID(ctx context.Context, roleID uuid.UUID) ([]entities.Permission, error) {
|
||||||
|
var perms []entities.Permission
|
||||||
|
if err := r.db.WithContext(ctx).
|
||||||
|
Table("permissions p").
|
||||||
|
Select("p.*").
|
||||||
|
Joins("JOIN role_permissions rp ON rp.permission_id = p.id").
|
||||||
|
Where("rp.role_id = ?", roleID).
|
||||||
|
Find(&perms).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return perms, nil
|
||||||
|
}
|
||||||
@ -18,3 +18,15 @@ type FileHandler interface {
|
|||||||
UploadProfileAvatar(c *gin.Context)
|
UploadProfileAvatar(c *gin.Context)
|
||||||
UploadDocument(c *gin.Context)
|
UploadDocument(c *gin.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RBACHandler interface {
|
||||||
|
CreatePermission(c *gin.Context)
|
||||||
|
UpdatePermission(c *gin.Context)
|
||||||
|
DeletePermission(c *gin.Context)
|
||||||
|
ListPermissions(c *gin.Context)
|
||||||
|
|
||||||
|
CreateRole(c *gin.Context)
|
||||||
|
UpdateRole(c *gin.Context)
|
||||||
|
DeleteRole(c *gin.Context)
|
||||||
|
ListRoles(c *gin.Context)
|
||||||
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ type Router struct {
|
|||||||
authMiddleware AuthMiddleware
|
authMiddleware AuthMiddleware
|
||||||
userHandler UserHandler
|
userHandler UserHandler
|
||||||
fileHandler FileHandler
|
fileHandler FileHandler
|
||||||
|
rbacHandler RBACHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(
|
func NewRouter(
|
||||||
@ -23,6 +24,7 @@ func NewRouter(
|
|||||||
healthHandler HealthHandler,
|
healthHandler HealthHandler,
|
||||||
userHandler UserHandler,
|
userHandler UserHandler,
|
||||||
fileHandler FileHandler,
|
fileHandler FileHandler,
|
||||||
|
rbacHandler RBACHandler,
|
||||||
) *Router {
|
) *Router {
|
||||||
return &Router{
|
return &Router{
|
||||||
config: cfg,
|
config: cfg,
|
||||||
@ -31,6 +33,7 @@ func NewRouter(
|
|||||||
healthHandler: healthHandler,
|
healthHandler: healthHandler,
|
||||||
userHandler: userHandler,
|
userHandler: userHandler,
|
||||||
fileHandler: fileHandler,
|
fileHandler: fileHandler,
|
||||||
|
rbacHandler: rbacHandler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,5 +80,18 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
|||||||
{
|
{
|
||||||
files.POST("/documents", r.fileHandler.UploadDocument)
|
files.POST("/documents", r.fileHandler.UploadDocument)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rbac := v1.Group("/rbac")
|
||||||
|
rbac.Use(r.authMiddleware.RequireAuth())
|
||||||
|
{
|
||||||
|
rbac.GET("/permissions", r.rbacHandler.ListPermissions)
|
||||||
|
rbac.POST("/permissions", r.rbacHandler.CreatePermission)
|
||||||
|
rbac.PUT("/permissions/:id", r.rbacHandler.UpdatePermission)
|
||||||
|
rbac.DELETE("/permissions/:id", r.rbacHandler.DeletePermission)
|
||||||
|
rbac.GET("/roles", r.rbacHandler.ListRoles)
|
||||||
|
rbac.POST("/roles", r.rbacHandler.CreateRole)
|
||||||
|
rbac.PUT("/roles/:id", r.rbacHandler.UpdateRole)
|
||||||
|
rbac.DELETE("/roles/:id", r.rbacHandler.DeleteRole)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
128
internal/service/rbac_service.go
Normal file
128
internal/service/rbac_service.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"eslogad-be/internal/contract"
|
||||||
|
"eslogad-be/internal/entities"
|
||||||
|
"eslogad-be/internal/repository"
|
||||||
|
"eslogad-be/internal/transformer"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RBACServiceImpl struct {
|
||||||
|
repo *repository.RBACRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRBACService(repo *repository.RBACRepository) *RBACServiceImpl {
|
||||||
|
return &RBACServiceImpl{repo: repo}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permissions
|
||||||
|
func (s *RBACServiceImpl) CreatePermission(ctx context.Context, req *contract.CreatePermissionRequest) (*contract.PermissionResponse, error) {
|
||||||
|
p := &entities.Permission{Code: req.Code}
|
||||||
|
if req.Description != nil {
|
||||||
|
p.Description = *req.Description
|
||||||
|
}
|
||||||
|
if err := s.repo.CreatePermission(ctx, p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &contract.PermissionResponse{ID: p.ID, Code: p.Code, Description: &p.Description, CreatedAt: p.CreatedAt, UpdatedAt: p.UpdatedAt}, nil
|
||||||
|
}
|
||||||
|
func (s *RBACServiceImpl) UpdatePermission(ctx context.Context, id uuid.UUID, req *contract.UpdatePermissionRequest) (*contract.PermissionResponse, error) {
|
||||||
|
p := &entities.Permission{ID: id}
|
||||||
|
if req.Code != nil {
|
||||||
|
p.Code = *req.Code
|
||||||
|
}
|
||||||
|
if req.Description != nil {
|
||||||
|
p.Description = *req.Description
|
||||||
|
}
|
||||||
|
if err := s.repo.UpdatePermission(ctx, p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// fetch full row
|
||||||
|
perms, err := s.repo.ListPermissions(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, x := range perms {
|
||||||
|
if x.ID == id {
|
||||||
|
return &contract.PermissionResponse{ID: x.ID, Code: x.Code, Description: &x.Description, CreatedAt: x.CreatedAt, UpdatedAt: x.UpdatedAt}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (s *RBACServiceImpl) DeletePermission(ctx context.Context, id uuid.UUID) error {
|
||||||
|
return s.repo.DeletePermission(ctx, id)
|
||||||
|
}
|
||||||
|
func (s *RBACServiceImpl) ListPermissions(ctx context.Context) (*contract.ListPermissionsResponse, error) {
|
||||||
|
perms, err := s.repo.ListPermissions(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &contract.ListPermissionsResponse{Permissions: transformer.PermissionsToContract(perms)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roles
|
||||||
|
func (s *RBACServiceImpl) CreateRole(ctx context.Context, req *contract.CreateRoleRequest) (*contract.RoleWithPermissionsResponse, error) {
|
||||||
|
role := &entities.Role{Name: req.Name, Code: req.Code}
|
||||||
|
if req.Description != nil {
|
||||||
|
role.Description = *req.Description
|
||||||
|
}
|
||||||
|
if err := s.repo.CreateRole(ctx, role); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(req.PermissionCodes) > 0 {
|
||||||
|
_ = s.repo.SetRolePermissionsByCodes(ctx, role.ID, req.PermissionCodes)
|
||||||
|
}
|
||||||
|
perms, _ := s.repo.GetPermissionsByRoleID(ctx, role.ID)
|
||||||
|
resp := transformer.RoleWithPermissionsToContract(*role, perms)
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
func (s *RBACServiceImpl) UpdateRole(ctx context.Context, id uuid.UUID, req *contract.UpdateRoleRequest) (*contract.RoleWithPermissionsResponse, error) {
|
||||||
|
role := &entities.Role{ID: id}
|
||||||
|
if req.Name != nil {
|
||||||
|
role.Name = *req.Name
|
||||||
|
}
|
||||||
|
if req.Code != nil {
|
||||||
|
role.Code = *req.Code
|
||||||
|
}
|
||||||
|
if req.Description != nil {
|
||||||
|
role.Description = *req.Description
|
||||||
|
}
|
||||||
|
if err := s.repo.UpdateRole(ctx, role); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if req.PermissionCodes != nil {
|
||||||
|
_ = s.repo.SetRolePermissionsByCodes(ctx, id, *req.PermissionCodes)
|
||||||
|
}
|
||||||
|
perms, _ := s.repo.GetPermissionsByRoleID(ctx, id)
|
||||||
|
// fetch updated role
|
||||||
|
roles, err := s.repo.ListRoles(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, r := range roles {
|
||||||
|
if r.ID == id {
|
||||||
|
resp := transformer.RoleWithPermissionsToContract(r, perms)
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (s *RBACServiceImpl) DeleteRole(ctx context.Context, id uuid.UUID) error {
|
||||||
|
return s.repo.DeleteRole(ctx, id)
|
||||||
|
}
|
||||||
|
func (s *RBACServiceImpl) ListRoles(ctx context.Context) (*contract.ListRolesResponse, error) {
|
||||||
|
roles, err := s.repo.ListRoles(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out := make([]contract.RoleWithPermissionsResponse, 0, len(roles))
|
||||||
|
for _, r := range roles {
|
||||||
|
perms, _ := s.repo.GetPermissionsByRoleID(ctx, r.ID)
|
||||||
|
out = append(out, transformer.RoleWithPermissionsToContract(r, perms))
|
||||||
|
}
|
||||||
|
return &contract.ListRolesResponse{Roles: out}, nil
|
||||||
|
}
|
||||||
@ -170,3 +170,23 @@ func TitlesToContract(titles []entities.Title) []contract.TitleResponse {
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PermissionsToContract(perms []entities.Permission) []contract.PermissionResponse {
|
||||||
|
out := make([]contract.PermissionResponse, 0, len(perms))
|
||||||
|
for _, p := range perms {
|
||||||
|
out = append(out, contract.PermissionResponse{ID: p.ID, Code: p.Code, Description: &p.Description, CreatedAt: p.CreatedAt, UpdatedAt: p.UpdatedAt})
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func RoleWithPermissionsToContract(role entities.Role, perms []entities.Permission) contract.RoleWithPermissionsResponse {
|
||||||
|
return contract.RoleWithPermissionsResponse{
|
||||||
|
ID: role.ID,
|
||||||
|
Name: role.Name,
|
||||||
|
Code: role.Code,
|
||||||
|
Description: &role.Description,
|
||||||
|
Permissions: PermissionsToContract(perms),
|
||||||
|
CreatedAt: role.CreatedAt,
|
||||||
|
UpdatedAt: role.UpdatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user