samba-tool group addmembers: add --member-dn option
authorBjörn Baumbach <bb@sernet.de>
Tue, 17 Dec 2019 15:26:23 +0000 (16:26 +0100)
committerStefan Metzmacher <metze@samba.org>
Tue, 21 Jan 2020 14:38:46 +0000 (14:38 +0000)
The --member-dn option allows to specify an object by it's DN.

This is required to select a specific object if there are more than one
with the same name. Multiple contacts can exist with the same name in
different OUs.

Signed-off-by: Björn Baumbach <bb@sernet.de>
Reviewed-by: Ralph Boehme <slow@samba.org>
python/samba/netcmd/group.py
python/samba/samdb.py

index 6a8da402c47c691c33970d5c3ab9578066b0b183..4bf417cc878b413e0f46a725129cadeb6114635c 100644 (file)
@@ -218,7 +218,7 @@ sudo samba-tool group addmembers supergroup User2
 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.
 """
 
-    synopsis = "%prog <groupname> <listofmembers> [options]"
+    synopsis = "%prog <groupname> (<listofmembers>]|--member-dn=<member-dn>) [options]"
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -229,6 +229,10 @@ Example2 shows how to add a single user account, User2, to the supergroup AD gro
     takes_options = [
         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
                metavar="URL", dest="H"),
+        Option("--member-dn",
+               help=("DN of the new group member to be added.\n"
+                     "The --object-types option will be ignored."),
+               type=str),
         Option("--object-types",
                help=("Comma separated list of object types.\n"
                      "The types are used to filter the search for the "
@@ -240,15 +244,16 @@ Example2 shows how to add a single user account, User2, to the supergroup AD gro
                type=str),
     ]
 
-    takes_args = ["groupname", "listofmembers"]
+    takes_args = ["groupname", "listofmembers?"]
 
     def run(self,
             groupname,
-            listofmembers,
+            listofmembers=None,
             credopts=None,
             sambaopts=None,
             versionopts=None,
             H=None,
+            member_dn=None,
             object_types="user,group,computer"):
 
         lp = sambaopts.get_loadparm()
@@ -257,7 +262,10 @@ Example2 shows how to add a single user account, User2, to the supergroup AD gro
         try:
             samdb = SamDB(url=H, session_info=system_session(),
                           credentials=creds, lp=lp)
-            groupmembers = listofmembers.split(',')
+            if member_dn is not None:
+                groupmembers = [ member_dn ]
+            else:
+                groupmembers = listofmembers.split(',')
             group_member_types = object_types.split(',')
             samdb.add_remove_group_members(groupname, groupmembers,
                                            add_members_operation=True,
index af3a7ddf96af43ca9d389c2bf0ea29a22bb98289..d0320c1d2cc85569192b0d5f28ead6f04014b6b0 100644 (file)
@@ -334,35 +334,42 @@ changetype: modify
 """ % (str(targetgroup[0].dn))
 
             for member in members:
-                filter = self.group_member_filter(member, member_types)
-                foreign_msg = None
+                targetmember_dn = None
+
                 try:
                     membersid = security.dom_sid(member)
+                    targetmember_dn = "<SID=%s>" % str(membersid)
                 except TypeError as e:
-                    membersid = None
-
-                if membersid is not None:
-                    filter = '(objectSid=%s)' % str(membersid)
-                    dn_str = "<SID=%s>" % str(membersid)
-                    foreign_msg = ldb.Message()
-                    foreign_msg.dn = ldb.Dn(self, dn_str)
-
-                targetmember = self.search(base=self.domain_dn(),
-                                           scope=ldb.SCOPE_SUBTREE,
-                                           expression="%s" % filter,
-                                           attrs=[])
-
-                if len(targetmember) > 1:
-                    memberlist_str = ""
-                    for msg in targetmember:
-                        memberlist_str += "%s\n" % msg.get("dn")
-                    raise Exception('Found multiple results for "%s":\n%s' %
-                                    (member, memberlist_str))
-                if len(targetmember) == 0 and foreign_msg is not None:
-                    targetmember = [foreign_msg]
-                if len(targetmember) != 1:
-                    raise Exception('Unable to find "%s". Operation cancelled.' % member)
-                targetmember_dn = targetmember[0].dn.extended_str(1)
+                    pass
+
+                if targetmember_dn is None:
+                    try:
+                        member_dn = ldb.Dn(self, member)
+                        if member_dn.get_linearized() == member_dn.extended_str(1):
+                            full_member_dn = self.normalize_dn_in_domain(member_dn)
+                        else:
+                            full_member_dn = member_dn
+                        targetmember_dn = full_member_dn.extended_str(1)
+                    except ValueError as e:
+                        pass
+
+                if targetmember_dn is None:
+                    filter = self.group_member_filter(member, member_types)
+                    targetmember = self.search(base=self.domain_dn(),
+                                               scope=ldb.SCOPE_SUBTREE,
+                                               expression=filter,
+                                               attrs=[])
+
+                    if len(targetmember) > 1:
+                        targetmemberlist_str = ""
+                        for msg in targetmember:
+                            targetmemberlist_str += "%s\n" % msg.get("dn")
+                        raise Exception('Found multiple results for "%s":\n%s' %
+                                        (member, targetmemberlist_str))
+                    if len(targetmember) != 1:
+                        raise Exception('Unable to find "%s". Operation cancelled.' % member)
+                    targetmember_dn = targetmember[0].dn.extended_str(1)
+
                 if add_members_operation is True and (targetgroup[0].get('member') is None or get_bytes(targetmember_dn) not in [str(x) for x in targetgroup[0]['member']]):
                     modified = True
                     addtargettogroup += """add: member