]> git.proxmox.com Git - pve-firewall.git/blob - src/PVE/API2/Firewall/Rules.pm
implement generic rule API class
[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;