package iam import ( "database/sql" "time" "github.com/atlasos/calypso/internal/common/database" ) // User represents a system user type User struct { ID string Username string Email string PasswordHash string FullName string IsActive bool IsSystem bool CreatedAt time.Time UpdatedAt time.Time LastLoginAt sql.NullTime Roles []string Permissions []string } // GetUserByID retrieves a user by ID func GetUserByID(db *database.DB, userID string) (*User, error) { query := ` SELECT id, username, email, password_hash, full_name, is_active, is_system, created_at, updated_at, last_login_at FROM users WHERE id = $1 ` var user User var lastLogin sql.NullTime err := db.QueryRow(query, userID).Scan( &user.ID, &user.Username, &user.Email, &user.PasswordHash, &user.FullName, &user.IsActive, &user.IsSystem, &user.CreatedAt, &user.UpdatedAt, &lastLogin, ) if err != nil { return nil, err } user.LastLoginAt = lastLogin return &user, nil } // GetUserByUsername retrieves a user by username func GetUserByUsername(db *database.DB, username string) (*User, error) { query := ` SELECT id, username, email, password_hash, full_name, is_active, is_system, created_at, updated_at, last_login_at FROM users WHERE username = $1 ` var user User var lastLogin sql.NullTime err := db.QueryRow(query, username).Scan( &user.ID, &user.Username, &user.Email, &user.PasswordHash, &user.FullName, &user.IsActive, &user.IsSystem, &user.CreatedAt, &user.UpdatedAt, &lastLogin, ) if err != nil { return nil, err } user.LastLoginAt = lastLogin return &user, nil } // GetUserRoles retrieves all roles for a user func GetUserRoles(db *database.DB, userID string) ([]string, error) { query := ` SELECT r.name FROM roles r INNER JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = $1 ` rows, err := db.Query(query, userID) if err != nil { return nil, err } defer rows.Close() var roles []string for rows.Next() { var role string if err := rows.Scan(&role); err != nil { return nil, err } roles = append(roles, role) } return roles, rows.Err() } // GetUserPermissions retrieves all permissions for a user (via roles) func GetUserPermissions(db *database.DB, userID string) ([]string, error) { query := ` SELECT DISTINCT p.name FROM permissions p INNER JOIN role_permissions rp ON p.id = rp.permission_id INNER JOIN user_roles ur ON rp.role_id = ur.role_id WHERE ur.user_id = $1 ` rows, err := db.Query(query, userID) 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() } // AddUserRole assigns a role to a user func AddUserRole(db *database.DB, userID, roleID, assignedBy string) error { query := ` INSERT INTO user_roles (user_id, role_id, assigned_by) VALUES ($1, $2, $3) ON CONFLICT (user_id, role_id) DO NOTHING ` _, err := db.Exec(query, userID, roleID, assignedBy) return err } // RemoveUserRole removes a role from a user func RemoveUserRole(db *database.DB, userID, roleID string) error { query := `DELETE FROM user_roles WHERE user_id = $1 AND role_id = $2` _, err := db.Exec(query, userID, roleID) return err } // GetRoleIDByName retrieves a role ID by name func GetRoleIDByName(db *database.DB, roleName string) (string, error) { var roleID string err := db.QueryRow("SELECT id FROM roles WHERE name = $1", roleName).Scan(&roleID) return roleID, err }