From 2c64a25fc0484eb78874630fad9fa5c4e85527c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= Date: Wed, 13 Mar 2019 21:40:25 +0100 Subject: samba-tool computer: add 'edit' command to edit an AD computer object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to the samba-tool user edit command. Signed-off-by: Björn Baumbach Reviewed-by: Andrew Bartlett --- python/samba/netcmd/computer.py | 123 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/python/samba/netcmd/computer.py b/python/samba/netcmd/computer.py index 81b401db9b3..b66dcc4a7a5 100644 --- a/python/samba/netcmd/computer.py +++ b/python/samba/netcmd/computer.py @@ -25,6 +25,8 @@ import ldb import socket import samba import re +import os +import tempfile from samba import sd_utils from samba.dcerpc import dnsserver, dnsp, security from samba.dnsserver import ARecord, AAAARecord @@ -32,6 +34,8 @@ from samba.ndr import ndr_unpack, ndr_pack, ndr_print from samba.remove_dc import remove_dns_references from samba.auth import system_session from samba.samdb import SamDB +from samba.compat import get_bytes +from subprocess import check_call, CalledProcessError from samba import ( credentials, @@ -48,7 +52,6 @@ from samba.netcmd import ( Option, ) - def _is_valid_ip(ip_string, address_families=None): """Check ip string is valid address""" # by default, check both ipv4 and ipv6 @@ -400,6 +403,123 @@ sudo is used so a computer may run the command as root. self.outf.write("Deleted computer %s\n" % computername) +class cmd_computer_edit(Command): + """Modify Computer AD object. + + This command will allow editing of a computer account in the Active + Directory domain. You will then be able to add or change attributes and + their values. + + The computername specified on the command is the sAMaccountName with or + without the trailing $ (dollar sign). + + The command may be run from the root userid or another authorized userid. + + The -H or --URL= option can be used to execute the command against a remote + server. + + Example1: + samba-tool computer edit Computer1 -H ldap://samba.samdom.example.com \\ + -U administrator --password=passw1rd + + Example1 shows how to edit a computers attributes in the domain against a + remote LDAP server. + + The -H parameter is used to specify the remote target server. + + Example2: + samba-tool computer edit Computer2 + + Example2 shows how to edit a computers attributes in the domain against a + local LDAP server. + + Example3: + samba-tool computer edit Computer3 --editor=nano + + Example3 shows how to edit a computers attributes in the domain against a + local LDAP server using the 'nano' editor. + """ + synopsis = "%prog [options]" + + takes_options = [ + Option("-H", "--URL", help="LDB URL for database or target server", + type=str, metavar="URL", dest="H"), + Option("--editor", help="Editor to use instead of the system default," + " or 'vi' if no system default is set.", type=str), + ] + + takes_args = ["computername"] + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "credopts": options.CredentialsOptions, + "versionopts": options.VersionOptions, + } + + def run(self, computername, credopts=None, sambaopts=None, versionopts=None, + H=None, editor=None): + from . import common + + lp = sambaopts.get_loadparm() + creds = credopts.get_credentials(lp, fallback_machine=True) + samdb = SamDB(url=H, session_info=system_session(), + credentials=creds, lp=lp) + + samaccountname = computername + if not computername.endswith('$'): + samaccountname = "%s$" % computername + + filter = ("(&(sAMAccountType=%d)(sAMAccountName=%s))" % + (dsdb.ATYPE_WORKSTATION_TRUST, + ldb.binary_encode(samaccountname))) + + domaindn = samdb.domain_dn() + + try: + res = samdb.search(base=domaindn, + expression=filter, + scope=ldb.SCOPE_SUBTREE) + computer_dn = res[0].dn + except IndexError: + raise CommandError('Unable to find computer "%s"' % (computername)) + + if len(res) != 1: + raise CommandError('Invalid number of results: for "%s": %d' % + ((computername), len(res))) + + msg = res[0] + result_ldif = common.get_ldif_for_editor(samdb, msg) + + if editor is None: + editor = os.environ.get('EDITOR') + if editor is None: + editor = 'vi' + + with tempfile.NamedTemporaryFile(suffix=".tmp") as t_file: + t_file.write(get_bytes(result_ldif)) + t_file.flush() + try: + check_call([editor, t_file.name]) + except CalledProcessError as e: + raise CalledProcessError("ERROR: ", e) + with open(t_file.name) as edited_file: + edited_message = edited_file.read() + + msgs_edited = samdb.parse_ldif(edited_message) + msg_edited = next(msgs_edited)[1] + + res_msg_diff = samdb.msg_diff(msg, msg_edited) + if len(res_msg_diff) == 0: + self.outf.write("Nothing to do\n") + return + + try: + samdb.modify(res_msg_diff) + except Exception as e: + raise CommandError("Failed to modify computer '%s': " % + (computername, e)) + + self.outf.write("Modified computer '%s' successfully\n" % computername) + class cmd_computer_list(Command): """List all computers.""" @@ -583,6 +703,7 @@ class cmd_computer(SuperCommand): subcommands = {} subcommands["create"] = cmd_computer_create() subcommands["delete"] = cmd_computer_delete() + subcommands["edit"] = cmd_computer_edit() subcommands["list"] = cmd_computer_list() subcommands["show"] = cmd_computer_show() subcommands["move"] = cmd_computer_move() -- cgit v1.2.1