[PATCH] samba-tool user/group/computer edit fixes and contact management
Björn Baumbach
bb at sernet.de
Fri Mar 22 17:19:06 UTC 2019
Hi!
I did some work to improve the "edit" sub-command of the samba-tool and
need a review:
https://gitlab.com/samba-team/devel/samba/pipelines/53186176
There was an additional successful run a few hours ago, with nearly the
same code:
https://gitlab.com/samba-team/devel/samba/pipelines/53110583
The set of patches solve different things:
- Without the patches it is not possible to modify base64 encoded
attribute values.
- Allow to edit attribute values as clear text, instead of
base64, if possible.
- Add the group and computer edit commands, which were missing.
- Add new contact object management:
Usage: samba-tool contact <subcommand>
Contact management.
Available subcommands:
create - Create a new contact.
delete - Delete a contact.
edit - Modify a contact.
list - List all contacts.
move - Move a contact object to an organizational unit or container.
show - Display a contact.
Best regards,
Björn
--
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: 0551-370000-0, mailto:kontakt at sernet.de
Gesch.F.: Dr. Johannes Loxen und Reinhild Jung
AG Göttingen: HR-B 2816 - http://www.sernet.de
Samba eXPerience 2019, Hotel Freizeit In
sponsored by Google, Microsoft & Red Hat
June, 4th - 6th 2019, http://sambaXP.org
-------------- next part --------------
From 86adc7c4b94f265dcca8e60f1577d0d571507d19 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Thu, 21 Mar 2019 14:15:22 +0100
Subject: [PATCH 01/18] samba-tool: fix format of command description (help
messages)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Need to quote the backslash '\'.
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/netcmd/computer.py | 6 +++---
python/samba/netcmd/group.py | 10 +++++-----
python/samba/netcmd/ou.py | 4 ++--
python/samba/netcmd/schema.py | 2 +-
python/samba/netcmd/user.py | 14 +++++++-------
5 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/python/samba/netcmd/computer.py b/python/samba/netcmd/computer.py
index ff4c3979e78..81b401db9b3 100644
--- a/python/samba/netcmd/computer.py
+++ b/python/samba/netcmd/computer.py
@@ -181,7 +181,7 @@ accounts are also referred to as security principals and are assigned a
security identifier (SID).
Example1:
-samba-tool computer create Computer1 -H ldap://samba.samdom.example.com \
+samba-tool computer create Computer1 -H ldap://samba.samdom.example.com \\
-Uadministrator%passw1rd
Example1 shows how to create a new computer in the domain against a remote LDAP
@@ -323,7 +323,7 @@ userid. The -H or --URL= option can be used to execute the command against
a remote server.
Example1:
-samba-tool computer delete Computer1 -H ldap://samba.samdom.example.com \
+samba-tool computer delete Computer1 -H ldap://samba.samdom.example.com \\
-Uadministrator%passw1rd
Example1 shows how to delete a computer in the domain against a remote LDAP
@@ -450,7 +450,7 @@ The -H or --URL= option can be used to execute the command against a remote
server.
Example1:
-samba-tool computer show Computer1 -H ldap://samba.samdom.example.com \
+samba-tool computer show Computer1 -H ldap://samba.samdom.example.com \\
-U administrator
Example1 shows how display a computers attributes in the domain against a
diff --git a/python/samba/netcmd/group.py b/python/samba/netcmd/group.py
index 3d55222e8d0..6976f82d132 100644
--- a/python/samba/netcmd/group.py
+++ b/python/samba/netcmd/group.py
@@ -444,7 +444,7 @@ class cmd_group_move(Command):
server.
Example1:
- samba-tool group move Group1 'OU=OrgUnit,DC=samdom.DC=example,DC=com' \
+ samba-tool group move Group1 'OU=OrgUnit,DC=samdom.DC=example,DC=com' \\
-H ldap://samba.samdom.example.com -U administrator
Example1 shows how to move a group Group1 into the 'OrgUnit' organizational
@@ -522,11 +522,11 @@ The -H or --URL= option can be used to execute the command against a remote
server.
Example1:
-samba-tool group show Group1 -H ldap://samba.samdom.example.com \
--U administrator --password=passw1rd
+samba-tool group show Group1 -H ldap://samba.samdom.example.com \\
+ -U administrator --password=passw1rd
-Example1 shows how to display a group's attributes in the domain against a remote
-LDAP server.
+Example1 shows how to display a group's attributes in the domain against a
+remote LDAP server.
The -H parameter is used to specify the remote target server.
diff --git a/python/samba/netcmd/ou.py b/python/samba/netcmd/ou.py
index f4e01838e6e..ecc4582cbcf 100644
--- a/python/samba/netcmd/ou.py
+++ b/python/samba/netcmd/ou.py
@@ -38,7 +38,7 @@ class cmd_rename(Command):
or without the domainDN component.
Examples:
- samba-tool ou rename 'OU=OrgUnit,DC=samdom,DC=example,DC=com' \
+ samba-tool ou rename 'OU=OrgUnit,DC=samdom,DC=example,DC=com' \\
'OU=NewNameOfOrgUnit,DC=samdom,DC=example,DC=com'
samba-tool ou rename 'OU=OrgUnit' 'OU=NewNameOfOrgUnit'
@@ -102,7 +102,7 @@ class cmd_move(Command):
or without the domainDN component.
Examples:
- samba-tool ou move 'OU=OrgUnit,DC=samdom,DC=example,DC=com' \
+ samba-tool ou move 'OU=OrgUnit,DC=samdom,DC=example,DC=com' \\
'OU=NewParentOfOrgUnit,DC=samdom,DC=example,DC=com'
samba-tool ou rename 'OU=OrgUnit' 'OU=NewParentOfOrgUnit'
diff --git a/python/samba/netcmd/schema.py b/python/samba/netcmd/schema.py
index 889dd3fb539..d322da015ae 100644
--- a/python/samba/netcmd/schema.py
+++ b/python/samba/netcmd/schema.py
@@ -38,7 +38,7 @@ class cmd_schema_attribute_modify(Command):
so be sure to view the current content before making changes.
Example1:
- samba-tool schema attribute modify uid \
+ samba-tool schema attribute modify uid \\
--searchflags="fATTINDEX,fPRESERVEONDELETE"
This alters the uid attribute to be indexed and to be preserved when
diff --git a/python/samba/netcmd/user.py b/python/samba/netcmd/user.py
index 8ead8e583f3..a64d2176dfa 100644
--- a/python/samba/netcmd/user.py
+++ b/python/samba/netcmd/user.py
@@ -291,8 +291,8 @@ samba-tool user create User4 passw4rd --rfc2307-from-nss --gecos 'some text'
Example4 shows how to create a new user with Unix UID, GID and login-shell set from the local NSS and GECOS set to 'some text'.
Example5:
-samba-tool user create User5 passw5rd --nis-domain=samdom --unix-home=/home/User5 \
- --uid-number=10005 --login-shell=/bin/false --gid-number=10000
+samba-tool user create User5 passw5rd --nis-domain=samdom --unix-home=/home/User5 \\
+ --uid-number=10005 --login-shell=/bin/false --gid-number=10000
Example5 shows how to create an RFC2307/NIS domain enabled user account. If
--nis-domain is set, then the other four parameters are mandatory.
@@ -2389,8 +2389,8 @@ The -H or --URL= option can be used to execute the command against a remote
server.
Example1:
-samba-tool user edit User1 -H ldap://samba.samdom.example.com \
--U administrator --password=passw1rd
+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
LDAP server.
@@ -2522,8 +2522,8 @@ The -H or --URL= option can be used to execute the command against a remote
server.
Example1:
-samba-tool user show User1 -H ldap://samba.samdom.example.com \
--U administrator --password=passw1rd
+samba-tool user show User1 -H ldap://samba.samdom.example.com \\
+ -U administrator --password=passw1rd
Example1 shows how to display a users attributes in the domain against a remote
LDAP server.
@@ -2603,7 +2603,7 @@ class cmd_user_move(Command):
server.
Example1:
- samba-tool user move User1 'OU=OrgUnit,DC=samdom.DC=example,DC=com' \
+ samba-tool user move User1 'OU=OrgUnit,DC=samdom.DC=example,DC=com' \\
-H ldap://samba.samdom.example.com -U administrator
Example1 shows how to move a user User1 into the 'OrgUnit' organizational
--
2.19.2
From 8048255b89e58c597dc9bc41b6874e57f9ff3f81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Thu, 14 Mar 2019 16:47:36 +0100
Subject: [PATCH 02/18] samba-tool tests: rename "user edit" test from edit.sh
to user_edit.sh
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/tests/samba_tool/{edit.sh => user_edit.sh} | 2 +-
source4/selftest/tests.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
rename python/samba/tests/samba_tool/{edit.sh => user_edit.sh} (97%)
diff --git a/python/samba/tests/samba_tool/edit.sh b/python/samba/tests/samba_tool/user_edit.sh
similarity index 97%
rename from python/samba/tests/samba_tool/edit.sh
rename to python/samba/tests/samba_tool/user_edit.sh
index aca4cc247eb..3bbad411b82 100755
--- a/python/samba/tests/samba_tool/edit.sh
+++ b/python/samba/tests/samba_tool/user_edit.sh
@@ -4,7 +4,7 @@
if [ $# -lt 3 ]; then
cat <<EOF
-Usage: edit.sh SERVER USERNAME PASSWORD
+Usage: user_edit.sh SERVER USERNAME PASSWORD
EOF
exit 1;
fi
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index c6355f3a41f..b0c4ce581b3 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -657,7 +657,7 @@ for env in all_fl_envs:
# test user.edit
for env in all_fl_envs:
env += ":local"
- plantestsuite("samba.tests.samba_tool.edit", env, [os.path.join(srcdir(), "python/samba/tests/samba_tool/edit.sh"), '$SERVER', '$USERNAME', '$PASSWORD'])
+ plantestsuite("samba.tests.samba_tool.user_edit", env, [os.path.join(srcdir(), "python/samba/tests/samba_tool/user_edit.sh"), '$SERVER', '$USERNAME', '$PASSWORD'])
# We run this test against both AD DC implementations because it is
# the only test we have of GPO get/set behaviour, and this involves
--
2.19.2
From 97c9f43932ba4075910bbc077d4e370cc26106e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Thu, 14 Mar 2019 12:29:13 +0100
Subject: [PATCH 03/18] samba-tool tests: remove probably outdated comment
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/tests/samba_tool/user_edit.sh | 1 -
1 file changed, 1 deletion(-)
diff --git a/python/samba/tests/samba_tool/user_edit.sh b/python/samba/tests/samba_tool/user_edit.sh
index 3bbad411b82..66ae1d00721 100755
--- a/python/samba/tests/samba_tool/user_edit.sh
+++ b/python/samba/tests/samba_tool/user_edit.sh
@@ -17,7 +17,6 @@ STpath=$(pwd)
. $STpath/testprogs/blackbox/subunit.sh
# create editor.sh
-# this has to be hard linked to /tmp or 'samba-tool user edit' cannot find it
tmpeditor=$(mktemp --suffix .sh -p $STpath/bin samba-tool-editor-XXXXXXXX)
cat >$tmpeditor <<-'EOF'
--
2.19.2
From d2af90fdb2a8a15a051a91ad8492e0b07549552f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Thu, 14 Mar 2019 16:55:42 +0100
Subject: [PATCH 04/18] samba-tool user edit test: use testit instead of
subunit_start_test, pass/failed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/tests/samba_tool/user_edit.sh | 61 ++++++++--------------
1 file changed, 23 insertions(+), 38 deletions(-)
diff --git a/python/samba/tests/samba_tool/user_edit.sh b/python/samba/tests/samba_tool/user_edit.sh
index 66ae1d00721..166d45ae4d6 100755
--- a/python/samba/tests/samba_tool/user_edit.sh
+++ b/python/samba/tests/samba_tool/user_edit.sh
@@ -16,55 +16,40 @@ PASSWORD="$3"
STpath=$(pwd)
. $STpath/testprogs/blackbox/subunit.sh
-# create editor.sh
tmpeditor=$(mktemp --suffix .sh -p $STpath/bin samba-tool-editor-XXXXXXXX)
+chmod +x $tmpeditor
+
+create_test_user() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ user create sambatool1 --random-password \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
-cat >$tmpeditor <<-'EOF'
+edit_user() {
+ # create editor.sh
+ cat >$tmpeditor <<-'EOF'
#!/usr/bin/env bash
user_ldif="$1"
SED=$(which sed)
$SED -i -e 's/userAccountControl: 512/userAccountControl: 514/' $user_ldif
EOF
-chmod +x $tmpeditor
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ user edit sambatool1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
-failed=0
+delete_user() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ user delete sambatool1 \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
-# Create a test user
-subunit_start_test "Create_User"
-output=$($PYTHON ${STpath}/source4/scripting/bin/samba-tool user create sambatool1 --random-password \
--H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD")
-status=$?
-if [ "x$status" = "x0" ]; then
- subunit_pass_test "Create_User"
-else
- echo "$output" | subunit_fail_test "Create_User"
- failed=$((failed + 1))
-fi
-
-# Edit test user
-subunit_start_test "Edit_User"
-output=$($PYTHON ${STpath}/source4/scripting/bin/samba-tool user edit sambatool1 --editor=$tmpeditor \
--H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD")
-status=$?
-if [ "x$status" = "x0" ]; then
- subunit_pass_test "Edit_User"
-else
- echo "$output" | subunit_fail_test "Edit_User"
- failed=$((failed + 1))
-fi
+failed=0
-# Delete test user
-subunit_start_test "Delete_User"
-output=$($PYTHON ${STpath}/source4/scripting/bin/samba-tool user delete sambatool1 \
--H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD")
-status=$?
-if [ "x$status" = "x0" ]; then
- subunit_pass_test "Delete_User"
-else
- echo "$output" | subunit_fail_test "Delete_User"
- failed=$((failed + 1))
-fi
+testit "create_test_user" create_test_user || failed=`expr $failed + 1`
+testit "edit_user" edit_user || failed=`expr $failed + 1`
+testit "delete_user" delete_user || failed=`expr $failed + 1`
rm -f $tmpeditor
--
2.19.2
From 8d14531c3119c878bd5666db3afb46074fbf9134 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Tue, 19 Feb 2019 12:14:37 +0100
Subject: [PATCH 05/18] ldb/ldb_ldif: add copy_raw_bytes helper variable to
ldb_ldif_write_trace()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
lib/ldb/common/ldb_ldif.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/ldb/common/ldb_ldif.c b/lib/ldb/common/ldb_ldif.c
index e69467891c9..90bddd2f43d 100644
--- a/lib/ldb/common/ldb_ldif.c
+++ b/lib/ldb/common/ldb_ldif.c
@@ -352,6 +352,7 @@ static int ldb_ldif_write_trace(struct ldb_context *ldb,
for (j=0;j<msg->elements[i].num_values;j++) {
struct ldb_val v;
bool use_b64_encode = false;
+ bool copy_raw_bytes = false;
ret = a->syntax->ldif_write_fn(ldb, mem_ctx, &msg->elements[i].values[j], &v);
if (ret != LDB_SUCCESS) {
@@ -360,6 +361,7 @@ static int ldb_ldif_write_trace(struct ldb_context *ldb,
if (ldb->flags & LDB_FLG_SHOW_BINARY) {
use_b64_encode = false;
+ copy_raw_bytes = true;
} else if (a->flags & LDB_ATTR_FLAG_FORCE_BASE64_LDIF) {
use_b64_encode = true;
} else {
@@ -379,7 +381,7 @@ static int ldb_ldif_write_trace(struct ldb_context *ldb,
} else {
ret = fprintf_fn(private_data, "%s: ", msg->elements[i].name);
CHECK_RET;
- if (ldb->flags & LDB_FLG_SHOW_BINARY) {
+ if (copy_raw_bytes) {
ret = fprintf_fn(private_data, "%*.*s",
v.length, v.length, (char *)v.data);
} else {
--
2.19.2
From db342cca31cb0728f0da390a98aec52e961afa61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Tue, 19 Feb 2019 12:29:58 +0100
Subject: [PATCH 06/18] ldb/ldb_ldif: add LDB_FLAG_FORCE_NO_BASE64_LDIF flag
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Flag is used to enforce binary encoded attribute values per attribute.
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
lib/ldb/common/ldb_ldif.c | 4 ++++
lib/ldb/include/ldb.h | 6 ++++++
lib/ldb/pyldb.c | 1 +
3 files changed, 11 insertions(+)
diff --git a/lib/ldb/common/ldb_ldif.c b/lib/ldb/common/ldb_ldif.c
index 90bddd2f43d..56be9e6574e 100644
--- a/lib/ldb/common/ldb_ldif.c
+++ b/lib/ldb/common/ldb_ldif.c
@@ -364,6 +364,10 @@ static int ldb_ldif_write_trace(struct ldb_context *ldb,
copy_raw_bytes = true;
} else if (a->flags & LDB_ATTR_FLAG_FORCE_BASE64_LDIF) {
use_b64_encode = true;
+ } else if (msg->elements[i].flags &
+ LDB_FLAG_FORCE_NO_BASE64_LDIF) {
+ use_b64_encode = false;
+ copy_raw_bytes = true;
} else {
use_b64_encode = ldb_should_b64_encode(ldb, &v);
}
diff --git a/lib/ldb/include/ldb.h b/lib/ldb/include/ldb.h
index 81bee934da5..9349dbaa500 100644
--- a/lib/ldb/include/ldb.h
+++ b/lib/ldb/include/ldb.h
@@ -139,6 +139,12 @@ struct ldb_dn;
*/
#define LDB_FLAG_MOD_DELETE 3
+/**
+ Flag value used in ldb_ldif_write_trace() to enforce binary encoded
+ attribute values per attribute.
+*/
+#define LDB_FLAG_FORCE_NO_BASE64_LDIF 4
+
/**
flag bits on an element usable only by the internal implementation
*/
diff --git a/lib/ldb/pyldb.c b/lib/ldb/pyldb.c
index 10a4b6cb55d..e41c3b7fe2a 100644
--- a/lib/ldb/pyldb.c
+++ b/lib/ldb/pyldb.c
@@ -4323,6 +4323,7 @@ static PyObject* module_init(void)
ADD_LDB_INT(FLAG_MOD_ADD);
ADD_LDB_INT(FLAG_MOD_REPLACE);
ADD_LDB_INT(FLAG_MOD_DELETE);
+ ADD_LDB_INT(FLAG_FORCE_NO_BASE64_LDIF);
ADD_LDB_INT(ATTR_FLAG_HIDDEN);
ADD_LDB_INT(ATTR_FLAG_UNIQUE_INDEX);
--
2.19.2
From c1b3bcb6cba50073eb846922f7b3f35ebe245927 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 15 Mar 2019 15:27:36 +0100
Subject: [PATCH 07/18] ldb: version 1.6.3
* Enable make test even without lmdb
* Add LDB_FLAG_FORCE_NO_BASE64_LDIF
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
lib/ldb/ABI/ldb-1.6.3.sigs | 280 ++++++++++++++++++++++++++
lib/ldb/ABI/pyldb-util-1.6.3.sigs | 2 +
lib/ldb/ABI/pyldb-util.py3-1.6.3.sigs | 2 +
lib/ldb/wscript | 2 +-
4 files changed, 285 insertions(+), 1 deletion(-)
create mode 100644 lib/ldb/ABI/ldb-1.6.3.sigs
create mode 100644 lib/ldb/ABI/pyldb-util-1.6.3.sigs
create mode 100644 lib/ldb/ABI/pyldb-util.py3-1.6.3.sigs
diff --git a/lib/ldb/ABI/ldb-1.6.3.sigs b/lib/ldb/ABI/ldb-1.6.3.sigs
new file mode 100644
index 00000000000..0c1234f1c97
--- /dev/null
+++ b/lib/ldb/ABI/ldb-1.6.3.sigs
@@ -0,0 +1,280 @@
+ldb_add: int (struct ldb_context *, const struct ldb_message *)
+ldb_any_comparison: int (struct ldb_context *, void *, ldb_attr_handler_t, const struct ldb_val *, const struct ldb_val *)
+ldb_asprintf_errstring: void (struct ldb_context *, const char *, ...)
+ldb_attr_casefold: char *(TALLOC_CTX *, const char *)
+ldb_attr_dn: int (const char *)
+ldb_attr_in_list: int (const char * const *, const char *)
+ldb_attr_list_copy: const char **(TALLOC_CTX *, const char * const *)
+ldb_attr_list_copy_add: const char **(TALLOC_CTX *, const char * const *, const char *)
+ldb_base64_decode: int (char *)
+ldb_base64_encode: char *(TALLOC_CTX *, const char *, int)
+ldb_binary_decode: struct ldb_val (TALLOC_CTX *, const char *)
+ldb_binary_encode: char *(TALLOC_CTX *, struct ldb_val)
+ldb_binary_encode_string: char *(TALLOC_CTX *, const char *)
+ldb_build_add_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
+ldb_build_del_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
+ldb_build_extended_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const char *, void *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
+ldb_build_mod_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
+ldb_build_rename_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
+ldb_build_search_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, const char *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
+ldb_build_search_req_ex: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, struct ldb_parse_tree *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
+ldb_casefold: char *(struct ldb_context *, TALLOC_CTX *, const char *, size_t)
+ldb_casefold_default: char *(void *, TALLOC_CTX *, const char *, size_t)
+ldb_check_critical_controls: int (struct ldb_control **)
+ldb_comparison_binary: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *)
+ldb_comparison_fold: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *)
+ldb_connect: int (struct ldb_context *, const char *, unsigned int, const char **)
+ldb_control_to_string: char *(TALLOC_CTX *, const struct ldb_control *)
+ldb_controls_except_specified: struct ldb_control **(struct ldb_control **, TALLOC_CTX *, struct ldb_control *)
+ldb_debug: void (struct ldb_context *, enum ldb_debug_level, const char *, ...)
+ldb_debug_add: void (struct ldb_context *, const char *, ...)
+ldb_debug_end: void (struct ldb_context *, enum ldb_debug_level)
+ldb_debug_set: void (struct ldb_context *, enum ldb_debug_level, const char *, ...)
+ldb_delete: int (struct ldb_context *, struct ldb_dn *)
+ldb_dn_add_base: bool (struct ldb_dn *, struct ldb_dn *)
+ldb_dn_add_base_fmt: bool (struct ldb_dn *, const char *, ...)
+ldb_dn_add_child: bool (struct ldb_dn *, struct ldb_dn *)
+ldb_dn_add_child_fmt: bool (struct ldb_dn *, const char *, ...)
+ldb_dn_add_child_val: bool (struct ldb_dn *, const char *, struct ldb_val)
+ldb_dn_alloc_casefold: char *(TALLOC_CTX *, struct ldb_dn *)
+ldb_dn_alloc_linearized: char *(TALLOC_CTX *, struct ldb_dn *)
+ldb_dn_canonical_ex_string: char *(TALLOC_CTX *, struct ldb_dn *)
+ldb_dn_canonical_string: char *(TALLOC_CTX *, struct ldb_dn *)
+ldb_dn_check_local: bool (struct ldb_module *, struct ldb_dn *)
+ldb_dn_check_special: bool (struct ldb_dn *, const char *)
+ldb_dn_compare: int (struct ldb_dn *, struct ldb_dn *)
+ldb_dn_compare_base: int (struct ldb_dn *, struct ldb_dn *)
+ldb_dn_copy: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *)
+ldb_dn_escape_value: char *(TALLOC_CTX *, struct ldb_val)
+ldb_dn_extended_add_syntax: int (struct ldb_context *, unsigned int, const struct ldb_dn_extended_syntax *)
+ldb_dn_extended_filter: void (struct ldb_dn *, const char * const *)
+ldb_dn_extended_syntax_by_name: const struct ldb_dn_extended_syntax *(struct ldb_context *, const char *)
+ldb_dn_from_ldb_val: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const struct ldb_val *)
+ldb_dn_get_casefold: const char *(struct ldb_dn *)
+ldb_dn_get_comp_num: int (struct ldb_dn *)
+ldb_dn_get_component_name: const char *(struct ldb_dn *, unsigned int)
+ldb_dn_get_component_val: const struct ldb_val *(struct ldb_dn *, unsigned int)
+ldb_dn_get_extended_comp_num: int (struct ldb_dn *)
+ldb_dn_get_extended_component: const struct ldb_val *(struct ldb_dn *, const char *)
+ldb_dn_get_extended_linearized: char *(TALLOC_CTX *, struct ldb_dn *, int)
+ldb_dn_get_ldb_context: struct ldb_context *(struct ldb_dn *)
+ldb_dn_get_linearized: const char *(struct ldb_dn *)
+ldb_dn_get_parent: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *)
+ldb_dn_get_rdn_name: const char *(struct ldb_dn *)
+ldb_dn_get_rdn_val: const struct ldb_val *(struct ldb_dn *)
+ldb_dn_has_extended: bool (struct ldb_dn *)
+ldb_dn_is_null: bool (struct ldb_dn *)
+ldb_dn_is_special: bool (struct ldb_dn *)
+ldb_dn_is_valid: bool (struct ldb_dn *)
+ldb_dn_map_local: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *)
+ldb_dn_map_rebase_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *)
+ldb_dn_map_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *)
+ldb_dn_minimise: bool (struct ldb_dn *)
+ldb_dn_new: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *)
+ldb_dn_new_fmt: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *, ...)
+ldb_dn_remove_base_components: bool (struct ldb_dn *, unsigned int)
+ldb_dn_remove_child_components: bool (struct ldb_dn *, unsigned int)
+ldb_dn_remove_extended_components: void (struct ldb_dn *)
+ldb_dn_replace_components: bool (struct ldb_dn *, struct ldb_dn *)
+ldb_dn_set_component: int (struct ldb_dn *, int, const char *, const struct ldb_val)
+ldb_dn_set_extended_component: int (struct ldb_dn *, const char *, const struct ldb_val *)
+ldb_dn_update_components: int (struct ldb_dn *, const struct ldb_dn *)
+ldb_dn_validate: bool (struct ldb_dn *)
+ldb_dump_results: void (struct ldb_context *, struct ldb_result *, FILE *)
+ldb_error_at: int (struct ldb_context *, int, const char *, const char *, int)
+ldb_errstring: const char *(struct ldb_context *)
+ldb_extended: int (struct ldb_context *, const char *, void *, struct ldb_result **)
+ldb_extended_default_callback: int (struct ldb_request *, struct ldb_reply *)
+ldb_filter_from_tree: char *(TALLOC_CTX *, const struct ldb_parse_tree *)
+ldb_get_config_basedn: struct ldb_dn *(struct ldb_context *)
+ldb_get_create_perms: unsigned int (struct ldb_context *)
+ldb_get_default_basedn: struct ldb_dn *(struct ldb_context *)
+ldb_get_event_context: struct tevent_context *(struct ldb_context *)
+ldb_get_flags: unsigned int (struct ldb_context *)
+ldb_get_opaque: void *(struct ldb_context *, const char *)
+ldb_get_root_basedn: struct ldb_dn *(struct ldb_context *)
+ldb_get_schema_basedn: struct ldb_dn *(struct ldb_context *)
+ldb_global_init: int (void)
+ldb_handle_get_event_context: struct tevent_context *(struct ldb_handle *)
+ldb_handle_new: struct ldb_handle *(TALLOC_CTX *, struct ldb_context *)
+ldb_handle_use_global_event_context: void (struct ldb_handle *)
+ldb_handler_copy: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *)
+ldb_handler_fold: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *)
+ldb_init: struct ldb_context *(TALLOC_CTX *, struct tevent_context *)
+ldb_ldif_message_redacted_string: char *(struct ldb_context *, TALLOC_CTX *, enum ldb_changetype, const struct ldb_message *)
+ldb_ldif_message_string: char *(struct ldb_context *, TALLOC_CTX *, enum ldb_changetype, const struct ldb_message *)
+ldb_ldif_parse_modrdn: int (struct ldb_context *, const struct ldb_ldif *, TALLOC_CTX *, struct ldb_dn **, struct ldb_dn **, bool *, struct ldb_dn **, struct ldb_dn **)
+ldb_ldif_read: struct ldb_ldif *(struct ldb_context *, int (*)(void *), void *)
+ldb_ldif_read_file: struct ldb_ldif *(struct ldb_context *, FILE *)
+ldb_ldif_read_file_state: struct ldb_ldif *(struct ldb_context *, struct ldif_read_file_state *)
+ldb_ldif_read_free: void (struct ldb_context *, struct ldb_ldif *)
+ldb_ldif_read_string: struct ldb_ldif *(struct ldb_context *, const char **)
+ldb_ldif_write: int (struct ldb_context *, int (*)(void *, const char *, ...), void *, const struct ldb_ldif *)
+ldb_ldif_write_file: int (struct ldb_context *, FILE *, const struct ldb_ldif *)
+ldb_ldif_write_redacted_trace_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *)
+ldb_ldif_write_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *)
+ldb_load_modules: int (struct ldb_context *, const char **)
+ldb_map_add: int (struct ldb_module *, struct ldb_request *)
+ldb_map_delete: int (struct ldb_module *, struct ldb_request *)
+ldb_map_init: int (struct ldb_module *, const struct ldb_map_attribute *, const struct ldb_map_objectclass *, const char * const *, const char *, const char *)
+ldb_map_modify: int (struct ldb_module *, struct ldb_request *)
+ldb_map_rename: int (struct ldb_module *, struct ldb_request *)
+ldb_map_search: int (struct ldb_module *, struct ldb_request *)
+ldb_match_message: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, enum ldb_scope, bool *)
+ldb_match_msg: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope)
+ldb_match_msg_error: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope, bool *)
+ldb_match_msg_objectclass: int (const struct ldb_message *, const char *)
+ldb_mod_register_control: int (struct ldb_module *, const char *)
+ldb_modify: int (struct ldb_context *, const struct ldb_message *)
+ldb_modify_default_callback: int (struct ldb_request *, struct ldb_reply *)
+ldb_module_call_chain: char *(struct ldb_request *, TALLOC_CTX *)
+ldb_module_connect_backend: int (struct ldb_context *, const char *, const char **, struct ldb_module **)
+ldb_module_done: int (struct ldb_request *, struct ldb_control **, struct ldb_extended *, int)
+ldb_module_flags: uint32_t (struct ldb_context *)
+ldb_module_get_ctx: struct ldb_context *(struct ldb_module *)
+ldb_module_get_name: const char *(struct ldb_module *)
+ldb_module_get_ops: const struct ldb_module_ops *(struct ldb_module *)
+ldb_module_get_private: void *(struct ldb_module *)
+ldb_module_init_chain: int (struct ldb_context *, struct ldb_module *)
+ldb_module_load_list: int (struct ldb_context *, const char **, struct ldb_module *, struct ldb_module **)
+ldb_module_new: struct ldb_module *(TALLOC_CTX *, struct ldb_context *, const char *, const struct ldb_module_ops *)
+ldb_module_next: struct ldb_module *(struct ldb_module *)
+ldb_module_popt_options: struct poptOption **(struct ldb_context *)
+ldb_module_send_entry: int (struct ldb_request *, struct ldb_message *, struct ldb_control **)
+ldb_module_send_referral: int (struct ldb_request *, char *)
+ldb_module_set_next: void (struct ldb_module *, struct ldb_module *)
+ldb_module_set_private: void (struct ldb_module *, void *)
+ldb_modules_hook: int (struct ldb_context *, enum ldb_module_hook_type)
+ldb_modules_list_from_string: const char **(struct ldb_context *, TALLOC_CTX *, const char *)
+ldb_modules_load: int (const char *, const char *)
+ldb_msg_add: int (struct ldb_message *, const struct ldb_message_element *, int)
+ldb_msg_add_empty: int (struct ldb_message *, const char *, int, struct ldb_message_element **)
+ldb_msg_add_fmt: int (struct ldb_message *, const char *, const char *, ...)
+ldb_msg_add_linearized_dn: int (struct ldb_message *, const char *, struct ldb_dn *)
+ldb_msg_add_steal_string: int (struct ldb_message *, const char *, char *)
+ldb_msg_add_steal_value: int (struct ldb_message *, const char *, struct ldb_val *)
+ldb_msg_add_string: int (struct ldb_message *, const char *, const char *)
+ldb_msg_add_value: int (struct ldb_message *, const char *, const struct ldb_val *, struct ldb_message_element **)
+ldb_msg_canonicalize: struct ldb_message *(struct ldb_context *, const struct ldb_message *)
+ldb_msg_check_string_attribute: int (const struct ldb_message *, const char *, const char *)
+ldb_msg_copy: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *)
+ldb_msg_copy_attr: int (struct ldb_message *, const char *, const char *)
+ldb_msg_copy_shallow: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *)
+ldb_msg_diff: struct ldb_message *(struct ldb_context *, struct ldb_message *, struct ldb_message *)
+ldb_msg_difference: int (struct ldb_context *, TALLOC_CTX *, struct ldb_message *, struct ldb_message *, struct ldb_message **)
+ldb_msg_element_compare: int (struct ldb_message_element *, struct ldb_message_element *)
+ldb_msg_element_compare_name: int (struct ldb_message_element *, struct ldb_message_element *)
+ldb_msg_element_equal_ordered: bool (const struct ldb_message_element *, const struct ldb_message_element *)
+ldb_msg_find_attr_as_bool: int (const struct ldb_message *, const char *, int)
+ldb_msg_find_attr_as_dn: struct ldb_dn *(struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, const char *)
+ldb_msg_find_attr_as_double: double (const struct ldb_message *, const char *, double)
+ldb_msg_find_attr_as_int: int (const struct ldb_message *, const char *, int)
+ldb_msg_find_attr_as_int64: int64_t (const struct ldb_message *, const char *, int64_t)
+ldb_msg_find_attr_as_string: const char *(const struct ldb_message *, const char *, const char *)
+ldb_msg_find_attr_as_uint: unsigned int (const struct ldb_message *, const char *, unsigned int)
+ldb_msg_find_attr_as_uint64: uint64_t (const struct ldb_message *, const char *, uint64_t)
+ldb_msg_find_common_values: int (struct ldb_context *, TALLOC_CTX *, struct ldb_message_element *, struct ldb_message_element *, uint32_t)
+ldb_msg_find_duplicate_val: int (struct ldb_context *, TALLOC_CTX *, const struct ldb_message_element *, struct ldb_val **, uint32_t)
+ldb_msg_find_element: struct ldb_message_element *(const struct ldb_message *, const char *)
+ldb_msg_find_ldb_val: const struct ldb_val *(const struct ldb_message *, const char *)
+ldb_msg_find_val: struct ldb_val *(const struct ldb_message_element *, struct ldb_val *)
+ldb_msg_new: struct ldb_message *(TALLOC_CTX *)
+ldb_msg_normalize: int (struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_message **)
+ldb_msg_remove_attr: void (struct ldb_message *, const char *)
+ldb_msg_remove_element: void (struct ldb_message *, struct ldb_message_element *)
+ldb_msg_rename_attr: int (struct ldb_message *, const char *, const char *)
+ldb_msg_sanity_check: int (struct ldb_context *, const struct ldb_message *)
+ldb_msg_sort_elements: void (struct ldb_message *)
+ldb_next_del_trans: int (struct ldb_module *)
+ldb_next_end_trans: int (struct ldb_module *)
+ldb_next_init: int (struct ldb_module *)
+ldb_next_prepare_commit: int (struct ldb_module *)
+ldb_next_read_lock: int (struct ldb_module *)
+ldb_next_read_unlock: int (struct ldb_module *)
+ldb_next_remote_request: int (struct ldb_module *, struct ldb_request *)
+ldb_next_request: int (struct ldb_module *, struct ldb_request *)
+ldb_next_start_trans: int (struct ldb_module *)
+ldb_op_default_callback: int (struct ldb_request *, struct ldb_reply *)
+ldb_options_find: const char *(struct ldb_context *, const char **, const char *)
+ldb_pack_data: int (struct ldb_context *, const struct ldb_message *, struct ldb_val *)
+ldb_parse_control_from_string: struct ldb_control *(struct ldb_context *, TALLOC_CTX *, const char *)
+ldb_parse_control_strings: struct ldb_control **(struct ldb_context *, TALLOC_CTX *, const char **)
+ldb_parse_tree: struct ldb_parse_tree *(TALLOC_CTX *, const char *)
+ldb_parse_tree_attr_replace: void (struct ldb_parse_tree *, const char *, const char *)
+ldb_parse_tree_copy_shallow: struct ldb_parse_tree *(TALLOC_CTX *, const struct ldb_parse_tree *)
+ldb_parse_tree_walk: int (struct ldb_parse_tree *, int (*)(struct ldb_parse_tree *, void *), void *)
+ldb_qsort: void (void * const, size_t, size_t, void *, ldb_qsort_cmp_fn_t)
+ldb_register_backend: int (const char *, ldb_connect_fn, bool)
+ldb_register_extended_match_rule: int (struct ldb_context *, const struct ldb_extended_match_rule *)
+ldb_register_hook: int (ldb_hook_fn)
+ldb_register_module: int (const struct ldb_module_ops *)
+ldb_rename: int (struct ldb_context *, struct ldb_dn *, struct ldb_dn *)
+ldb_reply_add_control: int (struct ldb_reply *, const char *, bool, void *)
+ldb_reply_get_control: struct ldb_control *(struct ldb_reply *, const char *)
+ldb_req_get_custom_flags: uint32_t (struct ldb_request *)
+ldb_req_is_untrusted: bool (struct ldb_request *)
+ldb_req_location: const char *(struct ldb_request *)
+ldb_req_mark_trusted: void (struct ldb_request *)
+ldb_req_mark_untrusted: void (struct ldb_request *)
+ldb_req_set_custom_flags: void (struct ldb_request *, uint32_t)
+ldb_req_set_location: void (struct ldb_request *, const char *)
+ldb_request: int (struct ldb_context *, struct ldb_request *)
+ldb_request_add_control: int (struct ldb_request *, const char *, bool, void *)
+ldb_request_done: int (struct ldb_request *, int)
+ldb_request_get_control: struct ldb_control *(struct ldb_request *, const char *)
+ldb_request_get_status: int (struct ldb_request *)
+ldb_request_replace_control: int (struct ldb_request *, const char *, bool, void *)
+ldb_request_set_state: void (struct ldb_request *, int)
+ldb_reset_err_string: void (struct ldb_context *)
+ldb_save_controls: int (struct ldb_control *, struct ldb_request *, struct ldb_control ***)
+ldb_schema_attribute_add: int (struct ldb_context *, const char *, unsigned int, const char *)
+ldb_schema_attribute_add_with_syntax: int (struct ldb_context *, const char *, unsigned int, const struct ldb_schema_syntax *)
+ldb_schema_attribute_by_name: const struct ldb_schema_attribute *(struct ldb_context *, const char *)
+ldb_schema_attribute_fill_with_syntax: int (struct ldb_context *, TALLOC_CTX *, const char *, unsigned int, const struct ldb_schema_syntax *, struct ldb_schema_attribute *)
+ldb_schema_attribute_remove: void (struct ldb_context *, const char *)
+ldb_schema_attribute_remove_flagged: void (struct ldb_context *, unsigned int)
+ldb_schema_attribute_set_override_handler: void (struct ldb_context *, ldb_attribute_handler_override_fn_t, void *)
+ldb_schema_set_override_GUID_index: void (struct ldb_context *, const char *, const char *)
+ldb_schema_set_override_indexlist: void (struct ldb_context *, bool)
+ldb_search: int (struct ldb_context *, TALLOC_CTX *, struct ldb_result **, struct ldb_dn *, enum ldb_scope, const char * const *, const char *, ...)
+ldb_search_default_callback: int (struct ldb_request *, struct ldb_reply *)
+ldb_sequence_number: int (struct ldb_context *, enum ldb_sequence_type, uint64_t *)
+ldb_set_create_perms: void (struct ldb_context *, unsigned int)
+ldb_set_debug: int (struct ldb_context *, void (*)(void *, enum ldb_debug_level, const char *, va_list), void *)
+ldb_set_debug_stderr: int (struct ldb_context *)
+ldb_set_default_dns: void (struct ldb_context *)
+ldb_set_errstring: void (struct ldb_context *, const char *)
+ldb_set_event_context: void (struct ldb_context *, struct tevent_context *)
+ldb_set_flags: void (struct ldb_context *, unsigned int)
+ldb_set_modules_dir: void (struct ldb_context *, const char *)
+ldb_set_opaque: int (struct ldb_context *, const char *, void *)
+ldb_set_require_private_event_context: void (struct ldb_context *)
+ldb_set_timeout: int (struct ldb_context *, struct ldb_request *, int)
+ldb_set_timeout_from_prev_req: int (struct ldb_context *, struct ldb_request *, struct ldb_request *)
+ldb_set_utf8_default: void (struct ldb_context *)
+ldb_set_utf8_fns: void (struct ldb_context *, void *, char *(*)(void *, void *, const char *, size_t))
+ldb_setup_wellknown_attributes: int (struct ldb_context *)
+ldb_should_b64_encode: int (struct ldb_context *, const struct ldb_val *)
+ldb_standard_syntax_by_name: const struct ldb_schema_syntax *(struct ldb_context *, const char *)
+ldb_strerror: const char *(int)
+ldb_string_to_time: time_t (const char *)
+ldb_string_utc_to_time: time_t (const char *)
+ldb_timestring: char *(TALLOC_CTX *, time_t)
+ldb_timestring_utc: char *(TALLOC_CTX *, time_t)
+ldb_transaction_cancel: int (struct ldb_context *)
+ldb_transaction_cancel_noerr: int (struct ldb_context *)
+ldb_transaction_commit: int (struct ldb_context *)
+ldb_transaction_prepare_commit: int (struct ldb_context *)
+ldb_transaction_start: int (struct ldb_context *)
+ldb_unpack_data: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *)
+ldb_unpack_data_only_attr_list: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *, const char * const *, unsigned int, unsigned int *)
+ldb_unpack_data_only_attr_list_flags: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *, const char * const *, unsigned int, unsigned int, unsigned int *)
+ldb_val_dup: struct ldb_val (TALLOC_CTX *, const struct ldb_val *)
+ldb_val_equal_exact: int (const struct ldb_val *, const struct ldb_val *)
+ldb_val_map_local: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *)
+ldb_val_map_remote: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *)
+ldb_val_string_cmp: int (const struct ldb_val *, const char *)
+ldb_val_to_time: int (const struct ldb_val *, time_t *)
+ldb_valid_attr_name: int (const char *)
+ldb_vdebug: void (struct ldb_context *, enum ldb_debug_level, const char *, va_list)
+ldb_wait: int (struct ldb_handle *, enum ldb_wait_type)
diff --git a/lib/ldb/ABI/pyldb-util-1.6.3.sigs b/lib/ldb/ABI/pyldb-util-1.6.3.sigs
new file mode 100644
index 00000000000..74d6719d2bc
--- /dev/null
+++ b/lib/ldb/ABI/pyldb-util-1.6.3.sigs
@@ -0,0 +1,2 @@
+pyldb_Dn_FromDn: PyObject *(struct ldb_dn *)
+pyldb_Object_AsDn: bool (TALLOC_CTX *, PyObject *, struct ldb_context *, struct ldb_dn **)
diff --git a/lib/ldb/ABI/pyldb-util.py3-1.6.3.sigs b/lib/ldb/ABI/pyldb-util.py3-1.6.3.sigs
new file mode 100644
index 00000000000..74d6719d2bc
--- /dev/null
+++ b/lib/ldb/ABI/pyldb-util.py3-1.6.3.sigs
@@ -0,0 +1,2 @@
+pyldb_Dn_FromDn: PyObject *(struct ldb_dn *)
+pyldb_Object_AsDn: bool (TALLOC_CTX *, PyObject *, struct ldb_context *, struct ldb_dn **)
diff --git a/lib/ldb/wscript b/lib/ldb/wscript
index 8f14b09b583..f48724e4d6c 100644
--- a/lib/ldb/wscript
+++ b/lib/ldb/wscript
@@ -1,7 +1,7 @@
#!/usr/bin/env python
APPNAME = 'ldb'
-VERSION = '1.6.2'
+VERSION = '1.6.3'
import sys, os
--
2.19.2
From 166d3c34d492bef977f80013e336f4c5e52eaa83 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Thu, 14 Mar 2019 14:04:28 +0100
Subject: [PATCH 08/18] samba-tool tests: add additional tests for "samba-tool
user edit" command
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Especially test handling of base64 encoded attribute values here.
Add selftest/knownfail.d/samba_tool.user_edit.
Tests fail, because:
- can not work with ldif without a trailing new line
- can not handle base64 strings
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/tests/samba_tool/user_edit.sh | 97 ++++++++++++++++++++++
selftest/knownfail.d/samba_tool.user_edit | 3 +
2 files changed, 100 insertions(+)
create mode 100644 selftest/knownfail.d/samba_tool.user_edit
diff --git a/python/samba/tests/samba_tool/user_edit.sh b/python/samba/tests/samba_tool/user_edit.sh
index 166d45ae4d6..0535efedbdd 100755
--- a/python/samba/tests/samba_tool/user_edit.sh
+++ b/python/samba/tests/samba_tool/user_edit.sh
@@ -16,6 +16,13 @@ PASSWORD="$3"
STpath=$(pwd)
. $STpath/testprogs/blackbox/subunit.sh
+display_name="Björn"
+display_name_b64="QmrDtnJu"
+display_name_new="Renamed Bjoern"
+# attribute value including control character
+# echo -e "test \a string" | base64
+display_name_con_b64="dGVzdCAHIHN0cmluZwo="
+
tmpeditor=$(mktemp --suffix .sh -p $STpath/bin samba-tool-editor-XXXXXXXX)
chmod +x $tmpeditor
@@ -39,6 +46,89 @@ EOF
-H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
}
+# Test edit user - add base64 attributes
+add_attribute_base64() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+user_ldif="\$1"
+
+grep -v '^$' \$user_ldif > \${user_ldif}.tmp
+echo "displayName:: $display_name_b64" >> \${user_ldif}.tmp
+
+mv \${user_ldif}.tmp \$user_ldif
+EOF
+
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool user edit \
+ sambatool1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool user show \
+ sambatool1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+delete_attribute() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+user_ldif="\$1"
+
+grep -v '^displayName' \$user_ldif >> \${user_ldif}.tmp
+mv \${user_ldif}.tmp \$user_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool user edit \
+ sambatool1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+# Test edit user - add base64 attribute value including control character
+add_attribute_base64_control() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+user_ldif="\$1"
+
+grep -v '^$' \$user_ldif > \${user_ldif}.tmp
+echo "displayName:: $display_name_con_b64" >> \${user_ldif}.tmp
+
+mv \${user_ldif}.tmp \$user_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool user edit \
+ sambatool1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64_control() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool user show \
+ sambatool1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+
+# Test edit user - change base64 attribute value including control character
+change_attribute_base64_control() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+user_ldif="\$1"
+
+sed -i -e 's/displayName:: $display_name_con_b64/displayName: $display_name/' \
+ \$user_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool user edit \
+ sambatool1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64_control() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool user show \
+ sambatool1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
delete_user() {
$PYTHON ${STpath}/source4/scripting/bin/samba-tool \
user delete sambatool1 \
@@ -49,6 +139,13 @@ failed=0
testit "create_test_user" create_test_user || failed=`expr $failed + 1`
testit "edit_user" edit_user || failed=`expr $failed + 1`
+testit "add_attribute_base64" add_attribute_base64 || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64" "^displayName:: $display_name_b64" get_attribute_base64 || failed=`expr $failed + 1`
+testit "delete_attribute" delete_attribute || failed=`expr $failed + 1`
+testit "add_attribute_base64_control" add_attribute_base64_control || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64_control" "^displayName:: $display_name_con_b64" get_attribute_base64_control || failed=`expr $failed + 1`
+testit "change_attribute_base64_control" change_attribute_base64_control || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64_control" "^displayName:: $display_name_b64" get_attribute_base64_control || failed=`expr $failed + 1`
testit "delete_user" delete_user || failed=`expr $failed + 1`
rm -f $tmpeditor
diff --git a/selftest/knownfail.d/samba_tool.user_edit b/selftest/knownfail.d/samba_tool.user_edit
new file mode 100644
index 00000000000..46a1f2b5853
--- /dev/null
+++ b/selftest/knownfail.d/samba_tool.user_edit
@@ -0,0 +1,3 @@
+samba.tests.samba_tool.user_edit.add_attribute_base64
+samba.tests.samba_tool.user_edit.add_attribute_base64_control
+samba.tests.samba_tool.user_edit.change_attribute_base64_control
--
2.19.2
From 0559f11c71474a0fbe3c057cd63d7a5427a0fb46 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Fri, 15 Mar 2019 14:19:35 +0100
Subject: [PATCH 09/18] samba-tool user edit: use ldb methods to create ldif to
modify user
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Remove tests from knownfail:
samba.tests.samba_tool.user_edit.add_attribute_base64
samba.tests.samba_tool.user_edit.add_attribute_base64_control
samba.tests.samba_tool.user_edit.change_attribute_base64_control
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/netcmd/user.py | 51 ++++++-----------------
selftest/knownfail.d/samba_tool.user_edit | 3 --
2 files changed, 13 insertions(+), 41 deletions(-)
delete mode 100644 selftest/knownfail.d/samba_tool.user_edit
diff --git a/python/samba/netcmd/user.py b/python/samba/netcmd/user.py
index a64d2176dfa..28ff617f12b 100644
--- a/python/samba/netcmd/user.py
+++ b/python/samba/netcmd/user.py
@@ -2467,46 +2467,21 @@ LDAP server using the 'nano' editor.
with open(t_file.name) as edited_file:
edited_message = edited_file.read()
- if result_ldif != edited_message:
- diff = difflib.ndiff(result_ldif.splitlines(),
- edited_message.splitlines())
- minus_lines = []
- plus_lines = []
- for line in diff:
- if line.startswith('-'):
- line = line[2:]
- minus_lines.append(line)
- elif line.startswith('+'):
- line = line[2:]
- plus_lines.append(line)
-
- user_ldif = "dn: %s\n" % user_dn
- user_ldif += "changetype: modify\n"
-
- for line in minus_lines:
- attr, val = line.split(':', 1)
- search_attr = "%s:" % attr
- if not re.search(r'^' + search_attr, str(plus_lines)):
- user_ldif += "delete: %s\n" % attr
- user_ldif += "%s: %s\n" % (attr, val)
-
- for line in plus_lines:
- attr, val = line.split(':', 1)
- search_attr = "%s:" % attr
- if re.search(r'^' + search_attr, str(minus_lines)):
- user_ldif += "replace: %s\n" % attr
- user_ldif += "%s: %s\n" % (attr, val)
- if not re.search(r'^' + search_attr, str(minus_lines)):
- user_ldif += "add: %s\n" % attr
- user_ldif += "%s: %s\n" % (attr, val)
- try:
- samdb.modify_ldif(user_ldif)
- except Exception as e:
- raise CommandError("Failed to modify user '%s': " %
- username, e)
+ 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 user '%s': " % username, e)
- self.outf.write("Modified User '%s' successfully\n" % username)
+ self.outf.write("Modified User '%s' successfully\n" % username)
class cmd_user_show(Command):
diff --git a/selftest/knownfail.d/samba_tool.user_edit b/selftest/knownfail.d/samba_tool.user_edit
deleted file mode 100644
index 46a1f2b5853..00000000000
--- a/selftest/knownfail.d/samba_tool.user_edit
+++ /dev/null
@@ -1,3 +0,0 @@
-samba.tests.samba_tool.user_edit.add_attribute_base64
-samba.tests.samba_tool.user_edit.add_attribute_base64_control
-samba.tests.samba_tool.user_edit.change_attribute_base64_control
--
2.19.2
From bd2113e2017245bd4f06113dc9a28ef49dbff8be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Wed, 13 Mar 2019 17:40:37 +0100
Subject: [PATCH 10/18] samba-tool user edit: simplify code
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Use "None"-changetype here, instead of "Add". This avoids the need to
remove the changetype line afterwards.
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/netcmd/user.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/python/samba/netcmd/user.py b/python/samba/netcmd/user.py
index 28ff617f12b..112756ea4f5 100644
--- a/python/samba/netcmd/user.py
+++ b/python/samba/netcmd/user.py
@@ -2448,9 +2448,7 @@ LDAP server using the 'nano' editor.
raise CommandError('Unable to find user "%s"' % (username))
for msg in res:
- r_ldif = samdb.write_ldif(msg, 1)
- # remove 'changetype' line
- result_ldif = re.sub('changetype: add\n', '', r_ldif)
+ result_ldif = samdb.write_ldif(msg, ldb.CHANGETYPE_NONE)
if editor is None:
editor = os.environ.get('EDITOR')
--
2.19.2
From e451c363f49841a0a0f8fa57d4a0cbdb9bdd364c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Fri, 15 Mar 2019 12:59:09 +0100
Subject: [PATCH 11/18] samba-tool tests: add test for 'samba-tool user edit',
using LDB_FLAG_FORCE_NO_BASE64_LDIF
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Test to edit a user: Change attributes with LDB_FLAG_FORCE_NO_BASE64_LDIF
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/tests/samba_tool/user_edit.sh | 26 ++++++++++++++++++++++
selftest/knownfail.d/samba_tool.user_edit | 1 +
2 files changed, 27 insertions(+)
create mode 100644 selftest/knownfail.d/samba_tool.user_edit
diff --git a/python/samba/tests/samba_tool/user_edit.sh b/python/samba/tests/samba_tool/user_edit.sh
index 0535efedbdd..03fbd61ff5d 100755
--- a/python/samba/tests/samba_tool/user_edit.sh
+++ b/python/samba/tests/samba_tool/user_edit.sh
@@ -129,6 +129,30 @@ get_attribute_base64_control() {
-H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
}
+# Test edit user - change attributes with LDB_FLAG_FORCE_NO_BASE64_LDIF
+change_attribute_force_no_base64() {
+ # create editor.sh
+ # Expects that the original attribute is available as clear text,
+ # because the LDB_FLAG_FORCE_NO_BASE64_LDIF should be used here.
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+user_ldif="\$1"
+
+sed -i -e 's/displayName: $display_name/displayName: $display_name_new/' \
+ \$user_ldif
+EOF
+
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool user edit \
+ sambatool1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_changed_attribute_force_no_base64() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool user show \
+ sambatool1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
delete_user() {
$PYTHON ${STpath}/source4/scripting/bin/samba-tool \
user delete sambatool1 \
@@ -146,6 +170,8 @@ testit "add_attribute_base64_control" add_attribute_base64_control || failed=`ex
testit_grep "get_attribute_base64_control" "^displayName:: $display_name_con_b64" get_attribute_base64_control || failed=`expr $failed + 1`
testit "change_attribute_base64_control" change_attribute_base64_control || failed=`expr $failed + 1`
testit_grep "get_attribute_base64_control" "^displayName:: $display_name_b64" get_attribute_base64_control || failed=`expr $failed + 1`
+testit "change_attribute_force_no_base64" change_attribute_force_no_base64 || failed=`expr $failed + 1`
+testit_grep "get_changed_attribute_force_no_base64" "^displayName: $display_name_new" get_changed_attribute_force_no_base64 || failed=`expr $failed + 1`
testit "delete_user" delete_user || failed=`expr $failed + 1`
rm -f $tmpeditor
diff --git a/selftest/knownfail.d/samba_tool.user_edit b/selftest/knownfail.d/samba_tool.user_edit
new file mode 100644
index 00000000000..5c5c9a6d781
--- /dev/null
+++ b/selftest/knownfail.d/samba_tool.user_edit
@@ -0,0 +1 @@
+samba.tests.samba_tool.user_edit.change_attribute_force_no_base64
--
2.19.2
From 9c66b0c2408c4d7df1a02fbeb045f0ab72ed9729 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Fri, 15 Mar 2019 14:20:05 +0100
Subject: [PATCH 12/18] samba-tool user edit: avoid base64 encoded strings in
editable ldif if possible
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Use clear text arguments strings if possible. Makes it more comfortable
for users to edit the user objects attributes.
Remove test from knownfail:
samba.tests.samba_tool.user_edit.change_attribute_force_no_base64
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/netcmd/common.py | 45 ++++++++++++++++++++++-
python/samba/netcmd/user.py | 3 +-
selftest/knownfail.d/samba_tool.user_edit | 1 -
3 files changed, 46 insertions(+), 3 deletions(-)
delete mode 100644 selftest/knownfail.d/samba_tool.user_edit
diff --git a/python/samba/netcmd/common.py b/python/samba/netcmd/common.py
index c68cbabf42e..664d3a83ac5 100644
--- a/python/samba/netcmd/common.py
+++ b/python/samba/netcmd/common.py
@@ -20,7 +20,7 @@
import re
from samba.dcerpc import nbt
from samba.net import Net
-
+import ldb
def _get_user_realm_domain(user):
r""" get the realm or the domain and the base user
@@ -69,3 +69,46 @@ def netcmd_get_domain_infos_via_cldap(lp, creds, address=None):
cldap_ret = net.finddc(address=address,
flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
return cldap_ret
+
+def is_printable_attr_val(val):
+ import unicodedata
+
+ # The value must be convertable to a string value.
+ try:
+ str_val = str(val)
+ except:
+ return False
+
+ # Characters of the Unicode Character Category "C" ("Other") are
+ # supposed to be not printable. The category "C" includes control
+ # characters, format specifier and others.
+ for c in str_val:
+ if unicodedata.category(c)[0] == 'C':
+ return False
+
+ return True
+
+def get_ldif_for_editor(samdb, msg):
+
+ # Copy the given message, because we do not
+ # want to modify the original message.
+ m = ldb.Message()
+ m.dn = msg.dn
+
+ for k in msg.keys():
+ if k == "dn":
+ continue
+ vals = msg[k]
+ m[k] = vals
+ need_base64 = False
+ for v in vals:
+ if is_printable_attr_val(v):
+ continue
+ need_base64 = True
+ break
+ if not need_base64:
+ m[k].set_flags(ldb.FLAG_FORCE_NO_BASE64_LDIF)
+
+ result_ldif = samdb.write_ldif(m, ldb.CHANGETYPE_NONE)
+
+ return result_ldif
diff --git a/python/samba/netcmd/user.py b/python/samba/netcmd/user.py
index 112756ea4f5..121050a26e6 100644
--- a/python/samba/netcmd/user.py
+++ b/python/samba/netcmd/user.py
@@ -2428,6 +2428,7 @@ LDAP server using the 'nano' editor.
def run(self, username, 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)
@@ -2448,7 +2449,7 @@ LDAP server using the 'nano' editor.
raise CommandError('Unable to find user "%s"' % (username))
for msg in res:
- result_ldif = samdb.write_ldif(msg, ldb.CHANGETYPE_NONE)
+ result_ldif = common.get_ldif_for_editor(samdb, msg)
if editor is None:
editor = os.environ.get('EDITOR')
diff --git a/selftest/knownfail.d/samba_tool.user_edit b/selftest/knownfail.d/samba_tool.user_edit
deleted file mode 100644
index 5c5c9a6d781..00000000000
--- a/selftest/knownfail.d/samba_tool.user_edit
+++ /dev/null
@@ -1 +0,0 @@
-samba.tests.samba_tool.user_edit.change_attribute_force_no_base64
--
2.19.2
From 8ef8da57cfdc766a0f6fcadec224f855f14f6023 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Wed, 13 Mar 2019 21:40:25 +0100
Subject: [PATCH 13/18] 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 <bbaumbach at samba.org>
---
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 <computername> [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()
--
2.19.2
From 299f81143c376c39ab2fdf64b45c9b8800da8140 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Mon, 18 Mar 2019 12:00:24 +0100
Subject: [PATCH 14/18] samba-tool tests: add test for 'samba-tool computer
edit' command
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
.../samba/tests/samba_tool/computer_edit.sh | 180 ++++++++++++++++++
source4/selftest/tests.py | 3 +-
2 files changed, 182 insertions(+), 1 deletion(-)
create mode 100755 python/samba/tests/samba_tool/computer_edit.sh
diff --git a/python/samba/tests/samba_tool/computer_edit.sh b/python/samba/tests/samba_tool/computer_edit.sh
new file mode 100755
index 00000000000..fb6c668f2a3
--- /dev/null
+++ b/python/samba/tests/samba_tool/computer_edit.sh
@@ -0,0 +1,180 @@
+#!/bin/sh
+#
+# Test for 'samba-tool computer edit'
+
+if [ $# -lt 3 ]; then
+cat <<EOF
+Usage: computer_edit.sh SERVER USERNAME PASSWORD
+EOF
+exit 1;
+fi
+
+SERVER="$1"
+USERNAME="$2"
+PASSWORD="$3"
+
+STpath=$(pwd)
+. $STpath/testprogs/blackbox/subunit.sh
+
+display_name="Björns laptop"
+display_name_b64="QmrDtnJucyBsYXB0b3A="
+display_name_new="Bjoerns new laptop"
+# attribute value including control character
+# echo -e "test \a string" | base64
+display_name_con_b64="dGVzdCAHIHN0cmluZwo="
+
+tmpeditor=$(mktemp --suffix .sh -p $STpath/bin samba-tool-editor-XXXXXXXX)
+chmod +x $tmpeditor
+
+create_test_computer() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ computer create testmachine1 \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+edit_computer() {
+ # create editor.sh
+ # enable computer account
+ cat >$tmpeditor <<-'EOF'
+#!/usr/bin/env bash
+computer_ldif="$1"
+SED=$(which sed)
+$SED -i -e 's/userAccountControl: 4098/userAccountControl: 4096/' $computer_ldif
+EOF
+
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ computer edit testmachine1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+# Test edit computer - add base64 attributes
+add_attribute_base64() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+computer_ldif="\$1"
+
+grep -v '^$' \$computer_ldif > \${computer_ldif}.tmp
+echo "displayName:: $display_name_b64" >> \${computer_ldif}.tmp
+
+mv \${computer_ldif}.tmp \$computer_ldif
+EOF
+
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool computer edit \
+ testmachine1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool computer show \
+ testmachine1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+delete_attribute() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+computer_ldif="\$1"
+
+grep -v '^displayName' \$computer_ldif >> \${computer_ldif}.tmp
+mv \${computer_ldif}.tmp \$computer_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool computer edit \
+ testmachine1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+# Test edit computer - add base64 attribute value including control character
+add_attribute_base64_control() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+computer_ldif="\$1"
+
+grep -v '^$' \$computer_ldif > \${computer_ldif}.tmp
+echo "displayName:: $display_name_con_b64" >> \${computer_ldif}.tmp
+
+mv \${computer_ldif}.tmp \$computer_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool computer edit \
+ testmachine1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64_control() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool computer show \
+ testmachine1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+
+# Test edit computer - change base64 attribute value including control character
+change_attribute_base64_control() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+computer_ldif="\$1"
+
+sed -i -e 's/displayName:: $display_name_con_b64/displayName: $display_name/' \
+ \$computer_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool computer edit \
+ testmachine1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64_control() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool computer show \
+ testmachine1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+# Test edit computer - change attributes with LDB_FLAG_FORCE_NO_BASE64_LDIF
+change_attribute_force_no_base64() {
+ # create editor.sh
+ # Expects that the original attribute is available as clear text,
+ # because the LDB_FLAG_FORCE_NO_BASE64_LDIF should be used here.
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+computer_ldif="\$1"
+
+sed -i -e 's/displayName: $display_name/displayName: $display_name_new/' \
+ \$computer_ldif
+EOF
+
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool computer edit \
+ testmachine1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_changed_attribute_force_no_base64() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool computer show \
+ testmachine1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+delete_computer() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ computer delete testmachine1 \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+failed=0
+
+testit "create_test_computer" create_test_computer || failed=`expr $failed + 1`
+testit "edit_computer" edit_computer || failed=`expr $failed + 1`
+testit "add_attribute_base64" add_attribute_base64 || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64" "^displayName:: $display_name_b64" get_attribute_base64 || failed=`expr $failed + 1`
+testit "delete_attribute" delete_attribute || failed=`expr $failed + 1`
+testit "add_attribute_base64_control" add_attribute_base64_control || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64_control" "^displayName:: $display_name_con_b64" get_attribute_base64_control || failed=`expr $failed + 1`
+testit "change_attribute_base64_control" change_attribute_base64_control || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64_control" "^displayName:: $display_name_b64" get_attribute_base64_control || failed=`expr $failed + 1`
+testit "change_attribute_force_no_base64" change_attribute_force_no_base64 || failed=`expr $failed + 1`
+testit_grep "get_changed_attribute_force_no_base64" "^displayName: $display_name_new" get_changed_attribute_force_no_base64 || failed=`expr $failed + 1`
+testit "delete_computer" delete_computer || failed=`expr $failed + 1`
+
+rm -f $tmpeditor
+
+exit $failed
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index b0c4ce581b3..ed4d695f78f 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -654,10 +654,11 @@ planpythontestsuite("none", "samba.tests.samba_tool.visualize", py3_compatible=T
for env in all_fl_envs:
planpythontestsuite(env + ":local", "samba.tests.samba_tool.fsmo", py3_compatible=True)
-# test user.edit
+# test samba-tool user and computer edit command
for env in all_fl_envs:
env += ":local"
plantestsuite("samba.tests.samba_tool.user_edit", env, [os.path.join(srcdir(), "python/samba/tests/samba_tool/user_edit.sh"), '$SERVER', '$USERNAME', '$PASSWORD'])
+ plantestsuite("samba.tests.samba_tool.computer_edit", env, [os.path.join(srcdir(), "python/samba/tests/samba_tool/computer_edit.sh"), '$SERVER', '$USERNAME', '$PASSWORD'])
# We run this test against both AD DC implementations because it is
# the only test we have of GPO get/set behaviour, and this involves
--
2.19.2
From 5a6468ffe10e5f0e8cdbb426076a8f6a652d59db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Wed, 13 Mar 2019 21:20:29 +0100
Subject: [PATCH 15/18] samba-tool group: add 'edit' command to edit an AD
group object
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Same like the samba-tool user edit command.
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/netcmd/group.py | 114 +++++++++++++++++++++++++++++++++++
1 file changed, 114 insertions(+)
diff --git a/python/samba/netcmd/group.py b/python/samba/netcmd/group.py
index 6976f82d132..536c1cba613 100644
--- a/python/samba/netcmd/group.py
+++ b/python/samba/netcmd/group.py
@@ -35,6 +35,10 @@ from samba.dsdb import (
GTYPE_DISTRIBUTION_UNIVERSAL_GROUP,
)
from collections import defaultdict
+from subprocess import check_call, CalledProcessError
+from samba.compat import get_bytes
+import os
+import tempfile
security_group = dict({"Builtin": GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
"Domain": GTYPE_SECURITY_DOMAIN_LOCAL_GROUP,
@@ -691,12 +695,122 @@ class cmd_group_stats(Command):
self.outf.write("\n* Note this does not include nested group memberships\n")
+class cmd_group_edit(Command):
+ """Modify Group AD object.
+
+ This command will allow editing of a group account 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.
+
+ Example1:
+ samba-tool group edit Group1 -H ldap://samba.samdom.example.com \\
+ -U administrator --password=passw1rd
+
+ Example1 shows how to edit a groups attributes in the domain against a
+ remote LDAP server.
+
+ The -H parameter is used to specify the remote target server.
+
+ Example2:
+ samba-tool group edit Group2
+
+ Example2 shows how to edit a groups attributes in the domain against a local
+ server.
+
+ Example3:
+ samba-tool group edit Group3 --editor=nano
+
+ Example3 shows how to edit a groups attributes in the domain against a local
+ 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,
+ 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)
+
+ filter = ("(&(sAMAccountName=%s)(objectClass=group))" % groupname)
+
+ domaindn = samdb.domain_dn()
+
+ try:
+ res = samdb.search(base=domaindn,
+ expression=filter,
+ scope=ldb.SCOPE_SUBTREE)
+ group_dn = res[0].dn
+ except IndexError:
+ raise CommandError('Unable to find group "%s"' % (groupname))
+
+ if len(res) != 1:
+ raise CommandError('Invalid number of results: for "%s": %d' %
+ ((groupname), 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 group '%s': " % groupname, e)
+
+ self.outf.write("Modified group '%s' successfully\n" % groupname)
+
+
class cmd_group(SuperCommand):
"""Group management."""
subcommands = {}
subcommands["add"] = cmd_group_add()
subcommands["delete"] = cmd_group_delete()
+ subcommands["edit"] = cmd_group_edit()
subcommands["addmembers"] = cmd_group_add_members()
subcommands["removemembers"] = cmd_group_remove_members()
subcommands["list"] = cmd_group_list()
--
2.19.2
From fae029ed7fa0c8ff2fb55c3cd94a6d164793bfca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Mon, 18 Mar 2019 13:31:04 +0100
Subject: [PATCH 16/18] samba-tool tests: add test for 'samba-tool group edit'
command
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/tests/samba_tool/group_edit.sh | 208 ++++++++++++++++++++
source4/selftest/tests.py | 3 +-
2 files changed, 210 insertions(+), 1 deletion(-)
create mode 100755 python/samba/tests/samba_tool/group_edit.sh
diff --git a/python/samba/tests/samba_tool/group_edit.sh b/python/samba/tests/samba_tool/group_edit.sh
new file mode 100755
index 00000000000..90f5252d926
--- /dev/null
+++ b/python/samba/tests/samba_tool/group_edit.sh
@@ -0,0 +1,208 @@
+#!/bin/sh
+#
+# Test for 'samba-tool group edit'
+
+if [ $# -lt 3 ]; then
+cat <<EOF
+Usage: group_edit.sh SERVER USERNAME PASSWORD
+EOF
+exit 1;
+fi
+
+SERVER="$1"
+USERNAME="$2"
+PASSWORD="$3"
+
+STpath=$(pwd)
+. $STpath/testprogs/blackbox/subunit.sh
+
+display_name="Users in Göttingen"
+display_name_b64="VXNlcnMgaW4gR8O2dHRpbmdlbg=="
+display_name_new="Users in Goettingen"
+# attribute value including control character
+# echo -e "test \a string" | base64
+display_name_con_b64="dGVzdCAHIHN0cmluZwo="
+
+tmpeditor=$(mktemp --suffix .sh -p $STpath/bin samba-tool-editor-XXXXXXXX)
+chmod +x $tmpeditor
+
+create_test_group() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ group add testgroup1 \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+delete_test_group() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ group delete testgroup1 \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+create_test_user() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ user create testuser1 --random-password \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+delete_test_user() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ user delete testuser1 \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+add_member() {
+ user_dn=$($PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ user show testuser1 --attributes=dn \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD" | \
+ grep ^dn: | cut -d' ' -f2)
+
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+group_ldif="\$1"
+
+grep -v '^$' \$group_ldif > \${group_ldif}.tmp
+echo "member: $user_dn" >> \${group_ldif}.tmp
+
+mv \${group_ldif}.tmp \$group_ldif
+EOF
+
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ group edit testgroup1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_member() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ group listmembers testgroup1 \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+# Test edit group - add base64 attributes
+add_attribute_base64() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+group_ldif="\$1"
+
+grep -v '^$' \$group_ldif > \${group_ldif}.tmp
+echo "displayName:: $display_name_b64" >> \${group_ldif}.tmp
+
+mv \${group_ldif}.tmp \$group_ldif
+EOF
+
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool group edit \
+ testgroup1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool group show \
+ testgroup1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+delete_attribute() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+group_ldif="\$1"
+
+grep -v '^displayName' \$group_ldif >> \${group_ldif}.tmp
+mv \${group_ldif}.tmp \$group_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool group edit \
+ testgroup1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+# Test edit group - add base64 attribute value including control character
+add_attribute_base64_control() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+group_ldif="\$1"
+
+grep -v '^$' \$group_ldif > \${group_ldif}.tmp
+echo "displayName:: $display_name_con_b64" >> \${group_ldif}.tmp
+
+mv \${group_ldif}.tmp \$group_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool group edit \
+ testgroup1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64_control() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool group show \
+ testgroup1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+
+# Test edit group - change base64 attribute value including control character
+change_attribute_base64_control() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+group_ldif="\$1"
+
+sed -i -e 's/displayName:: $display_name_con_b64/displayName: $display_name/' \
+ \$group_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool group edit \
+ testgroup1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64_control() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool group show \
+ testgroup1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+# Test edit group - change attributes with LDB_FLAG_FORCE_NO_BASE64_LDIF
+change_attribute_force_no_base64() {
+ # create editor.sh
+ # Expects that the original attribute is available as clear text,
+ # because the LDB_FLAG_FORCE_NO_BASE64_LDIF should be used here.
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+group_ldif="\$1"
+
+sed -i -e 's/displayName: $display_name/displayName: $display_name_new/' \
+ \$group_ldif
+EOF
+
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool group edit \
+ testgroup1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_changed_attribute_force_no_base64() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool group show \
+ testgroup1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+failed=0
+
+testit "create_test_group" create_test_group || failed=`expr $failed + 1`
+testit "create_test_user" create_test_user || failed=`expr $failed + 1`
+testit "add_member" add_member || failed=`expr $failed + 1`
+testit_grep "get_member" "^testuser1" get_member || failed=`expr $failed + 1`
+testit "add_attribute_base64" add_attribute_base64 || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64" "^displayName:: $display_name_b64" get_attribute_base64 || failed=`expr $failed + 1`
+testit "delete_attribute" delete_attribute || failed=`expr $failed + 1`
+testit "add_attribute_base64_control" add_attribute_base64_control || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64_control" "^displayName:: $display_name_con_b64" get_attribute_base64_control || failed=`expr $failed + 1`
+testit "change_attribute_base64_control" change_attribute_base64_control || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64_control" "^displayName:: $display_name_b64" get_attribute_base64_control || failed=`expr $failed + 1`
+testit "change_attribute_force_no_base64" change_attribute_force_no_base64 || failed=`expr $failed + 1`
+testit_grep "get_changed_attribute_force_no_base64" "^displayName: $display_name_new" get_changed_attribute_force_no_base64 || failed=`expr $failed + 1`
+testit "delete_test_group" delete_test_group || failed=`expr $failed + 1`
+testit "delete_test_user" delete_test_user || failed=`expr $failed + 1`
+
+rm -f $tmpeditor
+
+exit $failed
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index ed4d695f78f..cc2410537b4 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -654,10 +654,11 @@ planpythontestsuite("none", "samba.tests.samba_tool.visualize", py3_compatible=T
for env in all_fl_envs:
planpythontestsuite(env + ":local", "samba.tests.samba_tool.fsmo", py3_compatible=True)
-# test samba-tool user and computer edit command
+# test samba-tool user, group and computer edit command
for env in all_fl_envs:
env += ":local"
plantestsuite("samba.tests.samba_tool.user_edit", env, [os.path.join(srcdir(), "python/samba/tests/samba_tool/user_edit.sh"), '$SERVER', '$USERNAME', '$PASSWORD'])
+ plantestsuite("samba.tests.samba_tool.group_edit", env, [os.path.join(srcdir(), "python/samba/tests/samba_tool/group_edit.sh"), '$SERVER', '$USERNAME', '$PASSWORD'])
plantestsuite("samba.tests.samba_tool.computer_edit", env, [os.path.join(srcdir(), "python/samba/tests/samba_tool/computer_edit.sh"), '$SERVER', '$USERNAME', '$PASSWORD'])
# We run this test against both AD DC implementations because it is
--
2.19.2
From 04282aebfd8850872cf2f7fa56e58bc596f99c47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Tue, 19 Mar 2019 17:55:37 +0100
Subject: [PATCH 17/18] samba-tool: implement contact management commands
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Usage: samba-tool contact <subcommand>
Contact management.
Available subcommands:
create - Create a new contact.
delete - Delete a contact.
edit - Modify a contact.
list - List all contacts.
move - Move a contact object to an organizational unit or container.
show - Display a contact.
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/netcmd/contact.py | 687 +++++++++++++++++++++++++++++++++
python/samba/netcmd/main.py | 1 +
python/samba/samdb.py | 108 ++++++
3 files changed, 796 insertions(+)
create mode 100644 python/samba/netcmd/contact.py
diff --git a/python/samba/netcmd/contact.py b/python/samba/netcmd/contact.py
new file mode 100644
index 00000000000..9ed2f9f8d2c
--- /dev/null
+++ b/python/samba/netcmd/contact.py
@@ -0,0 +1,687 @@
+# samba-tool contact management
+#
+# Copyright Björn Baumbach 2019 <bbaumbach at samba.org>
+#
+# 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
+import ldb
+import os
+import tempfile
+from subprocess import check_call, CalledProcessError
+from operator import attrgetter
+from samba.auth import system_session
+from samba.samdb import SamDB
+from samba import (
+ credentials,
+ dsdb,
+)
+from samba.net import Net
+
+from samba.netcmd import (
+ Command,
+ CommandError,
+ SuperCommand,
+ Option,
+)
+from samba.compat import get_bytes
+
+
+class cmd_create(Command):
+ """Create a new contact.
+
+ This command creates a new contact in the Active Directory domain.
+
+ The name of the new contact can be specified by the first argument
+ 'contactname' or the --given-name, --initial and --surname arugments.
+ If no 'contactname' is given, contact's name will be made up of the given
+ arguments by combining the given-name, initials and surname. Each argument
+ is optional. A dot ('.') will be appended to the initials automatically.
+
+ Example1:
+ samba-tool contact create "James T. Kirk" --job-title=Captain \\
+ -H ldap://samba.samdom.example.com -UAdministrator%Passw1rd
+
+ The example shows how to create a new contact in the domain against a remote
+ LDAP server.
+
+ Example2:
+ samba-tool contact create --given-name=James --initials=T --surname=Kirk
+
+ The example shows how to create a new contact in the domain against a local
+ server. The resulting name is "James T. Kirk".
+ """
+
+ synopsis = "%prog [contactname] [options]"
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server",
+ type=str, metavar="URL", dest="H"),
+ Option("--ou",
+ help=("DN of alternative location (with or without domainDN "
+ "counterpart) in which the new contact will be created."
+ "E.g. 'OU=<OU name>'."
+ "Default is the domain base."),
+ type=str),
+ Option("--surname", help="Contact's surname", type=str),
+ Option("--given-name", help="Contact's given name", type=str),
+ Option("--initials", help="Contact's initials", type=str),
+ Option("--display-name", help="Contact's display name", type=str),
+ Option("--job-title", help="Contact's job title", type=str),
+ Option("--department", help="Contact's department", type=str),
+ Option("--company", help="Contact's company", type=str),
+ Option("--description", help="Contact's description", type=str),
+ Option("--mail-address", help="Contact's email address", type=str),
+ Option("--internet-address", help="Contact's home page", type=str),
+ Option("--telephone-number", help="Contact's phone number", type=str),
+ Option("--mobile-number",
+ help="Contact's mobile phone number",
+ type=str),
+ Option("--physical-delivery-office",
+ help="Contact's office location",
+ type=str),
+ ]
+
+ takes_args = ["fullcontactname?"]
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ "versionopts": options.VersionOptions,
+ }
+
+ def run(self,
+ fullcontactname=None,
+ sambaopts=None,
+ credopts=None,
+ versionopts=None,
+ H=None,
+ ou=None,
+ surname=None,
+ given_name=None,
+ initials=None,
+ display_name=None,
+ job_title=None,
+ department=None,
+ company=None,
+ description=None,
+ mail_address=None,
+ internet_address=None,
+ telephone_number=None,
+ mobile_number=None,
+ physical_delivery_office=None):
+
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp)
+
+ try:
+ samdb = SamDB(url=H,
+ session_info=system_session(),
+ credentials=creds,
+ lp=lp)
+ ret_name = samdb.newcontact(
+ fullcontactname=fullcontactname,
+ ou=ou,
+ surname=surname,
+ givenname=given_name,
+ initials=initials,
+ displayname=display_name,
+ jobtitle=job_title,
+ department=department,
+ company=company,
+ description=description,
+ mailaddress=mail_address,
+ internetaddress=internet_address,
+ telephonenumber=telephone_number,
+ mobilenumber=mobile_number,
+ physicaldeliveryoffice=physical_delivery_office)
+ except Exception as e:
+ raise CommandError("Failed to create contact", e)
+
+ self.outf.write("Contact '%s' created successfully\n" % ret_name)
+
+
+class cmd_delete(Command):
+ """Delete a contact.
+
+ This command deletes a contact object from the Active Directory domain.
+
+ The contactname specified on the command is the display name, common name or
+ the distinguished name of the contact object. The distinguished name of the
+ contact can be specified with or without the domainDN component.
+
+ Example:
+ samba-tool contact delete Contact1 \\
+ -H ldap://samba.samdom.example.com \\
+ --username=Administrator --password=Passw1rd
+
+ The example shows how to delete a contact in the domain against a remote
+ LDAP server.
+ """
+ synopsis = "%prog <contactname> [options]"
+
+ takes_options = [
+ Option("-H",
+ "--URL",
+ help="LDB URL for database or target server",
+ type=str,
+ metavar="URL",
+ dest="H"),
+ ]
+
+ takes_args = ["contactname"]
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ "versionopts": options.VersionOptions,
+ }
+
+ def run(self,
+ contactname,
+ sambaopts=None,
+ credopts=None,
+ versionopts=None,
+ H=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)
+ base_dn = samdb.domain_dn()
+ scope = ldb.SCOPE_SUBTREE
+
+ filter = ("(&(objectClass=contact)(|(name=%s)(displayName=%s)))" %
+ (ldb.binary_encode(contactname),
+ ldb.binary_encode(contactname)))
+
+ if contactname.upper().startswith("CN="):
+ # contact is specified by DN
+ filter = "(objectClass=contact)"
+ scope = ldb.SCOPE_BASE
+ try:
+ base_dn = samdb.normalize_dn_in_domain(contactname)
+ except Exception as e:
+ raise CommandError('Invalid dn "%s": %s' %
+ (contactname, e))
+
+ try:
+ res = samdb.search(base=base_dn,
+ scope=scope,
+ expression=filter,
+ attrs=["dn"])
+ contact_dn = res[0].dn
+ except IndexError:
+ raise CommandError('Unable to find contact "%s"' % (contactname))
+
+ if len(res) > 1:
+ for msg in sorted(res, key=attrgetter('dn')):
+ self.outf.write("found: %s\n" % msg.dn)
+ raise CommandError("Multiple results for contact '%s'\n"
+ "Please specify the contact's full DN" %
+ contactname)
+
+ try:
+ samdb.delete(contact_dn)
+ except Exception as e:
+ raise CommandError('Failed to remove contact "%s"' % contactname, e)
+ self.outf.write("Deleted contact %s\n" % contactname)
+
+
+class cmd_list(Command):
+ """List all contacts.
+
+ If a contact has a displayName specified, the displayName will be used.
+ Otherwise the common name will be used for listing.
+ """
+
+ synopsis = "%prog [options]"
+
+ takes_options = [
+ Option("-H",
+ "--URL",
+ help="LDB URL for database or target server",
+ type=str,
+ metavar="URL",
+ dest="H"),
+ Option("--full-dn",
+ dest="full_dn",
+ default=False,
+ action='store_true',
+ help="Display contact's full DN instead of the name."),
+ ]
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ "versionopts": options.VersionOptions,
+ }
+
+ def run(self,
+ sambaopts=None,
+ credopts=None,
+ versionopts=None,
+ H=None,
+ full_dn=False):
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp, fallback_machine=True)
+
+ samdb = SamDB(url=H,
+ session_info=system_session(),
+ credentials=creds,
+ lp=lp)
+
+ domain_dn = samdb.domain_dn()
+ res = samdb.search(domain_dn,
+ scope=ldb.SCOPE_SUBTREE,
+ expression="(objectClass=contact)",
+ attrs=["name",
+ "displayName"])
+ if (len(res) == 0):
+ return
+
+ if full_dn:
+ for msg in sorted(res, key=attrgetter('dn')):
+ self.outf.write("%s\n" % msg.dn)
+ return
+
+ for msg in res:
+ contact_str = msg.get("name", idx=0)
+ display_name = msg.get("displayName", idx=0)
+ if display_name:
+ contact_str = display_name
+
+ self.outf.write("%s\n" % contact_str)
+
+
+class cmd_edit(Command):
+ """Modify a contact.
+
+ This command will allow editing of a contact object in the Active Directory
+ domain. You will then be able to add or change attributes and their values.
+
+ The contactname specified on the command is the display name, common name or
+ the distinguished name of the contact object. The distinguished name of the
+ contact can be specified with or without the domainDN component.
+
+ 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 contact edit Contact1 -H ldap://samba.samdom.example.com \\
+ -U Administrator --password=Passw1rd
+
+ Example1 shows how to edit a contact's attributes in the domain against a
+ remote LDAP server.
+
+ The -H parameter is used to specify the remote target server.
+
+ Example2:
+ samba-tool contact edit CN=Contact2,OU=people,DC=samdom,DC=example,DC=com
+
+ Example2 shows how to edit a contact's attributes in the domain against a
+ local server. The contact, which is located in the 'people' OU,
+ is specified by the full distinguished name.
+
+ Example3:
+ samba-tool contact edit Contact3 --editor=nano
+
+ Example3 shows how to edit a contact's attributes in the domain against a
+ local server using the 'nano' editor.
+ """
+ synopsis = "%prog <contactname> [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 = ["contactname"]
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ "versionopts": options.VersionOptions,
+ }
+
+ def run(self,
+ contactname,
+ sambaopts=None,
+ credopts=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)
+ base_dn = samdb.domain_dn()
+ scope = ldb.SCOPE_SUBTREE
+
+ filter = ("(&(objectClass=contact)(|(name=%s)(displayName=%s)))" %
+ (ldb.binary_encode(contactname),
+ ldb.binary_encode(contactname)))
+
+ if contactname.upper().startswith("CN="):
+ # contact is specified by DN
+ filter = "(objectClass=contact)"
+ scope = ldb.SCOPE_BASE
+ try:
+ base_dn = samdb.normalize_dn_in_domain(contactname)
+ except Exception as e:
+ raise CommandError('Invalid dn "%s": %s' %
+ (contactname, e))
+
+ try:
+ res = samdb.search(base=base_dn,
+ scope=scope,
+ expression=filter)
+ contact_dn = res[0].dn
+ except IndexError:
+ raise CommandError('Unable to find contact "%s"' % (contactname))
+
+ if len(res) > 1:
+ for msg in sorted(res, key=attrgetter('dn')):
+ self.outf.write("found: %s\n" % msg.dn)
+ raise CommandError("Multiple results for contact '%s'\n"
+ "Please specify the contact's full DN" %
+ contactname)
+
+ for msg in res:
+ 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 contact '%s': " % contactname,
+ e)
+
+ self.outf.write("Modified contact '%s' successfully\n" % contactname)
+
+
+class cmd_show(Command):
+ """Display a contact.
+
+ This command displays a contact object with it's attributes in the Active
+ Directory domain.
+
+ The contactname specified on the command is the display name, common name or
+ the distinguished name of the contact object. The distinguished name of the
+ contact can be specified with or without the domainDN component.
+
+ 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 contact show Contact1 -H ldap://samba.samdom.example.com \\
+ -U Administrator --password=Passw1rd
+
+ Example1 shows how to display a contact's attributes in the domain against
+ a remote LDAP server.
+
+ The -H parameter is used to specify the remote target server.
+
+ Example2:
+ samba-tool contact show CN=Contact2,OU=people,DC=samdom,DC=example,DC=com
+
+ Example2 shows how to display a contact's attributes in the domain against
+ a local server. The contact, which is located in the 'people' OU, is
+ specified by the full distinguished name.
+
+ Example3:
+ samba-tool contact show Contact3 --attributes=mail,mobile
+
+ Example3 shows how to display a contact's mail and mobile attributes.
+ """
+ synopsis = "%prog <contactname> [options]"
+
+ takes_options = [
+ Option("-H",
+ "--URL",
+ help="LDB URL for database or target server",
+ type=str,
+ metavar="URL",
+ dest="H"),
+ Option("--attributes",
+ help=("Comma separated list of attributes, "
+ "which will be printed."),
+ type=str,
+ dest="contact_attrs"),
+ ]
+
+ takes_args = ["contactname"]
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ "versionopts": options.VersionOptions,
+ }
+
+ def run(self,
+ contactname,
+ sambaopts=None,
+ credopts=None,
+ versionopts=None,
+ H=None,
+ contact_attrs=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)
+ base_dn = samdb.domain_dn()
+ scope = ldb.SCOPE_SUBTREE
+
+ attrs = None
+ if contact_attrs:
+ attrs = contact_attrs.split(",")
+
+ filter = ("(&(objectClass=contact)(|(name=%s)(displayName=%s)))" %
+ (ldb.binary_encode(contactname),
+ ldb.binary_encode(contactname)))
+
+ if contactname.upper().startswith("CN="):
+ # contact is specified by DN
+ filter = "(objectClass=contact)"
+ scope = ldb.SCOPE_BASE
+ try:
+ base_dn = samdb.normalize_dn_in_domain(contactname)
+ except Exception as e:
+ raise CommandError('Invalid dn "%s": %s' %
+ (contactname, e))
+
+ try:
+ res = samdb.search(base=base_dn,
+ expression=filter,
+ scope=scope,
+ attrs=attrs)
+ contact_dn = res[0].dn
+ except IndexError:
+ raise CommandError('Unable to find contact "%s"' % (contactname))
+
+ if len(res) > 1:
+ for msg in sorted(res, key=attrgetter('dn')):
+ self.outf.write("found: %s\n" % msg.dn)
+ raise CommandError("Multiple results for contact '%s'\n"
+ "Please specify the contact's DN" %
+ contactname)
+
+ for msg in res:
+ contact_ldif = samdb.write_ldif(msg, ldb.CHANGETYPE_NONE)
+ self.outf.write(contact_ldif)
+
+
+class cmd_move(Command):
+ """Move a contact object to an organizational unit or container.
+
+ The contactname specified on the command is the display name, common name or
+ the distinguished name of the contact object. The distinguished name of the
+ contact can be specified with or without the domainDN component.
+
+ The name of the organizational unit or container can be specified as the
+ distinguished name, with or without the domainDN component.
+
+ 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 contact move Contact1 'OU=people' \\
+ -H ldap://samba.samdom.example.com -U Administrator
+
+ Example1 shows how to move a contact Contact1 into the 'people'
+ organizational unit on a remote LDAP server.
+
+ The -H parameter is used to specify the remote target server.
+
+ Example2:
+ samba-tool contact move Contact1 OU=Contacts,DC=samdom,DC=example,DC=com
+
+ Example2 shows how to move a contact Contact1 into the OU=Contacts
+ organizational unit on the local server.
+ """
+
+ synopsis = "%prog <contactname> <new_parent_dn> [options]"
+
+ takes_options = [
+ Option("-H",
+ "--URL",
+ help="LDB URL for database or target server",
+ type=str,
+ metavar="URL",
+ dest="H"),
+ ]
+
+ takes_args = ["contactname", "new_parent_dn"]
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ "versionopts": options.VersionOptions,
+ }
+
+ def run(self,
+ contactname,
+ new_parent_dn,
+ sambaopts=None,
+ credopts=None,
+ versionopts=None,
+ H=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)
+ base_dn = samdb.domain_dn()
+ scope = ldb.SCOPE_SUBTREE
+
+ filter = ("(&(objectClass=contact)(|(name=%s)(displayName=%s)))" %
+ (ldb.binary_encode(contactname),
+ ldb.binary_encode(contactname)))
+
+ if contactname.upper().startswith("CN="):
+ # contact is specified by DN
+ filter = "(objectClass=contact)"
+ scope = ldb.SCOPE_BASE
+ try:
+ base_dn = samdb.normalize_dn_in_domain(contactname)
+ except Exception as e:
+ raise CommandError('Invalid dn "%s": %s' %
+ (contactname, e))
+
+ try:
+ res = samdb.search(base=base_dn,
+ scope=scope,
+ expression=filter,
+ attrs=["dn"])
+ contact_dn = res[0].dn
+ except IndexError:
+ raise CommandError('Unable to find contact "%s"' % (contactname))
+
+ if len(res) > 1:
+ for msg in sorted(res, key=attrgetter('dn')):
+ self.outf.write("found: %s\n" % msg.dn)
+ raise CommandError("Multiple results for contact '%s'\n"
+ "Please specify the contact's full DN" %
+ contactname)
+
+ try:
+ full_new_parent_dn = samdb.normalize_dn_in_domain(new_parent_dn)
+ except Exception as e:
+ raise CommandError('Invalid new_parent_dn "%s": %s' %
+ (new_parent_dn, e))
+
+ full_new_contact_dn = ldb.Dn(samdb, str(contact_dn))
+ full_new_contact_dn.remove_base_components(len(contact_dn) - 1)
+ full_new_contact_dn.add_base(full_new_parent_dn)
+
+ try:
+ samdb.rename(contact_dn, full_new_contact_dn)
+ except Exception as e:
+ raise CommandError('Failed to move contact "%s"' % contactname, e)
+ self.outf.write('Moved contact "%s" into "%s"\n' %
+ (contactname, full_new_parent_dn))
+
+
+class cmd_contact(SuperCommand):
+ """Contact management."""
+
+ subcommands = {}
+ subcommands["create"] = cmd_create()
+ subcommands["delete"] = cmd_delete()
+ subcommands["edit"] = cmd_edit()
+ subcommands["list"] = cmd_list()
+ subcommands["move"] = cmd_move()
+ subcommands["show"] = cmd_show()
diff --git a/python/samba/netcmd/main.py b/python/samba/netcmd/main.py
index 261cb78163d..88c33c5aa1a 100644
--- a/python/samba/netcmd/main.py
+++ b/python/samba/netcmd/main.py
@@ -58,6 +58,7 @@ class cmd_sambatool(SuperCommand):
subcommands = cache_loader()
subcommands["computer"] = None
+ subcommands["contact"] = None
subcommands["dbcheck"] = None
subcommands["delegation"] = None
subcommands["dns"] = None
diff --git a/python/samba/samdb.py b/python/samba/samdb.py
index 308b5f96a7b..278d7a0ca74 100644
--- a/python/samba/samdb.py
+++ b/python/samba/samdb.py
@@ -497,6 +497,114 @@ member: %s
else:
self.transaction_commit()
+ def newcontact(self,
+ fullcontactname=None,
+ ou=None,
+ surname=None,
+ givenname=None,
+ initials=None,
+ displayname=None,
+ jobtitle=None,
+ department=None,
+ company=None,
+ description=None,
+ mailaddress=None,
+ internetaddress=None,
+ telephonenumber=None,
+ mobilenumber=None,
+ physicaldeliveryoffice=None):
+ """Adds a new contact with additional parameters
+
+ :param fullcontactname: Optional full name of the new contact
+ :param ou: Object container for new contact
+ :param surname: Surname of the new contact
+ :param givenname: First name of the new contact
+ :param initials: Initials of the new contact
+ :param displayname: displayName of the new contact
+ :param jobtitle: Job title of the new contact
+ :param department: Department of the new contact
+ :param company: Company of the new contact
+ :param description: Description of the new contact
+ :param mailaddress: Email address of the new contact
+ :param internetaddress: Home page of the new contact
+ :param telephonenumber: Phone number of the new contact
+ :param mobilenumber: Primary mobile number of the new contact
+ :param physicaldeliveryoffice: Office location of the new contact
+ """
+
+ # Prepare the contact name like the RSAT, using the name parts.
+ cn = ""
+ if givenname is not None:
+ cn += givenname
+
+ if initials is not None:
+ cn += ' %s.' % initials
+
+ if surname is not None:
+ cn += ' %s' % surname
+
+ # Use the specified fullcontactname instead of the previously prepared
+ # contact name, if it is specified.
+ # This is similar to the "Full name" value of the RSAT.
+ if fullcontactname is not None:
+ cn = fullcontactname
+
+ if fullcontactname is None and cn == "":
+ raise Exception('No name for contact specified')
+
+ contactcontainer_dn = self.domain_dn()
+ if ou:
+ contactcontainer_dn = self.normalize_dn_in_domain(ou)
+
+ contact_dn = "CN=%s,%s" % (cn, contactcontainer_dn)
+
+ ldbmessage = {"dn": contact_dn,
+ "objectClass": "contact",
+ }
+
+ if surname is not None:
+ ldbmessage["sn"] = surname
+
+ if givenname is not None:
+ ldbmessage["givenName"] = givenname
+
+ if displayname is not None:
+ ldbmessage["displayName"] = displayname
+
+ if initials is not None:
+ ldbmessage["initials"] = '%s.' % initials
+
+ if jobtitle is not None:
+ ldbmessage["title"] = jobtitle
+
+ if department is not None:
+ ldbmessage["department"] = department
+
+ if company is not None:
+ ldbmessage["company"] = company
+
+ if description is not None:
+ ldbmessage["description"] = description
+
+ if mailaddress is not None:
+ ldbmessage["mail"] = mailaddress
+
+ if internetaddress is not None:
+ ldbmessage["wWWHomePage"] = internetaddress
+
+ if telephonenumber is not None:
+ ldbmessage["telephoneNumber"] = telephonenumber
+
+ if mobilenumber is not None:
+ ldbmessage["mobile"] = mobilenumber
+
+ if physicaldeliveryoffice is not None:
+ ldbmessage["physicalDeliveryOfficeName"] = physicaldeliveryoffice
+
+ self.add(ldbmessage)
+
+ return cn
+
def newcomputer(self, computername, computerou=None, description=None,
prepare_oldjoin=False, ip_address_list=None,
service_principal_name_list=None):
--
2.19.2
From e2ed8f2466fe88110e49bedb07fd876dd620a87d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Baumbach?= <bb at sernet.de>
Date: Wed, 20 Mar 2019 17:17:05 +0100
Subject: [PATCH 18/18] samba-tool tests: add tests for contact management
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Björn Baumbach <bbaumbach at samba.org>
---
python/samba/tests/samba_tool/contact.py | 321 ++++++++++++++++++
python/samba/tests/samba_tool/contact_edit.sh | 164 +++++++++
source4/selftest/tests.py | 4 +-
3 files changed, 488 insertions(+), 1 deletion(-)
create mode 100644 python/samba/tests/samba_tool/contact.py
create mode 100755 python/samba/tests/samba_tool/contact_edit.sh
diff --git a/python/samba/tests/samba_tool/contact.py b/python/samba/tests/samba_tool/contact.py
new file mode 100644
index 00000000000..0abf325e1f1
--- /dev/null
+++ b/python/samba/tests/samba_tool/contact.py
@@ -0,0 +1,321 @@
+# Unix SMB/CIFS implementation.
+#
+# Tests for samba-tool contact management commands
+#
+# Copyright (C) Björn Baumbach <bbaumbach at samba.org> 2019
+#
+# 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 os
+import ldb
+from samba.tests.samba_tool.base import SambaToolCmdTest
+
+class ContactCmdTestCase(SambaToolCmdTest):
+ """Tests for samba-tool contact subcommands"""
+ contacts = []
+ samdb = None
+
+ def setUp(self):
+ super(ContactCmdTestCase, self).setUp()
+ self.creds = "-U%s%%%s" % (os.environ["DC_USERNAME"],
+ os.environ["DC_PASSWORD"])
+ self.samdb = self.getSamDB("-H",
+ "ldap://%s" % os.environ["DC_SERVER"],
+ self.creds)
+ contact = None
+ self.contacts = []
+
+ contact = self._randomContact({"expectedname": "contact1",
+ "name": "contact1"})
+ self.contacts.append(contact)
+
+ # No 'name' is given here, so the name will be made from givenname.
+ contact = self._randomContact({"expectedname": "contact2",
+ "givenName": "contact2"})
+ self.contacts.append(contact)
+
+ contact = self._randomContact({"expectedname": "contact3",
+ "name": "contact3",
+ "displayName": "contact3displayname",
+ "givenName": "not_contact3",
+ "initials": "I",
+ "sn": "not_contact3",
+ "mobile": "12345"})
+ self.contacts.append(contact)
+
+ # No 'name' is given here, so the name will be made from the the
+ # sn, initials and givenName attributes.
+ contact = self._randomContact({"expectedname": "James T. Kirk",
+ "sn": "Kirk",
+ "initials": "T",
+ "givenName": "James"})
+ self.contacts.append(contact)
+
+ # setup the 4 contacts and ensure they are correct
+ for contact in self.contacts:
+ (result, out, err) = self._create_contact(contact)
+
+ self.assertCmdSuccess(result, out, err)
+ self.assertNotIn(
+ "ERROR", err, "There shouldn't be any error message")
+ self.assertIn("Contact '%s' created successfully" %
+ contact["expectedname"], out)
+
+ found = self._find_contact(contact["expectedname"])
+
+ self.assertIsNotNone(found)
+
+ contactname = contact["expectedname"]
+ self.assertEquals("%s" % found.get("name"), contactname)
+ self.assertEquals("%s" % found.get("description"),
+ contact["description"])
+
+ def tearDown(self):
+ super(ContactCmdTestCase, self).tearDown()
+ # clean up all the left over contacts, just in case
+ for contact in self.contacts:
+ if self._find_contact(contact["expectedname"]):
+ (result, out, err) = self.runsubcmd(
+ "contact", "delete", "%s" % contact["expectedname"])
+ self.assertCmdSuccess(result, out, err,
+ "Failed to delete contact '%s'" %
+ contact["expectedname"])
+
+ def test_newcontact(self):
+ """This tests the "contact create" and "contact delete" commands"""
+ # try to create all the contacts again, this should fail
+ for contact in self.contacts:
+ (result, out, err) = self._create_contact(contact)
+ self.assertCmdFail(result, "Succeeded to create existing contact")
+ self.assertIn("already exists", err)
+
+ # try to delete all the contacts we just created
+ for contact in self.contacts:
+ (result, out, err) = self.runsubcmd("contact", "delete", "%s" %
+ contact["expectedname"])
+ self.assertCmdSuccess(result, out, err,
+ "Failed to delete contact '%s'" %
+ contact["expectedname"])
+ found = self._find_contact(contact["expectedname"])
+ self.assertIsNone(found,
+ "Deleted contact '%s' still exists" %
+ contact["expectedname"])
+
+ # test creating contacts in an specified OU
+ parentou = self._randomOU({"name": "testOU"})
+ (result, out, err) = self._create_ou(parentou)
+ self.assertCmdSuccess(result, out, err)
+
+ for contact in self.contacts:
+ (result, out, err) = self._create_contact(contact, ou="OU=testOU")
+
+ self.assertCmdSuccess(result, out, err)
+ self.assertEquals(err, "", "There shouldn't be any error message")
+ self.assertIn("Contact '%s' created successfully" %
+ contact["expectedname"], out)
+
+ found = self._find_contact(contact["expectedname"])
+
+ contactname = contact["expectedname"]
+ self.assertEquals("%s" % found.get("name"), contactname)
+ self.assertEquals("%s" % found.get("description"),
+ contact["description"])
+
+ # try to delete all the contacts we just created, by DN
+ for contact in self.contacts:
+ expecteddn = ldb.Dn(self.samdb,
+ "CN=%s,OU=%s,%s" %
+ (contact["expectedname"],
+ parentou["name"],
+ self.samdb.domain_dn()))
+ (result, out, err) = self.runsubcmd("contact", "delete", "%s" %
+ expecteddn)
+ self.assertCmdSuccess(result, out, err,
+ "Failed to delete contact '%s'" %
+ contact["expectedname"])
+ found = self._find_contact(contact["expectedname"])
+ self.assertIsNone(found,
+ "Deleted contact '%s' still exists" %
+ contact["expectedname"])
+
+ (result, out, err) = self.runsubcmd("ou", "delete",
+ "OU=%s" % parentou["name"])
+ self.assertCmdSuccess(result, out, err,
+ "Failed to delete ou '%s'" % parentou["name"])
+
+ # creating contacts, again for further tests
+ for contact in self.contacts:
+ (result, out, err) = self._create_contact(contact)
+
+ self.assertCmdSuccess(result, out, err)
+ self.assertEquals(err, "", "There shouldn't be any error message")
+ self.assertIn("Contact '%s' created successfully" %
+ contact["expectedname"], out)
+
+ found = self._find_contact(contact["expectedname"])
+
+ contactname = contact["expectedname"]
+ self.assertEquals("%s" % found.get("name"), contactname)
+ self.assertEquals("%s" % found.get("description"),
+ contact["description"])
+
+ def test_list(self):
+ (result, out, err) = self.runsubcmd("contact", "list")
+ self.assertCmdSuccess(result, out, err, "Error running list")
+
+ search_filter = "(objectClass=contact)"
+ contactlist = self.samdb.search(base=self.samdb.domain_dn(),
+ scope=ldb.SCOPE_SUBTREE,
+ expression=search_filter,
+ attrs=["name", "displayName"])
+
+ self.assertTrue(len(contactlist) > 0, "no contacts found in samdb")
+
+ for contactobj in contactlist:
+ name = contactobj.get("displayName", idx=0)
+ if name == None:
+ name = contactobj.get("name", idx=0)
+ self.assertMatch(out, str(name),
+ "contact '%s' not found" % name)
+
+ def test_list_full_dn(self):
+ (result, out, err) = self.runsubcmd("contact", "list", "--full-dn")
+ self.assertCmdSuccess(result, out, err, "Error running list")
+
+ search_filter = "(objectClass=contact)"
+ contactlist = self.samdb.search(base=self.samdb.domain_dn(),
+ scope=ldb.SCOPE_SUBTREE,
+ expression=search_filter,
+ attrs=["dn"])
+
+ self.assertTrue(len(contactlist) > 0, "no contacts found in samdb")
+
+ for contactobj in contactlist:
+ self.assertMatch(out, str(contactobj.dn),
+ "contact '%s' not found" % str(contactobj.dn))
+
+ def test_move(self):
+ parentou = self._randomOU({"name": "parentOU"})
+ (result, out, err) = self._create_ou(parentou)
+ self.assertCmdSuccess(result, out, err)
+
+ for contact in self.contacts:
+ olddn = self._find_contact(contact["expectedname"]).get("dn")
+
+ (result, out, err) = self.runsubcmd("contact", "move",
+ "%s" % contact["expectedname"],
+ "OU=%s" % parentou["name"])
+ self.assertCmdSuccess(result, out, err,
+ "Failed to move contact '%s'" %
+ contact["expectedname"])
+ self.assertEquals(err, "", "There shouldn't be any error message")
+ self.assertIn('Moved contact "%s"' % contact["expectedname"], out)
+
+ found = self._find_contact(contact["expectedname"])
+ self.assertNotEquals(found.get("dn"), olddn,
+ ("Moved contact '%s' still exists with the "
+ "same dn" % contact["expectedname"]))
+ contactname = contact["expectedname"]
+ newexpecteddn = ldb.Dn(self.samdb,
+ "CN=%s,OU=%s,%s" %
+ (contactname,
+ parentou["name"],
+ self.samdb.domain_dn()))
+ self.assertEquals(found.get("dn"), newexpecteddn,
+ "Moved contact '%s' does not exist" %
+ contact["expectedname"])
+
+ (result, out, err) = self.runsubcmd("contact", "move",
+ "%s" % contact["expectedname"],
+ "%s" % olddn.parent())
+ self.assertCmdSuccess(result, out, err,
+ "Failed to move contact '%s'" %
+ contact["expectedname"])
+
+ (result, out, err) = self.runsubcmd("ou", "delete",
+ "OU=%s" % parentou["name"])
+ self.assertCmdSuccess(result, out, err,
+ "Failed to delete ou '%s'" % parentou["name"])
+
+ def _randomContact(self, base={}):
+ """Create a contact with random attribute values, you can specify base
+ attributes"""
+
+ # No name attributes are given here, because the object name will
+ # be made from the sn, givenName and initials attributes, if no name
+ # is given.
+ contact = {
+ "description": self.randomName(count=100),
+ }
+ contact.update(base)
+ return contact
+
+ def _randomOU(self, base={}):
+ """Create an ou with random attribute values, you can specify base
+ attributes."""
+
+ ou = {
+ "name": self.randomName(),
+ "description": self.randomName(count=100),
+ }
+ ou.update(base)
+ return ou
+
+ def _create_contact(self, contact, ou=None):
+ args = ""
+
+ if "name" in contact:
+ args += '{0}'.format(contact['name'])
+
+ args += ' {0}'.format(self.creds)
+
+ if ou is not None:
+ args += ' --ou={0}'.format(ou)
+
+ if "description" in contact:
+ args += ' --description={0}'.format(contact["description"])
+ if "sn" in contact:
+ args += ' --surname={0}'.format(contact["sn"])
+ if "initials" in contact:
+ args += ' --initials={0}'.format(contact["initials"])
+ if "givenName" in contact:
+ args += ' --given-name={0}'.format(contact["givenName"])
+ if "displayName" in contact:
+ args += ' --display-name={0}'.format(contact["displayName"])
+ if "mobile" in contact:
+ args += ' --mobile-number={0}'.format(contact["mobile"])
+
+ args = args.split()
+
+ return self.runsubcmd('contact', 'create', *args)
+
+ def _create_ou(self, ou):
+ return self.runsubcmd("ou",
+ "create",
+ "OU=%s" % ou["name"],
+ "--description=%s" % ou["description"])
+
+ def _find_contact(self, name):
+ contactname = name
+ search_filter = ("(&(objectClass=contact)(name=%s))" %
+ ldb.binary_encode(contactname))
+ contactlist = self.samdb.search(base=self.samdb.domain_dn(),
+ scope=ldb.SCOPE_SUBTREE,
+ expression=search_filter,
+ attrs=[])
+ if contactlist:
+ return contactlist[0]
+ else:
+ return None
diff --git a/python/samba/tests/samba_tool/contact_edit.sh b/python/samba/tests/samba_tool/contact_edit.sh
new file mode 100755
index 00000000000..ca38900062a
--- /dev/null
+++ b/python/samba/tests/samba_tool/contact_edit.sh
@@ -0,0 +1,164 @@
+#!/bin/sh
+#
+# Test for 'samba-tool contact edit'
+
+if [ $# -lt 3 ]; then
+cat <<EOF
+Usage: contact_edit.sh SERVER USERNAME PASSWORD
+EOF
+exit 1;
+fi
+
+SERVER="$1"
+USERNAME="$2"
+PASSWORD="$3"
+
+STpath=$(pwd)
+. $STpath/testprogs/blackbox/subunit.sh
+
+display_name="Björn"
+display_name_b64="QmrDtnJu"
+display_name_new="Renamed Bjoern"
+# attribute value including control character
+# echo -e "test \a string" | base64
+display_name_con_b64="dGVzdCAHIHN0cmluZwo="
+
+tmpeditor=$(mktemp --suffix .sh -p $STpath/bin samba-tool-editor-XXXXXXXX)
+chmod +x $tmpeditor
+
+create_test_contact() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ contact create testcontact1 \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+# Test edit contact - add base64 attributes
+add_attribute_base64() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+contact_ldif="\$1"
+
+grep -v '^$' \$contact_ldif > \${contact_ldif}.tmp
+echo "displayName:: $display_name_b64" >> \${contact_ldif}.tmp
+
+mv \${contact_ldif}.tmp \$contact_ldif
+EOF
+
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool contact edit \
+ testcontact1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool contact show \
+ testcontact1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+delete_attribute() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+contact_ldif="\$1"
+
+grep -v '^displayName' \$contact_ldif >> \${contact_ldif}.tmp
+mv \${contact_ldif}.tmp \$contact_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool contact edit \
+ testcontact1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+# Test edit contact - add base64 attribute value including control character
+add_attribute_base64_control() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+contact_ldif="\$1"
+
+grep -v '^$' \$contact_ldif > \${contact_ldif}.tmp
+echo "displayName:: $display_name_con_b64" >> \${contact_ldif}.tmp
+
+mv \${contact_ldif}.tmp \$contact_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool contact edit \
+ testcontact1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64_control() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool contact show \
+ testcontact1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+
+# Test edit contact - change base64 attribute value including control character
+change_attribute_base64_control() {
+ # create editor.sh
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+contact_ldif="\$1"
+
+sed -i -e 's/displayName:: $display_name_con_b64/displayName: $display_name/' \
+ \$contact_ldif
+EOF
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool contact edit \
+ testcontact1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_attribute_base64_control() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool contact show \
+ testcontact1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+# Test edit contact - change attributes with LDB_FLAG_FORCE_NO_BASE64_LDIF
+change_attribute_force_no_base64() {
+ # create editor.sh
+ # Expects that the original attribute is available as clear text,
+ # because the LDB_FLAG_FORCE_NO_BASE64_LDIF should be used here.
+ cat >$tmpeditor <<EOF
+#!/usr/bin/env bash
+contact_ldif="\$1"
+
+sed -i -e 's/displayName: $display_name/displayName: $display_name_new/' \
+ \$contact_ldif
+EOF
+
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool contact edit \
+ testcontact1 --editor=$tmpeditor \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+get_changed_attribute_force_no_base64() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool contact show \
+ testcontact1 --attributes=displayName \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+delete_contact() {
+ $PYTHON ${STpath}/source4/scripting/bin/samba-tool \
+ contact delete testcontact1 \
+ -H "ldap://$SERVER" "-U$USERNAME" "--password=$PASSWORD"
+}
+
+failed=0
+
+testit "create_test_contact" create_test_contact || failed=`expr $failed + 1`
+testit "add_attribute_base64" add_attribute_base64 || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64" "^displayName:: $display_name_b64" get_attribute_base64 || failed=`expr $failed + 1`
+testit "delete_attribute" delete_attribute || failed=`expr $failed + 1`
+testit "add_attribute_base64_control" add_attribute_base64_control || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64_control" "^displayName:: $display_name_con_b64" get_attribute_base64_control || failed=`expr $failed + 1`
+testit "change_attribute_base64_control" change_attribute_base64_control || failed=`expr $failed + 1`
+testit_grep "get_attribute_base64_control" "^displayName:: $display_name_b64" get_attribute_base64_control || failed=`expr $failed + 1`
+testit "change_attribute_force_no_base64" change_attribute_force_no_base64 || failed=`expr $failed + 1`
+testit_grep "get_changed_attribute_force_no_base64" "^displayName: $display_name_new" get_changed_attribute_force_no_base64 || failed=`expr $failed + 1`
+testit "delete_contact" delete_contact || failed=`expr $failed + 1`
+
+rm -f $tmpeditor
+
+exit $failed
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index cc2410537b4..eb0ab38de66 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -654,11 +654,12 @@ planpythontestsuite("none", "samba.tests.samba_tool.visualize", py3_compatible=T
for env in all_fl_envs:
planpythontestsuite(env + ":local", "samba.tests.samba_tool.fsmo", py3_compatible=True)
-# test samba-tool user, group and computer edit command
+# test samba-tool user, group, contact and computer edit command
for env in all_fl_envs:
env += ":local"
plantestsuite("samba.tests.samba_tool.user_edit", env, [os.path.join(srcdir(), "python/samba/tests/samba_tool/user_edit.sh"), '$SERVER', '$USERNAME', '$PASSWORD'])
plantestsuite("samba.tests.samba_tool.group_edit", env, [os.path.join(srcdir(), "python/samba/tests/samba_tool/group_edit.sh"), '$SERVER', '$USERNAME', '$PASSWORD'])
+ plantestsuite("samba.tests.samba_tool.contact_edit", env, [os.path.join(srcdir(), "python/samba/tests/samba_tool/contact_edit.sh"), '$SERVER', '$USERNAME', '$PASSWORD'])
plantestsuite("samba.tests.samba_tool.computer_edit", env, [os.path.join(srcdir(), "python/samba/tests/samba_tool/computer_edit.sh"), '$SERVER', '$USERNAME', '$PASSWORD'])
# We run this test against both AD DC implementations because it is
@@ -679,6 +680,7 @@ planpythontestsuite("chgdcpass:local", "samba.tests.samba_tool.user_check_passwo
planpythontestsuite("ad_dc_default:local", "samba.tests.samba_tool.group", py3_compatible=True)
planpythontestsuite("ad_dc_default:local", "samba.tests.samba_tool.ou", py3_compatible=True)
planpythontestsuite("ad_dc_default:local", "samba.tests.samba_tool.computer", py3_compatible=True)
+planpythontestsuite("ad_dc_default:local", "samba.tests.samba_tool.contact", py3_compatible=True)
planpythontestsuite("ad_dc_default:local", "samba.tests.samba_tool.forest", py3_compatible=True)
planpythontestsuite("ad_dc_default:local", "samba.tests.samba_tool.schema", py3_compatible=True)
planpythontestsuite("ad_dc:local", "samba.tests.samba_tool.ntacl", py3_compatible=True)
--
2.19.2
More information about the samba-technical
mailing list