From 8cc990ad5733d8eed06459e089c53c2a0571b394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lesimple?= Date: Wed, 31 Mar 2021 06:54:15 +0000 Subject: [PATCH] feat: add filtering options to several cmds,nicify print_acls() The commands selfListAccesses, accountListAccesses, groupList, groupListServers, groupListGuestAccesses and accountList now have options to filter their output through pattern matching, with --include and --exclude. The output from the commands using print_acls() is also more human-friendly, with auto-adjusting column length, and empty columns omitted. Closes #60. --- .../group-gatekeeper/groupListGuestAccesses | 23 +++- bin/plugin/open/groupList | 18 +-- bin/plugin/open/groupListServers | 26 +++-- bin/plugin/open/selfListAccesses | 23 ++-- bin/plugin/restricted/accountList | 18 +-- bin/plugin/restricted/accountListAccesses | 50 ++++---- .../groupListGuestAccesses.rst | 19 ++++ doc/sphinx/plugins/open/groupList.rst | 18 +-- doc/sphinx/plugins/open/groupListServers.rst | 23 +++- doc/sphinx/plugins/open/selfListAccesses.rst | 24 +++- doc/sphinx/plugins/restricted/accountList.rst | 18 +-- .../restricted/accountListAccesses.rst | 19 +++- lib/perl/OVH/Bastion/allowdeny.inc | 107 ++++++++++++++---- lib/perl/OVH/Bastion/allowkeeper.inc | 10 +- tests/functional/tests.d/350-groups.sh | 12 ++ tests/unit/run.pl | 12 ++ 16 files changed, 316 insertions(+), 104 deletions(-) diff --git a/bin/plugin/group-gatekeeper/groupListGuestAccesses b/bin/plugin/group-gatekeeper/groupListGuestAccesses index 70366c9..ba36416 100755 --- a/bin/plugin/group-gatekeeper/groupListGuestAccesses +++ b/bin/plugin/group-gatekeeper/groupListGuestAccesses @@ -8,14 +8,15 @@ use OVH::Result; use OVH::Bastion; use OVH::Bastion::Plugin qw( :DEFAULT help ); -my ($group, $account, $reverse); my $remainingOptions = OVH::Bastion::Plugin::begin( argv => \@ARGV, - header => "lists partial access to group servers of a bastion account", + header => "lists guest accesses to group servers of a bastion account", options => { - "group=s" => \$group, - "account=s" => \$account, - "reverse-dns" => \$reverse, + "group=s" => \my $group, + "account=s" => \my $account, + "reverse-dns" => \my $reverse, + "include=s" => \my @includes, + "exclude=s" => \my @excludes, }, helptext => <<'EOF', List the guest accesses to servers of a group specifically granted to an account @@ -24,6 +25,16 @@ Usage: --osh SCRIPT_NAME --group GROUP --account ACCOUNT --group GROUP Look for accesses to servers of this GROUP --account ACCOUNT Which account to check + --reverse-dns Attempt to resolve the reverse hostnames (SLOW!) + --include PATTERN Only include servers matching the given PATTERN (see below) + This option can be used multiple times to refine results + --exclude PATTERN Omit servers matching the given PATTERN (see below) + This option can be used multiple times. + Note that --exclude takes precedence over --include + +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. +The matching is done on the text output of the command. EOF ); @@ -48,5 +59,5 @@ if (not @{$fnret->value}) { osh_ok R('OK_EMPTY', msg => "This account doesn't seem to have any guest access to this group"); } -OVH::Bastion::print_acls(acls => [{type => 'group-guest', group => $shortGroup, acl => $fnret->value}], reverse => $reverse); +OVH::Bastion::print_acls(acls => [{type => 'group-guest', group => $shortGroup, acl => $fnret->value}], reverse => $reverse, includes => \@includes, excludes => \@excludes); osh_ok($fnret->value); diff --git a/bin/plugin/open/groupList b/bin/plugin/open/groupList index ca6f7ae..3099b55 100755 --- a/bin/plugin/open/groupList +++ b/bin/plugin/open/groupList @@ -20,13 +20,17 @@ my $remainingOptions = OVH::Bastion::Plugin::begin( helptext => <<'EOF', List the groups available on this bastion -Usage: --osh SCRIPT_NAME [--all] [--exclude|--include WILDCARD [--exclude|--include WILDCARD ..]] +Usage: --osh SCRIPT_NAME [--all] [--exclude|--include PATTERN [--exclude|--include PATTERN ..]] --all List all groups, even those to which you don't have access - --include WILDCARD Only list groups that match the given WILDCARD string, '*' and '?' are recognized, - this option can be used multiple times to refine results. - --exclude WILDCARD Omit groups that match the given WILDCARD string, '*' and '?' are recognized, - can be used multiple times. Note that --exclude takes precedence over --include + --include PATTERN Only list groups that match the given PATTERN (see below) + This option can be used multiple times to refine results + --exclude PATTERN Omit groups that match the given PATTERN string (see below) + This option can be used multiple times. + Note that --exclude takes precedence over --include + +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. EOF ); @@ -35,8 +39,8 @@ my $fnret; $fnret = OVH::Bastion::get_group_list(groupType => "key"); $fnret or osh_exit $fnret; -my $includere = OVH::Bastion::build_re_from_wildcards(wildcards => \@includes)->value; -my $excludere = OVH::Bastion::build_re_from_wildcards(wildcards => \@excludes)->value; +my $includere = OVH::Bastion::build_re_from_wildcards(wildcards => \@includes, implicit_contains => 1)->value; +my $excludere = OVH::Bastion::build_re_from_wildcards(wildcards => \@excludes, implicit_contains => 1)->value; my $result_hash = {}; foreach my $name (sort keys %{$fnret->value}) { diff --git a/bin/plugin/open/groupListServers b/bin/plugin/open/groupListServers index aa6f3c8..77a65d8 100755 --- a/bin/plugin/open/groupListServers +++ b/bin/plugin/open/groupListServers @@ -8,21 +8,31 @@ use OVH::Result; use OVH::Bastion; use OVH::Bastion::Plugin qw( :DEFAULT help ); -my ($group, $reverse); my $remainingOptions = OVH::Bastion::Plugin::begin( argv => \@ARGV, header => "list of servers pertaining to the group", options => { - "group=s" => \$group, - "reverse-dns" => \$reverse, + "group=s" => \my $group, + "reverse-dns" => \my $reverse, + "include=s" => \my @includes, + "exclude=s" => \my @excludes, }, helptext => <<'EOF', List the servers (IPs and IP blocks) pertaining to a group Usage: --osh SCRIPT_NAME --group GROUP [--reverse-dns] - --group GROUP List the servers of this group - --reverse-dns Resolve and display the reverse DNS of each IP (SLOW!) + --group GROUP List the servers of this group + --reverse-dns Attempt to resolve the reverse hostnames (SLOW!) + --include PATTERN Only include servers matching the given PATTERN (see below) + This option can be used multiple times to refine results + --exclude PATTERN Omit servers matching the given PATTERN (see below) + This option can be used multiple times. + Note that --exclude takes precedence over --include + +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. +The matching is done on the text output of the command. EOF ); @@ -51,8 +61,8 @@ if ( osh_exit( R( 'KO_ACCESS_DENIED', - msg => -"Sorry, you're neither a member or aclkeeper of group $shortGroup, you can't list the servers of this group.\nIf you think you should be able to, use groupInfo to get contact info." + msg => "Sorry, you're neither a member or aclkeeper of group $shortGroup, you can't list the servers of this group.\n" + . "If you think you should be able to, use groupInfo to get contact info." ) ); } @@ -64,5 +74,5 @@ if (not @{$fnret->value}) { osh_ok R('OK_EMPTY', msg => "This group is empty, if you are an aclkeeper of this group, you might want to add servers to it with groupAddServer"); } -OVH::Bastion::print_acls(acls => [{type => 'group', group => $shortGroup, acl => $fnret->value}], reverse => $reverse); +OVH::Bastion::print_acls(acls => [{type => 'group', group => $shortGroup, acl => $fnret->value}], reverse => $reverse, includes => \@includes, excludes => \@excludes); osh_ok($fnret->value); diff --git a/bin/plugin/open/selfListAccesses b/bin/plugin/open/selfListAccesses index ef31f43..f4ac2cb 100755 --- a/bin/plugin/open/selfListAccesses +++ b/bin/plugin/open/selfListAccesses @@ -8,23 +8,32 @@ use OVH::Result; use OVH::Bastion; use OVH::Bastion::Plugin qw( :DEFAULT help ); -my ($hideGroups, $reverse); my $remainingOptions = OVH::Bastion::Plugin::begin( argv => \@ARGV, header => "your access list", options => { - "hide-groups" => \$hideGroups, - "reverse-dns" => \$reverse, + "hide-groups" => \my $hideGroups, + "reverse-dns" => \my $reverse, + "include=s" => \my @includes, + "exclude=s" => \my @excludes, }, helptext => <<'EOF', Show the list of servers you have access to Usage: --osh SCRIPT_NAME [--hide-groups] [--reverse-dns] - --hide-groups Don't show the machines you have access to through group rights. In other words, - list only your private accesses. + --hide-groups Don't show the machines you have access to through group rights. + In other words, list only your personal accesses. + --reverse-dns Attempt to resolve the reverse hostnames (SLOW!) + --include PATTERN Only include accesses matching the given PATTERN (see below) + This option can be used multiple times to refine results + --exclude PATTERN Omit accesses matching the given PATTERN (see below) + This option can be used multiple times. + Note that --exclude takes precedence over --include - --reverse-dns Attempt to resolve the reverse hostnames (SLOW!) +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. +The matching is done on the text output of the command. EOF ); @@ -39,5 +48,5 @@ if (not @{$fnret->value}) { osh_info "Dear $self, you have access to the following servers:\n"; -OVH::Bastion::print_acls(acls => $fnret->value, reverse => $reverse, hideGroups => $hideGroups); +OVH::Bastion::print_acls(acls => $fnret->value, reverse => $reverse, hideGroups => $hideGroups, includes => \@includes, excludes => \@excludes); osh_ok($fnret); diff --git a/bin/plugin/restricted/accountList b/bin/plugin/restricted/accountList index 250b43f..9f302e0 100755 --- a/bin/plugin/restricted/accountList +++ b/bin/plugin/restricted/accountList @@ -23,15 +23,19 @@ my $remainingOptions = OVH::Bastion::Plugin::begin( helptext => <<'EOF', List the bastion accounts -Usage: --osh SCRIPT_NAME [--account ACCOUNT] [--inactive-only] [--audit] +Usage: --osh SCRIPT_NAME [OPTIONS] --account ACCOUNT Only list the specified account. This is an easy way to check whether the account exists --inactive-only Only list inactive accounts --audit Show more verbose information (SLOW!), you need to be a bastion auditor - --include WILDCARD Only list accounts that match the given WILDCARD string, '*' and '?' are recognized, - this option can be used multiple times to refine results. - --exclude WILDCARD Omit accounts that match the given WILDCARD string, '*' and '?' are recognized, - can be used multiple times. Note that --exclude takes precedence over --include + --include PATTERN Only show accounts whose name match the given PATTERN (see below) + This option can be used multiple times to refine results + --exclude PATTERN Omit accounts whose name match the given PATTERN (see below) + This option can be used multiple times. + Note that --exclude takes precedence over --include + +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. EOF ); @@ -70,8 +74,8 @@ if ($audit) { } # if we have excludes and/or includes, transform those into regexes -my $includere = OVH::Bastion::build_re_from_wildcards(wildcards => \@includes)->value; -my $excludere = OVH::Bastion::build_re_from_wildcards(wildcards => \@excludes)->value; +my $includere = OVH::Bastion::build_re_from_wildcards(wildcards => \@includes, implicit_contains => 1)->value; +my $excludere = OVH::Bastion::build_re_from_wildcards(wildcards => \@excludes, implicit_contains => 1)->value; my $result_hash = {}; foreach my $account (sort keys %$accounts) { diff --git a/bin/plugin/restricted/accountListAccesses b/bin/plugin/restricted/accountListAccesses index b71c9a2..17f2271 100755 --- a/bin/plugin/restricted/accountListAccesses +++ b/bin/plugin/restricted/accountListAccesses @@ -8,14 +8,15 @@ use OVH::Result; use OVH::Bastion; use OVH::Bastion::Plugin qw( :DEFAULT help ); -my ($hideGroups, $reverse, $account); my $remainingOptions = OVH::Bastion::Plugin::begin( argv => \@ARGV, header => "access list of a bastion account", options => { - "hide-groups" => \$hideGroups, - "reverse-dns" => \$reverse, - "account=s" => \$account, + "account=s" => \my $account, + "hide-groups" => \my $hideGroups, + "reverse-dns" => \my $reverse, + "include=s" => \my @includes, + "exclude=s" => \my @excludes, }, helptext => <<'EOF', View the expanded access list of a given bastion account @@ -23,11 +24,18 @@ View the expanded access list of a given bastion account Usage: --osh SCRIPT_NAME --account ACCOUNT [--hide-groups] [--reverse-dns] --account ACCOUNT The account to work on - --hide-groups Don't show the machines the accouns has access to through group rights. - In other words, list only the account's private accesses. - + In other words, list only the account's personal accesses. --reverse-dns Attempt to resolve the reverse hostnames (SLOW!) + --include PATTERN Only include accesses matching the given PATTERN (see below) + This option can be used multiple times to refine results + --exclude PATTERN Omit accesses matching the given PATTERN (see below) + This option can be used multiple times. + Note that --exclude takes precedence over --include + +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. +The matching is done on the text output of the command. EOF ); @@ -42,23 +50,21 @@ $fnret = OVH::Bastion::is_bastion_account_valid_and_existing(account => $account $fnret or osh_exit $fnret; $account = $fnret->value->{'account'}; -# FIXME won't be able to see group accesses caller is not a member of -my $accessListResult = OVH::Bastion::get_acls(account => $account); -$accessListResult or osh_exit $accessListResult; +$fnret = OVH::Bastion::get_acls(account => $account); +$fnret or osh_exit $fnret; -if (not @{$accessListResult->value}) { +if (!OVH::Bastion::is_auditor(account => $self)) { + osh_info "NOTE: you're not a bastion auditor, hence you won't be able to"; + osh_info "see access lists of groups you're not yourself a member of."; + osh_info ' '; +} + +if (not @{$fnret->value}) { osh_ok R('OK_EMPTY', msg => "This account has no registered accesses to machines through this bastion yet"); } -else { - osh_info "NOTE: KNOWN LIMITATION in this version: you won't be able to"; - osh_info "see access lists of group you're not yourself a member of."; - osh_info "For such cases, an error 'unable to access' will be printed."; - osh_info ' '; - osh_info "This account has access to the following servers:"; - osh_info ' '; - OVH::Bastion::print_acls(acls => $accessListResult->value, reverse => $reverse, hideGroups => $hideGroups); - osh_ok($accessListResult); -} +osh_info "This account has access to the following servers:"; +osh_info ' '; -osh_exit 'ERR_INTERNAL'; +OVH::Bastion::print_acls(acls => $fnret->value, reverse => $reverse, hideGroups => $hideGroups, includes => \@includes, excludes => \@excludes); +osh_ok($fnret); diff --git a/doc/sphinx/plugins/group-gatekeeper/groupListGuestAccesses.rst b/doc/sphinx/plugins/group-gatekeeper/groupListGuestAccesses.rst index 46438ed..416da67 100644 --- a/doc/sphinx/plugins/group-gatekeeper/groupListGuestAccesses.rst +++ b/doc/sphinx/plugins/group-gatekeeper/groupListGuestAccesses.rst @@ -22,6 +22,25 @@ List the guest accesses to servers of a group specifically granted to an account Which account to check +.. option:: --reverse-dns + + Attempt to resolve the reverse hostnames (SLOW!) + +.. option:: --include PATTERN + + Only include servers matching the given PATTERN (see below) + + This option can be used multiple times to refine results +.. option:: --exclude PATTERN + + Omit servers matching the given PATTERN (see below) + + This option can be used multiple times. + Note that --exclude takes precedence over --include + +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. +The matching is done on the text output of the command. diff --git a/doc/sphinx/plugins/open/groupList.rst b/doc/sphinx/plugins/open/groupList.rst index 9d70024..4f8c114 100644 --- a/doc/sphinx/plugins/open/groupList.rst +++ b/doc/sphinx/plugins/open/groupList.rst @@ -9,7 +9,7 @@ List the groups available on this bastion .. admonition:: usage :class: cmdusage - --osh groupList [--all] [--exclude|--include WILDCARD [--exclude|--include WILDCARD ..]] + --osh groupList [--all] [--exclude|--include PATTERN [--exclude|--include PATTERN ..]] .. program:: groupList @@ -18,16 +18,20 @@ List the groups available on this bastion List all groups, even those to which you don't have access -.. option:: --include WILDCARD +.. option:: --include PATTERN - Only list groups that match the given WILDCARD string, '*' and '?' are recognized, + Only list groups that match the given PATTERN (see below) - this option can be used multiple times to refine results. -.. option:: --exclude WILDCARD + This option can be used multiple times to refine results +.. option:: --exclude PATTERN - Omit groups that match the given WILDCARD string, '*' and '?' are recognized, + Omit groups that match the given PATTERN string (see below) - can be used multiple times. Note that --exclude takes precedence over --include + This option can be used multiple times. + Note that --exclude takes precedence over --include + +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. diff --git a/doc/sphinx/plugins/open/groupListServers.rst b/doc/sphinx/plugins/open/groupListServers.rst index 9ddebf9..be36e21 100644 --- a/doc/sphinx/plugins/open/groupListServers.rst +++ b/doc/sphinx/plugins/open/groupListServers.rst @@ -14,14 +14,29 @@ List the servers (IPs and IP blocks) pertaining to a group .. program:: groupListServers -.. option:: --group GROUP +.. option:: --group GROUP List the servers of this group -.. option:: --reverse-dns - - Resolve and display the reverse DNS of each IP (SLOW!) +.. option:: --reverse-dns + Attempt to resolve the reverse hostnames (SLOW!) + +.. option:: --include PATTERN + + Only include servers matching the given PATTERN (see below) + + This option can be used multiple times to refine results +.. option:: --exclude PATTERN + + Omit servers matching the given PATTERN (see below) + + This option can be used multiple times. + Note that --exclude takes precedence over --include + +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. +The matching is done on the text output of the command. diff --git a/doc/sphinx/plugins/open/selfListAccesses.rst b/doc/sphinx/plugins/open/selfListAccesses.rst index ed55a49..8639dbd 100644 --- a/doc/sphinx/plugins/open/selfListAccesses.rst +++ b/doc/sphinx/plugins/open/selfListAccesses.rst @@ -14,16 +14,30 @@ Show the list of servers you have access to .. program:: selfListAccesses -.. option:: --hide-groups +.. option:: --hide-groups - Don't show the machines you have access to through group rights. In other words, + Don't show the machines you have access to through group rights. - list only your private accesses. - -.. option:: --reverse-dns + In other words, list only your personal accesses. +.. option:: --reverse-dns Attempt to resolve the reverse hostnames (SLOW!) +.. option:: --include PATTERN + + Only include accesses matching the given PATTERN (see below) + + This option can be used multiple times to refine results +.. option:: --exclude PATTERN + + Omit accesses matching the given PATTERN (see below) + + This option can be used multiple times. + Note that --exclude takes precedence over --include + +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. +The matching is done on the text output of the command. diff --git a/doc/sphinx/plugins/restricted/accountList.rst b/doc/sphinx/plugins/restricted/accountList.rst index 44a7160..5166323 100644 --- a/doc/sphinx/plugins/restricted/accountList.rst +++ b/doc/sphinx/plugins/restricted/accountList.rst @@ -9,7 +9,7 @@ List the bastion accounts .. admonition:: usage :class: cmdusage - --osh accountList [--account ACCOUNT] [--inactive-only] [--audit] + --osh accountList [OPTIONS] .. program:: accountList @@ -26,16 +26,20 @@ List the bastion accounts Show more verbose information (SLOW!), you need to be a bastion auditor -.. option:: --include WILDCARD +.. option:: --include PATTERN - Only list accounts that match the given WILDCARD string, '*' and '?' are recognized, + Only show accounts whose name match the given PATTERN (see below) - this option can be used multiple times to refine results. -.. option:: --exclude WILDCARD + This option can be used multiple times to refine results +.. option:: --exclude PATTERN - Omit accounts that match the given WILDCARD string, '*' and '?' are recognized, + Omit accounts whose name match the given PATTERN (see below) - can be used multiple times. Note that --exclude takes precedence over --include + This option can be used multiple times. + Note that --exclude takes precedence over --include + +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. diff --git a/doc/sphinx/plugins/restricted/accountListAccesses.rst b/doc/sphinx/plugins/restricted/accountListAccesses.rst index 1aee5a8..5bbda2a 100644 --- a/doc/sphinx/plugins/restricted/accountListAccesses.rst +++ b/doc/sphinx/plugins/restricted/accountListAccesses.rst @@ -18,17 +18,30 @@ View the expanded access list of a given bastion account The account to work on - .. option:: --hide-groups Don't show the machines the accouns has access to through group rights. - In other words, list only the account's private accesses. - + In other words, list only the account's personal accesses. .. option:: --reverse-dns Attempt to resolve the reverse hostnames (SLOW!) +.. option:: --include PATTERN + + Only include accesses matching the given PATTERN (see below) + + This option can be used multiple times to refine results +.. option:: --exclude PATTERN + + Omit accesses matching the given PATTERN (see below) + + This option can be used multiple times. + Note that --exclude takes precedence over --include + +**Note:** PATTERN supports the ``*`` and ``?`` wildcards. +If PATTERN is a simple string without wildcards, then names containing this string will be considered. +The matching is done on the text output of the command. diff --git a/lib/perl/OVH/Bastion/allowdeny.inc b/lib/perl/OVH/Bastion/allowdeny.inc index 5f13143..8493278 100644 --- a/lib/perl/OVH/Bastion/allowdeny.inc +++ b/lib/perl/OVH/Bastion/allowdeny.inc @@ -490,12 +490,20 @@ sub print_acls { my $acls = $params{'acls'} || []; my $reverse = $params{'reverse'}; my $hideGroups = $params{'hideGroups'}; + my $includes = $params{'includes'} || []; + my $excludes = $params{'excludes'} || []; - my $printIpLen = $reverse ? 30 : 15; - osh_info( - sprintf("%-" . $printIpLen . "s %5s %20s %30s %10s %13s %45s %40s %s", "IP", "PORT", "USER", "ACCESS-BY", "ADDED-BY", "ADDED-AT", "EXPIRY?", "COMMENT", "FORCED-KEY?")); + my $includere = OVH::Bastion::build_re_from_wildcards(wildcards => $includes, implicit_contains => 1)->value; + my $excludere = OVH::Bastion::build_re_from_wildcards(wildcards => $excludes, implicit_contains => 1)->value; - my @flatArray; + # first, get all the rows we'll print, and fill both the array that will be printed (printRows), + # and the one that will be returned as JSON (jsonRows). We also apply the filters here to include/exclude + # the requested patterns, if any + # also take this opportunity to remember the longest field for each column + my @printRows; + my @jsonRows; + my @columnNames = qw( IP PORT USER ACCESS-BY ADDED-BY ADDED-AT EXPIRY? COMMENT FORCED-KEY ); + my @printColumnLength = map { length } @columnNames; foreach my $contextAcl (@$acls) { my $type = $contextAcl->{'type'}; my $group = $contextAcl->{'group'}; @@ -504,32 +512,93 @@ sub print_acls { next if ($hideGroups and $type =~ /^group/); my $accessType = ($group ? "$group($type)" : $type); - foreach my $entry (@$acl) { - my $addedBy = $entry->{'addedBy'} || '(unknown)'; - my $addedDate = $entry->{'addedDate'} || '(unknown)'; + ENTRY: foreach my $entry (@$acl) { + my $addedBy = $entry->{'addedBy'} || '-'; + my $addedDate = $entry->{'addedDate'} || '-'; $addedDate = substr($addedDate, 0, 10); my $forceKey = $entry->{'forceKey'} || '-'; my $expiry = $entry->{'expiry'} ? (duration2human(seconds => ($entry->{'expiry'} - time()))->value->{'human'}) : '-'; - # type => member ('full'), guest ('partial'), personal or legacy + # resolve reverse if asked for it my $ipReverse; $ipReverse = OVH::Bastion::ip2host($entry->{'ip'})->value if $reverse; $entry->{'reverseDns'} = $ipReverse; - push @flatArray, $entry; - osh_info( - sprintf( - "%-" . $printIpLen . "s %5s %20s %30s %10s %13s %45s %40s %s", - $ipReverse ? $ipReverse : $entry->{'ip'}, - $entry->{'port'} ? $entry->{'port'} : '(any)', - $entry->{'user'} ? $entry->{'user'} : '(any)', - $accessType, $addedBy, $addedDate, $expiry, $entry->{'userComment'} || '-', $forceKey - ) + my @row = ( + $ipReverse ? $ipReverse : $entry->{'ip'}, + $entry->{'port'} ? $entry->{'port'} : '(any)', + $entry->{'user'} ? $entry->{'user'} : '(any)', + $accessType, $addedBy, $addedDate, $expiry, $entry->{'userComment'} || '-', $forceKey ); + + # if we have includes or excludes, match fields against the built regex + # for excludes, any field matching is enough to exclude the row + if ($excludere) { + foreach (@row) { + next ENTRY if ($_ =~ $excludere); + } + } + + # for includes, at least one field must match or we exclude the row + if ($includere) { + my $matched = 0; + foreach (@row) { + $matched++ if ($_ =~ $includere); + last if $matched; + } + next ENTRY if !$matched; + } + + # if we're here, row must be included + push @printRows, \@row; + push @jsonRows, $entry; + + # for each cell of this row, remember its len if its longer than any previously seen cell in the same column + for (0 .. @row) { + my $cellLen = length($row[$_]); + $printColumnLength[$_] = $cellLen if $printColumnLength[$_] < $cellLen; + } } } - osh_info(scalar(@flatArray) . " accesses listed"); - return R('OK', value => \@flatArray); + + # then, check if we have at least one non-empty row for each column, + # so that we can omit the empty columns on print (empty cells are '-') + my %atLeastOne; + foreach my $row (@printRows) { + my $i = 0; + foreach my $cell (@$row) { + $atLeastOne{$i}++ if $cell ne '-'; + $i++; + } + } + + # now build the header + my (@header, @format, @underline); + my $i = 0; + foreach (@columnNames) { + if ($atLeastOne{$i}) { + push @header, $_; + push @format, "%" . ($printColumnLength[$i] + 0) . "s"; + push @underline, "-" x ($printColumnLength[$i] + 0); + } + $i++; + } + my $formatstr = join(" ", @format); + osh_info(sprintf($formatstr, @header)); + osh_info(sprintf($formatstr, @underline)); + + # and print each row, potentially omitting empty columns (%atLeastOne) + foreach my $row (@printRows) { + my @fields; + $i = 0; + foreach my $cell (@$row) { + push @fields, $cell if ($atLeastOne{$i}); + $i++; + } + osh_info(sprintf($formatstr, @fields)); + } + osh_info("\n" . scalar(@printRows) . " accesses listed"); + return R('OK', value => \@jsonRows); } # checks if ip matches any given array of prefixes/networks diff --git a/lib/perl/OVH/Bastion/allowkeeper.inc b/lib/perl/OVH/Bastion/allowkeeper.inc index fdcacd9..77d470d 100644 --- a/lib/perl/OVH/Bastion/allowkeeper.inc +++ b/lib/perl/OVH/Bastion/allowkeeper.inc @@ -1035,8 +1035,9 @@ sub is_valid_ttl { # used by groupList and accountList sub build_re_from_wildcards { - my %params = @_; - my $wildcards = $params{'wildcards'}; + my %params = @_; + my $wildcards = $params{'wildcards'}; + my $implicit_contains = $params{'implicit_contains'}; # to avoid modifying the caller's array my @relist = @$wildcards; @@ -1045,6 +1046,11 @@ sub build_re_from_wildcards { return R('OK', value => undef) if !@relist; for (@relist) { + if ($implicit_contains) { + + # if we have a word without any ? or *, guess that the user expects a "contains" behavior, i.e. *item* + $_ = '*' . $_ . '*' if not /[\*\?]/; + } $_ = quotemeta; s/\\\*/.*/g; s/\\\?/./g; diff --git a/tests/functional/tests.d/350-groups.sh b/tests/functional/tests.d/350-groups.sh index 233c50d..f482991 100644 --- a/tests/functional/tests.d/350-groups.sh +++ b/tests/functional/tests.d/350-groups.sh @@ -938,6 +938,18 @@ EOS nocontain REGEX '127\.0\.0\.12[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+'$group1'\(group\)[[:space:]]+'$account1'[[:space:]]' contain '4 accesses listed' + success groupListServers include $a1 --osh groupListServers --group $group1 --include 127.0.0.1 + json .command groupListServers .error_code OK + contain REGEX '127\.0\.0\.1[[:space:]]+22[[:space:]]+g1[[:space:]]+'$group1'\(group\)[[:space:]]+'$account2'[[:space:]]' + contain REGEX '127\.0\.0\.10[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+'$group1'\(group\)[[:space:]]+'$account1'[[:space:]]' + contain REGEX '127\.0\.0\.11[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+'$group1'\(group\)[[:space:]]+'$account1'[[:space:]]' + contain '3 accesses listed' + + success groupListServers include_exclude $a1 --osh groupListServers --group $group1 --include 127.0.0.1 --exclude 127.0.0.10 --exclude 127.?.0.11 + json .command groupListServers .error_code OK + contain REGEX '127\.0\.0\.1[[:space:]]+22[[:space:]]+g1[[:space:]]+'$group1'\(group\)[[:space:]]+'$account2'[[:space:]]' + contain '1 accesses listed' + # group1: a1(owner,aclkeeper,gatekeeper,member) a2() servers(127.0.0.10,127.0.0.11) plgfail groupListServers list $a2 --osh groupListServers --group $group1 json .command groupListServers .error_code KO_ACCESS_DENIED .value null diff --git a/tests/unit/run.pl b/tests/unit/run.pl index 79362d8..9e9861c 100755 --- a/tests/unit/run.pl +++ b/tests/unit/run.pl @@ -231,4 +231,16 @@ is( "is_plugin_disabled()" ); +is( + OVH::Bastion::build_re_from_wildcards(wildcards => ["azerty", "st*ar", "que?stion", "c*ompl?i*cated*"])->value, + qr/^azerty$|^st.*ar$|^que.stion$|^c.*ompl.i.*cated.*$/, + "build_re_from_wildcards() 1" +); + +is( + OVH::Bastion::build_re_from_wildcards(wildcards => ["azerty", "st*ar", "que?stion", "c*ompl?i*cated*"], implicit_contains => 1)->value, + qr/^.*azerty.*$|^st.*ar$|^que.stion$|^c.*ompl.i.*cated.*$/, + "build_re_from_wildcards() 2" +); + done_testing();