Refactored ACL support with multi-object compare
Squashed commit of the following:
commit 4081e22202693bd7c4ea00e95daad8e628c6fd5a
Author: Oliver Gorwits <oliver@cpan.org>
Date: Mon May 29 21:02:07 2023 +0100
large rename of check_acl* to acl_matches*
commit 3cfa284ddd24d68765c255578cc5c184afbdcd83
Author: Oliver Gorwits <oliver@cpan.org>
Date: Fri May 19 20:39:03 2023 +0100
update permission doc
commit 8c7bb93cc5e9fafb770f98f446e45cbd94b14894
Author: Oliver Gorwits <oliver@cpan.org>
Date: Wed May 17 21:50:07 2023 +0100
migrate most check_acl_only to acl_matches_only
commit c47f699f2a22f08f2f3e093ed0f24c891e6f9a82
Author: Oliver Gorwits <oliver@cpan.org>
Date: Wed May 17 21:39:19 2023 +0100
rename check_acl* to be acl_matches*
commit a884a22c3ab1f3262118c3a47ed8e25b0b0a7336
Author: Oliver Gorwits <oliver@cpan.org>
Date: Sun May 14 16:50:42 2023 +0100
update macsuck_no_deviceports to use acl_matches
commit 8c256af728721329b64d071fa529dfc844073ac6
Author: Oliver Gorwits <oliver@cpan.org>
Date: Sun May 7 22:54:33 2023 +0100
update hide_deviceports to use acl_matches multi @things
commit cd5d9978aba1da459be4fed4500f395df13f7784
Author: Oliver Gorwits <oliver@cpan.org>
Date: Sun May 7 22:53:38 2023 +0100
check_acl fix to allow all @things to offer a property before fallback to missing as empty string
commit 1a3ab9a7646e9f994f03126d45fc36e9e5a13ed5
Author: Oliver Gorwits <oliver@cpan.org>
Date: Tue May 2 15:31:17 2023 +0100
add ignore_deviceports to portproperties discover; improve comments
commit 51385ce89458dc939587dae902fda431719c22c9
Merge: b97c07d2 3f8ffe78
Author: Oliver Gorwits <oliver@cpan.org>
Date: Tue May 2 15:21:48 2023 +0100
Merge branch 'master' into og-acl_multidict
commit b97c07d237d750c1d9eb3095d8ff3908512eac2a
Author: Oliver Gorwits <oliver@cpan.org>
Date: Sat Mar 25 14:37:53 2023 +0000
add support for arrayref of items, and unblessed hash, to check_acl
This commit is contained in:
@@ -2,7 +2,7 @@ package App::Netdisco::Util::Device;
|
||||
|
||||
use Dancer qw/:syntax :script/;
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
use App::Netdisco::Util::Permission qw/check_acl_no check_acl_only/;
|
||||
use App::Netdisco::Util::Permission qw/acl_matches acl_matches_only/;
|
||||
|
||||
use File::Spec::Functions qw(catdir catfile);
|
||||
use File::Path 'make_path';
|
||||
@@ -184,10 +184,10 @@ sub is_discoverable {
|
||||
if (match_to_setting($remote_type, 'discover_no_type'));
|
||||
|
||||
return _bail_msg("is_discoverable: $device matched discover_no")
|
||||
if check_acl_no($device, 'discover_no');
|
||||
if acl_matches($device, 'discover_no');
|
||||
|
||||
return _bail_msg("is_discoverable: $device failed to match discover_only")
|
||||
unless check_acl_only($device, 'discover_only');
|
||||
unless acl_matches_only($device, 'discover_only');
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -236,14 +236,14 @@ sub is_arpnipable {
|
||||
|
||||
return _bail_msg("is_arpnipable: $device has no layer 3 capability")
|
||||
if ($device->in_storage() and not ($device->has_layer(3)
|
||||
or check_acl_no($device, 'force_arpnip')
|
||||
or check_acl_no($device, 'ignore_layers')));
|
||||
or acl_matches($device, 'force_arpnip')
|
||||
or acl_matches($device, 'ignore_layers')));
|
||||
|
||||
return _bail_msg("is_arpnipable: $device matched arpnip_no")
|
||||
if check_acl_no($device, 'arpnip_no');
|
||||
if acl_matches($device, 'arpnip_no');
|
||||
|
||||
return _bail_msg("is_arpnipable: $device failed to match arpnip_only")
|
||||
unless check_acl_only($device, 'arpnip_only');
|
||||
unless acl_matches_only($device, 'arpnip_only');
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -292,17 +292,17 @@ sub is_macsuckable {
|
||||
|
||||
return _bail_msg("is_macsuckable: $device has no layer 2 capability")
|
||||
if ($device->in_storage() and not ($device->has_layer(2)
|
||||
or check_acl_no($device, 'force_macsuck')
|
||||
or check_acl_no($device, 'ignore_layers')));
|
||||
or acl_matches($device, 'force_macsuck')
|
||||
or acl_matches($device, 'ignore_layers')));
|
||||
|
||||
return _bail_msg("is_macsuckable: $device matched macsuck_no")
|
||||
if check_acl_no($device, 'macsuck_no');
|
||||
if acl_matches($device, 'macsuck_no');
|
||||
|
||||
return _bail_msg("is_macsuckable: $device matched macsuck_unsupported")
|
||||
if check_acl_no($device, 'macsuck_unsupported');
|
||||
if acl_matches($device, 'macsuck_unsupported');
|
||||
|
||||
return _bail_msg("is_macsuckable: $device failed to match macsuck_only")
|
||||
unless check_acl_only($device, 'macsuck_only');
|
||||
unless acl_matches_only($device, 'macsuck_only');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use warnings;
|
||||
use Dancer ':script';
|
||||
|
||||
use AnyEvent::DNS;
|
||||
use App::Netdisco::Util::Permission 'check_acl_no';
|
||||
use App::Netdisco::Util::Permission 'acl_matches';
|
||||
|
||||
use base 'Exporter';
|
||||
our @EXPORT = ();
|
||||
@@ -58,7 +58,7 @@ sub hostnames_resolve_async {
|
||||
|
||||
IP: foreach my $hash_ref (@$ips) {
|
||||
my $ip = $hash_ref->{'ip'} || $hash_ref->{'alias'} || $hash_ref->{'device'};
|
||||
next IP if check_acl_no($ip, $skip);
|
||||
next IP if acl_matches($ip, $skip);
|
||||
|
||||
# check /etc/hosts file and short-circuit if found
|
||||
foreach my $name (reverse sort keys %$ETCHOSTS) {
|
||||
|
||||
@@ -4,7 +4,7 @@ use Dancer qw/:syntax :script/;
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
|
||||
use NetAddr::MAC;
|
||||
use App::Netdisco::Util::Permission qw/check_acl_no check_acl_only/;
|
||||
use App::Netdisco::Util::Permission qw/acl_matches acl_matches_only/;
|
||||
|
||||
use base 'Exporter';
|
||||
our @EXPORT = ();
|
||||
@@ -138,9 +138,9 @@ Returns false if the host is not permitted to nbtstat the target node.
|
||||
sub is_nbtstatable {
|
||||
my $ip = shift;
|
||||
|
||||
return if check_acl_no($ip, 'nbtstat_no');
|
||||
return if acl_matches($ip, 'nbtstat_no');
|
||||
|
||||
return unless check_acl_only($ip, 'nbtstat_only');
|
||||
return unless acl_matches_only($ip, 'nbtstat_only');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use App::Netdisco::Util::DNS 'hostname_from_ip';
|
||||
|
||||
use base 'Exporter';
|
||||
our @EXPORT = ();
|
||||
our @EXPORT_OK = qw/check_acl check_acl_no check_acl_only/;
|
||||
our @EXPORT_OK = qw/acl_matches acl_matches_only/;
|
||||
our %EXPORT_TAGS = (all => \@EXPORT_OK);
|
||||
|
||||
=head1 NAME
|
||||
@@ -26,11 +26,17 @@ subroutines.
|
||||
|
||||
=head1 EXPORT_OK
|
||||
|
||||
=head2 check_acl_no( $ip | $instance, $setting_name | $acl_entry | \@acl )
|
||||
=head2 acl_matches( $ip | $object | \%hash | \@item_list, $setting_name | $acl_entry | \@acl )
|
||||
|
||||
Given an IP address or object instance, returns true if the configuration
|
||||
setting C<$setting_name> matches, else returns false. If the content of the
|
||||
setting is undefined or empty, then C<check_acl_no> also returns false.
|
||||
Given an IP address, object instance, or hash, returns true if the
|
||||
configuration setting C<$setting_name> matches, else returns false.
|
||||
|
||||
Usage of this function is strongly advised to be of the form:
|
||||
|
||||
QUIT/SKIP IF acl_matches
|
||||
|
||||
The function fails safe, so if the content of the setting or ACL is undefined
|
||||
or an empty string, then C<acl_matches> also returns true.
|
||||
|
||||
If C<$setting_name> is a valid setting, then it will be resolved to the access
|
||||
control list, else we assume you passed an ACL entry or ACL.
|
||||
@@ -40,19 +46,37 @@ for details of what C<$acl> may contain.
|
||||
|
||||
=cut
|
||||
|
||||
sub check_acl_no {
|
||||
sub acl_matches {
|
||||
my ($thing, $setting_name) = @_;
|
||||
return 1 unless $thing and $setting_name;
|
||||
# fail-safe so undef config should return true
|
||||
return true unless $thing and $setting_name;
|
||||
my $config = (exists config->{"$setting_name"} ? setting($setting_name)
|
||||
: $setting_name);
|
||||
return check_acl($thing, $config);
|
||||
}
|
||||
|
||||
=head2 check_acl_only( $ip | $instance, $setting_name | $acl_entry | \@acl )
|
||||
=head2 check_acl_no( $ip | $object | \%hash | \@item_list, $setting_name | $acl_entry | \@acl )
|
||||
|
||||
Given an IP address or object instance, returns true if the configuration
|
||||
setting C<$setting_name> matches, else returns false. If the content of the
|
||||
setting is undefined or empty, then C<check_acl_only> also returns true.
|
||||
This is an alias for L<acl_matches>.
|
||||
|
||||
=cut
|
||||
|
||||
sub check_acl_no { goto &acl_matches }
|
||||
|
||||
=head2 acl_matches_only( $ip | $object | \%hash | \@item_list, $setting_name | $acl_entry | \@acl )
|
||||
|
||||
Given an IP address, object instance, or hash, returns true if the
|
||||
configuration setting C<$setting_name> matches, else returns false.
|
||||
|
||||
Usage of this function is strongly advised to be of the form:
|
||||
|
||||
QUIT/SKIP UNLESS acl_matches_only
|
||||
|
||||
The function fails safe, so if the content of the setting or ACL is undefined
|
||||
or an empty string, then C<acl_matches_only> also returns false.
|
||||
|
||||
Further, if the setting or ACL resolves to a list but the list has no items,
|
||||
then C<acl_matches_only> returns true (as if there is a successful match).
|
||||
|
||||
If C<$setting_name> is a valid setting, then it will be resolved to the access
|
||||
control list, else we assume you passed an ACL entry or ACL.
|
||||
@@ -62,25 +86,40 @@ for details of what C<$acl> may contain.
|
||||
|
||||
=cut
|
||||
|
||||
sub check_acl_only {
|
||||
sub acl_matches_only {
|
||||
my ($thing, $setting_name) = @_;
|
||||
return 0 unless $thing and $setting_name;
|
||||
# logic to make an empty config be equivalent to 'any' (i.e. a match)
|
||||
# fail-safe so undef config should return false
|
||||
return false unless $thing and $setting_name;
|
||||
my $config = (exists config->{"$setting_name"} ? setting($setting_name)
|
||||
: $setting_name);
|
||||
return 1 if not $config # undef or empty string
|
||||
# logic to make an empty config be equivalent to 'any' (i.e. a match)
|
||||
# empty list check means truth check passes for match or empty list
|
||||
return true if not $config # undef or empty string
|
||||
or ((ref [] eq ref $config) and not scalar @$config);
|
||||
return check_acl($thing, $config);
|
||||
}
|
||||
|
||||
=head2 check_acl( $ip | $instance, $acl_entry | \@acl )
|
||||
=head2 check_acl_only( $ip | $object | \%hash | \@item_list, $setting_name | $acl_entry | \@acl )
|
||||
|
||||
Given an IP address or object instance, compares it to the items in C<< \@acl
|
||||
>> then returns true or false. You can control whether any item must match or
|
||||
all must match, and items can be negated to invert the match logic.
|
||||
This is an alias for L<acl_matches_only>.
|
||||
|
||||
Accepts instances of classes representing Netdisco Devices, Netdisco Device
|
||||
IPs, and L<NetAddr::IP> family objects.
|
||||
=cut
|
||||
|
||||
sub check_acl_only { goto &acl_matches_only }
|
||||
|
||||
=head2 check_acl( $ip | $object | \%hash | \@item_list, $acl_entry | \@acl )
|
||||
|
||||
Given an IP address, object instance, or hash, compares it to the items in
|
||||
C<< \@acl >> then returns true or false. You can control whether any item must
|
||||
match or all must match, and items can be negated to invert the match logic.
|
||||
|
||||
Also accepts an array reference of multiple IP addresses, object instances,
|
||||
and hashes, and will test against each in turn, for each ACL rule.
|
||||
|
||||
The slots C<alias>, C<ip>, C<switch>, and C<addr> are looked for in the
|
||||
instance or hash and used to compare a bare IP address (so it works with most
|
||||
Netdisco database classes, and the L<NetAddr::IP> class). Any instance or hash
|
||||
slot can be used as an ACL named property.
|
||||
|
||||
There are several options for what C<< \@acl >> may contain. See
|
||||
L<the Netdisco wiki|https://github.com/netdisco/netdisco/wiki/Configuration#access-control-lists>
|
||||
@@ -89,156 +128,199 @@ for the details.
|
||||
=cut
|
||||
|
||||
sub check_acl {
|
||||
my ($thing, $config) = @_;
|
||||
return 0 unless defined $thing and defined $config;
|
||||
my ($things, $config) = @_;
|
||||
return false unless defined $things and defined $config;
|
||||
return false if ref [] eq ref $things and not scalar @$things;
|
||||
$things = [$things] if ref [] ne ref $things;
|
||||
|
||||
my $real_ip = $thing;
|
||||
if (blessed $thing) {
|
||||
$real_ip = (
|
||||
$thing->can('alias') ? $thing->alias : (
|
||||
$thing->can('ip') ? $thing->ip : (
|
||||
$thing->can('addr') ? $thing->addr : $thing )));
|
||||
my $real_ip = ''; # valid to be empty
|
||||
ITEM: foreach my $item (@$things) {
|
||||
foreach my $slot (qw/alias ip switch addr/) {
|
||||
if (blessed $item) {
|
||||
$real_ip = $item->$slot if $item->can($slot)
|
||||
and eval { $item->$slot };
|
||||
}
|
||||
elsif (ref {} eq ref $item) {
|
||||
$real_ip = $item->{$slot} if exists $item->{$slot}
|
||||
and $item->{$slot};
|
||||
}
|
||||
last ITEM if $real_ip;
|
||||
}
|
||||
}
|
||||
ITEM: foreach my $item (@$things) {
|
||||
last ITEM if $real_ip;
|
||||
$real_ip = $item if (ref $item eq q{}) and $item;
|
||||
}
|
||||
return 0 if blessed $real_ip; # class we do not understand
|
||||
$real_ip ||= ''; # valid to be empty
|
||||
|
||||
$config = [$config] if ref q{} eq ref $config;
|
||||
$config = [$config] if ref $config eq q{};
|
||||
if (ref [] ne ref $config) {
|
||||
error "error: acl is not a single item or list (cannot compare to '$real_ip')";
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
my $all = (scalar grep {$_ eq 'op:and'} @$config);
|
||||
|
||||
# common case of using plain IP in ACL, so string compare for speed
|
||||
my $find = (scalar grep {not reftype $_ and $_ eq $real_ip} @$config);
|
||||
return 1 if $real_ip and $find and not $all;
|
||||
return true if $real_ip and $find and not $all;
|
||||
|
||||
my $addr = NetAddr::IP::Lite->new($real_ip);
|
||||
my $name = undef; # only look up once, and only if qr// is used
|
||||
my $ropt = { retry => 1, retrans => 1, udp_timeout => 1, tcp_timeout => 2 };
|
||||
my $qref = ref qr//;
|
||||
|
||||
INLIST: foreach (@$config) {
|
||||
my $item = $_; # must copy so that we can modify safely
|
||||
next INLIST if !defined $item or $item eq 'op:and';
|
||||
RULE: foreach (@$config) {
|
||||
my $rule = $_; # must copy so that we can modify safely
|
||||
next RULE if !defined $rule or $rule eq 'op:and';
|
||||
|
||||
if ($qref eq ref $item) {
|
||||
if ($qref eq ref $rule) {
|
||||
# if no IP addr, cannot match its dns
|
||||
next INLIST unless $addr;
|
||||
next RULE unless $addr;
|
||||
|
||||
$name = ($name || hostname_from_ip($addr->addr, $ropt) || '!!none!!');
|
||||
if ($name =~ $item) {
|
||||
return 1 if not $all;
|
||||
if ($name =~ $rule) {
|
||||
return true if not $all;
|
||||
}
|
||||
else {
|
||||
return 0 if $all;
|
||||
return false if $all;
|
||||
}
|
||||
next INLIST;
|
||||
next RULE;
|
||||
}
|
||||
|
||||
my $neg = ($item =~ s/^!//);
|
||||
my $neg = ($rule =~ s/^!//);
|
||||
|
||||
if ($item =~ m/^group:(.+)$/) {
|
||||
if ($rule =~ m/^group:(.+)$/) {
|
||||
my $group = $1;
|
||||
setting('host_groups')->{$group} ||= [];
|
||||
|
||||
if ($neg xor check_acl($thing, setting('host_groups')->{$group})) {
|
||||
return 1 if not $all;
|
||||
if ($neg xor check_acl($things, setting('host_groups')->{$group})) {
|
||||
return true if not $all;
|
||||
}
|
||||
else {
|
||||
return 0 if $all;
|
||||
return false if $all;
|
||||
}
|
||||
next INLIST;
|
||||
next RULE;
|
||||
}
|
||||
|
||||
if ($item =~ m/^([^:]+):([^:]*)$/) {
|
||||
# prop:val
|
||||
if ($rule =~ m/^([^:]+):([^:]*)$/) {
|
||||
my $prop = $1;
|
||||
my $match = $2 || '';
|
||||
my $found = false;
|
||||
|
||||
# if not an object, we can't do much with properties
|
||||
next INLIST unless blessed $thing;
|
||||
# property exists, undef is allowed to match empty string
|
||||
ITEM: foreach my $item (@$things) {
|
||||
if (blessed $item) {
|
||||
if ($neg xor ($item->can($prop) and
|
||||
((!defined eval { $item->$prop } and $match eq q{})
|
||||
or
|
||||
(defined eval { $item->$prop } and ref $item->$prop eq q{} and $item->$prop =~ m/^$match$/)) )) {
|
||||
return true if not $all;
|
||||
$found = true;
|
||||
last ITEM;
|
||||
}
|
||||
}
|
||||
elsif (ref {} eq ref $item) {
|
||||
if ($neg xor (exists $item->{$prop} and
|
||||
((!defined $item->{$prop} and $match eq q{})
|
||||
or
|
||||
(defined $item->{$prop} and ref $item->{$prop} eq q{} and $item->{$prop} =~ m/^$match$/)) )) {
|
||||
return true if not $all;
|
||||
$found = true;
|
||||
last ITEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# prop:val
|
||||
if ($neg xor ($thing->can($prop) and
|
||||
defined eval { $thing->$prop } and
|
||||
ref $thing->$prop eq q{}
|
||||
and $thing->$prop =~ m/^$match$/) ) {
|
||||
return 1 if not $all;
|
||||
# missing property matches empty string
|
||||
# (which is done in a second pass to allow all @$things to be
|
||||
# inspected for existing properties)
|
||||
ITEM: foreach my $item (@$things) {
|
||||
last ITEM if $found;
|
||||
|
||||
if (blessed $item) {
|
||||
if ($neg xor ($match eq q{} and ! $item->can($prop))) {
|
||||
return true if not $all;
|
||||
$found = true;
|
||||
last ITEM;
|
||||
}
|
||||
}
|
||||
elsif (ref {} eq ref $item) {
|
||||
# empty or missing property
|
||||
if ($neg xor ($match eq q{} and ! exists $item->{$prop})) {
|
||||
return true if not $all;
|
||||
$found = true;
|
||||
last ITEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
# empty or missing property
|
||||
elsif ($neg xor ($match eq q{} and
|
||||
(!defined eval { $thing->$prop } or $thing->$prop eq q{})) ) {
|
||||
return 1 if not $all;
|
||||
}
|
||||
else {
|
||||
return 0 if $all;
|
||||
}
|
||||
next INLIST;
|
||||
|
||||
return false if $all;
|
||||
next RULE;
|
||||
}
|
||||
|
||||
if ($item =~ m/[:.]([a-f0-9]+)-([a-f0-9]+)$/i) {
|
||||
if ($rule =~ m/[:.]([a-f0-9]+)-([a-f0-9]+)$/i) {
|
||||
my $first = $1;
|
||||
my $last = $2;
|
||||
|
||||
# if no IP addr, cannot match IP range
|
||||
next INLIST unless $addr;
|
||||
next RULE unless $addr;
|
||||
|
||||
if ($item =~ m/:/) {
|
||||
next INLIST if $addr->bits != 128 and not $all;
|
||||
if ($rule =~ m/:/) {
|
||||
next RULE if $addr->bits != 128 and not $all;
|
||||
|
||||
$first = hex $first;
|
||||
$last = hex $last;
|
||||
|
||||
(my $header = $item) =~ s/:[^:]+$/:/;
|
||||
(my $header = $rule) =~ s/:[^:]+$/:/;
|
||||
foreach my $part ($first .. $last) {
|
||||
my $ip = NetAddr::IP::Lite->new($header . sprintf('%x',$part) . '/128')
|
||||
or next;
|
||||
if ($neg xor ($ip == $addr)) {
|
||||
return 1 if not $all;
|
||||
next INLIST;
|
||||
return true if not $all;
|
||||
next RULE;
|
||||
}
|
||||
}
|
||||
return 0 if (not $neg and $all);
|
||||
return 1 if ($neg and not $all);
|
||||
return false if (not $neg and $all);
|
||||
return true if ($neg and not $all);
|
||||
}
|
||||
else {
|
||||
next INLIST if $addr->bits != 32 and not $all;
|
||||
next RULE if $addr->bits != 32 and not $all;
|
||||
|
||||
(my $header = $item) =~ s/\.[^.]+$/./;
|
||||
(my $header = $rule) =~ s/\.[^.]+$/./;
|
||||
foreach my $part ($first .. $last) {
|
||||
my $ip = NetAddr::IP::Lite->new($header . $part . '/32')
|
||||
or next;
|
||||
if ($neg xor ($ip == $addr)) {
|
||||
return 1 if not $all;
|
||||
next INLIST;
|
||||
return true if not $all;
|
||||
next RULE;
|
||||
}
|
||||
}
|
||||
return 0 if (not $neg and $all);
|
||||
return 1 if ($neg and not $all);
|
||||
return false if (not $neg and $all);
|
||||
return true if ($neg and not $all);
|
||||
}
|
||||
next INLIST;
|
||||
next RULE;
|
||||
}
|
||||
|
||||
# could be something in error, and IP/host is only option left
|
||||
next INLIST if ref $item;
|
||||
next RULE if ref $rule;
|
||||
|
||||
# if no IP addr, cannot match IP prefix
|
||||
next INLIST unless $addr;
|
||||
next RULE unless $addr;
|
||||
|
||||
my $ip = NetAddr::IP::Lite->new($item)
|
||||
or next INLIST;
|
||||
next INLIST if $ip->bits != $addr->bits and not $all;
|
||||
my $ip = NetAddr::IP::Lite->new($rule)
|
||||
or next RULE;
|
||||
next RULE if $ip->bits != $addr->bits and not $all;
|
||||
|
||||
if ($neg xor ($ip->contains($addr))) {
|
||||
return 1 if not $all;
|
||||
return true if not $all;
|
||||
}
|
||||
else {
|
||||
return 0 if $all;
|
||||
return false if $all;
|
||||
}
|
||||
next INLIST;
|
||||
next RULE;
|
||||
}
|
||||
|
||||
return ($all ? 1 : 0);
|
||||
return ($all ? true : false);
|
||||
}
|
||||
|
||||
1;
|
||||
true;
|
||||
|
||||
@@ -4,7 +4,7 @@ use Dancer qw/:syntax :script/;
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
|
||||
use App::Netdisco::Util::Device 'get_device';
|
||||
use App::Netdisco::Util::Permission qw/check_acl_no check_acl_only/;
|
||||
use App::Netdisco::Util::Permission qw/acl_matches acl_matches_only/;
|
||||
|
||||
use base 'Exporter';
|
||||
our @EXPORT = ();
|
||||
@@ -107,9 +107,9 @@ sub port_reconfig_check {
|
||||
|
||||
# check for limits on devices
|
||||
return "forbidden: device [$ip] is in denied ACL"
|
||||
if check_acl_no($ip, 'portctl_no');
|
||||
if acl_matches($ip, 'portctl_no');
|
||||
return "forbidden: device [$ip] is not in permitted ACL"
|
||||
unless check_acl_only($ip, 'portctl_only');
|
||||
unless acl_matches_only($ip, 'portctl_only');
|
||||
|
||||
# only permitted to change interface name
|
||||
return "forbidden: not permitted to change port configuration"
|
||||
|
||||
Reference in New Issue
Block a user