--- /dev/null
+package System::Introspector::Users;
+use Moo;
+use IPC::Run qw( run );
+
+sub gather {
+ my ($self) = @_;
+ my $fh = $self->_open_passwd_fh;
+ my %user;
+ while (defined( my $line = <$fh> )) {
+ my $data = $self->_deparse_htpasswd_line($line);
+ my $user = $data->{username};
+ my $home = $data->{home};
+ $data->{groups} = $self->_gather_user_groups($user);
+ $data->{ssh}{keys} = $self->_gather_ssh_keys($user, $home);
+ $data->{crontab} = $self->_gather_crontab($user);
+ $user{ $data->{uid} } = $data;
+ }
+ return \%user;
+}
+
+sub _gather_crontab {
+ my ($self, $user) = @_;
+ my ($in, $out, $err) = (('') x 3);
+ run(['crontab', '-u', $user, '-l'], \$in, \$out, \$err);
+ if (length $err) {
+ return {}
+ if $err =~ m{^no crontab}i;
+ return { error => $err };
+ }
+ return { body => $out };
+}
+
+sub _gather_ssh_keys {
+ my ($self, $user, $home) = @_;
+ my $ssh_dir = "$home/.ssh/";
+ return {}
+ unless -d $ssh_dir;
+ my $dh;
+ opendir $dh, $ssh_dir
+ or return { error => $! };
+ my %key;
+ while (defined( my $item = readdir $dh )) {
+ next unless $item =~ m{\.pub$};
+ my $item_path = "$ssh_dir/$item";
+ open my $key_fh, '<', $item_path;
+ if ($key_fh) {
+ $key{ $item }{body} = do { local $/; <$key_fh> };
+ }
+ else {
+ $key{ $item }{error} = $!;
+ }
+ }
+ return { files => \%key };
+}
+
+sub _gather_user_groups {
+ my ($self, $user) = @_;
+ return [split m{\s+}, `groups $user`];
+}
+
+sub _deparse_htpasswd_line {
+ my ($self, $line) = @_;
+ chomp $line;
+ my %value;
+ @value{qw( username uid gid comment home shell )}
+ = (split m{:}, $line)[0, 2..6];
+ return \%value;
+}
+
+sub _open_passwd_fh {
+ my ($self) = @_;
+ open my $fh, '<', '/etc/passwd'
+ or die "Unable to open /etc/passwd: $!\n";
+ return $fh;
+}
+
+1;