samba-tool: really deprecate 'samba-tool user add'
[nivanova/samba-autobuild/.git] / python / samba / netcmd / user.py
1 # user management
2 #
3 # Copyright Jelmer Vernooij 2010 <jelmer@samba.org>
4 # Copyright Theresa Halloran 2011 <theresahalloran@gmail.com>
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 #
19
20 import samba.getopt as options
21 import ldb
22 import pwd
23 from getpass import getpass
24 from samba.auth import system_session
25 from samba.samdb import SamDB
26 from samba import (
27     dsdb,
28     gensec,
29     generate_random_password,
30     )
31 from samba.net import Net
32
33 from samba.netcmd import (
34     Command,
35     CommandError,
36     SuperCommand,
37     Option,
38     )
39
40
41 class cmd_user_create(Command):
42     """Create a new user.
43
44 This command creates a new user account in the Active Directory domain.  The username specified on the command is the sAMaccountName.
45
46 User accounts may represent physical entities, such as people or may be used as service accounts for applications.  User accounts are also referred to as security principals and are assigned a security identifier (SID).
47
48 A user account enables a user to logon to a computer and domain with an identity that can be authenticated.  To maximize security, each user should have their own unique user account and password.  A user's access to domain resources is based on permissions assigned to the user account.
49
50 Unix (RFC2307) attributes may be added to the user account. Attributes taken from NSS are obtained on the local machine. Explicitly given values override values obtained from NSS. Configure 'idmap_ldb:use rfc2307 = Yes' to use these attributes for UID/GID mapping.
51
52 The command may be run from the root userid or another authorized userid.  The -H or --URL= option can be used to execute the command against a remote server.
53
54 Example1:
55 samba-tool user create User1 passw0rd --given-name=John --surname=Smith --must-change-at-next-login -H ldap://samba.samdom.example.com -Uadministrator%passw1rd
56
57 Example1 shows how to create a new user in the domain against a remote LDAP server.  The -H parameter is used to specify the remote target server.  The -U option is used to pass the userid and password authorized to issue the command remotely.
58
59 Example2:
60 sudo samba-tool user create User2 passw2rd --given-name=Jane --surname=Doe --must-change-at-next-login
61
62 Example2 shows how to create a new user in the domain against the local server.   sudo is used so a user may run the command as root.  In this example, after User2 is created, he/she will be forced to change their password when they logon.
63
64 Example3:
65 samba-tool user create User3 passw3rd --userou='OU=OrgUnit'
66
67 Example3 shows how to create a new user in the OrgUnit organizational unit.
68
69 Example4:
70 samba-tool user create User4 passw4rd --rfc2307-from-nss --gecos 'some text'
71
72 Example4 shows how to create a new user with Unix UID, GID and login-shell set from the local NSS and GECOS set to 'some text'.
73
74 Example5:
75 samba-tool user create User5 passw5rd --nis-domain=samdom --unix-home=/home/User5 \
76            --uid-number=10005 --login-shell=/bin/false --gid-number=10000
77
78 Example5 shows how to create an RFC2307/NIS domain enabled user account. If
79 --nis-domain is set, then the other four parameters are mandatory.
80
81 """
82     synopsis = "%prog <username> [<password>] [options]"
83
84     takes_options = [
85         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
86                 metavar="URL", dest="H"),
87         Option("--must-change-at-next-login",
88                 help="Force password to be changed on next login",
89                 action="store_true"),
90         Option("--random-password",
91                 help="Generate random password",
92                 action="store_true"),
93         Option("--use-username-as-cn",
94                 help="Force use of username as user's CN",
95                 action="store_true"),
96         Option("--userou",
97                 help="DN of alternative location (without domainDN counterpart) to default CN=Users in which new user object will be created. E. g. 'OU=<OU name>'",
98                 type=str),
99         Option("--surname", help="User's surname", type=str),
100         Option("--given-name", help="User's given name", type=str),
101         Option("--initials", help="User's initials", type=str),
102         Option("--profile-path", help="User's profile path", type=str),
103         Option("--script-path", help="User's logon script path", type=str),
104         Option("--home-drive", help="User's home drive letter", type=str),
105         Option("--home-directory", help="User's home directory path", type=str),
106         Option("--job-title", help="User's job title", type=str),
107         Option("--department", help="User's department", type=str),
108         Option("--company", help="User's company", type=str),
109         Option("--description", help="User's description", type=str),
110         Option("--mail-address", help="User's email address", type=str),
111         Option("--internet-address", help="User's home page", type=str),
112         Option("--telephone-number", help="User's phone number", type=str),
113         Option("--physical-delivery-office", help="User's office location", type=str),
114         Option("--rfc2307-from-nss",
115                 help="Copy Unix user attributes from NSS (will be overridden by explicit UID/GID/GECOS/shell)",
116                 action="store_true"),
117         Option("--nis-domain", help="User's Unix/RFC2307 NIS domain", type=str),
118         Option("--unix-home", help="User's Unix/RFC2307 home directory",
119                 type=str),
120         Option("--uid", help="User's Unix/RFC2307 username", type=str),
121         Option("--uid-number", help="User's Unix/RFC2307 numeric UID", type=int),
122         Option("--gid-number", help="User's Unix/RFC2307 primary GID number", type=int),
123         Option("--gecos", help="User's Unix/RFC2307 GECOS field", type=str),
124         Option("--login-shell", help="User's Unix/RFC2307 login shell", type=str),
125     ]
126
127     takes_args = ["username", "password?"]
128
129     takes_optiongroups = {
130         "sambaopts": options.SambaOptions,
131         "credopts": options.CredentialsOptions,
132         "versionopts": options.VersionOptions,
133         }
134
135     def run(self, username, password=None, credopts=None, sambaopts=None,
136             versionopts=None, H=None, must_change_at_next_login=False,
137             random_password=False, use_username_as_cn=False, userou=None,
138             surname=None, given_name=None, initials=None, profile_path=None,
139             script_path=None, home_drive=None, home_directory=None,
140             job_title=None, department=None, company=None, description=None,
141             mail_address=None, internet_address=None, telephone_number=None,
142             physical_delivery_office=None, rfc2307_from_nss=False,
143             nis_domain=None, unix_home=None, uid=None, uid_number=None,
144             gid_number=None, gecos=None, login_shell=None):
145
146         if random_password:
147             password = generate_random_password(128, 255)
148
149         while True:
150             if password is not None and password is not '':
151                 break
152             password = getpass("New Password: ")
153             passwordverify = getpass("Retype Password: ")
154             if not password == passwordverify:
155                 password = None
156                 self.outf.write("Sorry, passwords do not match.\n")
157
158         if rfc2307_from_nss:
159                 pwent = pwd.getpwnam(username)
160                 if uid is None:
161                     uid = username
162                 if uid_number is None:
163                     uid_number = pwent[2]
164                 if gid_number is None:
165                     gid_number = pwent[3]
166                 if gecos is None:
167                     gecos = pwent[4]
168                 if login_shell is None:
169                     login_shell = pwent[6]
170
171         lp = sambaopts.get_loadparm()
172         creds = credopts.get_credentials(lp)
173
174         if uid_number or gid_number:
175             if not lp.get("idmap_ldb:use rfc2307"):
176                 self.outf.write("You are setting a Unix/RFC2307 UID or GID. You may want to set 'idmap_ldb:use rfc2307 = Yes' to use those attributes for XID/SID-mapping.\n")
177
178         if nis_domain is not None:
179             if None in (uid_number, login_shell, unix_home, gid_number):
180                 raise CommandError('Missing parameters. To enable NIS features, '
181                                    'the following options have to be given: '
182                                    '--nis-domain=, --uidNumber=, --login-shell='
183                                    ', --unix-home=, --gid-number= Operation '
184                                    'cancelled.')
185
186         try:
187             samdb = SamDB(url=H, session_info=system_session(),
188                           credentials=creds, lp=lp)
189             samdb.newuser(username, password, force_password_change_at_next_login_req=must_change_at_next_login,
190                           useusernameascn=use_username_as_cn, userou=userou, surname=surname, givenname=given_name, initials=initials,
191                           profilepath=profile_path, homedrive=home_drive, scriptpath=script_path, homedirectory=home_directory,
192                           jobtitle=job_title, department=department, company=company, description=description,
193                           mailaddress=mail_address, internetaddress=internet_address,
194                           telephonenumber=telephone_number, physicaldeliveryoffice=physical_delivery_office,
195                           nisdomain=nis_domain, unixhome=unix_home, uid=uid,
196                           uidnumber=uid_number, gidnumber=gid_number,
197                           gecos=gecos, loginshell=login_shell)
198         except Exception, e:
199             raise CommandError("Failed to add user '%s': " % username, e)
200
201         self.outf.write("User '%s' created successfully\n" % username)
202
203
204 class cmd_user_add(cmd_user_create):
205     __doc__ = cmd_user_create.__doc__
206     # take this print out after the add subcommand is removed.
207     # the add subcommand is deprecated but left in for now to allow people to
208     # migrate to create
209
210     def run(self, *args, **kwargs):
211         self.outf.write(
212             "Note: samba-tool user add is deprecated.  "
213             "Please use samba-tool user create for the same function.\n")
214         return super(cmd_user_add, self).run(*args, **kwargs)
215
216
217 class cmd_user_delete(Command):
218     """Delete a user.
219
220 This command deletes a user account from the Active Directory domain.  The username specified on the command is the sAMAccountName.
221
222 Once the account is deleted, all permissions and memberships associated with that account are deleted.  If a new user account is added with the same name as a previously deleted account name, the new user does not have the previous permissions.  The new account user will be assigned a new security identifier (SID) and permissions and memberships will have to be added.
223
224 The command may be run from the root userid or another authorized userid.  The -H or --URL= option can be used to execute the command against a remote server.
225
226 Example1:
227 samba-tool user delete User1 -H ldap://samba.samdom.example.com --username=administrator --password=passw1rd
228
229 Example1 shows how to delete a user in the domain against a remote LDAP server.  The -H parameter is used to specify the remote target server.  The --username= and --password= options are used to pass the username and password of a user that exists on the remote server and is authorized to issue the command on that server.
230
231 Example2:
232 sudo samba-tool user delete User2
233
234 Example2 shows how to delete a user in the domain against the local server.   sudo is used so a user may run the command as root.
235
236 """
237     synopsis = "%prog <username> [options]"
238
239     takes_options = [
240         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
241                metavar="URL", dest="H"),
242     ]
243
244     takes_args = ["username"]
245     takes_optiongroups = {
246         "sambaopts": options.SambaOptions,
247         "credopts": options.CredentialsOptions,
248         "versionopts": options.VersionOptions,
249         }
250
251     def run(self, username, credopts=None, sambaopts=None, versionopts=None,
252             H=None):
253         lp = sambaopts.get_loadparm()
254         creds = credopts.get_credentials(lp, fallback_machine=True)
255
256         try:
257             samdb = SamDB(url=H, session_info=system_session(),
258                           credentials=creds, lp=lp)
259             samdb.deleteuser(username)
260         except Exception, e:
261             raise CommandError('Failed to remove user "%s"' % username, e)
262         self.outf.write("Deleted user %s\n" % username)
263
264
265 class cmd_user_list(Command):
266     """List all users."""
267
268     synopsis = "%prog [options]"
269
270     takes_options = [
271         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
272                metavar="URL", dest="H"),
273         ]
274
275     takes_optiongroups = {
276         "sambaopts": options.SambaOptions,
277         "credopts": options.CredentialsOptions,
278         "versionopts": options.VersionOptions,
279         }
280
281     def run(self, sambaopts=None, credopts=None, versionopts=None, H=None):
282         lp = sambaopts.get_loadparm()
283         creds = credopts.get_credentials(lp, fallback_machine=True)
284
285         samdb = SamDB(url=H, session_info=system_session(),
286             credentials=creds, lp=lp)
287
288         domain_dn = samdb.domain_dn()
289         res = samdb.search(domain_dn, scope=ldb.SCOPE_SUBTREE,
290                     expression=("(&(objectClass=user)(userAccountControl:%s:=%u))"
291                     % (ldb.OID_COMPARATOR_AND, dsdb.UF_NORMAL_ACCOUNT)),
292                     attrs=["samaccountname"])
293         if (len(res) == 0):
294             return
295
296         for msg in res:
297             self.outf.write("%s\n" % msg.get("samaccountname", idx=0))
298
299
300 class cmd_user_enable(Command):
301     """Enable an user.
302
303 This command enables a user account for logon to an Active Directory domain.  The username specified on the command is the sAMAccountName.  The username may also be specified using the --filter option.
304
305 There are many reasons why an account may become disabled.  These include:
306 - If a user exceeds the account policy for logon attempts
307 - If an administrator disables the account
308 - If the account expires
309
310 The samba-tool user enable command allows an administrator to enable an account which has become disabled.
311
312 Additionally, the enable function allows an administrator to have a set of created user accounts defined and setup with default permissions that can be easily enabled for use.
313
314 The command may be run from the root userid or another authorized userid.  The -H or --URL= option can be used to execute the command against a remote server.
315
316 Example1:
317 samba-tool user enable Testuser1 --URL=ldap://samba.samdom.example.com --username=administrator --password=passw1rd
318
319 Example1 shows how to enable a user in the domain against a remote LDAP server.  The --URL parameter is used to specify the remote target server.  The --username= and --password= options are used to pass the username and password of a user that exists on the remote server and is authorized to update that server.
320
321 Example2:
322 su samba-tool user enable Testuser2
323
324 Example2 shows how to enable user Testuser2 for use in the domain on the local server. sudo is used so a user may run the command as root.
325
326 Example3:
327 samba-tool user enable --filter=samaccountname=Testuser3
328
329 Example3 shows how to enable a user in the domain against a local LDAP server.  It uses the --filter=samaccountname to specify the username.
330
331 """
332     synopsis = "%prog (<username>|--filter <filter>) [options]"
333
334
335     takes_optiongroups = {
336         "sambaopts": options.SambaOptions,
337         "versionopts": options.VersionOptions,
338         "credopts": options.CredentialsOptions,
339     }
340
341     takes_options = [
342         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
343                metavar="URL", dest="H"),
344         Option("--filter", help="LDAP Filter to set password on", type=str),
345         ]
346
347     takes_args = ["username?"]
348
349     def run(self, username=None, sambaopts=None, credopts=None,
350             versionopts=None, filter=None, H=None):
351         if username is None and filter is None:
352             raise CommandError("Either the username or '--filter' must be specified!")
353
354         if filter is None:
355             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
356
357         lp = sambaopts.get_loadparm()
358         creds = credopts.get_credentials(lp, fallback_machine=True)
359
360         samdb = SamDB(url=H, session_info=system_session(),
361             credentials=creds, lp=lp)
362         try:
363             samdb.enable_account(filter)
364         except Exception, msg:
365             raise CommandError("Failed to enable user '%s': %s" % (username or filter, msg))
366         self.outf.write("Enabled user '%s'\n" % (username or filter))
367
368
369 class cmd_user_disable(Command):
370     """Disable an user."""
371
372     synopsis = "%prog (<username>|--filter <filter>) [options]"
373
374     takes_options = [
375         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
376                metavar="URL", dest="H"),
377         Option("--filter", help="LDAP Filter to set password on", type=str),
378         ]
379
380     takes_args = ["username?"]
381
382     takes_optiongroups = {
383         "sambaopts": options.SambaOptions,
384         "credopts": options.CredentialsOptions,
385         "versionopts": options.VersionOptions,
386         }
387
388     def run(self, username=None, sambaopts=None, credopts=None,
389             versionopts=None, filter=None, H=None):
390         if username is None and filter is None:
391             raise CommandError("Either the username or '--filter' must be specified!")
392
393         if filter is None:
394             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
395
396         lp = sambaopts.get_loadparm()
397         creds = credopts.get_credentials(lp, fallback_machine=True)
398
399         samdb = SamDB(url=H, session_info=system_session(),
400             credentials=creds, lp=lp)
401         try:
402             samdb.disable_account(filter)
403         except Exception, msg:
404             raise CommandError("Failed to disable user '%s': %s" % (username or filter, msg))
405
406
407 class cmd_user_setexpiry(Command):
408     """Set the expiration of a user account.
409
410 The user can either be specified by their sAMAccountName or using the --filter option.
411
412 When a user account expires, it becomes disabled and the user is unable to logon.  The administrator may issue the samba-tool user enable command to enable the account for logon.  The permissions and memberships associated with the account are retained when the account is enabled.
413
414 The command may be run from the root userid or another authorized userid.  The -H or --URL= option can be used to execute the command on a remote server.
415
416 Example1:
417 samba-tool user setexpiry User1 --days=20 --URL=ldap://samba.samdom.example.com --username=administrator --password=passw1rd
418
419 Example1 shows how to set the expiration of an account in a remote LDAP server.  The --URL parameter is used to specify the remote target server.  The --username= and --password= options are used to pass the username and password of a user that exists on the remote server and is authorized to update that server.
420
421 Example2:
422 su samba-tool user setexpiry User2
423
424 Example2 shows how to set the account expiration of user User2 so it will never expire.  The user in this example resides on the  local server.   sudo is used so a user may run the command as root.
425
426 Example3:
427 samba-tool user setexpiry --days=20 --filter=samaccountname=User3
428
429 Example3 shows how to set the account expiration date to end of day 20 days from the current day.  The username or sAMAccountName is specified using the --filter= parameter and the username in this example is User3.
430
431 Example4:
432 samba-tool user setexpiry --noexpiry User4
433 Example4 shows how to set the account expiration so that it will never expire.  The username and sAMAccountName in this example is User4.
434
435 """
436     synopsis = "%prog (<username>|--filter <filter>) [options]"
437
438     takes_optiongroups = {
439         "sambaopts": options.SambaOptions,
440         "versionopts": options.VersionOptions,
441         "credopts": options.CredentialsOptions,
442     }
443
444     takes_options = [
445         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
446                metavar="URL", dest="H"),
447         Option("--filter", help="LDAP Filter to set password on", type=str),
448         Option("--days", help="Days to expiry", type=int, default=0),
449         Option("--noexpiry", help="Password does never expire", action="store_true", default=False),
450     ]
451
452     takes_args = ["username?"]
453
454     def run(self, username=None, sambaopts=None, credopts=None,
455             versionopts=None, H=None, filter=None, days=None, noexpiry=None):
456         if username is None and filter is None:
457             raise CommandError("Either the username or '--filter' must be specified!")
458
459         if filter is None:
460             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
461
462         lp = sambaopts.get_loadparm()
463         creds = credopts.get_credentials(lp)
464
465         samdb = SamDB(url=H, session_info=system_session(),
466             credentials=creds, lp=lp)
467
468         try:
469             samdb.setexpiry(filter, days*24*3600, no_expiry_req=noexpiry)
470         except Exception, msg:
471             # FIXME: Catch more specific exception
472             raise CommandError("Failed to set expiry for user '%s': %s" % (
473                 username or filter, msg))
474         if noexpiry:
475             self.outf.write("Expiry for user '%s' disabled.\n" % (
476                 username or filter))
477         else:
478             self.outf.write("Expiry for user '%s' set to %u days.\n" % (
479                 username or filter, days))
480
481
482 class cmd_user_password(Command):
483     """Change password for a user account (the one provided in authentication).
484 """
485
486     synopsis = "%prog [options]"
487
488     takes_options = [
489         Option("--newpassword", help="New password", type=str),
490         ]
491
492     takes_optiongroups = {
493         "sambaopts": options.SambaOptions,
494         "credopts": options.CredentialsOptions,
495         "versionopts": options.VersionOptions,
496         }
497
498     def run(self, credopts=None, sambaopts=None, versionopts=None,
499                 newpassword=None):
500
501         lp = sambaopts.get_loadparm()
502         creds = credopts.get_credentials(lp)
503
504         # get old password now, to get the password prompts in the right order
505         old_password = creds.get_password()
506
507         net = Net(creds, lp, server=credopts.ipaddress)
508
509         password = newpassword
510         while True:
511             if password is not None and password is not '':
512                 break
513             password = getpass("New Password: ")
514             passwordverify = getpass("Retype Password: ")
515             if not password == passwordverify:
516                 password = None
517                 self.outf.write("Sorry, passwords do not match.\n")
518
519         try:
520             net.change_password(password)
521         except Exception, msg:
522             # FIXME: catch more specific exception
523             raise CommandError("Failed to change password : %s" % msg)
524         self.outf.write("Changed password OK\n")
525
526
527 class cmd_user_setpassword(Command):
528     """Set or reset the password of a user account.
529
530 This command sets or resets the logon password for a user account.  The username specified on the command is the sAMAccountName.  The username may also be specified using the --filter option.
531
532 If the password is not specified on the command through the --newpassword parameter, the user is prompted for the password to be entered through the command line.
533
534 It is good security practice for the administrator to use the --must-change-at-next-login option which requires that when the user logs on to the account for the first time following the password change, he/she must change the password.
535
536 The command may be run from the root userid or another authorized userid.  The -H or --URL= option can be used to execute the command against a remote server.
537
538 Example1:
539 samba-tool user setpassword TestUser1 --newpassword=passw0rd --URL=ldap://samba.samdom.example.com -Uadministrator%passw1rd
540
541 Example1 shows how to set the password of user TestUser1 on a remote LDAP server.  The --URL parameter is used to specify the remote target server.  The -U option is used to pass the username and password of a user that exists on the remote server and is authorized to update the server.
542
543 Example2:
544 sudo samba-tool user setpassword TestUser2 --newpassword=passw0rd --must-change-at-next-login
545
546 Example2 shows how an administrator would reset the TestUser2 user's password to passw0rd.  The user is running under the root userid using the sudo command.  In this example the user TestUser2 must change their password the next time they logon to the account.
547
548 Example3:
549 samba-tool user setpassword --filter=samaccountname=TestUser3 --newpassword=passw0rd
550
551 Example3 shows how an administrator would reset TestUser3 user's password to passw0rd using the --filter= option to specify the username.
552
553 """
554     synopsis = "%prog (<username>|--filter <filter>) [options]"
555
556     takes_optiongroups = {
557         "sambaopts": options.SambaOptions,
558         "versionopts": options.VersionOptions,
559         "credopts": options.CredentialsOptions,
560     }
561
562     takes_options = [
563         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
564                metavar="URL", dest="H"),
565         Option("--filter", help="LDAP Filter to set password on", type=str),
566         Option("--newpassword", help="Set password", type=str),
567         Option("--must-change-at-next-login",
568                help="Force password to be changed on next login",
569                action="store_true"),
570         Option("--random-password",
571                 help="Generate random password",
572                 action="store_true"),
573         ]
574
575     takes_args = ["username?"]
576
577     def run(self, username=None, filter=None, credopts=None, sambaopts=None,
578             versionopts=None, H=None, newpassword=None,
579             must_change_at_next_login=False, random_password=False):
580         if filter is None and username is None:
581             raise CommandError("Either the username or '--filter' must be specified!")
582
583         if random_password:
584             password = generate_random_password(128, 255)
585         else:
586             password = newpassword
587
588         while 1:
589             if password is not None and password is not '':
590                 break
591             password = getpass("New Password: ")
592
593         if filter is None:
594             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
595
596         lp = sambaopts.get_loadparm()
597         creds = credopts.get_credentials(lp)
598
599         creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
600
601         samdb = SamDB(url=H, session_info=system_session(),
602                       credentials=creds, lp=lp)
603
604         try:
605             samdb.setpassword(filter, password,
606                               force_change_at_next_login=must_change_at_next_login,
607                               username=username)
608         except Exception, msg:
609             # FIXME: catch more specific exception
610             raise CommandError("Failed to set password for user '%s': %s" % (username or filter, msg))
611         self.outf.write("Changed password OK\n")
612
613
614 class cmd_user(SuperCommand):
615     """User management."""
616
617     subcommands = {}
618     subcommands["add"] = cmd_user_add()
619     subcommands["create"] = cmd_user_create()
620     subcommands["delete"] = cmd_user_delete()
621     subcommands["disable"] = cmd_user_disable()
622     subcommands["enable"] = cmd_user_enable()
623     subcommands["list"] = cmd_user_list()
624     subcommands["setexpiry"] = cmd_user_setexpiry()
625     subcommands["password"] = cmd_user_password()
626     subcommands["setpassword"] = cmd_user_setpassword()