adding new feature for updating new roles

This commit is contained in:
areeqakbr 2025-06-19 19:35:02 +07:00
parent f84ec65fbb
commit e35edf3172
4 changed files with 277 additions and 50 deletions

View File

@ -1,89 +1,263 @@
package controller package controller
import ( import (
"net/http" "errors"
"users_management/m/middleware" "net/http"
"users_management/m/usecase" "users_management/m/middleware"
"users_management/m/utils/common" "users_management/m/usecase"
"users_management/m/utils/common"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
) )
type UserManagementController struct { type UserRoleManagementController struct {
userUC usecase.UsersUsecase userUC usecase.UsersUsecase
rg *gin.RouterGroup rg *gin.RouterGroup
} }
func NewUserManagementController(userUC usecase.UsersUsecase, rg *gin.RouterGroup) *UserManagementController { func NewUserRoleManagementController(userUC usecase.UsersUsecase, rg *gin.RouterGroup) *UserRoleManagementController {
return &UserManagementController{ return &UserRoleManagementController{
userUC: userUC, userUC: userUC,
rg: rg, rg: rg,
} }
} }
func (c *UserManagementController) Route() { func (c *UserRoleManagementController) Route() {
users := c.rg.Group("/user-management") userRole := c.rg.Group("/user-role-management")
userRole.Use(middleware.RequireAdminRole()) // Only Admin and Super Admin can access
{ {
// Only superadmin can manage user roles userRole.PUT("/update/:id", c.updateUserRoleByID)
users.PUT("/role", middleware.RequireSuperAdminRole(), c.updateUserRole) userRole.PUT("/update-by-username", c.updateUserRoleByUsername)
userRole.GET("/users", c.getAllUsersWithRoles)
// Admins and superadmins can view all users userRole.GET("/available-roles", c.getAvailableRoles)
users.GET("/users", middleware.RequireAdminRole(), c.getAllUsers)
// Users can view their own profile
users.GET("/profile", c.getMyProfile)
} }
} }
type UpdateRoleRequest struct { type UpdateUserRoleByIDRequest struct {
NomorInduk string `json:"nomor_induk" binding:"required"` NewRole string `json:"new_role" binding:"required,oneof=Teknisi Admin"`
RoleName string `json:"role_name" binding:"required"`
} }
func (c *UserManagementController) updateUserRole(ctx *gin.Context) { type UpdateUserRoleByUsernameRequest struct {
var req UpdateRoleRequest Username string `json:"username" binding:"required"`
NewRole string `json:"new_role" binding:"required,oneof=Teknisi Admin"`
}
func (c *UserRoleManagementController) updateUserRoleByID(ctx *gin.Context) {
// Get user ID from URL parameter
userIDStr := ctx.Param("id")
userID, err := uuid.Parse(userIDStr)
if err != nil {
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid user ID format")
return
}
var req UpdateUserRoleByIDRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error()) common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
return return
} }
err := c.userUC.UpdateUserRole(req.NomorInduk, req.RoleName) // Get current user role to check permissions
if err != nil { currentUserRole, exists := ctx.Get("userRole")
common.ErrorResponses(ctx, http.StatusInternalServerError, err.Error())
return
}
common.SingleResponses(ctx, "User role updated successfully", nil)
}
func (c *UserManagementController) getAllUsers(ctx *gin.Context) {
users, err := c.userUC.GetAllUsers()
if err != nil {
common.ErrorResponses(ctx, http.StatusInternalServerError, err.Error())
return
}
common.SingleResponses(ctx, "Users retrieved successfully", users)
}
func (c *UserManagementController) getMyProfile(ctx *gin.Context) {
userID, exists := ctx.Get("userID")
if !exists { if !exists {
common.ErrorResponses(ctx, http.StatusUnauthorized, "User ID not found") common.ErrorResponses(ctx, http.StatusUnauthorized, "User role not found")
return return
} }
nomorInduk, ok := userID.(string) currentUserRoleStr, ok := currentUserRole.(string)
if !ok { if !ok {
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid user ID") common.ErrorResponses(ctx, http.StatusUnauthorized, "Invalid user role format")
return return
} }
user, err := c.userUC.GetUserByNomorInduk(nomorInduk) // Get target user to check current role and prevent invalid operations
targetUser, err := c.userUC.GetUserByID(userID)
if err != nil { if err != nil {
common.ErrorResponses(ctx, http.StatusNotFound, "User not found") common.ErrorResponses(ctx, http.StatusNotFound, "User not found")
return return
} }
common.SingleResponses(ctx, "User profile retrieved successfully", user) // Validate role change permissions
if err := c.validateRoleChange(currentUserRoleStr, targetUser.Role.Name, req.NewRole); err != nil {
common.ErrorResponses(ctx, http.StatusForbidden, err.Error())
return
}
// Update user role
err = c.userUC.UpdateUserRoleByID(userID, req.NewRole)
if err != nil {
common.ErrorResponses(ctx, http.StatusInternalServerError, "Failed to update user role: "+err.Error())
return
}
// Get updated user info
updatedUser, err := c.userUC.GetUserByID(userID)
if err != nil {
// Role was updated but couldn't fetch updated info
common.SingleResponses(ctx, "User role updated successfully", gin.H{
"user_id": userID,
"new_role": req.NewRole,
})
return
}
response := gin.H{
"user_id": updatedUser.ID,
"username": updatedUser.Username,
"name": updatedUser.Name,
"previous_role": targetUser.Role.Name,
"new_role": updatedUser.Role.Name,
"updated_by": ctx.GetString("userName"),
}
common.SingleResponses(ctx, "User role updated successfully", response)
}
func (c *UserRoleManagementController) updateUserRoleByUsername(ctx *gin.Context) {
var req UpdateUserRoleByUsernameRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
return
}
// Get current user role to check permissions
currentUserRole, exists := ctx.Get("userRole")
if !exists {
common.ErrorResponses(ctx, http.StatusUnauthorized, "User role not found")
return
}
currentUserRoleStr, ok := currentUserRole.(string)
if !ok {
common.ErrorResponses(ctx, http.StatusUnauthorized, "Invalid user role format")
return
}
// Get target user
targetUser, err := c.userUC.GetUserByUsernameWithStatus(req.Username)
if err != nil {
common.ErrorResponses(ctx, http.StatusNotFound, "User not found")
return
}
// Validate role change permissions
if err := c.validateRoleChange(currentUserRoleStr, targetUser.Role.Name, req.NewRole); err != nil {
common.ErrorResponses(ctx, http.StatusForbidden, err.Error())
return
}
// Update user role by username
err = c.userUC.UpdateUserRoleByUsername(req.Username, req.NewRole)
if err != nil {
common.ErrorResponses(ctx, http.StatusInternalServerError, "Failed to update user role: "+err.Error())
return
}
// Get updated user info
updatedUser, err := c.userUC.GetUserByUsernameWithStatus(req.Username)
if err != nil {
// Role was updated but couldn't fetch updated info
common.SingleResponses(ctx, "User role updated successfully", gin.H{
"username": req.Username,
"new_role": req.NewRole,
})
return
}
response := gin.H{
"user_id": updatedUser.ID,
"username": updatedUser.Username,
"name": updatedUser.Name,
"previous_role": targetUser.Role.Name,
"new_role": updatedUser.Role.Name,
"updated_by": ctx.GetString("userName"),
}
common.SingleResponses(ctx, "User role updated successfully", response)
}
func (c *UserRoleManagementController) getAllUsersWithRoles(ctx *gin.Context) {
users, err := c.userUC.GetAllUsers()
if err != nil {
common.ErrorResponses(ctx, http.StatusInternalServerError, "Failed to get users: "+err.Error())
return
}
// Filter and format response
var userList []gin.H
for _, user := range users {
// Only show approved users
if user.Status == "approved" {
userInfo := gin.H{
"id": user.ID,
"username": user.Username,
"name": user.Name,
"nomor_induk": user.NomorInduk,
"role": user.Role.Name,
"status": user.Status,
"created_at": user.CreatedAt,
"updated_at": user.UpdatedAt,
}
userList = append(userList, userInfo)
}
}
response := gin.H{
"users": userList,
"total": len(userList),
}
common.SingleResponses(ctx, "Users retrieved successfully", response)
}
func (c *UserRoleManagementController) getAvailableRoles(ctx *gin.Context) {
roles := []gin.H{
{
"name": "Teknisi",
"description": "Technical user with limited administrative access",
"can_be_assigned_by": []string{"Admin", "Super Admin"},
},
{
"name": "Admin",
"description": "Administrative user with full system access except user management",
"can_be_assigned_by": []string{"Admin", "Super Admin"},
},
}
currentUserRole := ctx.GetString("userRole")
response := gin.H{
"available_roles": roles,
"your_role": currentUserRole,
"note": "You can update users between Teknisi and Admin roles only",
}
common.SingleResponses(ctx, "Available roles for assignment", response)
}
// Helper function to validate role change permissions
func (c *UserRoleManagementController) validateRoleChange(currentUserRole, targetCurrentRole, newRole string) error {
// Super Admin can change any role to Teknisi or Admin
if currentUserRole == "Super Admin" {
return nil
}
// Admin can change roles but with some restrictions
if currentUserRole == "Admin" {
// Admin cannot change Super Admin roles
if targetCurrentRole == "Super Admin" {
return errors.New("cannot modify Super Admin users")
}
// Admin cannot assign Super Admin role
if newRole == "Super Admin" {
return errors.New("cannot assign Super Admin role")
}
return nil
}
// Only Admin and Super Admin can change roles
return errors.New("insufficient permissions to change user roles")
} }

View File

@ -78,6 +78,7 @@ func (s *Server) setupController() {
protectedRegistration.PUT("/approve/:id", middleware.RequireAdminRole(), controller.NewUserRegistrationController(s.ucManager.NewUserUsecase(), protected).ApproveUser) protectedRegistration.PUT("/approve/:id", middleware.RequireAdminRole(), controller.NewUserRegistrationController(s.ucManager.NewUserUsecase(), protected).ApproveUser)
protectedRegistration.PUT("/reject/:id", middleware.RequireAdminRole(), controller.NewUserRegistrationController(s.ucManager.NewUserUsecase(), protected).RejectUser) protectedRegistration.PUT("/reject/:id", middleware.RequireAdminRole(), controller.NewUserRegistrationController(s.ucManager.NewUserUsecase(), protected).RejectUser)
} }
controller.NewUserRoleManagementController(s.ucManager.NewUserUsecase(), protected).Route()
controller.NewUsersController(s.ucManager.NewUserUsecase(), s.ucManager.NewAuthUsecase(), protected).Route() controller.NewUsersController(s.ucManager.NewUserUsecase(), s.ucManager.NewAuthUsecase(), protected).Route()
controller.NewCountAssetsController(s.ucManager.NewCountAssetsUsecase(), protected).Route() controller.NewCountAssetsController(s.ucManager.NewCountAssetsUsecase(), protected).Route()
controller.NewDeviceController(s.ucManager.NewDeviceUsecase(), protected, s.cfg).Route() controller.NewDeviceController(s.ucManager.NewDeviceUsecase(), protected, s.cfg).Route()

View File

@ -21,6 +21,8 @@ type UsersRepo interface {
GetUserByID(userID uuid.UUID) (entity.User, error) GetUserByID(userID uuid.UUID) (entity.User, error)
GetUserByUsernameWithStatus(username string) (entity.User, error) GetUserByUsernameWithStatus(username string) (entity.User, error)
CreateSuperAdminBypass(user entity.User) error CreateSuperAdminBypass(user entity.User) error
UpdateUserRoleByID(userID uuid.UUID, roleID uuid.UUID) error
UpdateUserRoleByUsername(username string, roleID uuid.UUID) error
} }
type usersRepo struct { type usersRepo struct {
@ -33,6 +35,14 @@ func NewUsersRepo(db *gorm.DB) UsersRepo {
} }
} }
func (r *usersRepo) UpdateUserRoleByID(userID uuid.UUID, roleID uuid.UUID) error {
return r.db.Model(&entity.User{}).Where("id = ?", userID).Update("role_id", roleID).Error
}
func (r *usersRepo) UpdateUserRoleByUsername(username string, roleID uuid.UUID) error {
return r.db.Model(&entity.User{}).Where("username = ?", username).Update("role_id", roleID).Error
}
func (r *usersRepo) CreateSuperAdminBypass(user entity.User) error { func (r *usersRepo) CreateSuperAdminBypass(user entity.User) error {
return r.db.Create(&user).Error return r.db.Create(&user).Error
} }

View File

@ -25,6 +25,9 @@ type UsersUsecase interface {
RejectUser(userID uuid.UUID) error RejectUser(userID uuid.UUID) error
GetUserByUsernameWithStatus(username string) (entity.User, error) GetUserByUsernameWithStatus(username string) (entity.User, error)
CreateSuperAdminBypass(user entity.User) error CreateSuperAdminBypass(user entity.User) error
UpdateUserRoleByID(userID uuid.UUID, newRoleName string) error
UpdateUserRoleByUsername(username, newRoleName string) error
GetUserByID(userID uuid.UUID) (entity.User, error)
} }
@ -45,6 +48,45 @@ func (u *usersUsecase) CreateSuperAdminBypass(user entity.User) error {
return u.userRepo.CreateSuperAdminBypass(user) return u.userRepo.CreateSuperAdminBypass(user)
} }
// Add these implementations to the usersUsecase struct
func (u *usersUsecase) UpdateUserRoleByID(userID uuid.UUID, newRoleName string) error {
// Get the new role ID
newRoleID, err := u.GetRoleByDepartment(newRoleName)
if err != nil {
return errors.New("invalid role name: " + err.Error())
}
// Check if user exists
_, err = u.userRepo.GetUserByID(userID)
if err != nil {
return errors.New("user not found")
}
// Update user role
return u.userRepo.UpdateUserRoleByID(userID, newRoleID)
}
func (u *usersUsecase) UpdateUserRoleByUsername(username, newRoleName string) error {
// Get the new role ID
newRoleID, err := u.GetRoleByDepartment(newRoleName)
if err != nil {
return errors.New("invalid role name: " + err.Error())
}
// Check if user exists
_, err = u.userRepo.GetUserByUsernameWithStatus(username)
if err != nil {
return errors.New("user not found")
}
// Update user role by username
return u.userRepo.UpdateUserRoleByUsername(username, newRoleID)
}
func (u *usersUsecase) GetUserByID(userID uuid.UUID) (entity.User, error) {
return u.userRepo.GetUserByID(userID)
}
func (u *usersUsecase) RegisterUser(registerDTO dto.UserRegisterDTO) error { func (u *usersUsecase) RegisterUser(registerDTO dto.UserRegisterDTO) error {
// Validate input // Validate input
if err := u.validate.Struct(registerDTO); err != nil { if err := u.validate.Struct(registerDTO); err != nil {