Force change at next login fixes
[webmin.git] / ldap-useradmin / edit_user.cgi
1 #!/usr/local/bin/perl
2 # edit_user.cgi
3 # Display details of an existing user and allow editing
4
5 require './ldap-useradmin-lib.pl';
6 use Time::Local;
7 &ReadParse();
8 $ldap = &ldap_connect();
9 $schema = $ldap->schema();
10 if ($in{'new'}) {
11         $access{'ucreate'} || &error($text{'uedit_ecreate'});
12         $pass = $mconfig{'lock_string'};
13         $shell = $mconfig{'default_shell'} if ($mconfig{'default_shell'});
14         foreach $oec (split(/\s+/, $config{'other_class'})) {
15                 $oclass{$oec}++;
16                 }
17         if ($config{'samba_def'}) {
18                 $oclass{$samba_class}++;
19                 }
20         if ($config{'imap_def'}) {
21                 @cyrus_class_3 = split(' ',$cyrus_class);
22                 $oclass{$cyrus_class_3[0]}++;
23                 }
24
25         # Get initial values from form parameters
26         foreach $n ("user", "firstname", "lastname", "real", "home", "shell",
27                     "gid", "pass", "change", "expire", "min", "max", "warn",
28                     "inactive") {
29                 if (defined($in{$n})) {
30                         $$n = $in{$n};
31                         }
32                 }
33         &ui_print_header(undef, $text{'uedit_title2'}, "");
34         }
35 else {
36         # Get values from current user
37         $rv = $ldap->search(base => $in{'dn'},
38                             scope => 'base',
39                             filter => &user_filter());
40         ($uinfo) = $rv->all_entries;
41         @users = $uinfo->get_value('uid');
42         $user = $users[0];
43         $uid = $uinfo->get_value('uidNumber');
44         $firstname = $uinfo->get_value('givenName');
45         $lastname = $uinfo->get_value('sn');
46         $real = $uinfo->get_value('cn');
47         $home = $uinfo->get_value('homeDirectory');
48         $shell = $uinfo->get_value('loginShell');
49         $gid = $uinfo->get_value('gidNumber');
50         $pass = $uinfo->get_value('userPassword');
51         $change = $uinfo->get_value('shadowLastChange');
52         $expire = $uinfo->get_value('shadowExpire');
53         $min = $uinfo->get_value('shadowMin');
54         $max = $uinfo->get_value('shadowMax');
55         $warn = $uinfo->get_value('shadowWarning');
56         $inactive = $uinfo->get_value('shadowInactive');
57         foreach $oc ($uinfo->get_value('objectClass')) {
58                 $oclass{$oc} = 1;
59                 }
60         @alias = $uinfo->get_value($config{'maillocaladdress'} || 'alias');
61         %uinfo = &dn_to_hash($uinfo);
62         &can_edit_user(\%uinfo) || &error($text{'uedit_eedit'});
63         &ui_print_header(undef, $text{'uedit_title'}, "");
64         }
65 @tds = ( "width=30%" );
66
67 # build a list of used shells and uids
68 @shlist = ($mconfig{'default_shell'} ? ( $mconfig{'default_shell'} ) : ( ));
69 %shells = map { $_, 1 } split(/,/, $config{'shells'});
70 push(@shlist, "/bin/sh", "/bin/csh", "/bin/false") if ($shells{'fixed'});
71 if ($shells{'passwd'}) {
72         # Don't do this unless we need to, as scanning all users is slow
73         &build_user_used(undef, \@shlist);
74         }
75 if ($shells{'shells'}) {
76         open(SHELLS, "/etc/shells");
77         while(<SHELLS>) {
78                 s/\r|\n//g;
79                 s/#.*$//;
80                 push(@shlist, $_) if (/\S/);
81                 }
82         close(SHELLS);
83         }
84 push(@shlist, $shell) if ($shell);
85 @shlist = &unique(@shlist);
86
87 # Start of the form
88 print &ui_form_start("save_user.cgi", "post");
89 print &ui_hidden("new", $in{'new'});
90 print &ui_hidden("dn", $in{'dn'});
91 print &ui_table_start($text{'uedit_details'}, "width=100%", 2, \@tds);
92
93 # DN and classes
94 if (!$in{'new'}) {
95         print &ui_table_row($text{'uedit_dn'},
96                 "<tt>$in{'dn'}</tt>", 3);
97
98         print &ui_table_row($text{'uedit_classes'},
99                 ,join(" , ", map { "<tt>$_</tt>" }
100                         $uinfo->get_value('objectClass')), 3);
101         }
102
103 # Show username input
104 print &ui_table_row($text{'user'},
105         @users > 1 ? &ui_textarea("user", join("\n", @users), 2, 10)
106                    : &ui_textbox("user", $user, 20));
107
108 # Show UID input, filled in with a default for new users
109 if ($in{'new'}) {
110         # Find the first free UID above the base
111         $newuid = $mconfig{'base_uid'};
112         while(&check_uid_used($ldap, $newuid)) {
113                 $newuid++;
114                 }
115         $uidfield = &ui_textbox("uid", $newuid, 10);
116         }
117 else {
118         $uidfield = &ui_textbox("uid", $uid, 10);
119         }
120 print &ui_table_row($text{'uid'}, $uidfield);
121
122 if ($config{'given'}) {
123         # Show Full name inputs
124         if ($in{'new'}) {
125                 if ($config{'given_order'} == 0) {
126                         # Firstname surname
127                         $onch = "onChange='form.real.value = form.firstname.value+\" \"+form.lastname.value'";
128                         }
129                 else {
130                         # Surname, firstname
131                         $onch = "onChange='form.real.value = form.lastname.value+\", \"+form.firstname.value'";
132                         }
133                 }
134         print &ui_table_row($text{'uedit_firstname'},
135                 &ui_textbox("firstname", $firstname, 20, 0, undef, $onch));
136
137         print &ui_table_row($text{'uedit_lastname'},
138                 &ui_textbox("lastname", $lastname, 20, 0, undef, $onch));
139         }
140
141 # Show real name input
142 print &ui_table_row($text{'real'},
143         &ui_textbox("real", $real, 40));
144
145 # Work out group name
146 if ($in{'new'}) {
147         $grp = $mconfig{'default_group'};
148         }
149 else {
150         $grp = &all_getgrgid($gid);
151         }
152
153 # Show home directory input, with an 'automatic' option
154 if ($mconfig{'home_base'}) {
155         local $hb = $in{'new'} ||
156             &auto_home_dir($mconfig{'home_base'}, $user, $grp) eq $home;
157         $homefield = &ui_radio("home_base", $hb ? 1 : 0,
158                                [ [ 1, $text{'uedit_auto'} ],
159                                  [ 0, &ui_filebox("home", $hb ? "" : $home,
160                                                   25, 0, undef, undef, 1) ]
161                                ]);
162         }
163 else {
164         $homefield = &ui_filebox("home", $home, 25, 0, undef, undef, 1);
165         }
166 print &ui_table_row($text{'home'}, $homefield);
167
168 # Show shell selection menu
169 print &ui_table_row($text{'shell'},
170         &ui_select("shell", $uinfo{'shell'}, \@shlist, 1, 0, 0, 0,
171            "onChange='form.othersh.disabled = form.shell.value != \"*\"'").
172         &ui_filebox("othersh", undef, 40, 1));
173
174 # Generate password if needed
175 if ($in{'new'} && $mconfig{'random_password'}) {
176         $random_password = &useradmin::generate_random_password();
177         }
178
179 # Check if temporary locking is supported
180 if (!$in{'new'} && $pass ne $mconfig{'lock_string'} && $pass ne "") {
181         # Can disable if not already locked, or if a new account
182         $can_disable = 1;
183         if ($pass =~ /^\Q$useradmin::disable_string\E/) {
184                 $disabled = 1;
185                 $pass =~ s/^\Q$useradmin::disable_string\E//;
186                 }
187         }
188 elsif ($in{'new'}) {
189         $can_disable = 1;
190         }
191
192 # Show password field
193 $passmode = $pass eq "" && $random_password eq "" ? 0 :
194             $pass eq $mconfig{'lock_string'} && $random_password eq "" ? 1 :
195             $random_password ne "" ? 3 :
196             $pass && $pass ne $mconfig{'lock_string'} &&
197                 $random_password eq "" ? 2 : -1;
198 $pffunc = $mconfig{'passwd_stars'} ? \&ui_password : \&ui_textbox;
199 print &ui_table_row($text{'pass'},
200         &ui_radio_table("passmode", $passmode,
201           [ [ 0, $mconfig{'empty_mode'} ? $text{'none1'} : $text{'none2'} ],
202             [ 1, $text{'nologin'} ],
203             [ 3, $text{'clear'},
204               &$pffunc("pass", $mconfig{'random_password'} && $n eq "" ?
205                                 $random_password : "", 15) ],
206             $access{'nocrypt'} ?                 ( [ 2, $text{'nochange'},
207                     &ui_hidden("encpass", $pass) ] ) :
208                 ( [ 2, $text{'encrypted'},
209                     &ui_textbox("encpass", $passmode == 2 ? $pass : "", 40) ] )
210           ]).
211           ($can_disable ? "&nbsp;&nbsp;".&ui_checkbox("disable", 1,
212                                 $text{'uedit_disabled'}, $disabled) : "")
213           );
214
215 print &ui_table_end();
216
217 # Show shadow password options
218 if (&in_schema($schema, "shadowLastChange")) {
219         print &ui_table_start($text{'uedit_passopts'}, "width=100%", 4, \@tds);
220
221         # Last change date
222         print &ui_table_row($text{'change'},
223                 ($uinfo{'change'} ? &make_date(timelocal(
224                                        gmtime($change * 60*60*24)),1) :
225                  $n eq "" ? $text{'uedit_never'} :
226                             $text{'uedit_unknown'}));
227
228         # Expiry date
229         if ($in{'new'} &&
230             $mconfig{'default_expire'} =~ /^(\d+)\/(\d+)\/(\d+)$/) {
231                 $eday = $1;
232                 $emon = $2;
233                 $eyear = $3;
234                 }
235         elsif ($expire) {
236                 @tm = localtime(timelocal(gmtime($expire * 60*60*24)));
237                 $eday = $tm[3];
238                 $emon = $tm[4]+1;
239                 $eyear = $tm[5]+1900;
240                 }
241         print &ui_table_row($text{'expire'},
242                 &useradmin::date_input($eday, $emon, $eyear, 'expire'));
243
244         # Minimum and maximum days for changing
245         print &ui_table_row($text{'min'},
246                 &ui_textbox("min", $in{'new'} ? $mconfig{'default_min'}
247                                               : $min, 5));
248         print &ui_table_row($text{'max'},
249                 &ui_textbox("max", $in{'new'} ? $mconfig{'default_max'}
250                                               : $max, 5));
251
252         # Password warning days
253         print &ui_table_row($text{'warn'},
254                 &ui_textbox("warn", $in{'new'} ? $mconfig{'default_warn'}
255                                                : $warn, 5));
256
257         # Inactive dats
258         print &ui_table_row($text{'inactive'},
259                 &ui_textbox("inactive", $in{'new'} ?$mconfig{'default_inactive'}
260                                                    : $inactive, 5));
261
262         # Force password change at next login
263         print &ui_table_row(
264                 $text{'uedit_forcechange'},
265                         &ui_yesno_radio("forcechange", 0));
266
267
268         print &ui_table_end();
269         }
270
271 # Group memberships section
272 print &ui_table_start($text{'uedit_gmem'}, "width=100%", 4, \@tds);
273
274 # Primary group
275 print &ui_table_row($text{'group'},
276         &ui_textbox("gid", $grp || $gid, 13).
277         " ".&group_chooser_button("gid"), 3);
278
279 if ($config{'secmode'} != 1) {
280         # Work out which secondary groups the user is in
281         @defsecs = &split_quoted_string($mconfig{'default_secs'});
282         $base = &get_group_base();
283         $rv = $ldap->search(base => $base,
284                             filter => &group_filter());
285         %ingroups = ( );
286         foreach $g (sort { lc($a->dn()) cmp lc($b->dn()) } $rv->all_entries) {
287                 $group = $g->get_value("cn");
288                 @mems = $g->get_value("memberUid");
289                 $desc = $g->get_value("description");
290                 local $ismem = &indexof($user, @mems) >= 0;
291                 if ($n eq "") {
292                         $ismem = 1 if (&indexof($group, @defsecs) >= 0);
293                         }
294                 $ingroups{$group} = $ismem;
295                 $descgroups{$group} = " ($desc)";
296                 }
297         }
298
299 if ($config{'secmode'} == 0) {
300         # Show secondary groups with select menu
301         foreach $g (sort { lc($a->dn()) cmp lc($b->dn()) } $rv->all_entries) {
302                 $group = $g->get_value("cn");
303                 push(@canglist, [ $group, $group ]);
304                 }
305         @ingroups = map { [ $_, $_.$descgroups{$_} ] } sort { $a cmp $b }
306                         grep { $ingroups{$_} } (keys %ingroups);
307         $groupfield = &ui_multi_select("sgid", \@ingroups, \@canglist, 5, 1, 0,
308                              $text{'uedit_allg'}, $text{'uedit_ing'});
309         }
310 elsif ($config{'secmode'} == 2) {
311         # Show a text box
312         @insecs = ( );
313         foreach $g (sort { lc($a->dn()) cmp lc($b->dn()) } $rv->all_entries) {
314                 $group = $g->get_value("cn");
315                 if ($ingroups{$group}) {
316                         push(@insecs, $group);
317                         }
318                 }
319         $groupfield = &ui_textarea("sgid", join("\n", @insecs), 5, 20);
320         }
321 if ($groupfield) {
322         print &ui_table_row($text{'uedit_2nd'}, $groupfield, 3);
323         }
324
325 print &ui_table_end();
326
327 # Show extra fields (if any)
328 &extra_fields_input($config{'fields'}, $uinfo, \@tds);
329
330 # Show capabilties section
331 print &ui_table_start($text{'uedit_cap'}, "width=100%", 4, \@tds);
332
333 # Samba login?
334 print &ui_table_row($text{'uedit_samba'},
335         &ui_yesno_radio("samba", $oclass{$samba_class} ? 1 : 0));
336
337 if ($config{'imap_host'}) {
338         # Cyrus IMAP login
339         @cyrus_class_3 = split(' ',$cyrus_class);
340         print &ui_table_row($text{'uedit_cyrus'},
341                 &ui_yesno_radio("cyrus", $oclass{$cyrus_class_3[0]} ? 1 : 0));
342
343         # IMAP domain
344         if ($config{'domain'}) {
345                 print &ui_table_row($text{'uedit_alias'},
346                         &ui_textbox("alias", join(" ", @alias), 40), 3);
347                 }
348
349         # Show field for changing the quota on existing users, or setting
350         # it for new users
351         if ($config{'quota_support'}) {
352                 print &ui_table_row($text{'uedit_quota'},
353                         $in{'new'} || !$oclass{$cyrus_class_3[0]} ?
354                           &ui_textbox("quota", $config{'quota'}, 10)." kB" :
355                           &ui_opt_textbox("quota", undef, 10,
356                                               $text{'uedit_unquota'})." Kb");
357                 }
358         }
359 else {
360         print &ui_hidden("cyrus", $oclass{$cyrus_class});
361         }
362 print &ui_table_end();
363
364 if ($in{'new'}) {
365         # On-create options
366         print &ui_table_start($text{'uedit_oncreate'}, "width=100%",
367                               2, \@tds);
368
369         # Create home dir?
370         print &ui_table_row($text{'uedit_makehome'},
371                 &ui_yesno_radio("makehome", 1));
372
373         # Create in other modules?
374         print &ui_table_row($text{'uedit_cothers'},
375                 &ui_yesno_radio("others", $mconfig{'default_other'}));
376
377         print &ui_table_end();
378         }
379 else {
380         # On save options
381         print &ui_table_start($text{'onsave'}, "width=100%", 2, \@tds);
382
383         # Move home directory
384         print &ui_table_row($text{'uedit_movehome'},
385                 &ui_yesno_radio("movehome", 1));
386
387         # Change UID on files
388         print &ui_table_row($text{'uedit_chuid'},
389                 &ui_radio("chuid", 1,
390                           [ [ 0, $text{'no'} ],
391                             [ 1, $text{'home'} ],
392                             [ 2, $text{'uedit_allfiles'} ] ]));
393
394         # Change GID on files
395         print &ui_table_row($text{'uedit_chgid'},
396                 &ui_radio("chgid", 1,
397                           [ [ 0, $text{'no'} ],
398                             [ 1, $text{'home'} ],
399                             [ 2, $text{'uedit_allfiles'} ] ]));
400
401         # Modify in other modules
402         print &ui_table_row($text{'uedit_mothers'},
403                 &ui_yesno_radio("others",
404                         $mconfig{'default_other'} ? 1 : 0));
405
406         print &ui_table_end();
407         }
408
409 # Build buttons for end of form
410 @buts = ( );
411 if ($in{'new'}) {
412         # Show buttons for new users
413         push(@buts, [ undef, $text{'create'} ]);
414         }
415 else {
416         # Show buttons for existing users
417         push(@buts, [ undef, $text{'save'} ],
418                     [ 'raw', $text{'uedit_raw'} ]);
419
420         if (&foreign_available("mailboxes") &&
421             &foreign_installed("mailboxes", 1)) {
422                 # Link to the mailboxes module, if installed
423                 push(@buts, [ 'mailboxes', $text{'uedit_mail'} ]);
424                 }
425
426         if (&foreign_available("usermin") &&
427             &foreign_installed("usermin", 1) &&
428             (%uacl = &get_module_acl("usermin") &&
429             $uacl{'sessions'})) {
430                 # Link to Usermin module for switching to some user
431                 &foreign_require("usermin", "usermin-lib.pl");
432                 local %uminiserv;
433                 &usermin::get_usermin_miniserv_config(\%uminiserv);
434                 if ($uminiserv{'session'}) {
435                         push(@buts, [ "switch", $text{'uedit_swit'} ]);
436                         }
437                 }
438
439         push(@buts, [ 'delete', $text{'delete'} ]);
440         }
441 print &ui_form_end(\@buts);
442
443 &ui_print_footer("", $text{'index_return'});
444