adding new feature for updating new roles
This commit is contained in:
parent
f84ec65fbb
commit
e35edf3172
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue