summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorRob van der Linde <rob@catalyst.net.nz>2023-05-16 12:15:06 +1200
committerAndrew Bartlett <abartlet@samba.org>2023-06-25 23:29:32 +0000
commit3df634e7527c2e0f9c71d62afc7a48300b7bd388 (patch)
tree15c5ac249a7a3dfe4f5b6c85f65fa81379abc778 /python
parent3a0160ae94301c9931ee25eb7a87cf77cd588f33 (diff)
downloadsamba-3df634e7527c2e0f9c71d62afc7a48300b7bd388.tar.gz
samba-3df634e7527c2e0f9c71d62afc7a48300b7bd388.tar.bz2
samba-3df634e7527c2e0f9c71d62afc7a48300b7bd388.zip
netcmd: domain: add authentication silo commands
Authentication policies: * samba-tool domain auth policy list * samba-tool domain auth policy view * samba-tool domain auth policy create * samba-tool domain auth policy modify * samba-tool domain auth policy delete Authentication silos: * samba-tool domain auth silo list * samba-tool domain auth silo view * samba-tool domain auth silo create * samba-tool domain auth silo modify * samba-tool domain auth silo delete Authentication silo members: * samba-tool domain auth silo member list * samba-tool domain auth silo member add * samba-tool domain auth silo member remove 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')
-rw-r--r--python/samba/netcmd/domain/__init__.py2
-rw-r--r--python/samba/netcmd/domain/auth/__init__.py35
-rw-r--r--python/samba/netcmd/domain/auth/base.py61
-rw-r--r--python/samba/netcmd/domain/auth/policy.py396
-rw-r--r--python/samba/netcmd/domain/auth/silo.py367
-rw-r--r--python/samba/netcmd/domain/auth/silo_member.py216
6 files changed, 1077 insertions, 0 deletions
diff --git a/python/samba/netcmd/domain/__init__.py b/python/samba/netcmd/domain/__init__.py
index 3defc120be7..1c527f1baec 100644
--- a/python/samba/netcmd/domain/__init__.py
+++ b/python/samba/netcmd/domain/__init__.py
@@ -25,6 +25,7 @@
from samba import is_ad_dc_built
from samba.netcmd import SuperCommand
+from .auth import cmd_domain_auth
from .backup import cmd_domain_backup
from .claim import cmd_domain_claim
from .classicupgrade import cmd_domain_classicupgrade
@@ -56,6 +57,7 @@ class cmd_domain(SuperCommand):
subcommands["join"] = cmd_domain_join()
subcommands["leave"] = cmd_domain_leave()
subcommands["claim"] = cmd_domain_claim()
+ subcommands["auth"] = cmd_domain_auth()
if is_ad_dc_built():
subcommands["demote"] = cmd_domain_demote()
subcommands["provision"] = cmd_domain_provision()
diff --git a/python/samba/netcmd/domain/auth/__init__.py b/python/samba/netcmd/domain/auth/__init__.py
new file mode 100644
index 00000000000..fd74f3e92be
--- /dev/null
+++ b/python/samba/netcmd/domain/auth/__init__.py
@@ -0,0 +1,35 @@
+# Unix SMB/CIFS implementation.
+#
+# authentication silos
+#
+# 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/>.
+#
+
+from samba.netcmd import SuperCommand
+
+from .policy import cmd_domain_auth_policy
+from .silo import cmd_domain_auth_silo
+
+
+class cmd_domain_auth(SuperCommand):
+ """Manage authentication silos and policies on the domain."""
+
+ subcommands = {
+ "policy": cmd_domain_auth_policy(),
+ "silo": cmd_domain_auth_silo(),
+ }
diff --git a/python/samba/netcmd/domain/auth/base.py b/python/samba/netcmd/domain/auth/base.py
new file mode 100644
index 00000000000..6734751e6ad
--- /dev/null
+++ b/python/samba/netcmd/domain/auth/base.py
@@ -0,0 +1,61 @@
+# Unix SMB/CIFS implementation.
+#
+# authentication silos - base class and common code
+#
+# 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 samba.auth import system_session
+from samba.netcmd import Command, CommandError
+from samba.netcmd.encoders import JSONEncoder
+from samba.netcmd.domain.models import AuthenticationPolicy
+from samba.samdb import SamDB
+
+
+class SiloCommand(Command):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.ldb = None
+
+ def ldb_connect(self, ldap_url, sambaopts, credopts):
+ """Helper to connect to Ldb database using command line opts."""
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp)
+ return SamDB(ldap_url, credentials=creds,
+ session_info=system_session(lp), lp=lp)
+
+ def print_json(self, data):
+ """Print json on the screen using consistent formatting and sorting.
+
+ A custom JSONEncoder class is used to help with serializing unknown
+ objects such as Dn for example.
+ """
+ json.dump(data, self.outf, cls=JSONEncoder, indent=2, sort_keys=True)
+ self.outf.write("\n")
+
+ def get_policy(self, name):
+ """Helper function to return auth policy or raise CommandError.
+
+ :raises CommandError: if the policy was not found.
+ """
+ policy = AuthenticationPolicy.get(self.ldb, cn=name)
+ if policy is None:
+ raise CommandError(f"Authentication policy {name} not found.")
+ return policy
diff --git a/python/samba/netcmd/domain/auth/policy.py b/python/samba/netcmd/domain/auth/policy.py
new file mode 100644
index 00000000000..d132b0df948
--- /dev/null
+++ b/python/samba/netcmd/domain/auth/policy.py
@@ -0,0 +1,396 @@
+# Unix SMB/CIFS implementation.
+#
+# authentication silos - authentication policy management
+#
+# 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 samba.getopt as options
+from ldb import LdbError
+from samba.netcmd import CommandError, Option, SuperCommand
+from samba.netcmd.domain.models import AuthenticationPolicy
+from samba.netcmd.domain.models.auth_policy import MIN_TGT_LIFETIME,\
+ MAX_TGT_LIFETIME, StrongNTLMPolicy
+from samba.netcmd.validators import Range
+
+from .base import SiloCommand
+
+
+class cmd_domain_auth_policy_list(SiloCommand):
+ """List authentication policies on the domain."""
+
+ synopsis = "%prog -H <URL> [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server.",
+ type=str, metavar="URL", dest="ldap_url"),
+ Option("--json", help="Output results in JSON format.",
+ dest="output_format", action="store_const", const="json"),
+ ]
+
+ def run(self, ldap_url=None, sambaopts=None, credopts=None,
+ output_format=None):
+
+ self.ldb = self.ldb_connect(ldap_url, sambaopts, credopts)
+
+ # Authentication policies grouped by cn.
+ policies = {policy.cn: policy.as_dict()
+ for policy in AuthenticationPolicy.query(self.ldb)}
+
+ # Using json output format gives more detail.
+ if output_format == "json":
+ self.print_json(policies)
+ else:
+ for policy in policies.keys():
+ self.outf.write(f"{policy}\n")
+
+
+class cmd_domain_auth_policy_view(SiloCommand):
+ """View an authentication policy on the domain."""
+
+ synopsis = "%prog -H <URL> [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server.",
+ type=str, metavar="URL", dest="ldap_url"),
+ Option("--name",
+ help="Name of authentication policy to view (required).",
+ dest="name", action="store", type=str),
+ ]
+
+ def run(self, ldap_url=None, sambaopts=None, credopts=None, name=None):
+
+ if not name:
+ raise CommandError("Argument --name is required.")
+
+ self.ldb = self.ldb_connect(ldap_url, sambaopts, credopts)
+
+ # Check if authentication policy exists first.
+ policy = AuthenticationPolicy.get(self.ldb, cn=name)
+ if policy is None:
+ raise CommandError(f"Authentication policy {name} not found.")
+
+ # Display policy as JSON.
+ self.print_json(policy.as_dict())
+
+
+class cmd_domain_auth_policy_create(SiloCommand):
+ """Create an authentication policy on the domain."""
+
+ synopsis = "%prog -H <URL> [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server.",
+ type=str, metavar="URL", dest="ldap_url"),
+ Option("--name", help="Name of authentication policy (required).",
+ dest="name", action="store", type=str),
+ Option("--description",
+ help="Optional description for authentication policy.",
+ dest="description", action="store", type=str),
+ Option("--protect",
+ help="Protect authentication silo from accidental deletion.",
+ dest="protect", action="store_true"),
+ Option("--unprotect",
+ help="Unprotect authentication silo from accidental deletion.",
+ dest="unprotect", action="store_true"),
+ Option("--audit",
+ help="Only audit authentication policy.",
+ dest="audit", action="store_true"),
+ Option("--enforce",
+ help="Enforce authentication policy.",
+ dest="enforce", action="store_true"),
+ Option("--strong-ntlm-policy",
+ help=f"Strong NTLM Policy ({StrongNTLMPolicy.choices_str()}).",
+ dest="strong_ntlm_policy", type="choice", action="store",
+ choices=StrongNTLMPolicy.get_choices(),
+ default="Disabled"),
+ Option("--user-tgt-lifetime",
+ help="Ticket-Granting-Ticket lifetime for user accounts.",
+ dest="user_tgt_lifetime", type=int, action="store",
+ validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]),
+ Option("--user-allow-ntlm-auth",
+ help="Allow NTLM network authentication when user "
+ "is restricted to selected devices.",
+ dest="user_allow_ntlm_auth", action="store_true",
+ default=False),
+ Option("--service-tgt-lifetime",
+ help="Ticket-Granting-Ticket lifetime for service accounts.",
+ dest="service_tgt_lifetime", type=int, action="store",
+ validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]),
+ Option("--service-allow-ntlm-auth",
+ help="Allow NTLM network authentication when service "
+ "is restricted to selected devices.",
+ dest="service_allow_ntlm_auth", action="store_true",
+ default=False),
+ Option("--computer-tgt-lifetime",
+ help="Ticket-Granting-Ticket lifetime for computer accounts.",
+ dest="computer_tgt_lifetime", type=int, action="store",
+ validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]),
+ ]
+
+ def run(self, ldap_url=None, sambaopts=None, credopts=None, name=None,
+ description=None, protect=None, unprotect=None, audit=None,
+ enforce=None, strong_ntlm_policy=None, user_tgt_lifetime=None,
+ user_allow_ntlm_auth=None, service_tgt_lifetime=None,
+ service_allow_ntlm_auth=None, computer_tgt_lifetime=None):
+
+ if not name:
+ raise CommandError("Argument --name is required.")
+ if protect and unprotect:
+ raise CommandError("--protect and --unprotect cannot be used together.")
+ if audit and enforce:
+ raise CommandError("--audit and --enforce cannot be used together.")
+
+ self.ldb = self.ldb_connect(ldap_url, sambaopts, credopts)
+
+ # Make sure authentication policy doesn't already exist.
+ policy = AuthenticationPolicy.get(self.ldb, cn=name)
+ if policy is not None:
+ raise CommandError(f"Authentication policy {name} already exists.")
+
+ # New policy object.
+ policy = AuthenticationPolicy(
+ cn=name,
+ description=description,
+ strong_ntlm_policy=StrongNTLMPolicy[strong_ntlm_policy.upper()],
+ user_allow_ntlm_auth=user_allow_ntlm_auth,
+ user_tgt_lifetime=user_tgt_lifetime,
+ service_allow_ntlm_auth=service_allow_ntlm_auth,
+ service_tgt_lifetime=service_tgt_lifetime,
+ computer_tgt_lifetime=computer_tgt_lifetime,
+ )
+
+ # Either --enforce will be set or --audit but never both.
+ # The default if both are missing is enforce=True.
+ if enforce is not None:
+ policy.enforced = enforce
+ else:
+ policy.enforced = not audit
+
+ # Create policy.
+ try:
+ policy.save(self.ldb)
+
+ if protect:
+ policy.protect(self.ldb)
+ except LdbError as e:
+ raise CommandError(e)
+
+ # Authentication policy created successfully.
+ self.outf.write(f"Created authentication policy: {name}\n")
+
+
+class cmd_domain_auth_policy_modify(SiloCommand):
+ """Modify authentication policies on the domain."""
+
+ synopsis = "%prog -H <URL> [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server.",
+ type=str, metavar="URL", dest="ldap_url"),
+ Option("--name", help="Name of authentication policy (required).",
+ dest="name", action="store", type=str),
+ Option("--description",
+ help="Optional description for authentication policy.",
+ dest="description", action="store", type=str),
+ Option("--protect",
+ help="Protect authentication silo from accidental deletion.",
+ dest="protect", action="store_true"),
+ Option("--unprotect",
+ help="Unprotect authentication silo from accidental deletion.",
+ dest="unprotect", action="store_true"),
+ Option("--audit",
+ help="Only audit authentication policy.",
+ dest="audit", action="store_true"),
+ Option("--enforce",
+ help="Enforce authentication policy.",
+ dest="enforce", action="store_true"),
+ Option("--strong-ntlm-policy",
+ help=f"Strong NTLM Policy ({StrongNTLMPolicy.choices_str()}).",
+ dest="strong_ntlm_policy", type="choice", action="store",
+ choices=StrongNTLMPolicy.get_choices()),
+ Option("--user-tgt-lifetime",
+ help="Ticket-Granting-Ticket lifetime for user accounts.",
+ dest="user_tgt_lifetime", type=int, action="store",
+ validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]),
+ Option("--user-allow-ntlm-auth",
+ help="Allow NTLM network authentication when user "
+ "is restricted to selected devices.",
+ dest="user_allow_ntlm_auth", action="store_true",
+ default=False),
+ Option("--service-tgt-lifetime",
+ help="Ticket-Granting-Ticket lifetime for service accounts.",
+ dest="service_tgt_lifetime", type=int, action="store",
+ validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]),
+ Option("--service-allow-ntlm-auth",
+ help="Allow NTLM network authentication when service "
+ "is restricted to selected devices.",
+ dest="service_allow_ntlm_auth", action="store_true",
+ default=False),
+ Option("--computer-tgt-lifetime",
+ help="Ticket-Granting-Ticket lifetime for computer accounts.",
+ dest="computer_tgt_lifetime", type=int, action="store",
+ validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]),
+ ]
+
+ def run(self, ldap_url=None, sambaopts=None, credopts=None, name=None,
+ description=None, protect=None, unprotect=None, audit=None,
+ enforce=None, strong_ntlm_policy=None, user_tgt_lifetime=None,
+ user_allow_ntlm_auth=None, service_tgt_lifetime=None,
+ service_allow_ntlm_auth=None, computer_tgt_lifetime=None):
+
+ if not name:
+ raise CommandError("Argument --name is required.")
+ if protect and unprotect:
+ raise CommandError("--protect and --unprotect cannot be used together.")
+ if audit and enforce:
+ raise CommandError("--audit and --enforce cannot be used together.")
+
+ self.ldb = self.ldb_connect(ldap_url, sambaopts, credopts)
+
+ # Check if authentication policy exists.
+ policy = AuthenticationPolicy.get(self.ldb, cn=name)
+ if policy is None:
+ raise CommandError(f"Authentication policy {name} not found.")
+
+ # Either --enforce will be set or --audit but never both.
+ if enforce:
+ policy.enforced = True
+ elif audit:
+ policy.enforced = False
+
+ # Update the description.
+ if description is not None:
+ policy.description = description
+
+ # User sign on
+ ###############
+
+ if strong_ntlm_policy is not None:
+ policy.strong_ntlm_policy = \
+ StrongNTLMPolicy[strong_ntlm_policy.upper()]
+
+ if user_tgt_lifetime is not None:
+ policy.user_tgt_lifetime = user_tgt_lifetime
+
+ # Service sign on
+ ##################
+
+ if service_tgt_lifetime is not None:
+ policy.service_tgt_lifetime = service_tgt_lifetime
+
+ # Computer
+ ###########
+
+ if computer_tgt_lifetime is not None:
+ policy.computer_tgt_lifetime = computer_tgt_lifetime
+
+ # Update policy.
+ try:
+ policy.save(self.ldb)
+
+ if protect:
+ policy.protect(self.ldb)
+ elif unprotect:
+ policy.unprotect(self.ldb)
+ except LdbError as e:
+ raise CommandError(e)
+
+ # Authentication policy updated successfully.
+ self.outf.write(f"Updated authentication policy: {name}\n")
+
+
+class cmd_domain_auth_policy_delete(SiloCommand):
+ """Delete authentication policies on the domain."""
+
+ synopsis = "%prog -H <URL> [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server.",
+ type=str, metavar="URL", dest="ldap_url"),
+ Option("--name", help="Name of authentication policy (required).",
+ dest="name", action="store", type=str),
+ Option("--force", help="Force delete protected authentication policy.",
+ dest="force", action="store_true")
+ ]
+
+ def run(self, ldap_url=None, sambaopts=None, credopts=None, name=None,
+ force=None):
+
+ if not name:
+ raise CommandError("Argument --name is required.")
+
+ self.ldb = self.ldb_connect(ldap_url, sambaopts, credopts)
+
+ # Check if authentication policy exists first.
+ policy = AuthenticationPolicy.get(self.ldb, cn=name)
+ if policy is None:
+ raise CommandError(f"Authentication policy {name} not found.")
+
+ # Delete item, --force removes delete protection first.
+ try:
+ if force:
+ policy.unprotect(self.ldb)
+
+ policy.delete(self.ldb)
+ except LdbError as e:
+ if not force:
+ raise CommandError(
+ f"{e}\nTry --force to delete protected authentication policies.")
+ else:
+ raise CommandError(e)
+
+ # Authentication policy deleted successfully.
+ self.outf.write(f"Deleted authentication policy: {name}\n")
+
+
+class cmd_domain_auth_policy(SuperCommand):
+ """Manage authentication policies on the domain."""
+
+ subcommands = {
+ "list": cmd_domain_auth_policy_list(),
+ "view": cmd_domain_auth_policy_view(),
+ "create": cmd_domain_auth_policy_create(),
+ "modify": cmd_domain_auth_policy_modify(),
+ "delete": cmd_domain_auth_policy_delete(),
+ }
diff --git a/python/samba/netcmd/domain/auth/silo.py b/python/samba/netcmd/domain/auth/silo.py
new file mode 100644
index 00000000000..a7017e1dfe1
--- /dev/null
+++ b/python/samba/netcmd/domain/auth/silo.py
@@ -0,0 +1,367 @@
+# Unix SMB/CIFS implementation.
+#
+# authentication silos - authentication silo management
+#
+# 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 samba.getopt as options
+from ldb import LdbError
+from samba.netcmd import CommandError, Option, SuperCommand
+from samba.netcmd.domain.models import AuthenticationSilo
+
+from .base import SiloCommand
+from .silo_member import cmd_domain_auth_silo_member
+
+
+class cmd_domain_auth_silo_list(SiloCommand):
+ """List authentication silos on the domain."""
+
+ synopsis = "%prog -H <URL> [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server.",
+ type=str, metavar="URL", dest="ldap_url"),
+ Option("--json", help="Output results in JSON format.",
+ dest="output_format", action="store_const", const="json"),
+ ]
+
+ def run(self, ldap_url=None, sambaopts=None, credopts=None,
+ output_format=None):
+
+ self.ldb = self.ldb_connect(ldap_url, sambaopts, credopts)
+
+ # Authentication silos grouped by cn.
+ silos = {silo.cn: silo.as_dict()
+ for silo in AuthenticationSilo.query(self.ldb)}
+
+ # Using json output format gives more detail.
+ if output_format == "json":
+ self.print_json(silos)
+ else:
+ for silo in silos.keys():
+ self.outf.write(f"{silo}\n")
+
+
+class cmd_domain_auth_silo_view(SiloCommand):
+ """View an authentication silo on the domain."""
+
+ synopsis = "%prog -H <URL> [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server.",
+ type=str, metavar="URL", dest="ldap_url"),
+ Option("--name",
+ help="Name of authentication silo to view (required).",
+ dest="name", action="store", type=str),
+ ]
+
+ def run(self, ldap_url=None, sambaopts=None, credopts=None, name=None):
+
+ if not name:
+ raise CommandError("Argument --name is required.")
+
+ self.ldb = self.ldb_connect(ldap_url, sambaopts, credopts)
+
+ # Check if silo exists first.
+ silo = AuthenticationSilo.get(self.ldb, cn=name)
+ if silo is None:
+ raise CommandError(f"Authentication silo {name} not found.")
+
+ # Display silo as JSON.
+ self.print_json(silo.as_dict())
+
+
+class cmd_domain_auth_silo_create(SiloCommand):
+ """Create a new authentication silo on the domain."""
+
+ synopsis = "%prog -H <URL> [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server.",
+ type=str, metavar="URL", dest="ldap_url"),
+ Option("--name", help="Name of authentication silo (required).",
+ dest="name", action="store", type=str),
+ Option("--description",
+ help="Optional description for authentication silo.",
+ dest="description", action="store", type=str),
+ Option("--policy",
+ help="Use single policy for all principals in this silo.",
+ dest="policy", action="store", type=str),
+ Option("--user-policy",
+ help="User account policy.",
+ dest="user_policy", action="store", type=str),
+ Option("--service-policy",
+ help="Managed Service Account policy.",
+ dest="service_policy", action="store", type=str),
+ Option("--computer-policy",
+ help="Computer account policy.",
+ dest="computer_policy", action="store", type=str),
+ Option("--protect",
+ help="Protect authentication silo from accidental deletion.",
+ dest="protect", action="store_true"),
+ Option("--unprotect",
+ help="Unprotect authentication silo from accidental deletion.",
+ dest="unprotect", action="store_true"),
+ Option("--audit",
+ help="Only audit silo policies.",
+ dest="audit", action="store_true"),
+ Option("--enforce",
+ help="Enforce silo policies.",
+ dest="enforce", action="store_true")
+ ]
+
+ def run(self, ldap_url=None, sambaopts=None, credopts=None, name=None,
+ description=None, policy=None, user_policy=None,
+ service_policy=None, computer_policy=None, protect=None,
+ unprotect=None, audit=None, enforce=None):
+
+ if not name:
+ raise CommandError("Argument --name is required.")
+ if protect and unprotect:
+ raise CommandError("--protect and --unprotect cannot be used together.")
+ if audit and enforce:
+ raise CommandError("--audit and --enforce cannot be used together.")
+
+ # If --policy is present start with that as the base. Then optionally
+ # --user-policy, --service-policy, --computer-policy can override this.
+ if policy is not None:
+ user_policy = user_policy or policy
+ service_policy = service_policy or policy
+ computer_policy = computer_policy or policy
+
+ self.ldb = self.ldb_connect(ldap_url, sambaopts, credopts)
+
+ # Make sure silo doesn't already exist.
+ silo = AuthenticationSilo.get(self.ldb, cn=name)
+ if silo is not None:
+ raise CommandError(f"Authentication silo {name} already exists.")
+
+ # New silo object.
+ silo = AuthenticationSilo(cn=name, description=description)
+
+ # Set user policy
+ if user_policy:
+ silo.user_policy = self.get_policy(user_policy).dn
+
+ # Set service policy
+ if service_policy:
+ silo.service_policy = self.get_policy(service_policy).dn
+
+ # Set computer policy
+ if computer_policy:
+ silo.computer_policy = self.get_policy(computer_policy).dn
+
+ # Either --enforce will be set or --audit but never both.
+ # The default if both are missing is enforce=True.
+ if enforce is not None:
+ silo.enforced = enforce
+ else:
+ silo.enforced = not audit
+
+ # Create silo
+ try:
+ silo.save(self.ldb)
+
+ if protect:
+ silo.protect(self.ldb)
+ except LdbError as e:
+ raise CommandError(e)
+
+ # Authentication silo created successfully.
+ self.outf.write(f"Created authentication silo: {name}\n")
+
+
+class cmd_domain_auth_silo_modify(SiloCommand):
+ """Modify an authentication silo on the domain."""
+
+ synopsis = "%prog -H <URL> [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server.",
+ type=str, metavar="URL", dest="ldap_url"),
+ Option("--name", help="Name of authentication silo (required).",
+ dest="name", action="store", type=str),
+ Option("--description",
+ help="Optional description for authentication silo.",
+ dest="description", action="store", type=str),
+ Option("--policy",
+ help="Set single policy for all principals in this silo.",
+ dest="policy", action="store", type=str),
+ Option("--user-policy",
+ help="Set User account policy.",
+ dest="user_policy", action="store", type=str),
+ Option("--service-policy",
+ help="Set Managed Service Account policy.",
+ dest="service_policy", action="store", type=str),
+ Option("--computer-policy",
+ help="Set Computer Account policy.",
+ dest="computer_policy", action="store", type=str),
+ Option("--protect",
+ help="Protect authentication silo from accidental deletion.",
+ dest="protect", action="store_true"),
+ Option("--unprotect",
+ help="Unprotect authentication silo from accidental deletion.",
+ dest="unprotect", action="store_true"),
+ Option("--audit",
+ help="Only audit silo policies.",
+ dest="audit", action="store_true"),
+ Option("--enforce",
+ help="Enforce silo policies.",
+ dest="enforce", action="store_true")
+ ]
+
+ def run(self, ldap_url=None, sambaopts=None, credopts=None, name=None,
+ description=None, policy=None, user_policy=None,
+ service_policy=None, computer_policy=None, protect=None,
+ unprotect=None, audit=None, enforce=None):
+
+ if not name:
+ raise CommandError("Argument --name is required.")
+ if audit and enforce:
+ raise CommandError("--audit and --enforce cannot be used together.")
+ if protect and unprotect:
+ raise CommandError("--protect and --unprotect cannot be used together.")
+
+ # If --policy is set then start with that for all policies.
+ # They can be individually overridden as well after that.
+ if policy is not None:
+ user_policy = user_policy or policy
+ service_policy = service_policy or policy
+ computer_policy = computer_policy or policy
+
+ self.ldb = self.ldb_connect(ldap_url, sambaopts, credopts)
+
+ # Check if silo exists first.
+ silo = AuthenticationSilo.get(self.ldb, cn=name)
+ if silo is None:
+ raise CommandError(f"Authentication silo {name} not found.")
+
+ # Either --enforce will be set or --audit but never both.
+ if enforce:
+ silo.enforced = True
+ elif audit:
+ silo.enforced = False
+
+ # Update the description.
+ if description is not None:
+ silo.description = description
+
+ # Silo policies.
+ if user_policy is not None:
+ silo.user_policy = self.get_policy(user_policy).dn
+ if service_policy is not None:
+ silo.service_policy = self.get_policy(service_policy).dn
+ if computer_policy is not None:
+ silo.computer_policy = self.get_policy(computer_policy).dn
+
+ # Update silo
+ try:
+ silo.save(self.ldb)
+
+ if protect:
+ silo.protect(self.ldb)
+ elif unprotect:
+ silo.unprotect(self.ldb)
+ except LdbError as e:
+ raise CommandError(e)
+
+ # Silo updated successfully.
+ self.outf.write(f"Updated authentication silo: {name}\n")
+
+
+class cmd_domain_auth_silo_delete(SiloCommand):
+ """Delete an authentication silo on the domain."""
+
+ synopsis = "%prog -H <URL> [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server.",
+ type=str, metavar="URL", dest="ldap_url"),
+ Option("--name", help="Name of authentication silo (required).",