]> git.proxmox.com Git - pmg-api.git/blame_incremental - src/PMG/API2/LDAP.pm
bump version to 8.1.4
[pmg-api.git] / src / PMG / API2 / LDAP.pm
... / ...
CommitLineData
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;
13use PVE::INotify;
14
15use PMG::LDAPConfig;
16use PMG::LDAPCache;
17use PMG::LDAPSet;
18
19use 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', 'audit' ] },
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
85my $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', 'audit' ] },
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 # we do not want to get the password over the api
205 delete $data->{bindpw};
206
207 $data->{digest} = $cfg->{digest};
208
209 return $data;
210 }});
211
212__PACKAGE__->register_method ({
213 name => 'update_config',
214 path => '{profile}/config',
215 method => 'PUT',
216 description => "Update LDAP profile settings.",
217 permissions => { check => [ 'admin' ] },
218 protected => 1,
219 proxyto => 'master',
220 parameters => PMG::LDAPConfig->updateSchema(),
221 returns => { type => 'null' },
222 code => sub {
223 my ($param) = @_;
224
225 my $code = sub {
226
227 my $cfg = PMG::LDAPConfig->new();
228 my $ids = $cfg->{ids};
229
230 my $digest = extract_param($param, 'digest');
231 PVE::SectionConfig::assert_if_modified($cfg, $digest);
232
233 my $profile = extract_param($param, 'profile');
234
235 die "LDAP profile '$profile' does not exist\n"
236 if !$ids->{$profile};
237
238 my $delete_str = extract_param($param, 'delete');
239 die "no options specified\n"
240 if !$delete_str && !scalar(keys %$param);
241
242 foreach my $opt (PVE::Tools::split_list($delete_str)) {
243 delete $ids->{$profile}->{$opt};
244 }
245
246 my $config = PMG::LDAPConfig->check_config($profile, $param, 0, 1);
247
248 foreach my $p (keys %$config) {
249 $ids->{$profile}->{$p} = $config->{$p};
250 }
251
252 $forced_ldap_sync->($profile, $ids->{$profile})
253 if !$config->{disable};
254
255 $cfg->write();
256 };
257
258 PMG::LDAPConfig::lock_config($code, "update LDAP profile failed");
259
260 return undef;
261 }});
262
263__PACKAGE__->register_method ({
264 name => 'sync_profile',
265 path => '{profile}/sync',
266 method => 'POST',
267 description => "Synchronice LDAP users to local database.",
268 permissions => { check => [ 'admin' ] },
269 protected => 1,
270 proxyto => 'master',
271 parameters => {
272 additionalProperties => 0,
273 properties => {
274 profile => {
275 description => "Profile ID.",
276 type => 'string', format => 'pve-configid',
277 },
278 },
279 },
280 returns => { type => 'null' },
281 code => sub {
282 my ($param) = @_;
283
284 my $cfg = PMG::LDAPConfig->new();
285 my $ids = $cfg->{ids};
286
287 my $profile = extract_param($param, 'profile');
288
289 die "LDAP profile '$profile' does not exist\n"
290 if !$ids->{$profile};
291
292 my $config = $ids->{$profile};
293
294 if ($config->{disable}) {
295 die "LDAP profile '$profile' is disabled\n";
296 } else {
297 $forced_ldap_sync->($profile, $config)
298 }
299
300 return undef;
301 }});
302
303__PACKAGE__->register_method ({
304 name => 'delete',
305 path => '{profile}',
306 method => 'DELETE',
307 description => "Delete an LDAP profile",
308 permissions => { check => [ 'admin' ] },
309 protected => 1,
310 proxyto => 'master',
311 parameters => {
312 additionalProperties => 0,
313 properties => {
314 profile => {
315 description => "Profile ID.",
316 type => 'string', format => 'pve-configid',
317 },
318 }
319 },
320 returns => { type => 'null' },
321 code => sub {
322 my ($param) = @_;
323
324 my $code = sub {
325
326 my $cfg = PMG::LDAPConfig->new();
327 my $ids = $cfg->{ids};
328
329 my $profile = $param->{profile};
330
331 die "LDAP profile '$profile' does not exist\n"
332 if !$ids->{$profile};
333
334 delete $ids->{$profile};
335
336 PMG::LDAPCache->delete($profile);
337
338 $cfg->write();
339 };
340
341 PMG::LDAPConfig::lock_config($code, "delete LDAP profile failed");
342
343 return undef;
344 }});
345
346__PACKAGE__->register_method ({
347 name => 'profile_list_users',
348 path => '{profile}/users',
349 method => 'GET',
350 description => "List LDAP users.",
351 permissions => { check => [ 'admin', 'audit' ] },
352 protected => 1,
353 proxyto => 'master',
354 parameters => {
355 additionalProperties => 0,
356 properties => {
357 profile => {
358 description => "Profile ID.",
359 type => 'string', format => 'pve-configid',
360 },
361 },
362 },
363 returns => {
364 type => 'array',
365 items => {
366 type => "object",
367 properties => {
368 dn => { type => 'string'},
369 account => { type => 'string'},
370 pmail => { type => 'string'},
371 },
372 },
373 links => [ { rel => 'child', href => "{pmail}" } ],
374 },
375 code => sub {
376 my ($param) = @_;
377
378 my $cfg = PMG::LDAPConfig->new();
379 my $ids = $cfg->{ids};
380
381 my $profile = $param->{profile};
382
383 die "LDAP profile '$profile' does not exist\n"
384 if !$ids->{$profile};
385
386 my $config = $ids->{$profile};
387
388 return [] if $config->{disable};
389
390 my $ldapcache = PMG::LDAPCache->new(
391 id => $profile, syncmode => 1, %$config);
392
393 return $ldapcache->list_users();
394 }});
395
396__PACKAGE__->register_method ({
397 name => 'address_list',
398 path => '{profile}/users/{email}',
399 method => 'GET',
400 description => "Get all email addresses for the specified user.",
401 permissions => { check => [ 'admin', 'audit' ] },
402 protected => 1,
403 proxyto => 'master',
404 parameters => {
405 additionalProperties => 0,
406 properties => {
407 profile => {
408 description => "Profile ID.",
409 type => 'string', format => 'pve-configid',
410 },
411 email => get_standard_option('pmg-email-address', {
412 description => "Email address.",
413 }),
414 },
415 },
416 returns => {
417 type => 'array',
418 items => {
419 type => "object",
420 properties => {
421 primary => { type => 'boolean'},
422 email => { type => 'string'},
423 },
424 },
425 },
426 code => sub {
427 my ($param) = @_;
428
429 my $cfg = PMG::LDAPConfig->new();
430 my $ids = $cfg->{ids};
431
432 my $profile = $param->{profile};
433
434 die "LDAP profile '$profile' does not exist\n"
435 if !$ids->{$profile};
436
437 my $config = $ids->{$profile};
438
439 die "profile '$profile' is disabled\n" if $config->{disable};
440
441 my $ldapcache = PMG::LDAPCache->new(
442 id => $profile, syncmode => 1, %$config);
443
444 my $res = $ldapcache->list_addresses($param->{email});
445
446 die "unable to find ldap user with email address '$param->{email}'\n"
447 if !$res;
448
449 return $res;
450
451 }});
452
453__PACKAGE__->register_method ({
454 name => 'profile_list_groups',
455 path => '{profile}/groups',
456 method => 'GET',
457 description => "List LDAP groups.",
458 permissions => { check => [ 'admin', 'audit' ] },
459 protected => 1,
460 proxyto => 'master',
461 parameters => {
462 additionalProperties => 0,
463 properties => {
464 profile => {
465 description => "Profile ID.",
466 type => 'string', format => 'pve-configid',
467 },
468 },
469 },
470 returns => {
471 type => 'array',
472 items => {
473 type => "object",
474 properties => {
475 dn => { type => 'string'},
476 gid => { type => 'number' },
477 },
478 },
479 links => [ { rel => 'child', href => "{gid}" } ],
480 },
481 code => sub {
482 my ($param) = @_;
483
484 my $cfg = PMG::LDAPConfig->new();
485 my $ids = $cfg->{ids};
486
487 my $profile = $param->{profile};
488
489 die "LDAP profile '$profile' does not exist\n"
490 if !$ids->{$profile};
491
492 my $config = $ids->{$profile};
493
494 return [] if $config->{disable};
495
496 my $ldapcache = PMG::LDAPCache->new(
497 id => $profile, syncmode => 1, %$config);
498
499 return $ldapcache->list_groups();
500 }});
501
502__PACKAGE__->register_method ({
503 name => 'profile_list_group_members',
504 path => '{profile}/groups/{gid}',
505 method => 'GET',
506 description => "List LDAP group members.",
507 permissions => { check => [ 'admin', 'audit' ] },
508 protected => 1,
509 proxyto => 'master',
510 parameters => {
511 additionalProperties => 0,
512 properties => {
513 profile => {
514 description => "Profile ID.",
515 type => 'string', format => 'pve-configid',
516 },
517 gid => {
518 description => "Group ID",
519 type => 'number',
520 },
521 },
522 },
523 returns => {
524 type => 'array',
525 items => {
526 type => "object",
527 properties => {
528 dn => { type => 'string'},
529 account => { type => 'string' },
530 pmail => { type => 'string' },
531 },
532 },
533 },
534 code => sub {
535 my ($param) = @_;
536
537 my $cfg = PMG::LDAPConfig->new();
538 my $ids = $cfg->{ids};
539
540 my $profile = $param->{profile};
541
542 die "LDAP profile '$profile' does not exist\n"
543 if !$ids->{$profile};
544
545 my $config = $ids->{$profile};
546
547 return [] if $config->{disable};
548
549 my $ldapcache = PMG::LDAPCache->new(
550 id => $profile, syncmode => 1, %$config);
551
552 return $ldapcache->list_users($param->{gid});
553 }});
554
5551;