diff options
| author | Rob van der Linde <rob@catalyst.net.nz> | 2023-05-16 12:22:25 +1200 |
|---|---|---|
| committer | Andrew Bartlett <abartlet@samba.org> | 2023-06-25 23:29:32 +0000 |
| commit | 35d04e2463f05ee0067b288774dd6238f12935ed (patch) | |
| tree | f7b486823f6bec048149f087f065042451ab833f /python/samba | |
| parent | 3df634e7527c2e0f9c71d62afc7a48300b7bd388 (diff) | |
| download | samba-35d04e2463f05ee0067b288774dd6238f12935ed.tar.gz samba-35d04e2463f05ee0067b288774dd6238f12935ed.tar.bz2 samba-35d04e2463f05ee0067b288774dd6238f12935ed.zip | |
netcmd: domain: tests for auth silo command line tools
Signed-off-by: Rob van der Linde <rob@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Diffstat (limited to 'python/samba')
| -rw-r--r-- | python/samba/tests/samba_tool/domain_auth_base.py | 199 | ||||
| -rw-r--r-- | python/samba/tests/samba_tool/domain_auth_policy.py | 607 | ||||
| -rw-r--r-- | python/samba/tests/samba_tool/domain_auth_silo.py | 420 |
3 files changed, 1226 insertions, 0 deletions
diff --git a/python/samba/tests/samba_tool/domain_auth_base.py b/python/samba/tests/samba_tool/domain_auth_base.py new file mode 100644 index 00000000000..43795ddbd9e --- /dev/null +++ b/python/samba/tests/samba_tool/domain_auth_base.py @@ -0,0 +1,199 @@ +# Unix SMB/CIFS implementation. +# +# Base class for samba-tool domain auth policy and silo commands +# +# Copyright (C) Catalyst.Net Ltd. 2023 +# +# Written by Rob van der Linde <rob@catalyst.net.nz> +# +# 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 ldb import SCOPE_ONELEVEL + +from .base import SambaToolCmdTest + + +class BaseAuthCmdTest(SambaToolCmdTest): + def setUp(self): + super().setUp() + self.host = "ldap://{DC_SERVER}".format(**os.environ) + self.creds = "-U{DC_USERNAME}%{DC_PASSWORD}".format(**os.environ) + self.samdb = self.getSamDB("-H", self.host, self.creds) + + # Generate some test data. + self.policies = [] + self.create_authentication_policy(name="Single Policy") + self.create_authentication_policy(name="User Policy") + self.create_authentication_policy(name="Service Policy") + self.create_authentication_policy(name="Computer Policy") + + self.silos = [] + self.create_authentication_silo(name="Developers", + description="Developers, Developers", + policy="Single Policy") + self.create_authentication_silo(name="Managers", + description="Managers", + policy="Single Policy") + self.create_authentication_silo(name="QA", + description="Quality Assurance", + user_policy="User Policy", + service_policy="Service Policy", + computer_policy="Computer Policy") + + def tearDown(self): + """Remove data created by setUp.""" + for policy in self.policies: + self.delete_authentication_policy(policy, force=True) + + for silo in self.silos: + self.delete_authentication_silo(silo, force=True) + + super().tearDown() + + def get_services_dn(self): + """Returns Services DN.""" + services_dn = self.samdb.get_config_basedn() + services_dn.add_child("CN=Services") + return services_dn + + def get_authn_configuration_dn(self): + """Returns AuthN Configuration DN.""" + authn_policy_configuration = self.get_services_dn() + authn_policy_configuration.add_child("CN=AuthN Policy Configuration") + return authn_policy_configuration + + def get_authn_silos_dn(self): + """Returns AuthN Silos DN.""" + authn_silos_dn = self.get_authn_configuration_dn() + authn_silos_dn.add_child("CN=AuthN Silos") + return authn_silos_dn + + def get_authn_policies_dn(self): + """Returns AuthN Policies DN.""" + authn_policies_dn = self.get_authn_configuration_dn() + authn_policies_dn.add_child("CN=AuthN Policies") + return authn_policies_dn + + def _run(self, *argv): + """Override _run, so we don't always have to pass host and creds.""" + args = list(argv) + args.extend(["-H", self.host, self.creds]) + return super()._run(*args) + + runcmd = _run + runsubcmd = _run + + def create_authentication_policy(self, name, description=None, audit=False, + protect=False): + """Create an authentication policy.""" + + # base command for create authentication policy + cmd = ["domain", "auth", "policy", "create", "--name", name] + + # optional attributes + if description is not None: + cmd.append(f"--description={description}") + if audit: + cmd.append("--audit") + if protect: + cmd.append("--protect") + + # Run command and store name in self.silos for tearDown to clean up. + result, out, err = self.runcmd(*cmd) + self.assertIsNone(result, msg=err) + self.assertTrue(out.startswith("Created authentication policy")) + self.policies.append(name) + return name + + def delete_authentication_policy(self, name, force=False): + """Delete authentication policy by name.""" + cmd = ["domain", "auth", "policy", "delete", "--name", name] + + # Force-delete protected authentication policy. + if force: + cmd.append("--force") + + result, out, err = self.runcmd(*cmd) + self.assertIsNone(result, msg=err) + self.assertIn("Deleted authentication policy", out) + + def create_authentication_silo(self, name, description=None, policy=None, + user_policy=None, service_policy=None, + computer_policy=None, audit=False, + protect=False): + """Create an authentication silo using the samba-tool command.""" + + # Base command for create authentication policy. + cmd = ["domain", "auth", "silo", "create", "--name", name] + + # If --policy is present, use a singular authentication policy. + # otherwise use --user-policy, --service-policy, --computer-policy + if policy is not None: + cmd += ["--policy", policy] + else: + cmd += ["--user-policy", user_policy, + "--service-policy", service_policy, + "--computer-policy", computer_policy] + + # Other optional attributes. + if description is not None: + cmd.append(f"--description={description}") + if protect: + cmd.append("--protect") + if audit: + cmd.append("--audit") + + # Run command and store name in self.silos for tearDown to clean up. + result, out, err = self.runcmd(*cmd) + self.assertIsNone(result, msg=err) + self.assertTrue(out.startswith("Created authentication silo")) + self.silos.append(name) + return name + + def delete_authentication_silo(self, name, force=False): + """Delete authentication silo by name.""" + cmd = ["domain", "auth", "silo", "delete", "--name", name] + + # Force-delete protected authentication silo. + if force: + cmd.append("--force") + + result, out, err = self.runcmd(*cmd) + self.assertIsNone(result, msg=err) + self.assertIn("Deleted authentication silo", out) + + def get_authentication_silo(self, name): + """Get authentication silo by name.""" + authn_silos_dn = self.get_authn_silos_dn() + + result = self.samdb.search(base=authn_silos_dn, + scope=SCOPE_ONELEVEL, + expression=f"(CN={name})") + + if len(result) == 1: + return result[0] + + def get_authentication_policy(self, name): + """Get authentication policy by name.""" + authn_policies_dn = self.get_authn_policies_dn() + + result = self.samdb.search(base=authn_policies_dn, + scope=SCOPE_ONELEVEL, + expression=f"(CN={name})") + + if len(result) == 1: + return result[0] diff --git a/python/samba/tests/samba_tool/domain_auth_policy.py b/python/samba/tests/samba_tool/domain_auth_policy.py new file mode 100644 index 00000000000..d73af183f0b --- /dev/null +++ b/python/samba/tests/samba_tool/domain_auth_policy.py @@ -0,0 +1,607 @@ +# Unix SMB/CIFS implementation. +# +# Tests for samba-tool domain auth policy command +# +# Copyright (C) Catalyst.Net Ltd. 2023 +# +# Written by Rob van der Linde <rob@catalyst.net.nz> +# +# 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 json +from optparse import OptionValueError +from unittest.mock import patch + +from ldb import LdbError +from samba.netcmd import CommandError +from samba.samdb import SamDB +from samba.sd_utils import SDUtils + +from .domain_auth_base import BaseAuthCmdTest + + +class AuthPolicyCmdTestCase(BaseAuthCmdTest): + + def test_authentication_policy_list(self): + """Test listing authentication policies in list format.""" + result, out, err = self.runcmd("domain", "auth", "policy", "list") + self.assertIsNone(result, msg=err) + + # Check each authentication policy we created is there. + for policy in self.policies: + self.assertIn(policy, out) + + def test_authentication_policy_list_json(self): + """Test listing authentication policies in JSON format.""" + result, out, err = self.runcmd("domain", "auth", "policy", + "list", "--json") + self.assertIsNone(result, msg=err) + + # we should get valid json + policies = json.loads(out) + + # each policy in self.policies must be present + for name in self.policies: + policy = policies[name] + self.assertIn("name", policy) + self.assertIn("msDS-AuthNPolicy", list(policy["objectClass"])) + self.assertIn("msDS-AuthNPolicyEnforced", policy) + self.assertIn("msDS-StrongNTLMPolicy", policy) + self.assertIn("objectGUID", policy) + + def test_authentication_policy_view(self): + """Test viewing a single authentication policy.""" + result, out, err = self.runcmd("domain", "auth", "policy", "view", + "--name", "Single Policy") + self.assertIsNone(result, msg=err) + + # we should get valid json + policy = json.loads(out) + + # check a few fields only + self.assertEqual(policy["cn"], "Single Policy") + self.assertEqual(policy["msDS-AuthNPolicyEnforced"], True) + + def test_authentication_policy_view_notfound(self): + """Test viewing an authentication policy that doesn't exist.""" + result, out, err = self.runcmd("domain", "auth", "policy", "view", + "--name", "doesNotExist") + self.assertEqual(result, -1) + self.assertIn("Authentication policy doesNotExist not found.", err) + + def test_authentication_policy_view_name_required(self): + """Test view authentication policy without --name argument.""" + result, out, err = self.runcmd("domain", "auth", "policy", "view") + self.assertEqual(result, -1) + self.assertIn("Argument --name is required.", err) + + def test_authentication_policy_create(self): + """Test creating a new authentication policy.""" + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name", "createTest") + self.assertIsNone(result, msg=err) + + # Check policy that was created + policy = self.get_authentication_policy("createTest") + self.assertEqual(str(policy["cn"]), "createTest") + self.assertEqual(str(policy["msDS-AuthNPolicyEnforced"]), "TRUE") + + def test_authentication_policy_create_description(self): + """Test creating a new authentication policy with description set.""" + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name", "descriptionTest", + "--description", "Custom Description") + self.assertIsNone(result, msg=err) + + # Check policy description + policy = self.get_authentication_policy("descriptionTest") + self.assertEqual(str(policy["cn"]), "descriptionTest") + self.assertEqual(str(policy["description"]), "Custom Description") + + def test_authentication_policy_create_user_tgt_lifetime(self): + """Test create a new authentication policy with --user-tgt-lifetime. + + Also checks the upper and lower bounds are handled. + """ + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name", "userTGTLifetime", + "--user-tgt-lifetime", "60") + self.assertIsNone(result, msg=err) + + # Check policy fields. + policy = self.get_authentication_policy("userTGTLifetime") + self.assertEqual(str(policy["cn"]), "userTGTLifetime") + self.assertEqual(str(policy["msDS-UserTGTLifetime"]), "60") + + # check lower bounds (45) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "create", + "--name", "userTGTLifetimeLower", + "--user-tgt-lifetime", "44") + + self.assertIn("--user-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + # check upper bounds (2147483647) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "create", + "--name", "userTGTLifetimeUpper", + "--user-tgt-lifetime", "2147483648") + + self.assertIn("--user-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + def test_authentication_policy_create_service_tgt_lifetime(self): + """Test create a new authentication policy with --service-tgt-lifetime. + + Also checks the upper and lower bounds are handled. + """ + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name", "serviceTGTLifetime", + "--service-tgt-lifetime", "60") + self.assertIsNone(result, msg=err) + + # Check policy fields. + policy = self.get_authentication_policy("serviceTGTLifetime") + self.assertEqual(str(policy["cn"]), "serviceTGTLifetime") + self.assertEqual(str(policy["msDS-ServiceTGTLifetime"]), "60") + + # check lower bounds (45) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "create", + "--name", "serviceTGTLifetimeLower", + "--service-tgt-lifetime", "44") + + self.assertIn("--service-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + # check upper bounds (2147483647) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "create", + "--name", "serviceTGTLifetimeUpper", + "--service-tgt-lifetime", "2147483648") + + self.assertIn("--service-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + def test_authentication_policy_create_computer_tgt_lifetime(self): + """Test create a new authentication policy with --computer-tgt-lifetime. + + Also checks the upper and lower bounds are handled. + """ + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name", "computerTGTLifetime", + "--computer-tgt-lifetime", "60") + self.assertIsNone(result, msg=err) + + # Check policy fields. + policy = self.get_authentication_policy("computerTGTLifetime") + self.assertEqual(str(policy["cn"]), "computerTGTLifetime") + self.assertEqual(str(policy["msDS-ComputerTGTLifetime"]), "60") + + # check lower bounds (45) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "create", + "--name", "computerTGTLifetimeLower", + "--computer-tgt-lifetime", "44") + + self.assertIn("--computer-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + # check upper bounds (2147483647) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "create", + "--name", "computerTGTLifetimeUpper", + "--computer-tgt-lifetime", "2147483648") + + self.assertIn("--computer-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + def test_authentication_policy_create_already_exists(self): + """Test creating a new authentication policy that already exists.""" + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name", "Single Policy") + self.assertEqual(result, -1) + self.assertIn("Authentication policy Single Policy already exists", err) + + def test_authentication_policy_create_name_missing(self): + """Test create authentication policy without --name argument.""" + result, out, err = self.runcmd("domain", "auth", "policy", "create") + self.assertEqual(result, -1) + self.assertIn("Argument --name is required.", err) + + def test_authentication_policy_create_audit(self): + """Test create authentication policy with --audit flag.""" + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name", "auditPolicy", + "--audit") + self.assertIsNone(result, msg=err) + + # fetch and check policy + policy = self.get_authentication_policy("auditPolicy") + self.assertEqual(str(policy["msDS-AuthNPolicyEnforced"]), "FALSE") + + def test_authentication_policy_create_enforce(self): + """Test create authentication policy with --enforce flag.""" + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name", "enforcePolicy", + "--enforce") + self.assertIsNone(result, msg=err) + + # fetch and check policy + policy = self.get_authentication_policy("enforcePolicy") + self.assertEqual(str(policy["msDS-AuthNPolicyEnforced"]), "TRUE") + + def test_authentication_policy_create_audit_enforce_together(self): + """Test create auth policy using both --audit and --enforce.""" + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name", "enforceTogether", + "--audit", "--enforce") + self.assertEqual(result, -1) + self.assertIn("--audit and --enforce cannot be used together.", err) + + def test_authentication_policy_create_protect_unprotect_together(self): + """Test create authentication policy using --protect and --unprotect.""" + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name", "protectTogether", + "--protect", "--unprotect") + self.assertEqual(result, -1) + self.assertIn("--protect and --unprotect cannot be used together.", err) + + def test_authentication_policy_create_fails(self): + """Test creating an authentication policy, but it fails.""" + # Raise LdbError when ldb.add() is called. + with patch.object(SamDB, "add") as add_mock: + add_mock.side_effect = LdbError("Custom error message") + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name", "createFails") + self.assertEqual(result, -1) + self.assertIn("ERROR: Custom error message", err) + + def test_authentication_policy_modify_description(self): + """Test modifying an authentication policy description.""" + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--description", "NewDescription") + self.assertIsNone(result, msg=err) + + # Verify fields were changed. + policy = self.get_authentication_policy("Single Policy") + self.assertEqual(str(policy["description"]), "NewDescription") + + def test_authentication_policy_modify_strong_ntlm_policy(self): + """Test modify strong ntlm policy on the authentication policy.""" + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--strong-ntlm-policy", "Required") + self.assertIsNone(result, msg=err) + + # Verify fields were changed. + policy = self.get_authentication_policy("Single Policy") + self.assertEqual(str(policy["msDS-StrongNTLMPolicy"]), "2") + + # Check an invalid choice. + with self.assertRaises((OptionValueError, SystemExit)): + self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--strong-ntlm-policy", "Invalid") + + # It is difficult to test the error message text for invalid + # choices because inside optparse it will raise OptionValueError + # followed by raising SystemExit(2). + + def test_authentication_policy_modify_user_tgt_lifetime(self): + """Test modifying a authentication policy --user-tgt-lifetime. + + This includes checking the upper and lower bounds. + """ + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--user-tgt-lifetime", "120") + self.assertIsNone(result, msg=err) + + # Verify field was changed. + policy = self.get_authentication_policy("Single Policy") + self.assertEqual(str(policy["msDS-UserTGTLifetime"]), "120") + + # check lower bounds (45) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--user-tgt-lifetime", "44") + + self.assertIn("--user-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + # check upper bounds (2147483647) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--user-tgt-lifetime", "2147483648") + + self.assertIn("-user-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + def test_authentication_policy_modify_service_tgt_lifetime(self): + """Test modifying a authentication policy --service-tgt-lifetime. + + This includes checking the upper and lower bounds. + """ + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--service-tgt-lifetime", "120") + self.assertIsNone(result, msg=err) + + # Verify field was changed. + policy = self.get_authentication_policy("Single Policy") + self.assertEqual(str(policy["msDS-ServiceTGTLifetime"]), "120") + + # check lower bounds (45) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--service-tgt-lifetime", "44") + + self.assertIn("--service-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + # check upper bounds (2147483647) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--service-tgt-lifetime", "2147483648") + + self.assertIn("--service-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + def test_authentication_policy_modify_computer_tgt_lifetime(self): + """Test modifying a authentication policy --computer-tgt-lifetime. + + This includes checking the upper and lower bounds. + """ + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--computer-tgt-lifetime", "120") + self.assertIsNone(result, msg=err) + + # Verify field was changed. + policy = self.get_authentication_policy("Single Policy") + self.assertEqual(str(policy["msDS-ComputerTGTLifetime"]), "120") + + # check lower bounds (45) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--computer-tgt-lifetime", "44") + + self.assertIn("--computer-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + # check upper bounds (2147483647) + with self.assertRaises(CommandError) as e: + self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--computer-tgt-lifetime", "2147483648") + + self.assertIn("--computer-tgt-lifetime must be between 45 and 2147483647", + str(e.exception)) + + def test_authentication_policy_modify_name_missing(self): + """Test modify authentication but the --name argument is missing.""" + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--description", "NewDescription") + self.assertEqual(result, -1) + self.assertIn("Argument --name is required.", err) + + def test_authentication_policy_modify_notfound(self): + """Test modify an authentication silo that doesn't exist.""" + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "doesNotExist", + "--description", "NewDescription") + self.assertEqual(result, -1) + self.assertIn("ERROR: Authentication policy doesNotExist not found.", + err) + + def test_authentication_policy_modify_audit_enforce(self): + """Test modify authentication policy using --audit and --enforce.""" + # Change to audit, the default is --enforce. + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--audit") + self.assertIsNone(result, msg=err) + + # Check that the policy was changed to --audit. + policy = self.get_authentication_policy("Single Policy") + self.assertEqual(str(policy["msDS-AuthNPolicyEnforced"]), "FALSE") + + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--enforce") + self.assertIsNone(result, msg=err) + + # Check if the policy was changed back to --enforce. + policy = self.get_authentication_policy("Single Policy") + self.assertEqual(str(policy["msDS-AuthNPolicyEnforced"]), "TRUE") + + def test_authentication_policy_modify_protect_unprotect(self): + """Test modify authentication policy using --protect and --unprotect.""" + utils = SDUtils(self.samdb) + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--protect") + self.assertIsNone(result, msg=err) + + # Check that claim type was protected. + policy = self.get_authentication_policy("Single Policy") + desc = utils.get_sd_as_sddl(policy["dn"]) + self.assertIn("(D;;DTSD;;;WD)", desc) + + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--unprotect") + self.assertIsNone(result, msg=err) + + # Check that claim type was unprotected. + policy = self.get_authentication_policy("Single Policy") + desc = utils.get_sd_as_sddl(policy["dn"]) + self.assertNotIn("(D;;DTSD;;;WD)", desc) + + def test_authentication_policy_modify_audit_enforce_together(self): + """Test modify auth policy using both --audit and --enforce.""" + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--audit", "--enforce") + self.assertEqual(result, -1) + self.assertIn("--audit and --enforce cannot be used together.", err) + + def test_authentication_policy_modify_protect_unprotect_together(self): + """Test modify authentication policy using --protect and --unprotect.""" + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--protect", "--unprotect") + self.assertEqual(result, -1) + self.assertIn("--protect and --unprotect cannot be used together.", err) + + def test_authentication_policy_modify_fails(self): + """Test modifying an authentication policy, but it fails.""" + # Raise LdbError when ldb.add() is called. + with patch.object(SamDB, "modify") as modify_mock: + modify_mock.side_effect = LdbError("Custom error message") + result, out, err = self.runcmd("domain", "auth", "policy", "modify", + "--name", "Single Policy", + "--description", "New description") + self.assertEqual(result, -1) + self.assertIn("ERROR: Custom error message", err) + + def test_authentication_policy_delete(self): + """Test deleting an authentication policy that is not protected.""" + # Create non-protected authentication policy. + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name=deleteTest") + self.assertIsNone(result, msg=err) + policy = self.get_authentication_policy("deleteTest") + self.assertIsNotNone(policy) + + # Do the deletion. + result, out, err = self.runcmd("domain", "auth", "policy", "delete", + "--name", "deleteTest") + self.assertIsNone(result, msg=err) + + # Authentication policy shouldn't exist anymore. + policy = self.get_authentication_policy("deleteTest") + self.assertIsNone(policy) + + def test_authentication_policy_delete_protected(self): + """Test deleting a protected auth policy, with and without --force.""" + # Create protected authentication policy. + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name=deleteProtected", + "--protect") + self.assertIsNone(result, msg=err) + policy = self.get_authentication_policy("deleteProtected") + self.assertIsNotNone(policy) + + # Do the deletion. + result, out, err = self.runcmd("domain", "auth", "policy", "delete", + "--name=deleteProtected") + self.assertEqual(result, -1) + + # Authentication silo should still exist. + policy = self.get_authentication_policy("deleteProtected") + self.assertIsNotNone(policy) + + # Try a force delete instead. + result, out, err = self.runcmd("domain", "auth", "policy", "delete", + "--name=deleteProtected", "--force") + self.assertIsNone(result, msg=err) + + # Authentication silo shouldn't exist anymore. + policy = self.get_authentication_policy("deleteProtected") + self.assertIsNone(policy) + + def test_authentication_policy_delete_notfound(self): + """Test deleting an authentication policy that doesn't exist.""" + result, out, err = self.runcmd("domain", "auth", "policy", "delete", + "--name", "doesNotExist") + self.assertEqual(result, -1) + self.assertIn("Authentication policy doesNotExist not found.", err) + + def test_authentication_policy_delete_name_required(self): + """Test deleting an authentication policy without --name argument.""" + result, out, err = self.runcmd("domain", "auth", "policy", "delete") + self.assertEqual(result, -1) + self.assertIn("Argument --name is required.", err) + + def test_authentication_policy_delete_force_fails(self): + """Test deleting an authentication policy with --force, but it fails.""" + # Create protected authentication policy. + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name=deleteForceFail", + "--protect") + self.assertIsNone(result, msg=err) + policy = self.get_authentication_policy("deleteForceFail") + self.assertIsNotNone(policy) + + # Try delete with --force. + # Patch SDUtils.dacl_delete_aces with a Mock that raises LdbError. + with patch.object(SDUtils, "dacl_delete_aces") as delete_mock: + delete_mock.side_effect = LdbError("Custom error message") + result, out, err = self.runcmd("domain", "auth", "policy", "delete", + "--name", "deleteForceFail", + "--force") + self.assertEqual(result, -1) + self.assertIn("ERROR: Custom error message", err) + + def test_authentication_policy_delete_fails(self): + """Test deleting an authentication policy, but it fails.""" + # Create regular authentication policy. + result, out, err = self.runcmd("domain", "auth", "policy", "create", + "--name=regularPolicy") + self.assertIsNone(result, msg=err) + policy = self.get_authentication_policy("regularPolicy") + self.assertIsNotNone(policy) + + # Raise LdbError when ldb.delete() is called. + with patch.object(SamDB, "delete") as delete_mock: + delete_mock.side_effect = LdbError("Custom error message") + result, out, err = self.runcmd("domain", "auth", "policy", "delete", + "--name", "regularPolicy") + self.assertEqual(result, -1) + self.assertIn("ERROR: Custom error message", err) + + # When not using --force we get a hint. + self.assertIn("Try --force", err) + + def test_authentication_policy_delete_protected_fails(self): + """Test deleting an authentication policy, but it fails.""" + # Create protected authentication |
