619fe305cfee99aed97ac0de9ed0924f49e1fe33
[kai/samba.git] / source4 / scripting / 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 from getpass import getpass
23 from samba.auth import system_session
24 from samba.samdb import SamDB
25 from samba import (
26     dsdb,
27     gensec,
28     generate_random_password,
29     )
30 from samba.net import Net
31
32 from samba.netcmd import (
33     Command,
34     CommandError,
35     SuperCommand,
36     Option,
37     )
38
39
40 class cmd_user_create(Command):
41     """Create a new user.
42
43 This command creates a new user account in the Active Directory domain.  The username specified on the command is the sAMaccountName.
44
45 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).
46
47 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.
48
49 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.
50
51 Example1:
52 samba-tool user add User1 passw0rd --given-name=John --surname=Smith --must-change-at-next-login -H ldap://samba.samdom.example.com -Uadministrator%passw1rd
53
54 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.
55
56 Example2:
57 sudo samba-tool user add User2 passw2rd --given-name=Jane --surname=Doe --must-change-at-next-login
58
59 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.
60
61 Example3:
62 samba-tool user add User3 passw3rd --userou=OrgUnit
63
64 Example3 shows how to create a new user in the OrgUnit organizational unit.
65
66 """
67     synopsis = "%prog <username> [<password>] [options]"
68
69     takes_options = [
70         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
71                 metavar="URL", dest="H"),
72         Option("--must-change-at-next-login",
73                 help="Force password to be changed on next login",
74                 action="store_true"),
75         Option("--random-password",
76                 help="Generate random password",
77                 action="store_true"),
78         Option("--use-username-as-cn",
79                 help="Force use of username as user's CN",
80                 action="store_true"),
81         Option("--userou",
82                 help="Alternative location (without domainDN counterpart) to default CN=Users in which new user object will be created",
83                 type=str),
84         Option("--surname", help="User's surname", type=str),
85         Option("--given-name", help="User's given name", type=str),
86         Option("--initials", help="User's initials", type=str),
87         Option("--profile-path", help="User's profile path", type=str),
88         Option("--script-path", help="User's logon script path", type=str),
89         Option("--home-drive", help="User's home drive letter", type=str),
90         Option("--home-directory", help="User's home directory path", type=str),
91         Option("--job-title", help="User's job title", type=str),
92         Option("--department", help="User's department", type=str),
93         Option("--company", help="User's company", type=str),
94         Option("--description", help="User's description", type=str),
95         Option("--mail-address", help="User's email address", type=str),
96         Option("--internet-address", help="User's home page", type=str),
97         Option("--telephone-number", help="User's phone number", type=str),
98         Option("--physical-delivery-office", help="User's office location", type=str),
99     ]
100
101     takes_args = ["username", "password?"]
102
103     takes_optiongroups = {
104         "sambaopts": options.SambaOptions,
105         "credopts": options.CredentialsOptions,
106         "versionopts": options.VersionOptions,
107         }
108
109     def run(self, username, password=None, credopts=None, sambaopts=None,
110             versionopts=None, H=None, must_change_at_next_login=False,
111             random_password=False, use_username_as_cn=False, userou=None,
112             surname=None, given_name=None, initials=None, profile_path=None,
113             script_path=None, home_drive=None, home_directory=None,
114             job_title=None, department=None, company=None, description=None,
115             mail_address=None, internet_address=None, telephone_number=None,
116             physical_delivery_office=None):
117
118         if random_password:
119             password = generate_random_password(128, 255)
120
121         while True:
122             if password is not None and password is not '':
123                 break
124             password = getpass("New Password: ")
125             passwordverify = getpass("Retype Password: ")
126             if not password == passwordverify:
127                 password = None
128                 self.outf.write("Sorry, passwords do not match.\n")
129
130         lp = sambaopts.get_loadparm()
131         creds = credopts.get_credentials(lp)
132
133         try:
134             samdb = SamDB(url=H, session_info=system_session(),
135                           credentials=creds, lp=lp)
136             samdb.newuser(username, password, force_password_change_at_next_login_req=must_change_at_next_login,
137                           useusernameascn=use_username_as_cn, userou=userou, surname=surname, givenname=given_name, initials=initials,
138                           profilepath=profile_path, homedrive=home_drive, scriptpath=script_path, homedirectory=home_directory,
139                           jobtitle=job_title, department=department, company=company, description=description,
140                           mailaddress=mail_address, internetaddress=internet_address,
141                           telephonenumber=telephone_number, physicaldeliveryoffice=physical_delivery_office)
142         except Exception, e:
143             raise CommandError("Failed to add user '%s': " % username, e)
144
145         self.outf.write("User '%s' created successfully\n" % username)
146
147
148 class cmd_user_add(cmd_user_create):
149     __doc__ = cmd_user_create.__doc__
150     # take this print out after the add subcommand is removed.
151     # the add subcommand is deprecated but left in for now to allow people to
152     # migrate to create
153
154     def run(self, *args, **kwargs):
155         self.err.write(
156             "Note: samba-tool user add is deprecated.  "
157             "Please use samba-tool user create for the same function.\n")
158         return super(self, cmd_user_add).run(*args, **kwargs)
159
160
161 class cmd_user_delete(Command):
162     """Delete a user.
163
164 This command deletes a user account from the Active Directory domain.  The username specified on the command is the sAMAccountName.
165
166 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.
167
168 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.
169
170 Example1:
171 samba-tool user delete User1 -H ldap://samba.samdom.example.com --username=administrator --password=passw1rd
172
173 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.
174
175 Example2:
176 sudo samba-tool user delete User2
177
178 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.
179
180 """
181     synopsis = "%prog <username> [options]"
182
183     takes_options = [
184         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
185                metavar="URL", dest="H"),
186     ]
187
188     takes_args = ["username"]
189     takes_optiongroups = {
190         "sambaopts": options.SambaOptions,
191         "credopts": options.CredentialsOptions,
192         "versionopts": options.VersionOptions,
193         }
194
195     def run(self, username, credopts=None, sambaopts=None, versionopts=None,
196             H=None):
197         lp = sambaopts.get_loadparm()
198         creds = credopts.get_credentials(lp, fallback_machine=True)
199
200         try:
201             samdb = SamDB(url=H, session_info=system_session(),
202                           credentials=creds, lp=lp)
203             samdb.deleteuser(username)
204         except Exception, e:
205             raise CommandError('Failed to remove user "%s"' % username, e)
206         self.outf.write("Deleted user %s\n" % username)
207
208
209 class cmd_user_list(Command):
210     """List all users."""
211
212     synopsis = "%prog [options]"
213
214     takes_options = [
215         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
216                metavar="URL", dest="H"),
217         ]
218
219     takes_optiongroups = {
220         "sambaopts": options.SambaOptions,
221         "credopts": options.CredentialsOptions,
222         "versionopts": options.VersionOptions,
223         }
224
225     def run(self, sambaopts=None, credopts=None, versionopts=None, H=None):
226         lp = sambaopts.get_loadparm()
227         creds = credopts.get_credentials(lp, fallback_machine=True)
228
229         samdb = SamDB(url=H, session_info=system_session(),
230             credentials=creds, lp=lp)
231
232         domain_dn = samdb.domain_dn()
233         res = samdb.search(domain_dn, scope=ldb.SCOPE_SUBTREE,
234                     expression=("(&(objectClass=user)(userAccountControl:%s:=%u))"
235                     % (ldb.OID_COMPARATOR_AND, dsdb.UF_NORMAL_ACCOUNT)),
236                     attrs=["samaccountname"])
237         if (len(res) == 0):
238             return
239
240         for msg in res:
241             self.outf.write("%s\n" % msg.get("samaccountname", idx=0))
242
243
244 class cmd_user_enable(Command):
245     """Enable an user.
246
247 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.
248
249 There are many reasons why an account may become disabled.  These include:
250 - If a user exceeds the account policy for logon attempts
251 - If an administrator disables the account
252 - If the account expires
253
254 The samba-tool user enable command allows an administrator to enable an account which has become disabled.
255
256 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.
257
258 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.
259
260 Example1:
261 samba-tool user enable Testuser1 --URL=ldap://samba.samdom.example.com --username=administrator --password=passw1rd
262
263 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.
264
265 Exampl2:
266 su samba-tool user enable Testuser2
267
268 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.
269
270 Example3:
271 samba-tool user enable --filter=samaccountname=Testuser3
272
273 Example3 shows how to enable a user in the domain against a local LDAP server.  It uses the --filter=samaccountname to specify the username.
274
275 """
276     synopsis = "%prog (<username>|--filter <filter>) [options]"
277
278
279     takes_optiongroups = {
280         "sambaopts": options.SambaOptions,
281         "versionopts": options.VersionOptions,
282         "credopts": options.CredentialsOptions,
283     }
284
285     takes_options = [
286         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
287                metavar="URL", dest="H"),
288         Option("--filter", help="LDAP Filter to set password on", type=str),
289         ]
290
291     takes_args = ["username?"]
292
293     def run(self, username=None, sambaopts=None, credopts=None,
294             versionopts=None, filter=None, H=None):
295         if username is None and filter is None:
296             raise CommandError("Either the username or '--filter' must be specified!")
297
298         if filter is None:
299             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
300
301         lp = sambaopts.get_loadparm()
302         creds = credopts.get_credentials(lp, fallback_machine=True)
303
304         samdb = SamDB(url=H, session_info=system_session(),
305             credentials=creds, lp=lp)
306         try:
307             samdb.enable_account(filter)
308         except Exception, msg:
309             raise CommandError("Failed to enable user '%s': %s" % (username or filter, msg))
310         self.outf.write("Enabled user '%s'\n" % (username or filter))
311
312
313 class cmd_user_disable(Command):
314     """Disable an user."""
315
316     synopsis = "%prog (<username>|--filter <filter>) [options]"
317
318     takes_options = [
319         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
320                metavar="URL", dest="H"),
321         Option("--filter", help="LDAP Filter to set password on", type=str),
322         ]
323
324     takes_args = ["username?"]
325
326     takes_optiongroups = {
327         "sambaopts": options.SambaOptions,
328         "credopts": options.CredentialsOptions,
329         "versionopts": options.VersionOptions,
330         }
331
332     def run(self, username=None, sambaopts=None, credopts=None,
333             versionopts=None, filter=None, H=None):
334         if username is None and filter is None:
335             raise CommandError("Either the username or '--filter' must be specified!")
336
337         if filter is None:
338             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
339
340         lp = sambaopts.get_loadparm()
341         creds = credopts.get_credentials(lp, fallback_machine=True)
342
343         samdb = SamDB(url=H, session_info=system_session(),
344             credentials=creds, lp=lp)
345         try:
346             samdb.disable_account(filter)
347         except Exception, msg:
348             raise CommandError("Failed to disable user '%s': %s" % (username or filter, msg))
349
350
351 class cmd_user_setexpiry(Command):
352     """Set the expiration of a user account.
353
354 This command sets the expiration of a user account.  The username specified on the command is the sAMAccountName.  The username may also be specified using the --filter option.
355
356 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.
357
358 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.
359
360 Example1:
361 samba-tool user setexpiry User1 --days=20 --URL=ldap://samba.samdom.example.com --username=administrator --password=passw1rd
362
363 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.
364
365 Exampl2:
366 su samba-tool user setexpiry User2
367
368 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.
369
370 Example3:
371 samba-tool user setexpiry --days=20 --filter=samaccountname=User3
372
373 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= paramter and the username in this example is User3.
374
375 Example4:
376 samba-tool user setexpiry --noexpiry User4
377 Example4 shows how to set the account expiration so that it will never expire.  The username and sAMAccountName in this example is User4.
378
379 """
380     synopsis = "%prog (<username>|--filter <filter>) [options]"
381
382     takes_optiongroups = {
383         "sambaopts": options.SambaOptions,
384         "versionopts": options.VersionOptions,
385         "credopts": options.CredentialsOptions,
386     }
387
388     takes_options = [
389         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
390                metavar="URL", dest="H"),
391         Option("--filter", help="LDAP Filter to set password on", type=str),
392         Option("--days", help="Days to expiry", type=int, default=0),
393         Option("--noexpiry", help="Password does never expire", action="store_true", default=False),
394     ]
395
396     takes_args = ["username?"]
397
398     def run(self, username=None, sambaopts=None, credopts=None,
399             versionopts=None, H=None, filter=None, days=None, noexpiry=None):
400         if username is None and filter is None:
401             raise CommandError("Either the username or '--filter' must be specified!")
402
403         if filter is None:
404             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
405
406         lp = sambaopts.get_loadparm()
407         creds = credopts.get_credentials(lp)
408
409         samdb = SamDB(url=H, session_info=system_session(),
410             credentials=creds, lp=lp)
411
412         try:
413             samdb.setexpiry(filter, days*24*3600, no_expiry_req=noexpiry)
414         except Exception, msg:
415             # FIXME: Catch more specific exception
416             raise CommandError("Failed to set expiry for user '%s': %s" % (
417                 username or filter, msg))
418         self.outf.write("Set expiry for user '%s' to %u days\n" % (
419             username or filter, days))
420
421
422 class cmd_user_password(Command):
423     """Change password for a user account (the one provided in authentication).
424 """
425
426     synopsis = "%prog [options]"
427
428     takes_options = [
429         Option("--newpassword", help="New password", type=str),
430         ]
431
432     takes_optiongroups = {
433         "sambaopts": options.SambaOptions,
434         "credopts": options.CredentialsOptions,
435         "versionopts": options.VersionOptions,
436         }
437
438     def run(self, credopts=None, sambaopts=None, versionopts=None,
439                 newpassword=None):
440
441         lp = sambaopts.get_loadparm()
442         creds = credopts.get_credentials(lp)
443
444         # get old password now, to get the password prompts in the right order
445         old_password = creds.get_password()
446
447         net = Net(creds, lp, server=credopts.ipaddress)
448
449         password = newpassword
450         while True:
451             if password is not None and password is not '':
452                 break
453             password = getpass("New Password: ")
454             passwordverify = getpass("Retype Password: ")
455             if not password == passwordverify:
456                 password = None
457                 self.outf.write("Sorry, passwords do not match.\n")
458
459         try:
460             net.change_password(password)
461         except Exception, msg:
462             # FIXME: catch more specific exception
463             raise CommandError("Failed to change password : %s" % msg)
464         self.outf.write("Changed password OK\n")
465
466
467 class cmd_user_setpassword(Command):
468     """Set or reset the password of a user account.
469
470 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.
471
472 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.
473
474 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.
475
476 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.
477
478 Example1:
479 samba-tool user setpassword TestUser1 passw0rd --URL=ldap://samba.samdom.example.com -Uadministrator%passw1rd
480
481 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.
482
483 Example2:
484 sudo samba-tool user setpassword TestUser2 passw0rd --must-change-at-next-login
485
486 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.
487
488 Example3:
489 samba-tool user setpassword --filter=samaccountname=TestUser3 --password=passw0rd
490
491 Example3 shows how an administrator would reset TestUser3 user's password to passw0rd using the --filter= option to specify the username.
492
493 """
494     synopsis = "%prog (<username>|--filter <filter>) [options]"
495
496     takes_optiongroups = {
497         "sambaopts": options.SambaOptions,
498         "versionopts": options.VersionOptions,
499         "credopts": options.CredentialsOptions,
500     }
501
502     takes_options = [
503         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
504                metavar="URL", dest="H"),
505         Option("--filter", help="LDAP Filter to set password on", type=str),
506         Option("--newpassword", help="Set password", type=str),
507         Option("--must-change-at-next-login",
508                help="Force password to be changed on next login",
509                action="store_true"),
510         Option("--random-password",
511                 help="Generate random password",
512                 action="store_true"),
513         ]
514
515     takes_args = ["username?"]
516
517     def run(self, username=None, filter=None, credopts=None, sambaopts=None,
518             versionopts=None, H=None, newpassword=None,
519             must_change_at_next_login=False, random_password=False):
520         if filter is None and username is None:
521             raise CommandError("Either the username or '--filter' must be specified!")
522
523         if random_password:
524             password = generate_random_password(128, 255)
525         else:
526             password = newpassword
527
528         while 1:
529             if password is not None and password is not '':
530                 break
531             password = getpass("New Password: ")
532
533         if filter is None:
534             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
535
536         lp = sambaopts.get_loadparm()
537         creds = credopts.get_credentials(lp)
538
539         creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
540
541         samdb = SamDB(url=H, session_info=system_session(),
542                       credentials=creds, lp=lp)
543
544         try:
545             samdb.setpassword(filter, password,
546                               force_change_at_next_login=must_change_at_next_login,
547                               username=username)
548         except Exception, msg:
549             # FIXME: catch more specific exception
550             raise CommandError("Failed to set password for user '%s': %s" % (username or filter, msg))
551         self.outf.write("Changed password OK\n")
552
553
554 class cmd_user(SuperCommand):
555     """User management."""
556
557     subcommands = {}
558     subcommands["add"] = cmd_user_create()
559     subcommands["create"] = cmd_user_create()
560     subcommands["delete"] = cmd_user_delete()
561     subcommands["disable"] = cmd_user_disable()
562     subcommands["enable"] = cmd_user_enable()
563     subcommands["list"] = cmd_user_list()
564     subcommands["setexpiry"] = cmd_user_setexpiry()
565     subcommands["password"] = cmd_user_password()
566     subcommands["setpassword"] = cmd_user_setpassword()