package Farly::Remove::Rule;
use 5.008008;
use strict;
use warnings;
use Carp;
use Farly::Rule::Expander;
our $VERSION = '0.12';
# one object per firewall
sub new {
my ( $class, $container ) = @_;
confess "firewall configuration container object required"
unless ( defined($container) );
confess "Object::KVC::List object required"
unless ( $container->isa("Object::KVC::List") );
my $self = {
CONFIG => $container,
RESULT => Object::KVC::List->new(),
};
bless $self, $class;
return $self;
}
# given a config container ::List and a ::List of rules to remove
# modify the configuration to remove the remove rules
# if the config has a group, then remove the rule with the group
# and keep the expanded rules in the configuration
sub config { return $_[0]->{CONFIG} }
sub result { return $_[0]->{RESULT} }
sub _fetch_config_acl {
my ( $self, $id ) = @_;
my $search = Object::KVC::Hash->new();
$search->set( 'ENTRY', Object::KVC::String->new('RULE') );
$search->set( 'ID', $id );
my $search_result = Object::KVC::List->new();
$self->config->matches( $search, $search_result );
return $search_result;
}
sub _has_group {
my ( $self, $rule_object ) = @_;
my $GROUP = Object::KVC::HashRef->new();
$GROUP->set( 'ENTRY', Object::KVC::String->new('GROUP') );
foreach my $property ( $rule_object->get_keys() ) {
if ( $rule_object->get($property)->isa('Object::KVC::HashRef') ) {
if ( $rule_object->get($property)->matches($GROUP) ) {
return 1;
}
}
}
}
sub _is_unique {
my ( $self, $rule_set, $id ) = @_;
foreach my $object ( $rule_set->iter() ) {
if ( !$object->get('ID')->equals($id) ) {
confess "more than one rule $id ", $object->get('ID')->as_string(),
"\n";
}
}
}
sub _is_expanded {
my ( $self, $rule_set ) = @_;
foreach my $object ( $rule_set->iter() ) {
if ( $self->_has_group($object) ) {
confess "ruleset not expanded ", $object->get('ID')->as_string(),
"\n";
}
}
}
sub remove {
my ( $self, $remove_list ) = @_;
# $remove_list isa Object::KVC::List of expanded Rules (not config rules)
confess "remove list of rules container object required"
unless ( defined($remove_list) );
confess "Object::KVC::List object required"
unless ( $remove_list->isa("Object::KVC::List") );
#$remove_list must contain one expanded rule set only
my $id = $remove_list->[0]->get('ID');
$self->_is_unique( $remove_list, $id );
#$remove_list must be expanded rules, it must not contain groups
$self->_is_expanded($remove_list);
# get the configuration rule set
my $config_list = $self->_fetch_config_acl($id);
# create indexes by LINE, 'LINE NO.' => ::Set
my $config_index = Object::KVC::Index->new($config_list);
$config_index->make_index("LINE");
my $removed_index = Object::KVC::Index->new($remove_list);
$removed_index->make_index("LINE");
# get an array of the rule line numbers that have errors
my @remove_rules = sort { $a <=> $b } keys %{ $removed_index->get_index };
# create a rule expander object
my $rule_expander = Farly::Rule::Expander->new( $self->config );
# check each config rule to see if it uses a group or not
foreach my $line_number (@remove_rules) {
# get the config rule
my $config_rule_set = $config_index->fetch($line_number);
if ( $config_rule_set->size != 1 ) {
confess "config rule set size not 1. that was unexpected!";
}
my $config_rule = $config_rule_set->[0];
#get the rule entries that need to be removed from the config
my $remove_set = $removed_index->fetch($line_number);
#if the config rule has an object-group then replace the config
#rule with all expanded rule entries that need to be kept
if ( $self->_has_group($config_rule) ) {
# clone and expand the config rule, putting the rule
# entries into a ::Set
my $clone = $config_rule->clone();
my $exp_config_rule_set = Object::KVC::Set->new();
$rule_expander->expand( $clone, $exp_config_rule_set );
# ::Set difference is all rule entries in the config that
# are not in remove, i.e. that need to be kept
my $diff = $exp_config_rule_set->difference($remove_set);
# the rule entries to be kept are added to the config in raw form
# diff is empty if the entire config rule needs to be removed
foreach my $keep_object ( $diff->iter() ) {
$self->result->add($keep_object);
}
}
#the running config rule had an error, so remove it
#use the expanded rule entries instead
my $r = $config_rule->clone();
$r->set( 'REMOVE', Object::KVC::String->new('RULE') );
$r->delete_key('LINE');
$self->result->add($r);
}
}
1;
__END__
=head1 NAME
Farly::Remove::Rule - Removes a list of firewall rule entries
=head1 DESCRIPTION
Farly::Remove::Rule calculates dependencies and generates the commands needed
to remove a $list<Object::KVC::List<Object::KVC::Hash>> of firewall rule entries
from the given firewall configuration.
If the firewall configuration rule uses a group, then the configuration rule is removed
and the expanded firewall rule entries are used in the configuration.
=head1 METHODS
=head2 new( $list<Object::KVC::List<Object::KVC::Hash>> )
The constructor. A firewall configuration $list must be provided.
$rule_remover = Farly::Remove::Rule->new( $list );
=head2 remove( $list<Object::KVC::List<Object::KVC::Hash>> )
Resolves dependencies and removes the list of firewall rule entries from the
current Farly firewall model.
$remover->remove( $list );
=head2 result()
Returns an Object::KVC::Set<Object::KVC::Hash> object containing all objects
which need to be removed or added to the current Farly firewall model in order
to remove all references to the list of removed firewall rule entries.
$remove_result_set = $remover->result();
=head2 config()
Return the current configuration Object::KVC::List<Object::KVC::Hash> object.
$fw_config = $remover->config();
After calling remove() this will be the up to date configuration, with configuration
rules removed and expanded rule entries added in.
=head1 COPYRIGHT AND LICENCE
Farly::Remove::Rule
Copyright (C) 2012 Trystan Johnson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.