4004a7d6c37cafc9a0e8b742a7ebdd2c89ffbaaa
[metze/samba/wip.git] / python / samba / netcmd / group.py
1 # Adds a new user to a Samba4 server
2 # Copyright Jelmer Vernooij 2008
3 #
4 # Based on the original in EJS:
5 # Copyright Andrew Tridgell 2005
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 import samba.getopt as options
21 from samba.netcmd import Command, SuperCommand, CommandError, Option
22 import ldb
23 from samba.ndr import ndr_unpack
24 from samba.dcerpc import security
25
26 from getpass import getpass
27 from samba.auth import system_session
28 from samba.samdb import SamDB
29 from samba.dsdb import (
30     GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
31     GTYPE_SECURITY_DOMAIN_LOCAL_GROUP,
32     GTYPE_SECURITY_GLOBAL_GROUP,
33     GTYPE_SECURITY_UNIVERSAL_GROUP,
34     GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP,
35     GTYPE_DISTRIBUTION_GLOBAL_GROUP,
36     GTYPE_DISTRIBUTION_UNIVERSAL_GROUP,
37 )
38
39 security_group = dict({"Builtin": GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
40                        "Domain": GTYPE_SECURITY_DOMAIN_LOCAL_GROUP,
41                        "Global": GTYPE_SECURITY_GLOBAL_GROUP,
42                        "Universal": GTYPE_SECURITY_UNIVERSAL_GROUP})
43 distribution_group = dict({"Domain": GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP,
44                            "Global": GTYPE_DISTRIBUTION_GLOBAL_GROUP,
45                            "Universal": GTYPE_DISTRIBUTION_UNIVERSAL_GROUP})
46
47
48 class cmd_group_add(Command):
49     """Creates a new AD group.
50
51 This command creates a new Active Directory group.  The groupname specified on the command is a unique sAMAccountName.
52
53 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
55 Groups may also be used to establish email distribution lists, using --group-type=Distribution.
56
57 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
59 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
61 The command may be run from the root userid or another authorized userid.  The
62 -H or --URL= option can be used to execute the command on a remote server.
63
64 Example1:
65 samba-tool group add Group1 -H ldap://samba.samdom.example.com --description='Simple group'
66
67 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
69 Example2:
70 sudo samba-tool group add Group2 --group-type=Distribution
71
72 Example2 adds a new distribution group to the local server.  The command is run under root using the sudo command.
73 """
74
75     synopsis = "%prog <groupname> [options]"
76
77     takes_optiongroups = {
78         "sambaopts": options.SambaOptions,
79         "versionopts": options.VersionOptions,
80         "credopts": options.CredentialsOptions,
81     }
82
83     takes_options = [
84         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
85                metavar="URL", dest="H"),
86         Option("--groupou",
87            help="Alternative location (without domainDN counterpart) to default CN=Users in which new user object will be created",
88            type=str),
89         Option("--group-scope", type="choice", choices=["Domain", "Global", "Universal"],
90             help="Group scope (Domain | Global | Universal)"),
91         Option("--group-type", type="choice", choices=["Security", "Distribution"],
92             help="Group type (Security | Distribution)"),
93         Option("--description", help="Group's description", type=str),
94         Option("--mail-address", help="Group's email address", type=str),
95         Option("--notes", help="Groups's notes", type=str),
96     ]
97
98     takes_args = ["groupname"]
99
100     def run(self, groupname, credopts=None, sambaopts=None,
101             versionopts=None, H=None, groupou=None, group_scope=None,
102             group_type=None, description=None, mail_address=None, notes=None):
103
104         if (group_type or "Security") == "Security":
105             gtype = security_group.get(group_scope, GTYPE_SECURITY_GLOBAL_GROUP)
106         else:
107             gtype = distribution_group.get(group_scope, GTYPE_DISTRIBUTION_GLOBAL_GROUP)
108
109         lp = sambaopts.get_loadparm()
110         creds = credopts.get_credentials(lp, fallback_machine=True)
111
112         try:
113             samdb = SamDB(url=H, session_info=system_session(),
114                           credentials=creds, lp=lp)
115             samdb.newgroup(groupname, groupou=groupou, grouptype = gtype,
116                           description=description, mailaddress=mail_address, notes=notes)
117         except Exception, e:
118             # FIXME: catch more specific exception
119             raise CommandError('Failed to create group "%s"' % groupname, e)
120         self.outf.write("Added group %s\n" % groupname)
121
122
123 class cmd_group_delete(Command):
124     """Deletes an AD group.
125
126 The command deletes an existing AD group from the Active Directory domain.  The groupname specified on the command is the sAMAccountName.
127
128 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.
129
130 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.
131
132 Example1:
133 samba-tool group delete Group1 -H ldap://samba.samdom.example.com -Uadministrator%passw0rd
134
135 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.
136
137 Example2:
138 sudo samba-tool group delete Group2
139
140 Example2 deletes group Group2 from the local server.  The command is run under root using the sudo command.
141 """
142
143     synopsis = "%prog <groupname> [options]"
144
145     takes_optiongroups = {
146         "sambaopts": options.SambaOptions,
147         "versionopts": options.VersionOptions,
148         "credopts": options.CredentialsOptions,
149     }
150
151     takes_options = [
152         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
153                metavar="URL", dest="H"),
154     ]
155
156     takes_args = ["groupname"]
157
158     def run(self, groupname, credopts=None, sambaopts=None, versionopts=None, H=None):
159
160         lp = sambaopts.get_loadparm()
161         creds = credopts.get_credentials(lp, fallback_machine=True)
162
163         try:
164             samdb = SamDB(url=H, session_info=system_session(),
165                           credentials=creds, lp=lp)
166             samdb.deletegroup(groupname)
167         except Exception, e:
168             # FIXME: catch more specific exception
169             raise CommandError('Failed to remove group "%s"' % groupname, e)
170         self.outf.write("Deleted group %s\n" % groupname)
171
172
173 class cmd_group_add_members(Command):
174     """Add members to an AD group.
175
176 This command adds one or more members to an existing Active Directory group.  The command accepts one or more group member names seperated by commas.  A group member may be a user or computer account or another Active Directory group.
177
178 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.
179
180 Example1:
181 samba-tool group addmembers supergroup Group1,Group2,User1 -H ldap://samba.samdom.example.com -Uadministrator%passw0rd
182
183 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.
184
185 Example2:
186 sudo samba-tool group addmembers supergroup User2
187
188 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.
189 """
190
191     synopsis = "%prog <groupname> <listofmembers> [options]"
192
193     takes_optiongroups = {
194         "sambaopts": options.SambaOptions,
195         "versionopts": options.VersionOptions,
196         "credopts": options.CredentialsOptions,
197     }
198
199     takes_options = [
200         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
201                metavar="URL", dest="H"),
202     ]
203
204     takes_args = ["groupname", "listofmembers"]
205
206     def run(self, groupname, listofmembers, credopts=None, sambaopts=None,
207             versionopts=None, H=None):
208
209         lp = sambaopts.get_loadparm()
210         creds = credopts.get_credentials(lp, fallback_machine=True)
211
212         try:
213             samdb = SamDB(url=H, session_info=system_session(),
214                           credentials=creds, lp=lp)
215             groupmembers = listofmembers.split(',')
216             samdb.add_remove_group_members(groupname, groupmembers,
217                     add_members_operation=True)
218         except Exception, e:
219             # FIXME: catch more specific exception
220             raise CommandError('Failed to add members "%s" to group "%s"' % (
221                 listofmembers, groupname), e)
222         self.outf.write("Added members to group %s\n" % groupname)
223
224
225 class cmd_group_remove_members(Command):
226     """Remove members from an AD group.
227
228 This command removes one or more members from an existing Active Directory group.  The command accepts one or more group member names seperated 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.
229
230 When a member is removed from a group, inherited permissions and rights will no longer apply to the member.
231
232 Example1:
233 samba-tool group removemembers supergroup Group1 -H ldap://samba.samdom.example.com -Uadministrator%passw0rd
234
235 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.
236
237 Example2:
238 sudo samba-tool group removemembers supergroup User1
239
240 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.
241 """
242
243     synopsis = "%prog <groupname> <listofmembers> [options]"
244
245     takes_optiongroups = {
246         "sambaopts": options.SambaOptions,
247         "versionopts": options.VersionOptions,
248         "credopts": options.CredentialsOptions,
249     }
250
251     takes_options = [
252         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
253                metavar="URL", dest="H"),
254     ]
255
256     takes_args = ["groupname", "listofmembers"]
257
258     def run(self, groupname, listofmembers, credopts=None, sambaopts=None,
259             versionopts=None, H=None):
260
261         lp = sambaopts.get_loadparm()
262         creds = credopts.get_credentials(lp, fallback_machine=True)
263
264         try:
265             samdb = SamDB(url=H, session_info=system_session(),
266                           credentials=creds, lp=lp)
267             samdb.add_remove_group_members(groupname, listofmembers.split(","),
268                     add_members_operation=False)
269         except Exception, e:
270             # FIXME: Catch more specific exception
271             raise CommandError('Failed to remove members "%s" from group "%s"' % (listofmembers, groupname), e)
272         self.outf.write("Removed members from group %s\n" % groupname)
273
274
275 class cmd_group_list(Command):
276     """List all groups."""
277
278     synopsis = "%prog [options]"
279
280     takes_options = [
281         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
282                metavar="URL", dest="H"),
283         Option("-v", "--verbose",
284                help="Verbose output, showing group type and group scope.",
285                action="store_true"),
286
287         ]
288
289     takes_optiongroups = {
290         "sambaopts": options.SambaOptions,
291         "credopts": options.CredentialsOptions,
292         "versionopts": options.VersionOptions,
293         }
294
295     def run(self, sambaopts=None, credopts=None, versionopts=None, H=None,
296             verbose=False):
297         lp = sambaopts.get_loadparm()
298         creds = credopts.get_credentials(lp, fallback_machine=True)
299
300         samdb = SamDB(url=H, session_info=system_session(),
301             credentials=creds, lp=lp)
302
303         domain_dn = samdb.domain_dn()
304         res = samdb.search(domain_dn, scope=ldb.SCOPE_SUBTREE,
305                     expression=("(objectClass=group)"),
306                     attrs=["samaccountname", "grouptype"])
307         if (len(res) == 0):
308             return
309
310         if verbose:
311             self.outf.write("Group Name                                  Group Type      Group Scope\n")
312             self.outf.write("-----------------------------------------------------------------------------\n")
313
314             for msg in res:
315                 self.outf.write("%-44s" % msg.get("samaccountname", idx=0))
316                 hgtype = hex(int("%s" % msg["grouptype"]) & 0x00000000FFFFFFFF)
317                 if (hgtype == hex(int(security_group.get("Builtin")))):
318                     self.outf.write("Security         Builtin\n")
319                 elif (hgtype == hex(int(security_group.get("Domain")))):
320                     self.outf.write("Security         Domain\n")
321                 elif (hgtype == hex(int(security_group.get("Global")))):
322                     self.outf.write("Security         Global\n")
323                 elif (hgtype == hex(int(security_group.get("Universal")))):
324                     self.outf.write("Security         Universal\n")
325                 elif (hgtype == hex(int(distribution_group.get("Global")))):
326                     self.outf.write("Distribution     Global\n")
327                 elif (hgtype == hex(int(distribution_group.get("Domain")))):
328                     self.outf.write("Distribution     Domain\n")
329                 elif (hgtype == hex(int(distribution_group.get("Universal")))):
330                     self.outf.write("Distribution     Universal\n")
331                 else:
332                     self.outf.write("\n")
333         else:
334             for msg in res:
335                 self.outf.write("%s\n" % msg.get("samaccountname", idx=0))
336
337 class cmd_group_list_members(Command):
338     """List all members of an AD group.
339
340 This command lists members from an existing Active Directory group. The command accepts one group name.
341
342 Example1:
343 samba-tool group listmembers \"Domain Users\" -H ldap://samba.samdom.example.com -Uadministrator%passw0rd
344 """
345
346     synopsis = "%prog <groupname> [options]"
347
348     takes_options = [
349         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
350                metavar="URL", dest="H"),
351         ]
352
353     takes_optiongroups = {
354         "sambaopts": options.SambaOptions,
355         "credopts": options.CredentialsOptions,
356         "versionopts": options.VersionOptions,
357         }
358
359     takes_args = ["groupname"]
360
361     def run(self, groupname, credopts=None, sambaopts=None, versionopts=None, H=None):
362         lp = sambaopts.get_loadparm()
363         creds = credopts.get_credentials(lp, fallback_machine=True)
364
365         try:
366             samdb = SamDB(url=H, session_info=system_session(),
367                           credentials=creds, lp=lp)
368
369             search_filter = "(&(objectClass=group)(samaccountname=%s))" % groupname
370             res = samdb.search(samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE,
371                                expression=(search_filter),
372                                attrs=["objectSid"])
373
374             if (len(res) != 1):
375                 return
376
377             group_dn = res[0].get('dn', idx=0)
378             object_sid = res[0].get('objectSid', idx=0)
379
380             object_sid = ndr_unpack(security.dom_sid, object_sid)
381             (group_dom_sid, rid) = object_sid.split()
382
383             search_filter = "(|(primaryGroupID=%s)(memberOf=%s))" % (rid, group_dn)
384             res = samdb.search(samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE,
385                                expression=(search_filter),
386                                attrs=["samAccountName", "cn"])
387
388             if (len(res) == 0):
389                 return
390
391             for msg in res:
392                 member_name = msg.get("samAccountName", idx=0)
393                 if member_name is None:
394                     member_name = msg.get("cn", idx=0)
395                 self.outf.write("%s\n" % member_name)
396
397         except Exception, e:
398             raise CommandError('Failed to list members of "%s" group ' % groupname, e)
399
400
401 class cmd_group(SuperCommand):
402     """Group management."""
403
404     subcommands = {}
405     subcommands["add"] = cmd_group_add()
406     subcommands["delete"] = cmd_group_delete()
407     subcommands["addmembers"] = cmd_group_add_members()
408     subcommands["removemembers"] = cmd_group_remove_members()
409     subcommands["list"] = cmd_group_list()
410     subcommands["listmembers"] = cmd_group_list_members()