summaryrefslogtreecommitdiffstats
path: root/python/samba/tests
diff options
context:
space:
mode:
authorJelmer Vernooij <jelmer@samba.org>2012-12-28 15:37:14 +0100
committerAndrew Bartlett <abartlet@samba.org>2013-03-02 03:57:34 +0100
commit87afc3aee1ea593069322a49355dd8780d99e123 (patch)
tree8e1ea6678d93b53f21b34c4940b7d5a64e0f5020 /python/samba/tests
parent80fce353e740c793619005ac102ab07fb5e7d280 (diff)
downloadsamba-87afc3aee1ea593069322a49355dd8780d99e123.tar.gz
samba-87afc3aee1ea593069322a49355dd8780d99e123.tar.xz
samba-87afc3aee1ea593069322a49355dd8780d99e123.zip
Move python modules from source4/scripting/python/ to python/.
Reviewed-by: Andrew Bartlett <abartlet@samba.org> Autobuild-User(master): Andrew Bartlett <abartlet@samba.org> Autobuild-Date(master): Sat Mar 2 03:57:34 CET 2013 on sn-devel-104
Diffstat (limited to 'python/samba/tests')
-rw-r--r--python/samba/tests/__init__.py237
-rw-r--r--python/samba/tests/auth.py31
-rw-r--r--python/samba/tests/blackbox/__init__.py17
-rw-r--r--python/samba/tests/blackbox/ndrdump.py49
-rw-r--r--python/samba/tests/blackbox/samba_tool_drs.py97
-rw-r--r--python/samba/tests/common.py40
-rw-r--r--python/samba/tests/core.py63
-rw-r--r--python/samba/tests/credentials.py98
-rw-r--r--python/samba/tests/dcerpc/__init__.py20
-rw-r--r--python/samba/tests/dcerpc/bare.py51
-rw-r--r--python/samba/tests/dcerpc/dnsserver.py241
-rw-r--r--python/samba/tests/dcerpc/misc.py62
-rw-r--r--python/samba/tests/dcerpc/registry.py51
-rw-r--r--python/samba/tests/dcerpc/rpc_talloc.py84
-rw-r--r--python/samba/tests/dcerpc/rpcecho.py71
-rw-r--r--python/samba/tests/dcerpc/sam.py50
-rw-r--r--python/samba/tests/dcerpc/srvsvc.py68
-rw-r--r--python/samba/tests/dcerpc/testrpc.py141
-rw-r--r--python/samba/tests/dcerpc/unix.py49
-rw-r--r--python/samba/tests/dns.py622
-rw-r--r--python/samba/tests/docs.py127
-rw-r--r--python/samba/tests/dsdb.py130
-rw-r--r--python/samba/tests/gensec.py146
-rw-r--r--python/samba/tests/getopt.py55
-rw-r--r--python/samba/tests/hostconfig.py74
-rw-r--r--python/samba/tests/libsmb_samba_internal.py78
-rw-r--r--python/samba/tests/messaging.py67
-rw-r--r--python/samba/tests/netcmd.py90
-rw-r--r--python/samba/tests/ntacls.py83
-rw-r--r--python/samba/tests/param.py57
-rw-r--r--python/samba/tests/policy.py34
-rw-r--r--python/samba/tests/posixacl.py732
-rw-r--r--python/samba/tests/provision.py203
-rw-r--r--python/samba/tests/registry.py60
-rw-r--r--python/samba/tests/samba3.py219
-rw-r--r--python/samba/tests/samba3sam.py1125
-rw-r--r--python/samba/tests/samba_tool/__init__.py15
-rw-r--r--python/samba/tests/samba_tool/base.py114
-rw-r--r--python/samba/tests/samba_tool/gpo.py79
-rw-r--r--python/samba/tests/samba_tool/group.py169
-rw-r--r--python/samba/tests/samba_tool/ntacl.py135
-rw-r--r--python/samba/tests/samba_tool/processes.py35
-rw-r--r--python/samba/tests/samba_tool/timecmd.py43
-rw-r--r--python/samba/tests/samba_tool/user.py362
-rw-r--r--python/samba/tests/samdb.py96
-rw-r--r--python/samba/tests/security.py143
-rw-r--r--python/samba/tests/source.py264
-rw-r--r--python/samba/tests/strings.py103
-rw-r--r--python/samba/tests/unicodenames.py29
-rw-r--r--python/samba/tests/upgrade.py40
-rw-r--r--python/samba/tests/upgradeprovision.py135
-rw-r--r--python/samba/tests/upgradeprovisionneeddc.py179
-rw-r--r--python/samba/tests/xattr.py126
53 files changed, 7489 insertions, 0 deletions
diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py
new file mode 100644
index 00000000000..2df30a641bf
--- /dev/null
+++ b/python/samba/tests/__init__.py
@@ -0,0 +1,237 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
+#
+# 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/>.
+#
+
+"""Samba Python tests."""
+
+import os
+import ldb
+import samba
+import samba.auth
+from samba import param
+from samba.samdb import SamDB
+import subprocess
+import tempfile
+
+samba.ensure_external_module("testtools", "testtools")
+
+# Other modules import these two classes from here, for convenience:
+from testtools.testcase import (
+ TestCase as TesttoolsTestCase,
+ TestSkipped,
+ )
+
+
+class TestCase(TesttoolsTestCase):
+ """A Samba test case."""
+
+ def setUp(self):
+ super(TestCase, self).setUp()
+ test_debug_level = os.getenv("TEST_DEBUG_LEVEL")
+ if test_debug_level is not None:
+ test_debug_level = int(test_debug_level)
+ self._old_debug_level = samba.get_debug_level()
+ samba.set_debug_level(test_debug_level)
+ self.addCleanup(samba.set_debug_level, test_debug_level)
+
+ def get_loadparm(self):
+ return env_loadparm()
+
+ def get_credentials(self):
+ return cmdline_credentials
+
+
+class LdbTestCase(TesttoolsTestCase):
+ """Trivial test case for running tests against a LDB."""
+
+ def setUp(self):
+ super(LdbTestCase, self).setUp()
+ self.filename = os.tempnam()
+ self.ldb = samba.Ldb(self.filename)
+
+ def set_modules(self, modules=[]):
+ """Change the modules for this Ldb."""
+ m = ldb.Message()
+ m.dn = ldb.Dn(self.ldb, "@MODULES")
+ m["@LIST"] = ",".join(modules)
+ self.ldb.add(m)
+ self.ldb = samba.Ldb(self.filename)
+
+
+class TestCaseInTempDir(TestCase):
+
+ def setUp(self):
+ super(TestCaseInTempDir, self).setUp()
+ self.tempdir = tempfile.mkdtemp()
+ self.addCleanup(self._remove_tempdir)
+
+ def _remove_tempdir(self):
+ self.assertEquals([], os.listdir(self.tempdir))
+ os.rmdir(self.tempdir)
+ self.tempdir = None
+
+
+def env_loadparm():
+ lp = param.LoadParm()
+ try:
+ lp.load(os.environ["SMB_CONF_PATH"])
+ except KeyError:
+ raise Exception("SMB_CONF_PATH not set")
+ return lp
+
+
+def env_get_var_value(var_name):
+ """Returns value for variable in os.environ
+
+ Function throws AssertionError if variable is defined.
+ Unit-test based python tests require certain input params
+ to be set in environment, otherwise they can't be run
+ """
+ assert var_name in os.environ.keys(), "Please supply %s in environment" % var_name
+ return os.environ[var_name]
+
+
+cmdline_credentials = None
+
+class RpcInterfaceTestCase(TestCase):
+ """DCE/RPC Test case."""
+
+
+class ValidNetbiosNameTests(TestCase):
+
+ def test_valid(self):
+ self.assertTrue(samba.valid_netbios_name("FOO"))
+
+ def test_too_long(self):
+ self.assertFalse(samba.valid_netbios_name("FOO"*10))
+
+ def test_invalid_characters(self):
+ self.assertFalse(samba.valid_netbios_name("*BLA"))
+
+
+class BlackboxProcessError(Exception):
+ """This is raised when check_output() process returns a non-zero exit status
+
+ Exception instance should contain the exact exit code (S.returncode),
+ command line (S.cmd), process output (S.stdout) and process error stream
+ (S.stderr)
+ """
+
+ def __init__(self, returncode, cmd, stdout, stderr):
+ self.returncode = returncode
+ self.cmd = cmd
+ self.stdout = stdout
+ self.stderr = stderr
+
+ def __str__(self):
+ return "Command '%s'; exit status %d; stdout: '%s'; stderr: '%s'" % (self.cmd, self.returncode,
+ self.stdout, self.stderr)
+
+class BlackboxTestCase(TestCase):
+ """Base test case for blackbox tests."""
+
+ def _make_cmdline(self, line):
+ bindir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../../bin"))
+ parts = line.split(" ")
+ if os.path.exists(os.path.join(bindir, parts[0])):
+ parts[0] = os.path.join(bindir, parts[0])
+ line = " ".join(parts)
+ return line
+
+ def check_run(self, line):
+ line = self._make_cmdline(line)
+ p = subprocess.Popen(line, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+ retcode = p.wait()
+ if retcode:
+ raise BlackboxProcessError(retcode, line, p.stdout.read(), p.stderr.read())
+
+ def check_output(self, line):
+ line = self._make_cmdline(line)
+ p = subprocess.Popen(line, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, close_fds=True)
+ retcode = p.wait()
+ if retcode:
+ raise BlackboxProcessError(retcode, line, p.stdout.read(), p.stderr.read())
+ return p.stdout.read()
+
+def connect_samdb(samdb_url, lp=None, session_info=None, credentials=None,
+ flags=0, ldb_options=None, ldap_only=False):
+ """Create SamDB instance and connects to samdb_url database.
+
+ :param samdb_url: Url for database to connect to.
+ :param lp: Optional loadparm object
+ :param session_info: Optional session information
+ :param credentials: Optional credentials, defaults to anonymous.
+ :param flags: Optional LDB flags
+ :param ldap_only: If set, only remote LDAP connection will be created.
+
+ Added value for tests is that we have a shorthand function
+ to make proper URL for ldb.connect() while using default
+ parameters for connection based on test environment
+ """
+ samdb_url = samdb_url.lower()
+ if not "://" in samdb_url:
+ if not ldap_only and os.path.isfile(samdb_url):
+ samdb_url = "tdb://%s" % samdb_url
+ else:
+ samdb_url = "ldap://%s" % samdb_url
+ # use 'paged_search' module when connecting remotely
+ if samdb_url.startswith("ldap://"):
+ ldb_options = ["modules:paged_searches"]
+ elif ldap_only:
+ raise AssertionError("Trying to connect to %s while remote "
+ "connection is required" % samdb_url)
+
+ # set defaults for test environment
+ if lp is None:
+ lp = env_loadparm()
+ if session_info is None:
+ session_info = samba.auth.system_session(lp)
+ if credentials is None:
+ credentials = cmdline_credentials
+
+ return SamDB(url=samdb_url,
+ lp=lp,
+ session_info=session_info,
+ credentials=credentials,
+ flags=flags,
+ options=ldb_options)
+
+
+def connect_samdb_ex(samdb_url, lp=None, session_info=None, credentials=None,
+ flags=0, ldb_options=None, ldap_only=False):
+ """Connects to samdb_url database
+
+ :param samdb_url: Url for database to connect to.
+ :param lp: Optional loadparm object
+ :param session_info: Optional session information
+ :param credentials: Optional credentials, defaults to anonymous.
+ :param flags: Optional LDB flags
+ :param ldap_only: If set, only remote LDAP connection will be created.
+ :return: (sam_db_connection, rootDse_record) tuple
+ """
+ sam_db = connect_samdb(samdb_url, lp, session_info, credentials,
+ flags, ldb_options, ldap_only)
+ # fetch RootDse
+ res = sam_db.search(base="", expression="", scope=ldb.SCOPE_BASE,
+ attrs=["*"])
+ return (sam_db, res[0])
+
+
+def delete_force(samdb, dn):
+ try:
+ samdb.delete(dn)
+ except ldb.LdbError, (num, _):
+ assert(num == ldb.ERR_NO_SUCH_OBJECT)
diff --git a/python/samba/tests/auth.py b/python/samba/tests/auth.py
new file mode 100644
index 00000000000..f71e1a784d2
--- /dev/null
+++ b/python/samba/tests/auth.py
@@ -0,0 +1,31 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+#
+# 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/>.
+#
+
+"""Tests for the Auth Python bindings.
+
+Note that this just tests the bindings work. It does not intend to test
+the functionality, that's already done in other tests.
+"""
+
+from samba import auth
+import samba.tests
+
+class AuthTests(samba.tests.TestCase):
+
+ def test_system_session(self):
+ auth.system_session()
+
diff --git a/python/samba/tests/blackbox/__init__.py b/python/samba/tests/blackbox/__init__.py
new file mode 100644
index 00000000000..361e5cfe5e5
--- /dev/null
+++ b/python/samba/tests/blackbox/__init__.py
@@ -0,0 +1,17 @@
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+
+# 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/>.
+#
+
+"""Blackbox tests. """
diff --git a/python/samba/tests/blackbox/ndrdump.py b/python/samba/tests/blackbox/ndrdump.py
new file mode 100644
index 00000000000..fca9a931533
--- /dev/null
+++ b/python/samba/tests/blackbox/ndrdump.py
@@ -0,0 +1,49 @@
+# Blackbox tests for ndrdump
+# Copyright (C) 2008 Andrew Tridgell <tridge@samba.org>
+# Copyright (C) 2008 Andrew Bartlett <abartlet@samba.org>
+# Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
+# based on test_smbclient.sh
+
+# 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/>.
+#
+
+"""Blackbox tests for ndrdump."""
+
+import os
+from samba.tests import BlackboxTestCase
+
+for p in [ "../../../../../source4/librpc/tests", "../../../../../librpc/tests"]:
+ data_path_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), p))
+ print data_path_dir
+ if os.path.exists(data_path_dir):
+ break
+
+
+class NdrDumpTests(BlackboxTestCase):
+ """Blackbox tests for ndrdump."""
+
+ def data_path(self, name):
+ return os.path.join(data_path_dir, name)
+
+ def test_ndrdump_with_in(self):
+ self.check_run("ndrdump samr samr_CreateUser in %s" % (self.data_path("samr-CreateUser-in.dat")))
+
+ def test_ndrdump_with_out(self):
+ self.check_run("ndrdump samr samr_CreateUser out %s" % (self.data_path("samr-CreateUser-out.dat")))
+
+ def test_ndrdump_context_file(self):
+ self.check_run("ndrdump --context-file %s samr samr_CreateUser out %s" % (self.data_path("samr-CreateUser-in.dat"), self.data_path("samr-CreateUser-out.dat")))
+
+ def test_ndrdump_with_validate(self):
+ self.check_run("ndrdump --validate samr samr_CreateUser in %s" % (self.data_path("samr-CreateUser-in.dat")))
diff --git a/python/samba/tests/blackbox/samba_tool_drs.py b/python/samba/tests/blackbox/samba_tool_drs.py
new file mode 100644
index 00000000000..62d7bf123bb
--- /dev/null
+++ b/python/samba/tests/blackbox/samba_tool_drs.py
@@ -0,0 +1,97 @@
+# Blackbox tests for "samba-tool drs" command
+# Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2011
+#
+# 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/>.
+#
+
+"""Blackbox tests for samba-tool drs."""
+
+import samba.tests
+
+
+class SambaToolDrsTests(samba.tests.BlackboxTestCase):
+ """Blackbox test case for samba-tool drs."""
+
+ def setUp(self):
+ super(SambaToolDrsTests, self).setUp()
+
+ self.dc1 = samba.tests.env_get_var_value("DC1")
+ self.dc2 = samba.tests.env_get_var_value("DC2")
+
+ creds = self.get_credentials()
+ self.cmdline_creds = "-U%s/%s%%%s" % (creds.get_domain(),
+ creds.get_username(), creds.get_password())
+
+ def _get_rootDSE(self, dc):
+ samdb = samba.tests.connect_samdb(dc, lp=self.get_loadparm(),
+ credentials=self.get_credentials(),
+ ldap_only=True)
+ return samdb.search(base="", scope=samba.tests.ldb.SCOPE_BASE)[0]
+
+ def test_samba_tool_bind(self):
+ """Tests 'samba-tool drs bind' command
+ Output should be like:
+ Extensions supported:
+ <list-of-supported-extensions>
+ Site GUID: <GUID>
+ Repl epoch: 0"""
+ out = self.check_output("samba-tool drs bind %s %s" % (self.dc1,
+ self.cmdline_creds))
+ self.assertTrue("Site GUID:" in out)
+ self.assertTrue("Repl epoch:" in out)
+
+ def test_samba_tool_kcc(self):
+ """Tests 'samba-tool drs kcc' command
+ Output should be like 'Consistency check on <DC> successful.'"""
+ out = self.check_output("samba-tool drs kcc %s %s" % (self.dc1,
+ self.cmdline_creds))
+ self.assertTrue("Consistency check on" in out)
+ self.assertTrue("successful" in out)
+
+ def test_samba_tool_showrepl(self):
+ """Tests 'samba-tool drs showrepl' command
+ Output should be like:
+ <site-name>/<domain-name>
+ DSA Options: <hex-options>
+ DSA object GUID: <DSA-object-GUID>
+ DSA invocationId: <DSA-invocationId>
+ <Inbound-connections-list>
+ <Outbound-connections-list>
+ <KCC-objects>
+ ...
+ TODO: Perhaps we should check at least for
+ DSA's objectGUDI and invocationId"""
+ out = self.check_output("samba-tool drs showrepl %s %s" % (self.dc1,
+ self.cmdline_creds))
+ self.assertTrue("DSA Options:" in out)
+ self.assertTrue("DSA object GUID:" in out)
+ self.assertTrue("DSA invocationId:" in out)
+
+ def test_samba_tool_options(self):
+ """Tests 'samba-tool drs options' command
+ Output should be like 'Current DSA options: IS_GC <OTHER_FLAGS>'"""
+ out = self.check_output("samba-tool drs options %s %s" % (self.dc1,
+ self.cmdline_creds))
+ self.assertTrue("Current DSA options:" in out)
+
+ def test_samba_tool_replicate(self):
+ """Tests 'samba-tool drs replicate' command
+ Output should be like 'Replicate from <DC-SRC> to <DC-DEST> was successful.'"""
+ nc_name = self._get_rootDSE(self.dc1)["defaultNamingContext"]
+ out = self.check_output("samba-tool drs replicate %s %s %s %s" % (self.dc1,
+ self.dc2,
+ nc_name,
+ self.cmdline_creds))
+ self.assertTrue("Replicate from" in out)
+ self.assertTrue("was successful" in out)
diff --git a/python/samba/tests/common.py b/python/samba/tests/common.py
new file mode 100644
index 00000000000..8794e9dc8b3
--- /dev/null
+++ b/python/samba/tests/common.py
@@ -0,0 +1,40 @@
+# Unix SMB/CIFS implementation. Tests for common.py routines
+# Copyright (C) Andrew Tridgell 2011
+#
+# 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/>.
+#
+
+"""Tests for samba.common"""
+
+import samba, os
+import samba.tests
+from samba.common import *
+from samba.samdb import SamDB
+
+
+class CommonTests(samba.tests.TestCase):
+
+ def test_normalise_int32(self):
+ self.assertEquals('17', normalise_int32(17))
+ self.assertEquals('17', normalise_int32('17'))
+ self.assertEquals('-123', normalise_int32('-123'))
+ self.assertEquals('-1294967296', normalise_int32('3000000000'))
+
+ def test_dsdb_Dn(self):
+ sam = samba.Ldb(url='dntest.ldb')
+ dn1 = dsdb_Dn(sam, "DC=foo,DC=bar")
+ dn2 = dsdb_Dn(sam, "B:8:0000000D:<GUID=b3f0ec29-17f4-452a-b002-963e1909d101>;DC=samba,DC=example,DC=com")
+ self.assertEquals(dn2.binary, "0000000D")
+ self.assertEquals(13, dn2.get_binary_integer())
+ os.unlink('dntest.ldb')
diff --git a/python/samba/tests/core.py b/python/samba/tests/core.py
new file mode 100644
index 00000000000..8206e68d4fb
--- /dev/null
+++ b/python/samba/tests/core.py
@@ -0,0 +1,63 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
+#
+# 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/>.
+#
+
+"""Samba Python tests."""
+
+import ldb
+import os
+import samba
+from samba.tests import TestCase, TestCaseInTempDir
+
+class SubstituteVarTestCase(TestCase):
+
+ def test_empty(self):
+ self.assertEquals("", samba.substitute_var("", {}))
+
+ def test_nothing(self):
+ self.assertEquals("foo bar",
+ samba.substitute_var("foo bar", {"bar": "bla"}))
+
+ def test_replace(self):
+ self.assertEquals("foo bla",
+ samba.substitute_var("foo ${bar}", {"bar": "bla"}))
+
+ def test_broken(self):
+ self.assertEquals("foo ${bdkjfhsdkfh sdkfh ",
+ samba.substitute_var("foo ${bdkjfhsdkfh sdkfh ", {"bar": "bla"}))
+
+ def test_unknown_var(self):
+ self.assertEquals("foo ${bla} gsff",
+ samba.substitute_var("foo ${bla} gsff", {"bar": "bla"}))
+
+ def test_check_all_substituted(self):
+ samba.check_all_substituted("nothing to see here")
+ self.assertRaises(Exception, samba.check_all_substituted,
+ "Not subsituted: ${FOOBAR}")
+
+
+class LdbExtensionTests(TestCaseInTempDir):
+
+ def test_searchone(self):
+ path = self.tempdir + "/searchone.ldb"
+ l = samba.Ldb(path)
+ try:
+ l.add({"dn": "foo=dc", "bar": "bla"})
+ self.assertEquals("bla",
+ l.searchone(basedn=ldb.Dn(l, "foo=dc"), attribute="bar"))
+ finally:
+ del l
+ os.unlink(path)
diff --git a/python/samba/tests/credentials.py b/python/samba/tests/credentials.py
new file mode 100644
index 00000000000..95ee0fa0deb
--- /dev/null
+++ b/python/samba/tests/credentials.py
@@ -0,0 +1,98 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+#
+# 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/>.
+#
+
+"""Tests for the Credentials Python bindings.
+
+Note that this just tests the bindings work. It does not intend to test
+the functionality, that's already done in other tests.
+"""
+
+from samba import credentials
+import samba.tests
+
+class CredentialsTests(samba.tests.TestCase):
+
+ def setUp(self):
+ super(CredentialsTests, self).setUp()
+ self.creds = credentials.Credentials()
+
+ def test_set_username(self):
+ self.creds.set_username("somebody")
+ self.assertEquals("somebody", self.creds.get_username())
+
+ def test_set_password(self):
+ self.creds.set_password("S3CreT")
+ self.assertEquals("S3CreT", self.creds.get_password())
+
+ def test_set_domain(self):
+ self.creds.set_domain("ABMAS")
+ self.assertEquals("ABMAS", self.creds.get_domain())
+
+ def test_set_realm(self):
+ self.creds.set_realm("myrealm")
+ self.assertEquals("MYREALM", self.creds.get_realm())
+
+ def test_parse_string_anon(self):
+ self.creds.parse_string("%")
+ self.assertEquals("", self.creds.get_username())
+ self.assertEquals(None, self.creds.get_password())
+
+ def test_parse_string_user_pw_domain(self):
+ self.creds.parse_string("dom\\someone%secr")
+ self.assertEquals("someone", self.creds.get_username())
+ self.assertEquals("secr", self.creds.get_password())
+ self.assertEquals("DOM", self.creds.get_domain())
+
+ def test_bind_dn(self):
+ self.assertEquals(None, self.creds.get_bind_dn())
+ self.creds.set_bind_dn("dc=foo,cn=bar")
+ self.assertEquals("dc=foo,cn=bar", self.creds.get_bind_dn())
+
+ def test_is_anon(self):
+ self.creds.set_username("")
+ self.assertTrue(self.creds.is_anonymous())
+ self.creds.set_username("somebody")
+ self.assertFalse(self.creds.is_anonymous())
+ self.creds.set_anonymous()
+ self.assertTrue(self.creds.is_anonymous())
+
+ def test_workstation(self):
+ # FIXME: This is uninitialised, it should be None
+ #self.assertEquals(None, self.creds.get_workstation())
+ self.creds.set_workstation("myworksta")
+ self.assertEquals("myworksta", self.creds.get_workstation())
+
+ def test_get_nt_hash(self):
+ self.creds.set_password("geheim")
+ self.assertEquals('\xc2\xae\x1f\xe6\xe6H\x84cRE>\x81o*\xeb\x93',
+ self.creds.get_nt_hash())
+
+ def test_guess(self):
+ # Just check the method is there and doesn't raise an exception
+ self.creds.guess()
+
+ def test_set_cmdline_callbacks(self):
+ self.creds.set_cmdline_callbacks()
+
+ def test_authentication_requested(self):
+ self.creds.set_username("")
+ self.assertFalse(self.creds.authentication_requested())
+ self.creds.set_username("somebody")
+ self.assertTrue(self.creds.authentication_requested())
+
+ def test_wrong_password(self):
+ self.assertFalse(self.creds.wrong_password())
diff --git a/python/samba/tests/dcerpc/__init__.py b/python/samba/tests/dcerpc/__init__.py
new file mode 100644
index 00000000000..d84cb57a096
--- /dev/null
+++ b/python/samba/tests/dcerpc/__init__.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+#
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# 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/>.
+
+"""Tests for the DCE/RPC Python bindings."""
+
diff --git a/python/samba/tests/dcerpc/bare.py b/python/samba/tests/dcerpc/bare.py
new file mode 100644
index 00000000000..3efbf9d4cf3
--- /dev/null
+++ b/python/samba/tests/dcerpc/bare.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+#
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# 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/>.
+#
+
+"""Tests for samba.tests.dcerpc.bare."""
+
+from samba.dcerpc import ClientConnection
+import samba.tests
+
+class BareTestCase(samba.tests.TestCase):
+
+ def test_bare(self):
+ # Connect to the echo pipe
+ x = ClientConnection("ncalrpc:localhost[DEFAULT]",
+ ("60a15ec5-4de8-11d7-a637-005056a20182", 1),
+ lp_ctx=samba.tests.env_loadparm())
+ self.assertEquals("\x01\x00\x00\x00", x.request(0, chr(0) * 4))
+
+ def test_alter_context(self):
+ x = ClientConnection("ncalrpc:localhost[DEFAULT]",
+ ("12345778-1234-abcd-ef00-0123456789ac", 1),
+ lp_ctx=samba.tests.env_loadparm())
+ y = ClientConnection("ncalrpc:localhost",
+ ("60a15ec5-4de8-11d7-a637-005056a20182", 1),
+ basis_connection=x, lp_ctx=samba.tests.env_loadparm())
+ x.alter_context(("60a15ec5-4de8-11d7-a637-005056a20182", 1))
+ # FIXME: self.assertEquals("\x01\x00\x00\x00", x.request(0, chr(0) * 4))
+
+ def test_two_connections(self):
+ x = ClientConnection("ncalrpc:localhost[DEFAULT]",
+ ("60a15ec5-4de8-11d7-a637-005056a20182", 1),
+ lp_ctx=samba.tests.env_loadparm())
+ y = ClientConnection("ncalrpc:localhost",
+ ("60a15ec5-4de8-11d7-a637-005056a20182", 1),
+ basis_connection=x, lp_ctx=samba.tests.env_loadparm())
+ self.assertEquals("\x01\x00\x00\x00", y.request(0, chr(0) * 4))
diff --git a/python/samba/tests/dcerpc/dnsserver.py b/python/samba/tests/dcerpc/dnsserver.py
new file mode 100644
index 00000000000..59d6eee7618
--- /dev/null
+++ b/python/samba/tests/dcerpc/dnsserver.py
@@ -0,0 +1,241 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Amitay Isaacs <amitay@gmail.com> 2011
+#
+# 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/>.
+#
+
+"""Tests for samba.dcerpc.dnsserver"""
+
+from samba.dcerpc import dnsp, dnsserver
+from samba.tests import RpcInterfaceTestCase, env_get_var_value
+from samba.netcmd.dns import ARecord
+
+class DnsserverTests(RpcInterfaceTestCase):
+
+ def setUp(self):
+ super(DnsserverTests, self).setUp()
+ self.server = env_get_var_value("SERVER_IP")
+ self.zone = env_get_var_value("REALM").lower()
+ self.conn = dnsserver.dnsserver("ncacn_ip_tcp:%s" % (self.server),
+ self.get_loadparm(),
+ self.get_credentials())
+
+ def test_operation2(self):
+ pass
+
+
+ def test_query2(self):
+ typeid, result = self.conn.DnssrvQuery2(dnsserver.DNS_CLIENT_VERSION_W2K,
+ 0,
+ self.server,
+ None,
+ 'ServerInfo')
+ self.assertEquals(dnsserver.DNSSRV_TYPEID_SERVER_INFO_W2K, typeid)
+
+ typeid, result = self.conn.DnssrvQuery2(dnsserver.DNS_CLIENT_VERSION_DOTNET,
+ 0,
+ self.server,
+ None,
+ 'ServerInfo')
+ self.assertEquals(dnsserver.DNSSRV_TYPEID_SERVER_INFO_DOTNET, typeid)
+
+ typeid, result = self.conn.DnssrvQuery2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
+ 0,
+ self.server,
+ None,
+ 'ServerInfo')
+ self.assertEquals(dnsserver.DNSSRV_TYPEID_SERVER_INFO, typeid)
+
+ def test_operation2(self):
+ client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
+ rev_zone = '1.168.192.in-addr.arpa'
+
+ zone_create = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN()
+ zone_create.pszZoneName = rev_zone
+ zone_create.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
+ zone_create.fAllowUpdate = dnsp.DNS_ZONE_UPDATE_SECURE
+ zone_create.fAging = 0
+ zone_create.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
+
+ # Create zone
+ self.conn.DnssrvOperation2(client_version,
+ 0,
+ self.server,
+ None,
+ 0,
+ 'ZoneCreate',
+ dnsserver.DNSSRV_TYPEID_ZONE_CREATE,
+ zone_create)
+
+ request_filter = (dnsserver.DNS_ZONE_REQUEST_REVERSE |
+ dnsserver.DNS_ZONE_REQUEST_PRIMARY)
+ typeid, zones = self.conn.DnssrvComplexOperation2(client_version,
+ 0,
+ self.server,
+ None,
+ 'EnumZones',
+ dnsserver.DNSSRV_TYPEID_DWORD,
+ request_filter)
+ self.assertEquals(1, zones.dwZoneCount)
+
+ # Delete zone
+ self.conn.DnssrvOperation2(client_version,
+ 0,
+ self.server,
+ rev_zone,
+ 0,
+ 'DeleteZoneFromDs',
+ dnsserver.DNSSRV_TYPEID_NULL,
+ None)
+
+ typeid, zones = self.conn.DnssrvComplexOperation2(client_version,
+ 0,
+ self.server,
+ None,
+ 'EnumZones',
+ dnsserver.DNSSRV_TYPEID_DWORD,
+ request_filter)
+ self.assertEquals(0, zones.dwZoneCount)
+
+
+ def test_complexoperation2(self):
+ client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
+ request_filter = (dnsserver.DNS_ZONE_REQUEST_FORWARD |
+ dnsserver.DNS_ZONE_REQUEST_PRIMARY)
+ typeid, zones = self.conn.DnssrvComplexOperation2(client_version,
+ 0,
+ self.server,
+ None,
+ 'EnumZones',
+ dnsserver.DNSSRV_TYPEID_DWORD,
+ request_filter)
+ self.assertEquals(dnsserver.DNSSRV_TYPEID_ZONE_LIST, typeid)
+ self.assertEquals(2, zones.dwZoneCount)
+
+ request_filter = (dnsserver.DNS_ZONE_REQUEST_REVERSE |
+ dnsserver.DNS_ZONE_REQUEST_PRIMARY)
+ typeid, zones = self.conn.DnssrvComplexOperation2(client_version,
+ 0,
+ self.server,
+ None,
+ 'EnumZones',
+ dnsserver.DNSSRV_TYPEID_DWORD,
+ request_filter)
+ self.assertEquals(dnsserver.DNSSRV_TYPEID_ZONE_LIST, typeid)
+ self.assertEquals(0, zones.dwZoneCount)
+
+
+ def test_enumrecords2(self):
+ client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
+ record_type = dnsp.DNS_TYPE_NS
+ select_flags = (dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA |
+ dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA)
+ buflen, roothints = self.conn.DnssrvEnumRecords2(client_version,
+ 0,
+ self.server,
+ '..RootHints',
+ '.',
+ None,
+ record_type,
+ select_flags,
+ None,
+ None)
+ self.assertEquals(14, roothints.count) # 1 NS + 13 A records (a-m)
+
+
+ def test_updaterecords2(self):
+ client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
+ record_type = dnsp.DNS_TYPE_A
+ select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
+
+ name = 'dummy'
+ rec = ARecord('1.2.3.4')
+ rec2 = ARecord('5.6.7.8')
+
+ # Add record
+ add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
+ add_rec_buf.rec = rec
+ self.conn.DnssrvUpdateRecord2(client_version,
+ 0,
+ self.server,
+ self.zone,
+ name,
+ add_rec_buf,
+ None)
+
+ buflen, result = self.conn.DnssrvEnumRecords2(client_version,
+ 0,
+ self.server,
+ self.zone,
+ name,
+ None,
+ record_type,
+ select_flags,
+ None,
+ None)
+ self.assertEquals(1, result.count)
+ self.assertEquals(1, result.rec[0].wRecordCount)
+ self.assertEquals(dnsp.DNS_TYPE_A, result.rec[0].records[0].wType)
+ self.assertEquals('1.2.3.4', result.rec[0].records[0].data)
+
+ # Update record
+ add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
+ add_rec_buf.rec = rec2
+ del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
+ del_rec_buf.rec = rec
+ self.conn.DnssrvUpdateRecord2(client_version,
+ 0,
+ self.server,
+ self.zone,
+ name,
+ add_rec_buf,
+ del_rec_buf)
+
+ buflen, result = self.conn.DnssrvEnumRecords2(client_version,
+ 0,
+ self.server,
+ self.zone,
+ name,
+ None,
+ record_type,
+ select_flags,
+ None,
+ None)
+ self.assertEquals(1, result.count)
+ self.assertEquals(1, result.rec[0].wRecordCount)
+ self.assertEquals(dnsp.DNS_TYPE_A, result.rec[0].records[0].wType)
+ self.assertEquals('5.6.7.8', result.rec[0].records[0].data)
+
+ # Delete record
+ del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
+ del_rec_buf.rec = rec2
+ self.conn.DnssrvUpdateRecord2(client_version,
+ 0,
+ self.server,
+ self.zone,
+ name,
+ None,
+ del_rec_buf)
+
+ self.assertRaises(RuntimeError, self.conn.DnssrvEnumRecords2,
+ client_version,
+ 0,
+ self.server,
+ self.zone,
+ name,
+ None,
+ record_type,
+ select_flags,
+ None,
+ None)
diff --git a/python/samba/tests/dcerpc/misc.py b/python/samba/tests/dcerpc/misc.py
new file mode 100644
index 00000000000..11e14aadfa7
--- /dev/null
+++ b/python/samba/tests/dcerpc/misc.py
@@ -0,0 +1,62 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+#
+# 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/>.
+#
+
+"""Tests for samba.dcerpc.misc."""
+
+from samba.dcerpc import misc
+import samba.tests
+
+text1 = "76f53846-a7c2-476a-ae2c-20e2b80d7b34"
+text2 = "344edffa-330a-4b39-b96e-2c34da52e8b1"
+
+class GUIDTests(samba.tests.TestCase):
+
+ def test_str(self):
+ guid = misc.GUID(text1)
+ self.assertEquals(text1, str(guid))
+
+ def test_repr(self):
+ guid = misc.GUID(text1)
+ self.assertEquals("GUID('%s')" % text1, repr(guid))
+
+ def test_compare_different(self):
+ guid1 = misc.GUID(text1)
+ guid2 = misc.GUID(text2)
+ self.assertTrue(cmp(guid1, guid2) > 0)
+
+ def test_compare_same(self):
+ guid1 = misc.GUID(text1)
+ guid2 = misc.GUID(text1)
+ self.assertEquals(0, cmp(guid1, guid2))
+ self.assertEquals(guid1, guid2)
+
+
+class PolicyHandleTests(samba.tests.TestCase):
+
+ def test_init(self):
+ x = misc.policy_handle(text1, 1)
+ self.assertEquals(1, x.handle_type)
+ self.assertEquals(text1, str(x.uuid))
+
+ def test_repr(self):
+ x = misc.policy_handle(text1, 42)
+ self.assertEquals("policy_handle(%d, '%s')" % (42, text1), repr(x))
+
+ def test_str(self):
+ x = misc.policy_handle(text1, 42)
+ self.assertEquals("%d, %s" % (42, text1), str(x))
+
diff --git a/python/samba/tests/dcerpc/registry.py b/python/samba/tests/dcerpc/registry.py
new file mode 100644
index 00000000000..c7bcbfd530a
--- /dev/null
+++ b/python/samba/tests/dcerpc/registry.py
@@ -0,0 +1,51 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# 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/>.
+#
+
+"""Tests for samba.dcerpc.registry."""
+
+from samba.dcerpc import winreg
+from samba.tests import RpcInterfaceTestCase
+
+
+class WinregTests(RpcInterfaceTestCase):
+
+ def setUp(self):
+ super(WinregTests, self).setUp()
+ self.conn = winreg.winreg("ncalrpc:", self.get_loadparm(),
+ self.get_credentials())
+
+ def get_hklm(self):
+ return self.conn.OpenHKLM(None,
+ winreg.KEY_QUERY_VALUE | winreg.KEY_ENUMERATE_SUB_KEYS)
+
+ def test_hklm(self):
+ handle = self.conn.OpenHKLM(None,
+ winreg.KEY_QUERY_VALUE | winreg.KEY_ENUMERATE_SUB_KEYS)
+ self.conn.CloseKey(handle)
+
+ def test_getversion(self):
+ handle = self.get_hklm()
+ version = self.conn.GetVersion(handle)
+ self.assertEquals(int, version.__class__)
+ self.conn.CloseKey(handle)
+
+ def test_getkeyinfo(self):
+ handle = self.conn.OpenHKLM(None,
+ winreg.KEY_QUERY_VALUE | winreg.KEY_ENUMERATE_SUB_KEYS)
+ x = self.conn.QueryInfoKey(handle, winreg.String())
+ self.assertEquals(9, len(x)) # should return a 9-tuple
+ self.conn.CloseKey(handle)
diff --git a/python/samba/tests/dcerpc/rpc_talloc.py b/python/samba/tests/dcerpc/rpc_talloc.py
new file mode 100644
index 00000000000..c091f26c1b9
--- /dev/null
+++ b/python/samba/tests/dcerpc/rpc_talloc.py
@@ -0,0 +1,84 @@
+# test generated python code from pidl
+# Copyright (C) Andrew Tridgell August 2010
+#
+# 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/>.
+#
+#
+# to run this test, use one of these:
+#
+# python -m testtools.run samba.tests.dcerpc.rpc_talloc
+#
+# or if you have trial installed (from twisted), use
+#
+# trial samba.tests.dcerpc.rpc_talloc
+
+"""Tests for the talloc handling in the generated Python DCE/RPC bindings."""
+
+import sys
+
+sys.path.insert(0, "bin/python")
+
+import samba
+import samba.tests
+from samba.dcerpc import drsuapi
+import talloc
+
+talloc.enable_null_tracking()
+
+
+class TallocTests(samba.tests.TestCase):
+ '''test talloc behaviour of pidl generated python code'''
+
+ def check_blocks(self, object, num_expected):
+ '''check that the number of allocated blocks is correct'''
+ nblocks = talloc.total_blocks(object)
+ if object is None:
+ nblocks -= self.initial_blocks
+ self.assertEquals(nblocks, num_expected)
+
+ def get_rodc_partial_attribute_set(self):
+ '''get a list of attributes for RODC replication'''
+ partial_attribute_set = drsuapi.DsPartialAttributeSet()
+
+ # we expect one block for the object, and one for the structure
+ self.check_blocks(partial_attribute_set, 2)
+
+ attids = [1, 2, 3]
+ partial_attribute_set.version = 1
+ partial_attribute_set.attids = attids
+ partial_attribute_set.num_attids = len(attids)
+
+ # we expect one block object, a structure, an ARRAY, and a
+ # reference to the array
+ self.check_blocks(partial_attribute_set, 3)
+
+ return partial_attribute_set
+
+ def pas_test(self):
+ pas = self.get_rodc_partial_attribute_set()
+ self.check_blocks(pas, 3)
+ req8 = drsuapi.DsGetNCChangesRequest8()
+ self.check_blocks(req8, 2)
+ self.check_blocks(None, 5)
+ req8.partial_attribute_set = pas
+ if req8.partial_attribute_set.attids[1] != 2:
+ raise Exception("Wrong value in attids[2]")
+ # we now get an additional reference
+ self.check_blocks(None, 6)
+
+ def test_run(self):
+ self.initial_blocks = talloc.total_blocks(None)
+ self.check_blocks(None, 0)
+ self.pas_test()
+ self.check_blocks(None, 0)
diff --git a/python/samba/tests/dcerpc/rpcecho.py b/python/samba/tests/dcerpc/rpcecho.py
new file mode 100644
index 00000000000..099f8f619ce
--- /dev/null
+++ b/python/samba/tests/dcerpc/rpcecho.py
@@ -0,0 +1,71 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# 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/>.
+#
+
+"""Tests for samba.dceprc.rpcecho."""
+
+from samba.dcerpc import echo
+from samba.ndr import ndr_pack, ndr_unpack
+from samba.tests import RpcInterfaceTestCase, TestCase
+
+
+class RpcEchoTests(RpcInterfaceTestCase):
+
+ def setUp(self):
+ super(RpcEchoTests, self).setUp()
+ self.conn = echo.rpcecho("ncalrpc:", self.get_loadparm())
+
+ def test_two_contexts(self):
+ self.conn2 = echo.rpcecho("ncalrpc:", self.get_loadparm(), basis_connection=self.conn)
+ self.assertEquals(3, self.conn2.AddOne(2))
+
+ def test_abstract_syntax(self):
+ self.assertEquals(("60a15ec5-4de8-11d7-a637-005056a20182", 1),
+ self.conn.abstract_syntax)
+
+ def test_addone(self):
+ self.assertEquals(2, self.conn.AddOne(1))
+
+ def test_echodata(self):
+ self.assertEquals([1,2,3], self.conn.EchoData([1, 2, 3]))
+
+ def test_call(self):
+ self.assertEquals(u"foobar", self.conn.TestCall(u"foobar"))
+
+ def test_surrounding(self):
+ surrounding_struct = echo.Surrounding()
+ surrounding_struct.x = 4
+ surrounding_struct.surrounding = [1,2,3,4]
+ y = self.conn.TestSurrounding(surrounding_struct)
+ self.assertEquals(8 * [0], y.surrounding)
+
+ def test_manual_request(self):
+ self.assertEquals("\x01\x00\x00\x00", self.conn.request(0, chr(0) * 4))
+
+ def test_server_name(self):
+ self.assertEquals(None, self.conn.server_name)
+
+
+class NdrEchoTests(TestCase):
+
+ def test_info1_push(self):
+ x = echo.info1()
+ x.v = 42
+ self.assertEquals("\x2a", ndr_pack(x))
+
+ def test_info1_pull(self):
+ x = ndr_unpack(echo.info1, "\x42")
+ self.assertEquals(x.v, 66)
diff --git a/python/samba/tests/dcerpc/sam.py b/python/samba/tests/dcerpc/sam.py
new file mode 100644
index 00000000000..0e09323adbe
--- /dev/null
+++ b/python/samba/tests/dcerpc/sam.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+#
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# 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/>.
+#
+
+"""Tests for samba.dcerpc.sam."""
+
+from samba.dcerpc import samr, security
+from samba.tests import RpcInterfaceTestCase
+
+# FIXME: Pidl should be doing this for us
+def toArray((handle, array, num_entries)):
+ ret = []
+ for x in range(num_entries):
+ ret.append((array.entries[x].idx, array.entries[x].name))
+ return ret
+
+
+class SamrTests(RpcInterfaceTestCase):
+
+ def setUp(self):
+ super(SamrTests, self).setUp()
+ self.conn = samr.samr("ncalrpc:", self.get_loadparm())
+
+ def test_connect5(self):
+ (level, info, handle) = self.conn.Connect5(None, 0, 1, samr.ConnectInfo1())
+
+ def test_connect2(self):
+ handle = self.conn.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
+ self.assertTrue(handle is not None)
+
+ def test_EnumDomains(self):
+ handle = self.conn.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
+ domains = toArray(self.conn.EnumDomains(handle, 0, -1))
+ self.conn.Close(handle)
+
diff --git a/python/samba/tests/dcerpc/srvsvc.py b/python/samba/tests/dcerpc/srvsvc.py
new file mode 100644
index 00000000000..3206a27e678
--- /dev/null
+++ b/python/samba/tests/dcerpc/srvsvc.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+#
+# Unix SMB/CIFS implementation.
+# Copyright © Dhananjay Sathe <dhanajaysathe@gmail.com> 2011
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 2011
+#
+# 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/>.
+#
+
+"""Tests for samba.dcerpc.srvsvc."""
+
+from samba.dcerpc import srvsvc
+from samba.tests import RpcInterfaceTestCase
+
+
+class SrvsvcTests(RpcInterfaceTestCase):
+
+ def setUp(self):
+ super(SrvsvcTests, self).setUp()
+ self.conn = srvsvc.srvsvc("ncalrpc:", self.get_loadparm())
+ self.server_unc = "\\\\."
+
+ def getDummyShareObject(self):
+ share = srvsvc.NetShareInfo2()
+
+ share.name = u'test'
+ share.comment = u'test share'
+ share.type = srvsvc.STYPE_DISKTREE
+ share.current_users = 0x00000000
+ share.max_users = -1
+ share.password = None
+ share.path = u'C:\\tmp' # some random path
+ share.permissions = 123434566
+ return share
+
+ def test_NetShareAdd(self):
+ self.skip("Dangerous test")
+ share = self.getDummyShareObject()
+ self.conn.NetShareAdd(self.server_unc, 2, share, None)
+
+ def test_NetShareSetInfo(self):
+ self.skip("Dangerous test")
+ share = self.getDummyShareObject()
+ parm_error = 0x00000000
+ self.conn.NetShareAdd(self.server_unc, 502, share, parm_error)
+ name = share.name
+ share.comment = "now sucessfully modified "
+ parm_error = self.pipe.NetShareSetInfo(self.server_unc, name,
+ 502, share, parm_error)
+
+ def test_NetShareDel(self):
+ self.skip("Dangerous test")
+ share = self.getDummyShareObject()
+ parm_error = 0x00000000
+ self.expectFailure("NetShareAdd doesn't work properly from Python",
+ self.conn.NetShareAdd, self.server_unc, 502, share, parm_error)
+ self.conn.NetShareDel(self.server_unc, share.name, 0)
diff --git a/python/samba/tests/dcerpc/testrpc.py b/python/samba/tests/dcerpc/testrpc.py
new file mode 100644
index 00000000000..e35d6b55446
--- /dev/null
+++ b/python/samba/tests/dcerpc/testrpc.py
@@ -0,0 +1,141 @@
+# test generated python code from pidl
+# Copyright (C) Andrew Tridgell August 2010
+#
+# 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 sys
+
+sys.path.insert(0, "bin/python")
+
+import samba
+import samba.tests
+from samba.dcerpc import drsuapi
+import talloc
+
+talloc.enable_null_tracking()
+
+class RpcTests(object):
+ '''test type behaviour of pidl generated python RPC code'''
+
+ def check_blocks(self, object, num_expected):
+ '''check that the number of allocated blocks is correct'''
+ nblocks = talloc.total_blocks(object)
+ if object is None:
+ nblocks -= self.initial_blocks
+ leaked_blocks = (nblocks - num_expected)
+ if leaked_blocks != 0:
+ print "Leaked %d blocks" % leaked_blocks
+
+ def check_type(self, interface, typename, type):
+ print "Checking type %s" % typename
+ v = type()
+ for n in dir(v):
+ if n[0] == '_':
+ continue
+ try:
+ value = getattr(v, n)
+ except TypeError, errstr:
+ if str(errstr) == "unknown union level":
+ print "ERROR: Unknown union level in %s.%s" % (typename, n)
+ self.errcount += 1
+ continue
+ print str(errstr)[1:21]
+ if str(errstr)[0:21] == "Can not convert C Type":
+ print "ERROR: Unknown C type for %s.%s" % (typename, n)
+ self.errcount += 1
+ continue
+ else:
+ print "ERROR: Failed to instantiate %s.%s" % (typename, n)
+ self.errcount += 1
+ continue
+ except Exception:
+ print "ERROR: Failed to instantiate %s.%s" % (typename, n)
+ self.errcount += 1
+ continue
+
+ # now try setting the value back
+ try:
+ print "Setting %s.%s" % (typename, n)
+ setattr(v, n, value)
+ except Exception, e:
+ if isinstance(e, AttributeError) and str(e).endswith("is read-only"):
+ # readonly, ignore
+ continue
+ else:
+ print "ERROR: Failed to set %s.%s: %r: %s" % (typename, n, e.__class__, e)
+ self.errcount += 1
+ continue
+
+ # and try a comparison
+ try:
+ if value != getattr(v, n):
+ print "ERROR: Comparison failed for %s.%s: %r != %r" % (typename, n, value, getattr(v, n))
+ continue
+ except Exception, e:
+ print "ERROR: compare exception for %s.%s: %r: %s" % (typename, n, e.__class__, e)
+ continue
+
+ def check_interface(self, interface, iname):
+ errcount = self.errcount
+ for n in dir(interface):
+ if n[0] == '_' or n == iname:
+ # skip the special ones
+ continue
+ value = getattr(interface, n)
+ if isinstance(value, str):
+ #print "%s=\"%s\"" % (n, value)
+ pass
+ elif isinstance(value, int) or isinstance(value, long):
+ #print "%s=%d" % (n, value)
+ pass
+ elif isinstance(value, type):
+ try:
+ initial_blocks = talloc.total_blocks(None)
+ self.check_type(interface, n, value)
+ self.check_blocks(None, initial_blocks)
+ except Exception, e:
+ print "ERROR: Failed to check_type %s.%s: %r: %s" % (iname, n, e.__class__, e)
+ self.errcount += 1
+ elif callable(value):
+ pass # Method
+ else:
+ print "UNKNOWN: %s=%s" % (n, value)
+ if self.errcount - errcount != 0:
+ print "Found %d errors in %s" % (self.errcount - errcount, iname)
+
+ def check_all_interfaces(self):
+ for iname in dir(samba.dcerpc):
+ if iname[0] == '_':
+ continue
+ if iname == 'ClientConnection' or iname == 'base':
+ continue
+ print "Checking interface %s" % iname
+ iface = getattr(samba.dcerpc, iname)
+ initial_blocks = talloc.total_blocks(None)
+ self.check_interface(iface, iname)
+ self.check_blocks(None, initial_blocks)
+
+ def run(self):
+ self.initial_blocks = talloc.total_blocks(None)
+ self.errcount = 0
+ self.check_all_interfaces()
+ return self.errcount
+
+tests = RpcTests()
+errcount = tests.run()
+if errcount == 0:
+ sys.exit(0)
+else:
+ print "%d failures" % errcount
+ sys.exit(1)
diff --git a/python/samba/tests/dcerpc/unix.py b/python/samba/tests/dcerpc/unix.py
new file mode 100644
index 00000000000..e8ef4da8630
--- /dev/null
+++ b/python/samba/tests/dcerpc/unix.py
@@ -0,0 +1,49 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# 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/>.
+#
+
+"""Tests for samba.dcerpc.unixinfo."""
+
+
+from samba.dcerpc import unixinfo
+from samba.tests import RpcInterfaceTestCase
+
+class UnixinfoTests(RpcInterfaceTestCase):
+
+ def setUp(self):
+ super(UnixinfoTests, self).setUp()
+ self.conn = unixinfo.unixinfo("ncalrpc:", self.get_loadparm())
+
+ def test_getpwuid_int(self):
+ infos = self.conn.GetPWUid(range(512))
+ self.assertEquals(512, len(infos))
+ self.assertEquals("/bin/false", infos[0].shell)
+ self.assertTrue(isinstance(infos[0].homedir, unicode))
+
+ def test_getpwuid(self):
+ infos = self.conn.GetPWUid(map(long, range(512)))
+ self.assertEquals(512, len(infos))
+ self.assertEquals("/bin/false", infos[0].shell)
+ self.assertTrue(isinstance(infos[0].homedir, unicode))
+
+ def test_gidtosid(self):
+ self.conn.GidToSid(1000L)
+
+ def test_uidtosid(self):
+ self.conn.UidToSid(1000)
+
+ def test_uidtosid_fail(self):
+ self.assertRaises(TypeError, self.conn.UidToSid, "100")
diff --git a/python/samba/tests/dns.py b/python/samba/tests/dns.py
new file mode 100644
index 00000000000..49d699edb78
--- /dev/null
+++ b/python/samba/tests/dns.py
@@ -0,0 +1,622 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Kai Blin <kai@samba.org> 2011
+#
+# 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 struct
+import random
+from samba import socket
+import samba.ndr as ndr
+import samba.dcerpc.dns as dns
+from samba.tests import TestCase
+
+class DNSTest(TestCase):
+
+ def errstr(self, errcode):
+ "Return a readable error code"
+ string_codes = [
+ "OK",
+ "FORMERR",
+ "SERVFAIL",
+ "NXDOMAIN",
+ "NOTIMP",
+ "REFUSED",
+ "YXDOMAIN",
+ "YXRRSET",
+ "NXRRSET",
+ "NOTAUTH",
+ "NOTZONE",
+ ]
+
+ return string_codes[errcode]
+
+
+ def assert_dns_rcode_equals(self, packet, rcode):
+ "Helper function to check return code"
+ p_errcode = packet.operation & 0x000F
+ self.assertEquals(p_errcode, rcode, "Expected RCODE %s, got %s" %
+ (self.errstr(rcode), self.errstr(p_errcode)))
+
+ def assert_dns_opcode_equals(self, packet, opcode):
+ "Helper function to check opcode"
+ p_opcode = packet.operation & 0x7800
+ self.assertEquals(p_opcode, opcode, "Expected OPCODE %s, got %s" %
+ (opcode, p_opcode))
+
+ def make_name_packet(self, opcode, qid=None):
+ "Helper creating a dns.name_packet"
+ p = dns.name_packet()
+ if qid is None:
+ p.id = random.randint(0x0, 0xffff)
+ p.operation = opcode
+ p.questions = []
+ return p
+
+ def finish_name_packet(self, packet, questions):
+ "Helper to finalize a dns.name_packet"
+ packet.qdcount = len(questions)
+ packet.questions = questions
+
+ def make_name_question(self, name, qtype, qclass):
+ "Helper creating a dns.name_question"
+ q = dns.name_question()
+ q.name = name
+ q.question_type = qtype
+ q.question_class = qclass
+ return q
+
+ def get_dns_domain(self):
+ "Helper to get dns domain"
+ return os.getenv('REALM', 'example.com').lower()
+
+ def dns_transaction_udp(self, packet, host=os.getenv('SERVER_IP')):
+ "send a DNS query and read the reply"
+ s = None
+ try:
+ send_packet = ndr.ndr_pack(packet)
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+ s.connect((host, 53))
+ s.send(send_packet, 0)
+ recv_packet = s.recv(2048, 0)
+ return ndr.ndr_unpack(dns.name_packet, recv_packet)
+ finally:
+ if s is not None:
+ s.close()
+
+ def dns_transaction_tcp(self, packet, host=os.getenv('SERVER_IP')):
+ "send a DNS query and read the reply"
+ s = None
+ try:
+ send_packet = ndr.ndr_pack(packet)
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+ s.connect((host, 53))
+ tcp_packet = struct.pack('!H', len(send_packet))
+ tcp_packet += send_packet
+ s.send(tcp_packet, 0)
+ recv_packet = s.recv(0xffff + 2, 0)
+ return ndr.ndr_unpack(dns.name_packet, recv_packet[2:])
+ finally:
+ if s is not None:
+ s.close()
+
+
+class TestSimpleQueries(DNSTest):
+
+ def test_one_a_query(self):
+ "create a query packet containing one query record"
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+
+ name = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+ q = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN)
+ print "asking for ", q.name
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+ self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY)
+ self.assertEquals(response.ancount, 1)
+ self.assertEquals(response.answers[0].rdata,
+ os.getenv('SERVER_IP'))
+
+ def test_one_a_query_tcp(self):
+ "create a query packet containing one query record via TCP"
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+
+ name = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+ q = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN)
+ print "asking for ", q.name
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_tcp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+ self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY)
+ self.assertEquals(response.ancount, 1)
+ self.assertEquals(response.answers[0].rdata,
+ os.getenv('SERVER_IP'))
+
+ def test_two_queries(self):
+ "create a query packet containing two query records"
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+
+ name = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+ q = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN)
+ questions.append(q)
+
+ name = "%s.%s" % ('bogusname', self.get_dns_domain())
+ q = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN)
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_FORMERR)
+
+ def test_qtype_all_query(self):
+ "create a QTYPE_ALL query"
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+
+ name = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+ q = self.make_name_question(name, dns.DNS_QTYPE_ALL, dns.DNS_QCLASS_IN)
+ print "asking for ", q.name
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_udp(p)
+
+ num_answers = 1
+ dc_ipv6 = os.getenv('SERVER_IPV6')
+ if dc_ipv6 is not None:
+ num_answers += 1
+
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+ self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY)
+ self.assertEquals(response.ancount, num_answers)
+ self.assertEquals(response.answers[0].rdata,
+ os.getenv('SERVER_IP'))
+ if dc_ipv6 is not None:
+ self.assertEquals(response.answers[1].rdata, dc_ipv6)
+
+ def test_qclass_none_query(self):
+ "create a QCLASS_NONE query"
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+
+ name = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+ q = self.make_name_question(name, dns.DNS_QTYPE_ALL, dns.DNS_QCLASS_NONE)
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_NOTIMP)
+
+# Only returns an authority section entry in BIND and Win DNS
+# FIXME: Enable one Samba implements this feature
+# def test_soa_hostname_query(self):
+# "create a SOA query for a hostname"
+# p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+# questions = []
+#
+# name = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+# q = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+# questions.append(q)
+#
+# self.finish_name_packet(p, questions)
+# response = self.dns_transaction_udp(p)
+# self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+# self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY)
+# # We don't get SOA records for single hosts
+# self.assertEquals(response.ancount, 0)
+
+ def test_soa_domain_query(self):
+ "create a SOA query for a domain"
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+
+ name = self.get_dns_domain()
+ q = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+ self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY)
+ self.assertEquals(response.ancount, 1)
+
+
+class TestDNSUpdates(DNSTest):
+
+ def test_two_updates(self):
+ "create two update requests"
+ p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+ updates = []
+
+ name = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+ u = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN)
+ updates.append(u)
+
+ name = self.get_dns_domain()
+ u = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN)
+ updates.append(u)
+
+ self.finish_name_packet(p, updates)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_FORMERR)
+
+ def test_update_wrong_qclass(self):
+ "create update with DNS_QCLASS_NONE"
+ p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+ updates = []
+
+ name = self.get_dns_domain()
+ u = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_NONE)
+ updates.append(u)
+
+ self.finish_name_packet(p, updates)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_NOTIMP)
+
+ def test_update_prereq_with_non_null_ttl(self):
+ "test update with a non-null TTL"
+ p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+ updates = []
+
+ name = self.get_dns_domain()
+
+ u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+ updates.append(u)
+ self.finish_name_packet(p, updates)
+
+ prereqs = []
+ r = dns.res_rec()
+ r.name = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+ r.rr_type = dns.DNS_QTYPE_TXT
+ r.rr_class = dns.DNS_QCLASS_NONE
+ r.ttl = 1
+ r.length = 0
+ prereqs.append(r)
+
+ p.ancount = len(prereqs)
+ p.answers = prereqs
+
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_FORMERR)
+
+# I'd love to test this one, but it segfaults. :)
+# def test_update_prereq_with_non_null_length(self):
+# "test update with a non-null length"
+# p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+# updates = []
+#
+# name = self.get_dns_domain()
+#
+# u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+# updates.append(u)
+# self.finish_name_packet(p, updates)
+#
+# prereqs = []
+# r = dns.res_rec()
+# r.name = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+# r.rr_type = dns.DNS_QTYPE_TXT
+# r.rr_class = dns.DNS_QCLASS_ANY
+# r.ttl = 0
+# r.length = 1
+# prereqs.append(r)
+#
+# p.ancount = len(prereqs)
+# p.answers = prereqs
+#
+# response = self.dns_transaction_udp(p)
+# self.assert_dns_rcode_equals(response, dns.DNS_RCODE_FORMERR)
+
+ def test_update_prereq_nonexisting_name(self):
+ "test update with a nonexisting name"
+ p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+ updates = []
+
+ name = self.get_dns_domain()
+
+ u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+ updates.append(u)
+ self.finish_name_packet(p, updates)
+
+ prereqs = []
+ r = dns.res_rec()
+ r.name = "idontexist.%s" % self.get_dns_domain()
+ r.rr_type = dns.DNS_QTYPE_TXT
+ r.rr_class = dns.DNS_QCLASS_ANY
+ r.ttl = 0
+ r.length = 0
+ prereqs.append(r)
+
+ p.ancount = len(prereqs)
+ p.answers = prereqs
+
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_NXRRSET)
+
+ def test_update_add_txt_record(self):
+ "test adding records works"
+ p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+ updates = []
+
+ name = self.get_dns_domain()
+
+ u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+ updates.append(u)
+ self.finish_name_packet(p, updates)
+
+ updates = []
+ r = dns.res_rec()
+ r.name = "textrec.%s" % self.get_dns_domain()
+ r.rr_type = dns.DNS_QTYPE_TXT
+ r.rr_class = dns.DNS_QCLASS_IN
+ r.ttl = 900
+ r.length = 0xffff
+ r.rdata = dns.txt_record()
+ r.rdata.txt = '"This is a test"'
+ updates.append(r)
+ p.nscount = len(updates)
+ p.nsrecs = updates
+
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+
+ name = "textrec.%s" % self.get_dns_domain()
+ q = self.make_name_question(name, dns.DNS_QTYPE_TXT, dns.DNS_QCLASS_IN)
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+ self.assertEquals(response.ancount, 1)
+ self.assertEquals(response.answers[0].rdata.txt, '"This is a test"')
+
+ def test_update_add_two_txt_records(self):
+ "test adding two txt records works"
+ p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+ updates = []
+
+ name = self.get_dns_domain()
+
+ u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+ updates.append(u)
+ self.finish_name_packet(p, updates)
+
+ updates = []
+ r = dns.res_rec()
+ r.name = "textrec2.%s" % self.get_dns_domain()
+ r.rr_type = dns.DNS_QTYPE_TXT
+ r.rr_class = dns.DNS_QCLASS_IN
+ r.ttl = 900
+ r.length = 0xffff
+ r.rdata = dns.txt_record()
+ r.rdata.txt = '"This is a test" "and this is a test, too"'
+ updates.append(r)
+ p.nscount = len(updates)
+ p.nsrecs = updates
+
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+
+ name = "textrec2.%s" % self.get_dns_domain()
+ q = self.make_name_question(name, dns.DNS_QTYPE_TXT, dns.DNS_QCLASS_IN)
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+ self.assertEquals(response.ancount, 1)
+ self.assertEquals(response.answers[0].rdata.txt, '"This is a test" "and this is a test, too"')
+
+ def test_delete_record(self):
+ "Test if deleting records works"
+
+ NAME = "deleterec.%s" % self.get_dns_domain()
+
+ # First, create a record to make sure we have a record to delete.
+ p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+ updates = []
+
+ name = self.get_dns_domain()
+
+ u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+ updates.append(u)
+ self.finish_name_packet(p, updates)
+
+ updates = []
+ r = dns.res_rec()
+ r.name = NAME
+ r.rr_type = dns.DNS_QTYPE_TXT
+ r.rr_class = dns.DNS_QCLASS_IN
+ r.ttl = 900
+ r.length = 0xffff
+ r.rdata = dns.txt_record()
+ r.rdata.txt = '"This is a test"'
+ updates.append(r)
+ p.nscount = len(updates)
+ p.nsrecs = updates
+
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+
+ # Now check the record is around
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+ q = self.make_name_question(NAME, dns.DNS_QTYPE_TXT, dns.DNS_QCLASS_IN)
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+
+ # Now delete the record
+ p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+ updates = []
+
+ name = self.get_dns_domain()
+
+ u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+ updates.append(u)
+ self.finish_name_packet(p, updates)
+
+ updates = []
+ r = dns.res_rec()
+ r.name = NAME
+ r.rr_type = dns.DNS_QTYPE_TXT
+ r.rr_class = dns.DNS_QCLASS_NONE
+ r.ttl = 0
+ r.length = 0xffff
+ r.rdata = dns.txt_record()
+ r.rdata.txt = '"This is a test"'
+ updates.append(r)
+ p.nscount = len(updates)
+ p.nsrecs = updates
+
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+
+ # And finally check it's gone
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+
+ q = self.make_name_question(NAME, dns.DNS_QTYPE_TXT, dns.DNS_QCLASS_IN)
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_NXDOMAIN)
+
+
+class TestComplexQueries(DNSTest):
+
+ def setUp(self):
+ super(TestComplexQueries, self).setUp()
+ p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+ updates = []
+
+ name = self.get_dns_domain()
+
+ u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+ updates.append(u)
+ self.finish_name_packet(p, updates)
+
+ updates = []
+ r = dns.res_rec()
+ r.name = "cname_test.%s" % self.get_dns_domain()
+ r.rr_type = dns.DNS_QTYPE_CNAME
+ r.rr_class = dns.DNS_QCLASS_IN
+ r.ttl = 900
+ r.length = 0xffff
+ r.rdata = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+ updates.append(r)
+ p.nscount = len(updates)
+ p.nsrecs = updates
+
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+
+ def tearDown(self):
+ super(TestComplexQueries, self).tearDown()
+ p = self.make_name_packet(dns.DNS_OPCODE_UPDATE)
+ updates = []
+
+ name = self.get_dns_domain()
+
+ u = self.make_name_question(name, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN)
+ updates.append(u)
+ self.finish_name_packet(p, updates)
+
+ updates = []
+ r = dns.res_rec()
+ r.name = "cname_test.%s" % self.get_dns_domain()
+ r.rr_type = dns.DNS_QTYPE_CNAME
+ r.rr_class = dns.DNS_QCLASS_NONE
+ r.ttl = 0
+ r.length = 0xffff
+ r.rdata = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+ updates.append(r)
+ p.nscount = len(updates)
+ p.nsrecs = updates
+
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+
+ def test_one_a_query(self):
+ "create a query packet containing one query record"
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+
+ name = "cname_test.%s" % self.get_dns_domain()
+ q = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN)
+ print "asking for ", q.name
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+ self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY)
+ self.assertEquals(response.ancount, 2)
+ self.assertEquals(response.answers[0].rr_type, dns.DNS_QTYPE_CNAME)
+ self.assertEquals(response.answers[0].rdata, "%s.%s" %
+ (os.getenv('SERVER'), self.get_dns_domain()))
+ self.assertEquals(response.answers[1].rr_type, dns.DNS_QTYPE_A)
+ self.assertEquals(response.answers[1].rdata,
+ os.getenv('SERVER_IP'))
+
+class TestInvalidQueries(DNSTest):
+
+ def test_one_a_query(self):
+ "send 0 bytes follows by create a query packet containing one query record"
+
+ s = None
+ try:
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+ s.connect((os.getenv('SERVER_IP'), 53))
+ s.send("", 0)
+ finally:
+ if s is not None:
+ s.close()
+
+ p = self.make_name_packet(dns.DNS_OPCODE_QUERY)
+ questions = []
+
+ name = "%s.%s" % (os.getenv('SERVER'), self.get_dns_domain())
+ q = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN)
+ print "asking for ", q.name
+ questions.append(q)
+
+ self.finish_name_packet(p, questions)
+ response = self.dns_transaction_udp(p)
+ self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK)
+ self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY)
+ self.assertEquals(response.ancount, 1)
+ self.assertEquals(response.answers[0].rdata,
+ os.getenv('SERVER_IP'))
+
+if __name__ == "__main__":
+ import unittest
+ unittest.main()
diff --git a/python/samba/tests/docs.py b/python/samba/tests/docs.py
new file mode 100644
index 00000000000..c1b371680d7
--- /dev/null
+++ b/python/samba/tests/docs.py
@@ -0,0 +1,127 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
+#
+# Tests for documentation.
+#
+# 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/>.
+#
+
+"""Tests for presence of documentation."""
+
+import samba
+import samba.tests
+from samba.tests import TestSkipped
+
+import errno
+import os
+import re
+import subprocess
+
+
+class TestCase(samba.tests.TestCase):
+
+ def _format_message(self, parameters, message):
+ parameters = list(parameters)
+ parameters.sort()
+ return message + '\n\n %s' % ('\n '.join(parameters))
+
+
+class NoXsltProc(Exception):
+
+ def __init__(self):
+ Exception.__init__(self, "'xsltproc' is not installed")
+
+
+def get_documented_parameters(sourcedir):
+ path = os.path.join(sourcedir, "bin", "default", "docs-xml", "smbdotconf")
+ if not os.path.exists(os.path.join(path, "parameters.all.xml")):
+ raise Exception("Unable to find parameters.all.xml")
+ try:
+ p = subprocess.Popen(
+ ["xsltproc", "--xinclude", "--param", "smb.context", "ALL", os.path.join(sourcedir, "docs-xml", "smbdotconf", "generate-context.xsl"), "parameters.all.xml"],
+ stderr=subprocess.STDOUT, stdout=subprocess.PIPE,
+ cwd=path)
+ except OSError, e:
+ if e.errno == errno.ENOENT:
+ raise NoXsltProc()
+ raise
+ out, err = p.communicate()
+ assert p.returncode == 0, "returncode was %r" % p.returncode
+ for l in out.splitlines():
+ m = re.match('<samba:parameter .*?name="([^"]*?)"', l)
+ if "removed=\"1\"" in l:
+ continue
+ if m:
+ name = m.group(1)
+ yield name
+ m = re.match('.*<synonym>(.*)</synonym>.*', l)
+ if m:
+ name = m.group(1)
+ yield name
+
+
+def get_implementation_parameters(sourcedir):
+ # Reading entries from source code
+ f = open(os.path.join(sourcedir, "lib/param/param_table.c"), "r")
+ try:
+ # burn through the preceding lines
+ while True:
+ l = f.readline()
+ if l.startswith("static struct parm_struct parm_table"):
+ break
+
+ for l in f.readlines():
+ if re.match("^\s*\}\;\s*$", l):
+ break
+ # pull in the param names only
+ if re.match(".*P_SEPARATOR.*", l):
+ continue
+ m = re.match("\s*\.label\s*=\s*\"(.*)\".*", l)
+ if not m:
+ continue
+
+ name = m.group(1)
+ yield name
+ finally:
+ f.close()
+
+
+class SmbDotConfTests(TestCase):
+
+ def test_unknown(self):
+ topdir = samba.source_tree_topdir()
+ try:
+ documented = set(get_documented_parameters(topdir))
+ except NoXsltProc:
+ raise TestSkipped("'xsltproc' is missing, unable to load parameters")
+ parameters = set(get_implementation_parameters(topdir))
+ # Filter out parametric options, since we can't find them in the parm
+ # table
+ documented = set([p for p in documented if not ":" in p])
+ unknown = documented.difference(parameters)
+ if len(unknown) > 0:
+ self.fail(self._format_message(unknown,
+ "Parameters that are documented but not in the implementation:"))
+
+ def test_undocumented(self):
+ topdir = samba.source_tree_topdir()
+ try:
+ documented = set(get_documented_parameters(topdir))
+ except NoXsltProc:
+ raise TestSkipped("'xsltproc' is missing, unable to load parameters")
+ parameters = set(get_implementation_parameters(topdir))
+ undocumented = parameters.difference(documented)
+ if len(undocumented) > 0:
+ self.fail(self._format_message(undocumented,
+ "Parameters that are in the implementation but undocumented:"))
diff --git a/python/samba/tests/dsdb.py b/python/samba/tests/dsdb.py
new file mode 100644
index 00000000000..3aef1d2fa48
--- /dev/null
+++ b/python/samba/tests/dsdb.py
@@ -0,0 +1,130 @@
+# Unix SMB/CIFS implementation. Tests for dsdb
+# Copyright (C) Matthieu Patou <mat@matws.net> 2010
+#
+# 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/>.
+#
+
+"""Tests for samba.dsdb."""
+
+from samba.credentials import Credentials
+from samba.samdb import SamDB
+from samba.auth import system_session
+from samba.tests import TestCase
+from samba.ndr import ndr_unpack, ndr_pack
+from samba.dcerpc import drsblobs
+import ldb
+import os
+import samba
+
+
+class DsdbTests(TestCase):
+
+ def setUp(self):
+ super(DsdbTests, self).setUp()
+ self.lp = samba.param.LoadParm()
+ self.lp.load(os.path.join(os.path.join(self.baseprovpath(), "etc"), "smb.conf"))
+ self.creds = Credentials()
+ self.creds.guess(self.lp)
+ self.session = system_session()
+ self.samdb = SamDB(os.path.join(self.baseprovpath(), "private", "sam.ldb"),
+ session_info=self.session, credentials=self.creds,lp=self.lp)
+
+ def baseprovpath(self):
+ return os.path.join(os.environ['SELFTEST_PREFIX'], "dc")
+
+ def test_get_oid_from_attrid(self):
+ oid = self.samdb.get_oid_from_attid(591614)
+ self.assertEquals(oid, "1.2.840.113556.1.4.1790")
+
+ def test_error_replpropertymetadata(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["replPropertyMetaData"])
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(res[0]["replPropertyMetaData"]))
+ ctr = repl.ctr
+ for o in ctr.array:
+ # Search for Description
+ if o.attid == 13:
+ old_version = o.version
+ o.version = o.version + 1
+ replBlob = ndr_pack(repl)
+ msg = ldb.Message()
+ msg.dn = res[0].dn
+ msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
+ self.assertRaises(ldb.LdbError, self.samdb.modify, msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
+
+ def test_twoatt_replpropertymetadata(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["replPropertyMetaData", "uSNChanged"])
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(res[0]["replPropertyMetaData"]))
+ ctr = repl.ctr
+ for o in ctr.array:
+ # Search for Description
+ if o.attid == 13:
+ old_version = o.version
+ o.version = o.version + 1
+ o.local_usn = long(str(res[0]["uSNChanged"])) + 1
+ replBlob = ndr_pack(repl)
+ msg = ldb.Message()
+ msg.dn = res[0].dn
+ msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
+ msg["description"] = ldb.MessageElement("new val", ldb.FLAG_MOD_REPLACE, "description")
+ self.assertRaises(ldb.LdbError, self.samdb.modify, msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
+
+ def test_set_replpropertymetadata(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["replPropertyMetaData", "uSNChanged"])
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(res[0]["replPropertyMetaData"]))
+ ctr = repl.ctr
+ for o in ctr.array:
+ # Search for Description
+ if o.attid == 13:
+ old_version = o.version
+ o.version = o.version + 1
+ o.local_usn = long(str(res[0]["uSNChanged"])) + 1
+ o.originating_usn = long(str(res[0]["uSNChanged"])) + 1
+ replBlob = ndr_pack(repl)
+ msg = ldb.Message()
+ msg.dn = res[0].dn
+ msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
+ self.samdb.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
+
+ def test_ok_get_attribute_from_attid(self):
+ self.assertEquals(self.samdb.get_attribute_from_attid(13), "description")
+
+ def test_ko_get_attribute_from_attid(self):
+ self.assertEquals(self.samdb.get_attribute_from_attid(11979), None)
+
+ def test_get_attribute_replmetadata_version(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["dn"])
+ self.assertEquals(len(res), 1)
+ dn = str(res[0].dn)
+ self.assertEqual(self.samdb.get_attribute_replmetadata_version(dn, "unicodePwd"), 1)
+
+ def test_set_attribute_replmetadata_version(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["dn"])
+ self.assertEquals(len(res), 1)
+ dn = str(res[0].dn)
+ version = self.samdb.get_attribute_replmetadata_version(dn, "description")
+ self.samdb.set_attribute_replmetadata_version(dn, "description", version + 2)
+ self.assertEqual(self.samdb.get_attribute_replmetadata_version(dn, "description"), version + 2)
diff --git a/python/samba/tests/gensec.py b/python/samba/tests/gensec.py
new file mode 100644
index 00000000000..e270c418ea6
--- /dev/null
+++ b/python/samba/tests/gensec.py
@@ -0,0 +1,146 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2009
+#
+# 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/>.
+#
+
+"""Tests for GENSEC.
+
+Note that this just tests the bindings work. It does not intend to test
+the functionality, that's already done in other tests.
+"""
+
+from samba.credentials import Credentials
+from samba import gensec, auth
+import samba.tests
+
+class GensecTests(samba.tests.TestCase):
+
+ def setUp(self):
+ super(GensecTests, self).setUp()
+ self.settings = {}
+ self.settings["lp_ctx"] = self.lp_ctx = samba.tests.env_loadparm()
+ self.settings["target_hostname"] = self.lp_ctx.get("netbios name")
+ """This is just for the API tests"""
+ self.gensec = gensec.Security.start_client(self.settings)
+
+ def test_start_mech_by_unknown_name(self):
+ self.assertRaises(RuntimeError, self.gensec.start_mech_by_name, "foo")
+
+ def test_start_mech_by_name(self):
+ self.gensec.start_mech_by_name("spnego")
+
+ def test_info_uninitialized(self):
+ self.assertRaises(RuntimeError, self.gensec.session_info)
+
+ def test_update(self):
+ """Test GENSEC by doing an exchange with ourselves using GSSAPI against a KDC"""
+
+ """Start up a client and server GENSEC instance to test things with"""
+
+ self.gensec_client = gensec.Security.start_client(self.settings)
+ self.gensec_client.set_credentials(self.get_credentials())
+ self.gensec_client.want_feature(gensec.FEATURE_SEAL)
+ self.gensec_client.start_mech_by_sasl_name("GSSAPI")
+
+ self.gensec_server = gensec.Security.start_server(settings=self.settings,
+ auth_context=auth.AuthContext(lp_ctx=self.lp_ctx))
+ creds = Credentials()
+ creds.guess(self.lp_ctx)
+ creds.set_machine_account(self.lp_ctx)
+ self.gensec_server.set_credentials(creds)
+
+ self.gensec_server.want_feature(gensec.FEATURE_SEAL)
+ self.gensec_server.start_mech_by_sasl_name("GSSAPI")
+
+ client_finished = False
+ server_finished = False
+ server_to_client = ""
+
+ """Run the actual call loop"""
+ while not client_finished and not server_finished:
+ if not client_finished:
+ print "running client gensec_update"
+ (client_finished, client_to_server) = self.gensec_client.update(server_to_client)
+ if not server_finished:
+ print "running server gensec_update"
+ (server_finished, server_to_client) = self.gensec_server.update(client_to_server)
+ session_info = self.gensec_server.session_info()
+
+ test_string = "Hello Server"
+ test_wrapped = self.gensec_client.wrap(test_string)
+ test_unwrapped = self.gensec_server.unwrap(test_wrapped)
+ self.assertEqual(test_string, test_unwrapped)
+ test_string = "Hello Client"
+ test_wrapped = self.gensec_server.wrap(test_string)
+ test_unwrapped = self.gensec_client.unwrap(test_wrapped)
+ self.assertEqual(test_string, test_unwrapped)
+
+ client_session_key = self.gensec_client.session_key()
+ server_session_key = self.gensec_server.session_key()
+ self.assertEqual(client_session_key, server_session_key)
+
+ def test_max_update_size(self):
+ """Test GENSEC by doing an exchange with ourselves using GSSAPI against a KDC"""
+
+ """Start up a client and server GENSEC instance to test things with"""
+
+ self.gensec_client = gensec.Security.start_client(self.settings)
+ self.gensec_client.set_credentials(self.get_credentials())
+ self.gensec_client.want_feature(gensec.FEATURE_SIGN)
+ self.gensec_client.set_max_update_size(5)
+ self.gensec_client.start_mech_by_name("spnego")
+
+ self.gensec_server = gensec.Security.start_server(settings=self.settings,
+ auth_context=auth.AuthContext(lp_ctx=self.lp_ctx))
+ creds = Credentials()
+ creds.guess(self.lp_ctx)
+ creds.set_machine_account(self.lp_ctx)
+ self.gensec_server.set_credentials(creds)
+ self.gensec_server.want_feature(gensec.FEATURE_SIGN)
+ self.gensec_server.set_max_update_size(5)
+ self.gensec_server.start_mech_by_name("spnego")
+
+ client_finished = False
+ server_finished = False
+ server_to_client = ""
+
+ """Run the actual call loop"""
+ i = 0
+ while not client_finished or not server_finished:
+ i += 1
+ if not client_finished:
+ print "running client gensec_update: %d: %r" % (len(server_to_client), server_to_client)
+ (client_finished, client_to_server) = self.gensec_client.update(server_to_client)
+ if not server_finished:
+ print "running server gensec_update: %d: %r" % (len(client_to_server), client_to_server)
+ (server_finished, server_to_client) = self.gensec_server.update(client_to_server)
+
+ """Here we expect a lot more than the typical 1 or 2 roundtrips"""
+ self.assertTrue(i > 10)
+
+ session_info = self.gensec_server.session_info()
+
+ test_string = "Hello Server"
+ test_wrapped = self.gensec_client.wrap(test_string)
+ test_unwrapped = self.gensec_server.unwrap(test_wrapped)
+ self.assertEqual(test_string, test_unwrapped)
+ test_string = "Hello Client"
+ test_wrapped = self.gensec_server.wrap(test_string)
+ test_unwrapped = self.gensec_client.unwrap(test_wrapped)
+ self.assertEqual(test_string, test_unwrapped)
+
+ client_session_key = self.gensec_client.session_key()
+ server_session_key = self.gensec_server.session_key()
+ self.assertEqual(client_session_key, server_session_key)
diff --git a/python/samba/tests/getopt.py b/python/samba/tests/getopt.py
new file mode 100644
index 00000000000..14ee0a7428b
--- /dev/null
+++ b/python/samba/tests/getopt.py
@@ -0,0 +1,55 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2011
+#
+# 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/>.
+#
+
+"""Tests for option parsing.
+
+"""
+
+import optparse
+from samba.getopt import (
+ AUTO_USE_KERBEROS,
+ DONT_USE_KERBEROS,
+ MUST_USE_KERBEROS,
+ parse_kerberos_arg,
+ )
+import samba.tests
+
+class KerberosOptionTests(samba.tests.TestCase):
+
+ def test_parse_true(self):
+ self.assertEquals(
+ MUST_USE_KERBEROS, parse_kerberos_arg("yes", "--kerberos"))
+ self.assertEquals(
+ MUST_USE_KERBEROS, parse_kerberos_arg("true", "--kerberos"))
+ self.assertEquals(
+ MUST_USE_KERBEROS, parse_kerberos_arg("1", "--kerberos"))
+
+ def test_parse_false(self):
+ self.assertEquals(
+ DONT_USE_KERBEROS, parse_kerberos_arg("no", "--kerberos"))
+ self.assertEquals(
+ DONT_USE_KERBEROS, parse_kerberos_arg("false", "--kerberos"))
+ self.assertEquals(
+ DONT_USE_KERBEROS, parse_kerberos_arg("0", "--kerberos"))
+
+ def test_parse_auto(self):
+ self.assertEquals(
+ AUTO_USE_KERBEROS, parse_kerberos_arg("auto", "--kerberos"))
+
+ def test_parse_invalid(self):
+ self.assertRaises(optparse.OptionValueError,
+ parse_kerberos_arg, "blah?", "--kerberos")
diff --git a/python/samba/tests/hostconfig.py b/python/samba/tests/hostconfig.py
new file mode 100644
index 00000000000..526dc0fe4e3
--- /dev/null
+++ b/python/samba/tests/hostconfig.py
@@ -0,0 +1,74 @@
+# Unix SMB/CIFS implementation. Tests for shares
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2009
+#
+# 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/>.
+#
+
+"""Tests for samba.hostconfig."""
+
+from samba.hostconfig import SharesContainer
+from samba.tests import TestCase
+
+
+class MockService(object):
+
+ def __init__(self, data):
+ self.data = data
+
+ def __getitem__(self, name):
+ return self.data[name]
+
+
+class MockLoadParm(object):
+
+ def __init__(self, data):
+ self.data = data
+
+ def __getitem__(self, name):
+ return MockService(self.data[name])
+
+ def __len__(self):
+ return len(self.data)
+
+ def services(self):
+ return self.data.keys()
+
+
+class ShareTests(TestCase):
+
+ def _get_shares(self, conf):
+ return SharesContainer(MockLoadParm(conf))
+
+ def test_len_no_global(self):
+ shares = self._get_shares({})
+ self.assertEquals(0, len(shares))
+
+ def test_iter(self):
+ self.assertEquals([], list(self._get_shares({})))
+ self.assertEquals([], list(self._get_shares({"global":{}})))
+ self.assertEquals(
+ ["bla"],
+ list(self._get_shares({"global":{}, "bla":{}})))
+
+ def test_len(self):
+ shares = self._get_shares({"global": {}})
+ self.assertEquals(0, len(shares))
+
+ def test_getitem_nonexistent(self):
+ shares = self._get_shares({"global": {}})
+ self.assertRaises(KeyError, shares.__getitem__, "bla")
+
+ def test_getitem_global(self):
+ shares = self._get_shares({"global": {}})
+ self.assertRaises(KeyError, shares.__getitem__, "global")
diff --git a/python/samba/tests/libsmb_samba_internal.py b/python/samba/tests/libsmb_samba_internal.py
new file mode 100644
index 00000000000..fe9f197a2c2
--- /dev/null
+++ b/python/samba/tests/libsmb_samba_internal.py
@@ -0,0 +1,78 @@
+# Unix SMB/CIFS implementation.
+# Copyright Volker Lendecke <vl@samba.org> 2012
+#
+# 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/>.
+#
+
+"""Tests for samba.samba3.libsmb_samba_internal."""
+
+from samba.samba3 import libsmb_samba_internal
+from samba.dcerpc import security
+from samba.samba3 import param as s3param
+from samba import credentials
+import samba.tests
+import threading
+import sys
+import os
+
+class LibsmbTestCase(samba.tests.TestCase):
+
+ class OpenClose(threading.Thread):
+
+ def __init__(self, conn, filename, num_ops):
+ threading.Thread.__init__(self)
+ self.conn = conn
+ self.filename = filename
+ self.num_ops = num_ops
+ self.exc = False
+
+ def run(self):
+ c = self.conn
+ try:
+ for i in range(self.num_ops):
+ f = c.create(self.filename, CreateDisposition=3,
+ DesiredAccess=security.SEC_STD_DELETE)
+ c.delete_on_close(f, True)
+ c.close(f)
+ except Exception:
+ self.exc = sys.exc_info()
+
+ def test_OpenClose(self):
+
+ lp = s3param.get_context()
+ lp.load(os.getenv("SMB_CONF_PATH"))
+
+ creds = credentials.Credentials()
+ creds.set_username(os.getenv("USERNAME"))
+ creds.set_password(os.getenv("PASSWORD"))
+
+ c = libsmb_samba_internal.Conn(os.getenv("SERVER_IP"), "tmp", creds)
+
+ mythreads = []
+
+ for i in range(3):
+ t = LibsmbTestCase.OpenClose(c, "test" + str(i), 10)
+ mythreads.append(t)
+
+ for t in mythreads:
+ t.start()
+
+ for t in mythreads:
+ t.join()
+ if t.exc:
+ raise t.exc[0](t.exc[1])
+
+if __name__ == "__main__":
+ import unittest
+ unittest.main()
diff --git a/python/samba/tests/messaging.py b/python/samba/tests/messaging.py
new file mode 100644
index 00000000000..f0cd368195f
--- /dev/null
+++ b/python/samba/tests/messaging.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+#
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# 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/>.
+#
+
+"""Tests for samba.messaging."""
+
+from samba.messaging import Messaging
+from samba.tests import TestCase
+from samba.dcerpc.server_id import server_id
+
+class MessagingTests(TestCase):
+
+ def get_context(self, *args, **kwargs):
+ return Messaging(*args, **kwargs)
+
+ def test_register(self):
+ x = self.get_context()
+ def callback():
+ pass
+ msg_type = x.register(callback)
+ x.deregister(callback, msg_type)
+
+ def test_all_servers(self):
+ x = self.get_context()
+ self.assertTrue(isinstance(x.irpc_all_servers(), list))
+
+ def test_by_name(self):
+ x = self.get_context()
+ for name in x.irpc_all_servers():
+ self.assertTrue(isinstance(x.irpc_servers_byname(name.name), list))
+
+ def test_assign_server_id(self):
+ x = self.get_context()
+ self.assertTrue(isinstance(x.server_id, server_id))
+
+ def test_ping_speed(self):
+ server_ctx = self.get_context((0, 1))
+ def ping_callback(src, data):
+ server_ctx.send(src, data)
+ def exit_callback():
+ print "received exit"
+ msg_ping = server_ctx.register(ping_callback)
+ msg_exit = server_ctx.register(exit_callback)
+
+ def pong_callback():
+ print "received pong"
+ client_ctx = self.get_context((0, 2))
+ msg_pong = client_ctx.register(pong_callback)
+
+ client_ctx.send((0, 1), msg_ping, "testing")
+ client_ctx.send((0, 1), msg_ping, "")
+
diff --git a/python/samba/tests/netcmd.py b/python/samba/tests/netcmd.py
new file mode 100644
index 00000000000..2cbac4e8bff
--- /dev/null
+++ b/python/samba/tests/netcmd.py
@@ -0,0 +1,90 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2009-2011
+#
+# 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/>.
+#
+
+"""Tests for samba.netcmd."""
+
+from cStringIO import StringIO
+from samba.netcmd import Command
+from samba.netcmd.testparm import cmd_testparm
+from samba.netcmd.main import cmd_sambatool
+import samba.tests
+
+class NetCmdTestCase(samba.tests.TestCase):
+
+ def run_netcmd(self, cmd_klass, args, retcode=0):
+ cmd = cmd_klass(outf=StringIO(), errf=StringIO())
+ try:
+ retval = cmd._run(cmd_klass.__name__, *args)
+ except Exception, e:
+ cmd.show_command_error(e)
+ retval = 1
+ self.assertEquals(retcode, retval)
+ return cmd.outf.getvalue(), cmd.errf.getvalue()
+
+ def iter_all_subcommands(self):
+ todo = []
+ todo.extend(cmd_sambatool.subcommands.items())
+ while todo:
+ (path, cmd) = todo.pop()
+ yield path, cmd
+ subcmds = getattr(cmd, "subcommands", {})
+ todo.extend([(path + " " + k, v) for (k, v) in
+ subcmds.iteritems()])
+
+
+class TestParmTests(NetCmdTestCase):
+
+ def test_no_client_ip(self):
+ out, err = self.run_netcmd(cmd_testparm, ["--client-name=foo"],
+ retcode=-1)
+ self.assertEquals("", out)
+ self.assertEquals(
+ "ERROR: Both a DNS name and an IP address are "
+ "required for the host access check\n", err)
+
+
+class CommandTests(NetCmdTestCase):
+
+ def test_description(self):
+ class cmd_foo(Command):
+ """Mydescription"""
+ self.assertEquals("Mydescription", cmd_foo().short_description)
+
+ def test_name(self):
+ class cmd_foo(Command):
+ pass
+ self.assertEquals("foo", cmd_foo().name)
+
+ def test_synopsis_everywhere(self):
+ missing = []
+ for path, cmd in self.iter_all_subcommands():
+ if cmd.synopsis is None:
+ missing.append(path)
+ if missing:
+ self.fail("The following commands do not have a synopsis set: %r" %
+ missing)
+
+ def test_short_description_everywhere(self):
+ missing = []
+ for path, cmd in self.iter_all_subcommands():
+ if cmd.short_description is None:
+ missing.append(path)
+ if not missing:
+ return
+ self.fail(
+ "The following commands do not have a short description set: %r" %
+ missing)
diff --git a/python/samba/tests/ntacls.py b/python/samba/tests/ntacls.py
new file mode 100644
index 00000000000..aa9ef6852ba
--- /dev/null
+++ b/python/samba/tests/ntacls.py
@@ -0,0 +1,83 @@
+# Unix SMB/CIFS implementation. Tests for ntacls manipulation
+# Copyright (C) Matthieu Patou <mat@matws.net> 2009-2010
+# Copyright (C) Andrew Bartlett 2012
+#
+# 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/>.
+#
+
+"""Tests for samba.ntacls."""
+
+from samba.ntacls import setntacl, getntacl, XattrBackendError
+from samba.dcerpc import xattr, security
+from samba.param import LoadParm
+from samba.tests import TestCaseInTempDir, TestSkipped
+import random
+import os
+
+class NtaclsTests(TestCaseInTempDir):
+
+ def test_setntacl(self):
+ lp = LoadParm()
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ open(self.tempf, 'w').write("empty")
+ lp.set("posix:eadb",os.path.join(self.tempdir,"eadbtest.tdb"))
+ setntacl(lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467")
+ os.unlink(os.path.join(self.tempdir,"eadbtest.tdb"))
+
+ def test_setntacl_getntacl(self):
+ lp = LoadParm()
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ open(self.tempf, 'w').write("empty")
+ lp.set("posix:eadb",os.path.join(self.tempdir,"eadbtest.tdb"))
+ setntacl(lp,self.tempf,acl,"S-1-5-21-2212615479-2695158682-2101375467")
+ facl = getntacl(lp,self.tempf)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(facl.as_sddl(anysid),acl)
+ os.unlink(os.path.join(self.tempdir,"eadbtest.tdb"))
+
+ def test_setntacl_getntacl_param(self):
+ lp = LoadParm()
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ open(self.tempf, 'w').write("empty")
+ setntacl(lp,self.tempf,acl,"S-1-5-21-2212615479-2695158682-2101375467","tdb",os.path.join(self.tempdir,"eadbtest.tdb"))
+ facl=getntacl(lp,self.tempf,"tdb",os.path.join(self.tempdir,"eadbtest.tdb"))
+ domsid=security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(facl.as_sddl(domsid),acl)
+ os.unlink(os.path.join(self.tempdir,"eadbtest.tdb"))
+
+ def test_setntacl_invalidbackend(self):
+ lp = LoadParm()
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ open(self.tempf, 'w').write("empty")
+ self.assertRaises(XattrBackendError, setntacl, lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467","ttdb", os.path.join(self.tempdir,"eadbtest.tdb"))
+
+ def test_setntacl_forcenative(self):
+ if os.getuid() == 0:
+ raise TestSkipped("Running test as root, test skipped")
+ lp = LoadParm()
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ open(self.tempf, 'w').write("empty")
+ lp.set("posix:eadb", os.path.join(self.tempdir,"eadbtest.tdb"))
+ self.assertRaises(Exception, setntacl, lp, self.tempf ,acl,
+ "S-1-5-21-2212615479-2695158682-2101375467","native")
+
+
+ def setUp(self):
+ super(NtaclsTests, self).setUp()
+ self.tempf = os.path.join(self.tempdir, "test")
+ open(self.tempf, 'w').write("empty")
+
+ def tearDown(self):
+ os.unlink(self.tempf)
+ super(NtaclsTests, self).tearDown()
diff --git a/python/samba/tests/param.py b/python/samba/tests/param.py
new file mode 100644
index 00000000000..f539eba140d
--- /dev/null
+++ b/python/samba/tests/param.py
@@ -0,0 +1,57 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+#
+# 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/>.
+#
+
+"""Tests for samba.param."""
+
+from samba import param
+import samba.tests
+
+class LoadParmTestCase(samba.tests.TestCase):
+
+ def test_init(self):
+ file = param.LoadParm()
+ self.assertTrue(file is not None)
+
+ def test_length(self):
+ file = param.LoadParm()
+ self.assertEquals(0, len(file))
+
+ def test_set_workgroup(self):
+ file = param.LoadParm()
+ file.set("workgroup", "bla")
+ self.assertEquals("BLA", file.get("workgroup"))
+
+ def test_is_mydomain(self):
+ file = param.LoadParm()
+ file.set("workgroup", "bla")
+ self.assertTrue(file.is_mydomain("BLA"))
+ self.assertFalse(file.is_mydomain("FOOBAR"))
+
+ def test_is_myname(self):
+ file = param.LoadParm()
+ file.set("netbios name", "bla")
+ self.assertTrue(file.is_myname("BLA"))
+ self.assertFalse(file.is_myname("FOOBAR"))
+
+ def test_load_default(self):
+ file = param.LoadParm()
+ file.load_default()
+
+ def test_section_nonexistent(self):
+ samba_lp = param.LoadParm()
+ samba_lp.load_default()
+ self.assertRaises(KeyError, samba_lp.__getitem__, "nonexistent")
diff --git a/python/samba/tests/policy.py b/python/samba/tests/policy.py
new file mode 100644
index 00000000000..b2457451528
--- /dev/null
+++ b/python/samba/tests/policy.py
@@ -0,0 +1,34 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2010
+#
+# 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/>.
+#
+
+"""Tests for the libpolicy Python bindings.
+
+"""
+
+from samba.tests import TestCase
+from samba import policy
+
+
+class PolicyTests(TestCase):
+
+ def test_get_gpo_flags(self):
+ self.assertEquals(["GPO_FLAG_USER_DISABLE"],
+ policy.get_gpo_flags(policy.GPO_FLAG_USER_DISABLE))
+
+ def test_get_gplink_options(self):
+ self.assertEquals(["GPLINK_OPT_DISABLE"],
+ policy.get_gplink_options(policy.GPLINK_OPT_DISABLE))
diff --git a/python/samba/tests/posixacl.py b/python/samba/tests/posixacl.py
new file mode 100644
index 00000000000..7cd22ebccd1
--- /dev/null
+++ b/python/samba/tests/posixacl.py
@@ -0,0 +1,732 @@
+# Unix SMB/CIFS implementation. Tests for NT and posix ACL manipulation
+# Copyright (C) Matthieu Patou <mat@matws.net> 2009-2010
+# Copyright (C) Andrew Bartlett 2012
+#
+# 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/>.
+#
+
+"""Tests for the Samba3 NT -> posix ACL layer"""
+
+from samba.ntacls import setntacl, getntacl, checkset_backend
+from samba.dcerpc import xattr, security, smb_acl, idmap
+from samba.param import LoadParm
+from samba.tests import TestCaseInTempDir
+from samba import provision
+import random
+import os
+from samba.samba3 import smbd, passdb
+from samba.samba3 import param as s3param
+
+# To print a posix ACL use:
+# for entry in posix_acl.acl:
+# print "a_type: %d" % entry.a_type
+# print "a_perm: %o" % entry.a_perm
+# print "uid: %d" % entry.uid
+# print "gid: %d" % entry.gid
+
+class PosixAclMappingTests(TestCaseInTempDir):
+
+ def test_setntacl(self):
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=False)
+
+ def test_setntacl_smbd_getntacl(self):
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=True)
+ facl = getntacl(self.lp, self.tempf, direct_db_access=True)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(facl.as_sddl(anysid),acl)
+
+ def test_setntacl_smbd_setposixacl_getntacl(self):
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=True)
+
+ # This will invalidate the ACL, as we have a hook!
+ smbd.set_simple_acl(self.tempf, 0640)
+
+ # However, this only asks the xattr
+ try:
+ facl = getntacl(self.lp, self.tempf, direct_db_access=True)
+ self.assertTrue(False)
+ except TypeError:
+ pass
+
+ def test_setntacl_invalidate_getntacl(self):
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=True)
+
+ # This should invalidate the ACL, as we include the posix ACL in the hash
+ (backend_obj, dbname) = checkset_backend(self.lp, None, None)
+ backend_obj.wrap_setxattr(dbname,
+ self.tempf, "system.fake_access_acl", "")
+
+ #however, as this is direct DB access, we do not notice it
+ facl = getntacl(self.lp, self.tempf, direct_db_access=True)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(acl, facl.as_sddl(anysid))
+
+ def test_setntacl_invalidate_getntacl_smbd(self):
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=False)
+
+ # This should invalidate the ACL, as we include the posix ACL in the hash
+ (backend_obj, dbname) = checkset_backend(self.lp, None, None)
+ backend_obj.wrap_setxattr(dbname,
+ self.tempf, "system.fake_access_acl", "")
+
+ #the hash would break, and we return an ACL based only on the mode, except we set the ACL using the 'ntvfs' mode that doesn't include a hash
+ facl = getntacl(self.lp, self.tempf)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(acl, facl.as_sddl(anysid))
+
+ def test_setntacl_smbd_invalidate_getntacl_smbd(self):
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ simple_acl_from_posix = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)(A;;0x001200a9;;;S-1-5-21-2212615479-2695158682-2101375467-513)(A;;;;;WD)"
+ os.chmod(self.tempf, 0750)
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=False)
+
+ # This should invalidate the ACL, as we include the posix ACL in the hash
+ (backend_obj, dbname) = checkset_backend(self.lp, None, None)
+ backend_obj.wrap_setxattr(dbname,
+ self.tempf, "system.fake_access_acl", "")
+
+ #the hash will break, and we return an ACL based only on the mode
+ facl = getntacl(self.lp, self.tempf, direct_db_access=False)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(simple_acl_from_posix, facl.as_sddl(anysid))
+
+ def test_setntacl_smbd_dont_invalidate_getntacl_smbd(self):
+ # set an ACL on a tempfile
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ os.chmod(self.tempf, 0750)
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=False)
+
+ # now influence the POSIX ACL->SD mapping it returns something else than
+ # what was set previously
+ # this should not invalidate the hash and the complete ACL should still
+ # be returned
+ self.lp.set("profile acls", "yes")
+ # we should still get back the ACL (and not one mapped from POSIX ACL)
+ facl = getntacl(self.lp, self.tempf, direct_db_access=False)
+ self.lp.set("profile acls", "no")
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(acl, facl.as_sddl(anysid))
+
+ def test_setntacl_getntacl_smbd(self):
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=True)
+ facl = getntacl(self.lp, self.tempf, direct_db_access=False)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(facl.as_sddl(anysid),acl)
+
+ def test_setntacl_smbd_getntacl_smbd(self):
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=False)
+ facl = getntacl(self.lp, self.tempf, direct_db_access=False)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(facl.as_sddl(anysid),acl)
+
+ def test_setntacl_smbd_setposixacl_getntacl_smbd(self):
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ simple_acl_from_posix = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;;0x001f019f;;;S-1-5-21-2212615479-2695158682-2101375467-512)(A;;0x00120089;;;S-1-5-21-2212615479-2695158682-2101375467-513)(A;;;;;WD)"
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=False)
+ # This invalidates the hash of the NT acl just set because there is a hook in the posix ACL set code
+ smbd.set_simple_acl(self.tempf, 0640)
+ facl = getntacl(self.lp, self.tempf, direct_db_access=False)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(simple_acl_from_posix, facl.as_sddl(anysid))
+
+ def test_setntacl_smbd_setposixacl_group_getntacl_smbd(self):
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ BA_sid = security.dom_sid(security.SID_BUILTIN_ADMINISTRATORS)
+ simple_acl_from_posix = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;;0x001f019f;;;S-1-5-21-2212615479-2695158682-2101375467-512)(A;;0x00120089;;;BA)(A;;0x00120089;;;S-1-5-21-2212615479-2695158682-2101375467-513)(A;;;;;WD)"
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=False)
+ # This invalidates the hash of the NT acl just set because there is a hook in the posix ACL set code
+ s4_passdb = passdb.PDB(self.lp.get("passdb backend"))
+ (BA_gid,BA_type) = s4_passdb.sid_to_id(BA_sid)
+ smbd.set_simple_acl(self.tempf, 0640, BA_gid)
+
+ # This should re-calculate an ACL based on the posix details
+ facl = getntacl(self.lp,self.tempf, direct_db_access=False)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(simple_acl_from_posix, facl.as_sddl(anysid))
+
+ def test_setntacl_smbd_getntacl_smbd_gpo(self):
+ acl = "O:DAG:DUD:P(A;OICI;0x001f01ff;;;DA)(A;OICI;0x001f01ff;;;EA)(A;OICIIO;0x001f01ff;;;CO)(A;OICI;0x001f01ff;;;DA)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001200a9;;;ED)S:AI(OU;CIIDSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CIIDSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)"
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=False)
+ facl = getntacl(self.lp, self.tempf, direct_db_access=False)
+ domsid = security.dom_sid("S-1-5-21-2212615479-2695158682-2101375467")
+ self.assertEquals(facl.as_sddl(domsid),acl)
+
+ def test_setntacl_getposixacl(self):
+ acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+ setntacl(self.lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467", use_ntvfs=False)
+ facl = getntacl(self.lp, self.tempf)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(facl.as_sddl(anysid),acl)
+ posix_acl = smbd.get_sys_acl(self.tempf, smb_acl.SMB_ACL_TYPE_ACCESS)
+
+ def test_setposixacl_getposixacl(self):
+ smbd.set_simple_acl(self.tempf, 0640)
+ posix_acl = smbd.get_sys_acl(self.tempf, smb_acl.SMB_ACL_TYPE_ACCESS)
+ self.assertEquals(posix_acl.count, 4)
+
+ self.assertEquals(posix_acl.acl[0].a_type, smb_acl.SMB_ACL_USER_OBJ)
+ self.assertEquals(posix_acl.acl[0].a_perm, 6)
+
+ self.assertEquals(posix_acl.acl[1].a_type, smb_acl.SMB_ACL_GROUP_OBJ)
+ self.assertEquals(posix_acl.acl[1].a_perm, 4)
+
+ self.assertEquals(posix_acl.acl[2].a_type, smb_acl.SMB_ACL_OTHER)
+ self.assertEquals(posix_acl.acl[2].a_perm, 0)
+
+ self.assertEquals(posix_acl.acl[3].a_type, smb_acl.SMB_ACL_MASK)
+ self.assertEquals(posix_acl.acl[3].a_perm, 6)
+
+ def test_setposixacl_getntacl(self):
+ acl = ""
+ smbd.set_simple_acl(self.tempf, 0750)
+ try:
+ facl = getntacl(self.lp, self.tempf)
+ self.assertTrue(False)
+ except TypeError:
+ # We don't expect the xattr to be filled in in this case
+ pass
+
+ def test_setposixacl_getntacl_smbd(self):
+ s4_passdb = passdb.PDB(self.lp.get("passdb backend"))
+ group_SID = s4_passdb.gid_to_sid(os.stat(self.tempf).st_gid)
+ user_SID = s4_passdb.uid_to_sid(os.stat(self.tempf).st_uid)
+ smbd.set_simple_acl(self.tempf, 0640)
+ facl = getntacl(self.lp, self.tempf, direct_db_access=False)
+ acl = "O:%sG:%sD:(A;;0x001f019f;;;%s)(A;;0x00120089;;;%s)(A;;;;;WD)" % (user_SID, group_SID, user_SID, group_SID)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(acl, facl.as_sddl(anysid))
+
+ def test_setposixacl_dir_getntacl_smbd(self):
+ s4_passdb = passdb.PDB(self.lp.get("passdb backend"))
+ user_SID = s4_passdb.uid_to_sid(os.stat(self.tempdir).st_uid)
+ BA_sid = security.dom_sid(security.SID_BUILTIN_ADMINISTRATORS)
+ s4_passdb = passdb.PDB(self.lp.get("passdb backend"))
+ (BA_id,BA_type) = s4_passdb.sid_to_id(BA_sid)
+ self.assertEquals(BA_type, idmap.ID_TYPE_BOTH)
+ SO_sid = security.dom_sid(security.SID_BUILTIN_SERVER_OPERATORS)
+ (SO_id,SO_type) = s4_passdb.sid_to_id(SO_sid)
+ self.assertEquals(SO_type, idmap.ID_TYPE_BOTH)
+ smbd.chown(self.tempdir, BA_id, SO_id)
+ smbd.set_simple_acl(self.tempdir, 0750)
+ facl = getntacl(self.lp, self.tempdir, direct_db_access=False)
+ acl = "O:BAG:SOD:(A;;0x001f01ff;;;BA)(A;;0x001200a9;;;SO)(A;;;;;WD)(A;OICIIO;0x001f01ff;;;CO)(A;OICIIO;0x001f01ff;;;CG)(A;OICIIO;0x001f01ff;;;WD)"
+
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(acl, facl.as_sddl(anysid))
+
+ def test_setposixacl_group_getntacl_smbd(self):
+ BA_sid = security.dom_sid(security.SID_BUILTIN_ADMINISTRATORS)
+ s4_passdb = passdb.PDB(self.lp.get("passdb backend"))
+ (BA_gid,BA_type) = s4_passdb.sid_to_id(BA_sid)
+ group_SID = s4_passdb.gid_to_sid(os.stat(self.tempf).st_gid)
+ user_SID = s4_passdb.uid_to_sid(os.stat(self.tempf).st_uid)
+ self.assertEquals(BA_type, idmap.ID_TYPE_BOTH)
+ smbd.set_simple_acl(self.tempf, 0640, BA_gid)
+ facl = getntacl(self.lp, self.tempf, direct_db_access=False)
+ domsid = passdb.get_global_sam_sid()
+ acl = "O:%sG:%sD:(A;;0x001f019f;;;%s)(A;;0x00120089;;;BA)(A;;0x00120089;;;%s)(A;;;;;WD)" % (user_SID, group_SID, user_SID, group_SID)
+ anysid = security.dom_sid(security.SID_NT_SELF)
+ self.assertEquals(acl, facl.as_sddl(anysid))
+
+ def test_setposixacl_getposixacl(self):
+ smbd.set_simple_acl(self.tempf, 0640)
+ posix_acl = smbd.get_sys_acl(self.tempf, smb_acl.SMB_ACL_TYPE_ACCESS)
+ self.assertEquals(posix_acl.count, 4)
+
+ self.assertEquals(posix_acl.acl[0].a_type, smb_acl.SMB_ACL_USER_OBJ)
+ self.assertEquals(posix_acl.acl[0].a_perm, 6)
+
+ self.assertEquals(posix_acl.acl[1].a_type, smb_acl.SMB_ACL_GROUP_OBJ)
+ self.assertEquals(posix_acl.acl[1].a_perm, 4)
+
+ self.assertEquals(posix_acl.acl[2].a_type, smb_acl.SMB_ACL_OTHER)
+ self.assertEquals(posix_acl.acl[2].a_perm, 0)
+
+ self.assertEquals(posix_acl.acl[3].a_type, smb_acl.SMB_ACL_MASK)
+ self.assertEquals(posix_acl.acl[3].a_perm, 7)
+
+ def test_setposixacl_dir_getposixacl(self):
+ smbd.set_simple_acl(self.tempdir, 0750)
+ posix_acl = smbd.get_sys_acl(self.tempdir, smb_acl.SMB_ACL_TYPE_ACCESS)
+ self.assertEquals(posix_acl.count, 4)
+
+ self.assertEquals(posix_acl.acl[0].a_type, smb_acl.SMB_ACL_USER_OBJ)
+ self.assertEquals(posix_acl.acl[0].a_perm, 7)
+
+ self.assertEquals(posix_acl.acl[1].a_type, smb_acl.SMB_ACL_GROUP_OBJ)
+ self.assertEquals(posix_acl.acl[1].a_perm, 5)
+
+ self.assertEquals(posix_acl.acl[2].a_type, smb_acl.SMB_ACL_OTHER)
+ self.assertEquals(posix_acl.acl[2].a_perm, 0)
+
+ self.assertEquals(posix_acl.acl[3].a_type, smb_acl.SMB_ACL_MASK)
+ self.assertEquals(posix_acl.acl[3].a_perm, 7)
+
+ def test_setposixacl_group_getposixacl(self):
+ BA_sid = security.dom_sid(security.SID_BUILTIN_ADMINISTRATORS)
+ s4_passdb = passdb.PDB(self.lp.get("passdb backend"))
+ (BA_gid,BA_type) = s4_passdb.sid_to_id(BA_sid)
+ self.assertEquals(BA_type, idmap.ID_TYPE_BOTH)
+ smbd.set_simple_acl(self.tempf, 0670, BA_gid)
+ posix_acl = smbd.get_sys_acl(self.tempf, smb_acl.SMB_ACL_TYPE_ACCESS)
+
+ self.assertEquals(posix_acl.count, 5)
+
+ self.assertEquals(posix_acl.acl[0].a_type, smb_acl.SMB_ACL_USER_OBJ)
+ self.assertEquals(posix_acl.acl[0].a_perm, 6)
+
+ self.assertEquals(posix_acl.acl[1].a_type, smb_acl.SMB_ACL_GROUP_OBJ)
+ self.assertEquals(posix_acl.acl[1].a_perm, 7)
+
+ self.assertEquals(posix_acl.acl[2].a_type, smb_acl.SMB_ACL_OTHER)
+ self.assertEquals(posix_acl.acl[2].a_perm, 0)
+
+ self.assertEquals(posix_acl.acl[3].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[3].a_perm, 7)
+ self.assertEquals(posix_acl.acl[3].info.gid, BA_gid)
+
+ self.assertEquals(posix_acl.acl[4].a_type, smb_acl.SMB_ACL_MASK)
+ self.assertEquals(posix_acl.acl[4].a_perm, 7)
+
+ def test_setntacl_sysvol_check_getposixacl(self):
+ acl = provision.SYSVOL_ACL
+ domsid = passdb.get_global_sam_sid()
+ setntacl(self.lp, self.tempf,acl,str(domsid), use_ntvfs=False)
+ facl = getntacl(self.lp, self.tempf)
+ self.assertEquals(facl.as_sddl(domsid),acl)
+ posix_acl = smbd.get_sys_acl(self.tempf, smb_acl.SMB_ACL_TYPE_ACCESS)
+
+ LA_sid = security.dom_sid(str(domsid)+"-"+str(security.DOMAIN_RID_ADMINISTRATOR))
+ BA_sid = security.dom_sid(security.SID_BUILTIN_ADMINISTRATORS)
+ SO_sid = security.dom_sid(security.SID_BUILTIN_SERVER_OPERATORS)
+ SY_sid = security.dom_sid(security.SID_NT_SYSTEM)
+ AU_sid = security.dom_sid(security.SID_NT_AUTHENTICATED_USERS)
+
+ s4_passdb = passdb.PDB(self.lp.get("passdb backend"))
+
+ # These assertions correct for current plugin_s4_dc selftest
+ # configuration. When other environments have a broad range of
+ # groups mapped via passdb, we can relax some of these checks
+ (LA_uid,LA_type) = s4_passdb.sid_to_id(LA_sid)
+ self.assertEquals(LA_type, idmap.ID_TYPE_UID)
+ (BA_gid,BA_type) = s4_passdb.sid_to_id(BA_sid)
+ self.assertEquals(BA_type, idmap.ID_TYPE_BOTH)
+ (SO_gid,SO_type) = s4_passdb.sid_to_id(SO_sid)
+ self.assertEquals(SO_type, idmap.ID_TYPE_BOTH)
+ (SY_gid,SY_type) = s4_passdb.sid_to_id(SY_sid)
+ self.assertEquals(SO_type, idmap.ID_TYPE_BOTH)
+ (AU_gid,AU_type) = s4_passdb.sid_to_id(AU_sid)
+ self.assertEquals(AU_type, idmap.ID_TYPE_BOTH)
+
+ self.assertEquals(posix_acl.count, 9)
+
+ self.assertEquals(posix_acl.acl[0].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[0].a_perm, 7)
+ self.assertEquals(posix_acl.acl[0].info.gid, BA_gid)
+
+ self.assertEquals(posix_acl.acl[1].a_type, smb_acl.SMB_ACL_USER)
+ self.assertEquals(posix_acl.acl[1].a_perm, 6)
+ self.assertEquals(posix_acl.acl[1].info.uid, LA_uid)
+
+ self.assertEquals(posix_acl.acl[2].a_type, smb_acl.SMB_ACL_OTHER)
+ self.assertEquals(posix_acl.acl[2].a_perm, 0)
+
+ self.assertEquals(posix_acl.acl[3].a_type, smb_acl.SMB_ACL_USER_OBJ)
+ self.assertEquals(posix_acl.acl[3].a_perm, 6)
+
+ self.assertEquals(posix_acl.acl[4].a_type, smb_acl.SMB_ACL_GROUP_OBJ)
+ self.assertEquals(posix_acl.acl[4].a_perm, 7)
+
+ self.assertEquals(posix_acl.acl[5].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[5].a_perm, 5)
+ self.assertEquals(posix_acl.acl[5].info.gid, SO_gid)
+
+ self.assertEquals(posix_acl.acl[6].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[6].a_perm, 7)
+ self.assertEquals(posix_acl.acl[6].info.gid, SY_gid)
+
+ self.assertEquals(posix_acl.acl[7].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[7].a_perm, 5)
+ self.assertEquals(posix_acl.acl[7].info.gid, AU_gid)
+
+ self.assertEquals(posix_acl.acl[8].a_type, smb_acl.SMB_ACL_MASK)
+ self.assertEquals(posix_acl.acl[8].a_perm, 7)
+
+
+# check that it matches:
+# user::rwx
+# user:root:rwx (selftest user actually)
+# group::rwx
+# group:Local Admins:rwx
+# group:3000000:r-x
+# group:3000001:rwx
+# group:3000002:r-x
+# mask::rwx
+# other::---
+
+#
+# This is in this order in the NDR smb_acl (not re-orderded for display)
+# a_type: GROUP
+# a_perm: 7
+# uid: -1
+# gid: 10
+# a_type: USER
+# a_perm: 6
+# uid: 0 (selftest user actually)
+# gid: -1
+# a_type: OTHER
+# a_perm: 0
+# uid: -1
+# gid: -1
+# a_type: USER_OBJ
+# a_perm: 6
+# uid: -1
+# gid: -1
+# a_type: GROUP_OBJ
+# a_perm: 7
+# uid: -1
+# gid: -1
+# a_type: GROUP
+# a_perm: 5
+# uid: -1
+# gid: 3000020
+# a_type: GROUP
+# a_perm: 7
+# uid: -1
+# gid: 3000000
+# a_type: GROUP
+# a_perm: 5
+# uid: -1
+# gid: 3000001
+# a_type: MASK
+# a_perm: 7
+# uid: -1
+# gid: -1
+
+#
+
+
+ def test_setntacl_sysvol_dir_check_getposixacl(self):
+ acl = provision.SYSVOL_ACL
+ domsid = passdb.get_global_sam_sid()
+ setntacl(self.lp, self.tempdir,acl,str(domsid), use_ntvfs=False)
+ facl = getntacl(self.lp, self.tempdir)
+ self.assertEquals(facl.as_sddl(domsid),acl)
+ posix_acl = smbd.get_sys_acl(self.tempdir, smb_acl.SMB_ACL_TYPE_ACCESS)
+
+ LA_sid = security.dom_sid(str(domsid)+"-"+str(security.DOMAIN_RID_ADMINISTRATOR))
+ BA_sid = security.dom_sid(security.SID_BUILTIN_ADMINISTRATORS)
+ SO_sid = security.dom_sid(security.SID_BUILTIN_SERVER_OPERATORS)
+ SY_sid = security.dom_sid(security.SID_NT_SYSTEM)
+ AU_sid = security.dom_sid(security.SID_NT_AUTHENTICATED_USERS)
+
+ s4_passdb = passdb.PDB(self.lp.get("passdb backend"))
+
+ # These assertions correct for current plugin_s4_dc selftest
+ # configuration. When other environments have a broad range of
+ # groups mapped via passdb, we can relax some of these checks
+ (LA_uid,LA_type) = s4_passdb.sid_to_id(LA_sid)
+ self.assertEquals(LA_type, idmap.ID_TYPE_UID)
+ (BA_gid,BA_type) = s4_passdb.sid_to_id(BA_sid)
+ self.assertEquals(BA_type, idmap.ID_TYPE_BOTH)
+ (SO_gid,SO_type) = s4_passdb.sid_to_id(SO_sid)
+ self.assertEquals(SO_type, idmap.ID_TYPE_BOTH)
+ (SY_gid,SY_type) = s4_passdb.sid_to_id(SY_sid)
+ self.assertEquals(SO_type, idmap.ID_TYPE_BOTH)
+ (AU_gid,AU_type) = s4_passdb.sid_to_id(AU_sid)
+ self.assertEquals(AU_type, idmap.ID_TYPE_BOTH)
+
+ self.assertEquals(posix_acl.count, 9)
+
+ self.assertEquals(posix_acl.acl[0].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[0].a_perm, 7)
+ self.assertEquals(posix_acl.acl[0].info.gid, BA_gid)
+
+ self.assertEquals(posix_acl.acl[1].a_type, smb_acl.SMB_ACL_USER)
+ self.assertEquals(posix_acl.acl[1].a_perm, 7)
+ self.assertEquals(posix_acl.acl[1].info.uid, LA_uid)
+
+ self.assertEquals(posix_acl.acl[2].a_type, smb_acl.SMB_ACL_OTHER)
+ self.assertEquals(posix_acl.acl[2].a_perm, 0)
+
+ self.assertEquals(posix_acl.acl[3].a_type, smb_acl.SMB_ACL_USER_OBJ)
+ self.assertEquals(posix_acl.acl[3].a_perm, 7)
+
+ self.assertEquals(posix_acl.acl[4].a_type, smb_acl.SMB_ACL_GROUP_OBJ)
+ self.assertEquals(posix_acl.acl[4].a_perm, 7)
+
+ self.assertEquals(posix_acl.acl[5].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[5].a_perm, 5)
+ self.assertEquals(posix_acl.acl[5].info.gid, SO_gid)
+
+ self.assertEquals(posix_acl.acl[6].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[6].a_perm, 7)
+ self.assertEquals(posix_acl.acl[6].info.gid, SY_gid)
+
+ self.assertEquals(posix_acl.acl[7].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[7].a_perm, 5)
+ self.assertEquals(posix_acl.acl[7].info.gid, AU_gid)
+
+ self.assertEquals(posix_acl.acl[8].a_type, smb_acl.SMB_ACL_MASK)
+ self.assertEquals(posix_acl.acl[8].a_perm, 7)
+
+
+# check that it matches:
+# user::rwx
+# user:root:rwx (selftest user actually)
+# group::rwx
+# group:3000000:rwx
+# group:3000001:r-x
+# group:3000002:rwx
+# group:3000003:r-x
+# mask::rwx
+# other::---
+
+
+ def test_setntacl_policies_dir_check_getposixacl(self):
+ acl = provision.POLICIES_ACL
+ domsid = passdb.get_global_sam_sid()
+ setntacl(self.lp, self.tempdir,acl,str(domsid), use_ntvfs=False)
+ facl = getntacl(self.lp, self.tempdir)
+ self.assertEquals(facl.as_sddl(domsid),acl)
+ posix_acl = smbd.get_sys_acl(self.tempdir, smb_acl.SMB_ACL_TYPE_ACCESS)
+
+ LA_sid = security.dom_sid(str(domsid)+"-"+str(security.DOMAIN_RID_ADMINISTRATOR))
+ BA_sid = security.dom_sid(security.SID_BUILTIN_ADMINISTRATORS)
+ SO_sid = security.dom_sid(security.SID_BUILTIN_SERVER_OPERATORS)
+ SY_sid = security.dom_sid(security.SID_NT_SYSTEM)
+ AU_sid = security.dom_sid(security.SID_NT_AUTHENTICATED_USERS)
+ PA_sid = security.dom_sid(str(domsid)+"-"+str(security.DOMAIN_RID_POLICY_ADMINS))
+
+ s4_passdb = passdb.PDB(self.lp.get("passdb backend"))
+
+ # These assertions correct for current plugin_s4_dc selftest
+ # configuration. When other environments have a broad range of
+ # groups mapped via passdb, we can relax some of these checks
+ (LA_uid,LA_type) = s4_passdb.sid_to_id(LA_sid)
+ self.assertEquals(LA_type, idmap.ID_TYPE_UID)
+ (BA_gid,BA_type) = s4_passdb.sid_to_id(BA_sid)
+ self.assertEquals(BA_type, idmap.ID_TYPE_BOTH)
+ (SO_gid,SO_type) = s4_passdb.sid_to_id(SO_sid)
+ self.assertEquals(SO_type, idmap.ID_TYPE_BOTH)
+ (SY_gid,SY_type) = s4_passdb.sid_to_id(SY_sid)
+ self.assertEquals(SO_type, idmap.ID_TYPE_BOTH)
+ (AU_gid,AU_type) = s4_passdb.sid_to_id(AU_sid)
+ self.assertEquals(AU_type, idmap.ID_TYPE_BOTH)
+ (PA_gid,PA_type) = s4_passdb.sid_to_id(PA_sid)
+ self.assertEquals(PA_type, idmap.ID_TYPE_BOTH)
+
+ self.assertEquals(posix_acl.count, 10)
+
+ self.assertEquals(posix_acl.acl[0].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[0].a_perm, 7)
+ self.assertEquals(posix_acl.acl[0].info.gid, BA_gid)
+
+ self.assertEquals(posix_acl.acl[1].a_type, smb_acl.SMB_ACL_USER)
+ self.assertEquals(posix_acl.acl[1].a_perm, 7)
+ self.assertEquals(posix_acl.acl[1].info.uid, LA_uid)
+
+ self.assertEquals(posix_acl.acl[2].a_type, smb_acl.SMB_ACL_OTHER)
+ self.assertEquals(posix_acl.acl[2].a_perm, 0)
+
+ self.assertEquals(posix_acl.acl[3].a_type, smb_acl.SMB_ACL_USER_OBJ)
+ self.assertEquals(posix_acl.acl[3].a_perm, 7)
+
+ self.assertEquals(posix_acl.acl[4].a_type, smb_acl.SMB_ACL_GROUP_OBJ)
+ self.assertEquals(posix_acl.acl[4].a_perm, 7)
+
+ self.assertEquals(posix_acl.acl[5].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[5].a_perm, 5)
+ self.assertEquals(posix_acl.acl[5].info.gid, SO_gid)
+
+ self.assertEquals(posix_acl.acl[6].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[6].a_perm, 7)
+ self.assertEquals(posix_acl.acl[6].info.gid, SY_gid)
+
+ self.assertEquals(posix_acl.acl[7].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[7].a_perm, 5)
+ self.assertEquals(posix_acl.acl[7].info.gid, AU_gid)
+
+ self.assertEquals(posix_acl.acl[8].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[8].a_perm, 7)
+ self.assertEquals(posix_acl.acl[8].info.gid, PA_gid)
+
+ self.assertEquals(posix_acl.acl[9].a_type, smb_acl.SMB_ACL_MASK)
+ self.assertEquals(posix_acl.acl[9].a_perm, 7)
+
+
+# check that it matches:
+# user::rwx
+# user:root:rwx (selftest user actually)
+# group::rwx
+# group:3000000:rwx
+# group:3000001:r-x
+# group:3000002:rwx
+# group:3000003:r-x
+# group:3000004:rwx
+# mask::rwx
+# other::---
+
+
+
+ def test_setntacl_policies_check_getposixacl(self):
+ acl = provision.POLICIES_ACL
+
+ domsid = passdb.get_global_sam_sid()
+ setntacl(self.lp, self.tempf, acl, str(domsid), use_ntvfs=False)
+ facl = getntacl(self.lp, self.tempf)
+ self.assertEquals(facl.as_sddl(domsid),acl)
+ posix_acl = smbd.get_sys_acl(self.tempf, smb_acl.SMB_ACL_TYPE_ACCESS)
+
+ LA_sid = security.dom_sid(str(domsid)+"-"+str(security.DOMAIN_RID_ADMINISTRATOR))
+ BA_sid = security.dom_sid(security.SID_BUILTIN_ADMINISTRATORS)
+ SO_sid = security.dom_sid(security.SID_BUILTIN_SERVER_OPERATORS)
+ SY_sid = security.dom_sid(security.SID_NT_SYSTEM)
+ AU_sid = security.dom_sid(security.SID_NT_AUTHENTICATED_USERS)
+ PA_sid = security.dom_sid(str(domsid)+"-"+str(security.DOMAIN_RID_POLICY_ADMINS))
+
+ s4_passdb = passdb.PDB(self.lp.get("passdb backend"))
+
+ # These assertions correct for current plugin_s4_dc selftest
+ # configuration. When other environments have a broad range of
+ # groups mapped via passdb, we can relax some of these checks
+ (LA_uid,LA_type) = s4_passdb.sid_to_id(LA_sid)
+ self.assertEquals(LA_type, idmap.ID_TYPE_UID)
+ (BA_gid,BA_type) = s4_passdb.sid_to_id(BA_sid)
+ self.assertEquals(BA_type, idmap.ID_TYPE_BOTH)
+ (SO_gid,SO_type) = s4_passdb.sid_to_id(SO_sid)
+ self.assertEquals(SO_type, idmap.ID_TYPE_BOTH)
+ (SY_gid,SY_type) = s4_passdb.sid_to_id(SY_sid)
+ self.assertEquals(SO_type, idmap.ID_TYPE_BOTH)
+ (AU_gid,AU_type) = s4_passdb.sid_to_id(AU_sid)
+ self.assertEquals(AU_type, idmap.ID_TYPE_BOTH)
+ (PA_gid,PA_type) = s4_passdb.sid_to_id(PA_sid)
+ self.assertEquals(PA_type, idmap.ID_TYPE_BOTH)
+
+ self.assertEquals(posix_acl.count, 10)
+
+ self.assertEquals(posix_acl.acl[0].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[0].a_perm, 7)
+ self.assertEquals(posix_acl.acl[0].info.gid, BA_gid)
+
+ self.assertEquals(posix_acl.acl[1].a_type, smb_acl.SMB_ACL_USER)
+ self.assertEquals(posix_acl.acl[1].a_perm, 6)
+ self.assertEquals(posix_acl.acl[1].info.uid, LA_uid)
+
+ self.assertEquals(posix_acl.acl[2].a_type, smb_acl.SMB_ACL_OTHER)
+ self.assertEquals(posix_acl.acl[2].a_perm, 0)
+
+ self.assertEquals(posix_acl.acl[3].a_type, smb_acl.SMB_ACL_USER_OBJ)
+ self.assertEquals(posix_acl.acl[3].a_perm, 6)
+
+ self.assertEquals(posix_acl.acl[4].a_type, smb_acl.SMB_ACL_GROUP_OBJ)
+ self.assertEquals(posix_acl.acl[4].a_perm, 7)
+
+ self.assertEquals(posix_acl.acl[5].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[5].a_perm, 5)
+ self.assertEquals(posix_acl.acl[5].info.gid, SO_gid)
+
+ self.assertEquals(posix_acl.acl[6].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[6].a_perm, 7)
+ self.assertEquals(posix_acl.acl[6].info.gid, SY_gid)
+
+ self.assertEquals(posix_acl.acl[7].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[7].a_perm, 5)
+ self.assertEquals(posix_acl.acl[7].info.gid, AU_gid)
+
+ self.assertEquals(posix_acl.acl[8].a_type, smb_acl.SMB_ACL_GROUP)
+ self.assertEquals(posix_acl.acl[8].a_perm, 7)
+ self.assertEquals(posix_acl.acl[8].info.gid, PA_gid)
+
+ self.assertEquals(posix_acl.acl[9].a_type, smb_acl.SMB_ACL_MASK)
+ self.assertEquals(posix_acl.acl[9].a_perm, 7)
+
+
+# check that it matches:
+# user::rwx
+# user:root:rwx (selftest user actually)
+# group::rwx
+# group:Local Admins:rwx
+# group:3000000:r-x
+# group:3000001:rwx
+# group:3000002:r-x
+# group:3000003:rwx
+# mask::rwx
+# other::---
+
+#
+# This is in this order in the NDR smb_acl (not re-orderded for display)
+# a_type: GROUP
+# a_perm: 7
+# uid: -1
+# gid: 10
+# a_type: USER
+# a_perm: 6
+# uid: 0 (selftest user actually)
+# gid: -1
+# a_type: OTHER
+# a_perm: 0
+# uid: -1
+# gid: -1
+# a_type: USER_OBJ
+# a_perm: 6
+# uid: -1
+# gid: -1
+# a_type: GROUP_OBJ
+# a_perm: 7
+# uid: -1
+# gid: -1
+# a_type: GROUP
+# a_perm: 5
+# uid: -1
+# gid: 3000020
+# a_type: GROUP
+# a_perm: 7
+# uid: -1
+# gid: 3000000
+# a_type: GROUP
+# a_perm: 5
+# uid: -1
+# gid: 3000001
+# a_type: GROUP
+# a_perm: 7
+# uid: -1
+# gid: 3000003
+# a_type: MASK
+# a_perm: 7
+# uid: -1
+# gid: -1
+
+#
+
+ def setUp(self):
+ super(PosixAclMappingTests, self).setUp()
+ s3conf = s3param.get_context()
+ s3conf.load(self.get_loadparm().configfile)
+ s3conf.set("xattr_tdb:file", os.path.join(self.tempdir,"xattr.tdb"))
+ self.lp = s3conf
+ self.tempf = os.path.join(self.tempdir, "test")
+ open(self.tempf, 'w').write("empty")
+
+ def tearDown(self):
+ smbd.unlink(self.tempf)
+ os.unlink(os.path.join(self.tempdir,"xattr.tdb"))
+ super(PosixAclMappingTests, self).tearDown()
diff --git a/python/samba/tests/provision.py b/python/samba/tests/provision.py
new file mode 100644
index 00000000000..929e7074f75
--- /dev/null
+++ b/python/samba/tests/provision.py
@@ -0,0 +1,203 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
+#
+# 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/>.
+#
+
+"""Tests for samba.provision."""
+
+import os
+from samba.provision import (
+ ProvisionNames,
+ ProvisionPaths,
+ ProvisionResult,
+ determine_netbios_name,
+ sanitize_server_role,
+ setup_secretsdb,
+ findnss,
+ )
+import samba.tests
+from samba.tests import env_loadparm, TestCase
+
+def create_dummy_secretsdb(path, lp=None):
+ """Create a dummy secrets database for use in tests.
+
+ :param path: Path to store the secrets db
+ :param lp: Optional loadparm context. A simple one will
+ be generated if not specified.
+ """
+ if lp is None:
+ lp = env_loadparm()
+ paths = ProvisionPaths()
+ paths.secrets = path
+ paths.private_dir = os.path.dirname(path)
+ paths.keytab = "no.keytab"
+ paths.dns_keytab = "no.dns.keytab"
+ secrets_ldb = setup_secretsdb(paths, None, None, lp=lp)
+ secrets_ldb.transaction_commit()
+ return secrets_ldb
+
+
+class ProvisionTestCase(samba.tests.TestCaseInTempDir):
+ """Some simple tests for individual functions in the provisioning code.
+ """
+
+ def test_setup_secretsdb(self):
+ path = os.path.join(self.tempdir, "secrets.ldb")
+ paths = ProvisionPaths()
+ secrets_tdb_path = os.path.join(self.tempdir, "secrets.tdb")
+ secrets_ntdb_path = os.path.join(self.tempdir, "secrets.ntdb")
+ paths.secrets = path
+ paths.private_dir = os.path.dirname(path)
+ paths.keytab = "no.keytab"
+ paths.dns_keytab = "no.dns.keytab"
+ ldb = setup_secretsdb(paths, None, None, lp=env_loadparm())
+ try:
+ self.assertEquals("LSA Secrets",
+ ldb.searchone(basedn="CN=LSA Secrets", attribute="CN"))
+ finally:
+ del ldb
+ os.unlink(path)
+ if os.path.exists(secrets_tdb_path):
+ os.unlink(secrets_tdb_path)
+ if os.path.exists(secrets_ntdb_path):
+ os.unlink(secrets_ntdb_path)
+
+class FindNssTests(TestCase):
+ """Test findnss() function."""
+
+ def test_nothing(self):
+ def x(y):
+ raise KeyError
+ self.assertRaises(KeyError, findnss, x, [])
+
+ def test_first(self):
+ self.assertEquals("bla", findnss(lambda x: "bla", ["bla"]))
+
+ def test_skip_first(self):
+ def x(y):
+ if y != "bla":
+ raise KeyError
+ return "ha"
+ self.assertEquals("ha", findnss(x, ["bloe", "bla"]))
+
+
+class Disabled(object):
+
+ def test_setup_templatesdb(self):
+ raise NotImplementedError(self.test_setup_templatesdb)
+
+ def test_setup_registry(self):
+ raise NotImplementedError(self.test_setup_registry)
+
+ def test_setup_samdb_rootdse(self):
+ raise NotImplementedError(self.test_setup_samdb_rootdse)
+
+ def test_setup_samdb_partitions(self):
+ raise NotImplementedError(self.test_setup_samdb_partitions)
+
+ def test_provision_dns(self):
+ raise NotImplementedError(self.test_provision_dns)
+
+ def test_provision_ldapbase(self):
+ raise NotImplementedError(self.test_provision_ldapbase)
+
+ def test_provision_guess(self):
+ raise NotImplementedError(self.test_provision_guess)
+
+ def test_join_domain(self):
+ raise NotImplementedError(self.test_join_domain)
+
+ def test_vampire(self):
+ raise NotImplementedError(self.test_vampire)
+
+
+class SanitizeServerRoleTests(TestCase):
+
+ def test_same(self):
+ self.assertEquals("standalone server",
+ sanitize_server_role("standalone server"))
+ self.assertEquals("member server",
+ sanitize_server_role("member server"))
+
+ def test_invalid(self):
+ self.assertRaises(ValueError, sanitize_server_role, "foo")
+
+ def test_valid(self):
+ self.assertEquals(
+ "standalone server",
+ sanitize_server_role("ROLE_STANDALONE"))
+ self.assertEquals(
+ "standalone server",
+ sanitize_server_role("standalone"))
+ self.assertEquals(
+ "active directory domain controller",
+ sanitize_server_role("domain controller"))
+
+
+class DummyLogger(object):
+
+ def __init__(self):
+ self.entries = []
+
+ def info(self, text, *args):
+ self.entries.append(("INFO", text % args))
+
+
+class ProvisionResultTests(TestCase):
+
+ def report_logger(self, result):
+ logger = DummyLogger()
+ result.report_logger(logger)
+ return logger.entries
+
+ def base_result(self):
+ result = ProvisionResult()
+ result.server_role = "domain controller"
+ result.names = ProvisionNames()
+ result.names.hostname = "hostnaam"
+ result.names.domain = "DOMEIN"
+ result.names.dnsdomain = "dnsdomein"
+ result.domainsid = "S1-1-1"
+ result.paths = ProvisionPaths()
+ return result
+
+ def test_basic_report_logger(self):
+ result = self.base_result()
+ entries = self.report_logger(result)
+ self.assertEquals(entries, [
+ ('INFO', 'Once the above files are installed, your Samba4 server '
+ 'will be ready to use'),
+ ('INFO', 'Server Role: domain controller'),
+ ('INFO', 'Hostname: hostnaam'),
+ ('INFO', 'NetBIOS Domain: DOMEIN'),
+ ('INFO', 'DNS Domain: dnsdomein'),
+ ('INFO', 'DOMAIN SID: S1-1-1')])
+
+ def test_report_logger_adminpass(self):
+ result = self.base_result()
+ result.adminpass_generated = True
+ result.adminpass = "geheim"
+ entries = self.report_logger(result)
+ self.assertEquals(entries[1],
+ ("INFO", 'Admin password: geheim'))
+
+
+class DetermineNetbiosNameTests(TestCase):
+
+ def test_limits_to_15(self):
+ self.assertEquals("A" * 15, determine_netbios_name("a" * 30))
+
+ def test_strips_invalid(self):
+ self.assertEquals("BLABLA", determine_netbios_name("bla/bla"))
diff --git a/python/samba/tests/registry.py b/python/samba/tests/registry.py
new file mode 100644
index 00000000000..8016a0bb686
--- /dev/null
+++ b/python/samba/tests/registry.py
@@ -0,0 +1,60 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+#
+# 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/>.
+#
+
+"""Tests for samba.registry."""
+
+import os
+from samba import registry
+import samba.tests
+
+class HelperTests(samba.tests.TestCase):
+
+ def test_predef_to_name(self):
+ self.assertEquals("HKEY_LOCAL_MACHINE",
+ registry.get_predef_name(0x80000002))
+
+ def test_str_regtype(self):
+ self.assertEquals("REG_DWORD", registry.str_regtype(4))
+
+
+
+class HiveTests(samba.tests.TestCaseInTempDir):
+
+ def setUp(self):
+ super(HiveTests, self).setUp()
+ self.hive_path = os.path.join(self.tempdir, "ldb_new.ldb")
+ self.hive = registry.open_ldb(self.hive_path)
+
+ def tearDown(self):
+ del self.hive
+ os.unlink(self.hive_path)
+ super(HiveTests, self).tearDown()
+
+ def test_ldb_new(self):
+ self.assertTrue(self.hive is not None)
+
+ #def test_flush(self):
+ # self.hive.flush()
+
+ #def test_del_value(self):
+ # self.hive.del_value("FOO")
+
+
+class RegistryTests(samba.tests.TestCase):
+
+ def test_new(self):
+ self.registry = registry.Registry()
diff --git a/python/samba/tests/samba3.py b/python/samba/tests/samba3.py
new file mode 100644
index 00000000000..0a7f13c66fa
--- /dev/null
+++ b/python/samba/tests/samba3.py
@@ -0,0 +1,219 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+#
+# 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/>.
+#
+
+"""Tests for samba.samba3."""
+
+from samba.samba3 import (
+ Registry,
+ WinsDatabase,
+ IdmapDatabase,
+ )
+from samba.samba3 import passdb
+from samba.samba3 import param as s3param
+from samba.tests import TestCase, TestCaseInTempDir
+from samba.dcerpc.security import dom_sid
+import os
+
+
+for p in [ "../../../../../testdata/samba3", "../../../../testdata/samba3" ]:
+ DATADIR = os.path.join(os.path.dirname(__file__), p)
+ if os.path.exists(DATADIR):
+ break
+
+
+class RegistryTestCase(TestCase):
+
+ def setUp(self):
+ super(RegistryTestCase, self).setUp()
+ self.registry = Registry(os.path.join(DATADIR, "registry.tdb"))
+
+ def tearDown(self):
+ self.registry.close()
+ super(RegistryTestCase, self).tearDown()
+
+ def test_length(self):
+ self.assertEquals(28, len(self.registry))
+
+ def test_keys(self):
+ self.assertTrue("HKLM" in self.registry.keys())
+
+ def test_subkeys(self):
+ self.assertEquals(["SOFTWARE", "SYSTEM"], self.registry.subkeys("HKLM"))
+
+ def test_values(self):
+ self.assertEquals({'DisplayName': (1L, 'E\x00v\x00e\x00n\x00t\x00 \x00L\x00o\x00g\x00\x00\x00'),
+ 'ErrorControl': (4L, '\x01\x00\x00\x00')},
+ self.registry.values("HKLM/SYSTEM/CURRENTCONTROLSET/SERVICES/EVENTLOG"))
+
+
+class PassdbTestCase(TestCaseInTempDir):
+
+ def setUp(self):
+ super (PassdbTestCase, self).setUp()
+ os.system("cp -r %s %s" % (DATADIR, self.tempdir))
+ datadir = os.path.join(self.tempdir, "samba3")
+
+ self.lp = s3param.get_context()
+ self.lp.load(os.path.join(datadir, "smb.conf"))
+ self.lp.set("private dir", datadir)
+ self.lp.set("state directory", datadir)
+ self.lp.set("lock directory", datadir)
+ passdb.set_secrets_dir(datadir)
+ self.pdb = passdb.PDB("tdbsam")
+
+ def tearDown(self):
+ self.lp = []
+ self.pdb = []
+ os.system("rm -rf %s" % os.path.join(self.tempdir, "samba3"))
+ super(PassdbTestCase, self).tearDown()
+
+ def test_param(self):
+ self.assertEquals("BEDWYR", self.lp.get("netbios name"))
+ self.assertEquals("SAMBA", self.lp.get("workgroup"))
+ self.assertEquals("USER", self.lp.get("security"))
+
+ def test_policy(self):
+ policy = self.pdb.get_account_policy()
+ self.assertEquals(0, policy['bad lockout attempt'])
+ self.assertEquals(-1, policy['disconnect time'])
+ self.assertEquals(0, policy['lockout duration'])
+ self.assertEquals(999999999, policy['maximum password age'])
+ self.assertEquals(0, policy['minimum password age'])
+ self.assertEquals(5, policy['min password length'])
+ self.assertEquals(0, policy['password history'])
+ self.assertEquals(0, policy['refuse machine password change'])
+ self.assertEquals(0, policy['reset count minutes'])
+ self.assertEquals(0, policy['user must logon to change password'])
+
+ def test_get_sid(self):
+ domain_sid = passdb.get_global_sam_sid()
+ self.assertEquals(dom_sid("S-1-5-21-2470180966-3899876309-2637894779"), domain_sid)
+
+ def test_usernames(self):
+ userlist = self.pdb.search_users(0)
+ self.assertEquals(3, len(userlist))
+
+ def test_getuser(self):
+ user = self.pdb.getsampwnam("root")
+
+ self.assertEquals(16, user.acct_ctrl)
+ self.assertEquals("", user.acct_desc)
+ self.assertEquals(0, user.bad_password_count)
+ self.assertEquals(0, user.bad_password_time)
+ self.assertEquals(0, user.code_page)
+ self.assertEquals(0, user.country_code)
+ self.assertEquals("", user.dir_drive)
+ self.assertEquals("BEDWYR", user.domain)
+ self.assertEquals("root", user.full_name)
+ self.assertEquals(dom_sid('S-1-5-21-2470180966-3899876309-2637894779-513'), user.group_sid)
+ self.assertEquals("\\\\BEDWYR\\root", user.home_dir)
+ self.assertEquals([-1 for i in range(21)], user.hours)
+ self.assertEquals(21, user.hours_len)
+ self.assertEquals(9223372036854775807, user.kickoff_time)
+ self.assertEquals(None, user.lanman_passwd)
+ self.assertEquals(9223372036854775807, user.logoff_time)
+ self.assertEquals(0, user.logon_count)
+ self.assertEquals(168, user.logon_divs)
+ self.assertEquals("", user.logon_script)
+ self.assertEquals(0, user.logon_time)
+ self.assertEquals("", user.munged_dial)
+ self.assertEquals('\x87\x8d\x80\x14`l\xda)gzD\xef\xa15?\xc7', user.nt_passwd)
+ self.assertEquals("", user.nt_username)
+ self.assertEquals(1125418267, user.pass_can_change_time)
+ self.assertEquals(1125418267, user.pass_last_set_time)
+ self.assertEquals(2125418266, user.pass_must_change_time)
+ self.assertEquals(None, user.plaintext_passwd)
+ self.assertEquals("\\\\BEDWYR\\root\\profile", user.profile_path)
+ self.assertEquals(None, user.pw_history)
+ self.assertEquals(dom_sid("S-1-5-21-2470180966-3899876309-2637894779-1000"), user.user_sid)
+ self.assertEquals("root", user.username)
+ self.assertEquals("", user.workstations)
+
+ def test_group_length(self):
+ grouplist = self.pdb.enum_group_mapping()
+ self.assertEquals(13, len(grouplist))
+
+ def test_get_group(self):
+ group = self.pdb.getgrsid(dom_sid("S-1-5-32-544"))
+ self.assertEquals("Administrators", group.nt_name)
+ self.assertEquals(-1, group.gid)
+ self.assertEquals(5, group.sid_name_use)
+
+ def test_groupsids(self):
+ grouplist = self.pdb.enum_group_mapping()
+ sids = []
+ for g in grouplist:
+ sids.append(str(g.sid))
+ self.assertTrue("S-1-5-32-544" in sids)
+ self.assertTrue("S-1-5-32-545" in sids)
+ self.assertTrue("S-1-5-32-546" in sids)
+ self.assertTrue("S-1-5-32-548" in sids)
+ self.assertTrue("S-1-5-32-549" in sids)
+ self.assertTrue("S-1-5-32-550" in sids)
+ self.assertTrue("S-1-5-32-551" in sids)
+
+ def test_alias_length(self):
+ aliaslist = self.pdb.search_aliases()
+ self.assertEquals(1, len(aliaslist))
+ self.assertEquals("Jelmers NT Group", aliaslist[0]['account_name'])
+
+
+class WinsDatabaseTestCase(TestCase):
+
+ def setUp(self):
+ super(WinsDatabaseTestCase, self).setUp()
+ self.winsdb = WinsDatabase(os.path.join(DATADIR, "wins.dat"))
+
+ def test_length(self):
+ self.assertEquals(22, len(self.winsdb))
+
+ def test_first_entry(self):
+ self.assertEqual((1124185120, ["192.168.1.5"], 0x64), self.winsdb["ADMINISTRATOR#03"])
+
+ def tearDown(self):
+ self.winsdb.close()
+ super(WinsDatabaseTestCase, self).tearDown()
+
+
+class IdmapDbTestCase(TestCase):
+
+ def setUp(self):
+ super(IdmapDbTestCase, self).setUp()
+ self.idmapdb = IdmapDatabase(os.path.join(DATADIR,
+ "winbindd_idmap.tdb"))
+
+ def test_user_hwm(self):
+ self.assertEquals(10000, self.idmapdb.get_user_hwm())
+
+ def test_group_hwm(self):
+ self.assertEquals(10002, self.idmapdb.get_group_hwm())
+
+ def test_uids(self):
+ self.assertEquals(1, len(list(self.idmapdb.uids())))
+
+ def test_gids(self):
+ self.assertEquals(3, len(list(self.idmapdb.gids())))
+
+ def test_get_user_sid(self):
+ self.assertEquals("S-1-5-21-58189338-3053988021-627566699-501", self.idmapdb.get_user_sid(65534))
+
+ def test_get_group_sid(self):
+ self.assertEquals("S-1-5-21-2447931902-1787058256-3961074038-3007", self.idmapdb.get_group_sid(10001))
+
+ def tearDown(self):
+ self.idmapdb.close()
+ super(IdmapDbTestCase, self).tearDown()
diff --git a/python/samba/tests/samba3sam.py b/python/samba/tests/samba3sam.py
new file mode 100644
index 00000000000..9c017fb79c3
--- /dev/null
+++ b/python/samba/tests/samba3sam.py
@@ -0,0 +1,1125 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2005-2008
+# Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+#
+# This is a Python port of the original in testprogs/ejs/samba3sam.js
+#
+# 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/>.
+#
+
+"""Tests for the samba3sam LDB module, which maps Samba3 LDAP to AD LDAP."""
+
+import os
+import ldb
+from ldb import SCOPE_DEFAULT, SCOPE_BASE
+from samba import Ldb, substitute_var
+from samba.tests import TestCaseInTempDir, env_loadparm
+import samba.dcerpc.security
+import samba.ndr
+from samba.auth import system_session
+from operator import attrgetter
+
+
+def read_datafile(filename):
+ paths = [ "../../../../../testdata/samba3",
+ "../../../../testdata/samba3" ]
+ for p in paths:
+ datadir = os.path.join(os.path.dirname(__file__), p)
+ if os.path.exists(datadir):
+ break
+ return open(os.path.join(datadir, filename), 'r').read()
+
+def ldb_debug(l, text):
+ print text
+
+
+class MapBaseTestCase(TestCaseInTempDir):
+ """Base test case for mapping tests."""
+
+ def setup_modules(self, ldb, s3, s4):
+ ldb.add({"dn": "@MAP=samba3sam",
+ "@FROM": s4.basedn,
+ "@TO": "sambaDomainName=TESTS," + s3.basedn})
+
+ ldb.add({"dn": "@MODULES",
+ "@LIST": "rootdse,paged_results,server_sort,asq,samldb,password_hash,operational,objectguid,rdn_name,samba3sam,samba3sid,show_deleted,partition"})
+
+ ldb.add({"dn": "@PARTITION",
+ "partition": ["%s" % (s4.basedn_casefold),
+ "%s" % (s3.basedn_casefold)],
+ "replicateEntries": ["@ATTRIBUTES", "@INDEXLIST"],
+ "modules": "*:"})
+
+ def setUp(self):
+ self.lp = env_loadparm()
+ self.lp.set("workgroup", "TESTS")
+ self.lp.set("netbios name", "TESTS")
+ super(MapBaseTestCase, self).setUp()
+
+ def make_dn(basedn, rdn):
+ return "%s,sambaDomainName=TESTS,%s" % (rdn, basedn)
+
+ def make_s4dn(basedn, rdn):
+ return "%s,%s" % (rdn, basedn)
+
+ self.ldbfile = os.path.join(self.tempdir, "test.ldb")
+ self.ldburl = "tdb://" + self.ldbfile
+
+ tempdir = self.tempdir
+
+ class Target:
+ """Simple helper class that contains data for a specific SAM
+ connection."""
+
+ def __init__(self, basedn, dn, lp):
+ self.db = Ldb(lp=lp, session_info=system_session())
+ self.db.set_opaque("skip_allocate_sids", "true");
+ self.basedn = basedn
+ self.basedn_casefold = ldb.Dn(self.db, basedn).get_casefold()
+ self.substvars = {"BASEDN": self.basedn}
+ self.file = os.path.join(tempdir, "%s.ldb" % self.basedn_casefold)
+ self.url = "tdb://" + self.file
+ self._dn = dn
+
+ def dn(self, rdn):
+ return self._dn(self.basedn, rdn)
+
+ def connect(self):
+ return self.db.connect(self.url)
+
+ def setup_data(self, path):
+ self.add_ldif(read_datafile(path))
+
+ def subst(self, text):
+ return substitute_var(text, self.substvars)
+
+ def add_ldif(self, ldif):
+ self.db.add_ldif(self.subst(ldif))
+
+ def modify_ldif(self, ldif):
+ self.db.modify_ldif(self.subst(ldif))
+
+ self.samba4 = Target("dc=vernstok,dc=nl", make_s4dn, self.lp)
+ self.samba3 = Target("cn=Samba3Sam", make_dn, self.lp)
+
+ self.samba3.connect()
+ self.samba4.connect()
+
+ def tearDown(self):
+ os.unlink(self.ldbfile)
+ os.unlink(self.samba3.file)
+ os.unlink(self.samba4.file)
+ pdir = "%s.d" % self.ldbfile
+ mdata = os.path.join(pdir, "metadata.tdb")
+ if os.path.exists(mdata):
+ os.unlink(mdata)
+ os.rmdir(pdir)
+ super(MapBaseTestCase, self).tearDown()
+
+ def assertSidEquals(self, text, ndr_sid):
+ sid_obj1 = samba.ndr.ndr_unpack(samba.dcerpc.security.dom_sid,
+ str(ndr_sid[0]))
+ sid_obj2 = samba.dcerpc.security.dom_sid(text)
+ self.assertEquals(sid_obj1, sid_obj2)
+
+
+class Samba3SamTestCase(MapBaseTestCase):
+
+ def setUp(self):
+ super(Samba3SamTestCase, self).setUp()
+ ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
+ ldb.set_opaque("skip_allocate_sids", "true");
+ self.samba3.setup_data("samba3.ldif")
+ ldif = read_datafile("provision_samba3sam.ldif")
+ ldb.add_ldif(self.samba4.subst(ldif))
+ self.setup_modules(ldb, self.samba3, self.samba4)
+ del ldb
+ self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
+ self.ldb.set_opaque("skip_allocate_sids", "true");
+
+ def test_search_non_mapped(self):
+ """Looking up by non-mapped attribute"""
+ msg = self.ldb.search(expression="(cn=Administrator)")
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(msg[0]["cn"], "Administrator")
+
+ def test_search_non_mapped(self):
+ """Looking up by mapped attribute"""
+ msg = self.ldb.search(expression="(name=Backup Operators)")
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(str(msg[0]["name"]), "Backup Operators")
+
+ def test_old_name_of_renamed(self):
+ """Looking up by old name of renamed attribute"""
+ msg = self.ldb.search(expression="(displayName=Backup Operators)")
+ self.assertEquals(len(msg), 0)
+
+ def test_mapped_containing_sid(self):
+ """Looking up mapped entry containing SID"""
+ msg = self.ldb.search(expression="(cn=Replicator)")
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(str(msg[0].dn),
+ "cn=Replicator,ou=Groups,dc=vernstok,dc=nl")
+ self.assertTrue("objectSid" in msg[0])
+ self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552",
+ msg[0]["objectSid"])
+ oc = set(msg[0]["objectClass"])
+ self.assertEquals(oc, set(["group"]))
+
+ def test_search_by_objclass(self):
+ """Looking up by objectClass"""
+ msg = self.ldb.search(expression="(|(objectClass=user)(cn=Administrator))")
+ self.assertEquals(set([str(m.dn) for m in msg]),
+ set(["unixName=Administrator,ou=Users,dc=vernstok,dc=nl",
+ "unixName=nobody,ou=Users,dc=vernstok,dc=nl"]))
+
+ def test_s3sam_modify(self):
+ # Adding a record that will be fallbacked
+ self.ldb.add({"dn": "cn=Foo",
+ "foo": "bar",
+ "blah": "Blie",
+ "cn": "Foo",
+ "showInAdvancedViewOnly": "TRUE"}
+ )
+
+ # Checking for existence of record (local)
+ # TODO: This record must be searched in the local database, which is
+ # currently only supported for base searches
+ # msg = ldb.search(expression="(cn=Foo)", ['foo','blah','cn','showInAdvancedViewOnly')]
+ # TODO: Actually, this version should work as well but doesn't...
+ #
+ #
+ msg = self.ldb.search(expression="(cn=Foo)", base="cn=Foo",
+ scope=SCOPE_BASE,
+ attrs=['foo','blah','cn','showInAdvancedViewOnly'])
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(str(msg[0]["showInAdvancedViewOnly"]), "TRUE")
+ self.assertEquals(str(msg[0]["foo"]), "bar")
+ self.assertEquals(str(msg[0]["blah"]), "Blie")
+
+ # Adding record that will be mapped
+ self.ldb.add({"dn": "cn=Niemand,cn=Users,dc=vernstok,dc=nl",
+ "objectClass": "user",
+ "unixName": "bin",
+ "sambaUnicodePwd": "geheim",
+ "cn": "Niemand"})
+
+ # Checking for existence of record (remote)
+ msg = self.ldb.search(expression="(unixName=bin)",
+ attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(str(msg[0]["cn"]), "Niemand")
+ self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim")
+
+ # Checking for existence of record (local && remote)
+ msg = self.ldb.search(expression="(&(unixName=bin)(sambaUnicodePwd=geheim))",
+ attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
+ self.assertEquals(len(msg), 1) # TODO: should check with more records
+ self.assertEquals(str(msg[0]["cn"]), "Niemand")
+ self.assertEquals(str(msg[0]["unixName"]), "bin")
+ self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim")
+
+ # Checking for existence of record (local || remote)
+ msg = self.ldb.search(expression="(|(unixName=bin)(sambaUnicodePwd=geheim))",
+ attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
+ #print "got %d replies" % len(msg)
+ self.assertEquals(len(msg), 1) # TODO: should check with more records
+ self.assertEquals(str(msg[0]["cn"]), "Niemand")
+ self.assertEquals(str(msg[0]["unixName"]), "bin")
+ self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim")
+
+ # Checking for data in destination database
+ msg = self.samba3.db.search(expression="(cn=Niemand)")
+ self.assertTrue(len(msg) >= 1)
+ self.assertEquals(str(msg[0]["sambaSID"]),
+ "S-1-5-21-4231626423-2410014848-2360679739-2001")
+ self.assertEquals(str(msg[0]["displayName"]), "Niemand")
+
+ # Adding attribute...
+ self.ldb.modify_ldif("""
+dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
+changetype: modify
+add: description
+description: Blah
+""")
+
+ # Checking whether changes are still there...
+ msg = self.ldb.search(expression="(cn=Niemand)")
+ self.assertTrue(len(msg) >= 1)
+ self.assertEquals(str(msg[0]["cn"]), "Niemand")
+ self.assertEquals(str(msg[0]["description"]), "Blah")
+
+ # Modifying attribute...
+ self.ldb.modify_ldif("""
+dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
+changetype: modify
+replace: description
+description: Blie
+""")
+
+ # Checking whether changes are still there...
+ msg = self.ldb.search(expression="(cn=Niemand)")
+ self.assertTrue(len(msg) >= 1)
+ self.assertEquals(str(msg[0]["description"]), "Blie")
+
+ # Deleting attribute...
+ self.ldb.modify_ldif("""
+dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
+changetype: modify
+delete: description
+""")
+
+ # Checking whether changes are no longer there...
+ msg = self.ldb.search(expression="(cn=Niemand)")
+ self.assertTrue(len(msg) >= 1)
+ self.assertTrue(not "description" in msg[0])
+
+ # Renaming record...
+ self.ldb.rename("cn=Niemand,cn=Users,dc=vernstok,dc=nl",
+ "cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
+
+ # Checking whether DN has changed...
+ msg = self.ldb.search(expression="(cn=Niemand2)")
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(str(msg[0].dn),
+ "cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
+
+ # Deleting record...
+ self.ldb.delete("cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
+
+ # Checking whether record is gone...
+ msg = self.ldb.search(expression="(cn=Niemand2)")
+ self.assertEquals(len(msg), 0)
+
+
+class MapTestCase(MapBaseTestCase):
+
+ def setUp(self):
+ super(MapTestCase, self).setUp()
+ ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
+ ldb.set_opaque("skip_allocate_sids", "true");
+ ldif = read_datafile("provision_samba3sam.ldif")
+ ldb.add_ldif(self.samba4.subst(ldif))
+ self.setup_modules(ldb, self.samba3, self.samba4)
+ del ldb
+ self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
+ self.ldb.set_opaque("skip_allocate_sids", "true");
+
+ def test_map_search(self):
+ """Running search tests on mapped data."""
+ self.samba3.db.add({
+ "dn": "sambaDomainName=TESTS," + self.samba3.basedn,
+ "objectclass": ["sambaDomain", "top"],
+ "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739",
+ "sambaNextRid": "2000",
+ "sambaDomainName": "TESTS"
+ })
+
+ # Add a set of split records
+ self.ldb.add_ldif("""
+dn: """+ self.samba4.dn("cn=Domain Users") + """
+objectClass: group
+cn: Domain Users
+objectSid: S-1-5-21-4231626423-2410014848-2360679739-513
+""")
+
+ # Add a set of split records
+ self.ldb.add_ldif("""
+dn: """+ self.samba4.dn("cn=X") + """
+objectClass: user
+cn: X
+codePage: x
+revision: x
+dnsHostName: x
+nextRid: y
+lastLogon: x
+description: x
+objectSid: S-1-5-21-4231626423-2410014848-2360679739-552
+""")
+
+ self.ldb.add({
+ "dn": self.samba4.dn("cn=Y"),
+ "objectClass": "top",
+ "cn": "Y",
+ "codePage": "x",
+ "revision": "x",
+ "dnsHostName": "y",
+ "nextRid": "y",
+ "lastLogon": "y",
+ "description": "x"})
+
+ self.ldb.add({
+ "dn": self.samba4.dn("cn=Z"),
+ "objectClass": "top",
+ "cn": "Z",
+ "codePage": "x",
+ "revision": "y",
+ "dnsHostName": "z",
+ "nextRid": "y",
+ "lastLogon": "z",
+ "description": "y"})
+
+ # Add a set of remote records
+
+ self.samba3.db.add({
+ "dn": self.samba3.dn("cn=A"),
+ "objectClass": "posixAccount",
+ "cn": "A",
+ "sambaNextRid": "x",
+ "sambaBadPasswordCount": "x",
+ "sambaLogonTime": "x",
+ "description": "x",
+ "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739-552",
+ "sambaPrimaryGroupSID": "S-1-5-21-4231626423-2410014848-2360679739-512"})
+
+ self.samba3.db.add({
+ "dn": self.samba3.dn("cn=B"),
+ "objectClass": "top",
+ "cn": "B",
+ "sambaNextRid": "x",
+ "sambaBadPasswordCount": "x",
+ "sambaLogonTime": "y",
+ "description": "x"})
+
+ self.samba3.db.add({
+ "dn": self.samba3.dn("cn=C"),
+ "objectClass": "top",
+ "cn": "C",
+ "sambaNextRid": "x",
+ "sambaBadPasswordCount": "y",
+ "sambaLogonTime": "z",
+ "description": "y"})
+
+ # Testing search by DN
+
+ # Search remote record by local DN
+ dn = self.samba4.dn("cn=A")
+ res = self.ldb.search(dn, scope=SCOPE_BASE,
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+
+ # Search remote record by remote DN
+ dn = self.samba3.dn("cn=A")
+ res = self.samba3.db.search(dn, scope=SCOPE_BASE,
+ attrs=["dnsHostName", "lastLogon", "sambaLogonTime"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertTrue(not "lastLogon" in res[0])
+ self.assertEquals(str(res[0]["sambaLogonTime"]), "x")
+
+ # Search split record by local DN
+ dn = self.samba4.dn("cn=X")
+ res = self.ldb.search(dn, scope=SCOPE_BASE,
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["dnsHostName"]), "x")
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+
+ # Search split record by remote DN
+ dn = self.samba3.dn("cn=X")
+ res = self.samba3.db.search(dn, scope=SCOPE_BASE,
+ attrs=["dnsHostName", "lastLogon", "sambaLogonTime"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertTrue(not "lastLogon" in res[0])
+ self.assertEquals(str(res[0]["sambaLogonTime"]), "x")
+
+ # Testing search by attribute
+
+ # Search by ignored attribute
+ res = self.ldb.search(expression="(revision=x)", scope=SCOPE_DEFAULT,
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "x")
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "y")
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+
+ # Search by kept attribute
+ res = self.ldb.search(expression="(description=y)",
+ scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "z")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "z")
+ self.assertEquals(str(res[1]["lastLogon"]), "z")
+
+ # Search by renamed attribute
+ res = self.ldb.search(expression="(badPwdCount=x)", scope=SCOPE_DEFAULT,
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+
+ # Search by converted attribute
+ # TODO:
+ # Using the SID directly in the parse tree leads to conversion
+ # errors, letting the search fail with no results.
+ #res = self.ldb.search("(objectSid=S-1-5-21-4231626423-2410014848-2360679739-552)", scope=SCOPE_DEFAULT, attrs)
+ res = self.ldb.search(expression="(objectSid=*)", base=None, scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon", "objectSid"])
+ self.assertEquals(len(res), 4)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "x")
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552",
+ res[1]["objectSid"])
+ self.assertTrue("objectSid" in res[1])
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552",
+ res[0]["objectSid"])
+ self.assertTrue("objectSid" in res[0])
+
+ # Search by generated attribute
+ # In most cases, this even works when the mapping is missing
+ # a `convert_operator' by enumerating the remote db.
+ res = self.ldb.search(expression="(primaryGroupID=512)",
+ attrs=["dnsHostName", "lastLogon", "primaryGroupID"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[0]["primaryGroupID"]), "512")
+
+ # Note that Xs "objectSid" seems to be fine in the previous search for
+ # "objectSid"...
+ #res = ldb.search(expression="(primaryGroupID=*)", NULL, ldb. SCOPE_DEFAULT, attrs)
+ #print len(res) + " results found"
+ #for i in range(len(res)):
+ # for (obj in res[i]) {
+ # print obj + ": " + res[i][obj]
+ # }
+ # print "---"
+ #
+
+ # Search by remote name of renamed attribute */
+ res = self.ldb.search(expression="(sambaBadPasswordCount=*)",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 0)
+
+ # Search by objectClass
+ attrs = ["dnsHostName", "lastLogon", "objectClass"]
+ res = self.ldb.search(expression="(objectClass=user)", attrs=attrs)
+ self.assertEquals(len(res), 2)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[0]["objectClass"][0]), "user")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "x")
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertEquals(str(res[1]["objectClass"][0]), "user")
+
+ # Prove that the objectClass is actually used for the search
+ res = self.ldb.search(expression="(|(objectClass=user)(badPwdCount=x))",
+ attrs=attrs)
+ self.assertEquals(len(res), 3)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(res[0]["objectClass"][0], "user")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+ self.assertEquals(set(res[1]["objectClass"]), set(["top"]))
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "x")
+ self.assertEquals(str(res[2]["lastLogon"]), "x")
+ self.assertEquals(str(res[2]["objectClass"][0]), "user")
+
+ # Testing search by parse tree
+
+ # Search by conjunction of local attributes
+ res = self.ldb.search(expression="(&(codePage=x)(revision=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "x")
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "y")
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+
+ # Search by conjunction of remote attributes
+ res = self.ldb.search(expression="(&(lastLogon=x)(description=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "x")
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+
+ # Search by conjunction of local and remote attribute
+ res = self.ldb.search(expression="(&(codePage=x)(description=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "x")
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "y")
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+
+ # Search by conjunction of local and remote attribute w/o match
+ attrs = ["dnsHostName", "lastLogon"]
+ res = self.ldb.search(expression="(&(codePage=x)(nextRid=x))",
+ attrs=attrs)
+ self.assertEquals(len(res), 0)
+ res = self.ldb.search(expression="(&(revision=x)(lastLogon=z))",
+ attrs=attrs)
+ self.assertEquals(len(res), 0)
+
+ # Search by disjunction of local attributes
+ res = self.ldb.search(expression="(|(revision=x)(dnsHostName=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "x")
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "y")
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+
+ # Search by disjunction of remote attributes
+ res = self.ldb.search(expression="(|(badPwdCount=x)(lastLogon=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 3)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertFalse("dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
+ self.assertFalse("dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "x")
+ self.assertEquals(str(res[2]["lastLogon"]), "x")
+
+ # Search by disjunction of local and remote attribute
+ res = self.ldb.search(expression="(|(revision=x)(lastLogon=y))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 3)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
+ self.assertFalse("dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "x")
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "y")
+ self.assertEquals(str(res[2]["lastLogon"]), "y")
+
+ # Search by disjunction of local and remote attribute w/o match
+ res = self.ldb.search(expression="(|(codePage=y)(nextRid=z))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 0)
+
+ # Search by negated local attribute
+ res = self.ldb.search(expression="(!(revision=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 6)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[2])
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[3]["dnsHostName"]), "z")
+ self.assertEquals(str(res[3]["lastLogon"]), "z")
+
+ # Search by negated remote attribute
+ res = self.ldb.search(expression="(!(description=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 4)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "z")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "z")
+ self.assertEquals(str(res[1]["lastLogon"]), "z")
+
+ # Search by negated conjunction of local attributes
+ res = self.ldb.search(expression="(!(&(codePage=x)(revision=x)))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 6)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[2])
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[3]["dnsHostName"]), "z")
+ self.assertEquals(str(res[3]["lastLogon"]), "z")
+
+ # Search by negated conjunction of remote attributes
+ res = self.ldb.search(expression="(!(&(lastLogon=x)(description=x)))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 6)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "z")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "y")
+ self.assertEquals(str(res[2]["lastLogon"]), "y")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[3]["dnsHostName"]), "z")
+ self.assertEquals(str(res[3]["lastLogon"]), "z")
+
+ # Search by negated conjunction of local and remote attribute
+ res = self.ldb.search(expression="(!(&(codePage=x)(description=x)))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 6)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[2])
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[3]["dnsHostName"]), "z")
+ self.assertEquals(str(res[3]["lastLogon"]), "z")
+
+ # Search by negated disjunction of local attributes
+ res = self.ldb.search(expression="(!(|(revision=x)(dnsHostName=x)))",
+ attrs=["dnsHostName", "lastLogon"])
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[2])
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[3]["dnsHostName"]), "z")
+ self.assertEquals(str(res[3]["lastLogon"]), "z")
+
+ # Search by negated disjunction of remote attributes
+ res = self.ldb.search(expression="(!(|(badPwdCount=x)(lastLogon=x)))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 5)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "z")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "y")
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "z")
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+
+ # Search by negated disjunction of local and remote attribute
+ res = self.ldb.search(expression="(!(|(revision=x)(lastLogon=y)))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 5)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "z")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "z")
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+
+ # Search by complex parse tree
+ res = self.ldb.search(expression="(|(&(revision=x)(dnsHostName=x))(!(&(description=x)(nextRid=y)))(badPwdCount=y))", attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 7)
+ res = sorted(res, key=attrgetter('dn'))
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[2])
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[3]["dnsHostName"]), "x")
+ self.assertEquals(str(res[3]["lastLogon"]), "x")
+ self.assertEquals(str(res[4].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[4]["dnsHostName"]), "z")
+ self.assertEquals(str(res[4]["lastLogon"]), "z")
+
+ # Clean up
+ dns = [self.samba4.dn("cn=%s" % n) for n in ["A","B","C","X","Y","Z"]]
+ for dn in dns:
+ self.ldb.delete(dn)
+
+ def test_map_modify_local(self):
+ """Modification of local records."""
+ # Add local record
+ dn = "cn=test,dc=idealx,dc=org"
+ self.ldb.add({"dn": dn,
+ "cn": "test",
+ "foo": "bar",
+ "revision": "1",
+ "description": "test"})
+ # Check it's there
+ attrs = ["foo", "revision", "description"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["foo"]), "bar")
+ self.assertEquals(str(res[0]["revision"]), "1")
+ self.assertEquals(str(res[0]["description"]), "test")
+ # Check it's not in the local db
+ res = self.samba4.db.search(expression="(cn=test)",
+ scope=SCOPE_DEFAULT, attrs=attrs)
+ self.assertEquals(len(res), 0)
+ # Check it's not in the remote db
+ res = self.samba3.db.search(expression="(cn=test)",
+ scope=SCOPE_DEFAULT, attrs=attrs)
+ self.assertEquals(len(res), 0)
+
+ # Modify local record
+ ldif = """
+dn: """ + dn + """
+replace: foo
+foo: baz
+replace: description
+description: foo
+"""
+ self.ldb.modify_ldif(ldif)
+ # Check in local db
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["foo"]), "baz")
+ self.assertEquals(str(res[0]["revision"]), "1")
+ self.assertEquals(str(res[0]["description"]), "foo")
+
+ # Rename local record
+ dn2 = "cn=toast,dc=idealx,dc=org"
+ self.ldb.rename(dn, dn2)
+ # Check in local db
+ res = self.ldb.search(dn2, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["foo"]), "baz")
+ self.assertEquals(str(res[0]["revision"]), "1")
+ self.assertEquals(str(res[0]["description"]), "foo")
+
+ # Delete local record
+ self.ldb.delete(dn2)
+ # Check it's gone
+ res = self.ldb.search(dn2, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+
+ def test_map_modify_remote_remote(self):
+ """Modification of remote data of remote records"""
+ # Add remote record
+ dn = self.samba4.dn("cn=test")
+ dn2 = self.samba3.dn("cn=test")
+ self.samba3.db.add({"dn": dn2,
+ "cn": "test",
+ "description": "foo",
+ "sambaBadPasswordCount": "3",
+ "sambaNextRid": "1001"})
+ # Check it's there
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
+ attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "foo")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+ # Check in mapped db
+ attrs = ["description", "badPwdCount", "nextRid"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs, expression="")
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "foo")
+ self.assertEquals(str(res[0]["badPwdCount"]), "3")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 0)
+
+ # Modify remote data of remote record
+ ldif = """
+dn: """ + dn + """
+replace: description
+description: test
+replace: badPwdCount
+badPwdCount: 4
+"""
+ self.ldb.modify_ldif(ldif)
+ # Check in mapped db
+ res = self.ldb.search(dn, scope=SCOPE_BASE,
+ attrs=["description", "badPwdCount", "nextRid"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["badPwdCount"]), "4")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ # Check in remote db
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
+ attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+
+ # Rename remote record
+ dn2 = self.samba4.dn("cn=toast")
+ self.ldb.rename(dn, dn2)
+ # Check in mapped db
+ dn = dn2
+ res = self.ldb.search(dn, scope=SCOPE_BASE,
+ attrs=["description", "badPwdCount", "nextRid"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["badPwdCount"]), "4")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ # Check in remote db
+ dn2 = self.samba3.dn("cn=toast")
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
+ attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+
+ # Delete remote record
+ self.ldb.delete(dn)
+ # Check in mapped db that it's removed
+ res = self.ldb.search(dn, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+ # Check in remote db
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+
+ def test_map_modify_remote_local(self):
+ """Modification of local data of remote records"""
+ # Add remote record (same as before)
+ dn = self.samba4.dn("cn=test")
+ dn2 = self.samba3.dn("cn=test")
+ self.samba3.db.add({"dn": dn2,
+ "cn": "test",
+ "description": "foo",
+ "sambaBadPasswordCount": "3",
+ "sambaNextRid": "1001"})
+
+ # Modify local data of remote record
+ ldif = """
+dn: """ + dn + """
+add: revision
+revision: 1
+replace: description
+description: test
+
+"""
+ self.ldb.modify_ldif(ldif)
+ # Check in mapped db
+ attrs = ["revision", "description"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["revision"]), "1")
+ # Check in remote db
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertTrue(not "revision" in res[0])
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "description" in res[0])
+ self.assertEquals(str(res[0]["revision"]), "1")
+
+ # Delete (newly) split record
+ self.ldb.delete(dn)
+
+ def test_map_modify_split(self):
+ """Testing modification of split records"""
+ # Add split record
+ dn = self.samba4.dn("cn=test")
+ dn2 = self.samba3.dn("cn=test")
+ self.ldb.add({
+ "dn": dn,
+ "cn": "test",
+ "description": "foo",
+ "badPwdCount": "3",
+ "nextRid": "1001",
+ "revision": "1"})
+ # Check it's there
+ attrs = ["description", "badPwdCount", "nextRid", "revision"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "foo")
+ self.assertEquals(str(res[0]["badPwdCount"]), "3")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ self.assertEquals(str(res[0]["revision"]), "1")
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "description" in res[0])
+ self.assertTrue(not "badPwdCount" in res[0])
+ self.assertTrue(not "nextRid" in res[0])
+ self.assertEquals(str(res[0]["revision"]), "1")
+ # Check in remote db
+ attrs = ["description", "sambaBadPasswordCount", "sambaNextRid",
+ "revision"]
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "foo")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+ self.assertTrue(not "revision" in res[0])
+
+ # Modify of split record
+ ldif = """
+dn: """ + dn + """
+replace: description
+description: test
+replace: badPwdCount
+badPwdCount: 4
+replace: revision
+revision: 2
+"""
+ self.ldb.modify_ldif(ldif)
+ # Check in mapped db
+ attrs = ["description", "badPwdCount", "nextRid", "revision"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["badPwdCount"]), "4")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ self.assertEquals(str(res[0]["revision"]), "2")
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "description" in res[0])
+ self.assertTrue(not "badPwdCount" in res[0])
+ self.assertTrue(not "nextRid" in res[0])
+ self.assertEquals(str(res[0]["revision"]), "2")
+ # Check in remote db
+ attrs = ["description", "sambaBadPasswordCount", "sambaNextRid",
+ "revision"]
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+ self.assertTrue(not "revision" in res[0])
+
+ # Rename split record
+ dn2 = self.samba4.dn("cn=toast")
+ self.ldb.rename(dn, dn2)
+ # Check in mapped db
+ dn = dn2
+ attrs = ["description", "badPwdCount", "nextRid", "revision"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["badPwdCount"]), "4")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ self.assertEquals(str(res[0]["revision"]), "2")
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "description" in res[0])
+ self.assertTrue(not "badPwdCount" in res[0])
+ self.assertTrue(not "nextRid" in res[0])
+ self.assertEquals(str(res[0]["revision"]), "2")
+ # Check in remote db
+ dn2 = self.samba3.dn("cn=toast")
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
+ attrs=["description", "sambaBadPasswordCount", "sambaNextRid",
+ "revision"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+ self.assertTrue(not "revision" in res[0])
+
+ # Delete split record
+ self.ldb.delete(dn)
+ # Check in mapped db
+ res = self.ldb.search(dn, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+ # Check in remote db
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
diff --git a/python/samba/tests/samba_tool/__init__.py b/python/samba/tests/samba_tool/__init__.py
new file mode 100644
index 00000000000..3d7f0591e25
--- /dev/null
+++ b/python/samba/tests/samba_tool/__init__.py
@@ -0,0 +1,15 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Sean Dague <sdague@linux.vnet.ibm.com
+#
+# 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/>.
diff --git a/python/samba/tests/samba_tool/base.py b/python/samba/tests/samba_tool/base.py
new file mode 100644
index 00000000000..60ccaa543d7
--- /dev/null
+++ b/python/samba/tests/samba_tool/base.py
@@ -0,0 +1,114 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Sean Dague <sdague@linux.vnet.ibm.com> 2011
+#
+# 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/>.
+
+# This provides a wrapper around the cmd interface so that tests can
+# easily be built on top of it and have minimal code to run basic tests
+# of the commands. A list of the environmental variables can be found in
+# ~/selftest/selftest.pl
+#
+# These can all be accesses via os.environ["VARIBLENAME"] when needed
+
+import random
+import string
+from samba.auth import system_session
+from samba.samdb import SamDB
+from cStringIO import StringIO
+from samba.netcmd.main import cmd_sambatool
+import samba.tests
+
+class SambaToolCmdTest(samba.tests.TestCaseInTempDir):
+
+ def getSamDB(self, *argv):
+ """a convenience function to get a samdb instance so that we can query it"""
+
+ # We build a fake command to get the options created the same
+ # way the command classes do it. It would be better if the command
+ # classes had a way to more cleanly do this, but this lets us write
+ # tests for now
+ cmd = cmd_sambatool.subcommands["user"].subcommands["setexpiry"]
+ parser, optiongroups = cmd._create_parser("user")
+ opts, args = parser.parse_args(list(argv))
+ # Filter out options from option groups
+ args = args[1:]
+ kwargs = dict(opts.__dict__)
+ for option_group in parser.option_groups:
+ for option in option_group.option_list:
+ if option.dest is not None:
+ del kwargs[option.dest]
+ kwargs.update(optiongroups)
+
+ H = kwargs.get("H", None)
+ sambaopts = kwargs.get("sambaopts", None)
+ credopts = kwargs.get("credopts", None)
+
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp, fallback_machine=True)
+
+ samdb = SamDB(url=H, session_info=system_session(),
+ credentials=creds, lp=lp)
+ return samdb
+
+
+ def runcmd(self, name, *args):
+ """run a single level command"""
+ cmd = cmd_sambatool.subcommands[name]
+ cmd.outf = StringIO()
+ cmd.errf = StringIO()
+ result = cmd._run(name, *args)
+ return (result, cmd.outf.getvalue(), cmd.errf.getvalue())
+
+ def runsubcmd(self, name, sub, *args):
+ """run a command with sub commands"""
+ # The reason we need this function seperate from runcmd is
+ # that the .outf StringIO assignment is overriden if we use
+ # runcmd, so we can't capture stdout and stderr
+ cmd = cmd_sambatool.subcommands[name].subcommands[sub]
+ cmd.outf = StringIO()
+ cmd.errf = StringIO()
+ result = cmd._run(name, *args)
+ return (result, cmd.outf.getvalue(), cmd.errf.getvalue())
+
+ def assertCmdSuccess(self, val, msg=""):
+ self.assertIsNone(val, msg)
+
+ def assertCmdFail(self, val, msg=""):
+ self.assertIsNotNone(val, msg)
+
+ def assertMatch(self, base, string, msg=""):
+ self.assertTrue(string in base, msg)
+
+ def randomName(self, count=8):
+ """Create a random name, cap letters and numbers, and always starting with a letter"""
+ name = random.choice(string.ascii_uppercase)
+ name += ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase+ string.digits) for x in range(count - 1))
+ return name
+
+ def randomPass(self, count=16):
+ name = random.choice(string.ascii_uppercase)
+ name += random.choice(string.digits)
+ name += random.choice(string.ascii_lowercase)
+ name += ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase+ string.digits) for x in range(count - 3))
+ return name
+
+ def randomXid(self):
+ # pick some hopefully unused, high UID/GID range to avoid interference
+ # from the system the test runs on
+ xid = random.randint(4711000, 4799000)
+ return xid
+
+ def assertWithin(self, val1, val2, delta, msg=""):
+ """Assert that val1 is within delta of val2, useful for time computations"""
+ self.assertTrue(((val1 + delta) > val2) and ((val1 - delta) < val2), msg)
diff --git a/python/samba/tests/samba_tool/gpo.py b/python/samba/tests/samba_tool/gpo.py
new file mode 100644
index 00000000000..e20a97794ae
--- /dev/null
+++ b/python/samba/tests/samba_tool/gpo.py
@@ -0,0 +1,79 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Andrew Bartlett 2012
+#
+# based on time.py:
+# Copyright (C) Sean Dague <sdague@linux.vnet.ibm.com> 2011
+#
+# 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
+from samba.tests.samba_tool.base import SambaToolCmdTest
+import shutil
+
+class GpoCmdTestCase(SambaToolCmdTest):
+ """Tests for samba-tool time subcommands"""
+
+ gpo_name = "testgpo"
+
+ def test_gpo_list(self):
+ """Run gpo list against the server and make sure it looks accurate"""
+ (result, out, err) = self.runsubcmd("gpo", "listall", "-H", "ldap://%s" % os.environ["SERVER"])
+ self.assertCmdSuccess(result, "Ensuring gpo listall ran successfully")
+
+ def test_fetchfail(self):
+ """Run against a non-existent GPO, and make sure it fails (this hard-coded UUID is very unlikely to exist"""
+ (result, out, err) = self.runsubcmd("gpo", "fetch", "c25cac17-a02a-4151-835d-fae17446ee43", "-H", "ldap://%s" % os.environ["SERVER"])
+ self.assertEquals(result, -1, "check for result code")
+
+ def test_fetch(self):
+ """Run against a real GPO, and make sure it passes"""
+ (result, out, err) = self.runsubcmd("gpo", "fetch", self.gpo_guid, "-H", "ldap://%s" % os.environ["SERVER"], "--tmpdir", self.tempdir)
+ self.assertCmdSuccess(result, "Ensuring gpo fetched successfully")
+ shutil.rmtree(os.path.join(self.tempdir, "policy"))
+
+ def test_show(self):
+ """Show a real GPO, and make sure it passes"""
+ (result, out, err) = self.runsubcmd("gpo", "show", self.gpo_guid, "-H", "ldap://%s" % os.environ["SERVER"])
+ self.assertCmdSuccess(result, "Ensuring gpo fetched successfully")
+
+ def test_show_as_admin(self):
+ """Show a real GPO, and make sure it passes"""
+ (result, out, err) = self.runsubcmd("gpo", "show", self.gpo_guid, "-H", "ldap://%s" % os.environ["SERVER"], "-U%s%%%s" % (os.environ["USERNAME"], os.environ["PASSWORD"]))
+ self.assertCmdSuccess(result, "Ensuring gpo fetched successfully")
+
+ def test_aclcheck(self):
+ """Check all the GPOs on the remote server have correct ACLs"""
+ (result, out, err) = self.runsubcmd("gpo", "aclcheck", "-H", "ldap://%s" % os.environ["SERVER"], "-U%s%%%s" % (os.environ["USERNAME"], os.environ["PASSWORD"]))
+ self.assertCmdSuccess(result, "Ensuring gpo checked successfully")
+
+ def setUp(self):
+ """set up a temporary GPO to work with"""
+ super(GpoCmdTestCase, self).setUp()
+ (result, out, err) = self.runsubcmd("gpo", "create", self.gpo_name,
+ "-H", "ldap://%s" % os.environ["SERVER"],
+ "-U%s%%%s" % (os.environ["USERNAME"], os.environ["PASSWORD"]),
+ "--tmpdir", self.tempdir)
+ shutil.rmtree(os.path.join(self.tempdir, "policy"))
+ self.assertCmdSuccess(result, "Ensuring gpo created successfully")
+ try:
+ self.gpo_guid = "{%s}" % out.split("{")[1].split("}")[0]
+ except IndexError:
+ self.fail("Failed to find GUID in output: %s" % out)
+
+ def tearDown(self):
+ """remove the temporary GPO to work with"""
+ (result, out, err) = self.runsubcmd("gpo", "del", self.gpo_guid, "-H", "ldap://%s" % os.environ["SERVER"], "-U%s%%%s" % (os.environ["USERNAME"], os.environ["PASSWORD"]))
+ self.assertCmdSuccess(result, "Ensuring gpo deleted successfully")
+ super(GpoCmdTestCase, self).tearDown()
diff --git a/python/samba/tests/samba_tool/group.py b/python/samba/tests/samba_tool/group.py
new file mode 100644
index 00000000000..2c0c46e5dc8
--- /dev/null
+++ b/python/samba/tests/samba_tool/group.py
@@ -0,0 +1,169 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Michael Adam 2012
+#
+# 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 time
+import ldb
+from samba.tests.samba_tool.base import SambaToolCmdTest
+from samba import (
+ nttime2unix,
+ dsdb
+ )
+
+class GroupCmdTestCase(SambaToolCmdTest):
+ """Tests for samba-tool group subcommands"""
+ groups = []
+ samdb = None
+
+ def setUp(self):
+ super(GroupCmdTestCase, self).setUp()
+ self.samdb = self.getSamDB("-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+ self.groups = []
+ self.groups.append(self._randomGroup({"name": "testgroup1"}))
+ self.groups.append(self._randomGroup({"name": "testgroup2"}))
+ self.groups.append(self._randomGroup({"name": "testgroup3"}))
+ self.groups.append(self._randomGroup({"name": "testgroup4"}))
+
+ # setup the 4 groups and ensure they are correct
+ for group in self.groups:
+ (result, out, err) = self._create_group(group)
+
+ self.assertCmdSuccess(result)
+ self.assertEquals(err, "", "There shouldn't be any error message")
+ self.assertIn("Added group %s" % group["name"], out)
+
+ found = self._find_group(group["name"])
+
+ self.assertIsNotNone(found)
+
+ self.assertEquals("%s" % found.get("name"), group["name"])
+ self.assertEquals("%s" % found.get("description"), group["description"])
+
+ def tearDown(self):
+ super(GroupCmdTestCase, self).tearDown()
+ # clean up all the left over groups, just in case
+ for group in self.groups:
+ if self._find_group(group["name"]):
+ self.runsubcmd("group", "delete", group["name"])
+
+
+ def test_newgroup(self):
+ """This tests the "group add" and "group delete" commands"""
+ # try to add all the groups again, this should fail
+ for group in self.groups:
+ (result, out, err) = self._create_group(group)
+ self.assertCmdFail(result, "Succeeded to create existing group")
+ self.assertIn("LDAP error 68 LDAP_ENTRY_ALREADY_EXISTS", err)
+
+ # try to delete all the groups we just added
+ for group in self.groups:
+ (result, out, err) = self.runsubcmd("group", "delete", group["name"])
+ self.assertCmdSuccess(result,
+ "Failed to delete group '%s'" % group["name"])
+ found = self._find_group(group["name"])
+ self.assertIsNone(found,
+ "Deleted group '%s' still exists" % group["name"])
+
+ # test adding groups
+ for group in self.groups:
+ (result, out, err) = self.runsubcmd("group", "add", group["name"],
+ "--description=%s" % group["description"],
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"],
+ os.environ["DC_PASSWORD"]))
+
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","There shouldn't be any error message")
+ self.assertIn("Added group %s" % group["name"], out)
+
+ found = self._find_group(group["name"])
+
+ self.assertEquals("%s" % found.get("samaccountname"),
+ "%s" % group["name"])
+
+
+ def test_list(self):
+ (result, out, err) = self.runsubcmd("group", "list",
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"],
+ os.environ["DC_PASSWORD"]))
+ self.assertCmdSuccess(result, "Error running list")
+
+ search_filter = "(objectClass=group)"
+
+ grouplist = self.samdb.search(base=self.samdb.domain_dn(),
+ scope=ldb.SCOPE_SUBTREE,
+ expression=search_filter,
+ attrs=["samaccountname"])
+
+ self.assertTrue(len(grouplist) > 0, "no groups found in samdb")
+
+ for groupobj in grouplist:
+ name = groupobj.get("samaccountname", idx=0)
+ found = self.assertMatch(out, name,
+ "group '%s' not found" % name)
+
+ def test_listmembers(self):
+ (result, out, err) = self.runsubcmd("group", "listmembers", "Domain Users",
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"],
+ os.environ["DC_PASSWORD"]))
+ self.assertCmdSuccess(result, "Error running listmembers")
+
+ search_filter = "(|(primaryGroupID=513)(memberOf=CN=Domain Users,CN=Users,%s))" % self.samdb.domain_dn()
+
+ grouplist = self.samdb.search(base=self.samdb.domain_dn(),
+ scope=ldb.SCOPE_SUBTREE,
+ expression=search_filter,
+ attrs=["samAccountName"])
+
+ self.assertTrue(len(grouplist) > 0, "no groups found in samdb")
+
+ for groupobj in grouplist:
+ name = groupobj.get("samAccountName", idx=0)
+ found = self.assertMatch(out, name, "group '%s' not found" % name)
+
+ def _randomGroup(self, base={}):
+ """create a group with random attribute values, you can specify base attributes"""
+ group = {
+ "name": self.randomName(),
+ "description": self.randomName(count=100),
+ }
+ group.update(base)
+ return group
+
+ def _create_group(self, group):
+ return self.runsubcmd("group", "add", group["name"],
+ "--description=%s" % group["description"],
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"],
+ os.environ["DC_PASSWORD"]))
+
+ def _find_group(self, name):
+ search_filter = ("(&(sAMAccountName=%s)(objectCategory=%s,%s))" %
+ (ldb.binary_encode(name),
+ "CN=Group,CN=Schema,CN=Configuration",
+ self.samdb.domain_dn()))
+ grouplist = self.samdb.search(base=self.samdb.domain_dn(),
+ scope=ldb.SCOPE_SUBTREE,
+ expression=search_filter,
+ attrs=[])
+ if grouplist:
+ return grouplist[0]
+ else:
+ return None
diff --git a/python/samba/tests/samba_tool/ntacl.py b/python/samba/tests/samba_tool/ntacl.py
new file mode 100644
index 00000000000..2a329fe7d40
--- /dev/null
+++ b/python/samba/tests/samba_tool/ntacl.py
@@ -0,0 +1,135 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Andrew Bartlett 2012
+#
+# Based on user.py:
+# Copyright (C) Sean Dague <sdague@linux.vnet.ibm.com> 2011
+#
+# 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 time
+import ldb
+from samba.tests.samba_tool.base import SambaToolCmdTest
+import random
+
+class NtACLCmdSysvolTestCase(SambaToolCmdTest):
+ """Tests for samba-tool ntacl sysvol* subcommands"""
+
+
+ def test_ntvfs(self):
+ (result, out, err) = self.runsubcmd("ntacl", "sysvolreset",
+ "--use-ntvfs")
+ self.assertCmdSuccess(result)
+ self.assertEquals(out,"","Shouldn't be any output messages")
+ self.assertIn("Please note that POSIX permissions have NOT been changed, only the stored NT ACL", err)
+
+ def test_s3fs(self):
+ (result, out, err) = self.runsubcmd("ntacl", "sysvolreset",
+ "--use-s3fs")
+
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","Shouldn't be any error messages")
+ self.assertEquals(out,"","Shouldn't be any output messages")
+
+ def test_ntvfs_check(self):
+ (result, out, err) = self.runsubcmd("ntacl", "sysvolreset",
+ "--use-ntvfs")
+ self.assertCmdSuccess(result)
+ self.assertEquals(out,"","Shouldn't be any output messages")
+ self.assertIn("Please note that POSIX permissions have NOT been changed, only the stored NT ACL", err)
+ # Now check they were set correctly
+ (result, out, err) = self.runsubcmd("ntacl", "sysvolcheck")
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","Shouldn't be any error messages")
+ self.assertEquals(out,"","Shouldn't be any output messages")
+
+ def test_s3fs_check(self):
+ (result, out, err) = self.runsubcmd("ntacl", "sysvolreset",
+ "--use-s3fs")
+
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","Shouldn't be any error messages")
+ self.assertEquals(out,"","Shouldn't be any output messages")
+
+ # Now check they were set correctly
+ (result, out, err) = self.runsubcmd("ntacl", "sysvolcheck")
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","Shouldn't be any error messages")
+ self.assertEquals(out,"","Shouldn't be any output messages")
+
+class NtACLCmdGetSetTestCase(SambaToolCmdTest):
+ """Tests for samba-tool ntacl get/set subcommands"""
+
+ acl = "O:DAG:DUD:P(A;OICI;0x001f01ff;;;DA)(A;OICI;0x001f01ff;;;EA)(A;OICIIO;0x001f01ff;;;CO)(A;OICI;0x001f01ff;;;DA)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001200a9;;;ED)S:AI(OU;CIIDSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CIIDSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)"
+
+
+ def test_ntvfs(self):
+ path = os.environ['SELFTEST_PREFIX']
+ tempf = os.path.join(path,"pytests"+str(int(100000*random.random())))
+ open(tempf, 'w').write("empty")
+
+ (result, out, err) = self.runsubcmd("ntacl", "set", self.acl, tempf,
+ "--use-ntvfs")
+ self.assertCmdSuccess(result)
+ self.assertEquals(out,"","Shouldn't be any output messages")
+ self.assertIn("Please note that POSIX permissions have NOT been changed, only the stored NT ACL", err)
+
+ def test_s3fs(self):
+ path = os.environ['SELFTEST_PREFIX']
+ tempf = os.path.join(path,"pytests"+str(int(100000*random.random())))
+ open(tempf, 'w').write("empty")
+
+ (result, out, err) = self.runsubcmd("ntacl", "set", self.acl, tempf,
+ "--use-s3fs")
+
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","Shouldn't be any error messages")
+ self.assertEquals(out,"","Shouldn't be any output messages")
+
+ def test_ntvfs_check(self):
+ path = os.environ['SELFTEST_PREFIX']
+ tempf = os.path.join(path,"pytests"+str(int(100000*random.random())))
+ open(tempf, 'w').write("empty")
+
+ (result, out, err) = self.runsubcmd("ntacl", "set", self.acl, tempf,
+ "--use-ntvfs")
+ self.assertCmdSuccess(result)
+ self.assertEquals(out,"","Shouldn't be any output messages")
+ self.assertIn("Please note that POSIX permissions have NOT been changed, only the stored NT ACL", err)
+
+ # Now check they were set correctly
+ (result, out, err) = self.runsubcmd("ntacl", "get", tempf,
+ "--use-ntvfs", "--as-sddl")
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","Shouldn't be any error messages")
+ self.assertEquals(self.acl+"\n", out, "Output should be the ACL")
+
+ def test_s3fs_check(self):
+ path = os.environ['SELFTEST_PREFIX']
+ tempf = os.path.join(path,"pytests"+str(int(100000*random.random())))
+ open(tempf, 'w').write("empty")
+
+ (result, out, err) = self.runsubcmd("ntacl", "set", self.acl, tempf,
+ "--use-s3fs")
+ self.assertCmdSuccess(result)
+ self.assertEquals(out,"","Shouldn't be any output messages")
+ self.assertEquals(err,"","Shouldn't be any error messages")
+
+ # Now check they were set correctly
+ (result, out, err) = self.runsubcmd("ntacl", "get", tempf,
+ "--use-s3fs", "--as-sddl")
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","Shouldn't be any error messages")
+ self.assertEquals(self.acl+"\n", out,"Output should be the ACL")
diff --git a/python/samba/tests/samba_tool/processes.py b/python/samba/tests/samba_tool/processes.py
new file mode 100644
index 00000000000..91a5266b54a
--- /dev/null
+++ b/python/samba/tests/samba_tool/processes.py
@@ -0,0 +1,35 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Andrew Bartlett 2012
+#
+# based on time.py:
+# Copyright (C) Sean Dague <sdague@linux.vnet.ibm.com> 2011
+#
+# 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
+from samba.tests.samba_tool.base import SambaToolCmdTest
+
+class ProcessCmdTestCase(SambaToolCmdTest):
+ """Tests for samba-tool process subcommands"""
+
+ def test_name(self):
+ """Run processes command"""
+ (result, out, err) = self.runcmd("processes", "--name", "samba")
+ self.assertCmdSuccess(result, "Ensuring processes ran successfully")
+
+ def test_all(self):
+ """Run processes command"""
+ (result, out, err) = self.runcmd("processes")
+ self.assertCmdSuccess(result, "Ensuring processes ran successfully")
diff --git a/python/samba/tests/samba_tool/timecmd.py b/python/samba/tests/samba_tool/timecmd.py
new file mode 100644
index 00000000000..000f0f28282
--- /dev/null
+++ b/python/samba/tests/samba_tool/timecmd.py
@@ -0,0 +1,43 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Sean Dague <sdague@linux.vnet.ibm.com> 2011
+#
+# 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
+from time import localtime, strptime, mktime
+from samba.tests.samba_tool.base import SambaToolCmdTest
+
+class TimeCmdTestCase(SambaToolCmdTest):
+ """Tests for samba-tool time subcommands"""
+
+ def test_timeget(self):
+ """Run time against the server and make sure it looks accurate"""
+ (result, out, err) = self.runcmd("time", os.environ["SERVER"])
+ self.assertCmdSuccess(result, "Ensuring time ran successfully")
+
+ timefmt = strptime(out, "%a %b %d %H:%M:%S %Y %Z\n")
+ servertime = int(mktime(timefmt))
+ now = int(mktime(localtime()))
+
+ # because there is a race here, allow up to 5 seconds difference in times
+ delta = 5
+ self.assertTrue((servertime > (now - delta) and (servertime < (now + delta)), "Time is now"))
+
+ def test_timefail(self):
+ """Run time against a non-existent server, and make sure it fails"""
+ (result, out, err) = self.runcmd("time", "notaserver")
+ self.assertEquals(result, -1, "check for result code")
+ self.assertTrue(err.strip().endswith("NT_STATUS_OBJECT_NAME_NOT_FOUND"), "ensure right error string")
+ self.assertEquals(out, "", "ensure no output returned")
diff --git a/python/samba/tests/samba_tool/user.py b/python/samba/tests/samba_tool/user.py
new file mode 100644
index 00000000000..33344cd3d31
--- /dev/null
+++ b/python/samba/tests/samba_tool/user.py
@@ -0,0 +1,362 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Sean Dague <sdague@linux.vnet.ibm.com> 2011
+#
+# 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 time
+import ldb
+from samba.tests.samba_tool.base import SambaToolCmdTest
+from samba import (
+ nttime2unix,
+ dsdb
+ )
+
+class UserCmdTestCase(SambaToolCmdTest):
+ """Tests for samba-tool user subcommands"""
+ users = []
+ samdb = None
+
+ def setUp(self):
+ super(UserCmdTestCase, self).setUp()
+ self.samdb = self.getSamDB("-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+ self.users = []
+ self.users.append(self._randomUser({"name": "sambatool1", "company": "comp1"}))
+ self.users.append(self._randomUser({"name": "sambatool2", "company": "comp1"}))
+ self.users.append(self._randomUser({"name": "sambatool3", "company": "comp2"}))
+ self.users.append(self._randomUser({"name": "sambatool4", "company": "comp2"}))
+ self.users.append(self._randomPosixUser({"name": "posixuser1"}))
+ self.users.append(self._randomPosixUser({"name": "posixuser2"}))
+ self.users.append(self._randomPosixUser({"name": "posixuser3"}))
+ self.users.append(self._randomPosixUser({"name": "posixuser4"}))
+
+ # setup the 8 users and ensure they are correct
+ for user in self.users:
+ (result, out, err) = user["createUserFn"](user)
+
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","Shouldn't be any error messages")
+ self.assertIn("User '%s' created successfully" % user["name"], out)
+
+ user["checkUserFn"](user)
+
+
+ def tearDown(self):
+ super(UserCmdTestCase, self).tearDown()
+ # clean up all the left over users, just in case
+ for user in self.users:
+ if self._find_user(user["name"]):
+ self.runsubcmd("user", "delete", user["name"])
+
+
+ def test_newuser(self):
+ # try to add all the users again, this should fail
+ for user in self.users:
+ (result, out, err) = self._create_user(user)
+ self.assertCmdFail(result, "Ensure that create user fails")
+ self.assertIn("LDAP error 68 LDAP_ENTRY_ALREADY_EXISTS", err)
+
+ # try to delete all the 4 users we just added
+ for user in self.users:
+ (result, out, err) = self.runsubcmd("user", "delete", user["name"])
+ self.assertCmdSuccess(result, "Can we delete users")
+ found = self._find_user(user["name"])
+ self.assertIsNone(found)
+
+ # test adding users with --use-username-as-cn
+ for user in self.users:
+ (result, out, err) = self.runsubcmd("user", "add", user["name"], user["password"],
+ "--use-username-as-cn",
+ "--surname=%s" % user["surname"],
+ "--given-name=%s" % user["given-name"],
+ "--job-title=%s" % user["job-title"],
+ "--department=%s" % user["department"],
+ "--description=%s" % user["description"],
+ "--company=%s" % user["company"],
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","Shouldn't be any error messages")
+ self.assertIn("User '%s' created successfully" % user["name"], out)
+
+ found = self._find_user(user["name"])
+
+ self.assertEquals("%s" % found.get("cn"), "%(name)s" % user)
+ self.assertEquals("%s" % found.get("name"), "%(name)s" % user)
+
+
+
+ def test_setpassword(self):
+ for user in self.users:
+ newpasswd = self.randomPass()
+ (result, out, err) = self.runsubcmd("user", "setpassword",
+ user["name"],
+ "--newpassword=%s" % newpasswd,
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+ # self.assertCmdSuccess(result, "Ensure setpassword runs")
+ self.assertEquals(err,"","setpassword with url")
+ self.assertMatch(out, "Changed password OK", "setpassword with url")
+
+ for user in self.users:
+ newpasswd = self.randomPass()
+ (result, out, err) = self.runsubcmd("user", "setpassword",
+ user["name"],
+ "--newpassword=%s" % newpasswd)
+ # self.assertCmdSuccess(result, "Ensure setpassword runs")
+ self.assertEquals(err,"","setpassword without url")
+ self.assertMatch(out, "Changed password OK", "setpassword without url")
+
+ for user in self.users:
+ newpasswd = self.randomPass()
+ (result, out, err) = self.runsubcmd("user", "setpassword",
+ user["name"],
+ "--newpassword=%s" % newpasswd,
+ "--must-change-at-next-login",
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+ # self.assertCmdSuccess(result, "Ensure setpassword runs")
+ self.assertEquals(err,"","setpassword with forced change")
+ self.assertMatch(out, "Changed password OK", "setpassword with forced change")
+
+
+
+
+ def test_setexpiry(self):
+ twodays = time.time() + (2 * 24 * 60 * 60)
+
+ for user in self.users:
+ (result, out, err) = self.runsubcmd("user", "setexpiry", user["name"],
+ "--days=2",
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+ self.assertCmdSuccess(result, "Can we run setexpiry with names")
+ self.assertIn("Expiry for user '%s' set to 2 days." % user["name"], out)
+
+ for user in self.users:
+ found = self._find_user(user["name"])
+
+ expires = nttime2unix(int("%s" % found.get("accountExpires")))
+ self.assertWithin(expires, twodays, 5, "Ensure account expires is within 5 seconds of the expected time")
+
+ # TODO: renable this after the filter case is sorted out
+ if "filters are broken, bail now":
+ return
+
+ # now run the expiration based on a filter
+ fourdays = time.time() + (4 * 24 * 60 * 60)
+ (result, out, err) = self.runsubcmd("user", "setexpiry",
+ "--filter", "(&(objectClass=user)(company=comp2))",
+ "--days=4",
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+ self.assertCmdSuccess(result, "Can we run setexpiry with a filter")
+
+ for user in self.users:
+ found = self._find_user(user["name"])
+ if ("%s" % found.get("company")) == "comp2":
+ expires = nttime2unix(int("%s" % found.get("accountExpires")))
+ self.assertWithin(expires, fourdays, 5, "Ensure account expires is within 5 seconds of the expected time")
+ else:
+ expires = nttime2unix(int("%s" % found.get("accountExpires")))
+ self.assertWithin(expires, twodays, 5, "Ensure account expires is within 5 seconds of the expected time")
+
+
+ def test_list(self):
+ (result, out, err) = self.runsubcmd("user", "list",
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"],
+ os.environ["DC_PASSWORD"]))
+ self.assertCmdSuccess(result, "Error running list")
+
+ search_filter = ("(&(objectClass=user)(userAccountControl:%s:=%u))" %
+ (ldb.OID_COMPARATOR_AND, dsdb.UF_NORMAL_ACCOUNT))
+
+ userlist = self.samdb.search(base=self.samdb.domain_dn(),
+ scope=ldb.SCOPE_SUBTREE,
+ expression=search_filter,
+ attrs=["samaccountname"])
+
+ self.assertTrue(len(userlist) > 0, "no users found in samdb")
+
+ for userobj in userlist:
+ name = userobj.get("samaccountname", idx=0)
+ found = self.assertMatch(out, name,
+ "user '%s' not found" % name)
+ def test_getpwent(self):
+ try:
+ import pwd
+ except ImportError:
+ self.skipTest("Skipping getpwent test, no 'pwd' module available")
+ return
+
+ # get the current user's data for the test
+ uid = os.geteuid()
+ try:
+ u = pwd.getpwuid(uid)
+ except KeyError:
+ self.skipTest("Skipping getpwent test, current EUID not found in NSS")
+ return
+
+ user = self._randomPosixUser({
+ "name": u[0],
+ "uid": u[0],
+ "uidNumber": u[2],
+ "gidNumber": u[3],
+ "gecos": u[4],
+ "loginShell": u[6],
+ })
+ # check if --rfc2307-from-nss sets the same values as we got from pwd.getpwuid()
+ (result, out, err) = self.runsubcmd("user", "add", user["name"], user["password"],
+ "--surname=%s" % user["surname"],
+ "--given-name=%s" % user["given-name"],
+ "--job-title=%s" % user["job-title"],
+ "--department=%s" % user["department"],
+ "--description=%s" % user["description"],
+ "--company=%s" % user["company"],
+ "--rfc2307-from-nss",
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","Shouldn't be any error messages")
+ self.assertIn("User '%s' created successfully" % user["name"], out)
+
+ self._check_posix_user(user)
+ self.runsubcmd("user", "delete", user["name"])
+
+ # Check if overriding the attributes from NSS with explicit values works
+ #
+ # get a user with all random posix attributes
+ user = self._randomPosixUser({"name": u[0]})
+ # create a user with posix attributes from nss but override all of them with the
+ # random ones just obtained
+ (result, out, err) = self.runsubcmd("user", "add", user["name"], user["password"],
+ "--surname=%s" % user["surname"],
+ "--given-name=%s" % user["given-name"],
+ "--job-title=%s" % user["job-title"],
+ "--department=%s" % user["department"],
+ "--description=%s" % user["description"],
+ "--company=%s" % user["company"],
+ "--rfc2307-from-nss",
+ "--gecos=%s" % user["gecos"],
+ "--login-shell=%s" % user["loginShell"],
+ "--uid=%s" % user["uid"],
+ "--uid-number=%s" % user["uidNumber"],
+ "--gid-number=%s" % user["gidNumber"],
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+
+ self.assertCmdSuccess(result)
+ self.assertEquals(err,"","Shouldn't be any error messages")
+ self.assertIn("User '%s' created successfully" % user["name"], out)
+
+ self._check_posix_user(user)
+ self.runsubcmd("user", "delete", user["name"])
+
+ def _randomUser(self, base={}):
+ """create a user with random attribute values, you can specify base attributes"""
+ user = {
+ "name": self.randomName(),
+ "password": self.randomPass(),
+ "surname": self.randomName(),
+ "given-name": self.randomName(),
+ "job-title": self.randomName(),
+ "department": self.randomName(),
+ "company": self.randomName(),
+ "description": self.randomName(count=100),
+ "createUserFn": self._create_user,
+ "checkUserFn": self._check_user,
+ }
+ user.update(base)
+ return user
+
+ def _randomPosixUser(self, base={}):
+ """create a user with random attribute values and additional RFC2307
+ attributes, you can specify base attributes"""
+ user = self._randomUser({})
+ user.update(base)
+ posixAttributes = {
+ "uid": self.randomName(),
+ "loginShell": self.randomName(),
+ "gecos": self.randomName(),
+ "uidNumber": self.randomXid(),
+ "gidNumber": self.randomXid(),
+ "createUserFn": self._create_posix_user,
+ "checkUserFn": self._check_posix_user,
+ }
+ user.update(posixAttributes)
+ user.update(base)
+ return user
+
+ def _check_user(self, user):
+ """ check if a user from SamDB has the same attributes as its template """
+ found = self._find_user(user["name"])
+
+ self.assertEquals("%s" % found.get("name"), "%(given-name)s %(surname)s" % user)
+ self.assertEquals("%s" % found.get("title"), user["job-title"])
+ self.assertEquals("%s" % found.get("company"), user["company"])
+ self.assertEquals("%s" % found.get("description"), user["description"])
+ self.assertEquals("%s" % found.get("department"), user["department"])
+
+ def _check_posix_user(self, user):
+ """ check if a posix_user from SamDB has the same attributes as its template """
+ found = self._find_user(user["name"])
+
+ self.assertEquals("%s" % found.get("loginShell"), user["loginShell"])
+ self.assertEquals("%s" % found.get("gecos"), user["gecos"])
+ self.assertEquals("%s" % found.get("uidNumber"), "%s" % user["uidNumber"])
+ self.assertEquals("%s" % found.get("gidNumber"), "%s" % user["gidNumber"])
+ self.assertEquals("%s" % found.get("uid"), user["uid"])
+ self._check_user(user)
+
+ def _create_user(self, user):
+ return self.runsubcmd("user", "add", user["name"], user["password"],
+ "--surname=%s" % user["surname"],
+ "--given-name=%s" % user["given-name"],
+ "--job-title=%s" % user["job-title"],
+ "--department=%s" % user["department"],
+ "--description=%s" % user["description"],
+ "--company=%s" % user["company"],
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+ def _create_posix_user(self, user):
+ """ create a new user with RFC2307 attributes """
+ return self.runsubcmd("user", "create", user["name"], user["password"],
+ "--surname=%s" % user["surname"],
+ "--given-name=%s" % user["given-name"],
+ "--job-title=%s" % user["job-title"],
+ "--department=%s" % user["department"],
+ "--description=%s" % user["description"],
+ "--company=%s" % user["company"],
+ "--gecos=%s" % user["gecos"],
+ "--login-shell=%s" % user["loginShell"],
+ "--uid=%s" % user["uid"],
+ "--uid-number=%s" % user["uidNumber"],
+ "--gid-number=%s" % user["gidNumber"],
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+
+ def _find_user(self, name):
+ search_filter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (ldb.binary_encode(name), "CN=Person,CN=Schema,CN=Configuration", self.samdb.domain_dn())
+ userlist = self.samdb.search(base=self.samdb.domain_dn(),
+ scope=ldb.SCOPE_SUBTREE,
+ expression=search_filter, attrs=[])
+ if userlist:
+ return userlist[0]
+ else:
+ return None
diff --git a/python/samba/tests/samdb.py b/python/samba/tests/samdb.py
new file mode 100644
index 00000000000..5c80391cbae
--- /dev/null
+++ b/python/samba/tests/samdb.py
@@ -0,0 +1,96 @@
+# Unix SMB/CIFS implementation. Tests for SamDB
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# 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/>.
+#
+
+"""Tests for samba.samdb."""
+
+import logging
+import os
+import uuid
+
+from samba.auth import system_session
+from samba.provision import (setup_samdb, guess_names, make_smbconf,
+ provision_paths_from_lp)
+from samba.provision import DEFAULT_POLICY_GUID, DEFAULT_DC_POLICY_GUID
+from samba.provision.backend import ProvisionBackend
+from samba.tests import TestCaseInTempDir
+from samba.dcerpc import security
+from samba.schema import Schema
+from samba import param
+
+
+class SamDBTestCase(TestCaseInTempDir):
+ """Base-class for tests with a Sam Database.
+
+ This is used by the Samba SamDB-tests, but e.g. also by the OpenChange
+ provisioning tests (which need a Sam).
+ """
+
+ def setUp(self):
+ super(SamDBTestCase, self).setUp()
+ invocationid = str(uuid.uuid4())
+ domaindn = "DC=COM,DC=EXAMPLE"
+ self.domaindn = domaindn
+ configdn = "CN=Configuration," + domaindn
+ schemadn = "CN=Schema," + configdn
+ domainguid = str(uuid.uuid4())
+ policyguid = DEFAULT_POLICY_GUID
+ domainsid = security.random_sid()
+ path = os.path.join(self.tempdir, "samdb.ldb")
+ session_info = system_session()
+
+ hostname="foo"
+ domain="EXAMPLE"
+ dnsdomain="example.com"
+ serverrole="domain controller"
+ policyguid_dc = DEFAULT_DC_POLICY_GUID
+
+ smbconf = os.path.join(self.tempdir, "smb.conf")
+ make_smbconf(smbconf, hostname, domain, dnsdomain,
+ self.tempdir, serverrole=serverrole)
+
+ self.lp = param.LoadParm()
+ self.lp.load(smbconf)
+
+ names = guess_names(lp=self.lp, hostname=hostname,
+ domain=domain, dnsdomain=dnsdomain,
+ serverrole=serverrole,
+ domaindn=self.domaindn, configdn=configdn,
+ schemadn=schemadn)
+
+ paths = provision_paths_from_lp(self.lp, names.dnsdomain)
+
+ logger = logging.getLogger("provision")
+
+ provision_backend = ProvisionBackend("ldb", paths=paths,
+ lp=self.lp, credentials=None,
+ names=names, logger=logger)
+
+ schema = Schema(domainsid, invocationid=invocationid,
+ schemadn=names.schemadn, serverdn=names.serverdn,
+ am_rodc=False)
+
+ self.samdb = setup_samdb(path, session_info,
+ provision_backend, self.lp, names, logger,
+ domainsid, domainguid, policyguid, policyguid_dc, False,
+ "secret", "secret", "secret", invocationid, "secret",
+ None, "domain controller", schema=schema)
+
+ def tearDown(self):
+ for f in ['schema.ldb', 'configuration.ldb',
+ 'users.ldb', 'samdb.ldb', 'smb.conf']:
+ os.remove(os.path.join(self.tempdir, f))
+ super(SamDBTestCase, self).tearDown()
diff --git a/python/samba/tests/security.py b/python/samba/tests/security.py
new file mode 100644
index 00000000000..d2938aacb02
--- /dev/null
+++ b/python/samba/tests/security.py
@@ -0,0 +1,143 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+#
+# 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/>.
+#
+
+"""Tests for samba.dcerpc.security."""
+
+import samba.tests
+from samba.dcerpc import security
+
+class SecurityTokenTests(samba.tests.TestCase):
+
+ def setUp(self):
+ super(SecurityTokenTests, self).setUp()
+ self.token = security.token()
+
+ def test_is_system(self):
+ self.assertFalse(self.token.is_system())
+
+ def test_is_anonymous(self):
+ self.assertFalse(self.token.is_anonymous())
+
+ def test_has_builtin_administrators(self):
+ self.assertFalse(self.token.has_builtin_administrators())
+
+ def test_has_nt_authenticated_users(self):
+ self.assertFalse(self.token.has_nt_authenticated_users())
+
+ def test_has_priv(self):
+ self.assertFalse(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN))
+
+ def test_set_priv(self):
+ self.assertFalse(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN))
+ self.assertFalse(self.token.set_privilege(security.SEC_PRIV_SHUTDOWN))
+ self.assertTrue(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN))
+
+
+class SecurityDescriptorTests(samba.tests.TestCase):
+
+ def setUp(self):
+ super(SecurityDescriptorTests, self).setUp()
+ self.descriptor = security.descriptor()
+
+ def test_from_sddl(self):
+ desc = security.descriptor.from_sddl("O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)", security.dom_sid("S-2-0-0"))
+ self.assertEquals(desc.group_sid, security.dom_sid('S-2-0-0-512'))
+ self.assertEquals(desc.owner_sid, security.dom_sid('S-1-5-32-548'))
+ self.assertEquals(desc.revision, 1)
+ self.assertEquals(desc.sacl, None)
+ self.assertEquals(desc.type, 0x8004)
+
+ def test_from_sddl_invalidsddl(self):
+ self.assertRaises(TypeError,security.descriptor.from_sddl, "foo",security.dom_sid("S-2-0-0"))
+
+ def test_from_sddl_invalidtype1(self):
+ self.assertRaises(TypeError, security.descriptor.from_sddl, security.dom_sid('S-2-0-0-512'),security.dom_sid("S-2-0-0"))
+
+ def test_from_sddl_invalidtype2(self):
+ sddl = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)"
+ self.assertRaises(TypeError, security.descriptor.from_sddl, sddl,
+ "S-2-0-0")
+
+ def test_as_sddl(self):
+ text = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)"
+ dom = security.dom_sid("S-2-0-0")
+ desc1 = security.descriptor.from_sddl(text, dom)
+ desc2 = security.descriptor.from_sddl(desc1.as_sddl(dom), dom)
+ self.assertEquals(desc1.group_sid, desc2.group_sid)
+ self.assertEquals(desc1.owner_sid, desc2.owner_sid)
+ self.assertEquals(desc1.sacl, desc2.sacl)
+ self.assertEquals(desc1.type, desc2.type)
+
+ def test_as_sddl_invalid(self):
+ text = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)"
+ dom = security.dom_sid("S-2-0-0")
+ desc1 = security.descriptor.from_sddl(text, dom)
+ self.assertRaises(TypeError, desc1.as_sddl,text)
+
+
+ def test_as_sddl_no_domainsid(self):
+ dom = security.dom_sid("S-2-0-0")
+ text = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)"
+ desc1 = security.descriptor.from_sddl(text, dom)
+ desc2 = security.descriptor.from_sddl(desc1.as_sddl(), dom)
+ self.assertEquals(desc1.group_sid, desc2.group_sid)
+ self.assertEquals(desc1.owner_sid, desc2.owner_sid)
+ self.assertEquals(desc1.sacl, desc2.sacl)
+ self.assertEquals(desc1.type, desc2.type)
+
+ def test_domsid_nodomsid_as_sddl(self):
+ dom = security.dom_sid("S-2-0-0")
+ text = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)"
+ desc1 = security.descriptor.from_sddl(text, dom)
+ self.assertNotEqual(desc1.as_sddl(), desc1.as_sddl(dom))
+
+ def test_split(self):
+ dom = security.dom_sid("S-2-0-7")
+ self.assertEquals((security.dom_sid("S-2-0"), 7), dom.split())
+
+
+class DomSidTests(samba.tests.TestCase):
+
+ def test_parse_sid(self):
+ sid = security.dom_sid("S-1-5-21")
+ self.assertEquals("S-1-5-21", str(sid))
+
+ def test_sid_equal(self):
+ sid1 = security.dom_sid("S-1-5-21")
+ sid2 = security.dom_sid("S-1-5-21")
+ self.assertEquals(sid1, sid1)
+ self.assertEquals(sid1, sid2)
+
+ def test_random(self):
+ sid = security.random_sid()
+ self.assertTrue(str(sid).startswith("S-1-5-21-"))
+
+ def test_repr(self):
+ sid = security.random_sid()
+ self.assertTrue(repr(sid).startswith("dom_sid('S-1-5-21-"))
+
+
+class PrivilegeTests(samba.tests.TestCase):
+
+ def test_privilege_name(self):
+ self.assertEquals("SeShutdownPrivilege",
+ security.privilege_name(security.SEC_PRIV_SHUTDOWN))
+
+ def test_privilege_id(self):
+ self.assertEquals(security.SEC_PRIV_SHUTDOWN,
+ security.privilege_id("SeShutdownPrivilege"))
+
diff --git a/python/samba/tests/source.py b/python/samba/tests/source.py
new file mode 100644
index 00000000000..2612ae68cf5
--- /dev/null
+++ b/python/samba/tests/source.py
@@ -0,0 +1,264 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2011
+#
+# Loosely based on bzrlib's test_source.py
+#
+# 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/>.
+#
+
+"""Source level Python tests."""
+
+import errno
+import os
+import re
+import warnings
+
+import samba
+samba.ensure_external_module("pep8", "pep8")
+import pep8
+
+from samba.tests import (
+ TestCase,
+ )
+
+
+def get_python_source_files():
+ """Iterate over all Python source files."""
+ library_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "samba"))
+ assert os.path.isdir(library_dir), library_dir
+
+ for root, dirs, files in os.walk(library_dir):
+ for f in files:
+ if f.endswith(".py"):
+ yield os.path.abspath(os.path.join(root, f))
+
+ bindir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "bin"))
+ assert os.path.isdir(bindir), bindir
+ for f in os.listdir(bindir):
+ p = os.path.abspath(os.path.join(bindir, f))
+ if not os.path.islink(p):
+ continue
+ target = os.readlink(p)
+ if os.path.dirname(target).endswith("scripting/bin"):
+ yield p
+ wafsambadir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "buildtools", "wafsamba"))
+ assert os.path.isdir(wafsambadir), wafsambadir
+ for root, dirs, files in os.walk(wafsambadir):
+ for f in files:
+ if f.endswith(".py"):
+ yield os.path.abspath(os.path.join(root, f))
+
+
+def get_source_file_contents():
+ """Iterate over the contents of all python files."""
+ for fname in get_python_source_files():
+ try:
+ f = open(fname, 'rb')
+ except IOError, e:
+ if e.errno == errno.ENOENT:
+ warnings.warn("source file %s broken link?" % fname)
+ continue
+ else:
+ raise
+ try:
+ text = f.read()
+ finally:
+ f.close()
+ yield fname, text
+
+
+class TestSource(TestCase):
+
+ def test_copyright(self):
+ """Test that all Python files have a valid copyright statement."""
+ incorrect = []
+
+ copyright_re = re.compile('#\\s*copyright.*(?=\n)', re.I)
+
+ for fname, text in get_source_file_contents():
+ if fname.endswith("ms_schema.py"):
+ # FIXME: Not sure who holds copyright on ms_schema.py
+ continue
+ if "wafsamba" in fname:
+ # FIXME: No copyright headers in wafsamba
+ continue
+ match = copyright_re.search(text)
+ if not match:
+ incorrect.append((fname, 'no copyright line found\n'))
+
+ if incorrect:
+ help_text = ["Some files have missing or incorrect copyright"
+ " statements.",
+ "",
+ ]
+ for fname, comment in incorrect:
+ help_text.append(fname)
+ help_text.append((' ' * 4) + comment)
+
+ self.fail('\n'.join(help_text))
+
+ def test_gpl(self):
+ """Test that all .py files have a GPL disclaimer."""
+ incorrect = []
+
+ gpl_txt = """
+# 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/>.
+"""
+ gpl_re = re.compile(re.escape(gpl_txt), re.MULTILINE)
+
+ for fname, text in get_source_file_contents():
+ if "wafsamba" in fname:
+ # FIXME: License to wafsamba hasn't been clarified yet
+ continue
+ if not gpl_re.search(text):
+ incorrect.append(fname)
+
+ if incorrect:
+ help_text = ['Some files have missing or incomplete GPL statement',
+ gpl_txt]
+ for fname in incorrect:
+ help_text.append((' ' * 4) + fname)
+
+ self.fail('\n'.join(help_text))
+
+ def _push_file(self, dict_, fname, line_no):
+ if fname not in dict_:
+ dict_[fname] = [line_no]
+ else:
+ dict_[fname].append(line_no)
+
+ def _format_message(self, dict_, message):
+ files = ["%s: %s" % (f, ', '.join([str(i + 1) for i in lines]))
+ for f, lines in dict_.items()]
+ files.sort()
+ return message + '\n\n %s' % ('\n '.join(files))
+
+ def _iter_source_files_lines(self):
+ for fname, text in get_source_file_contents():
+ lines = text.splitlines(True)
+ last_line_no = len(lines) - 1
+ for line_no, line in enumerate(lines):
+ yield fname, line_no, line
+
+ def test_no_tabs(self):
+ """Check that there are no tabs in Python files."""
+ tabs = {}
+ for fname, line_no, line in self._iter_source_files_lines():
+ if '\t' in line:
+ self._push_file(tabs, fname, line_no)
+ if tabs:
+ self.fail(self._format_message(tabs,
+ 'Tab characters were found in the following source files.'
+ '\nThey should either be replaced by "\\t" or by spaces:'))
+
+ def test_unix_newlines(self):
+ """Check for unix new lines."""
+ illegal_newlines = {}
+ for fname, line_no, line in self._iter_source_files_lines():
+ if not line.endswith('\n') or line.endswith('\r\n'):
+ self._push_file(illegal_newlines, fname, line_no)
+ if illegal_newlines:
+ self.fail(self._format_message(illegal_newlines,
+ 'Non-unix newlines were found in the following source files:'))
+
+ def test_trailing_whitespace(self):
+ """Check that there is not trailing whitespace in Python files."""
+ trailing_whitespace = {}
+ for fname, line_no, line in self._iter_source_files_lines():
+ if line.rstrip("\n").endswith(" "):
+ self._push_file(trailing_whitespace, fname, line_no)
+ if trailing_whitespace:
+ self.fail(self._format_message(trailing_whitespace,
+ 'Trailing whitespace was found in the following source files.'))
+
+ def test_shebang_lines(self):
+ """Check that files with shebang lines and only those are executable."""
+ files_with_shebang = {}
+ files_without_shebang= {}
+ for fname, line_no, line in self._iter_source_files_lines():
+ if line_no >= 1:
+ continue
+ executable = (os.stat(fname).st_mode & 0111)
+ has_shebang = line.startswith("#!")
+ if has_shebang and not executable:
+ self._push_file(files_with_shebang, fname, line_no)
+ if not has_shebang and executable:
+ self._push_file(files_without_shebang, fname, line_no)
+ if files_with_shebang:
+ self.fail(self._format_message(files_with_shebang,
+ 'Files with shebang line that are not executable:'))
+ if files_without_shebang:
+ self.fail(self._format_message(files_without_shebang,
+ 'Files without shebang line that are executable:'))
+
+ pep8_ignore = [
+ 'E401', # multiple imports on one line
+ 'E501', # line too long
+ 'E251', # no spaces around keyword / parameter equals
+ 'E201', # whitespace after '['
+ 'E202', # whitespace before ')'
+ 'E302', # expected 2 blank lines, found 1
+ 'E231', # missing whitespace after ','
+ 'E225', # missing whitespace around operator
+ 'E111', # indentation is not a multiple of four
+ 'E261', # at least two spaces before inline comment
+ 'E702', # multiple statements on one line (semicolon)
+ 'E221', # multiple spaces before operator
+ 'E303', # too many blank lines (2)
+ 'E203', # whitespace before ':'
+ 'E222', # multiple spaces after operator
+ 'E301', # expected 1 blank line, found 0
+ 'E211', # whitespace before '('
+ 'E701', # multiple statements on one line (colon)
+ ]
+
+ def test_pep8(self):
+ pep8.process_options()
+ pep8.options.repeat = True
+ pep8_errors = []
+ pep8_warnings = []
+ for fname, text in get_source_file_contents():
+ def report_error(line_number, offset, text, check):
+ code = text[:4]
+ if code in self.pep8_ignore:
+ code = 'W' + code[1:]
+ text = code + text[4:]
+ print "%s:%s: %s" % (fname, line_number, text)
+ summary = (fname, line_number, offset, text, check)
+ if code[0] == 'W':
+ pep8_warnings.append(summary)
+ else:
+ pep8_errors.append(summary)
+ lines = text.splitlines(True)
+ checker = pep8.Checker(fname, lines)
+ checker.report_error = report_error
+ checker.check_all()
+ if len(pep8_errors) > 0:
+ d = {}
+ for (fname, line_no, offset, text, check) in pep8_errors:
+ d.setdefault(fname, []).append(line_no - 1)
+ self.fail(self._format_message(d,
+ 'There were %d PEP8 errors:' % len(pep8_errors)))
+
diff --git a/python/samba/tests/strings.py b/python/samba/tests/strings.py
new file mode 100644
index 00000000000..23382d756ec
--- /dev/null
+++ b/python/samba/tests/strings.py
@@ -0,0 +1,103 @@
+# subunit test cases for Samba string functions.
+
+# Copyright (C) 2003 by Martin Pool <mbp@samba.org>
+# Copyright (C) 2011 Andrew Bartlett
+#
+# 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/>.
+#
+
+# XXX: All this code assumes that the Unix character set is UTF-8,
+# which is the most common setting. I guess it would be better to
+# force it to that value while running the tests. I'm not sure of the
+# best way to do that yet.
+#
+# -- mbp
+
+from unicodenames import *
+
+import samba.tests
+from samba import strcasecmp_m, strstr_m
+
+def signum(a):
+ if a < 0:
+ return -1
+ elif a > 0:
+ return +1
+ else:
+ return 0
+
+
+class strcasecmp_m_Tests(samba.tests.TestCase):
+ """String comparisons in simple ASCII and unicode"""
+ def test_strcasecmp_m(self):
+ # A, B, strcasecmp(A, B)
+ cases = [('hello', 'hello', 0),
+ ('hello', 'goodbye', +1),
+ ('goodbye', 'hello', -1),
+ ('hell', 'hello', -1),
+ ('', '', 0),
+ ('a', '', +1),
+ ('', 'a', -1),
+ ('a', 'A', 0),
+ ('aa', 'aA', 0),
+ ('Aa', 'aa', 0),
+ ('longstring ' * 100, 'longstring ' * 100, 0),
+ ('longstring ' * 100, 'longstring ' * 100 + 'a', -1),
+ ('longstring ' * 100 + 'a', 'longstring ' * 100, +1),
+ (KATAKANA_LETTER_A, KATAKANA_LETTER_A, 0),
+ (KATAKANA_LETTER_A, 'a', 1),
+ ]
+ for a, b, expect in cases:
+ self.assertEquals(signum(strcasecmp_m(a.encode('utf-8'),
+ b.encode('utf-8'))),
+ expect)
+
+class strstr_m_Tests(samba.tests.TestCase):
+ """strstr_m tests in simple ASCII and unicode strings"""
+
+ def test_strstr_m(self):
+ # A, B, strstr_m(A, B)
+ cases = [('hello', 'hello', 'hello'),
+ ('hello', 'goodbye', None),
+ ('goodbye', 'hello', None),
+ ('hell', 'hello', None),
+ ('hello', 'hell', 'hello'),
+ ('', '', ''),
+ ('a', '', 'a'),
+ ('', 'a', None),
+ ('a', 'A', None),
+ ('aa', 'aA', None),
+ ('Aa', 'aa', None),
+ ('%v foo', '%v', '%v foo'),
+ ('foo %v foo', '%v', '%v foo'),
+ ('foo %v', '%v', '%v'),
+ ('longstring ' * 100, 'longstring ' * 99, 'longstring ' * 100),
+ ('longstring ' * 99, 'longstring ' * 100, None),
+ ('longstring a' * 99, 'longstring ' * 100 + 'a', None),
+ ('longstring ' * 100 + 'a', 'longstring ' * 100, 'longstring ' * 100 + 'a'),
+ (KATAKANA_LETTER_A, KATAKANA_LETTER_A + 'bcd', None),
+ (KATAKANA_LETTER_A + 'bcde', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcde'),
+ ('d'+KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcd'),
+ ('d'+KATAKANA_LETTER_A + 'bd', KATAKANA_LETTER_A + 'bcd', None),
+
+ ('e'+KATAKANA_LETTER_A + 'bcdf', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcdf'),
+ (KATAKANA_LETTER_A, KATAKANA_LETTER_A + 'bcd', None),
+ (KATAKANA_LETTER_A*3, 'a', None),
+ ]
+ for a, b, expect in cases:
+ if expect is not None:
+ expect = expect.encode('utf-8')
+ self.assertEquals(strstr_m(a.encode('utf-8'),
+ b.encode('utf-8')),
+ expect)
diff --git a/python/samba/tests/unicodenames.py b/python/samba/tests/unicodenames.py
new file mode 100644
index 00000000000..ed65de6651f
--- /dev/null
+++ b/python/samba/tests/unicodenames.py
@@ -0,0 +1,29 @@
+# Copyright (C) 2003 by Martin Pool <mbp@samba.org>
+
+# 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/>.
+#
+
+"""
+Defines symbolic names for a few UNICODE characters, to make test
+source code more readable on machines that don't have all the
+necessary fonts.
+
+You can do "import *" on this file safely.
+"""
+
+LATIN_CAPITAL_LETTER_N_WITH_TILDE = u'\u004e'
+LATIN_CAPITAL_LETTER_O_WITH_DIARESIS = u'\u00d6'
+LATIN_SMALL_LETTER_O_WITH_DIARESIS = u'\u00f6'
+
+KATAKANA_LETTER_A = u'\u30a2'
diff --git a/python/samba/tests/upgrade.py b/python/samba/tests/upgrade.py
new file mode 100644
index 00000000000..b46a4173191
--- /dev/null
+++ b/python/samba/tests/upgrade.py
@@ -0,0 +1,40 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+#
+# 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/>.
+#
+
+"""Tests for samba.upgrade."""
+
+from samba.upgrade import import_wins
+from samba.tests import LdbTestCase
+
+
+class WinsUpgradeTests(LdbTestCase):
+
+ def test_upgrade(self):
+ winsdb = {
+ "FOO#20": (200, ["127.0.0.1", "127.0.0.2"], 0x60)
+ }
+ import_wins(self.ldb, winsdb)
+
+ self.assertEquals(
+ ['name=FOO,type=0x20'],
+ [str(m.dn) for m in
+ self.ldb.search(expression="(objectClass=winsRecord)")])
+
+ def test_version(self):
+ import_wins(self.ldb, {})
+ self.assertEquals("VERSION",
+ str(self.ldb.search(expression="(objectClass=winsMaxVersion)")[0]["cn"]))
diff --git a/python/samba/tests/upgradeprovision.py b/python/samba/tests/upgradeprovision.py
new file mode 100644
index 00000000000..93a6731c830
--- /dev/null
+++ b/python/samba/tests/upgradeprovision.py
@@ -0,0 +1,135 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
+#
+# 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/>.
+#
+
+"""Tests for samba.upgradeprovision."""
+
+import os
+from samba.upgradehelpers import (usn_in_range, dn_sort,
+ get_diff_sddls, update_secrets,
+ construct_existor_expr)
+
+from samba.tests.provision import create_dummy_secretsdb
+from samba.tests import TestCaseInTempDir
+from samba import Ldb
+from ldb import SCOPE_BASE
+import samba.tests
+
+def dummymessage(a=None, b=None):
+ pass
+
+
+class UpgradeProvisionTestCase(TestCaseInTempDir):
+ """Some simple tests for individual functions in the provisioning code.
+ """
+ def test_usn_in_range(self):
+ range = [5, 25, 35, 55]
+
+ vals = [3, 26, 56]
+
+ for v in vals:
+ self.assertFalse(usn_in_range(v, range))
+
+ vals = [5, 20, 25, 35, 36]
+
+ for v in vals:
+ self.assertTrue(usn_in_range(v, range))
+
+ def test_dn_sort(self):
+ # higher level comes after lower even if lexicographicaly closer
+ # ie dc=tata,dc=toto (2 levels), comes after dc=toto
+ # even if dc=toto is lexicographicaly after dc=tata, dc=toto
+ self.assertEquals(dn_sort("dc=tata,dc=toto", "dc=toto"), 1)
+ self.assertEquals(dn_sort("dc=zata", "dc=tata"), 1)
+ self.assertEquals(dn_sort("dc=toto,dc=tata",
+ "cn=foo,dc=toto,dc=tata"), -1)
+ self.assertEquals(dn_sort("cn=bar, dc=toto,dc=tata",
+ "cn=foo, dc=toto,dc=tata"), -1)
+
+ def test_get_diff_sddl(self):
+ sddl = "O:SAG:DUD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA)\
+(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)S:AI(AU;CIIDSA;WP;;;WD)"
+ sddl1 = "O:SAG:DUD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA)\
+(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)S:AI(AU;CIIDSA;WP;;;WD)"
+ sddl2 = "O:BAG:DUD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA)\
+(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)S:AI(AU;CIIDSA;WP;;;WD)"
+ sddl3 = "O:SAG:BAD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA)\
+(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)S:AI(AU;CIIDSA;WP;;;WD)"
+ sddl4 = "O:SAG:DUD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;BA)\
+(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)S:AI(AU;CIIDSA;WP;;;WD)"
+ sddl5 = "O:SAG:DUD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA)\
+(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)"
+
+ self.assertEquals(get_diff_sddls(sddl, sddl1), "")
+ txt = get_diff_sddls(sddl, sddl2)
+ self.assertEquals(txt, "\tOwner mismatch: SA (in ref) BA(in current)\n")
+ txt = get_diff_sddls(sddl, sddl3)
+ self.assertEquals(txt, "\tGroup mismatch: DU (in ref) BA(in current)\n")
+ txt = get_diff_sddls(sddl, sddl4)
+ txtmsg = "\tPart dacl is different between reference and current here\
+ is the detail:\n\t\t(A;CIID;RPWPCRCCLCLORCWOWDSW;;;BA) ACE is not present in\
+ the reference\n\t\t(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA) ACE is not present in\
+ the current\n"
+ self.assertEquals(txt, txtmsg)
+ txt = get_diff_sddls(sddl, sddl5)
+ self.assertEquals(txt, "\tCurrent ACL hasn't a sacl part\n")
+
+ def test_construct_existor_expr(self):
+ res = construct_existor_expr([])
+ self.assertEquals(res, "")
+
+ res = construct_existor_expr(["foo"])
+ self.assertEquals(res, "(|(foo=*))")
+
+ res = construct_existor_expr(["foo", "bar"])
+ self.assertEquals(res, "(|(foo=*)(bar=*))")
+
+
+class UpdateSecretsTests(samba.tests.TestCaseInTempDir):
+
+ def setUp(self):
+ super(UpdateSecretsTests, self).setUp()
+ self.referencedb = create_dummy_secretsdb(
+ os.path.join(self.tempdir, "ref.ldb"))
+
+ def _getEmptyDb(self):
+ return Ldb(os.path.join(self.tempdir, "secrets.ldb"))
+
+ def _getCurrentFormatDb(self):
+ return create_dummy_secretsdb(
+ os.path.join(self.tempdir, "secrets.ldb"))
+
+ def test_trivial(self):
+ # Test that updating an already up-to-date secretsdb works fine
+ self.secretsdb = self._getCurrentFormatDb()
+ self.assertEquals(None,
+ update_secrets(self.referencedb, self.secretsdb, dummymessage))
+
+ def test_update_modules(self):
+ empty_db = self._getEmptyDb()
+ update_secrets(self.referencedb, empty_db, dummymessage)
+ newmodules = empty_db.search(base="@MODULES", scope=SCOPE_BASE)
+ refmodules = self.referencedb.search(base="@MODULES", scope=SCOPE_BASE)
+ self.assertEquals(newmodules.msgs, refmodules.msgs)
+
+ def tearDown(self):
+ for name in ["ref.ldb", "secrets.ldb", "secrets.tdb", "secrets.tdb.bak", "secrets.ntdb"]:
+ path = os.path.join(self.tempdir, name)
+ if os.path.exists(path):
+ os.unlink(path)
+ super(UpdateSecretsTests, self).tearDown()
+
+
diff --git a/python/samba/tests/upgradeprovisionneeddc.py b/python/samba/tests/upgradeprovisionneeddc.py
new file mode 100644
index 00000000000..a7cb298ed7f
--- /dev/null
+++ b/python/samba/tests/upgradeprovisionneeddc.py
@@ -0,0 +1,179 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
+#
+# 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/>.
+#
+
+"""Tests for samba.upgradeprovision that need a DC."""
+
+import os
+import re
+import shutil
+
+from samba import param
+from samba.credentials import Credentials
+from samba.auth import system_session
+from samba.provision import getpolicypath,find_provision_key_parameters
+from samba.upgradehelpers import (get_paths, get_ldbs,
+ identic_rename,
+ updateOEMInfo, getOEMInfo, update_gpo,
+ delta_update_basesamdb,
+ update_dns_account_password,
+ search_constructed_attrs_stored,
+ increment_calculated_keyversion_number)
+from samba.tests import env_loadparm, TestCaseInTempDir
+from samba.tests.provision import create_dummy_secretsdb
+import ldb
+
+
+def dummymessage(a=None, b=None):
+ pass
+
+smb_conf_path = "%s/%s/%s" % (os.environ["SELFTEST_PREFIX"], "dc", "etc/smb.conf")
+
+class UpgradeProvisionBasicLdbHelpersTestCase(TestCaseInTempDir):
+ """Some simple tests for individual functions in the provisioning code.
+ """
+
+ def test_get_ldbs(self):
+ paths = get_paths(param, None, smb_conf_path)
+ creds = Credentials()
+ lp = env_loadparm()
+ creds.guess(lp)
+ get_ldbs(paths, creds, system_session(), lp)
+
+ def test_find_key_param(self):
+ paths = get_paths(param, None, smb_conf_path)
+ creds = Credentials()
+ lp = env_loadparm()
+ creds.guess(lp)
+ rootdn = "dc=samba,dc=example,dc=com"
+ ldbs = get_ldbs(paths, creds, system_session(), lp)
+ names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
+ paths, smb_conf_path, lp)
+ self.assertEquals(names.realm, "SAMBA.EXAMPLE.COM")
+ self.assertEquals(str(names.rootdn).lower(), rootdn.lower())
+ self.assertNotEquals(names.policyid_dc, None)
+ self.assertNotEquals(names.ntdsguid, "")
+
+
+class UpgradeProvisionWithLdbTestCase(TestCaseInTempDir):
+
+ def _getEmptyDbName(self):
+ return os.path.join(self.tempdir, "sam.ldb")
+
+ def setUp(self):
+ super(UpgradeProvisionWithLdbTestCase, self).setUp()
+ paths = get_paths(param, None, smb_conf_path)
+ self.creds = Credentials()
+ self.lp = env_loadparm()
+ self.creds.guess(self.lp)
+ self.paths = paths
+ self.ldbs = get_ldbs(paths, self.creds, system_session(), self.lp)
+ self.names = find_provision_key_parameters(self.ldbs.sam,
+ self.ldbs.secrets, self.ldbs.idmap, paths, smb_conf_path,
+ self.lp)
+ self.referencedb = create_dummy_secretsdb(
+ os.path.join(self.tempdir, "ref.ldb"))
+
+ def test_search_constructed_attrs_stored(self):
+ hashAtt = search_constructed_attrs_stored(self.ldbs.sam,
+ self.names.rootdn,
+ ["msds-KeyVersionNumber"])
+ self.assertFalse(hashAtt.has_key("msds-KeyVersionNumber"))
+
+ def test_increment_calculated_keyversion_number(self):
+ dn = "CN=Administrator,CN=Users,%s" % self.names.rootdn
+ # We conctruct a simple hash for the user administrator
+ hash = {}
+ # And we want the version to be 140
+ hash[dn.lower()] = 140
+
+ increment_calculated_keyversion_number(self.ldbs.sam,
+ self.names.rootdn,
+ hash)
+ self.assertEqual(self.ldbs.sam.get_attribute_replmetadata_version(dn,
+ "unicodePwd"),
+ 140)
+ # This function should not decrement the version
+ hash[dn.lower()] = 130
+
+ increment_calculated_keyversion_number(self.ldbs.sam,
+ self.names.rootdn,
+ hash)
+ self.assertEqual(self.ldbs.sam.get_attribute_replmetadata_version(dn,
+ "unicodePwd"),
+ 140)
+
+ def test_identic_rename(self):
+ rootdn = "DC=samba,DC=example,DC=com"
+
+ guestDN = ldb.Dn(self.ldbs.sam, "CN=Guest,CN=Users,%s" % rootdn)
+ identic_rename(self.ldbs.sam, guestDN)
+ res = self.ldbs.sam.search(expression="(name=Guest)", base=rootdn,
+ scope=ldb.SCOPE_SUBTREE, attrs=["dn"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0]["dn"]), "CN=Guest,CN=Users,%s" % rootdn)
+
+ def test_delta_update_basesamdb(self):
+ dummysampath = self._getEmptyDbName()
+ delta_update_basesamdb(self.paths.samdb, dummysampath,
+ self.creds, system_session(), self.lp,
+ dummymessage)
+
+ def test_update_gpo_simple(self):
+ dir = getpolicypath(self.paths.sysvol, self.names.dnsdomain,
+ self.names.policyid)
+ shutil.rmtree(dir)
+ self.assertFalse(os.path.isdir(dir))
+ update_gpo(self.paths, self.ldbs.sam, self.names, self.lp, dummymessage)
+ self.assertTrue(os.path.isdir(dir))
+
+ def test_update_gpo_acl(self):
+ path = os.path.join(self.tempdir, "testupdategpo")
+ save = self.paths.sysvol
+ self.paths.sysvol = path
+ os.mkdir(path)
+ os.mkdir(os.path.join(path, self.names.dnsdomain))
+ os.mkdir(os.path.join(os.path.join(path, self.names.dnsdomain),
+ "Policies"))
+ update_gpo(self.paths, self.ldbs.sam, self.names, self.lp, dummymessage)
+ shutil.rmtree(path)
+ self.paths.sysvol = save
+
+ def test_getOEMInfo(self):
+ realm = self.lp.get("realm")
+ basedn = "DC=%s" % realm.replace(".", ", DC=")
+ oem = getOEMInfo(self.ldbs.sam, basedn)
+ self.assertNotEquals(oem, "")
+
+ def test_update_dns_account(self):
+ update_dns_account_password(self.ldbs.sam, self.ldbs.secrets,
+ self.names)
+
+ def test_updateOEMInfo(self):
+ realm = self.lp.get("realm")
+ basedn = "DC=%s" % realm.replace(".", ", DC=")
+ oem = getOEMInfo(self.ldbs.sam, basedn)
+ updateOEMInfo(self.ldbs.sam, basedn)
+ oem2 = getOEMInfo(self.ldbs.sam, basedn)
+ self.assertNotEquals(str(oem), str(oem2))
+ self.assertTrue(re.match(".*upgrade to.*", str(oem2)))
+
+ def tearDown(self):
+ for name in ["ref.ldb", "secrets.ldb", "secrets.tdb", "secrets.tdb.bak", "secrets.ntdb", "sam.ldb"]:
+ path = os.path.join(self.tempdir, name)
+ if os.path.exists(path):
+ os.unlink(path)
+ super(UpgradeProvisionWithLdbTestCase, self).tearDown()
diff --git a/python/samba/tests/xattr.py b/python/samba/tests/xattr.py
new file mode 100644
index 00000000000..89add284566
--- /dev/null
+++ b/python/samba/tests/xattr.py
@@ -0,0 +1,126 @@
+# Unix SMB/CIFS implementation. Tests for xattr manipulation
+# Copyright (C) Matthieu Patou <mat@matws.net> 2009
+#
+# 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/>.
+#
+
+"""Tests for samba.xattr_native and samba.xattr_tdb."""
+
+import samba.xattr_native, samba.xattr_tdb
+from samba.xattr import copytree_with_xattrs
+from samba.dcerpc import xattr
+from samba.ndr import ndr_pack
+from samba.tests import (
+ TestCase,
+ TestCaseInTempDir,
+ TestSkipped,
+ )
+import random
+import shutil
+import os
+
+class XattrTests(TestCase):
+
+ def _tmpfilename(self):
+ random.seed()
+ path = os.environ['SELFTEST_PREFIX']
+ return os.path.join(path, "pytests"+str(int(100000*random.random())))
+
+ def _eadbpath(self):
+ return os.path.join(os.environ['SELFTEST_PREFIX'], "eadb.tdb")
+
+ def test_set_xattr_native(self):
+ if not samba.xattr_native.is_xattr_supported():
+ raise TestSkipped()
+ ntacl = xattr.NTACL()
+ ntacl.version = 1
+ tempf = self._tmpfilename()
+ open(tempf, 'w').write("empty")
+ try:
+ samba.xattr_native.wrap_setxattr(tempf, "user.unittests",
+ ndr_pack(ntacl))
+ except IOError:
+ raise TestSkipped("the filesystem where the tests are runned do not support XATTR")
+ os.unlink(tempf)
+
+ def test_set_and_get_native(self):
+ if not samba.xattr_native.is_xattr_supported():
+ raise TestSkipped()
+ tempf = self._tmpfilename()
+ reftxt = "this is a test"
+ open(tempf, 'w').write("empty")
+ try:
+ samba.xattr_native.wrap_setxattr(tempf, "user.unittests", reftxt)
+ text = samba.xattr_native.wrap_getxattr(tempf, "user.unittests")
+ self.assertEquals(text, reftxt)
+ except IOError:
+ raise TestSkipped("the filesystem where the tests are runned do not support XATTR")
+ os.unlink(tempf)
+
+ def test_set_xattr_tdb(self):
+ tempf = self._tmpfilename()
+ eadb_path = self._eadbpath()
+ ntacl = xattr.NTACL()
+ ntacl.version = 1
+ open(tempf, 'w').write("empty")
+ try:
+ samba.xattr_tdb.wrap_setxattr(eadb_path,
+ tempf, "user.unittests", ndr_pack(ntacl))
+ finally:
+ os.unlink(tempf)
+ os.unlink(eadb_path)
+
+ def test_set_tdb_not_open(self):
+ tempf = self._tmpfilename()
+ ntacl = xattr.NTACL()
+ ntacl.version = 1
+ open(tempf, 'w').write("empty")
+ try:
+ self.assertRaises(IOError, samba.xattr_tdb.wrap_setxattr,
+ os.path.join("nonexistent", "eadb.tdb"), tempf,
+ "user.unittests", ndr_pack(ntacl))
+ finally:
+ os.unlink(tempf)
+
+ def test_set_and_get_tdb(self):
+ tempf = self._tmpfilename()
+ eadb_path = self._eadbpath()
+ reftxt = "this is a test"
+ open(tempf, 'w').write("empty")
+ try:
+ samba.xattr_tdb.wrap_setxattr(eadb_path, tempf, "user.unittests",
+ reftxt)
+ text = samba.xattr_tdb.wrap_getxattr(eadb_path, tempf,
+ "user.unittests")
+ self.assertEquals(text, reftxt)
+ finally:
+ os.unlink(tempf)
+ os.unlink(eadb_path)
+
+
+class TestCopyTreeWithXattrs(TestCaseInTempDir):
+
+ def test_simple(self):
+ os.chdir(self.tempdir)
+ os.mkdir("a")
+ os.mkdir("a/b")
+ os.mkdir("a/b/c")
+ f = open('a/b/c/d', 'w')
+ try:
+ f.write("foo")
+ finally:
+ f.close()
+ copytree_with_xattrs("a", "b")
+ shutil.rmtree("a")
+ shutil.rmtree("b")