samba-tool: Support preloading multiple users
authorAndrew Bartlett <abartlet@samba.org>
Wed, 24 Feb 2016 03:39:38 +0000 (16:39 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 25 Feb 2016 06:58:55 +0000 (07:58 +0100)
Based on patches by Adrian Cochrane

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Thu Feb 25 07:58:55 CET 2016 on sn-devel-144

python/samba/netcmd/rodc.py
python/samba/tests/samba_tool/rodc.py [new file with mode: 0644]
source4/selftest/tests.py

index 4404b7b282ad948a5390339c293a8f342116f3ee..ba29c74640a688974e8115dc10a5f4695d70bf6a 100644 (file)
@@ -23,12 +23,12 @@ from samba.auth import system_session
 import ldb
 from samba.dcerpc import misc, drsuapi
 from samba.drs_utils import drs_Replicate
-
+import sys
 
 class cmd_rodc_preload(Command):
-    """Preload one account for an RODC."""
+    """Preload accounts for an RODC.  Multiple accounts may be requested."""
 
-    synopsis = "%prog (<SID>|<DN>|<accountname>) [options]"
+    synopsis = "%prog (<SID>|<DN>|<accountname>)+ ... [options]"
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -38,9 +38,10 @@ class cmd_rodc_preload(Command):
 
     takes_options = [
         Option("--server", help="DC to use", type=str),
+        Option("--file", help="Read account list from a file, or - for stdin (one per line)", type=str),
         ]
 
-    takes_args = ["account"]
+    takes_args = ["account*"]
 
     def get_dn(self, samdb, account):
         '''work out what DN they meant'''
@@ -62,12 +63,25 @@ class cmd_rodc_preload(Command):
         return str(res[0]["dn"])
 
 
-    def run(self, account, sambaopts=None,
-            credopts=None, versionopts=None, server=None):
+    def run(self, *accounts, **kwargs):
+        sambaopts = kwargs.get("sambaopts")
+        credopts = kwargs.get("credopts")
+        versionpts = kwargs.get("versionopts")
+        server = kwargs.get("server")
+        accounts_file = kwargs.get("file")
 
         if server is None:
             raise Exception("You must supply a server")
 
+        if accounts_file is not None:
+            accounts = []
+            if accounts_file == "-":
+                for line in sys.stdin:
+                    accounts.append(line.strip())
+            else:
+                for line in open(accounts_file, 'r'):
+                    accounts.append(line.strip())
+
         lp = sambaopts.get_loadparm()
 
         creds = credopts.get_credentials(lp, fallback_machine=True)
@@ -80,26 +94,27 @@ class cmd_rodc_preload(Command):
         local_samdb = SamDB(url=None, session_info=system_session(),
                             credentials=creds, lp=lp)
 
-        # work out the source and destination GUIDs
-        dc_ntds_dn = samdb.get_dsServiceName()
-        res = samdb.search(base=dc_ntds_dn, scope=ldb.SCOPE_BASE, attrs=["invocationId"])
-        source_dsa_invocation_id = misc.GUID(local_samdb.schema_format_value("objectGUID", res[0]["invocationId"][0]))
-
-        dn = self.get_dn(samdb, account)
-        self.outf.write("Replicating DN %s\n" % dn)
-
         destination_dsa_guid = misc.GUID(local_samdb.get_ntds_GUID())
 
-        local_samdb.transaction_start()
         repl = drs_Replicate("ncacn_ip_tcp:%s[seal,print]" % server, lp, creds,
                              local_samdb, destination_dsa_guid)
-        try:
-            repl.replicate(dn, source_dsa_invocation_id, destination_dsa_guid,
-                           exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
-        except Exception, e:
-            local_samdb.transaction_cancel()
-            raise CommandError("Error replicating DN %s" % dn, e)
-        local_samdb.transaction_commit()
+        for account in accounts:
+            # work out the source and destination GUIDs
+            dc_ntds_dn = samdb.get_dsServiceName()
+            res = samdb.search(base=dc_ntds_dn, scope=ldb.SCOPE_BASE, attrs=["invocationId"])
+            source_dsa_invocation_id = misc.GUID(local_samdb.schema_format_value("objectGUID", res[0]["invocationId"][0]))
+
+            dn = self.get_dn(samdb, account)
+            self.outf.write("Replicating DN %s\n" % dn)
+
+            local_samdb.transaction_start()
+            try:
+                repl.replicate(dn, source_dsa_invocation_id, destination_dsa_guid,
+                               exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
+            except Exception, e:
+                local_samdb.transaction_cancel()
+                raise CommandError("Error replicating DN %s" % dn, e)
+            local_samdb.transaction_commit()
 
 
 
diff --git a/python/samba/tests/samba_tool/rodc.py b/python/samba/tests/samba_tool/rodc.py
new file mode 100644 (file)
index 0000000..9ae5dd1
--- /dev/null
@@ -0,0 +1,94 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Catalyst IT Ltd. 2015
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import ldb
+import samba
+from samba.samdb import SamDB
+from samba.tests import delete_force
+from samba.tests.samba_tool.base import SambaToolCmdTest
+from samba.credentials import Credentials
+from samba.auth import system_session
+
+class RodcCmdTestCase(SambaToolCmdTest):
+    def setUp(self):
+        super(RodcCmdTestCase, self).setUp()
+        self.lp = samba.param.LoadParm()
+        self.lp.load(os.environ["SMB_CONF_PATH"])
+        self.creds = Credentials()
+        self.creds.set_username(os.environ["DC_USERNAME"])
+        self.creds.set_password(os.environ["DC_PASSWORD"])
+        self.creds.guess(self.lp)
+        self.session = system_session()
+        self.ldb = SamDB("ldap://" + os.environ["DC_SERVER"],
+            session_info=self.session, credentials=self.creds,lp=self.lp)
+
+        self.base_dn = self.ldb.domain_dn()
+
+        self.ldb.newuser("sambatool1", "1qazXSW@")
+        self.ldb.newuser("sambatool2", "2wsxCDE#")
+        self.ldb.newuser("sambatool3", "3edcVFR$")
+        self.ldb.newuser("sambatool4", "4rfvBGT%")
+
+        self.ldb.add_remove_group_members("Allowed RODC Password Replication Group",
+                                          ["sambatool1", "sambatool2", "sambatool3",
+                                           "sambatool4"],
+                                          add_members_operation=True)
+
+    def tearDown(self):
+        super(RodcCmdTestCase, self).tearDown()
+        self.ldb.deleteuser("sambatool1")
+        self.ldb.deleteuser("sambatool2")
+        self.ldb.deleteuser("sambatool3")
+        self.ldb.deleteuser("sambatool4")
+        (result, out, err) = self.runsubcmd("drs", "replicate", "--local", "unused",
+                                            os.environ["DC_SERVER"], self.base_dn)
+
+
+    def test_single_by_account_name(self):
+        (result, out, err) = self.runsubcmd("rodc", "preload", "sambatool1",
+                                            "--server", os.environ["DC_SERVER"])
+        self.assertCmdSuccess(result, "ensuring rodc prefetch ran successfully")
+        self.assertEqual(out, "Replicating DN CN=sambatool1,CN=Users,%s\n" % self.base_dn)
+        self.assertEqual(err, "")
+
+    def test_single_by_dn(self):
+        (result, out, err) = self.runsubcmd("rodc", "preload", "cn=sambatool2,cn=users,%s" % self.base_dn,
+                                            "--server", os.environ["DC_SERVER"])
+        self.assertCmdSuccess(result, "ensuring rodc prefetch ran successfully")
+        self.assertEqual(out, "Replicating DN CN=sambatool2,CN=Users,%s\n" % self.base_dn)
+
+    def test_multi_by_account_name(self):
+        (result, out, err) = self.runsubcmd("rodc", "preload", "sambatool1", "sambatool2",
+                                            "--server", os.environ["DC_SERVER"])
+        self.assertCmdSuccess(result, "ensuring rodc prefetch ran successfully")
+        self.assertEqual(out, "Replicating DN CN=sambatool1,CN=Users,%s\nReplicating DN CN=sambatool2,CN=Users,%s\n" % (self.base_dn, self.base_dn))
+
+    def test_multi_by_dn(self):
+        (result, out, err) = self.runsubcmd("rodc", "preload", "cn=sambatool3,cn=users,%s" % self.base_dn, "cn=sambatool4,cn=users,%s" % self.base_dn,
+                                            "--server", os.environ["DC_SERVER"])
+        self.assertCmdSuccess(result, "ensuring rodc prefetch ran successfully")
+        self.assertEqual(out, "Replicating DN CN=sambatool3,CN=Users,%s\nReplicating DN CN=sambatool4,CN=Users,%s\n" % (self.base_dn, self.base_dn))
+
+    def test_multi_in_file(self):
+        tempf = os.path.join(self.tempdir, "accountlist")
+        open(tempf, 'w').write("sambatool1\nsambatool2")
+        (result, out, err) = self.runsubcmd("rodc", "preload", "--file", tempf,
+                                            "--server", os.environ["DC_SERVER"])
+        self.assertCmdSuccess(result, "ensuring rodc prefetch ran successfully")
+        self.assertEqual(out, "Replicating DN CN=sambatool1,CN=Users,%s\nReplicating DN CN=sambatool2,CN=Users,%s\n" % (self.base_dn, self.base_dn))
+        os.unlink(tempf)
index d76eda67786cd0d028af7912ca68e325626030cb..32d47a0ccd2251919a5871c17404fc57cb3f83f8 100755 (executable)
@@ -544,6 +544,8 @@ plansmbtorture4testsuite(t, "vampire_dc", ['$SERVER', '-U$USERNAME%$PASSWORD', '
 for env in ['rodc']:
     plansmbtorture4testsuite('rpc.echo', env, ['ncacn_np:$SERVER', "-k", "yes", '-U$USERNAME%$PASSWORD', '--workgroup=$DOMAIN'], modname="samba4.rpc.echo")
     plansmbtorture4testsuite('rpc.echo', "%s:local" % env, ['ncacn_np:$SERVER', "-k", "yes", '-P', '--workgroup=$DOMAIN'], modname="samba4.rpc.echo")
+planpythontestsuite("rodc:local", "samba.tests.samba_tool.rodc")
+
 plantestsuite("samba4.blackbox.provision-backend", "none", ["PYTHON=%s" % python, os.path.join(samba4srcdir, "setup/tests/blackbox_provision-backend.sh"), '$PREFIX/provision'])
 
 # Test renaming the DC