masih ngerjain user management
This commit is contained in:
Binary file not shown.
@@ -270,7 +270,20 @@ func NewRouter(cfg *config.Config, db *database.DB, log *logger.Logger) *gin.Eng
|
||||
iamGroup.POST("/users", iamHandler.CreateUser)
|
||||
iamGroup.PUT("/users/:id", iamHandler.UpdateUser)
|
||||
iamGroup.DELETE("/users/:id", iamHandler.DeleteUser)
|
||||
// Roles routes
|
||||
iamGroup.GET("/roles", iamHandler.ListRoles)
|
||||
iamGroup.GET("/roles/:id", iamHandler.GetRole)
|
||||
iamGroup.POST("/roles", iamHandler.CreateRole)
|
||||
iamGroup.PUT("/roles/:id", iamHandler.UpdateRole)
|
||||
iamGroup.DELETE("/roles/:id", iamHandler.DeleteRole)
|
||||
iamGroup.GET("/roles/:id/permissions", iamHandler.GetRolePermissions)
|
||||
iamGroup.POST("/roles/:id/permissions", iamHandler.AssignPermissionToRole)
|
||||
iamGroup.DELETE("/roles/:id/permissions", iamHandler.RemovePermissionFromRole)
|
||||
|
||||
// Permissions routes
|
||||
iamGroup.GET("/permissions", iamHandler.ListPermissions)
|
||||
|
||||
// User role/group assignment
|
||||
iamGroup.POST("/users/:id/roles", iamHandler.AssignRoleToUser)
|
||||
iamGroup.DELETE("/users/:id/roles", iamHandler.RemoveRoleFromUser)
|
||||
iamGroup.POST("/users/:id/groups", iamHandler.AssignGroupToUser)
|
||||
|
||||
@@ -199,16 +199,21 @@ func (h *Handler) UpdateUser(c *gin.Context) {
|
||||
userID := c.Param("id")
|
||||
|
||||
var req struct {
|
||||
Email *string `json:"email"`
|
||||
FullName *string `json:"full_name"`
|
||||
IsActive *bool `json:"is_active"`
|
||||
Email *string `json:"email"`
|
||||
FullName *string `json:"full_name"`
|
||||
IsActive *bool `json:"is_active"`
|
||||
Roles *[]string `json:"roles"`
|
||||
Groups *[]string `json:"groups"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("Failed to bind JSON", "error", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("UpdateUser request received", "user_id", userID, "email", req.Email, "full_name", req.FullName, "is_active", req.IsActive, "roles", req.Roles, "groups", req.Groups)
|
||||
|
||||
// Build update query dynamically
|
||||
updates := []string{"updated_at = NOW()"}
|
||||
args := []interface{}{}
|
||||
@@ -230,19 +235,144 @@ func (h *Handler) UpdateUser(c *gin.Context) {
|
||||
argPos++
|
||||
}
|
||||
|
||||
if len(updates) == 1 {
|
||||
// Allow update if roles or groups are provided, even if no other fields are updated
|
||||
if len(updates) == 1 && req.Roles == nil && req.Groups == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no fields to update"})
|
||||
return
|
||||
}
|
||||
|
||||
args = append(args, userID)
|
||||
query := "UPDATE users SET " + strings.Join(updates, ", ") + fmt.Sprintf(" WHERE id = $%d", argPos)
|
||||
// Update user basic info if there are any changes
|
||||
if len(updates) > 1 {
|
||||
args = append(args, userID)
|
||||
query := "UPDATE users SET " + strings.Join(updates, ", ") + fmt.Sprintf(" WHERE id = $%d", argPos)
|
||||
_, err := h.db.Exec(query, args...)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to update user", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update user"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err := h.db.Exec(query, args...)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to update user", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update user"})
|
||||
return
|
||||
// Get current user ID from context for audit
|
||||
authUser, _ := c.Get("user")
|
||||
currentUser := authUser.(*User)
|
||||
|
||||
// Update roles if provided
|
||||
if req.Roles != nil {
|
||||
h.logger.Info("Updating user roles", "user_id", userID, "roles", *req.Roles)
|
||||
// Get current roles
|
||||
currentRoles, err := GetUserRoles(h.db, userID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to get current roles", "error", err)
|
||||
}
|
||||
h.logger.Info("Current roles", "user_id", userID, "current_roles", currentRoles)
|
||||
|
||||
// Remove roles that are not in the new list
|
||||
for _, role := range currentRoles {
|
||||
found := false
|
||||
for _, newRole := range *req.Roles {
|
||||
if role == newRole {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
roleID, err := GetRoleIDByName(h.db, role)
|
||||
if err == nil {
|
||||
err = RemoveUserRole(h.db, userID, roleID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to remove role", "error", err, "role", role)
|
||||
} else {
|
||||
h.logger.Info("Role removed", "user_id", userID, "role", role)
|
||||
}
|
||||
} else {
|
||||
h.logger.Error("Failed to get role ID", "error", err, "role", role)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new roles that are not in the current list
|
||||
for _, roleName := range *req.Roles {
|
||||
found := false
|
||||
for _, currentRole := range currentRoles {
|
||||
if roleName == currentRole {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
roleID, err := GetRoleIDByName(h.db, roleName)
|
||||
if err == nil {
|
||||
err = AddUserRole(h.db, userID, roleID, currentUser.ID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to add role", "error", err, "role", roleName)
|
||||
} else {
|
||||
h.logger.Info("Role added", "user_id", userID, "role", roleName)
|
||||
}
|
||||
} else {
|
||||
h.logger.Error("Failed to get role ID", "error", err, "role", roleName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update groups if provided
|
||||
if req.Groups != nil {
|
||||
h.logger.Info("Updating user groups", "user_id", userID, "groups", *req.Groups)
|
||||
// Get current groups
|
||||
currentGroups, err := GetUserGroups(h.db, userID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to get current groups", "error", err)
|
||||
}
|
||||
h.logger.Info("Current groups", "user_id", userID, "current_groups", currentGroups)
|
||||
|
||||
// Remove groups that are not in the new list
|
||||
for _, group := range currentGroups {
|
||||
found := false
|
||||
for _, newGroup := range *req.Groups {
|
||||
if group == newGroup {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
groupObj, err := GetGroupByName(h.db, group)
|
||||
if err == nil {
|
||||
err = RemoveUserFromGroup(h.db, userID, groupObj.ID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to remove group", "error", err, "group", group)
|
||||
} else {
|
||||
h.logger.Info("Group removed", "user_id", userID, "group", group)
|
||||
}
|
||||
} else {
|
||||
h.logger.Error("Failed to get group", "error", err, "group", group)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new groups that are not in the current list
|
||||
for _, groupName := range *req.Groups {
|
||||
found := false
|
||||
for _, currentGroup := range currentGroups {
|
||||
if groupName == currentGroup {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
groupObj, err := GetGroupByName(h.db, groupName)
|
||||
if err == nil {
|
||||
err = AddUserToGroup(h.db, userID, groupObj.ID, currentUser.ID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to add group", "error", err, "group", groupName)
|
||||
} else {
|
||||
h.logger.Info("Group added", "user_id", userID, "group", groupName)
|
||||
}
|
||||
} else {
|
||||
h.logger.Error("Failed to get group", "error", err, "group", groupName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h.logger.Info("User updated", "user_id", userID)
|
||||
@@ -581,17 +711,16 @@ func (h *Handler) AssignRoleToUser(c *gin.Context) {
|
||||
// RemoveRoleFromUser removes a role from a user
|
||||
func (h *Handler) RemoveRoleFromUser(c *gin.Context) {
|
||||
userID := c.Param("id")
|
||||
var req struct {
|
||||
RoleName string `json:"role_name" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
// Get role_name from query parameter
|
||||
roleName := c.Query("role_name")
|
||||
if roleName == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "role_name is required"})
|
||||
return
|
||||
}
|
||||
|
||||
// Get role ID by name
|
||||
roleID, err := GetRoleIDByName(h.db, req.RoleName)
|
||||
roleID, err := GetRoleIDByName(h.db, roleName)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "role not found"})
|
||||
@@ -609,7 +738,7 @@ func (h *Handler) RemoveRoleFromUser(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("Role removed from user", "user_id", userID, "role", req.RoleName)
|
||||
h.logger.Info("Role removed from user", "user_id", userID, "role", roleName)
|
||||
c.JSON(http.StatusOK, gin.H{"message": "role removed successfully"})
|
||||
}
|
||||
|
||||
@@ -637,15 +766,6 @@ func (h *Handler) AssignGroupToUser(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
groupID := group.ID
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
|
||||
return
|
||||
}
|
||||
h.logger.Error("Failed to get group", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign group"})
|
||||
return
|
||||
}
|
||||
|
||||
// Get current user ID from context
|
||||
authUser, _ := c.Get("user")
|
||||
@@ -665,27 +785,16 @@ func (h *Handler) AssignGroupToUser(c *gin.Context) {
|
||||
// RemoveGroupFromUser removes a group from a user
|
||||
func (h *Handler) RemoveGroupFromUser(c *gin.Context) {
|
||||
userID := c.Param("id")
|
||||
var req struct {
|
||||
GroupName string `json:"group_name" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
// Get group_name from query parameter
|
||||
groupName := c.Query("group_name")
|
||||
if groupName == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "group_name is required"})
|
||||
return
|
||||
}
|
||||
|
||||
// Get group ID by name
|
||||
group, err := GetGroupByName(h.db, req.GroupName)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
|
||||
return
|
||||
}
|
||||
h.logger.Error("Failed to get group", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign group"})
|
||||
return
|
||||
}
|
||||
groupID := group.ID
|
||||
group, err := GetGroupByName(h.db, groupName)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
|
||||
@@ -695,6 +804,7 @@ func (h *Handler) RemoveGroupFromUser(c *gin.Context) {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove group"})
|
||||
return
|
||||
}
|
||||
groupID := group.ID
|
||||
|
||||
err = RemoveUserFromGroup(h.db, userID, groupID)
|
||||
if err != nil {
|
||||
@@ -703,6 +813,320 @@ func (h *Handler) RemoveGroupFromUser(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("Group removed from user", "user_id", userID, "group", req.GroupName)
|
||||
h.logger.Info("Group removed from user", "user_id", userID, "group", groupName)
|
||||
c.JSON(http.StatusOK, gin.H{"message": "group removed successfully"})
|
||||
}
|
||||
|
||||
// ListRoles lists all available roles
|
||||
func (h *Handler) ListRoles(c *gin.Context) {
|
||||
roles, err := ListRoles(h.db)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to list roles", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list roles"})
|
||||
return
|
||||
}
|
||||
|
||||
var result []map[string]interface{}
|
||||
for _, role := range roles {
|
||||
// Get user count for this role
|
||||
userCount := 0
|
||||
userIDs, _ := GetRoleUsers(h.db, role.ID)
|
||||
userCount = len(userIDs)
|
||||
|
||||
result = append(result, map[string]interface{}{
|
||||
"id": role.ID,
|
||||
"name": role.Name,
|
||||
"description": role.Description,
|
||||
"is_system": role.IsSystem,
|
||||
"user_count": userCount,
|
||||
"created_at": role.CreatedAt,
|
||||
"updated_at": role.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"roles": result})
|
||||
}
|
||||
|
||||
// GetRole retrieves a single role
|
||||
func (h *Handler) GetRole(c *gin.Context) {
|
||||
roleID := c.Param("id")
|
||||
|
||||
role, err := GetRoleByID(h.db, roleID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "role not found"})
|
||||
return
|
||||
}
|
||||
h.logger.Error("Failed to get role", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get role"})
|
||||
return
|
||||
}
|
||||
|
||||
// Get user count
|
||||
userIDs, _ := GetRoleUsers(h.db, role.ID)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": role.ID,
|
||||
"name": role.Name,
|
||||
"description": role.Description,
|
||||
"is_system": role.IsSystem,
|
||||
"user_count": len(userIDs),
|
||||
"created_at": role.CreatedAt,
|
||||
"updated_at": role.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateRole creates a new role
|
||||
func (h *Handler) CreateRole(c *gin.Context) {
|
||||
var req struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("Invalid request to create role", "error", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Trim whitespace
|
||||
req.Name = strings.TrimSpace(req.Name)
|
||||
if req.Name == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
|
||||
return
|
||||
}
|
||||
|
||||
// Handle empty description
|
||||
description := strings.TrimSpace(req.Description)
|
||||
if description == "" {
|
||||
description = ""
|
||||
}
|
||||
|
||||
role, err := CreateRole(h.db, req.Name, description)
|
||||
if err != nil {
|
||||
// Check if it's a unique constraint violation
|
||||
if strings.Contains(err.Error(), "duplicate key") || strings.Contains(err.Error(), "unique constraint") {
|
||||
h.logger.Error("Role name already exists", "name", req.Name, "error", err)
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "role name already exists"})
|
||||
return
|
||||
}
|
||||
h.logger.Error("Failed to create role", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create role"})
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("Role created", "role_id", role.ID, "name", role.Name)
|
||||
c.JSON(http.StatusCreated, gin.H{"id": role.ID, "name": role.Name})
|
||||
}
|
||||
|
||||
// UpdateRole updates an existing role
|
||||
func (h *Handler) UpdateRole(c *gin.Context) {
|
||||
roleID := c.Param("id")
|
||||
|
||||
// Check if role is system role
|
||||
var isSystem bool
|
||||
err := h.db.QueryRow("SELECT is_system FROM roles WHERE id = $1", roleID).Scan(&isSystem)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "role not found"})
|
||||
return
|
||||
}
|
||||
|
||||
if isSystem {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "cannot modify system role"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Name *string `json:"name"`
|
||||
Description *string `json:"description"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
return
|
||||
}
|
||||
|
||||
// Build update query dynamically
|
||||
updates := []string{"updated_at = NOW()"}
|
||||
args := []interface{}{}
|
||||
argIndex := 1
|
||||
|
||||
if req.Name != nil {
|
||||
name := strings.TrimSpace(*req.Name)
|
||||
if name == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "name cannot be empty"})
|
||||
return
|
||||
}
|
||||
updates = append(updates, fmt.Sprintf("name = $%d", argIndex))
|
||||
args = append(args, name)
|
||||
argIndex++
|
||||
}
|
||||
|
||||
if req.Description != nil {
|
||||
description := strings.TrimSpace(*req.Description)
|
||||
updates = append(updates, fmt.Sprintf("description = $%d", argIndex))
|
||||
args = append(args, description)
|
||||
argIndex++
|
||||
}
|
||||
|
||||
if len(updates) == 1 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no fields to update"})
|
||||
return
|
||||
}
|
||||
|
||||
args = append(args, roleID)
|
||||
query := "UPDATE roles SET " + strings.Join(updates, ", ") + fmt.Sprintf(" WHERE id = $%d", argIndex)
|
||||
|
||||
_, err = h.db.Exec(query, args...)
|
||||
if err != nil {
|
||||
// Check if it's a unique constraint violation
|
||||
if strings.Contains(err.Error(), "duplicate key") || strings.Contains(err.Error(), "unique constraint") {
|
||||
h.logger.Error("Role name already exists", "error", err)
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "role name already exists"})
|
||||
return
|
||||
}
|
||||
h.logger.Error("Failed to update role", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update role"})
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("Role updated", "role_id", roleID)
|
||||
c.JSON(http.StatusOK, gin.H{"message": "role updated successfully"})
|
||||
}
|
||||
|
||||
// DeleteRole deletes a role
|
||||
func (h *Handler) DeleteRole(c *gin.Context) {
|
||||
roleID := c.Param("id")
|
||||
|
||||
// Check if role is system role
|
||||
var isSystem bool
|
||||
err := h.db.QueryRow("SELECT is_system FROM roles WHERE id = $1", roleID).Scan(&isSystem)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "role not found"})
|
||||
return
|
||||
}
|
||||
|
||||
if isSystem {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "cannot delete system role"})
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.db.Exec("DELETE FROM roles WHERE id = $1", roleID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to delete role", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete role"})
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("Role deleted", "role_id", roleID)
|
||||
c.JSON(http.StatusOK, gin.H{"message": "role deleted successfully"})
|
||||
}
|
||||
|
||||
// GetRolePermissions retrieves all permissions for a role
|
||||
func (h *Handler) GetRolePermissions(c *gin.Context) {
|
||||
roleID := c.Param("id")
|
||||
|
||||
permissions, err := GetRolePermissions(h.db, roleID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to get role permissions", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get role permissions"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"permissions": permissions})
|
||||
}
|
||||
|
||||
// AssignPermissionToRole assigns a permission to a role
|
||||
func (h *Handler) AssignPermissionToRole(c *gin.Context) {
|
||||
roleID := c.Param("id")
|
||||
var req struct {
|
||||
PermissionName string `json:"permission_name" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
return
|
||||
}
|
||||
|
||||
// Get permission ID by name
|
||||
permissionID, err := GetPermissionIDByName(h.db, req.PermissionName)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "permission not found"})
|
||||
return
|
||||
}
|
||||
h.logger.Error("Failed to get permission", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign permission"})
|
||||
return
|
||||
}
|
||||
|
||||
err = AddPermissionToRole(h.db, roleID, permissionID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to assign permission to role", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign permission"})
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("Permission assigned to role", "role_id", roleID, "permission", req.PermissionName)
|
||||
c.JSON(http.StatusOK, gin.H{"message": "permission assigned successfully"})
|
||||
}
|
||||
|
||||
// RemovePermissionFromRole removes a permission from a role
|
||||
func (h *Handler) RemovePermissionFromRole(c *gin.Context) {
|
||||
roleID := c.Param("id")
|
||||
|
||||
// For DELETE requests, we can get permission_name from query param or body
|
||||
var req struct {
|
||||
PermissionName string `json:"permission_name"`
|
||||
}
|
||||
|
||||
// Try to get from query param first
|
||||
permissionName := c.Query("permission_name")
|
||||
if permissionName == "" {
|
||||
// Try to get from body
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "permission_name is required"})
|
||||
return
|
||||
}
|
||||
permissionName = req.PermissionName
|
||||
}
|
||||
|
||||
if permissionName == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "permission_name is required"})
|
||||
return
|
||||
}
|
||||
|
||||
// Get permission ID by name
|
||||
permissionID, err := GetPermissionIDByName(h.db, permissionName)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "permission not found"})
|
||||
return
|
||||
}
|
||||
h.logger.Error("Failed to get permission", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove permission"})
|
||||
return
|
||||
}
|
||||
|
||||
err = RemovePermissionFromRole(h.db, roleID, permissionID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to remove permission from role", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove permission"})
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("Permission removed from role", "role_id", roleID, "permission", permissionName)
|
||||
c.JSON(http.StatusOK, gin.H{"message": "permission removed successfully"})
|
||||
}
|
||||
|
||||
// ListPermissions lists all available permissions
|
||||
func (h *Handler) ListPermissions(c *gin.Context) {
|
||||
permissions, err := ListPermissions(h.db)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to list permissions", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list permissions"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"permissions": permissions})
|
||||
}
|
||||
|
||||
237
backend/internal/iam/role.go
Normal file
237
backend/internal/iam/role.go
Normal file
@@ -0,0 +1,237 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/atlasos/calypso/internal/common/database"
|
||||
)
|
||||
|
||||
// Role represents a system role
|
||||
type Role struct {
|
||||
ID string
|
||||
Name string
|
||||
Description string
|
||||
IsSystem bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// GetRoleByID retrieves a role by ID
|
||||
func GetRoleByID(db *database.DB, roleID string) (*Role, error) {
|
||||
query := `
|
||||
SELECT id, name, description, is_system, created_at, updated_at
|
||||
FROM roles
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
var role Role
|
||||
err := db.QueryRow(query, roleID).Scan(
|
||||
&role.ID, &role.Name, &role.Description, &role.IsSystem,
|
||||
&role.CreatedAt, &role.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
// GetRoleByName retrieves a role by name
|
||||
func GetRoleByName(db *database.DB, name string) (*Role, error) {
|
||||
query := `
|
||||
SELECT id, name, description, is_system, created_at, updated_at
|
||||
FROM roles
|
||||
WHERE name = $1
|
||||
`
|
||||
|
||||
var role Role
|
||||
err := db.QueryRow(query, name).Scan(
|
||||
&role.ID, &role.Name, &role.Description, &role.IsSystem,
|
||||
&role.CreatedAt, &role.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
// ListRoles retrieves all roles
|
||||
func ListRoles(db *database.DB) ([]*Role, error) {
|
||||
query := `
|
||||
SELECT id, name, description, is_system, created_at, updated_at
|
||||
FROM roles
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
rows, err := db.Query(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var roles []*Role
|
||||
for rows.Next() {
|
||||
var role Role
|
||||
if err := rows.Scan(
|
||||
&role.ID, &role.Name, &role.Description, &role.IsSystem,
|
||||
&role.CreatedAt, &role.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roles = append(roles, &role)
|
||||
}
|
||||
|
||||
return roles, rows.Err()
|
||||
}
|
||||
|
||||
// CreateRole creates a new role
|
||||
func CreateRole(db *database.DB, name, description string) (*Role, error) {
|
||||
query := `
|
||||
INSERT INTO roles (name, description)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id, name, description, is_system, created_at, updated_at
|
||||
`
|
||||
|
||||
var role Role
|
||||
err := db.QueryRow(query, name, description).Scan(
|
||||
&role.ID, &role.Name, &role.Description, &role.IsSystem,
|
||||
&role.CreatedAt, &role.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
// UpdateRole updates an existing role
|
||||
func UpdateRole(db *database.DB, roleID, name, description string) error {
|
||||
query := `
|
||||
UPDATE roles
|
||||
SET name = $1, description = $2, updated_at = NOW()
|
||||
WHERE id = $3
|
||||
`
|
||||
_, err := db.Exec(query, name, description, roleID)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteRole deletes a role
|
||||
func DeleteRole(db *database.DB, roleID string) error {
|
||||
query := `DELETE FROM roles WHERE id = $1`
|
||||
_, err := db.Exec(query, roleID)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetRoleUsers retrieves all users with a specific role
|
||||
func GetRoleUsers(db *database.DB, roleID string) ([]string, error) {
|
||||
query := `
|
||||
SELECT u.id
|
||||
FROM users u
|
||||
INNER JOIN user_roles ur ON u.id = ur.user_id
|
||||
WHERE ur.role_id = $1
|
||||
ORDER BY u.username
|
||||
`
|
||||
|
||||
rows, err := db.Query(query, roleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var userIDs []string
|
||||
for rows.Next() {
|
||||
var userID string
|
||||
if err := rows.Scan(&userID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userIDs = append(userIDs, userID)
|
||||
}
|
||||
|
||||
return userIDs, rows.Err()
|
||||
}
|
||||
|
||||
// GetRolePermissions retrieves all permissions for a role
|
||||
func GetRolePermissions(db *database.DB, roleID string) ([]string, error) {
|
||||
query := `
|
||||
SELECT p.name
|
||||
FROM permissions p
|
||||
INNER JOIN role_permissions rp ON p.id = rp.permission_id
|
||||
WHERE rp.role_id = $1
|
||||
ORDER BY p.name
|
||||
`
|
||||
|
||||
rows, err := db.Query(query, roleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var permissions []string
|
||||
for rows.Next() {
|
||||
var perm string
|
||||
if err := rows.Scan(&perm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
permissions = append(permissions, perm)
|
||||
}
|
||||
|
||||
return permissions, rows.Err()
|
||||
}
|
||||
|
||||
// AddPermissionToRole assigns a permission to a role
|
||||
func AddPermissionToRole(db *database.DB, roleID, permissionID string) error {
|
||||
query := `
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
VALUES ($1, $2)
|
||||
ON CONFLICT (role_id, permission_id) DO NOTHING
|
||||
`
|
||||
_, err := db.Exec(query, roleID, permissionID)
|
||||
return err
|
||||
}
|
||||
|
||||
// RemovePermissionFromRole removes a permission from a role
|
||||
func RemovePermissionFromRole(db *database.DB, roleID, permissionID string) error {
|
||||
query := `DELETE FROM role_permissions WHERE role_id = $1 AND permission_id = $2`
|
||||
_, err := db.Exec(query, roleID, permissionID)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetPermissionIDByName retrieves a permission ID by name
|
||||
func GetPermissionIDByName(db *database.DB, permissionName string) (string, error) {
|
||||
var permissionID string
|
||||
err := db.QueryRow("SELECT id FROM permissions WHERE name = $1", permissionName).Scan(&permissionID)
|
||||
return permissionID, err
|
||||
}
|
||||
|
||||
// ListPermissions retrieves all permissions
|
||||
func ListPermissions(db *database.DB) ([]map[string]interface{}, error) {
|
||||
query := `
|
||||
SELECT id, name, resource, action, description
|
||||
FROM permissions
|
||||
ORDER BY resource, action
|
||||
`
|
||||
|
||||
rows, err := db.Query(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var permissions []map[string]interface{}
|
||||
for rows.Next() {
|
||||
var id, name, resource, action, description string
|
||||
if err := rows.Scan(&id, &name, &resource, &action, &description); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
permissions = append(permissions, map[string]interface{}{
|
||||
"id": id,
|
||||
"name": name,
|
||||
"resource": resource,
|
||||
"action": action,
|
||||
"description": description,
|
||||
})
|
||||
}
|
||||
|
||||
return permissions, rows.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user