]>
Commit | Line | Data |
---|---|---|
2c3a6c0a DM |
1 | package PVE::API2::Domains; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
b49abe2d | 5 | |
673d2bf2 | 6 | use PVE::Exception qw(raise_param_exc); |
5bb4e06a | 7 | use PVE::Tools qw(extract_param); |
2c3a6c0a DM |
8 | use PVE::Cluster qw (cfs_read_file cfs_write_file); |
9 | use PVE::AccessControl; | |
10 | use PVE::JSONSchema qw(get_standard_option); | |
11 | ||
12 | use PVE::SafeSyslog; | |
2c3a6c0a | 13 | use PVE::RESTHandler; |
5bb4e06a | 14 | use PVE::Auth::Plugin; |
2c3a6c0a DM |
15 | |
16 | my $domainconfigfile = "domains.cfg"; | |
17 | ||
18 | use base qw(PVE::RESTHandler); | |
19 | ||
98df2ebc DC |
20 | # maps old 'full'/'purge' parameters to new 'remove-vanished' |
21 | # TODO remove when we delete the 'full'/'purge' parameters | |
22 | my $map_remove_vanished = sub { | |
23 | my ($opt, $delete_deprecated) = @_; | |
24 | ||
25 | if (!defined($opt->{'remove-vanished'}) && ($opt->{full} || $opt->{purge})) { | |
26 | my $props = []; | |
27 | push @$props, 'entry', 'properties' if $opt->{full}; | |
28 | push @$props, 'acl' if $opt->{purge}; | |
29 | $opt->{'remove-vanished'} = join(';', @$props); | |
30 | } | |
31 | ||
32 | if ($delete_deprecated) { | |
33 | delete $opt->{full}; | |
34 | delete $opt->{purge}; | |
35 | } | |
36 | ||
37 | return $opt; | |
38 | }; | |
39 | ||
40 | my $map_sync_default_options = sub { | |
41 | my ($cfg, $delete_deprecated) = @_; | |
42 | ||
43 | my $opt = $cfg->{'sync-defaults-options'}; | |
44 | return if !defined($opt); | |
45 | my $sync_opts_fmt = PVE::JSONSchema::get_format('realm-sync-options'); | |
46 | ||
47 | my $old_opt = PVE::JSONSchema::parse_property_string($sync_opts_fmt, $opt); | |
48 | ||
49 | my $new_opt = $map_remove_vanished->($old_opt, $delete_deprecated); | |
50 | ||
51 | $cfg->{'sync-defaults-options'} = PVE::JSONSchema::print_property_string($new_opt, $sync_opts_fmt); | |
52 | }; | |
53 | ||
2c3a6c0a | 54 | __PACKAGE__->register_method ({ |
32449f35 DC |
55 | name => 'index', |
56 | path => '', | |
2c3a6c0a DM |
57 | method => 'GET', |
58 | description => "Authentication domain index.", | |
32449f35 | 59 | permissions => { |
82b63965 | 60 | description => "Anyone can access that, because we need that list for the login box (before the user is authenticated).", |
32449f35 | 61 | user => 'world', |
82b63965 | 62 | }, |
2c3a6c0a DM |
63 | parameters => { |
64 | additionalProperties => 0, | |
65 | properties => {}, | |
66 | }, | |
67 | returns => { | |
68 | type => 'array', | |
69 | items => { | |
70 | type => "object", | |
71 | properties => { | |
72 | realm => { type => 'string' }, | |
f3c87f9b | 73 | type => { type => 'string' }, |
96f8ebd6 DM |
74 | tfa => { |
75 | description => "Two-factor authentication provider.", | |
76 | type => 'string', | |
1abc2c0a | 77 | enum => [ 'yubico', 'oath' ], |
96f8ebd6 DM |
78 | optional => 1, |
79 | }, | |
52b2eff3 DM |
80 | comment => { |
81 | description => "A comment. The GUI use this text when you select a domain (Realm) on the login window.", | |
82 | type => 'string', | |
83 | optional => 1, | |
84 | }, | |
2c3a6c0a DM |
85 | }, |
86 | }, | |
87 | links => [ { rel => 'child', href => "{realm}" } ], | |
88 | }, | |
89 | code => sub { | |
90 | my ($param) = @_; | |
32449f35 | 91 | |
2c3a6c0a DM |
92 | my $res = []; |
93 | ||
94 | my $cfg = cfs_read_file($domainconfigfile); | |
5bb4e06a DM |
95 | my $ids = $cfg->{ids}; |
96 | ||
97 | foreach my $realm (keys %$ids) { | |
98 | my $d = $ids->{$realm}; | |
2c3a6c0a DM |
99 | my $entry = { realm => $realm, type => $d->{type} }; |
100 | $entry->{comment} = $d->{comment} if $d->{comment}; | |
101 | $entry->{default} = 1 if $d->{default}; | |
96f8ebd6 DM |
102 | if ($d->{tfa} && (my $tfa_cfg = PVE::Auth::Plugin::parse_tfa_config($d->{tfa}))) { |
103 | $entry->{tfa} = $tfa_cfg->{type}; | |
104 | } | |
2c3a6c0a DM |
105 | push @$res, $entry; |
106 | } | |
107 | ||
108 | return $res; | |
109 | }}); | |
110 | ||
111 | __PACKAGE__->register_method ({ | |
32449f35 | 112 | name => 'create', |
2c3a6c0a | 113 | protected => 1, |
32449f35 | 114 | path => '', |
2c3a6c0a | 115 | method => 'POST', |
32449f35 | 116 | permissions => { |
82b63965 | 117 | check => ['perm', '/access/realm', ['Realm.Allocate']], |
96919234 | 118 | }, |
2c3a6c0a | 119 | description => "Add an authentication server.", |
5bb4e06a | 120 | parameters => PVE::Auth::Plugin->createSchema(), |
2c3a6c0a DM |
121 | returns => { type => 'null' }, |
122 | code => sub { | |
123 | my ($param) = @_; | |
124 | ||
89338e4d TL |
125 | # always extract, add it with hook |
126 | my $password = extract_param($param, 'password'); | |
127 | ||
5bb4e06a | 128 | PVE::Auth::Plugin::lock_domain_config( |
2c3a6c0a | 129 | sub { |
32449f35 | 130 | |
2c3a6c0a | 131 | my $cfg = cfs_read_file($domainconfigfile); |
5bb4e06a | 132 | my $ids = $cfg->{ids}; |
2c3a6c0a | 133 | |
5bb4e06a DM |
134 | my $realm = extract_param($param, 'realm'); |
135 | my $type = $param->{type}; | |
32449f35 DC |
136 | |
137 | die "domain '$realm' already exists\n" | |
5bb4e06a | 138 | if $ids->{$realm}; |
2c3a6c0a DM |
139 | |
140 | die "unable to use reserved name '$realm'\n" | |
141 | if ($realm eq 'pam' || $realm eq 'pve'); | |
142 | ||
5bb4e06a DM |
143 | die "unable to create builtin type '$type'\n" |
144 | if ($type eq 'pam' || $type eq 'pve'); | |
af4a8a85 | 145 | |
98df2ebc DC |
146 | if ($type eq 'ad' || $type eq 'ldap') { |
147 | $map_sync_default_options->($param, 1); | |
148 | } | |
149 | ||
5bb4e06a DM |
150 | my $plugin = PVE::Auth::Plugin->lookup($type); |
151 | my $config = $plugin->check_config($realm, $param, 1, 1); | |
2c3a6c0a | 152 | |
5bb4e06a DM |
153 | if ($config->{default}) { |
154 | foreach my $r (keys %$ids) { | |
155 | delete $ids->{$r}->{default}; | |
0c156363 | 156 | } |
af4a8a85 DM |
157 | } |
158 | ||
5bb4e06a DM |
159 | $ids->{$realm} = $config; |
160 | ||
89338e4d TL |
161 | my $opts = $plugin->options(); |
162 | if (defined($password) && !defined($opts->{password})) { | |
163 | $password = undef; | |
164 | warn "ignoring password parameter"; | |
165 | } | |
166 | $plugin->on_add_hook($realm, $config, password => $password); | |
167 | ||
2c3a6c0a DM |
168 | cfs_write_file($domainconfigfile, $cfg); |
169 | }, "add auth server failed"); | |
170 | ||
171 | return undef; | |
172 | }}); | |
173 | ||
174 | __PACKAGE__->register_method ({ | |
32449f35 DC |
175 | name => 'update', |
176 | path => '{realm}', | |
2c3a6c0a | 177 | method => 'PUT', |
32449f35 | 178 | permissions => { |
82b63965 | 179 | check => ['perm', '/access/realm', ['Realm.Allocate']], |
96919234 | 180 | }, |
2c3a6c0a DM |
181 | description => "Update authentication server settings.", |
182 | protected => 1, | |
5bb4e06a | 183 | parameters => PVE::Auth::Plugin->updateSchema(), |
2c3a6c0a DM |
184 | returns => { type => 'null' }, |
185 | code => sub { | |
186 | my ($param) = @_; | |
187 | ||
89338e4d TL |
188 | # always extract, update in hook |
189 | my $password = extract_param($param, 'password'); | |
190 | ||
5bb4e06a | 191 | PVE::Auth::Plugin::lock_domain_config( |
2c3a6c0a | 192 | sub { |
32449f35 | 193 | |
2c3a6c0a | 194 | my $cfg = cfs_read_file($domainconfigfile); |
5bb4e06a | 195 | my $ids = $cfg->{ids}; |
2c3a6c0a | 196 | |
5bb4e06a DM |
197 | my $digest = extract_param($param, 'digest'); |
198 | PVE::SectionConfig::assert_if_modified($cfg, $digest); | |
199 | ||
200 | my $realm = extract_param($param, 'realm'); | |
2c3a6c0a | 201 | |
32449f35 | 202 | die "domain '$realm' does not exist\n" |
5bb4e06a DM |
203 | if !$ids->{$realm}; |
204 | ||
205 | my $delete_str = extract_param($param, 'delete'); | |
206 | die "no options specified\n" if !$delete_str && !scalar(keys %$param); | |
2c3a6c0a | 207 | |
89338e4d | 208 | my $delete_pw = 0; |
5bb4e06a DM |
209 | foreach my $opt (PVE::Tools::split_list($delete_str)) { |
210 | delete $ids->{$realm}->{$opt}; | |
89338e4d | 211 | $delete_pw = 1 if $opt eq 'password'; |
2c3a6c0a | 212 | } |
32449f35 | 213 | |
98df2ebc DC |
214 | my $type = $ids->{$realm}->{type}; |
215 | if ($type eq 'ad' || $type eq 'ldap') { | |
216 | $map_sync_default_options->($param, 1); | |
217 | } | |
218 | ||
219 | my $plugin = PVE::Auth::Plugin->lookup($type); | |
5bb4e06a | 220 | my $config = $plugin->check_config($realm, $param, 0, 1); |
2c3a6c0a | 221 | |
5bb4e06a DM |
222 | if ($config->{default}) { |
223 | foreach my $r (keys %$ids) { | |
224 | delete $ids->{$r}->{default}; | |
2c3a6c0a DM |
225 | } |
226 | } | |
227 | ||
5bb4e06a DM |
228 | foreach my $p (keys %$config) { |
229 | $ids->{$realm}->{$p} = $config->{$p}; | |
af4a8a85 DM |
230 | } |
231 | ||
89338e4d TL |
232 | my $opts = $plugin->options(); |
233 | if ($delete_pw || defined($password)) { | |
234 | $plugin->on_update_hook($realm, $config, password => $password); | |
235 | } else { | |
236 | $plugin->on_update_hook($realm, $config); | |
237 | } | |
238 | ||
2c3a6c0a DM |
239 | cfs_write_file($domainconfigfile, $cfg); |
240 | }, "update auth server failed"); | |
241 | ||
242 | return undef; | |
243 | }}); | |
244 | ||
245 | # fixme: return format! | |
246 | __PACKAGE__->register_method ({ | |
32449f35 DC |
247 | name => 'read', |
248 | path => '{realm}', | |
2c3a6c0a DM |
249 | method => 'GET', |
250 | description => "Get auth server configuration.", | |
32449f35 | 251 | permissions => { |
82b63965 | 252 | check => ['perm', '/access/realm', ['Realm.Allocate', 'Sys.Audit'], any => 1], |
96919234 | 253 | }, |
2c3a6c0a DM |
254 | parameters => { |
255 | additionalProperties => 0, | |
256 | properties => { | |
257 | realm => get_standard_option('realm'), | |
258 | }, | |
259 | }, | |
260 | returns => {}, | |
261 | code => sub { | |
262 | my ($param) = @_; | |
263 | ||
264 | my $cfg = cfs_read_file($domainconfigfile); | |
265 | ||
266 | my $realm = $param->{realm}; | |
32449f35 | 267 | |
5bb4e06a | 268 | my $data = $cfg->{ids}->{$realm}; |
2c3a6c0a DM |
269 | die "domain '$realm' does not exist\n" if !$data; |
270 | ||
98df2ebc DC |
271 | my $type = $data->{type}; |
272 | if ($type eq 'ad' || $type eq 'ldap') { | |
273 | $map_sync_default_options->($data); | |
274 | } | |
275 | ||
5bb4e06a DM |
276 | $data->{digest} = $cfg->{digest}; |
277 | ||
2c3a6c0a DM |
278 | return $data; |
279 | }}); | |
280 | ||
281 | ||
282 | __PACKAGE__->register_method ({ | |
32449f35 DC |
283 | name => 'delete', |
284 | path => '{realm}', | |
2c3a6c0a | 285 | method => 'DELETE', |
32449f35 | 286 | permissions => { |
82b63965 | 287 | check => ['perm', '/access/realm', ['Realm.Allocate']], |
96919234 | 288 | }, |
2c3a6c0a DM |
289 | description => "Delete an authentication server.", |
290 | protected => 1, | |
291 | parameters => { | |
292 | additionalProperties => 0, | |
293 | properties => { | |
294 | realm => get_standard_option('realm'), | |
295 | } | |
296 | }, | |
297 | returns => { type => 'null' }, | |
298 | code => sub { | |
299 | my ($param) = @_; | |
300 | ||
5bb4e06a | 301 | PVE::Auth::Plugin::lock_domain_config( |
2c3a6c0a DM |
302 | sub { |
303 | ||
304 | my $cfg = cfs_read_file($domainconfigfile); | |
5bb4e06a | 305 | my $ids = $cfg->{ids}; |
2c3a6c0a | 306 | my $realm = $param->{realm}; |
32449f35 | 307 | |
89338e4d TL |
308 | die "authentication domain '$realm' does not exist\n" if !$ids->{$realm}; |
309 | ||
310 | my $plugin = PVE::Auth::Plugin->lookup($ids->{$realm}->{type}); | |
311 | ||
312 | $plugin->on_delete_hook($realm, $ids->{$realm}); | |
2c3a6c0a | 313 | |
5bb4e06a | 314 | delete $ids->{$realm}; |
2c3a6c0a DM |
315 | |
316 | cfs_write_file($domainconfigfile, $cfg); | |
317 | }, "delete auth server failed"); | |
32449f35 | 318 | |
2c3a6c0a DM |
319 | return undef; |
320 | }}); | |
321 | ||
415179b0 TL |
322 | my $update_users = sub { |
323 | my ($usercfg, $realm, $synced_users, $opts) = @_; | |
324 | ||
187cb854 TL |
325 | if (defined(my $vanished = $opts->{'remove-vanished'})) { |
326 | print "syncing users (remove-vanished opts: $vanished)\n"; | |
327 | } else { | |
328 | print "syncing users\n"; | |
329 | } | |
98df2ebc | 330 | |
415179b0 TL |
331 | $usercfg->{users} = {} if !defined($usercfg->{users}); |
332 | my $users = $usercfg->{users}; | |
98df2ebc | 333 | my $to_remove = { map { $_ => 1 } split(';', $opts->{'remove-vanished'} // '') }; |
415179b0 | 334 | |
98df2ebc DC |
335 | print "deleting outdated existing users first\n" if $to_remove->{entry}; |
336 | foreach my $userid (sort keys %$users) { | |
337 | next if $userid !~ m/\@$realm$/; | |
338 | next if defined($synced_users->{$userid}); | |
339 | ||
340 | if ($to_remove->{entry}) { | |
341 | print "remove user '$userid'\n"; | |
342 | delete $users->{$userid}; | |
343 | } | |
344 | ||
345 | if ($to_remove->{acl}) { | |
346 | print "purge users '$userid' ACL entries\n"; | |
347 | PVE::AccessControl::delete_user_acl($userid, $usercfg); | |
415179b0 TL |
348 | } |
349 | } | |
350 | ||
351 | foreach my $userid (sort keys %$synced_users) { | |
352 | my $synced_user = $synced_users->{$userid} // {}; | |
98df2ebc DC |
353 | my $olduser = $users->{$userid}; |
354 | if ($to_remove->{properties} || !defined($olduser)) { | |
355 | # we use the synced user, but want to keep some properties on update | |
356 | if (defined($olduser)) { | |
357 | print "overwriting user '$userid'\n"; | |
358 | } else { | |
359 | $olduser = {}; | |
360 | print "adding user '$userid'\n"; | |
361 | } | |
415179b0 TL |
362 | my $user = $users->{$userid} = $synced_user; |
363 | ||
98df2ebc DC |
364 | my $enabled = $olduser->{enable} // $opts->{'enable-new'}; |
365 | $user->{enable} = $enabled if defined($enabled); | |
366 | $user->{tokens} = $olduser->{tokens} if defined($olduser->{tokens}); | |
415179b0 | 367 | |
415179b0 | 368 | } else { |
415179b0 TL |
369 | foreach my $attr (keys %$synced_user) { |
370 | $olduser->{$attr} = $synced_user->{$attr}; | |
371 | } | |
98df2ebc | 372 | print "updating user '$userid'\n"; |
415179b0 TL |
373 | } |
374 | } | |
375 | }; | |
376 | ||
377 | my $update_groups = sub { | |
378 | my ($usercfg, $realm, $synced_groups, $opts) = @_; | |
379 | ||
187cb854 TL |
380 | if (defined(my $vanished = $opts->{'remove-vanished'})) { |
381 | print "syncing groups (remove-vanished opts: $vanished)\n"; | |
382 | } else { | |
383 | print "syncing groups\n"; | |
384 | } | |
98df2ebc | 385 | |
415179b0 TL |
386 | $usercfg->{groups} = {} if !defined($usercfg->{groups}); |
387 | my $groups = $usercfg->{groups}; | |
98df2ebc DC |
388 | my $to_remove = { map { $_ => 1 } split(';', $opts->{'remove-vanished'} // '') }; |
389 | ||
390 | print "deleting outdated existing groups first\n" if $to_remove->{entry}; | |
391 | foreach my $groupid (sort keys %$groups) { | |
392 | next if $groupid !~ m/\-$realm$/; | |
393 | next if defined($synced_groups->{$groupid}); | |
394 | ||
395 | if ($to_remove->{entry}) { | |
396 | print "remove group '$groupid'\n"; | |
397 | delete $groups->{$groupid}; | |
398 | } | |
399 | ||
400 | if ($to_remove->{acl}) { | |
401 | print "purge groups '$groupid' ACL entries\n"; | |
402 | PVE::AccessControl::delete_group_acl($groupid, $usercfg); | |
415179b0 TL |
403 | } |
404 | } | |
405 | ||
406 | foreach my $groupid (sort keys %$synced_groups) { | |
407 | my $synced_group = $synced_groups->{$groupid}; | |
98df2ebc DC |
408 | my $oldgroup = $groups->{$groupid}; |
409 | if ($to_remove->{properties} || !defined($oldgroup)) { | |
410 | if (defined($oldgroup)) { | |
411 | print "overwriting group '$groupid'\n"; | |
415179b0 | 412 | } else { |
98df2ebc | 413 | print "adding group '$groupid'\n"; |
415179b0 | 414 | } |
98df2ebc | 415 | $groups->{$groupid} = $synced_group; |
415179b0 | 416 | } else { |
415179b0 | 417 | foreach my $attr (keys %$synced_group) { |
98df2ebc | 418 | $oldgroup->{$attr} = $synced_group->{$attr}; |
415179b0 | 419 | } |
98df2ebc | 420 | print "updating group '$groupid'\n"; |
415179b0 TL |
421 | } |
422 | } | |
423 | }; | |
424 | ||
d29d2d4a TL |
425 | my $parse_sync_opts = sub { |
426 | my ($param, $realmconfig) = @_; | |
427 | ||
428 | my $sync_opts_fmt = PVE::JSONSchema::get_format('realm-sync-options'); | |
429 | ||
6c42a103 | 430 | my $cfg_defaults = {}; |
d29d2d4a | 431 | if (defined(my $cfg_opts = $realmconfig->{'sync-defaults-options'})) { |
6c42a103 | 432 | $cfg_defaults = PVE::JSONSchema::parse_property_string($sync_opts_fmt, $cfg_opts); |
d29d2d4a TL |
433 | } |
434 | ||
6c42a103 | 435 | my $res = {}; |
d29d2d4a TL |
436 | for my $opt (sort keys %$sync_opts_fmt) { |
437 | my $fmt = $sync_opts_fmt->{$opt}; | |
438 | ||
6c42a103 | 439 | $res->{$opt} = $param->{$opt} // $cfg_defaults->{$opt} // $fmt->{default}; |
d29d2d4a | 440 | } |
98df2ebc DC |
441 | |
442 | $map_remove_vanished->($res, 1); | |
443 | ||
444 | # only scope has no implicit value | |
445 | raise_param_exc({ | |
446 | "scope" => 'Not passed as parameter and not defined in realm default sync options.' | |
447 | }) if !defined($res->{scope}); | |
448 | ||
d29d2d4a TL |
449 | return $res; |
450 | }; | |
451 | ||
673d2bf2 DC |
452 | __PACKAGE__->register_method ({ |
453 | name => 'sync', | |
454 | path => '{realm}/sync', | |
455 | method => 'POST', | |
456 | permissions => { | |
cf109814 TL |
457 | description => "'Realm.AllocateUser' on '/access/realm/<realm>' and " |
458 | ." 'User.Modify' permissions to '/access/groups/'.", | |
673d2bf2 | 459 | check => [ 'and', |
0c503211 WB |
460 | ['perm', '/access/realm/{realm}', ['Realm.AllocateUser']], |
461 | ['perm', '/access/groups', ['User.Modify']], | |
cf109814 | 462 | ], |
673d2bf2 | 463 | }, |
cf109814 TL |
464 | description => "Syncs users and/or groups from the configured LDAP to user.cfg." |
465 | ." NOTE: Synced groups will have the name 'name-\$realm', so make sure" | |
466 | ." those groups do not exist to prevent overwriting.", | |
673d2bf2 DC |
467 | protected => 1, |
468 | parameters => { | |
469 | additionalProperties => 0, | |
d29d2d4a TL |
470 | properties => get_standard_option('realm-sync-options', { |
471 | realm => get_standard_option('realm'), | |
38691d98 DC |
472 | 'dry-run' => { |
473 | description => "If set, does not write anything.", | |
474 | type => 'boolean', | |
475 | optional => 1, | |
476 | default => 0, | |
477 | }, | |
478 | }), | |
673d2bf2 | 479 | }, |
cf109814 TL |
480 | returns => { |
481 | description => 'Worker Task-UPID', | |
482 | type => 'string' | |
483 | }, | |
673d2bf2 DC |
484 | code => sub { |
485 | my ($param) = @_; | |
486 | ||
487 | my $rpcenv = PVE::RPCEnvironment::get(); | |
488 | my $authuser = $rpcenv->get_user(); | |
489 | ||
417309d7 | 490 | my $dry_run = extract_param($param, 'dry-run'); |
673d2bf2 DC |
491 | my $realm = $param->{realm}; |
492 | my $cfg = cfs_read_file($domainconfigfile); | |
cf109814 | 493 | my $realmconfig = $cfg->{ids}->{$realm}; |
673d2bf2 | 494 | |
cf109814 TL |
495 | raise_param_exc({ 'realm' => 'Realm does not exist.' }) if !defined($realmconfig); |
496 | my $type = $realmconfig->{type}; | |
673d2bf2 DC |
497 | |
498 | if ($type ne 'ldap' && $type ne 'ad') { | |
cf109814 | 499 | die "Cannot sync realm type '$type'! Only LDAP/AD realms can be synced.\n"; |
673d2bf2 | 500 | } |
673d2bf2 | 501 | |
d29d2d4a | 502 | my $opts = $parse_sync_opts->($param, $realmconfig); # can throw up |
673d2bf2 | 503 | |
d29d2d4a | 504 | my $scope = $opts->{scope}; |
cf109814 | 505 | my $whatstring = $scope eq 'both' ? "users and groups" : $scope; |
673d2bf2 | 506 | |
cf109814 | 507 | my $plugin = PVE::Auth::Plugin->lookup($type); |
673d2bf2 DC |
508 | |
509 | my $worker = sub { | |
417309d7 TL |
510 | print "(dry test run) " if $dry_run; |
511 | print "starting sync for realm $realm\n"; | |
cf109814 TL |
512 | |
513 | my ($synced_users, $dnmap) = $plugin->get_users($realmconfig, $realm); | |
514 | my $synced_groups = {}; | |
515 | if ($scope eq 'groups' || $scope eq 'both') { | |
516 | $synced_groups = $plugin->get_groups($realmconfig, $realm, $dnmap); | |
673d2bf2 DC |
517 | } |
518 | ||
cf109814 | 519 | PVE::AccessControl::lock_user_config(sub { |
673d2bf2 | 520 | my $usercfg = cfs_read_file("user.cfg"); |
cf109814 | 521 | print "got data from server, updating $whatstring\n"; |
673d2bf2 | 522 | |
415179b0 | 523 | if ($scope eq 'users' || $scope eq 'both') { |
d29d2d4a | 524 | $update_users->($usercfg, $realm, $synced_users, $opts); |
673d2bf2 DC |
525 | } |
526 | ||
415179b0 | 527 | if ($scope eq 'groups' || $scope eq 'both') { |
d29d2d4a | 528 | $update_groups->($usercfg, $realm, $synced_groups, $opts); |
673d2bf2 | 529 | } |
cf109814 | 530 | |
417309d7 TL |
531 | if ($dry_run) { |
532 | print "\nNOTE: Dry test run, changes were NOT written to the configuration.\n"; | |
533 | return; | |
38691d98 | 534 | } |
417309d7 TL |
535 | cfs_write_file("user.cfg", $usercfg); |
536 | print "successfully updated $whatstring configuration\n"; | |
cf109814 | 537 | }, "syncing $whatstring failed"); |
673d2bf2 DC |
538 | }; |
539 | ||
417309d7 | 540 | my $workerid = !$dry_run ? 'auth-realm-sync' : 'auth-realm-sync-test'; |
38691d98 | 541 | return $rpcenv->fork_worker($workerid, $realm, $authuser, $worker); |
673d2bf2 DC |
542 | }}); |
543 | ||
2c3a6c0a | 544 | 1; |