]> git.proxmox.com Git - pmg-api.git/blob - src/PMG/API2/Config.pm
certs: reload postfix to activate new certificate
[pmg-api.git] / src / PMG / API2 / Config.pm
1 package PMG::API2::Config;
2
3 use strict;
4 use warnings;
5 use Data::Dumper;
6
7 use PVE::SafeSyslog;
8 use PVE::Tools qw(extract_param);
9 use HTTP::Status qw(:constants);
10 use Storable qw(dclone);
11 use PVE::JSONSchema qw(get_standard_option);
12 use PVE::RESTHandler;
13 use Time::HiRes qw();
14
15 use PMG::Config;
16 use PMG::API2::RuleDB;
17 use PMG::API2::LDAP;
18 use PMG::API2::Domains;
19 use PMG::API2::Transport;
20 use PMG::API2::Cluster;
21 use PMG::API2::MyNetworks;
22 use PMG::API2::SMTPWhitelist;
23 use PMG::API2::MimeTypes;
24 use PMG::API2::Fetchmail;
25 use PMG::API2::DestinationTLSPolicy;
26 use PMG::API2::DKIMSign;
27 use PMG::API2::SACustom;
28 use PMG::API2::PBS::Remote;
29 use PMG::API2::ACME;
30
31 use base qw(PVE::RESTHandler);
32
33 my $section_type_enum = PMG::Config::Base->lookup_types();
34
35 __PACKAGE__->register_method ({
36 subclass => "PMG::API2::RuleDB",
37 path => 'ruledb',
38 });
39
40 __PACKAGE__->register_method ({
41 subclass => "PMG::API2::SMTPWhitelist",
42 path => 'whitelist',
43 });
44
45 __PACKAGE__->register_method ({
46 subclass => "PMG::API2::LDAP",
47 path => 'ldap',
48 });
49
50 __PACKAGE__->register_method ({
51 subclass => "PMG::API2::Domains",
52 path => 'domains',
53 });
54
55 __PACKAGE__->register_method ({
56 subclass => "PMG::API2::Fetchmail",
57 path => 'fetchmail',
58 });
59
60 __PACKAGE__->register_method ({
61 subclass => "PMG::API2::Transport",
62 path => 'transport',
63 });
64
65 __PACKAGE__->register_method ({
66 subclass => "PMG::API2::MyNetworks",
67 # set fragment delimiter (no subdirs) - we need that, because CIDRs
68 # contain a slash '/'
69 fragmentDelimiter => '',
70 path => 'mynetworks',
71 });
72
73 __PACKAGE__->register_method ({
74 subclass => "PMG::API2::Cluster",
75 path => 'cluster',
76 });
77
78 __PACKAGE__->register_method ({
79 subclass => "PMG::API2::MimeTypes",
80 path => 'mimetypes',
81 });
82
83 __PACKAGE__->register_method ({
84 subclass => "PMG::API2::DestinationTLSPolicy",
85 path => 'tlspolicy',
86 });
87
88 __PACKAGE__->register_method({
89 subclass => "PMG::API2::DKIMSign",
90 path => 'dkim',
91 });
92
93 __PACKAGE__->register_method({
94 subclass => "PMG::API2::SACustom",
95 path => 'customscores',
96 });
97
98 __PACKAGE__->register_method ({
99 subclass => "PMG::API2::PBS::Remote",
100 path => 'pbs',
101 });
102
103 __PACKAGE__->register_method ({
104 subclass => "PMG::API2::ACME",
105 path => 'acme',
106 });
107
108 __PACKAGE__->register_method ({
109 name => 'index',
110 path => '',
111 method => 'GET',
112 description => "Directory index.",
113 parameters => {
114 additionalProperties => 0,
115 properties => {},
116 },
117 returns => {
118 type => 'array',
119 items => {
120 type => "object",
121 properties => { section => { type => 'string'} },
122 },
123 links => [ { rel => 'child', href => "{section}" } ],
124 },
125 code => sub {
126 my ($param) = @_;
127
128 my $res = [];
129 foreach my $section (@$section_type_enum) {
130 push @$res, { section => $section };
131 }
132
133 push @$res, { section => 'ldap' };
134 push @$res, { section => 'mynetworks' };
135 push @$res, { section => 'mimetypes' };
136 push @$res, { section => 'users' };
137 push @$res, { section => 'domains' };
138 push @$res, { section => 'fetchmail' };
139 push @$res, { section => 'cluster' };
140 push @$res, { section => 'ruledb' };
141 push @$res, { section => 'transport' };
142 push @$res, { section => 'whitelist' };
143 push @$res, { section => 'regextest' };
144 push @$res, { section => 'tlspolicy' };
145 push @$res, { section => 'dkim' };
146 push @$res, { section => 'pbs' };
147 push @$res, { section => 'acme' };
148
149 return $res;
150 }});
151
152 my $api_read_config_section = sub {
153 my ($section) = @_;
154
155 my $cfg = PMG::Config->new();
156
157 my $data = dclone($cfg->{ids}->{$section} // {});
158 $data->{digest} = $cfg->{digest};
159 delete $data->{type};
160
161 return $data;
162 };
163
164 my $api_update_config_section = sub {
165 my ($section, $param) = @_;
166
167 my $code = sub {
168 my $cfg = PMG::Config->new();
169 my $ids = $cfg->{ids};
170
171 my $digest = extract_param($param, 'digest');
172 PVE::SectionConfig::assert_if_modified($cfg, $digest);
173
174 my $delete_str = extract_param($param, 'delete');
175 die "no options specified\n"
176 if !$delete_str && !scalar(keys %$param);
177
178 foreach my $opt (PVE::Tools::split_list($delete_str)) {
179 delete $ids->{$section}->{$opt};
180 }
181
182 my $plugin = PMG::Config::Base->lookup($section);
183 my $config = $plugin->check_config($section, $param, 0, 1);
184
185 foreach my $p (keys %$config) {
186 $ids->{$section}->{$p} = $config->{$p};
187 }
188
189 $cfg->write();
190
191 $cfg->rewrite_config(undef, 1);
192 };
193
194 PMG::Config::lock_config($code, "update config section '$section' failed");
195 };
196
197 foreach my $section (@$section_type_enum) {
198
199 my $plugin = PMG::Config::Base->lookup($section);
200
201 __PACKAGE__->register_method ({
202 name => "read_${section}_section",
203 path => $section,
204 method => 'GET',
205 proxyto => 'master',
206 permissions => { check => [ 'admin', 'audit' ] },
207 description => "Read $section configuration properties.",
208 parameters => {
209 additionalProperties => 0,
210 properties => {},
211 },
212 returns => { type => 'object' },
213 code => sub {
214 my ($param) = @_;
215
216 return $api_read_config_section->($section);
217 }});
218
219 __PACKAGE__->register_method ({
220 name => "update_${section}_section",
221 path => $section,
222 method => 'PUT',
223 proxyto => 'master',
224 protected => 1,
225 permissions => { check => [ 'admin' ] },
226 description => "Update $section configuration properties.",
227 parameters => $plugin->updateSchema(1),
228 returns => { type => 'null' },
229 code => sub {
230 my ($param) = @_;
231
232 $api_update_config_section->($section, $param);
233
234 return undef;
235 }});
236 }
237
238 __PACKAGE__->register_method({
239 name => 'regextest',
240 path => 'regextest',
241 method => 'POST',
242 protected => 0,
243 permissions => { check => [ 'admin', 'qmanager', 'audit' ] },
244 description => "Test Regex ignoring case",
245 parameters => {
246 additionalProperties => 0,
247 properties => {
248 regex => {
249 type => 'string',
250 description => 'The Regex to test',
251 maxLength => 1024,
252 },
253 text => {
254 type => 'string',
255 description => 'The String to test',
256 maxLength => 1024,
257 }
258 },
259 },
260 returns => {
261 type => 'number',
262 },
263 code => sub {
264 my ($param) = @_;
265
266 my $text = $param->{text};
267 my $regex = $param->{regex};
268
269 my $regex_check = sub {
270 my $start_time = [Time::HiRes::gettimeofday];
271 my $match = 0;
272 if ($text =~ /$regex/i) {
273 $match = 1;
274 }
275 my $elapsed = Time::HiRes::tv_interval($start_time) * 1000;
276 die "The Regular Expression '$regex' did not match the text '$text' (elapsed time: $elapsed ms)\n"
277 if !$match;
278 return $elapsed;
279 };
280
281 my $elapsed = PVE::Tools::run_fork_with_timeout(2, $regex_check);
282 if ($elapsed eq '') {
283 die "The Regular Expression timed out\n";
284 }
285
286 return $elapsed;
287 }});
288
289 1;