#! /usr/bin/env perl
# vim: set filetype=perl ts=4 sw=4 sts=4 et:
use common::sense;
use Sys::Hostname ();
use Term::ANSIColor;
use JSON;
use POSIX ();

use File::Basename;
use lib dirname(__FILE__) . '/../../../lib/perl';
use OVH::Result;
use OVH::Bastion;
use OVH::Bastion::Plugin qw( :DEFAULT help );

# globally allow sys_getpw* and sys_getgr* cache use
$ENV{'PW_GR_CACHE'} = 1;

my $withGroups       = 0;
my $withPasswordInfo = 0;
my $withEgressKeys   = 0;

sub toggle_all {
    my $v = shift;
    $withGroups       = $v;
    $withPasswordInfo = $v;
    $withEgressKeys   = $v;
    return;
}

OVH::Bastion::Plugin::begin(
    argv    => \@ARGV,
    header  => "account information",
    options => {
        'account=s'                 => \my $account,
        "all"                       => \my $all,
        "list-groups|with-groups"   => sub { $withGroups       = 1 },
        "without-groups"            => sub { $withGroups       = 0 },
        'with-mfa-password-info'    => sub { $withPasswordInfo = 1 },
        'without-mfa-password-info' => sub { $withPasswordInfo = 0 },
        'with-egress-keys'          => sub { $withEgressKeys   = 1 },
        'without-egress-keys'       => sub { $withEgressKeys   = 0 },
        'with-everything'           => sub { toggle_all(1) },
        'without-everything'        => sub { toggle_all(0) },
    },
    helptext => <<'EOF',
Display some information about an account

Usage: --osh SCRIPT_NAME <--account ACCOUNT|--all> [OPTIONS]

  --account ACCOUNT              The account name to work on
  --all                          Dump info for all accounts (auditors only), use with ``--json``

  --with[out]-everything         Include or exclude all below options, including future ones
  --with[out]-groups             Whether to include the groups the account has a role on (SLOW, default: no)
  --with[out]-mfa-password-info  Whether to include MFA password info of the account (SLOW, auditors only, default: no)
  --with[out]-egress-keys        Whether to include the account's egress keys (SLOW, auditors only, default: no)
EOF
);

my $fnret;

# check params

if ($account && $all) {
    osh_exit('ERR_INCOMPATIBLE_PARAMETERS', msg => "Can't use both --account and --all");
}

if (($all || $withPasswordInfo || $withEgressKeys) && !OVH::Bastion::is_auditor(account => $self)) {
    osh_exit('ERR_ACCESS_DENIED', msg => "This option can only be used by bastion auditors");
}

if (!$account && !$all) {
    help();
    osh_exit('ERR_MISSING_PARAMETER', msg => "Missing either --account or --all parameter");
}

# gather all accounts if $all, or only use the user-specified account if !$all

my @accountsToCheck;
if ($all) {
    $fnret = OVH::Bastion::get_account_list();
    $fnret or osh_exit $fnret;

    @accountsToCheck = sort keys %{$fnret->value};
    osh_info("Gathering data, this may take a few seconds...");
}
else {
    @accountsToCheck = ($account);
}

# validate each account and get their corresponding sys/remote name, while also untainting it

my @accounts;
foreach my $anAccount (@accountsToCheck) {
    $fnret = OVH::Bastion::is_bastion_account_valid_and_existing(account => $anAccount);
    $fnret or osh_exit $fnret;
    $account = $fnret->value->{'account'};
    my $sysaccount    = $fnret->value->{'sysaccount'};
    my $remoteaccount = $fnret->value->{'remoteaccount'};
    push @accounts, {account => $account, sysaccount => $sysaccount, remoteaccount => $remoteaccount};
}

# load these only once

$fnret = OVH::Bastion::get_plugin_list(restrictedOnly => 1);
$fnret or osh_exit $fnret;
my @commands = sort keys %{$fnret->value};

my @groups;
if ($withGroups) {
    $fnret = OVH::Bastion::get_group_list();
    $fnret or osh_exit $fnret;
    @groups = sort keys %{$fnret->value};
}

# gather info from the account(s)

my %return;
foreach my $accHash (@accounts) {
    my %ret;
    my ($account, $sysaccount, $remoteaccount) =
      ($accHash->{'account'}, $accHash->{'sysaccount'}, $accHash->{'remoteaccount'});

    $ret{'account'} = $account;
    if (OVH::Bastion::is_admin(account => $account)) {
        $ret{'is_admin'} = 1;
    }
    if (OVH::Bastion::is_super_owner(account => $account)) {
        $ret{'is_superowner'} = 1;
    }
    if (OVH::Bastion::is_auditor(account => $account)) {
        $ret{'is_auditor'} = 1;
    }

    my @granted;
    foreach my $plugin (@commands) {
        $fnret = OVH::Bastion::is_user_in_group(user => $account, group => "osh-$plugin");
        push @granted, $plugin if $fnret;
    }
    $ret{'allowed_commands'} = \@granted;

    my $groups_hash = {};
    if ($withGroups) {
        foreach my $name (@groups) {
            my @flags;
            push @flags, 'owner'      if OVH::Bastion::is_group_owner(group => $name, account => $account);
            push @flags, 'gatekeeper' if OVH::Bastion::is_group_gatekeeper(group => $name, account => $account);
            push @flags, 'aclkeeper'  if OVH::Bastion::is_group_aclkeeper(group => $name, account => $account);
            push @flags, 'member'     if OVH::Bastion::is_group_member(group => $name, account => $account);
            push @flags, 'guest'      if OVH::Bastion::is_group_guest(group => $name, account => $account);
            $groups_hash->{$name} = {flags => \@flags, name => $name} if @flags;
        }
    }
    $ret{'groups'} = $groups_hash;

    if (
        OVH::Bastion::account_config(
            account => $account,
            key     => OVH::Bastion::OPT_ACCOUNT_ALWAYS_ACTIVE,
            public  => 1
        )->value
      )
    {
        $ret{'always_active'}        = 1;
        $ret{'always_active_reason'} = 'account local configuration';
    }
    else {
        # maybe always_active through global configuration?
        my $alwaysActiveAccounts = OVH::Bastion::config('alwaysActiveAccounts');
        if ($alwaysActiveAccounts and $alwaysActiveAccounts->value) {
            if (grep { $sysaccount eq $_ } @{$alwaysActiveAccounts->value}) {
                $ret{'always_active'}        = 1;
                $ret{'always_active_reason'} = 'account listed in global configuration';
            }
        }
        else {
            $ret{'always_active'} = 0;
        }
    }

    my $canConnect = 1;
    $fnret = OVH::Bastion::is_account_active(account => $account);
    if ($fnret->is_ok) {
        $ret{'is_active'} = 1;
    }
    elsif ($fnret->is_ko) {
        $canConnect = 0;
        $ret{'is_active'} = 0;
    }

    if (OVH::Bastion::is_auditor(account => $self)) {

        # TTL check

        $fnret = OVH::Bastion::is_account_ttl_nonexpired(sysaccount => $sysaccount, account => $account);
        if ($fnret->is_ok && $fnret->err eq 'OK_NO_TTL') {
            $ret{'is_ttl_set'}     = 0;
            $ret{'is_ttl_expired'} = 0;
        }
        elsif ($fnret->is_ok && $fnret->err eq 'OK_TTL_VALID') {
            $ret{'is_ttl_set'}     = 1;
            $ret{'is_ttl_expired'} = 0;
        }
        elsif ($fnret->is_ko) {
            $canConnect            = 0;
            $ret{'is_ttl_set'}     = 1;
            $ret{'is_ttl_expired'} = 1;
        }
        else {
            osh_warn "Error getting account TTL expiration info (" . $fnret->msg . ")";
        }
        $ret{'ttl_timestamp'} =
          ($fnret->value && $fnret->value->{'expiry_time'}) ? $fnret->value->{'expiry_time'} : undef;

        # freeze check

        $fnret = OVH::Bastion::is_account_nonfrozen(account => $account);
        $ret{'is_frozen'} = undef;
        if ($fnret->is_ok) {
            $ret{'is_frozen'} = 0;
        }
        elsif ($fnret->is_ko) {
            $ret{'is_frozen'}   = 1;
            $ret{'freeze_info'} = $fnret->value;
            $canConnect         = 0;
        }

        # expi check

        $fnret = OVH::Bastion::is_account_nonexpired(sysaccount => $sysaccount, remoteaccount => $remoteaccount);
        if ($fnret->is_ok) {
            $ret{'is_expired'} = 0;
        }
        elsif ($fnret->is_ko) {
            $canConnect = 0;
            $ret{'is_expired'} = 1;
        }
        else {
            osh_warn "Error getting account expiration info (" . $fnret->msg . ")";
        }

        if (!$fnret->is_err) {
            $ret{'can_connect'} = $canConnect;
            if ($fnret->value->{'already_seen_before'}) {
                $ret{'already_seen_before'} = 1;
                if (defined $fnret->value->{'seconds'}) {
                    $fnret = OVH::Bastion::duration2human(seconds => $fnret->value->{'seconds'}, tense => "past");
                    if ($fnret) {
                        $ret{'last_activity'}{$_}          = $fnret->value->{$_} for qw{ datetime_local datetime_utc };
                        $ret{'last_activity'}{'ago'}       = $fnret->value->{'duration'};
                        $ret{'last_activity'}{'timestamp'} = time() - $fnret->value->{'seconds'};
                    }
                }
            }
            else {
                $ret{'already_seen_before'} = 0;
            }
        }

        $fnret = OVH::Bastion::account_config(account => $account, key => "creation_info");
        if ($fnret) {
            my $creation_info;
            eval { $creation_info = decode_json($fnret->value); };
            if ($@) {
                osh_warn(
                    "While reading creation metadata information for account '$account', couldn't decode JSON: $@");
            }
            else {
                $ret{'creation_information'} = $creation_info;
            }
        }

        $fnret = OVH::Bastion::account_ssh_config_get(account => $account);
        if ($fnret->err eq 'OK_EMPTY') {
            $ret{'account_egress_ssh_config'}{'type'} = 'default';
        }
        elsif ($fnret->err eq 'ERR_FILE_LOCALLY_MODIFIED') {
            $ret{'account_egress_ssh_config'}{'type'} = 'locally_modified';
        }
        elsif ($fnret) {
            $ret{'account_egress_ssh_config'}{'type'} = 'custom';
            foreach my $key (sort keys %{$fnret->value}) {
                $ret{'account_egress_ssh_config'}{'items'}{$key} = $fnret->value->{$key};
            }
        }
        else {
            $ret{'account_egress_ssh_config'}{'type'} = 'unknown';
        }

        $fnret = OVH::Bastion::account_config(
            account => $account,
            public  => 1,
            key     => OVH::Bastion::OPT_ACCOUNT_INGRESS_PIV_POLICY
        );
        $ret{'ingress_piv_enforced'} = ($fnret && $fnret->value eq 'yes') ? 1 : 0;    # keep for backwards compat
        $ret{'ingress_piv_policy'}   = $fnret->value || undef;

        $fnret = OVH::Bastion::account_config(
            account => $account,
            public  => 1,
            key     => OVH::Bastion::OPT_ACCOUNT_INGRESS_PIV_GRACE
        );
        if ($fnret && $fnret->value > time()) {
            my $expiry = $fnret->value - time();
            my $human  = OVH::Bastion::duration2human(seconds => $expiry)->value;
            $ret{'ingress_piv_grace'} = {
                enabled              => 1,
                expiration_timestamp => $fnret->value,
                seconds_remaining    => $expiry,
                expiration_date      => $human->{'date'},
                time_remaining       => $human->{'duration'},
            };
        }
        else {
            $ret{'ingress_piv_grace'} = {enabled => 0};
        }

        $ret{'global_ingress_policy'} = !!OVH::Bastion::config('ingressRequirePIV')->value + 0;

        $ret{'effective_ingress_piv_policy'} =
          !!OVH::Bastion::is_effective_piv_account_policy_enabled(account => $account)->is_ok + 0;

        $ret{'mfa_password_required'} =
          !!OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_PASSWORD_REQUIRED_GROUP) + 0;
        $ret{'mfa_password_bypass'} =
          !!OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_PASSWORD_BYPASS_GROUP) + 0;
        $ret{'mfa_password_configured'} =
          !!OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_PASSWORD_CONFIGURED_GROUP) + 0;

        $ret{'mfa_totp_required'} =
          !!OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_TOTP_REQUIRED_GROUP) + 0;
        $ret{'mfa_totp_bypass'} =
          !!OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_TOTP_BYPASS_GROUP) + 0;
        $ret{'mfa_totp_configured'} =
          !!OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_TOTP_CONFIGURED_GROUP) + 0;

        $ret{'pam_auth_bypass'} =
          !!OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::PAM_AUTH_BYPASS_GROUP) + 0;

        $ret{'pubkey_auth_optional'} =
          !!OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::OSH_PUBKEY_AUTH_OPTIONAL_GROUP) + 0;

        $ret{'personal_egress_mfa_required'} =
          OVH::Bastion::account_config(account => $account, key => "personal_egress_mfa_required")->value;
        $ret{'personal_egress_mfa_required'} ||= 'none';    # no config means no mfa

        $ret{'idle_ignore'} = !!OVH::Bastion::account_config(
            account => $account,
            key     => OVH::Bastion::OPT_ACCOUNT_IDLE_IGNORE,
            public  => 1
        ) + 0;

        $ret{'max_inactive_days'} =
          OVH::Bastion::account_config(account => $account, %{OVH::Bastion::OPT_ACCOUNT_MAX_INACTIVE_DAYS()})->value;

        $ret{'osh_only'} = !!OVH::Bastion::account_config(
            account => $account,
            key     => OVH::Bastion::OPT_ACCOUNT_OSH_ONLY
        ) + 0;

        if ($withPasswordInfo) {
            my @command = qw{ sudo -n -u root -- /usr/bin/env perl -T };
            push @command, $OVH::Bastion::BASEPATH . '/bin/helper/osh-accountGetPasswordInfo';
            push @command, '--account', $account;
            $fnret = OVH::Bastion::helper(cmd => \@command);
            if ($fnret) {
                $ret{'password'}{$_} = $fnret->value->{$_} for (keys %{$fnret->value});
            }
        }
    }

    $return{$account} = \%ret;

    # print all this in a human-readable format, except if we've been asked
    # to dump the data for all accounts, in which case the caller will only use
    # our JSON output
    print_account_info(%ret) if !$all;
}

sub print_account_info {
    my %ret = @_;

    my $acc = $ret{'account'};
    osh_info("$acc is a bastion " . colored('admin', 'green')) if $ret{'is_admin'};
    osh_info("$account is a bastion " . colored('superowner', 'green')) if $ret{'is_superowner'};
    osh_info("$account is a bastion " . colored('auditor',    'green')) if $ret{'is_auditor'};

    osh_info "This account has access to the following restricted commands:";

    osh_info("- $_") for @{$ret{'allowed_commands'}};
    osh_info "(none)" if (!@{$ret{'allowed_commands'}});

    if ($withGroups) {
        osh_info("\nThis account is part of the following groups:");
        foreach my $groupName (sort keys %{$ret{'groups'}}) {
            my @flags = @{$ret{'groups'}{$groupName}{'flags'}};
            my $line  = sprintf "%18s", $groupName;
            $line .= sprintf " %14s", colored(grep({ $_ eq 'owner' } @flags)      ? 'Owner'      : '-', 'red');
            $line .= sprintf " %19s", colored(grep({ $_ eq 'gatekeeper' } @flags) ? 'GateKeeper' : '-', 'yellow');
            $line .= sprintf " %18s", colored(grep({ $_ eq 'aclkeeper' } @flags)  ? 'ACLKeeper'  : '-', 'magenta');
            $line .= sprintf " %15s", colored(grep({ $_ eq 'member' } @flags)     ? 'Member'     : '-', 'green');
            $line .= sprintf " %14s", colored(grep({ $_ eq 'guest' } @flags)      ? 'Guest'      : '-', 'cyan');
            osh_info($line);
        }
        osh_info("(none)") if not %{$ret{'groups'}};
        osh_info("\n");
    }

    if ($ret{'always_active'}) {
        osh_info("This account is " . colored('always', 'green') . " active");
    }
    elsif ($ret{'is_active'}) {
        osh_info("This account is " . colored('active', 'green'));
    }
    else {
        osh_info "\nThis account is " . colored('INACTIVE', 'red');
    }

    if (defined $ret{'is_ttl_set'}) {
        my $human = '?';
        if (!$ret{'is_ttl_set'}) {
            osh_info("This account has " . colored('no TTL set', 'green'));
        }
        elsif (!$ret{'is_ttl_expired'}) {
            $fnret = OVH::Bastion::duration2human(seconds => $ret{'ttl_timestamp'} - time());
            $human = $fnret->value->{'human'};
            osh_info "This account " . colored('TTL is still valid', 'green') . " (for $human)";
        }
        else {
            $fnret = OVH::Bastion::duration2human(seconds => time() - $ret{'ttl_timestamp'}, tense => "past");
            $human = $fnret->value->{'human'};
            osh_info "This account " . colored('TTL is EXPIRED', 'red') . " (since $human)";
        }
    }

    if (defined $ret{'is_frozen'}) {
        if (!$ret{'is_frozen'}) {
            osh_info "This account is " . colored('not frozen', 'green');
        }
        else {
            my $freezeReason = $ret{'freeze_info'}{'reason'} || 'no reason given';
            my $freezeBy     = $ret{'freeze_info'}{'by'}     || '(unknown)';
            osh_info "This account has been " . colored('FROZEN', 'red') . " by $freezeBy ($freezeReason)";
        }
    }

    if (defined $ret{'is_expired'}) {
        if ($ret{'is_expired'}) {
            osh_info "This account is "
              . colored('EXPIRED', 'red')
              . " activity-wise (it hasn't been seen for a long time)";
        }
        else {
            osh_info "This account has seen recent-enough activity to " . colored('not be activity-expired', 'green');
        }
    }

    if (defined $ret{'can_connect'}) {
        osh_info "As a consequence, this account "
          . ($ret{'can_connect'} ? colored("can", 'green') : colored("CANNOT", 'red'))
          . " connect to this bastion\n\n";
    }

    if (defined $ret{'already_seen_before'}) {
        if ($ret{'already_seen_before'}) {
            if ($ret{'last_activity'}) {
                my $seenBeforeStr = $ret{'last_activity'}{'datetime_utc'};
                if (   $ret{'last_activity'}{'datetime_local'}
                    && $ret{'last_activity'}{'datetime_utc'} ne $ret{'last_activity'}{'datetime_local'})
                {
                    $seenBeforeStr .= " / " . $ret{'last_activity'}{'datetime_local'};
                }
                $seenBeforeStr = sprintf("Last seen on %s (%s ago)", colored($seenBeforeStr, 'magenta'),
                    $ret{'last_activity'}{'ago'},);
                osh_info($seenBeforeStr);
            }
            else {
                osh_info("This account has already been used at least once");
            }
        }
        else {
            osh_info("This account has " . colored('NEVER', 'red') . " been used (yet)");
        }
    }

    if (defined $ret{'creation_information'}) {
        if ($ret{'creation_information'}{'datetime_utc'}) {
            my $createdOnStr = $ret{'creation_information'}{'datetime_utc'};
            if (   $ret{'creation_information'}{'datetime_local'}
                && $ret{'creation_information'}{'datetime_utc'} ne $ret{'creation_information'}{'datetime_local'})
            {
                $createdOnStr .= " / " . $ret{'creation_information'}{'datetime_local'};
            }
            $createdOnStr = sprintf(
                "Created on %s (%s ago)",
                colored($createdOnStr, 'magenta'),
                OVH::Bastion::duration2human(seconds => time() - $ret{'creation_information'}{'timestamp'})
                  ->value->{'duration'}
            );
            osh_info($createdOnStr);
        }
        if ($ret{'creation_information'}{'by'}) {
            osh_info("Created by " . colored($ret{'creation_information'}{'by'}, 'magenta'));
        }
        if ($ret{'creation_information'}{'bastion_version'}) {
            osh_info("Created using The Bastion "
                  . colored('v' . $ret{'creation_information'}{'bastion_version'}, 'magenta'));
        }
        if ($ret{'creation_information'}{'comment'}) {
            osh_info(
                "Creation with the following comment: " . colored($ret{'creation_information'}{'comment'}, 'magenta'));
        }
    }

    if (defined $ret{'account_egress_ssh_config'}) {
        osh_info "\nAccount egress SSH config:";
        if ($ret{'account_egress_ssh_config'}{'type'} eq 'default') {
            osh_info "- (default)";
        }
        elsif ($ret{'account_egress_ssh_config'}{'type'} eq 'locally_modified') {
            osh_info "- (locally modified!)";
        }
        elsif ($ret{'account_egress_ssh_config'}{'type'} eq 'custom') {
            foreach my $key (sort keys %{$ret{'account_egress_ssh_config'}{'items'} || {}}) {
                osh_info "- $key " . $ret{'account_egress_ssh_config'}{'items'}{$key};
            }
        }
        else {
            osh_info "- (unknown)";
        }
    }

    if (defined $ret{'osh_only'}) {
        osh_info "\nThis account can only run commands (\"osh-only\"): "
          . ($ret{'osh_only'} ? colored('yes', 'red') : colored('no', 'blue'));
    }

    if (exists $ret{'ingress_piv_policy'} && exists $ret{'ingress_piv_grace'}) {
        osh_info "\nAccount PIV-only policy status:";
        my $ingress_piv_policy_print = $ret{'ingress_piv_policy'} || 'default';
        osh_info "- PIV policy for ingress keys on this account is set to "
          . colored($ingress_piv_policy_print, $ingress_piv_policy_print eq 'default' ? 'blue' : 'green');

        if ($ret{'ingress_piv_grace'} && $ret{'ingress_piv_grace'}{'seconds_remaining'}) {
            $fnret = OVH::Bastion::duration2human(seconds => $ret{'ingress_piv_grace'}{'seconds_remaining'})->value;
            osh_info("- PIV grace period for this account is "
                  . colored('set', 'green')
                  . " and expires in "
                  . $fnret->value->{'human'});
        }
        else {
            osh_info "- PIV grace period for this account is " . colored('inactive', 'blue');
        }
    }

    if (defined $ret{'global_ingress_policy'}) {
        osh_info "- Global PIV policy status is "
          . ($ret{'global_ingress_policy'} ? colored('enabled', 'red') : colored('disabled', 'blue'));
    }

    if (defined $ret{'effective_ingress_piv_policy'}) {
        osh_info "- As a consequence, PIV policy is "
          . ($ret{'effective_ingress_piv_policy'} ? colored('enforced', 'red') : colored('inactive', 'blue'))
          . " for this account";
    }

    if (exists $ret{'mfa_password_required'} && exists $ret{'mfa_totp_required'} && exists $ret{'pam_auth_bypass'}) {
        osh_info "\nAccount Multi-Factor Authentication status:";
        osh_info "- Additional password authentication is "
          . ($ret{'mfa_password_required'} ? colored('required', 'green') : colored('not required', 'blue'))
          . " for this account";
        osh_info "- Additional password authentication bypass is "
          . ($ret{'mfa_password_bypass'} ? colored('enabled', 'green') : colored('disabled', 'blue'))
          . " for this account";
        osh_info "- Additional password authentication is "
          . ($ret{'mfa_password_configured'} ? colored('enabled and active', 'green') : colored('disabled', 'blue'));

        osh_info "- Additional TOTP authentication is "
          . ($ret{'mfa_totp_required'} ? colored('required', 'green') : colored('not required', 'blue'))
          . " for this account";
        osh_info "- Additional TOTP authentication bypass is "
          . ($ret{'mfa_totp_bypass'} ? colored('enabled', 'green') : colored('disabled', 'blue'))
          . " for this account";
        osh_info "- Additional TOTP authentication is "
          . ($ret{'mfa_totp_configured'} ? colored('enabled and active', 'green') : colored('disabled', 'blue'));

        osh_info "- PAM authentication bypass is "
          . ($ret{'pam_auth_bypass'} ? colored('enabled', 'green') : colored('disabled', 'blue'));

        osh_info "- Optional public key authentication is "
          . ($ret{'pubkey_auth_optional'} ? colored('enabled', 'green') : colored('disabled', 'blue'));

        osh_info "- MFA policy on personal accesses (using personal keys) on egress side is: "
          . $ret{'personal_egress_mfa_required'};
    }

    if (exists $ret{'idle_ignore'}) {
        osh_info "\n- Account is immune to idle counter-measures: "
          . ($ret{'idle_ignore'} ? colored('yes', 'green') : colored('no', 'blue'));
    }

    if (exists $ret{'max_inactive_days'}) {
        if (!defined $ret{'max_inactive_days'}) {
            osh_info "- Maximum number of days of inactivity before account is disabled: (default)";
        }
        elsif ($ret{'max_inactive_days'} == 0) {
            osh_info "- Maximum number of days of inactivity before account is disabled: never";
        }
        else {
            osh_info "- Maximum number of days of inactivity before account is disabled: " . $ret{'max_inactive_days'};
        }
    }

    if (defined $ret{'password'}) {
        osh_info "Account PAM UNIX password information (used for password MFA):";
        if ($ret{'password'}{'password'} eq 'locked') {
            osh_info "- No valid password is set";
        }
        else {
            osh_info "- Password is " . $ret{'password'}{'password'};
        }
        osh_info "- Password was last changed on " . $ret{'password'}{'date_changed'};
        if ($ret{'password'}{'max_days'} == -1) {
            osh_info "- Password will never expire";
        }
        else {
            osh_info "- Password must be changed every " . $ret{'password'}{'max_days'} . " days at least";
            osh_info "- A warning is displayed " . $ret{'password'}{'warn_days'} . " days before expiration";
        }
        if ($ret{'password'}{'min_days'} != 0) {
            osh_info "- The minimum time between two password changes is " . $ret{'password'}{'min_days'} . " days";
        }
        if ($ret{'password'}{'max_days'} != -1) {
            if ($ret{'password'}{'inactive_days'} != -1) {
                osh_info "- Account will be disabled "
                  . $ret{'password'}{'inactive_days'}
                  . " days after password expiration";
            }
            else {
                osh_info "- Account will not be disabled after password expiration";
            }
        }
    }

    return;
}

if (!$all) {
    # only one account, don't return a hash of hash to keep backward compat
    my @keys = keys %return;
    osh_ok $return{$keys[0]};
}
else {
    osh_info "If you're only seeing this line, you might want to use --json";
    osh_ok \%return;
}
