package importer import ( "bufio" "context" "os" "sort" "strconv" "strings" "github.com/darroyo/nasctl/internal/db" "github.com/darroyo/nasctl/internal/system" ) const ( UserMinUID = 1000 UserMaxUID = 60000 ) var excludedUsernames = map[string]bool{ "nobody": true, "nogroup": true, "sshd": true, "systemd": true, "messagebus": true, "polkitd": true, } func ImportSystemUsers(ctx context.Context, adminUsername string) ([]db.User, error) { passwdMap, err := readPasswd() if err != nil { return nil, err } groupMap, err := readGroups() if err != nil { return nil, err } smbUsers, err := listSMBUsers(ctx) if err != nil { smbUsers = map[string]bool{} } var users []db.User for username, uid := range passwdMap { if uid < UserMinUID || uid > UserMaxUID { continue } if excludedUsernames[username] { continue } if username == adminUsername { continue } users = append(users, db.User{ Username: username, Groups: groupMap[username], SMBEnabled: smbUsers[username], Disabled: false, }) } sort.Slice(users, func(i, j int) bool { return users[i].Username < users[j].Username }) return users, nil } func readPasswd() (map[string]int, error) { f, err := os.Open("/etc/passwd") if err != nil { return nil, err } defer f.Close() result := make(map[string]int) scanner := bufio.NewScanner(f) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } parts := strings.Split(line, ":") if len(parts) < 3 { continue } username := parts[0] uid, err := strconv.Atoi(parts[2]) if err != nil { continue } result[username] = uid } if err := scanner.Err(); err != nil { return nil, err } return result, nil } func readGroups() (map[string][]string, error) { f, err := os.Open("/etc/group") if err != nil { return nil, err } defer f.Close() result := make(map[string][]string) scanner := bufio.NewScanner(f) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } parts := strings.Split(line, ":") if len(parts) < 4 { continue } membersStr := strings.TrimSpace(parts[3]) if membersStr == "" { continue } for _, member := range strings.Split(membersStr, ",") { member = strings.TrimSpace(member) if member != "" { result[member] = append(result[member], parts[0]) } } } if err := scanner.Err(); err != nil { return nil, err } return result, nil } func listSMBUsers(ctx context.Context) (map[string]bool, error) { stdout, _, err := system.Run(ctx, "pdbedit", "-L") if err != nil { return map[string]bool{}, nil } result := make(map[string]bool) for _, line := range strings.Split(stdout, "\n") { line = strings.TrimSpace(line) if line == "" { continue } parts := strings.SplitN(line, ":", 2) if len(parts) < 1 { continue } result[parts[0]] = true } return result, nil }