f7ede7319b091b0d2ef9621728245623e32c1c42
[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     group => {
13         description => "Security group name.",
14         type => 'string',
15         maxLength => 20, # fixme: what length?
16     },
17     pos => {
18         description => "Rule position.",
19         type => 'integer',
20         minimum => 0,
21     },
22 };
23
24 sub load_config {
25     my ($class, $param) = @_;
26
27     die "implement this in subclass";
28
29     #return ($fw_conf, $rules);
30 }
31
32 sub save_rules {
33     my ($class, $param, $fw_conf, $rules) = @_;
34
35     die "implement this in subclass";
36 }
37
38 my $need_group_param_hash = {};
39
40 sub need_group_param {
41     my ($class, $new_value) = @_;
42
43     $need_group_param_hash->{$class} = $new_value if defined($new_value);
44
45     return $need_group_param_hash->{$class};
46 }
47
48 sub register_get_rules {
49     my ($class) = @_;
50
51     my $properties = {};
52
53     if ($class->need_group_param()) {
54         $properties->{group} = $api_properties->{group};
55     } 
56
57     $class->register_method({
58         name => 'get_rules',
59         path => '',
60         method => 'GET',
61         description => "List rules.",
62         parameters => {
63             additionalProperties => 0,
64             properties => $properties,
65         },
66         returns => {
67             type => 'array',
68             items => {
69                 type => "object",
70                 properties => {
71                     pos => {
72                         type => 'integer',
73                     }
74                 },
75             },
76             links => [ { rel => 'child', href => "{pos}" } ],
77         },
78         code => sub {
79             my ($param) = @_;
80
81             my ($fw_conf, $rules) = $class->load_config($param);
82
83             my $digest = $fw_conf->{digest};
84
85             my $res = [];
86
87             my $ind = 0;
88             foreach my $rule (@$rules) {
89                 push @$res, PVE::Firewall::cleanup_fw_rule($rule, $digest, $ind++);
90             }
91
92             return $res;
93         }});
94 }
95
96 sub register_get_rule {
97     my ($class) = @_;
98
99     my $properties = {};
100
101     $properties->{pos} = $api_properties->{pos};
102     
103     if ($class->need_group_param()) {
104         $properties->{group} = $api_properties->{group};
105     }
106
107     $class->register_method({
108         name => 'get_rule',
109         path => '{pos}',
110         method => 'GET',
111         description => "Get single rule data.",
112         parameters => {
113             additionalProperties => 0,
114             properties => $properties,
115         },
116         returns => {
117             type => "object",
118             properties => {
119                 pos => {
120                     type => 'integer',
121                 }
122             },
123         },
124         code => sub {
125             my ($param) = @_;
126
127             my ($fw_conf, $rules) = $class->load_config($param);
128
129             my $digest = $fw_conf->{digest};
130             # fixme: check digest
131         
132             die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
133         
134             my $rule = $rules->[$param->{pos}];
135             
136             return PVE::Firewall::cleanup_fw_rule($rule, $digest, $param->{pos});
137         }});
138 }
139
140 sub register_create_rule {
141     my ($class) = @_;
142
143     my $properties = {};
144
145     if ($class->need_group_param()) {
146         $properties->{group} = $api_properties->{group};
147     }
148
149     my $create_rule_properties = PVE::Firewall::add_rule_properties($properties);
150
151     $class->register_method({
152         name => 'create_rule',
153         path => '',
154         method => 'POST',
155         description => "Create new rule.",
156         protected => 1,
157         parameters => {
158             additionalProperties => 0,
159             properties => $create_rule_properties,
160         },
161         returns => { type => "null" },
162         code => sub {
163             my ($param) = @_;
164
165             my ($fw_conf, $rules) = $class->load_config($param);
166
167             my $digest = $fw_conf->{digest};
168             
169             my $rule = { type => 'out', action => 'ACCEPT', enable => 0};
170
171             PVE::Firewall::copy_rule_data($rule, $param);
172
173             unshift @$rules, $rule;
174
175             $class->save_rules($param, $fw_conf, $rules);
176
177             return undef;
178         }});
179 }
180
181 sub register_update_rule {
182     my ($class) = @_;
183
184     my $properties = {};
185
186     $properties->{pos} = $api_properties->{pos};
187     
188     if ($class->need_group_param()) {
189         $properties->{group} = $api_properties->{group};
190     }
191
192     $properties->{moveto} = {
193         description => "Move rule to new position <moveto>. Other arguments are ignored.",
194         type => 'integer',
195         minimum => 0,
196         optional => 1,
197     };
198
199     my $update_rule_properties = PVE::Firewall::add_rule_properties($properties);
200
201     $class->register_method({
202         name => 'update_rule',
203         path => '{pos}',
204         method => 'PUT',
205         description => "Modify rule data.",
206         protected => 1,
207         parameters => {
208             additionalProperties => 0,
209             properties => $update_rule_properties,
210         },
211         returns => { type => "null" },
212         code => sub {
213             my ($param) = @_;
214
215             my ($fw_conf, $rules) = $class->load_config($param);
216
217             my $digest = $fw_conf->{digest};
218             # fixme: check digest
219         
220             die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
221         
222             my $rule = $rules->[$param->{pos}];
223
224             my $moveto = $param->{moveto};
225             if (defined($moveto) && $moveto != $param->{pos}) {
226                 my $newrules = [];
227                 for (my $i = 0; $i < scalar(@$rules); $i++) {
228                     next if $i == $param->{pos};
229                     if ($i == $moveto) {
230                         push @$newrules, $rule;
231                     }
232                     push @$newrules, $rules->[$i];
233                 }
234                 push @$newrules, $rule if $moveto >= scalar(@$rules);
235                 $rules = $newrules;
236             } else {
237                 PVE::Firewall::copy_rule_data($rule, $param);
238             }
239
240             $class->save_rules($param, $fw_conf, $rules);
241
242             return undef;
243         }});
244 }
245
246 sub register_delete_rule {
247     my ($class) = @_;
248
249     my $properties = {};
250
251     $properties->{pos} = $api_properties->{pos};
252     
253     if ($class->need_group_param()) {
254         $properties->{group} = $api_properties->{group};
255     }
256
257     $class->register_method({
258         name => 'delete_rule',
259         path => '{pos}',
260         method => 'DELETE',
261         description => "Delete rule.",
262         protected => 1,
263         parameters => {
264             additionalProperties => 0,
265             properties => $properties,
266         },
267         returns => { type => "null" },
268         code => sub {
269             my ($param) = @_;
270
271             my ($fw_conf, $rules) = $class->load_config($param);
272
273             my $digest = $fw_conf->{digest};
274             # fixme: check digest
275         
276             die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
277         
278             splice(@$rules, $param->{pos}, 1);
279             
280             $class->save_rules($param, $fw_conf, $rules);
281
282             return undef;
283         }});
284 }
285
286 sub register_handlers {
287     my ($class) = @_;
288
289     $class->register_get_rules();
290     $class->register_get_rule();
291     $class->register_create_rule();
292     $class->register_update_rule();
293     $class->register_delete_rule();
294 }
295
296 package PVE::API2::Firewall::GroupRules;
297
298 use strict;
299 use warnings;
300
301 use base qw(PVE::API2::Firewall::RulesBase);
302
303 __PACKAGE__->need_group_param(1);
304
305 sub load_config {
306     my ($class, $param) = @_;
307
308     my $fw_conf = PVE::Firewall::load_clusterfw_conf();
309     my $rules = $fw_conf->{groups}->{$param->{group}};
310     die "no such security group '$param->{group}'\n" if !defined($rules);
311
312     return ($fw_conf, $rules);
313 }
314
315 sub save_rules {
316     my ($class, $param, $fw_conf, $rules) = @_;
317
318     $fw_conf->{groups}->{$param->{group}} = $rules;
319     PVE::Firewall::save_clusterfw_conf($fw_conf);
320 }
321
322 __PACKAGE__->register_handlers('groups');
323
324 package PVE::API2::Firewall::ClusterRules;
325
326 use strict;
327 use warnings;
328
329 use base qw(PVE::API2::Firewall::RulesBase);
330
331 sub load_config {
332     my ($class, $param) = @_;
333
334     my $fw_conf = PVE::Firewall::load_clusterfw_conf();
335     my $rules = $fw_conf->{rules};
336
337     return ($fw_conf, $rules);
338 }
339
340 sub save_rules {
341     my ($class, $param, $fw_conf, $rules) = @_;
342
343     $fw_conf->{rules} = $rules;
344     PVE::Firewall::save_clusterfw_conf($fw_conf);
345 }
346
347 __PACKAGE__->register_handlers('cluster');
348
349 1;