From 96f00738cec72224487522c2c134862661f2b0e4 Mon Sep 17 00:00:00 2001 From: Rob van der Linde Date: Wed, 20 Mar 2024 19:20:06 +1300 Subject: [PATCH] netcmd: auth policy: extract policy base commands into policy.py Signed-off-by: Rob van der Linde Reviewed-by: Douglas Bagnall Reviewed-by: Andrew Bartlett --- .../netcmd/domain/auth/policy/__init__.py | 657 +---------------- .../samba/netcmd/domain/auth/policy/policy.py | 670 ++++++++++++++++++ 2 files changed, 679 insertions(+), 648 deletions(-) create mode 100644 python/samba/netcmd/domain/auth/policy/policy.py diff --git a/python/samba/netcmd/domain/auth/policy/__init__.py b/python/samba/netcmd/domain/auth/policy/__init__.py index 8cc6598f3fa..40ae7736be1 100644 --- a/python/samba/netcmd/domain/auth/policy/__init__.py +++ b/python/samba/netcmd/domain/auth/policy/__init__.py @@ -20,654 +20,15 @@ # along with this program. If not, see . # -import samba.getopt as options -from samba.domain.models import (MAX_TGT_LIFETIME, MIN_TGT_LIFETIME, - AuthenticationPolicy, AuthenticationSilo, - Group, StrongNTLMPolicy) -from samba.domain.models.exceptions import ModelError -from samba.netcmd import Command, CommandError, Option, SuperCommand -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-device-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.""" - - def __init__(self, parser): - super().__init__(parser, "User Options") - - self.add_option("--user-tgt-lifetime-mins", - help="Ticket-Granting-Ticket lifetime for user accounts.", - dest="tgt_lifetime", type=int, action="callback", - callback=self.set_option, - validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]) - self.add_option("--user-allow-ntlm-auth", - help="Allow NTLM network authentication despite the fact that the user " - "is restricted to selected devices.", - dest="allow_ntlm_auth", default=False, - action="callback", callback=self.set_option) - self.add_option("--user-allowed-to-authenticate-from", - help="SDDL Rules setting which device the user is allowed to authenticate from.", - type=str, dest="allowed_to_authenticate_from", - action="callback", callback=self.set_option, - metavar="SDDL") - self.add_option("--user-allowed-to-authenticate-from-device-silo", - help="To authenticate, the user must log in from a device in SILO.", - type=str, dest="allowed_to_authenticate_from_device_silo", - action="callback", callback=self.set_option, - metavar="SILO") - self.add_option("--user-allowed-to-authenticate-from-device-group", - help="To authenticate, the user must log in from a device in GROUP.", - type=str, dest="allowed_to_authenticate_from_device_group", - action="callback", callback=self.set_option, - metavar="GROUP") - self.add_option("--user-allowed-to-authenticate-to", - help="A target service, on a user account, requires the connecting user to match SDDL", - type=str, dest="allowed_to_authenticate_to", - action="callback", callback=self.set_option, - metavar="SDDL") - self.add_option("--user-allowed-to-authenticate-to-by-group", - help="A target service, on a user account, requires the connecting user to be in GROUP", - type=str, dest="allowed_to_authenticate_to_by_group", - action="callback", callback=self.set_option, - metavar="GROUP") - self.add_option("--user-allowed-to-authenticate-to-by-silo", - help="A target service, on a user account, requires the connecting user to be in SILO", - type=str, dest="allowed_to_authenticate_to_by_silo", - action="callback", callback=self.set_option, - metavar="SILO") - - -class ServiceOptions(options.OptionGroup): - """Service options used by policy create and policy modify commands.""" - - def __init__(self, parser): - super().__init__(parser, "Service Options") - - self.add_option("--service-tgt-lifetime-mins", - help="Ticket-Granting-Ticket lifetime for service accounts.", - dest="tgt_lifetime", type=int, action="callback", - callback=self.set_option, - validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]) - self.add_option("--service-allow-ntlm-auth", - help="Allow NTLM network authentication despite " - "the fact that the service account " - "is restricted to selected devices.", - dest="allow_ntlm_auth", default=False, - action="callback", callback=self.set_option) - self.add_option("--service-allowed-to-authenticate-from", - help="SDDL Rules setting which device the " - "service account is allowed to authenticate from.", - type=str, dest="allowed_to_authenticate_from", - action="callback", callback=self.set_option, - metavar="SDDL") - self.add_option("--service-allowed-to-authenticate-from-device-silo", - help="To authenticate, the service must authenticate on a device in SILO.", - type=str, dest="allowed_to_authenticate_from_device_silo", - action="callback", callback=self.set_option, - metavar="SILO") - self.add_option("--service-allowed-to-authenticate-from-device-group", - help="To authenticate, the service must authenticate on a device in GROUP.", - type=str, dest="allowed_to_authenticate_from_device_group", - action="callback", callback=self.set_option, - metavar="GROUP") - self.add_option("--service-allowed-to-authenticate-to", - help="The target service requires the connecting user to match SDDL", - type=str, dest="allowed_to_authenticate_to", - action="callback", callback=self.set_option, - metavar="SDDL") - self.add_option("--service-allowed-to-authenticate-to-by-group", - help="The target service requires the connecting user to be in GROUP", - type=str, dest="allowed_to_authenticate_to_by_group", - action="callback", callback=self.set_option, - metavar="GROUP") - self.add_option("--service-allowed-to-authenticate-to-by-silo", - help="The target service requires the connecting user to be in SILO", - type=str, dest="allowed_to_authenticate_to_by_silo", - action="callback", callback=self.set_option, - metavar="SILO") - - -class ComputerOptions(options.OptionGroup): - """Computer options used by policy create and policy modify commands.""" - - def __init__(self, parser): - super().__init__(parser, "Computer Options") - - self.add_option("--computer-tgt-lifetime-mins", - help="Ticket-Granting-Ticket lifetime for computer accounts.", - dest="tgt_lifetime", type=int, action="callback", - callback=self.set_option, - validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]) - self.add_option("--computer-allowed-to-authenticate-to", - help="The computer account (server, workstation) service requires the connecting user to match SDDL", - type=str, dest="allowed_to_authenticate_to", - action="callback", callback=self.set_option, - metavar="SDDL") - self.add_option("--computer-allowed-to-authenticate-to-by-group", - help="The computer account (server, workstation) service requires the connecting user to be in GROUP", - type=str, dest="allowed_to_authenticate_to_by_group", - action="callback", callback=self.set_option, - metavar="GROUP") - self.add_option("--computer-allowed-to-authenticate-to-by-silo", - help="The computer account (server, workstation) service requires the connecting user to be in SILO", - type=str, dest="allowed_to_authenticate_to_by_silo", - action="callback", callback=self.set_option, - metavar="SILO") - - -class cmd_domain_auth_policy_list(Command): - """List authentication policies on the domain.""" - - synopsis = "%prog -H [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "hostopts": options.HostOptions, - } - - takes_options = [ - Option("--json", help="Output results in JSON format.", - dest="output_format", action="store_const", const="json"), - ] - - def run(self, hostopts=None, sambaopts=None, credopts=None, - output_format=None): - - ldb = self.ldb_connect(hostopts, sambaopts, credopts) - - try: - policies = AuthenticationPolicy.query(ldb) - except ModelError as e: - raise CommandError(e) - - # Using json output format gives more detail. - if output_format == "json": - self.print_json({policy.name: policy for policy in policies}) - else: - for policy in policies: - print(policy.name, file=self.outf) - - -class cmd_domain_auth_policy_view(Command): - """View an authentication policy on the domain.""" - - synopsis = "%prog -H [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "hostopts": options.HostOptions, - } - - takes_options = [ - Option("--name", - help="Name of authentication policy to view (required).", - dest="name", action="store", type=str, required=True), - ] - - def run(self, hostopts=None, sambaopts=None, credopts=None, name=None): - - ldb = self.ldb_connect(hostopts, sambaopts, credopts) - - try: - policy = AuthenticationPolicy.get(ldb, cn=name) - except ModelError as e: - raise CommandError(e) - - # Check if authentication policy exists first. - 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(Command): - """Create an authentication policy on the domain.""" - - synopsis = "%prog -H [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "hostopts": options.HostOptions, - "useropts": UserOptions, - "serviceopts": ServiceOptions, - "computeropts": ComputerOptions, - } - - takes_options = [ - Option("--name", help="Name of authentication policy (required).", - dest="name", action="store", type=str, required=True), - 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"), - ] - - def run(self, hostopts=None, sambaopts=None, credopts=None, useropts=None, - serviceopts=None, computeropts=None, name=None, description=None, - protect=None, unprotect=None, audit=None, enforce=None, - strong_ntlm_policy=None): - - 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.") - - # Check for repeated, similar arguments. - check_similar_args("--user-allowed-to-authenticate-from", - [useropts.allowed_to_authenticate_from, - useropts.allowed_to_authenticate_from_device_group, - useropts.allowed_to_authenticate_from_device_silo]) - check_similar_args("--user-allowed-to-authenticate-to", - [useropts.allowed_to_authenticate_to, - useropts.allowed_to_authenticate_to_by_group, - useropts.allowed_to_authenticate_to_by_silo]) - check_similar_args("--service-allowed-to-authenticate-from", - [serviceopts.allowed_to_authenticate_from, - serviceopts.allowed_to_authenticate_from_device_group, - serviceopts.allowed_to_authenticate_from_device_silo]) - check_similar_args("--service-allowed-to-authenticate-to", - [serviceopts.allowed_to_authenticate_to, - serviceopts.allowed_to_authenticate_to_by_group, - serviceopts.allowed_to_authenticate_to_by_silo]) - check_similar_args("--computer-allowed-to-authenticate-to", - [computeropts.allowed_to_authenticate_to, - computeropts.allowed_to_authenticate_to_by_group, - computeropts.allowed_to_authenticate_to_by_silo]) - - ldb = self.ldb_connect(hostopts, sambaopts, credopts) - - # Generate SDDL for authenticating users from a device in a group - if useropts.allowed_to_authenticate_from_device_group: - group = Group.get( - ldb, cn=useropts.allowed_to_authenticate_from_device_group) - useropts.allowed_to_authenticate_from = group.get_authentication_sddl() - - # Generate SDDL for authenticating users from a device in a silo - if useropts.allowed_to_authenticate_from_device_silo: - silo = AuthenticationSilo.get( - ldb, cn=useropts.allowed_to_authenticate_from_device_silo) - useropts.allowed_to_authenticate_from = silo.get_authentication_sddl() - - # Generate SDDL for authenticating user accounts to a group - if useropts.allowed_to_authenticate_to_by_group: - group = Group.get( - ldb, cn=useropts.allowed_to_authenticate_to_by_group) - useropts.allowed_to_authenticate_to = group.get_authentication_sddl() - - # Generate SDDL for authenticating user accounts to a silo - if useropts.allowed_to_authenticate_to_by_silo: - silo = AuthenticationSilo.get( - ldb, cn=useropts.allowed_to_authenticate_to_by_silo) - useropts.allowed_to_authenticate_to = silo.get_authentication_sddl() - - # Generate SDDL for authenticating service accounts from a device in a group - if serviceopts.allowed_to_authenticate_from_device_group: - group = Group.get( - ldb, cn=serviceopts.allowed_to_authenticate_from_device_group) - serviceopts.allowed_to_authenticate_from = group.get_authentication_sddl() - - # Generate SDDL for authenticating service accounts from a device in a silo - if serviceopts.allowed_to_authenticate_from_device_silo: - silo = AuthenticationSilo.get( - ldb, cn=serviceopts.allowed_to_authenticate_from_device_silo) - serviceopts.allowed_to_authenticate_from = silo.get_authentication_sddl() - - # Generate SDDL for authenticating service accounts to a group - if serviceopts.allowed_to_authenticate_to_by_group: - group = Group.get( - ldb, cn=serviceopts.allowed_to_authenticate_to_by_group) - serviceopts.allowed_to_authenticate_to = group.get_authentication_sddl() - - # Generate SDDL for authenticating service accounts to a silo - if serviceopts.allowed_to_authenticate_to_by_silo: - silo = AuthenticationSilo.get( - ldb, cn=serviceopts.allowed_to_authenticate_to_by_silo) - serviceopts.allowed_to_authenticate_to = silo.get_authentication_sddl() - - # Generate SDDL for authenticating computer accounts to a group - if computeropts.allowed_to_authenticate_to_by_group: - group = Group.get( - ldb, cn=computeropts.allowed_to_authenticate_to_by_group) - computeropts.allowed_to_authenticate_to = group.get_authentication_sddl() - - # Generate SDDL for authenticating computer accounts to a silo - if computeropts.allowed_to_authenticate_to_by_silo: - silo = AuthenticationSilo.get( - ldb, cn=computeropts.allowed_to_authenticate_to_by_silo) - computeropts.allowed_to_authenticate_to = silo.get_authentication_sddl() - - try: - policy = AuthenticationPolicy.get(ldb, cn=name) - except ModelError as e: - raise CommandError(e) - - # Make sure authentication policy doesn't already exist. - 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=useropts.allow_ntlm_auth, - user_tgt_lifetime=useropts.tgt_lifetime, - user_allowed_to_authenticate_from=useropts.allowed_to_authenticate_from, - user_allowed_to_authenticate_to=useropts.allowed_to_authenticate_to, - service_allow_ntlm_auth=serviceopts.allow_ntlm_auth, - service_tgt_lifetime=serviceopts.tgt_lifetime, - service_allowed_to_authenticate_from=serviceopts.allowed_to_authenticate_from, - service_allowed_to_authenticate_to=serviceopts.allowed_to_authenticate_to, - computer_tgt_lifetime=computeropts.tgt_lifetime, - computer_allowed_to_authenticate_to=computeropts.allowed_to_authenticate_to, - ) - - # 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(ldb) - - if protect: - policy.protect(ldb) - except ModelError as e: - raise CommandError(e) - - # Authentication policy created successfully. - print(f"Created authentication policy: {name}", file=self.outf) - - -class cmd_domain_auth_policy_modify(Command): - """Modify authentication policies on the domain.""" - - synopsis = "%prog -H [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "hostopts": options.HostOptions, - "useropts": UserOptions, - "serviceopts": ServiceOptions, - "computeropts": ComputerOptions, - } - - takes_options = [ - Option("--name", help="Name of authentication policy (required).", - dest="name", action="store", type=str, required=True), - 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()), - ] - - def run(self, hostopts=None, sambaopts=None, credopts=None, useropts=None, - serviceopts=None, computeropts=None, name=None, description=None, - protect=None, unprotect=None, audit=None, enforce=None, - strong_ntlm_policy=None): - - 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.") - - # Check for repeated, similar arguments. - check_similar_args("--user-allowed-to-authenticate-from", - [useropts.allowed_to_authenticate_from, - useropts.allowed_to_authenticate_from_device_group, - useropts.allowed_to_authenticate_from_device_silo]) - check_similar_args("--user-allowed-to-authenticate-to", - [useropts.allowed_to_authenticate_to, - useropts.allowed_to_authenticate_to_by_group, - useropts.allowed_to_authenticate_to_by_silo]) - check_similar_args("--service-allowed-to-authenticate-from", - [serviceopts.allowed_to_authenticate_from, - serviceopts.allowed_to_authenticate_from_device_group, - serviceopts.allowed_to_authenticate_from_device_silo]) - check_similar_args("--service-allowed-to-authenticate-to", - [serviceopts.allowed_to_authenticate_to, - serviceopts.allowed_to_authenticate_to_by_group, - serviceopts.allowed_to_authenticate_to_by_silo]) - check_similar_args("--computer-allowed-to-authenticate-to", - [computeropts.allowed_to_authenticate_to, - computeropts.allowed_to_authenticate_to_by_group, - computeropts.allowed_to_authenticate_to_by_silo]) - - ldb = self.ldb_connect(hostopts, sambaopts, credopts) - - # Generate SDDL for authenticating users from a device in a group - if useropts.allowed_to_authenticate_from_device_group: - group = Group.get( - ldb, cn=useropts.allowed_to_authenticate_from_device_group) - useropts.allowed_to_authenticate_from = group.get_authentication_sddl() - - # Generate SDDL for authenticating users from a device in a silo - if useropts.allowed_to_authenticate_from_device_silo: - silo = AuthenticationSilo.get( - ldb, cn=useropts.allowed_to_authenticate_from_device_silo) - useropts.allowed_to_authenticate_from = silo.get_authentication_sddl() - - # Generate SDDL for authenticating user accounts to a group - if useropts.allowed_to_authenticate_to_by_group: - group = Group.get( - ldb, cn=useropts.allowed_to_authenticate_to_by_group) - useropts.allowed_to_authenticate_to = group.get_authentication_sddl() - - # Generate SDDL for authenticating user accounts to a silo - if useropts.allowed_to_authenticate_to_by_silo: - silo = AuthenticationSilo.get( - ldb, cn=useropts.allowed_to_authenticate_to_by_silo) - useropts.allowed_to_authenticate_to = silo.get_authentication_sddl() - - # Generate SDDL for authenticating users from a device a device in a group - if serviceopts.allowed_to_authenticate_from_device_group: - group = Group.get( - ldb, cn=serviceopts.allowed_to_authenticate_from_device_group) - serviceopts.allowed_to_authenticate_from = group.get_authentication_sddl() - - # Generate SDDL for authenticating service accounts from a device in a silo - if serviceopts.allowed_to_authenticate_from_device_silo: - silo = AuthenticationSilo.get( - ldb, cn=serviceopts.allowed_to_authenticate_from_device_silo) - serviceopts.allowed_to_authenticate_from = silo.get_authentication_sddl() - - # Generate SDDL for authenticating service accounts to a group - if serviceopts.allowed_to_authenticate_to_by_group: - group = Group.get( - ldb, cn=serviceopts.allowed_to_authenticate_to_by_group) - serviceopts.allowed_to_authenticate_to = group.get_authentication_sddl() - - # Generate SDDL for authenticating service accounts to a silo - if serviceopts.allowed_to_authenticate_to_by_silo: - silo = AuthenticationSilo.get( - ldb, cn=serviceopts.allowed_to_authenticate_to_by_silo) - serviceopts.allowed_to_authenticate_to = silo.get_authentication_sddl() - - # Generate SDDL for authenticating computer accounts to a group - if computeropts.allowed_to_authenticate_to_by_group: - group = Group.get( - ldb, cn=computeropts.allowed_to_authenticate_to_by_group) - computeropts.allowed_to_authenticate_to = group.get_authentication_sddl() - - # Generate SDDL for authenticating computer accounts to a silo - if computeropts.allowed_to_authenticate_to_by_silo: - silo = AuthenticationSilo.get( - ldb, cn=computeropts.allowed_to_authenticate_to_by_silo) - computeropts.allowed_to_authenticate_to = silo.get_authentication_sddl() - - try: - policy = AuthenticationPolicy.get(ldb, cn=name) - except ModelError as e: - raise CommandError(e) - - # Check if authentication policy exists. - 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 useropts.tgt_lifetime is not None: - policy.user_tgt_lifetime = useropts.tgt_lifetime - - if useropts.allowed_to_authenticate_from is not None: - policy.user_allowed_to_authenticate_from = \ - useropts.allowed_to_authenticate_from - - if useropts.allowed_to_authenticate_to is not None: - policy.user_allowed_to_authenticate_to = \ - useropts.allowed_to_authenticate_to - - # Service sign on - ################## - - if serviceopts.tgt_lifetime is not None: - policy.service_tgt_lifetime = serviceopts.tgt_lifetime - - if serviceopts.allowed_to_authenticate_from is not None: - policy.service_allowed_to_authenticate_from = \ - serviceopts.allowed_to_authenticate_from - - if serviceopts.allowed_to_authenticate_to is not None: - policy.service_allowed_to_authenticate_to = \ - serviceopts.allowed_to_authenticate_to - - # Computer - ########### - - if computeropts.tgt_lifetime is not None: - policy.computer_tgt_lifetime = computeropts.tgt_lifetime - - if computeropts.allowed_to_authenticate_to is not None: - policy.computer_allowed_to_authenticate_to = \ - computeropts.allowed_to_authenticate_to - - # Update policy. - try: - policy.save(ldb) - - if protect: - policy.protect(ldb) - elif unprotect: - policy.unprotect(ldb) - except ModelError as e: - raise CommandError(e) - - # Authentication policy updated successfully. - print(f"Updated authentication policy: {name}", file=self.outf) - - -class cmd_domain_auth_policy_delete(Command): - """Delete authentication policies on the domain.""" - - synopsis = "%prog -H [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "hostopts": options.HostOptions, - } - - takes_options = [ - Option("--name", help="Name of authentication policy (required).", - dest="name", action="store", type=str, required=True), - Option("--force", help="Force delete protected authentication policy.", - dest="force", action="store_true") - ] - - def run(self, hostopts=None, sambaopts=None, credopts=None, name=None, - force=None): - - ldb = self.ldb_connect(hostopts, sambaopts, credopts) - - try: - policy = AuthenticationPolicy.get(ldb, cn=name) - except ModelError as e: - raise CommandError(e) - - # Check if authentication policy exists first. - if policy is None: - raise CommandError(f"Authentication policy {name} not found.") - - # Delete item, --force removes delete protection first. - try: - if force: - policy.unprotect(ldb) - - policy.delete(ldb) - except ModelError as e: - if not force: - raise CommandError( - f"{e}\nTry --force to delete protected authentication policies.") - else: - raise CommandError(e) - - # Authentication policy deleted successfully. - print(f"Deleted authentication policy: {name}", file=self.outf) +from samba.netcmd import SuperCommand + +from .policy import ( + cmd_domain_auth_policy_create, + cmd_domain_auth_policy_delete, + cmd_domain_auth_policy_list, + cmd_domain_auth_policy_modify, + cmd_domain_auth_policy_view, +) class cmd_domain_auth_policy(SuperCommand): diff --git a/python/samba/netcmd/domain/auth/policy/policy.py b/python/samba/netcmd/domain/auth/policy/policy.py new file mode 100644 index 00000000000..a4e148cb55e --- /dev/null +++ b/python/samba/netcmd/domain/auth/policy/policy.py @@ -0,0 +1,670 @@ +# Unix SMB/CIFS implementation. +# +# authentication silos - authentication policy management +# +# Copyright (C) Catalyst.Net Ltd. 2023 +# +# Written by Rob van der Linde +# +# 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 . +# + +import samba.getopt as options +from samba.domain.models import (MAX_TGT_LIFETIME, MIN_TGT_LIFETIME, + AuthenticationPolicy, AuthenticationSilo, + Group, StrongNTLMPolicy) +from samba.domain.models.exceptions import ModelError +from samba.netcmd import Command, CommandError, Option +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-device-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.""" + + def __init__(self, parser): + super().__init__(parser, "User Options") + + self.add_option("--user-tgt-lifetime-mins", + help="Ticket-Granting-Ticket lifetime for user accounts.", + dest="tgt_lifetime", type=int, action="callback", + callback=self.set_option, + validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]) + self.add_option("--user-allow-ntlm-auth", + help="Allow NTLM network authentication despite the fact that the user " + "is restricted to selected devices.", + dest="allow_ntlm_auth", default=False, + action="callback", callback=self.set_option) + self.add_option("--user-allowed-to-authenticate-from", + help="SDDL Rules setting which device the user is allowed to authenticate from.", + type=str, dest="allowed_to_authenticate_from", + action="callback", callback=self.set_option, + metavar="SDDL") + self.add_option("--user-allowed-to-authenticate-from-device-silo", + help="To authenticate, the user must log in from a device in SILO.", + type=str, dest="allowed_to_authenticate_from_device_silo", + action="callback", callback=self.set_option, + metavar="SILO") + self.add_option("--user-allowed-to-authenticate-from-device-group", + help="To authenticate, the user must log in from a device in GROUP.", + type=str, dest="allowed_to_authenticate_from_device_group", + action="callback", callback=self.set_option, + metavar="GROUP") + self.add_option("--user-allowed-to-authenticate-to", + help="A target service, on a user account, requires the connecting user to match SDDL", + type=str, dest="allowed_to_authenticate_to", + action="callback", callback=self.set_option, + metavar="SDDL") + self.add_option("--user-allowed-to-authenticate-to-by-group", + help="A target service, on a user account, requires the connecting user to be in GROUP", + type=str, dest="allowed_to_authenticate_to_by_group", + action="callback", callback=self.set_option, + metavar="GROUP") + self.add_option("--user-allowed-to-authenticate-to-by-silo", + help="A target service, on a user account, requires the connecting user to be in SILO", + type=str, dest="allowed_to_authenticate_to_by_silo", + action="callback", callback=self.set_option, + metavar="SILO") + + +class ServiceOptions(options.OptionGroup): + """Service options used by policy create and policy modify commands.""" + + def __init__(self, parser): + super().__init__(parser, "Service Options") + + self.add_option("--service-tgt-lifetime-mins", + help="Ticket-Granting-Ticket lifetime for service accounts.", + dest="tgt_lifetime", type=int, action="callback", + callback=self.set_option, + validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]) + self.add_option("--service-allow-ntlm-auth", + help="Allow NTLM network authentication despite " + "the fact that the service account " + "is restricted to selected devices.", + dest="allow_ntlm_auth", default=False, + action="callback", callback=self.set_option) + self.add_option("--service-allowed-to-authenticate-from", + help="SDDL Rules setting which device the " + "service account is allowed to authenticate from.", + type=str, dest="allowed_to_authenticate_from", + action="callback", callback=self.set_option, + metavar="SDDL") + self.add_option("--service-allowed-to-authenticate-from-device-silo", + help="To authenticate, the service must authenticate on a device in SILO.", + type=str, dest="allowed_to_authenticate_from_device_silo", + action="callback", callback=self.set_option, + metavar="SILO") + self.add_option("--service-allowed-to-authenticate-from-device-group", + help="To authenticate, the service must authenticate on a device in GROUP.", + type=str, dest="allowed_to_authenticate_from_device_group", + action="callback", callback=self.set_option, + metavar="GROUP") + self.add_option("--service-allowed-to-authenticate-to", + help="The target service requires the connecting user to match SDDL", + type=str, dest="allowed_to_authenticate_to", + action="callback", callback=self.set_option, + metavar="SDDL") + self.add_option("--service-allowed-to-authenticate-to-by-group", + help="The target service requires the connecting user to be in GROUP", + type=str, dest="allowed_to_authenticate_to_by_group", + action="callback", callback=self.set_option, + metavar="GROUP") + self.add_option("--service-allowed-to-authenticate-to-by-silo", + help="The target service requires the connecting user to be in SILO", + type=str, dest="allowed_to_authenticate_to_by_silo", + action="callback", callback=self.set_option, + metavar="SILO") + + +class ComputerOptions(options.OptionGroup): + """Computer options used by policy create and policy modify commands.""" + + def __init__(self, parser): + super().__init__(parser, "Computer Options") + + self.add_option("--computer-tgt-lifetime-mins", + help="Ticket-Granting-Ticket lifetime for computer accounts.", + dest="tgt_lifetime", type=int, action="callback", + callback=self.set_option, + validators=[Range(min=MIN_TGT_LIFETIME, max=MAX_TGT_LIFETIME)]) + self.add_option("--computer-allowed-to-authenticate-to", + help="The computer account (server, workstation) service requires the connecting user to match SDDL", + type=str, dest="allowed_to_authenticate_to", + action="callback", callback=self.set_option, + metavar="SDDL") + self.add_option("--computer-allowed-to-authenticate-to-by-group", + help="The computer account (server, workstation) service requires the connecting user to be in GROUP", + type=str, dest="allowed_to_authenticate_to_by_group", + action="callback", callback=self.set_option, + metavar="GROUP") + self.add_option("--computer-allowed-to-authenticate-to-by-silo", + help="The computer account (server, workstation) service requires the connecting user to be in SILO", + type=str, dest="allowed_to_authenticate_to_by_silo", + action="callback", callback=self.set_option, + metavar="SILO") + + +class cmd_domain_auth_policy_list(Command): + """List authentication policies on the domain.""" + + synopsis = "%prog -H [options]" + + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "credopts": options.CredentialsOptions, + "hostopts": options.HostOptions, + } + + takes_options = [ + Option("--json", help="Output results in JSON format.", + dest="output_format", action="store_const", const="json"), + ] + + def run(self, hostopts=None, sambaopts=None, credopts=None, + output_format=None): + + ldb = self.ldb_connect(hostopts, sambaopts, credopts) + + try: + policies = AuthenticationPolicy.query(ldb) + except ModelError as e: + raise CommandError(e) + + # Using json output format gives more detail. + if output_format == "json": + self.print_json({policy.name: policy for policy in policies}) + else: + for policy in policies: + print(policy.name, file=self.outf) + + +class cmd_domain_auth_policy_view(Command): + """View an authentication policy on the domain.""" + + synopsis = "%prog -H [options]" + + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "credopts": options.CredentialsOptions, + "hostopts": options.HostOptions, + } + + takes_options = [ + Option("--name", + help="Name of authentication policy to view (required).", + dest="name", action="store", type=str, required=True), + ] + + def run(self, hostopts=None, sambaopts=None, credopts=None, name=None): + + ldb = self.ldb_connect(hostopts, sambaopts, credopts) + + try: + policy = AuthenticationPolicy.get(ldb, cn=name) + except ModelError as e: + raise CommandError(e) + + # Check if authentication policy exists first. + 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(Command): + """Create an authentication policy on the domain.""" + + synopsis = "%prog -H [options]" + + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "credopts": options.CredentialsOptions, + "hostopts": options.HostOptions, + "useropts": UserOptions, + "serviceopts": ServiceOptions, + "computeropts": ComputerOptions, + } + + takes_options = [ + Option("--name", help="Name of authentication policy (required).", + dest="name", action="store", type=str, required=True), + 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"), + ] + + def run(self, hostopts=None, sambaopts=None, credopts=None, useropts=None, + serviceopts=None, computeropts=None, name=None, description=None, + protect=None, unprotect=None, audit=None, enforce=None, + strong_ntlm_policy=None): + + 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.") + + # Check for repeated, similar arguments. + check_similar_args("--user-allowed-to-authenticate-from", + [useropts.allowed_to_authenticate_from, + useropts.allowed_to_authenticate_from_device_group, + useropts.allowed_to_authenticate_from_device_silo]) + check_similar_args("--user-allowed-to-authenticate-to", + [useropts.allowed_to_authenticate_to, + useropts.allowed_to_authenticate_to_by_group, + useropts.allowed_to_authenticate_to_by_silo]) + check_similar_args("--service-allowed-to-authenticate-from", + [serviceopts.allowed_to_authenticate_from, + serviceopts.allowed_to_authenticate_from_device_group, + serviceopts.allowed_to_authenticate_from_device_silo]) + check_similar_args("--service-allowed-to-authenticate-to", + [serviceopts.allowed_to_authenticate_to, + serviceopts.allowed_to_authenticate_to_by_group, + serviceopts.allowed_to_authenticate_to_by_silo]) + check_similar_args("--computer-allowed-to-authenticate-to", + [computeropts.allowed_to_authenticate_to, + computeropts.allowed_to_authenticate_to_by_group, + computeropts.allowed_to_authenticate_to_by_silo]) + + ldb = self.ldb_connect(hostopts, sambaopts, credopts) + + # Generate SDDL for authenticating users from a device in a group + if useropts.allowed_to_authenticate_from_device_group: + group = Group.get( + ldb, cn=useropts.allowed_to_authenticate_from_device_group) + useropts.allowed_to_authenticate_from = group.get_authentication_sddl() + + # Generate SDDL for authenticating users from a device in a silo + if useropts.allowed_to_authenticate_from_device_silo: + silo = AuthenticationSilo.get( + ldb, cn=useropts.allowed_to_authenticate_from_device_silo) + useropts.allowed_to_authenticate_from = silo.get_authentication_sddl() + + # Generate SDDL for authenticating user accounts to a group + if useropts.allowed_to_authenticate_to_by_group: + group = Group.get( + ldb, cn=useropts.allowed_to_authenticate_to_by_group) + useropts.allowed_to_authenticate_to = group.get_authentication_sddl() + + # Generate SDDL for authenticating user accounts to a silo + if useropts.allowed_to_authenticate_to_by_silo: + silo = AuthenticationSilo.get( + ldb, cn=useropts.allowed_to_authenticate_to_by_silo) + useropts.allowed_to_authenticate_to = silo.get_authentication_sddl() + + # Generate SDDL for authenticating service accounts from a device in a group + if serviceopts.allowed_to_authenticate_from_device_group: + group = Group.get( + ldb, cn=serviceopts.allowed_to_authenticate_from_device_group) + serviceopts.allowed_to_authenticate_from = group.get_authentication_sddl() + + # Generate SDDL for authenticating service accounts from a device in a silo + if serviceopts.allowed_to_authenticate_from_device_silo: + silo = AuthenticationSilo.get( + ldb, cn=serviceopts.allowed_to_authenticate_from_device_silo) + serviceopts.allowed_to_authenticate_from = silo.get_authentication_sddl() + + # Generate SDDL for authenticating service accounts to a group + if serviceopts.allowed_to_authenticate_to_by_group: + group = Group.get( + ldb, cn=serviceopts.allowed_to_authenticate_to_by_group) + serviceopts.allowed_to_authenticate_to = group.get_authentication_sddl() + + # Generate SDDL for authenticating service accounts to a silo + if serviceopts.allowed_to_authenticate_to_by_silo: + silo = AuthenticationSilo.get( + ldb, cn=serviceopts.allowed_to_authenticate_to_by_silo) + serviceopts.allowed_to_authenticate_to = silo.get_authentication_sddl() + + # Generate SDDL for authenticating computer accounts to a group + if computeropts.allowed_to_authenticate_to_by_group: + group = Group.get( + ldb, cn=computeropts.allowed_to_authenticate_to_by_group) + computeropts.allowed_to_authenticate_to = group.get_authentication_sddl() + + # Generate SDDL for authenticating computer accounts to a silo + if computeropts.allowed_to_authenticate_to_by_silo: + silo = AuthenticationSilo.get( + ldb, cn=computeropts.allowed_to_authenticate_to_by_silo) + computeropts.allowed_to_authenticate_to = silo.get_authentication_sddl() + + try: + policy = AuthenticationPolicy.get(ldb, cn=name) + except ModelError as e: + raise CommandError(e) + + # Make sure authentication policy doesn't already exist. + 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=useropts.allow_ntlm_auth, + user_tgt_lifetime=useropts.tgt_lifetime, + user_allowed_to_authenticate_from=useropts.allowed_to_authenticate_from, + user_allowed_to_authenticate_to=useropts.allowed_to_authenticate_to, + service_allow_ntlm_auth=serviceopts.allow_ntlm_auth, + service_tgt_lifetime=serviceopts.tgt_lifetime, + service_allowed_to_authenticate_from=serviceopts.allowed_to_authenticate_from, + service_allowed_to_authenticate_to=serviceopts.allowed_to_authenticate_to, + computer_tgt_lifetime=computeropts.tgt_lifetime, + computer_allowed_to_authenticate_to=computeropts.allowed_to_authenticate_to, + ) + + # 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(ldb) + + if protect: + policy.protect(ldb) + except ModelError as e: + raise CommandError(e) + + # Authentication policy created successfully. + print(f"Created authentication policy: {name}", file=self.outf) + + +class cmd_domain_auth_policy_modify(Command): + """Modify authentication policies on the domain.""" + + synopsis = "%prog -H [options]" + + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "credopts": options.CredentialsOptions, + "hostopts": options.HostOptions, + "useropts": UserOptions, + "serviceopts": ServiceOptions, + "computeropts": ComputerOptions, + } + + takes_options = [ + Option("--name", help="Name of authentication policy (required).", + dest="name", action="store", type=str, required=True), + 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()), + ] + + def run(self, hostopts=None, sambaopts=None, credopts=None, useropts=None, + serviceopts=None, computeropts=None, name=None, description=None, + protect=None, unprotect=None, audit=None, enforce=None, + strong_ntlm_policy=None): + + 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.") + + # Check for repeated, similar arguments. + check_similar_args("--user-allowed-to-authenticate-from", + [useropts.allowed_to_authenticate_from, + useropts.allowed_to_authenticate_from_device_group, + useropts.allowed_to_authenticate_from_device_silo]) + check_similar_args("--user-allowed-to-authenticate-to", + [useropts.allowed_to_authenticate_to, + useropts.allowed_to_authenticate_to_by_group, + useropts.allowed_to_authenticate_to_by_silo]) + check_similar_args("--service-allowed-to-authenticate-from", + [serviceopts.allowed_to_authenticate_from, + serviceopts.allowed_to_authenticate_from_device_group, + serviceopts.allowed_to_authenticate_from_device_silo]) + check_similar_args("--service-allowed-to-authenticate-to", + [serviceopts.allowed_to_authenticate_to, + serviceopts.allowed_to_authenticate_to_by_group, + serviceopts.allowed_to_authenticate_to_by_silo]) + check_similar_args("--computer-allowed-to-authenticate-to", + [computeropts.allowed_to_authenticate_to, + computeropts.allowed_to_authenticate_to_by_group, + computeropts.allowed_to_authenticate_to_by_silo]) + + ldb = self.ldb_connect(hostopts, sambaopts, credopts) + + # Generate SDDL for authenticating users from a device in a group + if useropts.allowed_to_authenticate_from_device_group: + group = Group.get( + ldb, cn=useropts.allowed_to_authenticate_from_device_group) + useropts.allowed_to_authenticate_from = group.get_authentication_sddl() + + # Generate SDDL for authenticating users from a device in a silo + if useropts.allowed_to_authenticate_from_device_silo: + silo = AuthenticationSilo.get( + ldb, cn=useropts.allowed_to_authenticate_from_device_silo) + useropts.allowed_to_authenticate_from = silo.get_authentication_sddl() + + # Generate SDDL for authenticating user accounts to a group + if useropts.allowed_to_authenticate_to_by_group: + group = Group.get( + ldb, cn=useropts.allowed_to_authenticate_to_by_group) + useropts.allowed_to_authenticate_to = group.get_authentication_sddl() + + # Generate SDDL for authenticating user accounts to a silo + if useropts.allowed_to_authenticate_to_by_silo: + silo = AuthenticationSilo.get( + ldb, cn=useropts.allowed_to_authenticate_to_by_silo) + useropts.allowed_to_authenticate_to = silo.get_authentication_sddl() + + # Generate SDDL for authenticating users from a device a device in a group + if serviceopts.allowed_to_authenticate_from_device_group: + group = Group.get( + ldb, cn=serviceopts.allowed_to_authenticate_from_device_group) + serviceopts.allowed_to_authenticate_from = group.get_authentication_sddl() + + # Generate SDDL for authenticating service accounts from a device in a silo + if serviceopts.allowed_to_authenticate_from_device_silo: + silo = AuthenticationSilo.get( + ldb, cn=serviceopts.allowed_to_authenticate_from_device_silo) + serviceopts.allowed_to_authenticate_from = silo.get_authentication_sddl() + + # Generate SDDL for authenticating service accounts to a group + if serviceopts.allowed_to_authenticate_to_by_group: + group = Group.get( + ldb, cn=serviceopts.allowed_to_authenticate_to_by_group) + serviceopts.allowed_to_authenticate_to = group.get_authentication_sddl() + + # Generate SDDL for authenticating service accounts to a silo + if serviceopts.allowed_to_authenticate_to_by_silo: + silo = AuthenticationSilo.get( + ldb, cn=serviceopts.allowed_to_authenticate_to_by_silo) + serviceopts.allowed_to_authenticate_to = silo.get_authentication_sddl() + + # Generate SDDL for authenticating computer accounts to a group + if computeropts.allowed_to_authenticate_to_by_group: + group = Group.get( + ldb, cn=computeropts.allowed_to_authenticate_to_by_group) + computeropts.allowed_to_authenticate_to = group.get_authentication_sddl() + + # Generate SDDL for authenticating computer accounts to a silo + if computeropts.allowed_to_authenticate_to_by_silo: + silo = AuthenticationSilo.get( + ldb, cn=computeropts.allowed_to_authenticate_to_by_silo) + computeropts.allowed_to_authenticate_to = silo.get_authentication_sddl() + + try: + policy = AuthenticationPolicy.get(ldb, cn=name) + except ModelError as e: + raise CommandError(e) + + # Check if authentication policy exists. + 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 useropts.tgt_lifetime is not None: + policy.user_tgt_lifetime = useropts.tgt_lifetime + + if useropts.allowed_to_authenticate_from is not None: + policy.user_allowed_to_authenticate_from = \ + useropts.allowed_to_authenticate_from + + if useropts.allowed_to_authenticate_to is not None: + policy.user_allowed_to_authenticate_to = \ + useropts.allowed_to_authenticate_to + + # Service sign on + ################## + + if serviceopts.tgt_lifetime is not None: + policy.service_tgt_lifetime = serviceopts.tgt_lifetime + + if serviceopts.allowed_to_authenticate_from is not None: + policy.service_allowed_to_authenticate_from = \ + serviceopts.allowed_to_authenticate_from + + if serviceopts.allowed_to_authenticate_to is not None: + policy.service_allowed_to_authenticate_to = \ + serviceopts.allowed_to_authenticate_to + + # Computer + ########### + + if computeropts.tgt_lifetime is not None: + policy.computer_tgt_lifetime = computeropts.tgt_lifetime + + if computeropts.allowed_to_authenticate_to is not None: + policy.computer_allowed_to_authenticate_to = \ + computeropts.allowed_to_authenticate_to + + # Update policy. + try: + policy.save(ldb) + + if protect: + policy.protect(ldb) + elif unprotect: + policy.unprotect(ldb) + except ModelError as e: + raise CommandError(e) + + # Authentication policy updated successfully. + print(f"Updated authentication policy: {name}", file=self.outf) + + +class cmd_domain_auth_policy_delete(Command): + """Delete authentication policies on the domain.""" + + synopsis = "%prog -H [options]" + + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "credopts": options.CredentialsOptions, + "hostopts": options.HostOptions, + } + + takes_options = [ + Option("--name", help="Name of authentication policy (required).", + dest="name", action="store", type=str, required=True), + Option("--force", help="Force delete protected authentication policy.", + dest="force", action="store_true") + ] + + def run(self, hostopts=None, sambaopts=None, credopts=None, name=None, + force=None): + + ldb = self.ldb_connect(hostopts, sambaopts, credopts) + + try: + policy = AuthenticationPolicy.get(ldb, cn=name) + except ModelError as e: + raise CommandError(e) + + # Check if authentication policy exists first. + if policy is None: + raise CommandError(f"Authentication policy {name} not found.") + + # Delete item, --force removes delete protection first. + try: + if force: + policy.unprotect(ldb) + + policy.delete(ldb) + except ModelError as e: + if not force: + raise CommandError( + f"{e}\nTry --force to delete protected authentication policies.") + else: + raise CommandError(e) + + # Authentication policy deleted successfully. + print(f"Deleted authentication policy: {name}", file=self.outf)