From: Dietmar Maurer Date: Wed, 2 Apr 2014 05:56:11 +0000 (+0200) Subject: implement generic rule API class X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=commitdiff_plain;h=8679128955c3dde0e3dbc61027b1f2d88e02e243 implement generic rule API class So that we can reuse the code. --- diff --git a/src/PVE/API2/Firewall/Cluster.pm b/src/PVE/API2/Firewall/Cluster.pm index ce56a6d..d7146f3 100644 --- a/src/PVE/API2/Firewall/Cluster.pm +++ b/src/PVE/API2/Firewall/Cluster.pm @@ -6,6 +6,7 @@ use PVE::Exception qw(raise raise_param_exc raise_perm_exc); use PVE::JSONSchema qw(get_standard_option); use PVE::Firewall; +use PVE::API2::Firewall::Rules; use PVE::API2::Firewall::Groups; #fixme: locking? @@ -19,6 +20,11 @@ __PACKAGE__->register_method ({ path => 'groups', }); +__PACKAGE__->register_method ({ + subclass => "PVE::API2::Firewall::ClusterRules", + path => 'rules', +}); + __PACKAGE__->register_method({ name => 'index', path => '', diff --git a/src/PVE/API2/Firewall/Groups.pm b/src/PVE/API2/Firewall/Groups.pm index 6a07fd0..a929236 100644 --- a/src/PVE/API2/Firewall/Groups.pm +++ b/src/PVE/API2/Firewall/Groups.pm @@ -5,7 +5,7 @@ use warnings; use PVE::JSONSchema qw(get_standard_option); use PVE::Firewall; - +use PVE::API2::Firewall::Rules; use Data::Dumper; # fixme: remove @@ -45,233 +45,9 @@ __PACKAGE__->register_method({ return $res; }}); -__PACKAGE__->register_method({ - name => 'get_rules', +__PACKAGE__->register_method ({ + subclass => "PVE::API2::Firewall::GroupRules", path => '{group}', - method => 'GET', - description => "List security groups rules.", - parameters => { - additionalProperties => 0, - properties => { - group => { - description => "Security group name.", - type => 'string', - }, - }, - }, - returns => { - type => 'array', - items => { - type => "object", - properties => { - pos => { - type => 'integer', - } - }, - }, - links => [ { rel => 'child', href => "{pos}" } ], - }, - code => sub { - my ($param) = @_; - - my $cluster_conf = PVE::Firewall::load_clusterfw_conf(); - - my $rules = $cluster_conf->{groups}->{$param->{group}}; - die "no such security group\n" if !defined($rules); - - my $digest = $cluster_conf->{digest}; - - my $res = []; - - my $ind = 0; - foreach my $rule (@$rules) { - push @$res, PVE::Firewall::cleanup_fw_rule($rule, $digest, $ind++); - } - - return $res; - }}); - -__PACKAGE__->register_method({ - name => 'get_rule', - path => '{group}/{pos}', - method => 'GET', - description => "Get single rule data.", - parameters => { - additionalProperties => 0, - properties => { - group => { - description => "Security group name.", - type => 'string', - }, - pos => { - description => "Return rule from position .", - type => 'integer', - minimum => 0, - }, - }, - }, - returns => { - type => "object", - properties => { - pos => { - type => 'integer', - } - }, - }, - code => sub { - my ($param) = @_; - - my $cluster_conf = PVE::Firewall::load_clusterfw_conf(); - - my $rules = $cluster_conf->{groups}->{$param->{group}}; - die "no such security group\n" if !defined($rules); - - my $digest = $cluster_conf->{digest}; - # fixme: check digest - - die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules); - - my $rule = $rules->[$param->{pos}]; - - return PVE::Firewall::cleanup_fw_rule($rule, $digest, $param->{pos}); - }}); - - -__PACKAGE__->register_method({ - name => 'create_rule', - path => '{group}', - method => 'POST', - description => "Create new rule.", - protected => 1, - parameters => { - additionalProperties => 0, - properties => PVE::Firewall::add_rule_properties({ - group => { - description => "Security group name.", - type => 'string', - }, - }), - }, - returns => { type => "null" }, - code => sub { - my ($param) = @_; - - my $cluster_conf = PVE::Firewall::load_clusterfw_conf(); - - my $rules = $cluster_conf->{groups}->{$param->{group}}; - die "no such security group\n" if !defined($rules); - - my $digest = $cluster_conf->{digest}; - - my $rule = { type => 'out', action => 'ACCEPT', enable => 0}; - - PVE::Firewall::copy_rule_data($rule, $param); - - unshift @$rules, $rule; - - PVE::Firewall::save_clusterfw_conf($cluster_conf); - - return undef; - }}); - -__PACKAGE__->register_method({ - name => 'update_rule', - path => '{group}/{pos}', - method => 'PUT', - description => "Modify rule data.", - protected => 1, - parameters => { - additionalProperties => 0, - properties => PVE::Firewall::add_rule_properties({ - group => { - description => "Security group name.", - type => 'string', - }, - moveto => { - description => "Move rule to new position . Other arguments are ignored.", - type => 'integer', - minimum => 0, - optional => 1, - }, - }), - }, - returns => { type => "null" }, - code => sub { - my ($param) = @_; - - my $cluster_conf = PVE::Firewall::load_clusterfw_conf(); - - my $rules = $cluster_conf->{groups}->{$param->{group}}; - die "no such security group\n" if !defined($rules); - - my $digest = $cluster_conf->{digest}; - # fixme: check digest - - die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules); - - my $rule = $rules->[$param->{pos}]; - - my $moveto = $param->{moveto}; - if (defined($moveto) && $moveto != $param->{pos}) { - my $newrules = []; - for (my $i = 0; $i < scalar(@$rules); $i++) { - next if $i == $param->{pos}; - if ($i == $moveto) { - push @$newrules, $rule; - } - push @$newrules, $rules->[$i]; - } - push @$newrules, $rule if $moveto >= scalar(@$rules); - - $cluster_conf->{groups}->{$param->{group}} = $newrules; - } else { - PVE::Firewall::copy_rule_data($rule, $param); - } - - PVE::Firewall::save_clusterfw_conf($cluster_conf); - - return undef; - }}); - -__PACKAGE__->register_method({ - name => 'delete_rule', - path => '{group}/{pos}', - method => 'DELETE', - description => "Delete rule.", - protected => 1, - parameters => { - additionalProperties => 0, - properties => { - group => { - description => "Security group name.", - type => 'string', - }, - pos => { - description => "Delete rule at position .", - type => 'integer', - minimum => 0, - }, - }, - }, - returns => { type => "null" }, - code => sub { - my ($param) = @_; - - my $cluster_conf = PVE::Firewall::load_clusterfw_conf(); - - my $rules = $cluster_conf->{groups}->{$param->{group}}; - die "no such security group\n" if !defined($rules); - - my $digest = $cluster_conf->{digest}; - # fixme: check digest - - die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules); - - splice(@$rules, $param->{pos}, 1); - - PVE::Firewall::save_clusterfw_conf($cluster_conf); - - return undef; - }}); +}); 1; diff --git a/src/PVE/API2/Firewall/Makefile b/src/PVE/API2/Firewall/Makefile index fbcb6d3..cb93029 100644 --- a/src/PVE/API2/Firewall/Makefile +++ b/src/PVE/API2/Firewall/Makefile @@ -1,4 +1,5 @@ LIB_SOURCES= \ + Rules.pm \ Cluster.pm \ Host.pm \ VM.pm \ diff --git a/src/PVE/API2/Firewall/Rules.pm b/src/PVE/API2/Firewall/Rules.pm new file mode 100644 index 0000000..f7ede73 --- /dev/null +++ b/src/PVE/API2/Firewall/Rules.pm @@ -0,0 +1,349 @@ +package PVE::API2::Firewall::RulesBase; + +use strict; +use warnings; +use PVE::JSONSchema qw(get_standard_option); + +use PVE::Firewall; + +use base qw(PVE::RESTHandler); + +my $api_properties = { + group => { + description => "Security group name.", + type => 'string', + maxLength => 20, # fixme: what length? + }, + pos => { + description => "Rule position.", + type => 'integer', + minimum => 0, + }, +}; + +sub load_config { + my ($class, $param) = @_; + + die "implement this in subclass"; + + #return ($fw_conf, $rules); +} + +sub save_rules { + my ($class, $param, $fw_conf, $rules) = @_; + + die "implement this in subclass"; +} + +my $need_group_param_hash = {}; + +sub need_group_param { + my ($class, $new_value) = @_; + + $need_group_param_hash->{$class} = $new_value if defined($new_value); + + return $need_group_param_hash->{$class}; +} + +sub register_get_rules { + my ($class) = @_; + + my $properties = {}; + + if ($class->need_group_param()) { + $properties->{group} = $api_properties->{group}; + } + + $class->register_method({ + name => 'get_rules', + path => '', + method => 'GET', + description => "List rules.", + parameters => { + additionalProperties => 0, + properties => $properties, + }, + returns => { + type => 'array', + items => { + type => "object", + properties => { + pos => { + type => 'integer', + } + }, + }, + links => [ { rel => 'child', href => "{pos}" } ], + }, + code => sub { + my ($param) = @_; + + my ($fw_conf, $rules) = $class->load_config($param); + + my $digest = $fw_conf->{digest}; + + my $res = []; + + my $ind = 0; + foreach my $rule (@$rules) { + push @$res, PVE::Firewall::cleanup_fw_rule($rule, $digest, $ind++); + } + + return $res; + }}); +} + +sub register_get_rule { + my ($class) = @_; + + my $properties = {}; + + $properties->{pos} = $api_properties->{pos}; + + if ($class->need_group_param()) { + $properties->{group} = $api_properties->{group}; + } + + $class->register_method({ + name => 'get_rule', + path => '{pos}', + method => 'GET', + description => "Get single rule data.", + parameters => { + additionalProperties => 0, + properties => $properties, + }, + returns => { + type => "object", + properties => { + pos => { + type => 'integer', + } + }, + }, + code => sub { + my ($param) = @_; + + my ($fw_conf, $rules) = $class->load_config($param); + + my $digest = $fw_conf->{digest}; + # fixme: check digest + + die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules); + + my $rule = $rules->[$param->{pos}]; + + return PVE::Firewall::cleanup_fw_rule($rule, $digest, $param->{pos}); + }}); +} + +sub register_create_rule { + my ($class) = @_; + + my $properties = {}; + + if ($class->need_group_param()) { + $properties->{group} = $api_properties->{group}; + } + + my $create_rule_properties = PVE::Firewall::add_rule_properties($properties); + + $class->register_method({ + name => 'create_rule', + path => '', + method => 'POST', + description => "Create new rule.", + protected => 1, + parameters => { + additionalProperties => 0, + properties => $create_rule_properties, + }, + returns => { type => "null" }, + code => sub { + my ($param) = @_; + + my ($fw_conf, $rules) = $class->load_config($param); + + my $digest = $fw_conf->{digest}; + + my $rule = { type => 'out', action => 'ACCEPT', enable => 0}; + + PVE::Firewall::copy_rule_data($rule, $param); + + unshift @$rules, $rule; + + $class->save_rules($param, $fw_conf, $rules); + + return undef; + }}); +} + +sub register_update_rule { + my ($class) = @_; + + my $properties = {}; + + $properties->{pos} = $api_properties->{pos}; + + if ($class->need_group_param()) { + $properties->{group} = $api_properties->{group}; + } + + $properties->{moveto} = { + description => "Move rule to new position . Other arguments are ignored.", + type => 'integer', + minimum => 0, + optional => 1, + }; + + my $update_rule_properties = PVE::Firewall::add_rule_properties($properties); + + $class->register_method({ + name => 'update_rule', + path => '{pos}', + method => 'PUT', + description => "Modify rule data.", + protected => 1, + parameters => { + additionalProperties => 0, + properties => $update_rule_properties, + }, + returns => { type => "null" }, + code => sub { + my ($param) = @_; + + my ($fw_conf, $rules) = $class->load_config($param); + + my $digest = $fw_conf->{digest}; + # fixme: check digest + + die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules); + + my $rule = $rules->[$param->{pos}]; + + my $moveto = $param->{moveto}; + if (defined($moveto) && $moveto != $param->{pos}) { + my $newrules = []; + for (my $i = 0; $i < scalar(@$rules); $i++) { + next if $i == $param->{pos}; + if ($i == $moveto) { + push @$newrules, $rule; + } + push @$newrules, $rules->[$i]; + } + push @$newrules, $rule if $moveto >= scalar(@$rules); + $rules = $newrules; + } else { + PVE::Firewall::copy_rule_data($rule, $param); + } + + $class->save_rules($param, $fw_conf, $rules); + + return undef; + }}); +} + +sub register_delete_rule { + my ($class) = @_; + + my $properties = {}; + + $properties->{pos} = $api_properties->{pos}; + + if ($class->need_group_param()) { + $properties->{group} = $api_properties->{group}; + } + + $class->register_method({ + name => 'delete_rule', + path => '{pos}', + method => 'DELETE', + description => "Delete rule.", + protected => 1, + parameters => { + additionalProperties => 0, + properties => $properties, + }, + returns => { type => "null" }, + code => sub { + my ($param) = @_; + + my ($fw_conf, $rules) = $class->load_config($param); + + my $digest = $fw_conf->{digest}; + # fixme: check digest + + die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules); + + splice(@$rules, $param->{pos}, 1); + + $class->save_rules($param, $fw_conf, $rules); + + return undef; + }}); +} + +sub register_handlers { + my ($class) = @_; + + $class->register_get_rules(); + $class->register_get_rule(); + $class->register_create_rule(); + $class->register_update_rule(); + $class->register_delete_rule(); +} + +package PVE::API2::Firewall::GroupRules; + +use strict; +use warnings; + +use base qw(PVE::API2::Firewall::RulesBase); + +__PACKAGE__->need_group_param(1); + +sub load_config { + my ($class, $param) = @_; + + my $fw_conf = PVE::Firewall::load_clusterfw_conf(); + my $rules = $fw_conf->{groups}->{$param->{group}}; + die "no such security group '$param->{group}'\n" if !defined($rules); + + return ($fw_conf, $rules); +} + +sub save_rules { + my ($class, $param, $fw_conf, $rules) = @_; + + $fw_conf->{groups}->{$param->{group}} = $rules; + PVE::Firewall::save_clusterfw_conf($fw_conf); +} + +__PACKAGE__->register_handlers('groups'); + +package PVE::API2::Firewall::ClusterRules; + +use strict; +use warnings; + +use base qw(PVE::API2::Firewall::RulesBase); + +sub load_config { + my ($class, $param) = @_; + + my $fw_conf = PVE::Firewall::load_clusterfw_conf(); + my $rules = $fw_conf->{rules}; + + return ($fw_conf, $rules); +} + +sub save_rules { + my ($class, $param, $fw_conf, $rules) = @_; + + $fw_conf->{rules} = $rules; + PVE::Firewall::save_clusterfw_conf($fw_conf); +} + +__PACKAGE__->register_handlers('cluster'); + +1;