I think passing filter is more flexible here.
There is one comment I'd like to address but otherwise it looks good.
See inline.
Post by William Brown via samba-technicalThanks for your review!
William
From 9961efc5f599d043479d2376c6d96a378302f57f Mon Sep 17 00:00:00 2001
Date: Mon, 30 Apr 2018 14:28:31 +1200
Subject: [PATCH] python/samba/netcmd/{__init__.py,user.py,group.py} improve
edit command.
samba-tool allows editing an ldif of objects which can be then commited
back to the directory server. This makes an EditCommand type which can
samba-tool group edit ...
samba-tool user edit ...
---
python/samba/netcmd/__init__.py | 108 ++++++++++++++++++++++++++++++++++++++++
python/samba/netcmd/group.py | 64 +++++++++++++++++++++++-
python/samba/netcmd/user.py | 87 +++-----------------------------
3 files changed, 179 insertions(+), 80 deletions(-)
diff --git a/python/samba/netcmd/__init__.py b/python/samba/netcmd/__init__.py
index 77976780150..65cfbcfc035 100644
--- a/python/samba/netcmd/__init__.py
+++ b/python/samba/netcmd/__init__.py
@@ -22,6 +22,16 @@ from ldb import LdbError
import sys, traceback
import textwrap
+# For editCommand
+import re
+import os
+import ldb
+import difflib
+import tempfile
+from samba.samdb import SamDB
+from samba.auth import system_session
+from subprocess import Popen, PIPE, STDOUT, check_call, CalledProcessError
+
pass
return logger
+ """A specialise variant of Command for DS object editing."""
+
+ def _run_edit(self, filt, objectname, objecttype, credopts=None,
+
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp, fallback_machine=True)
+ samdb = SamDB(url=H, session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ domaindn = samdb.domain_dn()
+
+ res = samdb.search(base=domaindn,
+ expression=filt,
+ scope=ldb.SCOPE_SUBTREE)
+ object_dn = res[0].dn
+ raise CommandError('Unable to find %s "%s"' %
+ (objecttype.lower(), objectname))
+
+ r_ldif = samdb.write_ldif(msg, 1)
+ # remove 'changetype' line
+ result_ldif = re.sub('changetype: add\n', '', r_ldif)
+ # Remove line wrapping where it exists.
+ # This fixes an issue when you do the diff that doesn't know how
+ # to handle a multi-line value - or add one ....
+ # In some cases *true* multiline values may need b64 encoding to
+ # preserve the newlines.
+ result_ldif = re.sub('\n ', '', result_ldif)
+
+ editor = os.environ.get('EDITOR')
+ editor = 'vi'
+
+ # Assert the editor chosen actually exists!
+ raise CommandError('Unable to access "%s". Does it exist?'
+ % editor)
an LDAP record if editor is missing. Original code didn't do
the loop.
Post by William Brown via samba-technical+
+ t_file.write(result_ldif)
+ t_file.flush()
+ check_call([editor, t_file.name])
+ raise CalledProcessError("ERROR: ", e)
+ edited_message = edited_file.read()
+
+ diff = difflib.ndiff(result_ldif.splitlines(),
+ edited_message.splitlines())
+ minus_lines = []
+ plus_lines = []
+ line = line[2:]
+ minus_lines.append(line)
+ line = line[2:]
+ plus_lines.append(line)
+
+ object_ldif="dn: %s\n" % object_dn
+ object_ldif += "changetype: modify\n"
+
+ attr, val = line.split(':', 1)
+ search_attr="%s:" % attr
+ object_ldif += "delete: %s\n" % attr
+ object_ldif += "%s: %s\n" % (attr, val)
+
+ attr, val = line.split(':', 1)
+ search_attr="%s:" % attr
+ object_ldif += "replace: %s\n" % attr
+ object_ldif += "%s: %s\n" % (attr, val)
+ object_ldif += "add: %s\n" % attr
+ object_ldif += "%s: %s\n" % (attr, val)
+
+ samdb.modify_ldif(object_ldif)
+ raise CommandError("Failed to modify %s '%s': " %
+ (objecttype.lower(), objectname), e)
+
+ self.outf.write("Modified %s '%s' successfully\n" %
+ (objecttype, objectname))
+
+
+
+
"""A samba-tool command with subcommands."""
diff --git a/python/samba/netcmd/group.py b/python/samba/netcmd/group.py
index a4969cc6ba9..c2713e1879a 100644
--- a/python/samba/netcmd/group.py
+++ b/python/samba/netcmd/group.py
@@ -17,7 +17,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import samba.getopt as options
-from samba.netcmd import Command, SuperCommand, CommandError, Option
+from samba.netcmd import Command, EditCommand, SuperCommand, CommandError, Option
import ldb
from samba.ndr import ndr_unpack
from samba.dcerpc import security
self.outf.write('Moved group "%s" into "%s"\n' %
(groupname, full_new_parent_dn))
+ """Modify Group AD object.
+
+This command will allow editing of a group object in the Active Directory
+domain. You will then be able to add or change attributes and their values.
+
+The groupname specified on the command is the sAMAccountName.
+
+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.
+
+samba-tool user edit Group1 -H ldap://samba.samdom.example.com \
+-U administrator --password=passw1rd
+
+Example1 shows how to edit a group's attributes in the domain against a remote
+LDAP server.
+
+The -H parameter is used to specify the remote target server.
+
+samba-tool user edit Group2
+
+Example2 shows how to edit a group's attributes in the domain against a local
+LDAP server.
+
+samba-tool user edit Group3 --editor=nano
+
+Example3 shows how to edit a group's attributes in the domain against a local
+LDAP server using the 'nano' editor.
+
+"""
+ synopsis = "%prog <groupname> [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 = ["groupname"]
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ "versionopts": options.VersionOptions,
+ }
+
+ def run(self, groupname, credopts=None, sambaopts=None, versionopts=None,
+
+ filt = ("(&(sAMAccountName=%s)(objectClass=group))" %
+ ldb.binary_encode(groupname))
+
+ self._run_edit(filt, groupname, 'Group', credopts, sambaopts, versionopts,
+ H, editor)
+
+
"""Group management."""
subcommands["list"] = cmd_group_list()
subcommands["listmembers"] = cmd_group_list_members()
subcommands["move"] = cmd_group_move()
+ subcommands["edit"] = cmd_group_edit()
diff --git a/python/samba/netcmd/user.py b/python/samba/netcmd/user.py
index dfe167d8c7d..89bae061c2b 100644
--- a/python/samba/netcmd/user.py
+++ b/python/samba/netcmd/user.py
@@ -51,6 +51,7 @@ from samba.net import Net
from samba.netcmd import (
Command,
CommandError,
+ EditCommand,
SuperCommand,
Option,
)
@@ -2294,7 +2295,7 @@ samba-tool user syncpasswords --terminate \\
update_pid(None)
return
"""Modify User AD object.
This command will allow editing of a user account in the Active Directory
samba-tool user edit User1 -H ldap://samba.samdom.example.com \
-U administrator --password=passw1rd
-Example1 shows how to edit a users attributes in the domain against a remote
+Example1 shows how to edit a user's attributes in the domain against a remote
LDAP server.
The -H parameter is used to specify the remote target server.
@@ -2319,13 +2320,13 @@ The -H parameter is used to specify the remote target server.
samba-tool user edit User2
-Example2 shows how to edit a users attributes in the domain against a local
+Example2 shows how to edit a user's attributes in the domain against a local
LDAP server.
samba-tool user edit User3 --editor=nano
-Example3 shows how to edit a users attributes in the domain against a local
+Example3 shows how to edit a user's attributes in the domain against a local
LDAP server using the 'nano' editor.
"""
@@ -2348,84 +2349,12 @@ LDAP server using the 'nano' editor.
def run(self, username, credopts=None, sambaopts=None, versionopts=None,
- lp = sambaopts.get_loadparm()
- creds = credopts.get_credentials(lp, fallback_machine=True)
- samdb = SamDB(url=H, session_info=system_session(),
- credentials=creds, lp=lp)
-
- filter = ("(&(sAMAccountType=%d)(sAMAccountName=%s))" %
+ filt = ("(&(sAMAccountType=%d)(sAMAccountName=%s))" %
(dsdb.ATYPE_NORMAL_ACCOUNT, ldb.binary_encode(username)))
- domaindn = samdb.domain_dn()
-
- res = samdb.search(base=domaindn,
- expression=filter,
- scope=ldb.SCOPE_SUBTREE)
- user_dn = res[0].dn
- raise CommandError('Unable to find user "%s"' % (username))
-
- r_ldif = samdb.write_ldif(msg, 1)
- # remove 'changetype' line
- result_ldif = re.sub('changetype: add\n', '', r_ldif)
-
- editor = os.environ.get('EDITOR')
- editor = 'vi'
-
- t_file.write(result_ldif)
- t_file.flush()
- check_call([editor, t_file.name])
- raise CalledProcessError("ERROR: ", e)
- edited_message = edited_file.read()
-
- diff = difflib.ndiff(result_ldif.splitlines(),
- edited_message.splitlines())
- minus_lines = []
- plus_lines = []
- line = line[2:]
- minus_lines.append(line)
- line = line[2:]
- plus_lines.append(line)
-
- user_ldif="dn: %s\n" % user_dn
- user_ldif += "changetype: modify\n"
-
- attr, val = line.split(':', 1)
- search_attr="%s:" % attr
- user_ldif += "delete: %s\n" % attr
- user_ldif += "%s: %s\n" % (attr, val)
-
- attr, val = line.split(':', 1)
- search_attr="%s:" % attr
- user_ldif += "replace: %s\n" % attr
- user_ldif += "%s: %s\n" % (attr, val)
- user_ldif += "add: %s\n" % attr
- user_ldif += "%s: %s\n" % (attr, val)
-
- samdb.modify_ldif(user_ldif)
- raise CommandError("Failed to modify user '%s': " %
- username, e)
+ self._run_edit(filt, username, 'User', credopts, sambaopts, versionopts,
+ H, editor)
- self.outf.write("Modified User '%s' successfully\n" % username)
"""Display a user AD object.
--
2.14.3