1
0
mirror of https://github.com/samba-team/samba.git synced 2025-02-04 17:47:26 +03:00

python: silos: add support for allowed to authenticate from silo shortcut

this avoids the need to write SDDL, the user just needs to give the silo name

Signed-off-by: Rob van der Linde <rob@catalyst.net.nz>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>

Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Fri Oct 27 00:30:05 UTC 2023 on atb-devel-224
This commit is contained in:
Rob van der Linde 2023-10-12 17:08:34 +13:00 committed by Andrew Bartlett
parent 8491693575
commit 0bb67a3a7e
3 changed files with 194 additions and 1 deletions

View File

@ -22,13 +22,24 @@
import samba.getopt as options
from samba.netcmd import Command, CommandError, Option, SuperCommand
from samba.netcmd.domain.models import AuthenticationPolicy
from samba.netcmd.domain.models import AuthenticationPolicy, AuthenticationSilo
from samba.netcmd.domain.models.auth_policy import MIN_TGT_LIFETIME,\
MAX_TGT_LIFETIME, StrongNTLMPolicy
from samba.netcmd.domain.models.exceptions import ModelError
from samba.netcmd.validators import Range
def check_similar_args(option, args):
"""Helper method for checking similar mutually exclusive args.
Example: --user-allowed-to-authenticate-from and
--user-allowed-to-authenticate-from-silo
"""
num = sum(arg is not None for arg in args)
if num > 1:
raise CommandError(f"{option} argument repeated {num} times.")
class UserOptions(options.OptionGroup):
"""User options used by policy create and policy modify commands."""
@ -49,6 +60,10 @@ class UserOptions(options.OptionGroup):
help="Conditions user is allowed to authenticate from.",
type=str, dest="allowed_to_authenticate_from",
action="callback", callback=self.set_option)
self.add_option("--user-allowed-to-authenticate-from-silo",
help="User is allowed to authenticate from silo.",
type=str, dest="allowed_to_authenticate_from_silo",
action="callback", callback=self.set_option)
self.add_option("--user-allowed-to-authenticate-to",
help="Conditions user is allowed to authenticate to.",
type=str, dest="allowed_to_authenticate_to",
@ -75,6 +90,10 @@ class ServiceOptions(options.OptionGroup):
help="Conditions service is allowed to authenticate from.",
type=str, dest="allowed_to_authenticate_from",
action="callback", callback=self.set_option)
self.add_option("--service-allowed-to-authenticate-from-silo",
help="Service is allowed to authenticate from silo.",
type=str, dest="allowed_to_authenticate_from_silo",
action="callback", callback=self.set_option)
self.add_option("--service-allowed-to-authenticate-to",
help="Conditions service is allowed to authenticate to.",
type=str, dest="allowed_to_authenticate_to",
@ -217,8 +236,28 @@ class cmd_domain_auth_policy_create(Command):
if audit and enforce:
raise CommandError("--audit and --enforce cannot be used together.")
# Check for repeated, similar arguments.
check_similar_args("--user-allowed-to-authenticate-from",
[useropts.allowed_to_authenticate_from,
useropts.allowed_to_authenticate_from_silo])
check_similar_args("--service-allowed-to-authenticate-from",
[serviceopts.allowed_to_authenticate_from,
serviceopts.allowed_to_authenticate_from_silo])
ldb = self.ldb_connect(hostopts, sambaopts, credopts)
# Generate SDDL for authenticating users from a silo
if useropts.allowed_to_authenticate_from_silo:
silo = AuthenticationSilo.get(
ldb, cn=useropts.allowed_to_authenticate_from_silo)
useropts.allowed_to_authenticate_from = silo.get_authentication_sddl()
# Generate SDDL for authenticating service accounts from a silo
if serviceopts.allowed_to_authenticate_from_silo:
silo = AuthenticationSilo.get(
ldb, cn=serviceopts.allowed_to_authenticate_from_silo)
serviceopts.allowed_to_authenticate_from = silo.get_authentication_sddl()
try:
policy = AuthenticationPolicy.get(ldb, cn=name)
except ModelError as e:
@ -313,8 +352,28 @@ class cmd_domain_auth_policy_modify(Command):
if audit and enforce:
raise CommandError("--audit and --enforce cannot be used together.")
# Check for repeated, similar arguments.
check_similar_args("--user-allowed-to-authenticate-from",
[useropts.allowed_to_authenticate_from,
useropts.allowed_to_authenticate_from_silo])
check_similar_args("--service-allowed-to-authenticate-from",
[serviceopts.allowed_to_authenticate_from,
serviceopts.allowed_to_authenticate_from_silo])
ldb = self.ldb_connect(hostopts, sambaopts, credopts)
# Generate SDDL for authenticating users from a silo
if useropts.allowed_to_authenticate_from_silo:
silo = AuthenticationSilo.get(
ldb, cn=useropts.allowed_to_authenticate_from_silo)
useropts.allowed_to_authenticate_from = silo.get_authentication_sddl()
# Generate SDDL for authenticating service accounts from a silo
if serviceopts.allowed_to_authenticate_from_silo:
silo = AuthenticationSilo.get(
ldb, cn=serviceopts.allowed_to_authenticate_from_silo)
serviceopts.allowed_to_authenticate_from = silo.get_authentication_sddl()
try:
policy = AuthenticationPolicy.get(ldb, cn=name)
except ModelError as e:

View File

@ -22,6 +22,8 @@
from ldb import FLAG_MOD_ADD, FLAG_MOD_DELETE, LdbError, Message, MessageElement
from samba.sd_utils import escaped_claim_id
from .exceptions import AddMemberError, RemoveMemberError
from .fields import DnField, BooleanField, StringField
from .model import Model
@ -96,3 +98,7 @@ class AuthenticationSilo(Model):
# If the modify operation was successful refresh members field.
self.refresh(ldb, fields=["members"])
def get_authentication_sddl(self):
return ("O:SYG:SYD:(XA;OICI;CR;;;WD;(@USER.ad://ext/"
f"AuthenticationSilo/{escaped_claim_id(self.name)}))")

View File

@ -153,6 +153,28 @@ class AuthPolicyCmdTestCase(BaseAuthCmdTest):
self.assertIn("--user-tgt-lifetime-mins must be between 45 and 2147483647",
err)
def test_create__user_allowed_to_authenticate_from_silo(self):
"""Tests the --user-allowed-to-authenticate-from-silo shortcut."""
name = self.unique_name()
self.addCleanup(self.delete_authentication_policy, name=name, force=True)
result, out, err = self.runcmd("domain", "auth", "policy", "create",
"--name", name,
"--user-allowed-to-authenticate-from-silo",
"Developers")
self.assertIsNone(result, msg=err)
# Check policy fields.
policy = self.get_authentication_policy(name)
self.assertEqual(str(policy["cn"]), name)
# Check generated SDDL.
desc = policy["msDS-UserAllowedToAuthenticateFrom"][0]
sddl = ndr_unpack(security.descriptor, desc).as_sddl()
self.assertEqual(
sddl,
"O:SYG:SYD:(XA;OICI;CR;;;WD;(@USER.ad://ext/AuthenticationSilo/Developers))")
def test_create__service_tgt_lifetime_mins(self):
"""Test create a new authentication policy with --service-tgt-lifetime-mins.
@ -187,6 +209,28 @@ class AuthPolicyCmdTestCase(BaseAuthCmdTest):
self.assertIn("--service-tgt-lifetime-mins must be between 45 and 2147483647",
err)
def test_create__service_allowed_to_authenticate_from_silo(self):
"""Tests the --service-allowed-to-authenticate-from-silo shortcut."""
name = self.unique_name()
self.addCleanup(self.delete_authentication_policy, name=name, force=True)
result, out, err = self.runcmd("domain", "auth", "policy", "create",
"--name", name,
"--service-allowed-to-authenticate-from-silo",
"Managers")
self.assertIsNone(result, msg=err)
# Check policy fields.
policy = self.get_authentication_policy(name)
self.assertEqual(str(policy["cn"]), name)
desc = policy["msDS-ServiceAllowedToAuthenticateFrom"][0]
# Check generated SDDL.
sddl = ndr_unpack(security.descriptor, desc).as_sddl()
self.assertEqual(
sddl,
"O:SYG:SYD:(XA;OICI;CR;;;WD;(@USER.ad://ext/AuthenticationSilo/Managers))")
def test_create__computer_tgt_lifetime_mins(self):
"""Test create a new authentication policy with --computer-tgt-lifetime-mins.
@ -316,6 +360,44 @@ class AuthPolicyCmdTestCase(BaseAuthCmdTest):
self.assertEqual(result, -1)
self.assertIn("--protect and --unprotect cannot be used together.", err)
def test_create__user_allowed_to_authenticate_from_repeated(self):
"""Test repeating similar arguments doesn't make sense to use together.
--user-allowed-to-authenticate-from
--user-allowed-to-authenticate-from-silo
"""
sddl = "O:SYG:SYD:(XA;OICI;CR;;;WD;(@USER.ad://ext/AuthenticationSilo/Developers))"
name = self.unique_name()
result, out, err = self.runcmd("domain", "auth", "policy", "create",
"--name", name,
"--user-allowed-to-authenticate-from",
sddl,
"--user-allowed-to-authenticate-from-silo",
"Managers")
self.assertEqual(result, -1)
self.assertIn("--user-allowed-to-authenticate-from argument repeated 2 times.", err)
def test_create__service_allowed_to_authenticate_from_repeated(self):
"""Test repeating similar arguments doesn't make sense to use together.
--service-allowed-to-authenticate-from
--service-allowed-to-authenticate-from-silo
"""
sddl = "O:SYG:SYD:(XA;OICI;CR;;;WD;(@USER.ad://ext/AuthenticationSilo/Managers))"
name = self.unique_name()
result, out, err = self.runcmd("domain", "auth", "policy", "create",
"--name", name,
"--service-allowed-to-authenticate-from",
sddl,
"--service-allowed-to-authenticate-from-silo",
"QA")
self.assertEqual(result, -1)
self.assertIn("--service-allowed-to-authenticate-from argument repeated 2 times.", err)
def test_create__fails(self):
"""Test creating an authentication policy, but it fails."""
name = self.unique_name()
@ -504,6 +586,29 @@ class AuthPolicyCmdTestCase(BaseAuthCmdTest):
sddl = ndr_unpack(security.descriptor, desc).as_sddl()
self.assertEqual(sddl, expected)
def test_modify__user_allowed_to_authenticate_from_silo(self):
"""Test the --user-allowed-to-authenticate-from-silo shortcut."""
name = self.unique_name()
# Create a policy to modify for this test.
self.addCleanup(self.delete_authentication_policy, name=name, force=True)
self.runcmd("domain", "auth", "policy", "create", "--name", name)
# Modify user allowed to authenticate from silo field
result, out, err = self.runcmd("domain", "auth", "policy", "modify",
"--name", name,
"--user-allowed-to-authenticate-from-silo",
"QA")
self.assertIsNone(result, msg=err)
# Check generated SDDL.
policy = self.get_authentication_policy(name)
desc = policy["msDS-UserAllowedToAuthenticateFrom"][0]
sddl = ndr_unpack(security.descriptor, desc).as_sddl()
self.assertEqual(
sddl,
"O:SYG:SYD:(XA;OICI;CR;;;WD;(@USER.ad://ext/AuthenticationSilo/QA))")
def test_modify__user_allowed_to_authenticate_to(self):
"""Modify authentication policy user allowed to authenticate to."""
name = self.unique_name()
@ -550,6 +655,29 @@ class AuthPolicyCmdTestCase(BaseAuthCmdTest):
sddl = ndr_unpack(security.descriptor, desc).as_sddl()
self.assertEqual(sddl, expected)
def test_modify__service_allowed_to_authenticate_from_silo(self):
"""Test the --service-allowed-to-authenticate-from-silo shortcut."""
name = self.unique_name()
# Create a policy to modify for this test.
self.addCleanup(self.delete_authentication_policy, name=name, force=True)
self.runcmd("domain", "auth", "policy", "create", "--name", name)
# Modify user allowed to authenticate from silo field
result, out, err = self.runcmd("domain", "auth", "policy", "modify",
"--name", name,
"--service-allowed-to-authenticate-from-silo",
"Developers")
self.assertIsNone(result, msg=err)
# Check generated SDDL.
policy = self.get_authentication_policy(name)
desc = policy["msDS-ServiceAllowedToAuthenticateFrom"][0]
sddl = ndr_unpack(security.descriptor, desc).as_sddl()
self.assertEqual(
sddl,
"O:SYG:SYD:(XA;OICI;CR;;;WD;(@USER.ad://ext/AuthenticationSilo/Developers))")
def test_modify__service_allowed_to_authenticate_to(self):
"""Modify authentication policy service allowed to authenticate to."""
name = self.unique_name()