implement delete parameter for rule update API
[pve-firewall.git] / src / PVE / API2 / Firewall / Rules.pm
1 package PVE::API2::Firewall::RulesBase;
2
3 use strict;
4 use warnings;
5 use PVE::JSONSchema qw(get_standard_option);
6
7 use PVE::Firewall;
8
9 use base qw(PVE::RESTHandler);
10
11 my $api_properties = { 
12     pos => {
13         description => "Rule position.",
14         type => 'integer',
15         minimum => 0,
16     },
17 };
18
19 sub load_config {
20     my ($class, $param) = @_;
21
22     die "implement this in subclass";
23
24     #return ($fw_conf, $rules);
25 }
26
27 sub save_rules {
28     my ($class, $param, $fw_conf, $rules) = @_;
29
30     die "implement this in subclass";
31 }
32
33 my $additional_param_hash = {};
34
35 sub additional_parameters {
36     my ($class, $new_value) = @_;
37
38     if (defined($new_value)) {
39         $additional_param_hash->{$class} = $new_value;
40     }
41
42     # return a copy
43     my $copy = {};
44     my $org = $additional_param_hash->{$class} || {};
45     foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
46     return $copy;
47 }
48
49 sub register_get_rules {
50     my ($class) = @_;
51
52     my $properties = $class->additional_parameters();
53
54     $class->register_method({
55         name => 'get_rules',
56         path => '',
57         method => 'GET',
58         description => "List rules.",
59         parameters => {
60             additionalProperties => 0,
61             properties => $properties,
62         },
63         returns => {
64             type => 'array',
65             items => {
66                 type => "object",
67                 properties => {
68                     pos => {
69                         type => 'integer',
70                     }
71                 },
72             },
73             links => [ { rel => 'child', href => "{pos}" } ],
74         },
75         code => sub {
76             my ($param) = @_;
77
78             my ($fw_conf, $rules) = $class->load_config($param);
79
80             my $digest = $fw_conf->{digest};
81
82             my $res = [];
83
84             my $ind = 0;
85             foreach my $rule (@$rules) {
86                 push @$res, PVE::Firewall::cleanup_fw_rule($rule, $digest, $ind++);
87             }
88
89             return $res;
90         }});
91 }
92
93 sub register_get_rule {
94     my ($class) = @_;
95
96     my $properties = $class->additional_parameters();
97
98     $properties->{pos} = $api_properties->{pos};
99     
100     $class->register_method({
101         name => 'get_rule',
102         path => '{pos}',
103         method => 'GET',
104         description => "Get single rule data.",
105         parameters => {
106             additionalProperties => 0,
107             properties => $properties,
108         },
109         returns => {
110             type => "object",
111             properties => {
112                 pos => {
113                     type => 'integer',
114                 }
115             },
116         },
117         code => sub {
118             my ($param) = @_;
119
120             my ($fw_conf, $rules) = $class->load_config($param);
121
122             my $digest = $fw_conf->{digest};
123             # fixme: check digest
124         
125             die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
126         
127             my $rule = $rules->[$param->{pos}];
128             
129             return PVE::Firewall::cleanup_fw_rule($rule, $digest, $param->{pos});
130         }});
131 }
132
133 sub register_create_rule {
134     my ($class) = @_;
135
136     my $properties = $class->additional_parameters();
137
138     my $create_rule_properties = PVE::Firewall::add_rule_properties($properties);
139     $create_rule_properties->{action}->{optional} = 0;
140     $create_rule_properties->{type}->{optional} = 0;
141     
142     $class->register_method({
143         name => 'create_rule',
144         path => '',
145         method => 'POST',
146         description => "Create new rule.",
147         protected => 1,
148         parameters => {
149             additionalProperties => 0,
150             properties => $create_rule_properties,
151         },
152         returns => { type => "null" },
153         code => sub {
154             my ($param) = @_;
155
156             my ($fw_conf, $rules) = $class->load_config($param);
157
158             my $digest = $fw_conf->{digest};
159
160             my $rule = {};
161
162             PVE::Firewall::copy_rule_data($rule, $param);
163
164             $rule->{enable} = 0 if !defined($param->{enable});
165
166             unshift @$rules, $rule;
167
168             $class->save_rules($param, $fw_conf, $rules);
169
170             return undef;
171         }});
172 }
173
174 sub register_update_rule {
175     my ($class) = @_;
176
177     my $properties = $class->additional_parameters();
178
179     $properties->{pos} = $api_properties->{pos};
180     
181     $properties->{moveto} = {
182         description => "Move rule to new position <moveto>. Other arguments are ignored.",
183         type => 'integer',
184         minimum => 0,
185         optional => 1,
186     };
187
188     $properties->{delete} = {
189         type => 'string', format => 'pve-configid-list',
190         description => "A list of settings you want to delete.",
191         optional => 1,
192     };
193
194     my $update_rule_properties = PVE::Firewall::add_rule_properties($properties);
195
196     $class->register_method({
197         name => 'update_rule',
198         path => '{pos}',
199         method => 'PUT',
200         description => "Modify rule data.",
201         protected => 1,
202         parameters => {
203             additionalProperties => 0,
204             properties => $update_rule_properties,
205         },
206         returns => { type => "null" },
207         code => sub {
208             my ($param) = @_;
209
210             my ($fw_conf, $rules) = $class->load_config($param);
211
212             my $digest = $fw_conf->{digest};
213             # fixme: check digest
214         
215             die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
216         
217             my $rule = $rules->[$param->{pos}];
218
219             my $moveto = $param->{moveto};
220             if (defined($moveto) && $moveto != $param->{pos}) {
221                 my $newrules = [];
222                 for (my $i = 0; $i < scalar(@$rules); $i++) {
223                     next if $i == $param->{pos};
224                     if ($i == $moveto) {
225                         push @$newrules, $rule;
226                     }
227                     push @$newrules, $rules->[$i];
228                 }
229                 push @$newrules, $rule if $moveto >= scalar(@$rules);
230                 $rules = $newrules;
231             } else {
232                 raise_param_exc({ type => "property is missing"})
233                     if !defined($param->{type});
234                 raise_param_exc({ action => "property is missing"})
235                     if !defined($param->{action});
236
237                 PVE::Firewall::copy_rule_data($rule, $param);
238                 
239                 PVE::Firewall::delete_rule_properties($rule, $param->{'delete'}) if $param->{'delete'};
240             }
241
242             $class->save_rules($param, $fw_conf, $rules);
243
244             return undef;
245         }});
246 }
247
248 sub register_delete_rule {
249     my ($class) = @_;
250
251     my $properties = $class->additional_parameters();
252
253     $properties->{pos} = $api_properties->{pos};
254     
255     $class->register_method({
256         name => 'delete_rule',
257         path => '{pos}',
258         method => 'DELETE',
259         description => "Delete rule.",
260         protected => 1,
261         parameters => {
262             additionalProperties => 0,
263             properties => $properties,
264         },
265         returns => { type => "null" },
266         code => sub {
267             my ($param) = @_;
268
269             my ($fw_conf, $rules) = $class->load_config($param);
270
271             my $digest = $fw_conf->{digest};
272             # fixme: check digest
273         
274             die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
275         
276             splice(@$rules, $param->{pos}, 1);
277             
278             $class->save_rules($param, $fw_conf, $rules);
279
280             return undef;
281         }});
282 }
283
284 sub register_handlers {
285     my ($class) = @_;
286
287     $class->register_get_rules();
288     $class->register_get_rule();
289     $class->register_create_rule();
290     $class->register_update_rule();
291     $class->register_delete_rule();
292 }
293
294 package PVE::API2::Firewall::GroupRules;
295
296 use strict;
297 use warnings;
298
299 use base qw(PVE::API2::Firewall::RulesBase);
300
301 __PACKAGE__->additional_parameters({ group => {
302     description => "Security group name.",
303     type => 'string',
304     maxLength => 20, # fixme: what length?
305 }});
306
307 sub load_config {
308     my ($class, $param) = @_;
309
310     my $fw_conf = PVE::Firewall::load_clusterfw_conf();
311     my $rules = $fw_conf->{groups}->{$param->{group}};
312     die "no such security group '$param->{group}'\n" if !defined($rules);
313
314     return ($fw_conf, $rules);
315 }
316
317 sub save_rules {
318     my ($class, $param, $fw_conf, $rules) = @_;
319
320     $fw_conf->{groups}->{$param->{group}} = $rules;
321     PVE::Firewall::save_clusterfw_conf($fw_conf);
322 }
323
324 __PACKAGE__->register_handlers();
325
326 package PVE::API2::Firewall::ClusterRules;
327
328 use strict;
329 use warnings;
330
331 use base qw(PVE::API2::Firewall::RulesBase);
332
333 sub load_config {
334     my ($class, $param) = @_;
335
336     my $fw_conf = PVE::Firewall::load_clusterfw_conf();
337     my $rules = $fw_conf->{rules};
338
339     return ($fw_conf, $rules);
340 }
341
342 sub save_rules {
343     my ($class, $param, $fw_conf, $rules) = @_;
344
345     $fw_conf->{rules} = $rules;
346     PVE::Firewall::save_clusterfw_conf($fw_conf);
347 }
348
349 __PACKAGE__->register_handlers();
350
351 package PVE::API2::Firewall::HostRules;
352
353 use strict;
354 use warnings;
355 use PVE::JSONSchema qw(get_standard_option);
356
357 use base qw(PVE::API2::Firewall::RulesBase);
358
359 __PACKAGE__->additional_parameters({ node => get_standard_option('pve-node')});
360
361 sub load_config {
362     my ($class, $param) = @_;
363
364     my $fw_conf = PVE::Firewall::load_hostfw_conf();
365     my $rules = $fw_conf->{rules};
366
367     return ($fw_conf, $rules);
368 }
369
370 sub save_rules {
371     my ($class, $param, $fw_conf, $rules) = @_;
372
373     $fw_conf->{rules} = $rules;
374     PVE::Firewall::save_hostfw_conf($fw_conf);
375 }
376
377 __PACKAGE__->register_handlers();
378
379 package PVE::API2::Firewall::VMRules;
380
381 use strict;
382 use warnings;
383 use PVE::JSONSchema qw(get_standard_option);
384
385 use base qw(PVE::API2::Firewall::RulesBase);
386
387 __PACKAGE__->additional_parameters({ 
388     node => get_standard_option('pve-node'),
389     vmid => get_standard_option('pve-vmid'),                               
390 });
391
392 sub load_config {
393     my ($class, $param) = @_;
394
395     my $fw_conf = PVE::Firewall::load_vmfw_conf($param->{vmid});
396     my $rules = $fw_conf->{rules};
397
398     return ($fw_conf, $rules);
399 }
400
401 sub save_rules {
402     my ($class, $param, $fw_conf, $rules) = @_;
403
404     $fw_conf->{rules} = $rules;
405     PVE::Firewall::save_vmfw_conf($param->{vmid}, $fw_conf);
406 }
407
408 __PACKAGE__->register_handlers();
409
410 1;