api: lock configs
[pve-firewall.git] / src / PVE / API2 / Firewall / Aliases.pm
1 package PVE::API2::Firewall::AliasesBase;
2
3 use strict;
4 use warnings;
5 use PVE::Exception qw(raise raise_param_exc);
6 use PVE::JSONSchema qw(get_standard_option);
7
8 use PVE::Firewall;
9
10 use base qw(PVE::RESTHandler);
11
12 my $api_properties = {
13     cidr => {
14         description => "Network/IP specification in CIDR format.",
15         type => 'string', format => 'IPorCIDR',
16     },
17     name => get_standard_option('pve-fw-alias'),
18     rename => get_standard_option('pve-fw-alias', {
19         description => "Rename an existing alias.",
20         optional => 1,
21     }),
22     comment => {
23         type => 'string',
24         optional => 1,
25     },
26 };
27
28 sub lock_config {
29     my ($class, $param, $code) = @_;
30
31     die "implement this in subclass";
32 }
33
34 sub load_config {
35     my ($class, $param) = @_;
36
37     die "implement this in subclass";
38
39     #return ($fw_conf, $rules);
40 }
41
42 sub save_aliases {
43     my ($class, $param, $fw_conf, $aliases) = @_;
44
45     die "implement this in subclass";
46 }
47
48 sub rule_env {
49     my ($class, $param) = @_;
50
51     die "implement this in subclass";
52 }
53
54 my $additional_param_hash = {};
55
56 sub additional_parameters {
57     my ($class, $new_value) = @_;
58
59     if (defined($new_value)) {
60         $additional_param_hash->{$class} = $new_value;
61     }
62
63     # return a copy
64     my $copy = {};
65     my $org = $additional_param_hash->{$class} || {};
66     foreach my $p (keys %$org) { $copy->{$p} = $org->{$p}; }
67     return $copy;
68 }
69
70 my $aliases_to_list = sub {
71     my ($aliases) = @_;
72
73     my $list = [];
74     foreach my $k (sort keys %$aliases) {
75         push @$list, $aliases->{$k};
76     }
77     return $list;
78 };
79
80 sub register_get_aliases {
81     my ($class) = @_;
82
83     my $properties = $class->additional_parameters();
84
85     $class->register_method({
86         name => 'get_aliases',
87         path => '',
88         method => 'GET',
89         description => "List aliases",
90         permissions => PVE::Firewall::rules_audit_permissions($class->rule_env()),
91         parameters => {
92             additionalProperties => 0,
93             properties => $properties,
94         },
95         returns => {
96             type => 'array',
97             items => {
98                 type => "object",
99                 properties => {
100                     name => { type => 'string' },
101                     cidr => { type => 'string' },
102                     comment => {
103                         type => 'string',
104                         optional => 1,
105                     },
106                     digest => get_standard_option('pve-config-digest', { optional => 0} ),
107                 },
108             },
109             links => [ { rel => 'child', href => "{name}" } ],
110         },
111         code => sub {
112             my ($param) = @_;
113
114             my ($fw_conf, $aliases) = $class->load_config($param);
115
116             my $list = &$aliases_to_list($aliases);
117
118             return PVE::Firewall::copy_list_with_digest($list);
119         }});
120 }
121
122 sub register_create_alias {
123     my ($class) = @_;
124
125     my $properties = $class->additional_parameters();
126
127     $properties->{name} = $api_properties->{name};
128     $properties->{cidr} = $api_properties->{cidr};
129     $properties->{comment} = $api_properties->{comment};
130
131     $class->register_method({
132         name => 'create_alias',
133         path => '',
134         method => 'POST',
135         description => "Create IP or Network Alias.",
136         permissions => PVE::Firewall::rules_modify_permissions($class->rule_env()),
137         protected => 1,
138         parameters => {
139             additionalProperties => 0,
140             properties => $properties,
141         },
142         returns => { type => "null" },
143         code => sub {
144             my ($param) = @_;
145
146             $class->lock_config($param, sub {
147                 my ($param) = @_;
148
149                 my ($fw_conf, $aliases) = $class->load_config($param);
150
151                 my $name = lc($param->{name});
152
153                 raise_param_exc({ name => "alias '$param->{name}' already exists" })
154                     if defined($aliases->{$name});
155
156                 my $data = { name => $param->{name}, cidr => $param->{cidr} };
157                 $data->{comment} = $param->{comment} if $param->{comment};
158
159                 $aliases->{$name} = $data;
160
161                 $class->save_aliases($param, $fw_conf, $aliases);
162             });
163
164             return undef;
165         }});
166 }
167
168 sub register_read_alias {
169     my ($class) = @_;
170
171     my $properties = $class->additional_parameters();
172
173     $properties->{name} = $api_properties->{name};
174
175     $class->register_method({
176         name => 'read_alias',
177         path => '{name}',
178         method => 'GET',
179         description => "Read alias.",
180         permissions => PVE::Firewall::rules_audit_permissions($class->rule_env()),
181         parameters => {
182             additionalProperties => 0,
183             properties => $properties,
184         },
185         returns => { type => "object" },
186         code => sub {
187             my ($param) = @_;
188
189             my ($fw_conf, $aliases) = $class->load_config($param);
190
191             my $name = lc($param->{name});
192
193             raise_param_exc({ name => "no such alias" })
194                 if !defined($aliases->{$name});
195
196             return $aliases->{$name};
197         }});
198 }
199
200 sub register_update_alias {
201     my ($class) = @_;
202
203     my $properties = $class->additional_parameters();
204
205     $properties->{name} = $api_properties->{name};
206     $properties->{rename} = $api_properties->{rename};
207     $properties->{cidr} = $api_properties->{cidr};
208     $properties->{comment} = $api_properties->{comment};
209     $properties->{digest} = get_standard_option('pve-config-digest');
210
211     $class->register_method({
212         name => 'update_alias',
213         path => '{name}',
214         method => 'PUT',
215         description => "Update IP or Network alias.",
216         permissions => PVE::Firewall::rules_modify_permissions($class->rule_env()),
217         protected => 1,
218         parameters => {
219             additionalProperties => 0,
220             properties => $properties,
221         },
222         returns => { type => "null" },
223         code => sub {
224             my ($param) = @_;
225
226             $class->lock_config($param, sub {
227                 my ($param) = @_;
228
229                 my ($fw_conf, $aliases) = $class->load_config($param);
230
231                 my $list = &$aliases_to_list($aliases);
232
233                 my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
234
235                 PVE::Tools::assert_if_modified($digest, $param->{digest});
236
237                 my $name = lc($param->{name});
238
239                 raise_param_exc({ name => "no such alias" }) if !$aliases->{$name};
240
241                 my $data = { name => $param->{name}, cidr => $param->{cidr} };
242                 $data->{comment} = $param->{comment} if $param->{comment};
243
244                 $aliases->{$name} = $data;
245
246                 my $rename = $param->{rename};
247                 $rename = lc($rename) if $rename;
248
249                 if ($rename && ($name ne $rename)) {
250                     raise_param_exc({ name => "alias '$param->{rename}' already exists" })
251                         if defined($aliases->{$rename});
252                     $aliases->{$name}->{name} = $param->{rename};
253                     $aliases->{$rename} = $aliases->{$name};
254                     delete $aliases->{$name};
255                 }
256
257                 $class->save_aliases($param, $fw_conf, $aliases);
258             });
259
260             return undef;
261         }});
262 }
263
264 sub register_delete_alias {
265     my ($class) = @_;
266
267     my $properties = $class->additional_parameters();
268
269     $properties->{name} = $api_properties->{name};
270     $properties->{digest} = get_standard_option('pve-config-digest');
271
272     $class->register_method({
273         name => 'remove_alias',
274         path => '{name}',
275         method => 'DELETE',
276         description => "Remove IP or Network alias.",
277         permissions => PVE::Firewall::rules_modify_permissions($class->rule_env()),
278         protected => 1,
279         parameters => {
280             additionalProperties => 0,
281             properties => $properties,
282         },
283         returns => { type => "null" },
284         code => sub {
285             my ($param) = @_;
286
287             $class->lock_config($param, sub {
288                 my ($param) = @_;
289
290                 my ($fw_conf, $aliases) = $class->load_config($param);
291
292                 my $list = &$aliases_to_list($aliases);
293                 my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
294                 PVE::Tools::assert_if_modified($digest, $param->{digest});
295
296                 my $name = lc($param->{name});
297                 delete $aliases->{$name};
298
299                 $class->save_aliases($param, $fw_conf, $aliases);
300             });
301
302             return undef;
303         }});
304 }
305
306 sub register_handlers {
307     my ($class) = @_;
308
309     $class->register_get_aliases();
310     $class->register_create_alias();
311     $class->register_read_alias();
312     $class->register_update_alias();
313     $class->register_delete_alias();
314 }
315
316 package PVE::API2::Firewall::ClusterAliases;
317
318 use strict;
319 use warnings;
320
321 use base qw(PVE::API2::Firewall::AliasesBase);
322
323 sub rule_env {
324     my ($class, $param) = @_;
325
326     return 'cluster';
327 }
328
329 sub lock_config {
330     my ($class, $param, $code) = @_;
331
332     PVE::Firewall::lock_clusterfw_conf(10, $code, $param);
333 }
334
335 sub load_config {
336     my ($class, $param) = @_;
337
338     my $fw_conf = PVE::Firewall::load_clusterfw_conf();
339     my $aliases = $fw_conf->{aliases};
340
341     return ($fw_conf, $aliases);
342 }
343
344 sub save_aliases {
345     my ($class, $param, $fw_conf, $aliases) = @_;
346
347     $fw_conf->{aliases} = $aliases;
348     PVE::Firewall::save_clusterfw_conf($fw_conf);
349 }
350
351 __PACKAGE__->register_handlers();
352
353 package PVE::API2::Firewall::VMAliases;
354
355 use strict;
356 use warnings;
357 use PVE::JSONSchema qw(get_standard_option);
358
359 use base qw(PVE::API2::Firewall::AliasesBase);
360
361 sub rule_env {
362     my ($class, $param) = @_;
363
364     return 'vm';
365 }
366
367 __PACKAGE__->additional_parameters({
368     node => get_standard_option('pve-node'),
369     vmid => get_standard_option('pve-vmid'),
370 });
371
372 sub lock_config {
373     my ($class, $param, $code) = @_;
374
375     PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
376 }
377
378 sub load_config {
379     my ($class, $param) = @_;
380
381     my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
382     my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, 'vm', $param->{vmid});
383     my $aliases = $fw_conf->{aliases};
384
385     return ($fw_conf, $aliases);
386 }
387
388 sub save_aliases {
389     my ($class, $param, $fw_conf, $aliases) = @_;
390
391     $fw_conf->{aliases} = $aliases;
392     PVE::Firewall::save_vmfw_conf($param->{vmid}, $fw_conf);
393 }
394
395 __PACKAGE__->register_handlers();
396
397 package PVE::API2::Firewall::CTAliases;
398
399 use strict;
400 use warnings;
401 use PVE::JSONSchema qw(get_standard_option);
402
403 use base qw(PVE::API2::Firewall::AliasesBase);
404
405 sub rule_env {
406     my ($class, $param) = @_;
407
408     return 'ct';
409 }
410
411 __PACKAGE__->additional_parameters({
412     node => get_standard_option('pve-node'),
413     vmid => get_standard_option('pve-vmid'),
414 });
415
416 sub lock_config {
417     my ($class, $param, $code) = @_;
418
419     PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, $code, $param);
420 }
421
422 sub load_config {
423     my ($class, $param) = @_;
424
425     my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
426     my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, 'ct', $param->{vmid});
427     my $aliases = $fw_conf->{aliases};
428
429     return ($fw_conf, $aliases);
430 }
431
432 sub save_aliases {
433     my ($class, $param, $fw_conf, $aliases) = @_;
434
435     $fw_conf->{aliases} = $aliases;
436     PVE::Firewall::save_vmfw_conf($param->{vmid}, $fw_conf);
437 }
438
439 __PACKAGE__->register_handlers();
440
441 1;