]> git.proxmox.com Git - pmg-api.git/blob - PMG/API2/LDAP.pm
delete/deliver_quarantined_mail: use receiver instead of pmail
[pmg-api.git] / PMG / API2 / LDAP.pm
1 package PMG::API2::LDAP;
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 PVE::INotify;
14
15 use PMG::LDAPConfig;
16 use PMG::LDAPCache;
17 use PMG::LDAPSet;
18
19 use base qw(PVE::RESTHandler);
20
21 __PACKAGE__->register_method ({
22 name => 'index',
23 path => '',
24 method => 'GET',
25 description => "List configured LDAP profiles.",
26 proxyto => 'master',
27 permissions => { check => [ 'admin' ] },
28 parameters => {
29 additionalProperties => 0,
30 properties => {},
31 },
32 returns => {
33 type => 'array',
34 items => {
35 type => "object",
36 properties => {
37 profile => { type => 'string'},
38 disable => { type => 'boolean' },
39 server1 => { type => 'string'},
40 server2 => { type => 'string', optional => 1},
41 comment => { type => 'string', optional => 1},
42 gcount => { type => 'integer', optional => 1},
43 mcount => { type => 'integer', optional => 1},
44 ucount => { type => 'integer', optional => 1},
45 mode => { type => 'string'},
46 },
47 },
48 links => [ { rel => 'child', href => "{profile}" } ],
49 },
50 code => sub {
51 my ($param) = @_;
52
53 my $ldap_cfg = PMG::LDAPConfig->new();
54
55 my $ldap_set = PMG::LDAPSet->new_from_ldap_cfg($ldap_cfg, 1);
56
57 my $res = [];
58
59 if (defined($ldap_cfg)) {
60 foreach my $profile (keys %{$ldap_cfg->{ids}}) {
61 my $d = $ldap_cfg->{ids}->{$profile};
62 my $entry = {
63 profile => $profile,
64 disable => $d->{disable} ? 1 : 0,
65 server1 => $d->{server1},
66 mode => $d->{mode} // 'ldap',
67 };
68 $entry->{server2} = $d->{server2} if defined($d->{server2});
69 $entry->{comment} = $d->{comment} if defined($d->{comment});
70
71 if (my $d = $ldap_set->{$profile}) {
72 foreach my $k (qw(gcount mcount ucount)) {
73 my $v = $d->{$k};
74 $entry->{$k} = $v if defined($v);
75 }
76 }
77
78 push @$res, $entry;
79 }
80 }
81
82 return $res;
83 }});
84
85 my $forced_ldap_sync = sub {
86 my ($profile, $config) = @_;
87
88 my $ldapcache = PMG::LDAPCache->new(
89 id => $profile, syncmode => 2, %$config);
90
91 die $ldapcache->{errors} if $ldapcache->{errors};
92
93 die "unable to find valid email addresses\n"
94 if !$ldapcache->{mcount};
95 };
96
97 __PACKAGE__->register_method ({
98 name => 'create',
99 path => '',
100 method => 'POST',
101 proxyto => 'master',
102 permissions => { check => [ 'admin' ] },
103 protected => 1,
104 description => "Add LDAP profile.",
105 parameters => PMG::LDAPConfig->createSchema(1),
106 returns => { type => 'null' },
107 code => sub {
108 my ($param) = @_;
109
110 my $code = sub {
111
112 my $cfg = PMG::LDAPConfig->new();
113
114 $cfg->{ids} //= {};
115
116 my $ids = $cfg->{ids};
117
118 my $profile = extract_param($param, 'profile');
119 my $type = $param->{type};
120
121 die "LDAP profile '$profile' already exists\n"
122 if $ids->{$profile};
123
124 my $config = PMG::LDAPConfig->check_config($profile, $param, 1, 1);
125
126 $ids->{$profile} = $config;
127
128 $forced_ldap_sync->($profile, $config)
129 if !$config->{disable};
130
131 $cfg->write();
132 };
133
134 PMG::LDAPConfig::lock_config($code, "add LDAP profile failed");
135
136 return undef;
137 }});
138
139 __PACKAGE__->register_method ({
140 name => 'profile_index',
141 path => '{profile}',
142 method => 'GET',
143 description => "Directory index",
144 permissions => {
145 user => 'all',
146 },
147 parameters => {
148 additionalProperties => 0,
149 properties => {
150 profile => {
151 description => "Profile ID.",
152 type => 'string', format => 'pve-configid',
153 },
154 },
155 },
156 returns => {
157 type => 'array',
158 items => {
159 type => "object",
160 properties => {
161 subdir => { type => 'string'},
162 },
163 },
164 links => [ { rel => 'child', href => "{subdir}" } ],
165 },
166 code => sub {
167 my ($param) = @_;
168
169 return [
170 { subdir => 'config' },
171 { subdir => 'sync' },
172 { subdir => 'users' },
173 { subdir => 'groups' },
174 ];
175 }});
176
177 __PACKAGE__->register_method ({
178 name => 'read_config',
179 path => '{profile}/config',
180 method => 'GET',
181 description => "Get LDAP profile configuration.",
182 proxyto => 'master',
183 permissions => { check => [ 'admin' ] },
184 parameters => {
185 additionalProperties => 0,
186 properties => {
187 profile => {
188 description => "Profile ID.",
189 type => 'string', format => 'pve-configid',
190 },
191 },
192 },
193 returns => {},
194 code => sub {
195 my ($param) = @_;
196
197 my $cfg = PMG::LDAPConfig->new();
198
199 my $profile = $param->{profile};
200
201 my $data = $cfg->{ids}->{$profile};
202 die "LDAP profile '$profile' does not exist\n" if !$data;
203
204 $data->{digest} = $cfg->{digest};
205
206 return $data;
207 }});
208
209 __PACKAGE__->register_method ({
210 name => 'update_config',
211 path => '{profile}/config',
212 method => 'PUT',
213 description => "Update LDAP profile settings.",
214 permissions => { check => [ 'admin' ] },
215 protected => 1,
216 proxyto => 'master',
217 parameters => PMG::LDAPConfig->updateSchema(),
218 returns => { type => 'null' },
219 code => sub {
220 my ($param) = @_;
221
222 my $code = sub {
223
224 my $cfg = PMG::LDAPConfig->new();
225 my $ids = $cfg->{ids};
226
227 my $digest = extract_param($param, 'digest');
228 PVE::SectionConfig::assert_if_modified($cfg, $digest);
229
230 my $profile = extract_param($param, 'profile');
231
232 die "LDAP profile '$profile' does not exist\n"
233 if !$ids->{$profile};
234
235 my $delete_str = extract_param($param, 'delete');
236 die "no options specified\n"
237 if !$delete_str && !scalar(keys %$param);
238
239 foreach my $opt (PVE::Tools::split_list($delete_str)) {
240 delete $ids->{$profile}->{$opt};
241 }
242
243 my $config = PMG::LDAPConfig->check_config($profile, $param, 0, 1);
244
245 foreach my $p (keys %$config) {
246 $ids->{$profile}->{$p} = $config->{$p};
247 }
248
249 $forced_ldap_sync->($profile, $config)
250 if !$config->{disable};
251
252 $cfg->write();
253 };
254
255 PMG::LDAPConfig::lock_config($code, "update LDAP profile failed");
256
257 return undef;
258 }});
259
260 __PACKAGE__->register_method ({
261 name => 'sync_profile',
262 path => '{profile}/sync',
263 method => 'POST',
264 description => "Synchronice LDAP users to local database.",
265 permissions => { check => [ 'admin' ] },
266 protected => 1,
267 proxyto => 'master',
268 parameters => {
269 additionalProperties => 0,
270 properties => {
271 profile => {
272 description => "Profile ID.",
273 type => 'string', format => 'pve-configid',
274 },
275 },
276 },
277 returns => { type => 'null' },
278 code => sub {
279 my ($param) = @_;
280
281 my $cfg = PMG::LDAPConfig->new();
282 my $ids = $cfg->{ids};
283
284 my $profile = extract_param($param, 'profile');
285
286 die "LDAP profile '$profile' does not exist\n"
287 if !$ids->{$profile};
288
289 my $config = $ids->{$profile};
290
291 if ($config->{disable}) {
292 die "LDAP profile '$profile' is disabled\n";
293 } else {
294 $forced_ldap_sync->($profile, $config)
295 }
296
297 return undef;
298 }});
299
300 __PACKAGE__->register_method ({
301 name => 'delete',
302 path => '{profile}',
303 method => 'DELETE',
304 description => "Delete an LDAP profile",
305 permissions => { check => [ 'admin' ] },
306 protected => 1,
307 proxyto => 'master',
308 parameters => {
309 additionalProperties => 0,
310 properties => {
311 profile => {
312 description => "Profile ID.",
313 type => 'string', format => 'pve-configid',
314 },
315 }
316 },
317 returns => { type => 'null' },
318 code => sub {
319 my ($param) = @_;
320
321 my $code = sub {
322
323 my $cfg = PMG::LDAPConfig->new();
324 my $ids = $cfg->{ids};
325
326 my $profile = $param->{profile};
327
328 die "LDAP profile '$profile' does not exist\n"
329 if !$ids->{$profile};
330
331 delete $ids->{$profile};
332
333 PMG::LDAPCache->delete($profile);
334
335 $cfg->write();
336 };
337
338 PMG::LDAPConfig::lock_config($code, "delete LDAP profile failed");
339
340 return undef;
341 }});
342
343 __PACKAGE__->register_method ({
344 name => 'profile_list_users',
345 path => '{profile}/users',
346 method => 'GET',
347 description => "List LDAP users.",
348 permissions => { check => [ 'admin' ] },
349 protected => 1,
350 proxyto => 'master',
351 parameters => {
352 additionalProperties => 0,
353 properties => {
354 profile => {
355 description => "Profile ID.",
356 type => 'string', format => 'pve-configid',
357 },
358 },
359 },
360 returns => {
361 type => 'array',
362 items => {
363 type => "object",
364 properties => {
365 dn => { type => 'string'},
366 account => { type => 'string'},
367 pmail => { type => 'string'},
368 },
369 },
370 links => [ { rel => 'child', href => "{pmail}" } ],
371 },
372 code => sub {
373 my ($param) = @_;
374
375 my $cfg = PMG::LDAPConfig->new();
376 my $ids = $cfg->{ids};
377
378 my $profile = $param->{profile};
379
380 die "LDAP profile '$profile' does not exist\n"
381 if !$ids->{$profile};
382
383 my $config = $ids->{$profile};
384
385 return [] if $config->{disable};
386
387 my $ldapcache = PMG::LDAPCache->new(
388 id => $profile, syncmode => 1, %$config);
389
390 return $ldapcache->list_users();
391 }});
392
393 __PACKAGE__->register_method ({
394 name => 'address_list',
395 path => '{profile}/users/{email}',
396 method => 'GET',
397 description => "Get all email addresses for the specified user.",
398 permissions => { check => [ 'admin' ] },
399 protected => 1,
400 proxyto => 'master',
401 parameters => {
402 additionalProperties => 0,
403 properties => {
404 profile => {
405 description => "Profile ID.",
406 type => 'string', format => 'pve-configid',
407 },
408 email => {
409 description => "Email address.",
410 type => 'string', format => 'email',
411 },
412 },
413 },
414 returns => {
415 type => 'array',
416 items => {
417 type => "object",
418 properties => {
419 primary => { type => 'boolean'},
420 email => { type => 'string'},
421 },
422 },
423 },
424 code => sub {
425 my ($param) = @_;
426
427 my $cfg = PMG::LDAPConfig->new();
428 my $ids = $cfg->{ids};
429
430 my $profile = $param->{profile};
431
432 die "LDAP profile '$profile' does not exist\n"
433 if !$ids->{$profile};
434
435 my $config = $ids->{$profile};
436
437 die "profile '$profile' is disabled\n" if $config->{disable};
438
439 my $ldapcache = PMG::LDAPCache->new(
440 id => $profile, syncmode => 1, %$config);
441
442 my $res = $ldapcache->list_addresses($param->{email});
443
444 die "unable to find ldap user with email address '$param->{email}'\n"
445 if !$res;
446
447 return $res;
448
449 }});
450
451 __PACKAGE__->register_method ({
452 name => 'profile_list_groups',
453 path => '{profile}/groups',
454 method => 'GET',
455 description => "List LDAP groups.",
456 permissions => { check => [ 'admin' ] },
457 protected => 1,
458 proxyto => 'master',
459 parameters => {
460 additionalProperties => 0,
461 properties => {
462 profile => {
463 description => "Profile ID.",
464 type => 'string', format => 'pve-configid',
465 },
466 },
467 },
468 returns => {
469 type => 'array',
470 items => {
471 type => "object",
472 properties => {
473 dn => { type => 'string'},
474 gid => { type => 'number' },
475 },
476 },
477 links => [ { rel => 'child', href => "{gid}" } ],
478 },
479 code => sub {
480 my ($param) = @_;
481
482 my $cfg = PMG::LDAPConfig->new();
483 my $ids = $cfg->{ids};
484
485 my $profile = $param->{profile};
486
487 die "LDAP profile '$profile' does not exist\n"
488 if !$ids->{$profile};
489
490 my $config = $ids->{$profile};
491
492 return [] if $config->{disable};
493
494 my $ldapcache = PMG::LDAPCache->new(
495 id => $profile, syncmode => 1, %$config);
496
497 return $ldapcache->list_groups();
498 }});
499
500 __PACKAGE__->register_method ({
501 name => 'profile_list_group_members',
502 path => '{profile}/groups/{gid}',
503 method => 'GET',
504 description => "List LDAP group members.",
505 permissions => { check => [ 'admin' ] },
506 protected => 1,
507 proxyto => 'master',
508 parameters => {
509 additionalProperties => 0,
510 properties => {
511 profile => {
512 description => "Profile ID.",
513 type => 'string', format => 'pve-configid',
514 },
515 gid => {
516 description => "Group ID",
517 type => 'number',
518 },
519 },
520 },
521 returns => {
522 type => 'array',
523 items => {
524 type => "object",
525 properties => {
526 dn => { type => 'string'},
527 account => { type => 'string' },
528 pmail => { type => 'string' },
529 },
530 },
531 },
532 code => sub {
533 my ($param) = @_;
534
535 my $cfg = PMG::LDAPConfig->new();
536 my $ids = $cfg->{ids};
537
538 my $profile = $param->{profile};
539
540 die "LDAP profile '$profile' does not exist\n"
541 if !$ids->{$profile};
542
543 my $config = $ids->{$profile};
544
545 return [] if $config->{disable};
546
547 my $ldapcache = PMG::LDAPCache->new(
548 id => $profile, syncmode => 1, %$config);
549
550 return $ldapcache->list_users($param->{gid});
551 }});
552
553 1;