1d84a3391caee1f829caf07589f09121c0cebe67
[tridge/samba.git] / source4 / scripting / python / samba / netcmd / user.py
1 #!/usr/bin/env python
2 #
3 # user management
4 #
5 # Copyright Jelmer Vernooij 2010 <jelmer@samba.org>
6 # Copyright Theresa Halloran 2011 <theresahalloran@gmail.com>
7 # Copyright Giampaolo Lauria 2011 <lauria2@yahoo.com>
8 #
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 #
22
23 import samba.getopt as options
24 import sys, ldb
25 from getpass import getpass
26 from samba.auth import system_session
27 from samba.samdb import SamDB
28 from samba import gensec, generate_random_password
29 from samba import dsdb
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     """Creates 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     def run(self, username, password=None, credopts=None, sambaopts=None,
104             versionopts=None, H=None, must_change_at_next_login=False, random_password=False,
105             use_username_as_cn=False, userou=None, surname=None, given_name=None, initials=None,
106             profile_path=None, script_path=None, home_drive=None, home_directory=None,
107             job_title=None, department=None, company=None, description=None,
108             mail_address=None, internet_address=None, telephone_number=None, physical_delivery_office=None):
109
110         if random_password:
111             password = generate_random_password(128, 255)
112
113         while 1:
114             if password is not None and password is not '':
115                 break
116             password = getpass("New Password: ")
117
118         lp = sambaopts.get_loadparm()
119         creds = credopts.get_credentials(lp)
120
121         try:
122             samdb = SamDB(url=H, session_info=system_session(),
123                           credentials=creds, lp=lp)
124             samdb.newuser(username, password,
125                           force_password_change_at_next_login_req=must_change_at_next_login,
126                           useusernameascn=use_username_as_cn, userou=userou, surname=surname, givenname=given_name, initials=initials,
127                           profilepath=profile_path, homedrive=home_drive, scriptpath=script_path, homedirectory=home_directory,
128                           jobtitle=job_title, department=department, company=company, description=description,
129                           mailaddress=mail_address, internetaddress=internet_address,
130                           telephonenumber=telephone_number, physicaldeliveryoffice=physical_delivery_office)
131         except Exception, e:
132             raise CommandError("Failed to add user '%s': " % username, e)
133
134         self.outf.write("User '%s' created successfully\n" % username)
135
136
137 class cmd_user_add(cmd_user_create):
138     __doc__ = cmd_user_create.__doc__
139     # take this print out after the add subcommand is removed.
140     # the add subcommand is deprecated but left in for now to allow people to migrate to create
141
142     def run(self, *args, **kwargs):
143         self.err.write("\nNote: samba-tool user add is deprecated.  Please use samba-tool user create for the same function.\n")
144         return super(self, cmd_user_add).run(*args, **kwargs)
145
146
147 class cmd_user_delete(Command):
148     """Deletes a user
149
150 This command deletes a user account from the Active Directory domain.  The username specified on the command is the sAMAccountName.
151
152 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.
153
154 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.
155
156 Example1:
157 samba-tool user delete User1 -H ldap://samba.samdom.example.com --username=administrator --password=passw1rd
158
159 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.
160
161 Example2:
162 sudo samba-tool user delete User2
163
164 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.
165
166 """
167     synopsis = "%prog <username> [options]"
168
169     takes_options = [
170         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
171                metavar="URL", dest="H"),
172     ]
173
174     takes_args = ["username"]
175
176     def run(self, username, credopts=None, sambaopts=None, versionopts=None, H=None):
177
178         lp = sambaopts.get_loadparm()
179         creds = credopts.get_credentials(lp, fallback_machine=True)
180
181         try:
182             samdb = SamDB(url=H, session_info=system_session(),
183                           credentials=creds, lp=lp)
184             samdb.deleteuser(username)
185         except Exception, e:
186             raise CommandError('Failed to remove user "%s"' % username, e)
187         self.outf.write("Deleted user %s\n" % username)
188
189
190 class cmd_user_list(Command):
191     """List all users"""
192
193     synopsis = "%prog [options]"
194
195     takes_options = [
196         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
197                metavar="URL", dest="H"),
198         ]
199
200     def run(self, sambaopts=None, credopts=None, versionopts=None, H=None):
201         lp = sambaopts.get_loadparm()
202         creds = credopts.get_credentials(lp, fallback_machine=True)
203
204         samdb = SamDB(url=H, session_info=system_session(),
205             credentials=creds, lp=lp)
206
207         domain_dn = samdb.domain_dn()
208         res = samdb.search(domain_dn, scope=ldb.SCOPE_SUBTREE,
209                     expression=("(&(objectClass=user)(userAccountControl:%s:=%u))"
210                     % (ldb.OID_COMPARATOR_AND, dsdb.UF_NORMAL_ACCOUNT)),
211                     attrs=["name"])
212         if (len(res) == 0):
213             return
214
215         try:
216             for msg in res:
217                 self.outf.write("%s\n" % msg["name"][0])
218         except Exception, msg:
219             raise CommandError("Failed to get user list: %s" % msg)
220
221
222 class cmd_user_enable(Command):
223     """Enables a user
224
225 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.
226
227 There are many reasons why an account may become disabled.  These include:
228 - If a user exceeds the account policy for logon attempts
229 - If an administrator disables the account
230 - If the account expires
231
232 The samba-tool user enable command allows an administrator to enable an account which has become disabled.
233
234 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.
235
236 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.
237
238 Example1:
239 samba-tool user enable Testuser1 --URL=ldap://samba.samdom.example.com --username=administrator --password=passw1rd
240
241 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.
242
243 Exampl2:
244 su samba-tool user enable Testuser2
245
246 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.
247
248 Example3:
249 samba-tool user enable --filter=samaccountname=Testuser3
250
251 Example3 shows how to enable a user in the domain against a local LDAP server.  It uses the --filter=samaccountname to specify the username.
252
253 """
254     synopsis = "%prog (<username>|--filter <filter>) [options]"
255
256     takes_options = [
257         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
258                metavar="URL", dest="H"),
259         Option("--filter", help="LDAP Filter to set password on", type=str),
260         ]
261
262     takes_args = ["username?"]
263
264     def run(self, username=None, sambaopts=None, credopts=None,
265             versionopts=None, filter=None, H=None):
266         if username is None and filter is None:
267             raise CommandError("Either the username or '--filter' must be specified!")
268
269         if filter is None:
270             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
271
272         lp = sambaopts.get_loadparm()
273         creds = credopts.get_credentials(lp, fallback_machine=True)
274
275         samdb = SamDB(url=H, session_info=system_session(),
276             credentials=creds, lp=lp)
277         try:
278             samdb.enable_account(filter)
279         except Exception, msg:
280             raise CommandError("Failed to enable user '%s': %s" % (username or filter, msg))
281         self.outf.write("Enabled user '%s'\n" % (username or filter))
282
283
284 class cmd_user_disable(Command):
285     """Disable a user"""
286
287     synopsis = "%prog (<username>|--filter <filter>) [options]"
288
289     takes_options = [
290         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
291                metavar="URL", dest="H"),
292         Option("--filter", help="LDAP Filter to set password on", type=str),
293         ]
294
295     takes_args = ["username?"]
296
297     def run(self, username=None, sambaopts=None, credopts=None,
298             versionopts=None, filter=None, H=None):
299         if username is None and filter is None:
300             raise CommandError("Either the username or '--filter' must be specified!")
301
302         if filter is None:
303             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
304
305         lp = sambaopts.get_loadparm()
306         creds = credopts.get_credentials(lp, fallback_machine=True)
307
308         samdb = SamDB(url=H, session_info=system_session(),
309             credentials=creds, lp=lp)
310         try:
311             samdb.disable_account(filter)
312         except Exception, msg:
313             raise CommandError("Failed to disable user '%s': %s" % (username or filter, msg))
314
315
316 class cmd_user_setexpiry(Command):
317     """Sets the expiration of a user account
318
319 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.
320
321 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.
322
323 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.
324
325 Example1:
326 samba-tool user setexpiry User1 --days=20 --URL=ldap://samba.samdom.example.com --username=administrator --password=passw1rd
327
328 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.
329
330 Exampl2:
331 su samba-tool user setexpiry User2
332
333 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.
334
335 Example3:
336 samba-tool user setexpiry --days=20 --filter=samaccountname=User3
337
338 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.
339
340 Example4:
341 samba-tool user setexpiry --noexpiry User4
342 Example4 shows how to set the account expiration so that it will never expire.  The username and sAMAccountName in this example is User4.
343
344 """
345     synopsis = "%prog (<username>|--filter <filter>) [options]"
346
347     takes_options = [
348         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
349                metavar="URL", dest="H"),
350         Option("--filter", help="LDAP Filter to set password on", type=str),
351         Option("--days", help="Days to expiry", type=int, default=0),
352         Option("--noexpiry", help="Password does never expire", action="store_true", default=False),
353     ]
354
355     takes_args = ["username?"]
356
357     def run(self, username=None, sambaopts=None, credopts=None,
358             versionopts=None, H=None, filter=None, days=None, noexpiry=None):
359         if username is None and filter is None:
360             raise CommandError("Either the username or '--filter' must be specified!")
361
362         if filter is None:
363             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
364
365         lp = sambaopts.get_loadparm()
366         creds = credopts.get_credentials(lp)
367
368         samdb = SamDB(url=H, session_info=system_session(),
369             credentials=creds, lp=lp)
370
371         try:
372             samdb.setexpiry(filter, days*24*3600, no_expiry_req=noexpiry)
373         except Exception, msg:
374             # FIXME: Catch more specific exception
375             raise CommandError("Failed to set expiry for user '%s': %s" % (
376                 username or filter, msg))
377         self.outf.write("Set expiry for user '%s' to %u days\n" % (
378             username or filter, days))
379
380
381 class cmd_user_password(Command):
382     """Change password for a user account (the one provided in authentication)
383
384
385
386 """
387
388     synopsis = "%prog [options]"
389
390     takes_options = [
391         Option("--newpassword", help="New password", type=str),
392         ]
393
394     def run(self, credopts=None, sambaopts=None, versionopts=None,
395                 newpassword=None):
396
397         lp = sambaopts.get_loadparm()
398         creds = credopts.get_credentials(lp)
399
400         # get old password now, to get the password prompts in the right order
401         old_password = creds.get_password()
402
403         net = Net(creds, lp, server=credopts.ipaddress)
404
405         password = newpassword
406         while 1:
407             if password is not None and password is not '':
408                 break
409             password = getpass("New Password: ")
410
411         try:
412             net.change_password(password)
413         except Exception, msg:
414             # FIXME: catch more specific exception
415             raise CommandError("Failed to change password : %s" % msg)
416         self.outf.write("Changed password OK\n")
417
418
419 class cmd_user_setpassword(Command):
420     """Sets or resets the password of a user account
421
422 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.
423
424 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.
425
426 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.
427
428 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.
429
430 Example1:
431 samba-tool user setpassword TestUser1 passw0rd --URL=ldap://samba.samdom.example.com -Uadministrator%passw1rd
432
433 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.
434
435 Example2:
436 sudo samba-tool user setpassword TestUser2 passw0rd --must-change-at-next-login
437
438 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.
439
440 Example3:
441 samba-tool user setpassword --filter=samaccountname=TestUser3 --password=passw0rd
442
443 Example3 shows how an administrator would reset TestUser3 user's password to passw0rd using the --filter= option to specify the username.
444
445 """
446     synopsis = "%prog (<username>|--filter <filter>) [options]"
447
448     takes_options = [
449         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
450                metavar="URL", dest="H"),
451         Option("--filter", help="LDAP Filter to set password on", type=str),
452         Option("--newpassword", help="Set password", type=str),
453         Option("--must-change-at-next-login",
454                help="Force password to be changed on next login",
455                action="store_true"),
456         Option("--random-password",
457                 help="Generate random password",
458                 action="store_true"),
459         ]
460
461     takes_args = ["username?"]
462
463     def run(self, username=None, filter=None, credopts=None, sambaopts=None,
464             versionopts=None, H=None, newpassword=None,
465             must_change_at_next_login=False, random_password=False):
466         if filter is None and username is None:
467             raise CommandError("Either the username or '--filter' must be specified!")
468
469         if random_password:
470             password = generate_random_password(128, 255)
471         else:
472             password = newpassword
473
474         while 1:
475             if password is not None and password is not '':
476                 break
477             password = getpass("New Password: ")
478
479         if filter is None:
480             filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
481
482         lp = sambaopts.get_loadparm()
483         creds = credopts.get_credentials(lp)
484
485         creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
486
487         samdb = SamDB(url=H, session_info=system_session(),
488                       credentials=creds, lp=lp)
489
490         try:
491             samdb.setpassword(filter, password,
492                               force_change_at_next_login=must_change_at_next_login,
493                               username=username)
494         except Exception, msg:
495             # FIXME: catch more specific exception
496             raise CommandError("Failed to set password for user '%s': %s" % (username or filter, msg))
497         self.outf.write("Changed password OK\n")
498
499
500 class cmd_user(SuperCommand):
501     """User management"""
502
503     subcommands = {}
504     subcommands["add"] = cmd_user_create()
505     subcommands["create"] = cmd_user_create()
506     subcommands["delete"] = cmd_user_delete()
507     subcommands["disable"] = cmd_user_disable()
508     subcommands["enable"] = cmd_user_enable()
509     subcommands["list"] = cmd_user_list()
510     subcommands["setexpiry"] = cmd_user_setexpiry()
511     subcommands["password"] = cmd_user_password()
512     subcommands["setpassword"] = cmd_user_setpassword()