use
Dancer
qw/:syntax :script/
;
our
@EXPORT
= ();
our
@EXPORT_OK
=
qw/check_acl check_acl_no check_acl_only acl_matches acl_matches_only/
;
our
%EXPORT_TAGS
= (
all
=> \
@EXPORT_OK
);
sub
acl_matches {
my
(
$thing
,
$setting_name
) =
@_
;
return
true
unless
$thing
and
$setting_name
;
my
$config
= (
exists
config->{
"$setting_name"
} ? setting(
$setting_name
)
:
$setting_name
);
return
check_acl(
$thing
,
$config
);
}
sub
check_acl_no {
goto
&acl_matches
}
sub
acl_matches_only {
my
(
$thing
,
$setting_name
) =
@_
;
return
false
unless
$thing
and
$setting_name
;
my
$config
= (
exists
config->{
"$setting_name"
} ? setting(
$setting_name
)
:
$setting_name
);
return
true
if
not
$config
or ((
ref
[] eq
ref
$config
) and not
scalar
@$config
);
return
check_acl(
$thing
,
$config
);
}
sub
check_acl_only {
goto
&acl_matches_only
}
sub
check_acl {
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
=
''
;
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
;
}
$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
false;
}
my
$all
= (
scalar
grep
{
$_
eq
'op:and'
}
@$config
);
my
$find
= (
scalar
grep
{not reftype
$_
and
$_
eq
$real_ip
}
@$config
);
return
true
if
$real_ip
and
$find
and not
$all
;
my
$addr
= NetAddr::IP::Lite->new(
$real_ip
);
my
$name
=
undef
;
my
$ropt
= {
retry
=> 1,
retrans
=> 1,
udp_timeout
=> 1,
tcp_timeout
=> 2 };
my
$qref
=
ref
qr//
;
RULE:
foreach
(
@$config
) {
my
$rule
=
$_
;
next
RULE
if
!
defined
$rule
or
$rule
eq
'op:and'
;
if
(
$qref
eq
ref
$rule
) {
next
RULE
unless
$addr
;
$name
= (
$name
|| hostname_from_ip(
$addr
->addr,
$ropt
) ||
'!!none!!'
);
if
(
$name
=~
$rule
) {
return
true
if
not
$all
;
}
else
{
return
false
if
$all
;
}
next
RULE;
}
my
$neg
= (
$rule
=~ s/^!//);
if
(
$rule
=~ m/^group:(.+)$/) {
my
$group
= $1;
setting(
'host_groups'
)->{
$group
} ||= [];
if
(
$neg
xor check_acl(
$things
, setting(
'host_groups'
)->{
$group
})) {
return
true
if
not
$all
;
}
else
{
return
false
if
$all
;
}
next
RULE;
}
if
(
$rule
=~ m/^tag:(.+)$/) {
my
$tag
= $1;
my
$found
= false;
ITEM:
foreach
my
$item
(
@$things
) {
if
(blessed
$item
and
$item
->can(
'tags'
)) {
if
(
$neg
xor
scalar
grep
{
$_
eq
$tag
} @{
$item
->tags || [] }) {
return
true
if
not
$all
;
$found
= true;
last
ITEM;
}
}
elsif
(
ref
{} eq
ref
$item
and
exists
$item
->{
'tags'
}) {
if
(
$neg
xor
scalar
grep
{
$_
eq
$tag
} @{
$item
->{
'tags'
} || [] }) {
return
true
if
not
$all
;
$found
= true;
last
ITEM;
}
}
}
return
false
if
$all
and not
$found
;
next
RULE;
}
if
(
$rule
=~ m/^cf:([^:]+):(.*)$/) {
my
$prop
= $1;
my
$match
= $2 ||
''
;
my
$found
= false;
ITEM:
foreach
my
$item
(
@$things
) {
my
$cf
= {};
if
(blessed
$item
and
$item
->can(
'custom_fields'
)) {
$cf
= from_json (
$item
->custom_fields ||
'{}'
);
}
elsif
(
ref
{} eq
ref
$item
and
exists
$item
->{
'custom_fields'
}) {
$cf
= from_json (
$item
->{
'custom_fields'
} ||
'{}'
);
}
if
(
$neg
xor (
ref
{} eq
ref
$cf
and
exists
$cf
->{
$prop
} and
((!
defined
$cf
->{
$prop
} and
$match
eq
q{}
)
or
(
defined
$cf
->{
$prop
} and
ref
$cf
->{
$prop
} eq
q{}
and
$cf
->{
$prop
} =~ m/^
$match
$/)) )) {
return
true
if
not
$all
;
$found
= true;
last
ITEM;
}
}
ITEM:
foreach
my
$item
(
@$things
) {
last
ITEM
if
$found
;
my
$cf
= {};
if
(blessed
$item
and
$item
->can(
'custom_fields'
)) {
$cf
= from_json (
$item
->custom_fields ||
'{}'
);
}
elsif
(
ref
{} eq
ref
$item
and
exists
$item
->{
'custom_fields'
}) {
$cf
= from_json (
$item
->{
'custom_fields'
} ||
'{}'
);
}
if
(
$neg
xor (
$match
eq
q{}
and !
exists
$cf
->{
$prop
})) {
return
true
if
not
$all
;
$found
= true;
last
ITEM;
}
}
return
false
if
$all
and not
$found
;
next
RULE;
}
if
(
$rule
=~ m/^([^:]+):(.*)$/ and $1 !~ m/^[a-f0-9]+$/i) {
my
$prop
= $1;
my
$match
= $2 ||
''
;
my
$found
= false;
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;
}
}
}
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
) {
if
(
$neg
xor (
$match
eq
q{}
and !
exists
$item
->{
$prop
})) {
return
true
if
not
$all
;
$found
= true;
last
ITEM;
}
}
}
return
false
if
$all
and not
$found
;
next
RULE;
}
if
(
$rule
=~ m/[:.]([a-f0-9]+)-([a-f0-9]+)$/i) {
my
$first
= $1;
my
$last
= $2;
next
RULE
unless
$addr
;
if
(
$rule
=~ m/:/) {
next
RULE
if
$addr
->bits != 128 and not
$all
;
$first
=
hex
$first
;
$last
=
hex
$last
;
(
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
true
if
not
$all
;
next
RULE;
}
}
return
false
if
(not
$neg
and
$all
);
return
true
if
(
$neg
and not
$all
);
}
else
{
next
RULE
if
$addr
->bits != 32 and not
$all
;
(
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
true
if
not
$all
;
next
RULE;
}
}
return
false
if
(not
$neg
and
$all
);
return
true
if
(
$neg
and not
$all
);
}
next
RULE;
}
next
RULE
if
ref
$rule
;
next
RULE
unless
$addr
;
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
true
if
not
$all
;
}
else
{
return
false
if
$all
;
}
next
RULE;
}
return
(
$all
? true : false);
}
true;