rule type and action are required parameters
[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     my $update_rule_properties = PVE::Firewall::add_rule_properties($properties);
189
190     $class->register_method({
191         name => 'update_rule',
192         path => '{pos}',
193         method => 'PUT',
194         description => "Modify rule data.",
195         protected => 1,
196         parameters => {
197             additionalProperties => 0,
198             properties => $update_rule_properties,
199         },
200         returns => { type => "null" },
201         code => sub {
202             my ($param) = @_;
203
204             my ($fw_conf, $rules) = $class->load_config($param);
205
206             my $digest = $fw_conf->{digest};
207             # fixme: check digest
208         
209             die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
210         
211             my $rule = $rules->[$param->{pos}];
212
213             my $moveto = $param->{moveto};
214             if (defined($moveto) && $moveto != $param->{pos}) {
215                 my $newrules = [];
216                 for (my $i = 0; $i < scalar(@$rules); $i++) {
217                     next if $i == $param->{pos};
218                     if ($i == $moveto) {
219                         push @$newrules, $rule;
220                     }
221                     push @$newrules, $rules->[$i];
222                 }
223                 push @$newrules, $rule if $moveto >= scalar(@$rules);
224                 $rules = $newrules;
225             } else {
226                 raise_param_exc({ type => "property is missing"})
227                     if !defined($param->{type});
228                 raise_param_exc({ action => "property is missing"})
229                     if !defined($param->{action});
230
231                 PVE::Firewall::copy_rule_data($rule, $param);
232             }
233
234             $class->save_rules($param, $fw_conf, $rules);
235
236             return undef;
237         }});
238 }
239
240 sub register_delete_rule {
241     my ($class) = @_;
242
243     my $properties = $class->additional_parameters();
244
245     $properties->{pos} = $api_properties->{pos};
246     
247     $class->register_method({
248         name => 'delete_rule',
249         path => '{pos}',
250         method => 'DELETE',
251         description => "Delete rule.",
252         protected => 1,
253         parameters => {
254             additionalProperties => 0,
255             properties => $properties,
256         },
257         returns => { type => "null" },
258         code => sub {
259             my ($param) = @_;
260
261             my ($fw_conf, $rules) = $class->load_config($param);
262
263             my $digest = $fw_conf->{digest};
264             # fixme: check digest
265         
266             die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
267         
268             splice(@$rules, $param->{pos}, 1);
269             
270             $class->save_rules($param, $fw_conf, $rules);
271
272             return undef;
273         }});
274 }
275
276 sub register_handlers {
277     my ($class) = @_;
278
279     $class->register_get_rules();
280     $class->register_get_rule();
281     $class->register_create_rule();
282     $class->register_update_rule();
283     $class->register_delete_rule();
284 }
285
286 package PVE::API2::Firewall::GroupRules;
287
288 use strict;
289 use warnings;
290
291 use base qw(PVE::API2::Firewall::RulesBase);
292
293 __PACKAGE__->additional_parameters({ group => {
294     description => "Security group name.",
295     type => 'string',
296     maxLength => 20, # fixme: what length?
297 }});
298
299 sub load_config {
300     my ($class, $param) = @_;
301
302     my $fw_conf = PVE::Firewall::load_clusterfw_conf();
303     my $rules = $fw_conf->{groups}->{$param->{group}};
304     die "no such security group '$param->{group}'\n" if !defined($rules);
305
306     return ($fw_conf, $rules);
307 }
308
309 sub save_rules {
310     my ($class, $param, $fw_conf, $rules) = @_;
311
312     $fw_conf->{groups}->{$param->{group}} = $rules;
313     PVE::Firewall::save_clusterfw_conf($fw_conf);
314 }
315
316 __PACKAGE__->register_handlers();
317
318 package PVE::API2::Firewall::ClusterRules;
319
320 use strict;
321 use warnings;
322
323 use base qw(PVE::API2::Firewall::RulesBase);
324
325 sub load_config {
326     my ($class, $param) = @_;
327
328     my $fw_conf = PVE::Firewall::load_clusterfw_conf();
329     my $rules = $fw_conf->{rules};
330
331     return ($fw_conf, $rules);
332 }
333
334 sub save_rules {
335     my ($class, $param, $fw_conf, $rules) = @_;
336
337     $fw_conf->{rules} = $rules;
338     PVE::Firewall::save_clusterfw_conf($fw_conf);
339 }
340
341 __PACKAGE__->register_handlers();
342
343 package PVE::API2::Firewall::HostRules;
344
345 use strict;
346 use warnings;
347 use PVE::JSONSchema qw(get_standard_option);
348
349 use base qw(PVE::API2::Firewall::RulesBase);
350
351 __PACKAGE__->additional_parameters({ node => get_standard_option('pve-node')});
352
353 sub load_config {
354     my ($class, $param) = @_;
355
356     my $fw_conf = PVE::Firewall::load_hostfw_conf();
357     my $rules = $fw_conf->{rules};
358
359     return ($fw_conf, $rules);
360 }
361
362 sub save_rules {
363     my ($class, $param, $fw_conf, $rules) = @_;
364
365     $fw_conf->{rules} = $rules;
366     PVE::Firewall::save_hostfw_conf($fw_conf);
367 }
368
369 __PACKAGE__->register_handlers();
370
371 package PVE::API2::Firewall::VMRules;
372
373 use strict;
374 use warnings;
375 use PVE::JSONSchema qw(get_standard_option);
376
377 use base qw(PVE::API2::Firewall::RulesBase);
378
379 __PACKAGE__->additional_parameters({ 
380     node => get_standard_option('pve-node'),
381     vmid => get_standard_option('pve-vmid'),                               
382 });
383
384 sub load_config {
385     my ($class, $param) = @_;
386
387     my $fw_conf = PVE::Firewall::load_vmfw_conf($param->{vmid});
388     my $rules = $fw_conf->{rules};
389
390     return ($fw_conf, $rules);
391 }
392
393 sub save_rules {
394     my ($class, $param, $fw_conf, $rules) = @_;
395
396     $fw_conf->{rules} = $rules;
397     PVE::Firewall::save_vmfw_conf($param->{vmid}, $fw_conf);
398 }
399
400 __PACKAGE__->register_handlers();
401
402 1;