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