]> git.proxmox.com Git - pmg-api.git/blame - PMG/API2/LDAP.pm
make ldap info visible for audit users
[pmg-api.git] / PMG / API2 / LDAP.pm
CommitLineData
be6d6ffc
DM
1package PMG::API2::LDAP;
2
3use strict;
4use warnings;
5use Data::Dumper;
6
7use PVE::SafeSyslog;
8use PVE::Tools qw(extract_param);
9use HTTP::Status qw(:constants);
10use Storable qw(dclone);
11use PVE::JSONSchema qw(get_standard_option);
12use PVE::RESTHandler;
2fdba966 13use PVE::INotify;
be6d6ffc
DM
14
15use PMG::LDAPConfig;
b183e761 16use PMG::LDAPCache;
21fb7eb2 17use PMG::LDAPSet;
be6d6ffc
DM
18
19use base qw(PVE::RESTHandler);
20
21__PACKAGE__->register_method ({
22 name => 'index',
23 path => '',
24 method => 'GET',
c2ef4490 25 description => "List configured LDAP profiles.",
be6d6ffc 26 proxyto => 'master',
6aa82c72 27 permissions => { check => [ 'admin', 'audit' ] },
be6d6ffc
DM
28 parameters => {
29 additionalProperties => 0,
30 properties => {},
31 },
32 returns => {
33 type => 'array',
34 items => {
35 type => "object",
36 properties => {
c2ef4490 37 profile => { type => 'string'},
9722e9bf 38 disable => { type => 'boolean' },
be6d6ffc 39 server1 => { type => 'string'},
bfed5777
DM
40 server2 => { type => 'string', optional => 1},
41 comment => { type => 'string', optional => 1},
dc7a750c
DM
42 gcount => { type => 'integer', optional => 1},
43 mcount => { type => 'integer', optional => 1},
44 ucount => { type => 'integer', optional => 1},
be6d6ffc
DM
45 mode => { type => 'string'},
46 },
47 },
c2ef4490 48 links => [ { rel => 'child', href => "{profile}" } ],
be6d6ffc
DM
49 },
50 code => sub {
51 my ($param) = @_;
52
d8fff1a9 53 my $ldap_cfg = PMG::LDAPConfig->new();
2fdba966 54
21fb7eb2
DM
55 my $ldap_set = PMG::LDAPSet->new_from_ldap_cfg($ldap_cfg, 1);
56
be6d6ffc
DM
57 my $res = [];
58
59 if (defined($ldap_cfg)) {
c2ef4490
DM
60 foreach my $profile (keys %{$ldap_cfg->{ids}}) {
61 my $d = $ldap_cfg->{ids}->{$profile};
bfed5777 62 my $entry = {
c2ef4490 63 profile => $profile,
9722e9bf 64 disable => $d->{disable} ? 1 : 0,
be6d6ffc
DM
65 server1 => $d->{server1},
66 mode => $d->{mode} // 'ldap',
67 };
bfed5777
DM
68 $entry->{server2} = $d->{server2} if defined($d->{server2});
69 $entry->{comment} = $d->{comment} if defined($d->{comment});
21fb7eb2 70
c2ef4490 71 if (my $d = $ldap_set->{$profile}) {
94a59215 72 foreach my $k (qw(gcount mcount ucount)) {
21fb7eb2 73 my $v = $d->{$k};
94a59215 74 $entry->{$k} = $v if defined($v);
21fb7eb2
DM
75 }
76 }
77
bfed5777 78 push @$res, $entry;
be6d6ffc
DM
79 }
80 }
2fdba966 81
be6d6ffc
DM
82 return $res;
83 }});
84
c88aa2fe 85my $forced_ldap_sync = sub {
c2ef4490 86 my ($profile, $config) = @_;
c88aa2fe
DM
87
88 my $ldapcache = PMG::LDAPCache->new(
c2ef4490 89 id => $profile, syncmode => 2, %$config);
c88aa2fe
DM
90
91 die $ldapcache->{errors} if $ldapcache->{errors};
92
93 die "unable to find valid email addresses\n"
94 if !$ldapcache->{mcount};
95};
2fdba966
DM
96
97__PACKAGE__->register_method ({
98 name => 'create',
99 path => '',
100 method => 'POST',
101 proxyto => 'master',
bdbc2bc5 102 permissions => { check => [ 'admin' ] },
2fdba966 103 protected => 1,
c2ef4490 104 description => "Add LDAP profile.",
2fdba966
DM
105 parameters => PMG::LDAPConfig->createSchema(1),
106 returns => { type => 'null' },
107 code => sub {
108 my ($param) = @_;
109
110 my $code = sub {
111
d8fff1a9 112 my $cfg = PMG::LDAPConfig->new();
98afc5ae
DM
113
114 $cfg->{ids} //= {};
115
2fdba966
DM
116 my $ids = $cfg->{ids};
117
c2ef4490 118 my $profile = extract_param($param, 'profile');
2fdba966
DM
119 my $type = $param->{type};
120
c2ef4490
DM
121 die "LDAP profile '$profile' already exists\n"
122 if $ids->{$profile};
2fdba966 123
c2ef4490 124 my $config = PMG::LDAPConfig->check_config($profile, $param, 1, 1);
2fdba966 125
c2ef4490 126 $ids->{$profile} = $config;
2fdba966 127
c2ef4490 128 $forced_ldap_sync->($profile, $config)
c88aa2fe 129 if !$config->{disable};
b183e761 130
d8fff1a9 131 $cfg->write();
2fdba966
DM
132 };
133
c2ef4490 134 PMG::LDAPConfig::lock_config($code, "add LDAP profile failed");
2fdba966
DM
135
136 return undef;
137 }});
138
139__PACKAGE__->register_method ({
03f6ea0e 140 name => 'profile_index',
c2ef4490 141 path => '{profile}',
2fdba966 142 method => 'GET',
03f6ea0e 143 description => "Directory index",
bdbc2bc5
DM
144 permissions => {
145 user => 'all',
146 },
03f6ea0e
DM
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' },
c2670481
DM
172 { subdir => 'users' },
173 { subdir => 'groups' },
03f6ea0e
DM
174 ];
175 }});
176
177__PACKAGE__->register_method ({
178 name => 'read_config',
179 path => '{profile}/config',
180 method => 'GET',
c2ef4490 181 description => "Get LDAP profile configuration.",
2fdba966 182 proxyto => 'master',
6aa82c72 183 permissions => { check => [ 'admin', 'audit' ] },
2fdba966
DM
184 parameters => {
185 additionalProperties => 0,
186 properties => {
c2ef4490 187 profile => {
03f6ea0e 188 description => "Profile ID.",
2fdba966
DM
189 type => 'string', format => 'pve-configid',
190 },
191 },
192 },
193 returns => {},
194 code => sub {
195 my ($param) = @_;
196
d8fff1a9 197 my $cfg = PMG::LDAPConfig->new();
2fdba966 198
c2ef4490 199 my $profile = $param->{profile};
2fdba966 200
c2ef4490
DM
201 my $data = $cfg->{ids}->{$profile};
202 die "LDAP profile '$profile' does not exist\n" if !$data;
2fdba966
DM
203
204 $data->{digest} = $cfg->{digest};
205
206 return $data;
207 }});
208
209__PACKAGE__->register_method ({
03f6ea0e
DM
210 name => 'update_config',
211 path => '{profile}/config',
2fdba966 212 method => 'PUT',
c2ef4490 213 description => "Update LDAP profile settings.",
bdbc2bc5 214 permissions => { check => [ 'admin' ] },
2fdba966
DM
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
d8fff1a9 224 my $cfg = PMG::LDAPConfig->new();
2fdba966
DM
225 my $ids = $cfg->{ids};
226
227 my $digest = extract_param($param, 'digest');
228 PVE::SectionConfig::assert_if_modified($cfg, $digest);
229
c2ef4490 230 my $profile = extract_param($param, 'profile');
2fdba966 231
c2ef4490
DM
232 die "LDAP profile '$profile' does not exist\n"
233 if !$ids->{$profile};
2fdba966
DM
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)) {
c2ef4490 240 delete $ids->{$profile}->{$opt};
2fdba966
DM
241 }
242
c2ef4490 243 my $config = PMG::LDAPConfig->check_config($profile, $param, 0, 1);
2fdba966
DM
244
245 foreach my $p (keys %$config) {
c2ef4490 246 $ids->{$profile}->{$p} = $config->{$p};
2fdba966
DM
247 }
248
c2ef4490 249 $forced_ldap_sync->($profile, $config)
c88aa2fe 250 if !$config->{disable};
b183e761 251
d8fff1a9 252 $cfg->write();
2fdba966
DM
253 };
254
c2ef4490 255 PMG::LDAPConfig::lock_config($code, "update LDAP profile failed");
2fdba966
DM
256
257 return undef;
258 }});
259
5a76fa30 260__PACKAGE__->register_method ({
03f6ea0e
DM
261 name => 'sync_profile',
262 path => '{profile}/sync',
5a76fa30
DM
263 method => 'POST',
264 description => "Synchronice LDAP users to local database.",
bdbc2bc5 265 permissions => { check => [ 'admin' ] },
5a76fa30
DM
266 protected => 1,
267 proxyto => 'master',
268 parameters => {
269 additionalProperties => 0,
270 properties => {
c2ef4490
DM
271 profile => {
272 description => "Profile ID.",
5a76fa30
DM
273 type => 'string', format => 'pve-configid',
274 },
275 },
276 },
277 returns => { type => 'null' },
278 code => sub {
279 my ($param) = @_;
280
d8fff1a9 281 my $cfg = PMG::LDAPConfig->new();
5a76fa30
DM
282 my $ids = $cfg->{ids};
283
c2ef4490 284 my $profile = extract_param($param, 'profile');
5a76fa30 285
c2ef4490
DM
286 die "LDAP profile '$profile' does not exist\n"
287 if !$ids->{$profile};
5a76fa30 288
c2ef4490 289 my $config = $ids->{$profile};
5a76fa30
DM
290
291 if ($config->{disable}) {
c2ef4490 292 die "LDAP profile '$profile' is disabled\n";
5a76fa30 293 } else {
c2ef4490 294 $forced_ldap_sync->($profile, $config)
5a76fa30
DM
295 }
296
297 return undef;
298 }});
299
2fdba966
DM
300__PACKAGE__->register_method ({
301 name => 'delete',
c2ef4490 302 path => '{profile}',
2fdba966 303 method => 'DELETE',
c2ef4490 304 description => "Delete an LDAP profile",
bdbc2bc5 305 permissions => { check => [ 'admin' ] },
2fdba966
DM
306 protected => 1,
307 proxyto => 'master',
308 parameters => {
309 additionalProperties => 0,
310 properties => {
c2ef4490
DM
311 profile => {
312 description => "Profile ID.",
2fdba966
DM
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
d8fff1a9 323 my $cfg = PMG::LDAPConfig->new();
2fdba966
DM
324 my $ids = $cfg->{ids};
325
c2ef4490 326 my $profile = $param->{profile};
2fdba966 327
c2ef4490
DM
328 die "LDAP profile '$profile' does not exist\n"
329 if !$ids->{$profile};
2fdba966 330
c2ef4490 331 delete $ids->{$profile};
2fdba966 332
c2ef4490 333 PMG::LDAPCache->delete($profile);
200f41ef 334
d8fff1a9 335 $cfg->write();
2fdba966
DM
336 };
337
c2ef4490 338 PMG::LDAPConfig::lock_config($code, "delete LDAP profile failed");
2fdba966
DM
339
340 return undef;
341 }});
342
c2670481
DM
343__PACKAGE__->register_method ({
344 name => 'profile_list_users',
345 path => '{profile}/users',
346 method => 'GET',
347 description => "List LDAP users.",
6aa82c72 348 permissions => { check => [ 'admin', 'audit' ] },
c2670481
DM
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 },
55e05031 370 links => [ { rel => 'child', href => "{pmail}" } ],
c2670481
DM
371 },
372 code => sub {
373 my ($param) = @_;
374
d8fff1a9 375 my $cfg = PMG::LDAPConfig->new();
c2670481
DM
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.",
6aa82c72 398 permissions => { check => [ 'admin', 'audit' ] },
c2670481
DM
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
d8fff1a9 427 my $cfg = PMG::LDAPConfig->new();
c2670481
DM
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.",
6aa82c72 456 permissions => { check => [ 'admin', 'audit' ] },
c2670481
DM
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'},
1d8a50bb 474 gid => { type => 'number' },
c2670481
DM
475 },
476 },
1d8a50bb 477 links => [ { rel => 'child', href => "{gid}" } ],
c2670481
DM
478 },
479 code => sub {
480 my ($param) = @_;
481
d8fff1a9 482 my $cfg = PMG::LDAPConfig->new();
c2670481
DM
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
1d8a50bb
DC
500__PACKAGE__->register_method ({
501 name => 'profile_list_group_members',
502 path => '{profile}/groups/{gid}',
503 method => 'GET',
504 description => "List LDAP group members.",
6aa82c72 505 permissions => { check => [ 'admin', 'audit' ] },
1d8a50bb
DC
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
be6d6ffc 5531;