]>
Commit | Line | Data |
---|---|---|
be6d6ffc DM |
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; | |
2fdba966 | 13 | use PVE::INotify; |
be6d6ffc DM |
14 | |
15 | use PMG::LDAPConfig; | |
b183e761 | 16 | use PMG::LDAPCache; |
21fb7eb2 | 17 | use PMG::LDAPSet; |
be6d6ffc DM |
18 | |
19 | use 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', |
bdbc2bc5 | 27 | permissions => { check => [ 'admin' ] }, |
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 | 85 | my $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', |
bdbc2bc5 | 183 | permissions => { check => [ 'admin' ] }, |
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.", | |
bdbc2bc5 | 348 | permissions => { check => [ 'admin' ] }, |
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.", | |
bdbc2bc5 | 398 | permissions => { check => [ 'admin' ] }, |
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.", | |
bdbc2bc5 | 456 | permissions => { check => [ 'admin' ] }, |
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.", | |
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 | ||
be6d6ffc | 553 | 1; |