add role based permissions
This commit is contained in:
parent
7d5f061a1b
commit
6da48504fa
@ -9,9 +9,8 @@ import (
|
||||
type PermissionResponse struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Code string `json:"code"`
|
||||
Action string `json:"action,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type CreatePermissionRequest struct {
|
||||
@ -55,3 +54,54 @@ type UpdateRoleRequest struct {
|
||||
type ListRolesResponse struct {
|
||||
Roles []RoleWithPermissionsResponse `json:"roles"`
|
||||
}
|
||||
|
||||
// Module contracts
|
||||
type ModuleResponse struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type ModuleWithPermissionsResponse struct {
|
||||
Module ModuleResponse `json:"module"`
|
||||
Permissions []PermissionResponse `json:"permissions"`
|
||||
}
|
||||
|
||||
type PermissionsGroupedResponse struct {
|
||||
Data []ModuleWithPermissionsResponse `json:"data"`
|
||||
}
|
||||
|
||||
// New Role contracts for the required API
|
||||
type ModulePermissionInput struct {
|
||||
Module string `json:"module" binding:"required"`
|
||||
Actions []string `json:"actions" binding:"required"`
|
||||
}
|
||||
|
||||
type CreateOrUpdateRoleRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Code string `json:"code" binding:"required"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Permissions []ModulePermissionInput `json:"permissions" binding:"required"`
|
||||
}
|
||||
|
||||
type RoleDetailResponse struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Permissions []RolePermissionModuleResponse `json:"permissions"`
|
||||
}
|
||||
|
||||
type RolePermissionModuleResponse struct {
|
||||
Module ModuleResponse `json:"module"`
|
||||
Actions []PermissionActionResponse `json:"actions"`
|
||||
}
|
||||
|
||||
type PermissionActionResponse struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Action string `json:"action"`
|
||||
Code string `json:"code"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
18
internal/entities/module.go
Normal file
18
internal/entities/module.go
Normal file
@ -0,0 +1,18 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Code string `gorm:"uniqueIndex;not null" json:"code"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
Permissions []Permission `gorm:"foreignKey:ModuleID" json:"permissions,omitempty"`
|
||||
}
|
||||
|
||||
func (Module) TableName() string { return "modules" }
|
||||
@ -18,11 +18,14 @@ type Role struct {
|
||||
func (Role) TableName() string { return "roles" }
|
||||
|
||||
type Permission struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
Code string `gorm:"uniqueIndex;not null" json:"code"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
ModuleID *uuid.UUID `gorm:"type:uuid" json:"module_id"`
|
||||
Module *Module `gorm:"foreignKey:ModuleID" json:"module,omitempty"`
|
||||
Action string `json:"action"`
|
||||
Code string `gorm:"uniqueIndex;not null" json:"code"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (Permission) TableName() string { return "permissions" }
|
||||
|
||||
@ -20,6 +20,11 @@ type RBACService interface {
|
||||
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)
|
||||
|
||||
// New methods
|
||||
GetPermissionsGrouped(ctx context.Context) (*contract.PermissionsGroupedResponse, error)
|
||||
CreateOrUpdateRole(ctx context.Context, req *contract.CreateOrUpdateRoleRequest) (*contract.RoleDetailResponse, error)
|
||||
GetRoleDetail(ctx context.Context, roleID uuid.UUID) (*contract.RoleDetailResponse, error)
|
||||
}
|
||||
|
||||
type RBACHandler struct{ svc RBACService }
|
||||
@ -135,3 +140,43 @@ func (h *RBACHandler) ListRoles(c *gin.Context) {
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// New handlers for the required API endpoints
|
||||
func (h *RBACHandler) GetPermissionsGrouped(c *gin.Context) {
|
||||
resp, err := h.svc.GetPermissionsGrouped(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *RBACHandler) CreateOrUpdateRole(c *gin.Context) {
|
||||
var req contract.CreateOrUpdateRoleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body: " + err.Error(), Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.CreateOrUpdateRole(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *RBACHandler) GetRoleDetail(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
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetRoleDetail(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ func (r *RBACRepository) DeletePermission(ctx context.Context, id uuid.UUID) err
|
||||
}
|
||||
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 {
|
||||
if err := r.db.WithContext(ctx).Preload("Module").Order("code ASC").Find(&perms).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return perms, nil
|
||||
@ -86,6 +86,7 @@ func (r *RBACRepository) SetRolePermissionsByCodes(ctx context.Context, roleID u
|
||||
func (r *RBACRepository) GetPermissionsByRoleID(ctx context.Context, roleID uuid.UUID) ([]entities.Permission, error) {
|
||||
var perms []entities.Permission
|
||||
if err := r.db.WithContext(ctx).
|
||||
Preload("Module").
|
||||
Table("permissions p").
|
||||
Select("p.*").
|
||||
Joins("JOIN role_permissions rp ON rp.permission_id = p.id").
|
||||
@ -95,3 +96,80 @@ func (r *RBACRepository) GetPermissionsByRoleID(ctx context.Context, roleID uuid
|
||||
}
|
||||
return perms, nil
|
||||
}
|
||||
|
||||
// Modules
|
||||
func (r *RBACRepository) CreateModule(ctx context.Context, m *entities.Module) error {
|
||||
return r.db.WithContext(ctx).Create(m).Error
|
||||
}
|
||||
|
||||
func (r *RBACRepository) UpdateModule(ctx context.Context, m *entities.Module) error {
|
||||
return r.db.WithContext(ctx).Model(&entities.Module{}).Where("id = ?", m.ID).Updates(m).Error
|
||||
}
|
||||
|
||||
func (r *RBACRepository) DeleteModule(ctx context.Context, id uuid.UUID) error {
|
||||
return r.db.WithContext(ctx).Delete(&entities.Module{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
func (r *RBACRepository) ListModules(ctx context.Context) ([]entities.Module, error) {
|
||||
var modules []entities.Module
|
||||
if err := r.db.WithContext(ctx).Order("name ASC").Find(&modules).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
func (r *RBACRepository) GetModuleByCode(ctx context.Context, code string) (*entities.Module, error) {
|
||||
var m entities.Module
|
||||
if err := r.db.WithContext(ctx).First(&m, "code = ?", code).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (r *RBACRepository) GetModuleByID(ctx context.Context, id uuid.UUID) (*entities.Module, error) {
|
||||
var m entities.Module
|
||||
if err := r.db.WithContext(ctx).First(&m, "id = ?", id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (r *RBACRepository) GetPermissionsGroupedByModule(ctx context.Context) ([]entities.Module, error) {
|
||||
var modules []entities.Module
|
||||
if err := r.db.WithContext(ctx).
|
||||
Preload("Permissions", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Order("action ASC")
|
||||
}).
|
||||
Find(&modules).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
func (r *RBACRepository) GetRoleByID(ctx context.Context, id uuid.UUID) (*entities.Role, error) {
|
||||
var role entities.Role
|
||||
if err := r.db.WithContext(ctx).First(&role, "id = ?", id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
func (r *RBACRepository) SetRolePermissionsByIDs(ctx context.Context, roleID uuid.UUID, permissionIDs []uuid.UUID) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// Delete existing permissions
|
||||
if err := tx.WithContext(ctx).Where("role_id = ?", roleID).Delete(&entities.RolePermission{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add new permissions
|
||||
if len(permissionIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
pairs := make([]entities.RolePermission, 0, len(permissionIDs))
|
||||
for _, permID := range permissionIDs {
|
||||
pairs = append(pairs, entities.RolePermission{RoleID: roleID, PermissionID: permID})
|
||||
}
|
||||
return tx.WithContext(ctx).Create(&pairs).Error
|
||||
})
|
||||
}
|
||||
|
||||
@ -31,6 +31,11 @@ type RBACHandler interface {
|
||||
UpdateRole(c *gin.Context)
|
||||
DeleteRole(c *gin.Context)
|
||||
ListRoles(c *gin.Context)
|
||||
|
||||
// New methods
|
||||
GetPermissionsGrouped(c *gin.Context)
|
||||
CreateOrUpdateRole(c *gin.Context)
|
||||
GetRoleDetail(c *gin.Context)
|
||||
}
|
||||
|
||||
type MasterHandler interface {
|
||||
|
||||
@ -113,6 +113,14 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
||||
rbac.DELETE("/roles/:id", r.rbacHandler.DeleteRole)
|
||||
}
|
||||
|
||||
roles := v1.Group("/roles")
|
||||
roles.Use(r.authMiddleware.RequireAuth())
|
||||
{
|
||||
roles.POST("", r.rbacHandler.CreateOrUpdateRole)
|
||||
roles.GET("/permissions", r.rbacHandler.GetPermissionsGrouped)
|
||||
roles.GET("/:id", r.rbacHandler.GetRoleDetail)
|
||||
}
|
||||
|
||||
master := v1.Group("/master")
|
||||
master.Use(r.authMiddleware.RequireAuth())
|
||||
{
|
||||
|
||||
@ -874,7 +874,6 @@ func transformLetterToResponse(letter *entities.LetterOutgoing) *contract.Outgoi
|
||||
}
|
||||
}
|
||||
|
||||
// Include Approvals if loaded
|
||||
if len(letter.Approvals) > 0 {
|
||||
resp.Approvals = make([]contract.OutgoingLetterApprovalResponse, len(letter.Approvals))
|
||||
for i, approval := range letter.Approvals {
|
||||
|
||||
@ -28,7 +28,12 @@ func (s *RBACServiceImpl) CreatePermission(ctx context.Context, req *contract.Cr
|
||||
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
|
||||
return &contract.PermissionResponse{
|
||||
ID: p.ID,
|
||||
Code: p.Code,
|
||||
Action: p.Action,
|
||||
Description: &p.Description,
|
||||
}, nil
|
||||
}
|
||||
func (s *RBACServiceImpl) UpdatePermission(ctx context.Context, id uuid.UUID, req *contract.UpdatePermissionRequest) (*contract.PermissionResponse, error) {
|
||||
p := &entities.Permission{ID: id}
|
||||
@ -48,7 +53,12 @@ func (s *RBACServiceImpl) UpdatePermission(ctx context.Context, id uuid.UUID, re
|
||||
}
|
||||
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 &contract.PermissionResponse{
|
||||
ID: x.ID,
|
||||
Code: x.Code,
|
||||
Action: x.Action,
|
||||
Description: &x.Description,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
@ -126,3 +136,151 @@ func (s *RBACServiceImpl) ListRoles(ctx context.Context) (*contract.ListRolesRes
|
||||
}
|
||||
return &contract.ListRolesResponse{Roles: out}, nil
|
||||
}
|
||||
|
||||
// New methods for the required API endpoints
|
||||
func (s *RBACServiceImpl) GetPermissionsGrouped(ctx context.Context) (*contract.PermissionsGroupedResponse, error) {
|
||||
modules, err := s.repo.ListModules(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]contract.ModuleWithPermissionsResponse, 0, len(modules))
|
||||
for _, module := range modules {
|
||||
perms, err := s.repo.ListPermissions(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modulePerms := make([]contract.PermissionResponse, 0)
|
||||
for _, perm := range perms {
|
||||
if perm.ModuleID != nil && *perm.ModuleID == module.ID {
|
||||
modulePerms = append(modulePerms, contract.PermissionResponse{
|
||||
ID: perm.ID,
|
||||
Code: perm.Code,
|
||||
Action: perm.Action,
|
||||
Description: &perm.Description,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, contract.ModuleWithPermissionsResponse{
|
||||
Module: contract.ModuleResponse{
|
||||
ID: module.ID,
|
||||
Name: module.Name,
|
||||
Code: module.Code,
|
||||
},
|
||||
Permissions: modulePerms,
|
||||
})
|
||||
}
|
||||
|
||||
return &contract.PermissionsGroupedResponse{Data: result}, nil
|
||||
}
|
||||
|
||||
func (s *RBACServiceImpl) CreateOrUpdateRole(ctx context.Context, req *contract.CreateOrUpdateRoleRequest) (*contract.RoleDetailResponse, error) {
|
||||
// Check if role exists
|
||||
existingRole, _ := s.repo.GetRoleByCode(ctx, req.Code)
|
||||
|
||||
var role *entities.Role
|
||||
if existingRole != nil {
|
||||
// Update existing role
|
||||
role = existingRole
|
||||
role.Name = req.Name
|
||||
role.Description = req.Description
|
||||
if err := s.repo.UpdateRole(ctx, role); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Create new role
|
||||
role = &entities.Role{
|
||||
Name: req.Name,
|
||||
Code: req.Code,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := s.repo.CreateRole(ctx, role); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Set permissions based on module and actions
|
||||
permissionIDs := make([]uuid.UUID, 0)
|
||||
for _, modPerm := range req.Permissions {
|
||||
_, err := s.repo.GetModuleByCode(ctx, modPerm.Module)
|
||||
if err != nil {
|
||||
continue // Skip if module not found
|
||||
}
|
||||
|
||||
for _, action := range modPerm.Actions {
|
||||
permCode := modPerm.Module + "_" + action
|
||||
perm, err := s.repo.GetPermissionByCode(ctx, permCode)
|
||||
if err == nil && perm != nil {
|
||||
permissionIDs = append(permissionIDs, perm.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.repo.SetRolePermissionsByIDs(ctx, role.ID, permissionIDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build response
|
||||
return s.GetRoleDetail(ctx, role.ID)
|
||||
}
|
||||
|
||||
func (s *RBACServiceImpl) GetRoleDetail(ctx context.Context, roleID uuid.UUID) (*contract.RoleDetailResponse, error) {
|
||||
role, err := s.repo.GetRoleByID(ctx, roleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
permissions, err := s.repo.GetPermissionsByRoleID(ctx, roleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Group permissions by module
|
||||
moduleMap := make(map[uuid.UUID]*contract.RolePermissionModuleResponse)
|
||||
|
||||
for _, perm := range permissions {
|
||||
if perm.ModuleID == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, exists := moduleMap[*perm.ModuleID]; !exists {
|
||||
if perm.Module != nil {
|
||||
moduleMap[*perm.ModuleID] = &contract.RolePermissionModuleResponse{
|
||||
Module: contract.ModuleResponse{
|
||||
ID: perm.Module.ID,
|
||||
Name: perm.Module.Name,
|
||||
Code: perm.Module.Code,
|
||||
},
|
||||
Actions: []contract.PermissionActionResponse{},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if modResp, exists := moduleMap[*perm.ModuleID]; exists {
|
||||
modResp.Actions = append(modResp.Actions, contract.PermissionActionResponse{
|
||||
ID: perm.ID,
|
||||
Action: perm.Action,
|
||||
Code: perm.Code,
|
||||
Description: perm.Description,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Convert map to slice
|
||||
permissionModules := make([]contract.RolePermissionModuleResponse, 0, len(moduleMap))
|
||||
for _, modResp := range moduleMap {
|
||||
permissionModules = append(permissionModules, *modResp)
|
||||
}
|
||||
|
||||
return &contract.RoleDetailResponse{
|
||||
ID: role.ID,
|
||||
Name: role.Name,
|
||||
Code: role.Code,
|
||||
Description: role.Description,
|
||||
CreatedAt: role.CreatedAt,
|
||||
UpdatedAt: role.UpdatedAt,
|
||||
Permissions: permissionModules,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -178,7 +178,12 @@ func TitlesToContract(titles []entities.Title) []contract.TitleResponse {
|
||||
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})
|
||||
out = append(out, contract.PermissionResponse{
|
||||
ID: p.ID,
|
||||
Code: p.Code,
|
||||
Action: p.Action,
|
||||
Description: &p.Description,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
15
migrations/000014_modules_and_permissions_update.down.sql
Normal file
15
migrations/000014_modules_and_permissions_update.down.sql
Normal file
@ -0,0 +1,15 @@
|
||||
-- Rollback modules and permissions update
|
||||
|
||||
-- Remove the new structured permissions
|
||||
DELETE FROM permissions WHERE code LIKE '%_READ' OR code LIKE '%_WRITE' OR code LIKE '%_CREATE' OR code LIKE '%_DELETE';
|
||||
|
||||
-- Drop indexes
|
||||
DROP INDEX IF EXISTS idx_permissions_module_id;
|
||||
|
||||
-- Remove columns from permissions table
|
||||
ALTER TABLE permissions
|
||||
DROP COLUMN IF EXISTS module_id,
|
||||
DROP COLUMN IF EXISTS action;
|
||||
|
||||
-- Drop modules table
|
||||
DROP TABLE IF EXISTS modules CASCADE;
|
||||
65
migrations/000014_modules_and_permissions_update.up.sql
Normal file
65
migrations/000014_modules_and_permissions_update.up.sql
Normal file
@ -0,0 +1,65 @@
|
||||
-- Add modules table and update permissions structure
|
||||
|
||||
-- Create modules table
|
||||
CREATE TABLE IF NOT EXISTS modules (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TRIGGER trg_modules_updated_at
|
||||
BEFORE UPDATE ON modules
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- Add module_id and action columns to permissions table
|
||||
ALTER TABLE permissions
|
||||
ADD COLUMN IF NOT EXISTS module_id UUID REFERENCES modules(id) ON DELETE CASCADE,
|
||||
ADD COLUMN IF NOT EXISTS action TEXT;
|
||||
|
||||
-- Create index on module_id for better query performance
|
||||
CREATE INDEX IF NOT EXISTS idx_permissions_module_id ON permissions(module_id);
|
||||
|
||||
-- Seed initial modules
|
||||
INSERT INTO modules (name, code) VALUES
|
||||
('User Management', 'USER_MANAGEMENT'),
|
||||
('Content Management', 'CONTENT_MANAGEMENT'),
|
||||
('Letter Management', 'LETTER_MANAGEMENT'),
|
||||
('Disposition Management', 'DISPOSITION_MANAGEMENT'),
|
||||
('Reporting', 'REPORTING'),
|
||||
('Settings', 'SETTINGS')
|
||||
ON CONFLICT (code) DO NOTHING;
|
||||
|
||||
-- Update existing permissions to include module_id and action
|
||||
-- This is a sample mapping - adjust based on your existing permission codes
|
||||
UPDATE permissions SET
|
||||
module_id = (SELECT id FROM modules WHERE code = 'LETTER_MANAGEMENT'),
|
||||
action = 'READ'
|
||||
WHERE code LIKE 'letter.%' AND code LIKE '%.view';
|
||||
|
||||
UPDATE permissions SET
|
||||
module_id = (SELECT id FROM modules WHERE code = 'LETTER_MANAGEMENT'),
|
||||
action = 'WRITE'
|
||||
WHERE code LIKE 'letter.%' AND code LIKE '%.edit';
|
||||
|
||||
UPDATE permissions SET
|
||||
module_id = (SELECT id FROM modules WHERE code = 'LETTER_MANAGEMENT'),
|
||||
action = 'CREATE'
|
||||
WHERE code LIKE 'letter.%' AND code LIKE '%.create';
|
||||
|
||||
UPDATE permissions SET
|
||||
module_id = (SELECT id FROM modules WHERE code = 'LETTER_MANAGEMENT'),
|
||||
action = 'DELETE'
|
||||
WHERE code LIKE 'letter.%' AND code LIKE '%.delete';
|
||||
|
||||
-- Insert new structured permissions for each module
|
||||
INSERT INTO permissions (module_id, action, code, description)
|
||||
SELECT
|
||||
m.id,
|
||||
a.action,
|
||||
CONCAT(m.code, '_', a.action),
|
||||
CONCAT('Can ', LOWER(a.action), ' ', LOWER(m.name))
|
||||
FROM modules m
|
||||
CROSS JOIN (VALUES ('READ'), ('WRITE'), ('CREATE'), ('DELETE')) AS a(action)
|
||||
ON CONFLICT (code) DO NOTHING;
|
||||
Loading…
x
Reference in New Issue
Block a user