bug 12293: stop group.py throwing errors if group is unknown
[Samba.git] / python / samba / netcmd / group.py
blob11f87732defe6acbab50ea636b84395dccd7a898
1 # Copyright Jelmer Vernooij 2008
3 # Based on the original in EJS:
4 # Copyright Andrew Tridgell 2005
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.
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.
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/>.
19 import samba.getopt as options
20 from samba.netcmd import Command, SuperCommand, CommandError, Option
21 import ldb
22 from samba.ndr import ndr_unpack
23 from samba.dcerpc import security
25 from getpass import getpass
26 from samba.auth import system_session
27 from samba.samdb import SamDB
28 from samba.dsdb import (
29 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
30 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP,
31 GTYPE_SECURITY_GLOBAL_GROUP,
32 GTYPE_SECURITY_UNIVERSAL_GROUP,
33 GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP,
34 GTYPE_DISTRIBUTION_GLOBAL_GROUP,
35 GTYPE_DISTRIBUTION_UNIVERSAL_GROUP,
38 security_group = dict({"Builtin": GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
39 "Domain": GTYPE_SECURITY_DOMAIN_LOCAL_GROUP,
40 "Global": GTYPE_SECURITY_GLOBAL_GROUP,
41 "Universal": GTYPE_SECURITY_UNIVERSAL_GROUP})
42 distribution_group = dict({"Domain": GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP,
43 "Global": GTYPE_DISTRIBUTION_GLOBAL_GROUP,
44 "Universal": GTYPE_DISTRIBUTION_UNIVERSAL_GROUP})
47 class cmd_group_add(Command):
48 """Creates a new AD group.
50 This command creates a new Active Directory group. The groupname specified on the command is a unique sAMAccountName.
52 An Active Directory group may contain user and computer accounts as well as other groups. An administrator creates a group and adds members to that group so they can be managed as a single entity. This helps to simplify security and system administration.
54 Groups may also be used to establish email distribution lists, using --group-type=Distribution.
56 Groups are located in domains in organizational units (OUs). The group's scope is a characteristic of the group that designates the extent to which the group is applied within the domain tree or forest.
58 The group location (OU), type (security or distribution) and scope may all be specified on the samba-tool command when the group is created.
60 The command may be run from the root userid or another authorized userid. The
61 -H or --URL= option can be used to execute the command on a remote server.
63 Example1:
64 samba-tool group add Group1 -H ldap://samba.samdom.example.com --description='Simple group'
66 Example1 adds a new group with the name Group1 added to the Users container on a remote LDAP server. The -U parameter is used to pass the userid and password of a user that exists on the remote server and is authorized to issue the command on that server. It defaults to the security type and global scope.
68 Example2:
69 sudo samba-tool group add Group2 --group-type=Distribution
71 Example2 adds a new distribution group to the local server. The command is run under root using the sudo command.
73 Example3:
74 samba-tool group add Group3 --nis-domain=samdom --gid-number=12345
76 Example3 adds a new RFC2307 enabled group for NIS domain samdom and GID 12345 (both options are required to enable this feature).
77 """
79 synopsis = "%prog <groupname> [options]"
81 takes_optiongroups = {
82 "sambaopts": options.SambaOptions,
83 "versionopts": options.VersionOptions,
84 "credopts": options.CredentialsOptions,
87 takes_options = [
88 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
89 metavar="URL", dest="H"),
90 Option("--groupou",
91 help="Alternative location (without domainDN counterpart) to default CN=Users in which new user object will be created",
92 type=str),
93 Option("--group-scope", type="choice", choices=["Domain", "Global", "Universal"],
94 help="Group scope (Domain | Global | Universal)"),
95 Option("--group-type", type="choice", choices=["Security", "Distribution"],
96 help="Group type (Security | Distribution)"),
97 Option("--description", help="Group's description", type=str),
98 Option("--mail-address", help="Group's email address", type=str),
99 Option("--notes", help="Groups's notes", type=str),
100 Option("--gid-number", help="Group's Unix/RFC2307 GID number", type=int),
101 Option("--nis-domain", help="SFU30 NIS Domain", type=str),
104 takes_args = ["groupname"]
106 def run(self, groupname, credopts=None, sambaopts=None,
107 versionopts=None, H=None, groupou=None, group_scope=None,
108 group_type=None, description=None, mail_address=None, notes=None, gid_number=None, nis_domain=None):
110 if (group_type or "Security") == "Security":
111 gtype = security_group.get(group_scope, GTYPE_SECURITY_GLOBAL_GROUP)
112 else:
113 gtype = distribution_group.get(group_scope, GTYPE_DISTRIBUTION_GLOBAL_GROUP)
115 if (gid_number is None and nis_domain is not None) or (gid_number is not None and nis_domain is None):
116 raise CommandError('Both --gid-number and --nis-domain have to be set for a RFC2307-enabled group. Operation cancelled.')
118 lp = sambaopts.get_loadparm()
119 creds = credopts.get_credentials(lp, fallback_machine=True)
121 try:
122 samdb = SamDB(url=H, session_info=system_session(),
123 credentials=creds, lp=lp)
124 samdb.newgroup(groupname, groupou=groupou, grouptype = gtype,
125 description=description, mailaddress=mail_address, notes=notes,
126 gidnumber=gid_number, nisdomain=nis_domain)
127 except Exception, e:
128 # FIXME: catch more specific exception
129 raise CommandError('Failed to create group "%s"' % groupname, e)
130 self.outf.write("Added group %s\n" % groupname)
133 class cmd_group_delete(Command):
134 """Deletes an AD group.
136 The command deletes an existing AD group from the Active Directory domain. The groupname specified on the command is the sAMAccountName.
138 Deleting a group is a permanent operation. When a group is deleted, all permissions and rights that users in the group had inherited from the group account are deleted as well.
140 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.
142 Example1:
143 samba-tool group delete Group1 -H ldap://samba.samdom.example.com -Uadministrator%passw0rd
145 Example1 shows how to delete an AD group from a remote LDAP server. The -U parameter is used to pass the userid and password of a user that exists on the remote server and is authorized to issue the command on that server.
147 Example2:
148 sudo samba-tool group delete Group2
150 Example2 deletes group Group2 from the local server. The command is run under root using the sudo command.
153 synopsis = "%prog <groupname> [options]"
155 takes_optiongroups = {
156 "sambaopts": options.SambaOptions,
157 "versionopts": options.VersionOptions,
158 "credopts": options.CredentialsOptions,
161 takes_options = [
162 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
163 metavar="URL", dest="H"),
166 takes_args = ["groupname"]
168 def run(self, groupname, credopts=None, sambaopts=None, versionopts=None, H=None):
170 lp = sambaopts.get_loadparm()
171 creds = credopts.get_credentials(lp, fallback_machine=True)
172 samdb = SamDB(url=H, session_info=system_session(),
173 credentials=creds, lp=lp)
175 filter = ("(&(sAMAccountName=%s)(objectClass=group))" %
176 groupname)
178 try:
179 res = samdb.search(base=samdb.domain_dn(),
180 scope=ldb.SCOPE_SUBTREE,
181 expression=filter,
182 attrs=["dn"])
183 group_dn = res[0].dn
184 except IndexError:
185 raise CommandError('Unable to find group "%s"' % (groupname))
187 try:
188 samdb.delete(group_dn)
189 except Exception, e:
190 # FIXME: catch more specific exception
191 raise CommandError('Failed to remove group "%s"' % groupname, e)
192 self.outf.write("Deleted group %s\n" % groupname)
195 class cmd_group_add_members(Command):
196 """Add members to an AD group.
198 This command adds one or more members to an existing Active Directory group. The command accepts one or more group member names separated by commas. A group member may be a user or computer account or another Active Directory group.
200 When a member is added to a group the member may inherit permissions and rights from the group. Likewise, when permission or rights of a group are changed, the changes may reflect in the members through inheritance.
202 Example1:
203 samba-tool group addmembers supergroup Group1,Group2,User1 -H ldap://samba.samdom.example.com -Uadministrator%passw0rd
205 Example1 shows how to add two groups, Group1 and Group2 and one user account, User1, to the existing AD group named supergroup. The command will be run on a remote server specified with the -H. The -U parameter is used to pass the userid and password of a user authorized to issue the command on the remote server.
207 Example2:
208 sudo samba-tool group addmembers supergroup User2
210 Example2 shows how to add a single user account, User2, to the supergroup AD group. It uses the sudo command to run as root when issuing the command.
213 synopsis = "%prog <groupname> <listofmembers> [options]"
215 takes_optiongroups = {
216 "sambaopts": options.SambaOptions,
217 "versionopts": options.VersionOptions,
218 "credopts": options.CredentialsOptions,
221 takes_options = [
222 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
223 metavar="URL", dest="H"),
226 takes_args = ["groupname", "listofmembers"]
228 def run(self, groupname, listofmembers, credopts=None, sambaopts=None,
229 versionopts=None, H=None):
231 lp = sambaopts.get_loadparm()
232 creds = credopts.get_credentials(lp, fallback_machine=True)
234 try:
235 samdb = SamDB(url=H, session_info=system_session(),
236 credentials=creds, lp=lp)
237 groupmembers = listofmembers.split(',')
238 samdb.add_remove_group_members(groupname, groupmembers,
239 add_members_operation=True)
240 except Exception, e:
241 # FIXME: catch more specific exception
242 raise CommandError('Failed to add members "%s" to group "%s"' % (
243 listofmembers, groupname), e)
244 self.outf.write("Added members to group %s\n" % groupname)
247 class cmd_group_remove_members(Command):
248 """Remove members from an AD group.
250 This command removes one or more members from an existing Active Directory group. The command accepts one or more group member names separated by commas. A group member may be a user or computer account or another Active Directory group that is a member of the group specified on the command.
252 When a member is removed from a group, inherited permissions and rights will no longer apply to the member.
254 Example1:
255 samba-tool group removemembers supergroup Group1 -H ldap://samba.samdom.example.com -Uadministrator%passw0rd
257 Example1 shows how to remove Group1 from supergroup. The command will run on the remote server specified on the -H parameter. The -U parameter is used to pass the userid and password of a user authorized to issue the command on the remote server.
259 Example2:
260 sudo samba-tool group removemembers supergroup User1
262 Example2 shows how to remove a single user account, User2, from the supergroup AD group. It uses the sudo command to run as root when issuing the command.
265 synopsis = "%prog <groupname> <listofmembers> [options]"
267 takes_optiongroups = {
268 "sambaopts": options.SambaOptions,
269 "versionopts": options.VersionOptions,
270 "credopts": options.CredentialsOptions,
273 takes_options = [
274 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
275 metavar="URL", dest="H"),
278 takes_args = ["groupname", "listofmembers"]
280 def run(self, groupname, listofmembers, credopts=None, sambaopts=None,
281 versionopts=None, H=None):
283 lp = sambaopts.get_loadparm()
284 creds = credopts.get_credentials(lp, fallback_machine=True)
286 try:
287 samdb = SamDB(url=H, session_info=system_session(),
288 credentials=creds, lp=lp)
289 samdb.add_remove_group_members(groupname, listofmembers.split(","),
290 add_members_operation=False)
291 except Exception, e:
292 # FIXME: Catch more specific exception
293 raise CommandError('Failed to remove members "%s" from group "%s"' % (listofmembers, groupname), e)
294 self.outf.write("Removed members from group %s\n" % groupname)
297 class cmd_group_list(Command):
298 """List all groups."""
300 synopsis = "%prog [options]"
302 takes_options = [
303 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
304 metavar="URL", dest="H"),
305 Option("-v", "--verbose",
306 help="Verbose output, showing group type and group scope.",
307 action="store_true"),
311 takes_optiongroups = {
312 "sambaopts": options.SambaOptions,
313 "credopts": options.CredentialsOptions,
314 "versionopts": options.VersionOptions,
317 def run(self, sambaopts=None, credopts=None, versionopts=None, H=None,
318 verbose=False):
319 lp = sambaopts.get_loadparm()
320 creds = credopts.get_credentials(lp, fallback_machine=True)
322 samdb = SamDB(url=H, session_info=system_session(),
323 credentials=creds, lp=lp)
325 domain_dn = samdb.domain_dn()
326 res = samdb.search(domain_dn, scope=ldb.SCOPE_SUBTREE,
327 expression=("(objectClass=group)"),
328 attrs=["samaccountname", "grouptype"])
329 if (len(res) == 0):
330 return
332 if verbose:
333 self.outf.write("Group Name Group Type Group Scope\n")
334 self.outf.write("-----------------------------------------------------------------------------\n")
336 for msg in res:
337 self.outf.write("%-44s" % msg.get("samaccountname", idx=0))
338 hgtype = hex(int("%s" % msg["grouptype"]) & 0x00000000FFFFFFFF)
339 if (hgtype == hex(int(security_group.get("Builtin")))):
340 self.outf.write("Security Builtin\n")
341 elif (hgtype == hex(int(security_group.get("Domain")))):
342 self.outf.write("Security Domain\n")
343 elif (hgtype == hex(int(security_group.get("Global")))):
344 self.outf.write("Security Global\n")
345 elif (hgtype == hex(int(security_group.get("Universal")))):
346 self.outf.write("Security Universal\n")
347 elif (hgtype == hex(int(distribution_group.get("Global")))):
348 self.outf.write("Distribution Global\n")
349 elif (hgtype == hex(int(distribution_group.get("Domain")))):
350 self.outf.write("Distribution Domain\n")
351 elif (hgtype == hex(int(distribution_group.get("Universal")))):
352 self.outf.write("Distribution Universal\n")
353 else:
354 self.outf.write("\n")
355 else:
356 for msg in res:
357 self.outf.write("%s\n" % msg.get("samaccountname", idx=0))
359 class cmd_group_list_members(Command):
360 """List all members of an AD group.
362 This command lists members from an existing Active Directory group. The command accepts one group name.
364 Example1:
365 samba-tool group listmembers \"Domain Users\" -H ldap://samba.samdom.example.com -Uadministrator%passw0rd
368 synopsis = "%prog <groupname> [options]"
370 takes_options = [
371 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
372 metavar="URL", dest="H"),
375 takes_optiongroups = {
376 "sambaopts": options.SambaOptions,
377 "credopts": options.CredentialsOptions,
378 "versionopts": options.VersionOptions,
381 takes_args = ["groupname"]
383 def run(self, groupname, credopts=None, sambaopts=None, versionopts=None, H=None):
384 lp = sambaopts.get_loadparm()
385 creds = credopts.get_credentials(lp, fallback_machine=True)
387 try:
388 samdb = SamDB(url=H, session_info=system_session(),
389 credentials=creds, lp=lp)
391 search_filter = "(&(objectClass=group)(samaccountname=%s))" % groupname
392 res = samdb.search(samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE,
393 expression=(search_filter),
394 attrs=["objectSid"])
396 if (len(res) != 1):
397 return
399 group_dn = res[0].get('dn', idx=0)
400 object_sid = res[0].get('objectSid', idx=0)
402 object_sid = ndr_unpack(security.dom_sid, object_sid)
403 (group_dom_sid, rid) = object_sid.split()
405 search_filter = "(|(primaryGroupID=%s)(memberOf=%s))" % (rid, group_dn)
406 res = samdb.search(samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE,
407 expression=(search_filter),
408 attrs=["samAccountName", "cn"])
410 if (len(res) == 0):
411 return
413 for msg in res:
414 member_name = msg.get("samAccountName", idx=0)
415 if member_name is None:
416 member_name = msg.get("cn", idx=0)
417 self.outf.write("%s\n" % member_name)
419 except Exception, e:
420 raise CommandError('Failed to list members of "%s" group ' % groupname, e)
423 class cmd_group(SuperCommand):
424 """Group management."""
426 subcommands = {}
427 subcommands["add"] = cmd_group_add()
428 subcommands["delete"] = cmd_group_delete()
429 subcommands["addmembers"] = cmd_group_add_members()
430 subcommands["removemembers"] = cmd_group_remove_members()
431 subcommands["list"] = cmd_group_list()
432 subcommands["listmembers"] = cmd_group_list_members()