working on some code

This commit is contained in:
Warp Agent
2025-12-27 16:58:19 +00:00
parent 8677820864
commit 97659421b5
16 changed files with 3318 additions and 151 deletions

View File

@@ -1,6 +1,7 @@
package iam
import (
"database/sql"
"fmt"
"net/http"
"strings"
@@ -64,15 +65,22 @@ func (h *Handler) ListUsers(c *gin.Context) {
continue
}
roles, _ := GetUserRoles(h.db, u.ID)
permissions, _ := GetUserPermissions(h.db, u.ID)
groups, _ := GetUserGroups(h.db, u.ID)
users = append(users, map[string]interface{}{
"id": u.ID,
"username": u.Username,
"email": u.Email,
"full_name": u.FullName,
"is_active": u.IsActive,
"is_system": u.IsSystem,
"created_at": u.CreatedAt,
"updated_at": u.UpdatedAt,
"id": u.ID,
"username": u.Username,
"email": u.Email,
"full_name": u.FullName,
"is_active": u.IsActive,
"is_system": u.IsSystem,
"roles": roles,
"permissions": permissions,
"groups": groups,
"created_at": u.CreatedAt,
"updated_at": u.UpdatedAt,
"last_login_at": u.LastLoginAt,
})
}
@@ -81,9 +89,45 @@ func (h *Handler) ListUsers(c *gin.Context) {
}
// GetUser retrieves a single user
// Permission: User can view their own profile, or admin can view any profile
func (h *Handler) GetUser(c *gin.Context) {
userID := c.Param("id")
// Get current authenticated user from context
authUser, exists := c.Get("user")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"})
return
}
currentUser, ok := authUser.(*User)
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user context"})
return
}
// Check permission: user can view own profile, or admin can view any profile
canView := false
if currentUser.ID == userID {
canView = true
} else {
// Check if current user is admin
roles, err := GetUserRoles(h.db, currentUser.ID)
if err == nil {
for _, role := range roles {
if role == "admin" {
canView = true
break
}
}
}
}
if !canView {
c.JSON(http.StatusForbidden, gin.H{"error": "insufficient permissions"})
return
}
user, err := GetUserByID(h.db, userID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
@@ -92,18 +136,21 @@ func (h *Handler) GetUser(c *gin.Context) {
roles, _ := GetUserRoles(h.db, userID)
permissions, _ := GetUserPermissions(h.db, userID)
groups, _ := GetUserGroups(h.db, userID)
c.JSON(http.StatusOK, gin.H{
"id": user.ID,
"username": user.Username,
"email": user.Email,
"full_name": user.FullName,
"is_active": user.IsActive,
"is_system": user.IsSystem,
"roles": roles,
"permissions": permissions,
"created_at": user.CreatedAt,
"updated_at": user.UpdatedAt,
"id": user.ID,
"username": user.Username,
"email": user.Email,
"full_name": user.FullName,
"is_active": user.IsActive,
"is_system": user.IsSystem,
"roles": roles,
"permissions": permissions,
"groups": groups,
"created_at": user.CreatedAt,
"updated_at": user.UpdatedAt,
"last_login_at": user.LastLoginAt,
})
}
@@ -230,3 +277,432 @@ func (h *Handler) DeleteUser(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "user deleted successfully"})
}
// ListGroups lists all groups
func (h *Handler) ListGroups(c *gin.Context) {
query := `
SELECT g.id, g.name, g.description, g.is_system, g.created_at, g.updated_at,
COUNT(DISTINCT ug.user_id) as user_count,
COUNT(DISTINCT gr.role_id) as role_count
FROM groups g
LEFT JOIN user_groups ug ON g.id = ug.group_id
LEFT JOIN group_roles gr ON g.id = gr.group_id
GROUP BY g.id, g.name, g.description, g.is_system, g.created_at, g.updated_at
ORDER BY g.name
`
rows, err := h.db.Query(query)
if err != nil {
h.logger.Error("Failed to list groups", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list groups"})
return
}
defer rows.Close()
var groups []map[string]interface{}
for rows.Next() {
var g struct {
ID string
Name string
Description sql.NullString
IsSystem bool
CreatedAt string
UpdatedAt string
UserCount int
RoleCount int
}
if err := rows.Scan(&g.ID, &g.Name, &g.Description, &g.IsSystem,
&g.CreatedAt, &g.UpdatedAt, &g.UserCount, &g.RoleCount); err != nil {
h.logger.Error("Failed to scan group", "error", err)
continue
}
groups = append(groups, map[string]interface{}{
"id": g.ID,
"name": g.Name,
"description": g.Description.String,
"is_system": g.IsSystem,
"user_count": g.UserCount,
"role_count": g.RoleCount,
"created_at": g.CreatedAt,
"updated_at": g.UpdatedAt,
})
}
c.JSON(http.StatusOK, gin.H{"groups": groups})
}
// GetGroup retrieves a single group
func (h *Handler) GetGroup(c *gin.Context) {
groupID := c.Param("id")
group, err := GetGroupByID(h.db, groupID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
return
}
users, _ := GetGroupUsers(h.db, groupID)
roles, _ := GetGroupRoles(h.db, groupID)
c.JSON(http.StatusOK, gin.H{
"id": group.ID,
"name": group.Name,
"description": group.Description,
"is_system": group.IsSystem,
"user_count": group.UserCount,
"role_count": group.RoleCount,
"users": users,
"roles": roles,
"created_at": group.CreatedAt,
"updated_at": group.UpdatedAt,
})
}
// CreateGroup creates a new group
func (h *Handler) CreateGroup(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 group", "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 = ""
}
query := `
INSERT INTO groups (name, description)
VALUES ($1, $2)
RETURNING id
`
var groupID string
err := h.db.QueryRow(query, req.Name, description).Scan(&groupID)
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("Group name already exists", "name", req.Name, "error", err)
c.JSON(http.StatusConflict, gin.H{"error": "group name already exists"})
return
}
h.logger.Error("Failed to create group", "error", err, "name", req.Name)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create group: " + err.Error()})
return
}
h.logger.Info("Group created successfully", "group_id", groupID, "name", req.Name)
c.JSON(http.StatusCreated, gin.H{"id": groupID, "name": req.Name})
}
// UpdateGroup updates an existing group
func (h *Handler) UpdateGroup(c *gin.Context) {
groupID := c.Param("id")
// Check if group is system group
var isSystem bool
err := h.db.QueryRow("SELECT is_system FROM groups WHERE id = $1", groupID).Scan(&isSystem)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
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
var updates []string
var args []interface{}
argIndex := 1
if req.Name != "" {
updates = append(updates, fmt.Sprintf("name = $%d", argIndex))
args = append(args, req.Name)
argIndex++
}
if req.Description != "" {
updates = append(updates, fmt.Sprintf("description = $%d", argIndex))
args = append(args, req.Description)
argIndex++
}
if len(updates) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "no fields to update"})
return
}
updates = append(updates, "updated_at = NOW()")
args = append(args, groupID)
query := fmt.Sprintf("UPDATE groups SET %s WHERE id = $%d", strings.Join(updates, ", "), argIndex)
_, err = h.db.Exec(query, args...)
if err != nil {
h.logger.Error("Failed to update group", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update group"})
return
}
h.logger.Info("Group updated", "group_id", groupID)
c.JSON(http.StatusOK, gin.H{"message": "group updated successfully"})
}
// DeleteGroup deletes a group
func (h *Handler) DeleteGroup(c *gin.Context) {
groupID := c.Param("id")
// Check if group is system group
var isSystem bool
err := h.db.QueryRow("SELECT is_system FROM groups WHERE id = $1", groupID).Scan(&isSystem)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
return
}
if isSystem {
c.JSON(http.StatusForbidden, gin.H{"error": "cannot delete system group"})
return
}
_, err = h.db.Exec("DELETE FROM groups WHERE id = $1", groupID)
if err != nil {
h.logger.Error("Failed to delete group", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete group"})
return
}
h.logger.Info("Group deleted", "group_id", groupID)
c.JSON(http.StatusOK, gin.H{"message": "group deleted successfully"})
}
// AddUserToGroup adds a user to a group
func (h *Handler) AddUserToGroup(c *gin.Context) {
groupID := c.Param("id")
var req struct {
UserID string `json:"user_id" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
return
}
// Get current user ID from context
authUser, _ := c.Get("user")
currentUser := authUser.(*User)
err := AddUserToGroup(h.db, req.UserID, groupID, currentUser.ID)
if err != nil {
h.logger.Error("Failed to add user to group", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to add user to group"})
return
}
h.logger.Info("User added to group", "user_id", req.UserID, "group_id", groupID)
c.JSON(http.StatusOK, gin.H{"message": "user added to group successfully"})
}
// RemoveUserFromGroup removes a user from a group
func (h *Handler) RemoveUserFromGroup(c *gin.Context) {
groupID := c.Param("id")
userID := c.Param("user_id")
err := RemoveUserFromGroup(h.db, userID, groupID)
if err != nil {
h.logger.Error("Failed to remove user from group", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove user from group"})
return
}
h.logger.Info("User removed from group", "user_id", userID, "group_id", groupID)
c.JSON(http.StatusOK, gin.H{"message": "user removed from group successfully"})
}
// AssignRoleToUser assigns a role to a user
func (h *Handler) AssignRoleToUser(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"})
return
}
// Get role ID by name
roleID, err := GetRoleIDByName(h.db, req.RoleName)
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 assign role"})
return
}
// Get current user ID from context
authUser, _ := c.Get("user")
currentUser := authUser.(*User)
err = AddUserRole(h.db, userID, roleID, currentUser.ID)
if err != nil {
h.logger.Error("Failed to assign role to user", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign role"})
return
}
h.logger.Info("Role assigned to user", "user_id", userID, "role", req.RoleName)
c.JSON(http.StatusOK, gin.H{"message": "role assigned successfully"})
}
// 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"})
return
}
// Get role ID by name
roleID, err := GetRoleIDByName(h.db, req.RoleName)
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 remove role"})
return
}
err = RemoveUserRole(h.db, userID, roleID)
if err != nil {
h.logger.Error("Failed to remove role from user", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove role"})
return
}
h.logger.Info("Role removed from user", "user_id", userID, "role", req.RoleName)
c.JSON(http.StatusOK, gin.H{"message": "role removed successfully"})
}
// AssignGroupToUser assigns a group to a user
func (h *Handler) AssignGroupToUser(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"})
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
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")
currentUser := authUser.(*User)
err = AddUserToGroup(h.db, userID, groupID, currentUser.ID)
if err != nil {
h.logger.Error("Failed to assign group to user", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to assign group"})
return
}
h.logger.Info("Group assigned to user", "user_id", userID, "group", req.GroupName)
c.JSON(http.StatusOK, gin.H{"message": "group assigned successfully"})
}
// 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"})
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
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 remove group"})
return
}
err = RemoveUserFromGroup(h.db, userID, groupID)
if err != nil {
h.logger.Error("Failed to remove group from user", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove group"})
return
}
h.logger.Info("Group removed from user", "user_id", userID, "group", req.GroupName)
c.JSON(http.StatusOK, gin.H{"message": "group removed successfully"})
}