1 # Blackbox tests for sambadowngradedatabase
3 # Copyright (C) Catalyst IT Ltd. 2019
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 from __future__ import print_function
19 from samba.tests import BlackboxTestCase
22 from subprocess import check_output
23 from samba.samdb import SamDB
25 COMMAND = os.path.join(os.path.dirname(__file__),
26 "../../../../../source4/scripting/bin/sambadowngradedatabase")
29 class DowngradeTestBase(BlackboxTestCase):
30 """Test that sambadowngradedatabase downgrades the samba database"""
33 super(DowngradeTestBase, self).setUp()
34 if not hasattr(self, "backend"):
35 self.fail("Subclass this class and set 'backend'")
37 # Don't assert on empty tempdir contents on tearDown
38 self.check_tempdir_empty = False
40 prov_cmd = "samba-tool domain provision " +\
41 "--domain FOO --realm foo.example.com " +\
42 "--targetdir {self.tempdir} " +\
43 "--backend-store {self.backend} " +\
44 "--host-name downgradetest " +\
45 "--option=\"vfs objects=fake_acls xattr_tdb\""
46 prov_cmd = prov_cmd.format(self=self)
47 self.check_run(prov_cmd, "Provisioning for downgrade")
49 private_dir = os.path.join(self.tempdir, "private")
50 self.sam_path = os.path.join(private_dir, "sam.ldb")
51 self.ldb = ldb.Ldb(self.sam_path, options=["modules:"])
53 partitions = self.ldb.search(base="@PARTITION",
56 partitions = partitions[0]['partition']
57 partitions = [str(p).split(":")[1] for p in partitions]
58 self.dbs = [os.path.join(private_dir, p)
60 self.dbs.append(self.sam_path)
62 # Parse out the comments above each record that ldbdump produces
63 # containing pack format version and KV level key for each record.
64 # Return all GUID keys and DN keys (without @attrs), and the set
65 # of all unique pack formats.
66 def ldbdump_keys_pack_formats(self):
67 # Get all comments from all partition dbs
70 dump = check_output(["bin/ldbdump", "-i", db])
71 dump = dump.decode("utf-8")
72 dump = dump.split("\n")
73 comments += [s for s in dump if s.startswith("#")]
75 guid_key_tag = "# key: GUID="
76 guid_keys = {c[len(guid_key_tag):] for c in comments
77 if c.startswith(guid_key_tag)}
79 dn_key_tag = "# key: DN="
80 dn_keys = {c[len(dn_key_tag):] for c in comments
81 if c.startswith(dn_key_tag)}
83 # Ignore @ attributes, they are always DN keyed
84 dn_keys_no_at_attrs = {d for d in dn_keys if not d.startswith("@")}
86 pack_format_tag = "# pack format: "
87 pack_formats = {c[len(pack_format_tag):] for c in comments
88 if c.startswith(pack_format_tag)}
89 pack_formats = [int(s, 16) for s in pack_formats]
91 return dn_keys_no_at_attrs, guid_keys, pack_formats
93 # Get a set of all distinct types in @ATTRIBUTES
94 def attribute_types(self):
95 at_attributes = self.ldb.search(base="@ATTRIBUTES",
98 self.assertEqual(len(at_attributes), 1)
99 keys = at_attributes[0].keys()
100 attribute_types = {str(at_attributes[0].get(k)) for k in keys}
102 return attribute_types
104 class DowngradeTestTDB(DowngradeTestBase):
107 # Check that running sambadowngradedatabase with a TDB backend:
108 # * Replaces all GUID keys with DN keys
109 # * Removes ORDERED_INTEGER from @ATTRIBUTES
110 # * Repacks database with pack format version 1
111 def test_downgrade_database(self):
112 type_prefix = "LDB_SYNTAX_"
113 ordered_int_type = ldb.SYNTAX_ORDERED_INTEGER[len(type_prefix):]
115 dn_keys, guid_keys, pack_formats = self.ldbdump_keys_pack_formats()
116 self.assertGreater(len(guid_keys), 20)
117 self.assertEqual(len(dn_keys), 0)
118 self.assertTrue(ordered_int_type in self.attribute_types())
119 self.assertEqual(pack_formats, [ldb.PACKING_FORMAT_V2])
121 num_guid_keys_before_downgrade = len(guid_keys)
123 self.check_run("%s -H %s" % (COMMAND, self.sam_path),
124 msg="Running sambadowngradedatabase")
126 dn_keys, guid_keys, pack_formats = self.ldbdump_keys_pack_formats()
127 self.assertEqual(len(guid_keys), 0)
128 self.assertEqual(len(dn_keys), num_guid_keys_before_downgrade)
129 self.assertTrue(ordered_int_type not in self.attribute_types())
130 self.assertEqual(pack_formats, [ldb.PACKING_FORMAT])
132 class DowngradeTestMDB(DowngradeTestBase):
135 # Check that running sambadowngradedatabase with a TDB backend:
136 # * Does NOT replace GUID keys with DN keys
137 # * Removes ORDERED_INTEGER from @ATTRIBUTES
138 # * Repacks database with pack format version 1
139 def test_undo_guid(self):
140 type_prefix = "LDB_SYNTAX_"
141 ordered_int_type = ldb.SYNTAX_ORDERED_INTEGER[len(type_prefix):]
143 dn_keys, guid_keys, pack_formats = self.ldbdump_keys_pack_formats()
144 self.assertGreater(len(guid_keys), 20)
145 self.assertEqual(len(dn_keys), 0)
146 self.assertTrue(ordered_int_type in self.attribute_types())
147 self.assertEqual(pack_formats, [ldb.PACKING_FORMAT_V2])
149 num_guid_keys_before_downgrade = len(guid_keys)
151 self.check_run("%s -H %s" % (COMMAND, self.sam_path),
152 msg="Running sambadowngradedatabase")
154 dn_keys, guid_keys, pack_formats = self.ldbdump_keys_pack_formats()
155 self.assertEqual(len(guid_keys), num_guid_keys_before_downgrade)
156 self.assertEqual(len(dn_keys), 0)
157 self.assertTrue(ordered_int_type not in self.attribute_types())
158 self.assertEqual(pack_formats, [ldb.PACKING_FORMAT])