389-ds-base/0002-Issue-5156-RFE-that-im...

6385 lines
310 KiB
Diff

From b7dccdc0b3b864c354416bf6a1d0040e0a586e97 Mon Sep 17 00:00:00 2001
From: tbordaz <tbordaz@redhat.com>
Date: Fri, 21 Apr 2023 11:25:29 +0200
Subject: [PATCH 2/5] Issue 5156 - RFE that implement slapi_memberof (#5694)
Bug description:
The RFE #5156 implements a new slapi function slapi_memberof.
This function is described in
https://www.port389.org/docs/389ds/design/slapi_memberof.html
For a given target entry, it allows the caller to retrieve
all entries that have a membership relation to that entry.
Typically, retrieving the groups that the given entry
is memberof.
This PR contains the implementation of slapi_memberof.
This PR contains part of the tests of slapi_memberof.
It does not contain the tests that are based on
already computed 'memberof' attribute (memberof plugin).
Those remaining tests will be reviewed later
This PR contains the remaining tests of slapi_memberof that
verify the ability of slapi_memberof to reuse 'memberof'
values. (MEMBEROF_REUSE_ONLY, MEMBEROF_REUSE_IF_POSSIBLE)
This PR also fixes some bugs in the slapi_membeof function
and the test plugin.
Fix description:
This PR contains the implementation of slapi_memberof.
The slapi_memberof function is called by the server or
plugins.
The tests implements a new extop plugins 2.3.4.5.113730.6.7.1
(test_slapi_memberof.c).
At startup the init function (test_slapi_memberof_init) of
the extop plugin read the plugin configuration entry. The
config entry contains params with which slapi_memberof
is called (scope, excludeScope, recurse, membership attr,...)
The extop receives a target entry as parameters and call
slapi_memberof with this target entry and the config params.
relates: #5156
Reviewed by: Pierre Rogier, Mark Reynolds, Simon Pichugin, William Brown (Very big thanks!!!)
---
Makefile.am | 1 +
.../slapi_memberof/basic_interface_test.py | 4423 +++++++++++++++++
ldap/servers/slapd/main.c | 2 +
ldap/servers/slapd/slapi-memberof.c | 1306 +++++
ldap/servers/slapd/slapi-plugin.h | 35 +
ldap/servers/slapd/slapi-private.h | 5 +
.../slapd/test-plugins/test_slapi_memberof.c | 476 ++
7 files changed, 6248 insertions(+)
create mode 100644 dirsrvtests/tests/suites/slapi_memberof/basic_interface_test.py
create mode 100644 ldap/servers/slapd/slapi-memberof.c
create mode 100644 ldap/servers/slapd/test-plugins/test_slapi_memberof.c
diff --git a/Makefile.am b/Makefile.am
index a6ce565f6..0dd546356 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1142,6 +1142,7 @@ libslapd_la_SOURCES = ldap/servers/slapd/add.c \
ldap/servers/slapd/security_wrappers.c \
ldap/servers/slapd/slapd_plhash.c \
ldap/servers/slapd/slapi_counter.c \
+ ldap/servers/slapd/slapi-memberof.c \
ldap/servers/slapd/slapi2runtime.c \
ldap/servers/slapd/snmp_collator.c \
ldap/servers/slapd/sort.c \
diff --git a/dirsrvtests/tests/suites/slapi_memberof/basic_interface_test.py b/dirsrvtests/tests/suites/slapi_memberof/basic_interface_test.py
new file mode 100644
index 000000000..c5ecf5227
--- /dev/null
+++ b/dirsrvtests/tests/suites/slapi_memberof/basic_interface_test.py
@@ -0,0 +1,4423 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2023 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ----
+
+import pytest, os
+
+import logging
+import ldap
+from lib389.backend import Backends, Backend
+from lib389.mappingTree import MappingTrees
+from lib389.configurations.sample import create_base_domain
+from ldap.extop import ExtendedRequest
+from pyasn1.type import namedtype, univ
+from pyasn1.codec.ber import encoder, decoder
+from lib389.utils import ensure_bytes, get_plugin_dir
+from ldap.extop import ExtendedRequest, ExtendedResponse
+from pyasn1.type import namedtype, univ
+from pyasn1.codec.ber import encoder, decoder
+from lib389 import Entry
+
+from lib389._constants import DEFAULT_SUFFIX, PW_DM
+from lib389.topologies import topology_st as topo
+from lib389.plugins import MemberOfPlugin
+
+from lib389.idm.user import UserAccount, UserAccounts
+from lib389.idm.account import Accounts
+
+pytestmark = pytest.mark.tier0
+log = logging.getLogger(__name__)
+
+
+class SlapiMemberofRequestValue(univ.Sequence):
+ pass
+
+class SlapiMemberofRequest(ExtendedRequest):
+ def __init__(self, requestValidLifeTime=0):
+ self.requestName = '2.3.4.5.113730.6.7.1'
+
+ def encodedRequestValue(self):
+ v = SlapiMemberofRequestValue()
+ return encoder.encode(v)
+
+@pytest.fixture(scope="module")
+def install_test_plugin(topo):
+ import subprocess
+ import shutil
+ import os
+ import sys
+ import re
+ import pdb
+ import random
+
+ current_dir = os.getcwd()
+ # create a build directory
+ build_dir="/tmp/build.%d" % (random.randint(1, 10000))
+ os.makedirs(build_dir, exist_ok=True)
+ cmd_str="chmod 755 %s" % build_dir
+ subprocess.run(cmd_str, shell=True)
+ os.chdir(build_dir)
+
+ # Retrieve the path of the workspace (from the path of the test)
+ workspace = None
+ for i in range(0, len(sys.argv)):
+ if sys.argv[i].find("slapi_memberof") > -1:
+ log.info("Workspace is: %s" % sys.argv[i])
+ workspace=re.sub("dirsrvtest.*$", "", sys.argv[i])
+ else:
+ log.info("Workspace is not: %s" % sys.argv[i])
+
+ if not workspace:
+ log.info("Fail to Retrieve from the repos containing slapi_memberof test plugin source")
+ log.info("using the current directory as workspace")
+ workspace=current_dir
+
+ # Gather the include files from 'ldap/include' and 'ldap/servers/slapd'
+ for the_include in ["portable.h", "avl.h", "ldaprot.h"]:
+ include_file="%s/ldap/include/%s" % (workspace, the_include)
+ log.info("Retrieved from the repos: %s" % include_file)
+ file_path="%s/%s" % (build_dir, the_include)
+ shutil.copy(include_file, file_path)
+ cmd_str="chmod 666 %s" % file_path
+ subprocess.run(cmd_str, shell=True)
+
+ for the_include in ["slap.h", "slapi-private.h", "slapi-plugin.h", "haproxy.h",\
+ "slapi_pal.h", "csngen.h", "uuid.h", "disconnect_errors.h",\
+ "pw.h", "filter.h", "proto-slap.h", "intrinsics.h", "slapi-plugin-compat4.h"]:
+ include_file="%s/ldap/servers/slapd/%s" % (workspace, the_include)
+ log.info("Retrieve from the repos: %s" % include_file)
+ file_path="%s/%s" % (build_dir, the_include)
+ shutil.copy(include_file, file_path)
+ cmd_str="chmod 666 %s" % file_path
+ subprocess.run(cmd_str, shell=True)
+
+ # retrieve the test plugin source
+ log.info("use the default location")
+ src_file="%s/ldap/servers/slapd/test-plugins/test_slapi_memberof.c" % (workspace)
+ dst_file="%s/%s" % (build_dir, "test_slapi_memberof.c")
+ log.info("Retrieve from the repos: %s" % src_file)
+ shutil.copy(src_file, dst_file)
+ cmd_str="chmod 666 %s" % dst_file
+ subprocess.run(cmd_str, shell=True)
+ test_plugin_location=dst_file
+
+ #
+ # If needed (if PR not pushed yet) to craft slapi-plugin.h
+ #
+ file_path_old = "%s/slapi-plugin.h" % (build_dir)
+ file_path_new = "%s/slapi-plugin.h.new" % (build_dir)
+ slapi_plugin_old = open(file_path_old)
+
+ # before crafting check if slapi_memberof defs are present
+ need_to_craft = True
+ for line in slapi_plugin_old:
+ if "Slapi_MemberOfConfig" in line:
+ need_to_craft = False
+ break
+
+ if need_to_craft:
+ log.info("Need to craft slapi-plugin.h")
+ slapi_plugin_old.seek(0, 0)
+ slapi_plugin_new = open(file_path_new, "w")
+
+ # definitions that were missing, add them
+ struct_slapi_memberof = """
+
+#include <plhash.h>
+typedef enum {
+ MEMBEROF_REUSE_ONLY,
+ MEMBEROF_REUSE_IF_POSSIBLE,
+ MEMBEROF_RECOMPUTE
+} memberof_flag_t;
+
+typedef struct _slapi_memberofresult {
+ Slapi_ValueSet *nsuniqueid_vals;
+ Slapi_ValueSet *dn_vals;
+ PRBool maxgroups_reached; /* flag is true if the number of groups hit the max limit */
+} Slapi_MemberOfResult;
+
+typedef struct _slapi_memberofconfig
+{
+ char **groupattrs;
+ PRBool subtree_search;
+ int allBackends;
+ Slapi_DN **entryScopes;
+ Slapi_DN **entryScopeExcludeSubtrees;
+ PRBool recurse;
+ int maxgroups;
+ memberof_flag_t flag;
+ char *error_msg;
+ int errot_msg_lenght;
+ int entryScopeCount; /* private to slapi_memberof */
+ int entryExcludeScopeCount; /* private to slapi_memberof */
+ PRBool maxgroups_reached; /* private to slapi_memberof */
+ const char *memberof_attr; /* private to slapi_memberof */
+ Slapi_Attr *dn_syntax_attr; /* private to slapi_memberof */
+ PLHashTable *ancestors_cache; /* private to slapi_memberof */
+ int current_maxgroup; /* private to slapi_memberof */
+} Slapi_MemberOfConfig;
+
+"""
+ for line in slapi_plugin_old:
+ if re.search(r"^#endif.*SLAPIPLUGIN_H_.*$", line):
+ slapi_plugin_new.write(struct_slapi_memberof)
+ slapi_plugin_new.write("\n")
+ slapi_plugin_new.write(line)
+
+ slapi_plugin_old.close()
+ slapi_plugin_new.close()
+ os.remove(file_path_old)
+ shutil.move(file_path_new, file_path_old)
+
+ #
+ # If needed (if PR not pushed yet) to craft slapi-private.h
+ #
+ file_path_old = "%s/slapi-private.h" % (build_dir)
+ file_path_new = "%s/slapi-private.h.new" % (build_dir)
+ slapi_private_old = open(file_path_old)
+
+ # before crafting check if slapi_memberof defs are present
+ need_to_craft = True
+ for line in slapi_private_old:
+ if "slapi_memberof" in line:
+ need_to_craft = False
+ break
+
+ if need_to_craft:
+ log.info("Need to craft slapi-private.h")
+ slapi_private_old.seek(0, 0)
+ slapi_private_new = open(file_path_new, "w")
+
+ # definitions that were missing, add them
+ struct_slapi_memberof = """
+int slapi_memberof(Slapi_MemberOfConfig *config, Slapi_DN *member_sdn, Slapi_MemberOfResult *result);
+void slapi_memberof_free_memberof_plugin_config();
+int slapi_memberof_load_memberof_plugin_config();
+
+"""
+
+ for line in slapi_private_old:
+ if re.search(r"^void dup_ldif_line.*$", line):
+ slapi_private_new.write(struct_slapi_memberof)
+ slapi_private_new.write("\n")
+ slapi_private_new.write(line)
+
+ slapi_private_old.close()
+ slapi_private_new.close()
+ os.remove(file_path_old)
+ shutil.move(file_path_new, file_path_old)
+
+ # build the plugin into a shared library
+ test_plugin_object="%s/test_slapi_memberof.o" % build_dir
+ test_plugin_sharedlib="%s/libtest_slapi_memberof-plugin.so" % build_dir
+ cmd_str="/usr/bin/gcc -I./ldap/include -I./ldap/servers/slapd -I./include -I. -I/usr/include -I/usr/include/nss3 -I%s -I/usr/include/nspr4 -g -O2 -Wall -c %s -fPIC -DPIC -o %s" % (build_dir, test_plugin_location, test_plugin_object)
+ subprocess.run(cmd_str, shell=True)
+ cmd_str="/usr/bin/gcc -shared -fPIC -DPIC %s -Wl,-rpath -Wl,/usr/lib64/dirsrv -L/usr/lib64/dirsrv/ /usr/lib64/dirsrv/libslapd.so.0 -lldap -llber -lc -Wl,-z,now -g -O2 -O2 -m64 -Wl,-z -Wl,relro -Wl,--as-needed -Wl,-z -Wl,now -Wl,-soname -Wl,libtest_slapi_memberof-plugin.so -o %s" % (test_plugin_object, test_plugin_sharedlib)
+ subprocess.run(cmd_str, shell=True)
+
+ # install the test plugin
+ cmd_str="chmod 755 %s" % test_plugin_sharedlib
+ subprocess.run(cmd_str, shell=True)
+ shutil.copy(test_plugin_sharedlib, topo.standalone.get_plugin_dir())
+
+
+def _check_res_vs_expected(msg, res, expected):
+ log.info("Checking %s expecting %d entries" % (msg, len(expected)))
+ assert len(expected) == len(res)
+ expected_str_lower = []
+ for i in expected:
+ expected_str_lower.append(str(i).lower())
+
+ res_str_lower = []
+ for i in res:
+ res_str_lower.append(str(i).lower())
+
+ for i in expected_str_lower:
+ log.info("Check that %s is present" % (i))
+ assert i in res_str_lower
+
+EMPTY_RESULT="no error msg"
+
+def _extop_test_slapi_member(server, dn, relation):
+ value = univ.OctetString(dn)
+ value_encoded = encoder.encode(value)
+
+ extop = ExtendedRequest(requestName = '2.3.4.5.113730.6.7.1', requestValue=value_encoded)
+ (oid_response, res) = server.extop_s(extop)
+ d1, d2 = decoder.decode(res)
+ log.info("The entries refering to %s as %s are:" % (dn, relation))
+ for i in d1:
+ log.info(" - %s" % i)
+ return d1
+
+
+def replace_manager(server, dn, managers):
+ mod = [(ldap.MOD_REPLACE, 'manager', managers)]
+ server.modify_s(dn, mod)
+
+def add_entry(server, uid, manager=None, subtree=None):
+ if (subtree):
+ dn = 'uid=%s,ou=%s,ou=People,%s' % (uid, subtree, DEFAULT_SUFFIX)
+ else:
+ dn = 'uid=%s,ou=People,%s' % (uid, DEFAULT_SUFFIX)
+ server.add_s(Entry((dn, {'objectclass': 'top person extensibleObject'.split(),
+ 'uid': uid,
+ 'cn': uid,
+ 'sn': uid})))
+ if manager:
+ replace_manager(server, dn, manager)
+ return dn
+
+def test_slapi_memberof_simple(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_member
+ with following parameters
+ - membership attribute: 'manager'
+ - span over all backends: 'on'
+ - skip nesting membership: 'off'
+ - computation mode: recompute
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 4c2595eb-a947-4c0b-996c-e499db67d11a
+ :setup: Standalone instance
+ :steps:
+ 1. provision a set of entry
+ 2. configure test_slapi_memberof as described above
+ 3. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfAllBackends': 'on',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_allbackends_on(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_member
+ It exists several backends and manager relationship cross those backends
+ with following parameters
+ - membership attribute: 'manager'
+ - span over all backends: 'on' <----
+ - skip nesting membership: 'off'
+ - computation mode: recompute
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 910c43a0-04ae-48f1-9e3c-6d97ba5bcb71
+ :setup: Standalone instance
+ :steps:
+ 1. create a second backend with foo_bar entry
+ 2. provision a set of entries in default backend with foo_bar being
+ manager of entry e_1_parent_1_1_1_3_0 that is in default backend
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ slapi_memberof(foo_bar, "manager") -> e_1_parent_1_1_1_3_0
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ # create a second backend
+ second_suffix='dc=foo,dc=bar'
+ be_name='fooBar'
+ be1 = Backend(topo.standalone)
+ be1.create(properties={
+ 'cn': be_name,
+ 'nsslapd-suffix': second_suffix,
+ },
+ )
+ # Create the domain entry
+ create_base_domain(topo.standalone, second_suffix)
+ rdn='foo_bar'
+ dn_entry_foo_bar='uid=%s,%s' % (rdn, second_suffix)
+ topo.standalone.add_s(Entry((dn_entry_foo_bar, {'objectclass': 'top person extensibleObject'.split(),
+ 'uid': rdn,
+ 'cn': rdn,
+ 'sn': rdn})))
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ # make foo_bar entry manager of e_1_parent_1_1_1_3_0
+ replace_manager(topo.standalone, e_1_parent_1_1_1_3_0, [ensure_bytes(dn_entry_foo_bar)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfAllBackends': 'on',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': [DEFAULT_SUFFIX, second_suffix],
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ # Check dn_entry_foo_bar
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=dn_entry_foo_bar, relation="manager")
+ _check_res_vs_expected("organisation reporting to dn_entry_foo_bar", res, expected)
+
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+ topo.standalone.delete_s(dn_entry_foo_bar)
+ be1.delete()
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_allbackends_off(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_member
+ It exists several backends and manager relationship cross those backends
+ with following parameters
+ - membership attribute: 'manager'
+ - span over all backends: 'off' <----
+ - skip nesting membership: 'off'
+ - computation mode: recompute
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 56fb0c16-8086-429b-adf0-fff0eb8e121e
+ :setup: Standalone instance
+ :steps:
+ 1. create a second backend with foo_bar entry
+ 2. provision a set of entries in default backend with foo_bar being
+ manager of entry e_1_parent_1_1_1_3_0 that is in default backend
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ slapi_memberof(foo_bar, "manager") NOT -> e_1_parent_1_1_1_3_0
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ # Create second backend
+ second_suffix='dc=foo,dc=bar'
+ be_name='fooBar'
+ be1 = Backend(topo.standalone)
+ be1.create(properties={
+ 'cn': be_name,
+ 'nsslapd-suffix': second_suffix,
+ },
+ )
+ # Create the domain entry
+ create_base_domain(topo.standalone, second_suffix)
+ rdn='foo_bar'
+ dn_entry_foo_bar='uid=%s,%s' % (rdn, second_suffix)
+ topo.standalone.add_s(Entry((dn_entry_foo_bar, {'objectclass': 'top person extensibleObject'.split(),
+ 'uid': rdn,
+ 'cn': rdn,
+ 'sn': rdn})))
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ # make foo_bar entry manager of e_1_parent_1_1_1_3_0
+ replace_manager(topo.standalone, e_1_parent_1_1_1_3_0, [ensure_bytes(dn_entry_foo_bar)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': [DEFAULT_SUFFIX, second_suffix],
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ # Check dn_entry_foo_bar is not manager of e_1_parent_1_1_1_3_0 because slapimemberOfAllBackends=off
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=dn_entry_foo_bar, relation="manager")
+ _check_res_vs_expected("organisation reporting to dn_entry_foo_bar", res, expected)
+
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+ topo.standalone.delete_s(dn_entry_foo_bar)
+ be1.delete()
+
+ request.addfinalizer(fin)
+
+
+def test_slapi_memberof_memberattr(topo, request, install_test_plugin):
+ """
+ Test that membership hierarchy (member) is computed with slapi_member
+ the membership is done with 'manager' attribute but slapi_memberof
+ called with 'member' attribute. As there is no 'member' then
+ membership returns empty_results
+ with following parameters
+ - membership attribute: 'member' <----
+ - span over all backends: 'on'
+ - skip nesting membership: 'off'
+ - computation mode: recompute
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 373f7f65-185f-4b06-a0a5-3e23692b87f1
+ :setup: Standalone instance
+ :steps:
+ 1. provision a set of entries in default backend
+ with membership using 'manager'
+ 2. configure test_slapi_memberof as described above
+ so checking membership using 'member'
+ 3. check computed membership vs expected result
+ all empty_result because no entry has 'member'
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'member',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfAllBackends': 'on',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+
+def test_slapi_memberof_scope(topo, request, install_test_plugin):
+ """
+ Test that membership hierarchy (member) is computed with slapi_member
+ Only entries in the subtree scope (e_2_parent_1_0) gets valid
+ computation of the membership
+ with following parameters
+ - membership attribute: 'manager'
+ - span over all backends: 'on'
+ - skip nesting membership: 'off'
+ - computation mode: recompute
+ - Scope: ou=subtree,ou=People,dc=example,dc=com <----
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 6c7587e0-0bc4-4847-b403-773d7314aa31
+ :setup: Standalone instance
+ :steps:
+ 1. provision a set of entries in default backend
+ 2. configure test_slapi_memberof as described above
+ so only entries under e_2_parent_1_0 are taken into
+ consideration
+ 3. check computed membership vs expected result
+ Only entries under e_2_parent_1_0 get no empty results
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0 (subtree) <----
+ -- e_1_parent_2_1_0 (subtree) <----
+ -- e_2_parent_2_1_0 (subtree) <----
+ --- e_1_parent_2_2_1_0 (subtree) <----
+ -- e_3_parent_2_1_0 (subtree) <----
+ -- e_4_parent_2_1_0 (subtree) <----
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+
+ subtree="subtree"
+ dn_subtree = 'ou=%s,ou=People,%s' % (subtree, DEFAULT_SUFFIX)
+ topo.standalone.add_s(Entry((dn_subtree, {'objectclass': 'top organizationalunit'.split(),
+ 'ou': subtree})))
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)], subtree=subtree)
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)], subtree=subtree)
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)], subtree=subtree)
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)], subtree=subtree)
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)], subtree=subtree)
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)], subtree=subtree)
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfAllBackends': 'on',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': dn_subtree,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # Check e_1_parent_2_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_2_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [ e_1_parent_2_2_1_0 ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+ topo.standalone.delete_s(dn_subtree)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_excludescope(topo, request, install_test_plugin):
+ """
+ Test that membership hierarchy (member) is computed with slapi_member
+ Entries in the subtree excludeescope (e_2_parent_1_0) are ignored
+ computation of the membership
+ with following parameters
+ - membership attribute: 'manager'
+ - span over all backends: 'on'
+ - skip nesting membership: 'off'
+ - computation mode: recompute
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: ou=subtree,ou=People,dc=example,dc=com <----
+ - Maximum return entries: None
+
+ :id: bdb17e7e-289c-4b56-83d5-0eb54d0c660e
+ :setup: Standalone instance
+ :steps:
+ 1. provision a set of entries in default backend
+ 2. configure test_slapi_memberof as described above
+ so entries under e_2_parent_1_0 are ignored
+ 3. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0 (subtree) <----
+ -- e_1_parent_2_1_0 (subtree) <----
+ -- e_2_parent_2_1_0 (subtree) <----
+ --- e_1_parent_2_2_1_0 (subtree) <----
+ -- e_3_parent_2_1_0 (subtree) <----
+ -- e_4_parent_2_1_0 (subtree) <----
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+
+ subtree="subtree"
+ dn_subtree = 'ou=%s,ou=People,%s' % (subtree, DEFAULT_SUFFIX)
+ topo.standalone.add_s(Entry((dn_subtree, {'objectclass': 'top organizationalunit'.split(),
+ 'ou': subtree})))
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)], subtree=subtree)
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)], subtree=subtree)
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)], subtree=subtree)
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)], subtree=subtree)
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)], subtree=subtree)
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)], subtree=subtree)
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfAllBackends': 'on',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScopeExcludeSubtree': dn_subtree,
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # Check e_1_parent_2_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_2_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [ EMPTY_RESULT ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [ e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0 ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [ e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0 ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [ e_1_parent_1_1_1_3_0 ]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+ topo.standalone.delete_s(dn_subtree)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_skip_nested(topo, request, install_test_plugin):
+ """
+ When searching the management (manager) hierarchy it stops at the first level
+ no recursion
+ Test that management hierarchy is computed with slapi_member
+ It is done stopping at the first level, so the direct subordinate
+ with following parameters
+ - membership attribute: 'manager'
+ - span over all backends: 'on'
+ - skip nesting membership: 'on' <----
+ - computation mode: recompute
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: ou=subtree,ou=People,dc=example,dc=com
+ - Maximum return entries: None
+
+ :id: c9b5617f-9058-40f5-bdd6-a560bc67b30d
+ :setup: Standalone instance
+ :steps:
+ 1. provision a set of entries in default backend
+ 2. configure test_slapi_memberof as described above
+ 3. check computed membership vs expected result
+ only direct subordinate are returned
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+
+ subtree="subtree"
+ dn_subtree = 'ou=%s,ou=People,%s' % (subtree, DEFAULT_SUFFIX)
+ topo.standalone.add_s(Entry((dn_subtree, {'objectclass': 'top organizationalunit'.split(),
+ 'ou': subtree})))
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfAllBackends': 'on',
+ 'slapimemberOfSkipNested': 'on',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_2_parent_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+ topo.standalone.delete_s(dn_subtree)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_maxgroup(topo, request, install_test_plugin):
+ """
+ When searching the management (manager) hierarchy it stops when
+ a maximum subordinates are retrieved
+ Test that management hierarchy is computed with slapi_member
+ with following parameters
+ - membership attribute: 'manager'
+ - span over all backends: 'on'
+ - skip nesting membership: 'off' <----
+ - computation mode: recompute
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: ou=subtree,ou=People,dc=example,dc=com
+ - Maximum return entries: 3 <--
+
+ :id: 83a4c668-99d0-4f47-ac89-a7f7fc620340
+ :setup: Standalone instance
+ :steps:
+ 1. provision a set of entries in default backend
+ 2. configure test_slapi_memberof as described above
+ 3. check computed membership vs expected result
+ only direct subordinate are returned
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfAllBackends': 'on',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '3',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_reuse_if_possible_1(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to reuse IF POSSIBLE the computed values
+ from memberof plugins. As memberof plugin is not enabled, it falls back
+ to regular computation (recompute)
+ with following parameters
+ - membership attribute: 'manager'
+ - span over all backends: 'on'
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 8f75e4c9-60d4-41b8-8b25-df9fe4b0231d
+ :setup: Standalone instance
+ :steps:
+ 1. provision a set of entry
+ 2. configure test_slapi_memberof as described above
+ 3. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_IF_POSSIBLE',
+ 'slapimemberOfAllBackends': 'on',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_reuse_if_possible_2(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to reuse IF POSSIBLE the computed values
+ from memberof plugins.
+ Memberof plugin is enabled, but with a different 'membership attribute'
+ it falls back to regular computation (recompute)
+ with following parameters
+ - membership attribute: 'manager' <--
+ - span over all backends: 'off'
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 2175578b-7f12-4f36-a4fe-eb401422643d
+ :setup: Standalone instance
+ :steps:
+ 1. Configure memberof with 'uniquemember' memberOfGroupAttr <--
+ 2. provision a set of entry
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ memberof = MemberOfPlugin(topo.standalone)
+ memberof.enable()
+ memberof.replace('memberOfGroupAttr', 'uniquemember')
+ topo.standalone.restart()
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_IF_POSSIBLE',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_reuse_if_possible_3(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to reuse IF POSSIBLE the computed values
+ from memberof plugins.
+ Memberof plugin is enabled, but with a different 'memberOfAllBackends attribute'
+ it falls back to regular computation (recompute)
+ with following parameters
+ - membership attribute: 'manager'
+ - span over all backends: 'off' <--
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 11615fc6-67e8-4c4a-be76-d57baf0e1706
+ :setup: Standalone instance
+ :steps:
+ 1. Configure memberof with 'memberOfAllBackends: on' <--
+ 2. provision a set of entry
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ memberof = MemberOfPlugin(topo.standalone)
+ memberof.enable()
+ memberof.replace('memberOfGroupAttr', 'manager')
+ memberof.replace('memberOfAllBackends', 'on')
+ topo.standalone.restart()
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_IF_POSSIBLE',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+
+def test_slapi_memberof_reuse_if_possible_4(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to reuse IF POSSIBLE the computed values
+ from memberof plugins.
+ Memberof plugin is enabled, but with a different 'memberOfSkipNested' attr
+ it falls back to regular computation (recompute)
+ with following parameters
+ - membership attribute: 'manager'
+ - span over all backends: 'off'
+ - skip nesting membership: 'off' <--
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 305c99ba-5835-4b8c-bfb7-11deeea5eedc
+ :setup: Standalone instance
+ :steps:
+ 1. Configure memberof with 'memberOfSkipNested: on' <--
+ 2. provision a set of entry
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ memberof = MemberOfPlugin(topo.standalone)
+ memberof.enable()
+ memberof.replace('memberOfGroupAttr', 'manager')
+ memberof.replace('memberOfSkipNested', 'on')
+ topo.standalone.restart()
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_IF_POSSIBLE',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+
+def test_slapi_memberof_reuse_if_possible_5(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to reuse IF POSSIBLE the computed values
+ from memberof plugins.
+ Memberof plugin is enabled, but with a different 'memberOfAttr' attr
+ it falls back to regular computation (recompute)
+ with following parameters
+ - member attribute: memberof <--
+ - membership attribute: 'manager'
+ - span over all backends: 'off'
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 66d2ed29-5d14-487a-b28a-5660962c7c6c
+ :setup: Standalone instance
+ :steps:
+ 1. Configure memberof with 'memberOfAttr: member' <--
+ 2. provision a set of entry
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ memberof = MemberOfPlugin(topo.standalone)
+ memberof.enable()
+ memberof.replace('memberOfGroupAttr', 'manager')
+
+ # For the test memberOfAttr should differ from 'memberof' that is
+ # used in slapi_memberof call. We can not use a dummy attribute
+ # because it requires to be a DN syntax. Let's use 'member'
+ memberof.replace('memberOfAttr', 'member')
+ topo.standalone.restart()
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_IF_POSSIBLE',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_reuse_if_possible_6(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to reuse IF POSSIBLE the computed values
+ from memberof plugins.
+ Memberof plugin is enabled, but with a different 'slapimemberOfEntryScope' attr
+ it falls back to regular computation (recompute)
+ with following parameters
+ - member attribute: memberof
+ - membership attribute: 'manager'
+ - span over all backends: 'off'
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: ou=people,dc=example,dc=com <--
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 4fbefa39-6c06-47c4-8818-a102944b7f29
+ :setup: Standalone instance
+ :steps:
+ 1. Configure memberof without 'memberOfEntryScope' <--
+ 2. provision a set of entry
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ memberof = MemberOfPlugin(topo.standalone)
+ memberof.enable()
+ memberof.replace('memberOfGroupAttr', 'manager')
+ topo.standalone.restart()
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_IF_POSSIBLE',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': "ou=People,%s" % DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+
+def test_slapi_memberof_reuse_if_possible_7(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to reuse IF POSSIBLE the computed values
+ from memberof plugins.
+ Memberof plugin is enabled, but with a different 'slapimemberOfEntryScope' attr
+ it falls back to regular computation (recompute)
+ with following parameters
+ - member attribute: memberof
+ - membership attribute: 'manager'
+ - span over all backends: 'off'
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: ou=people,dc=example,dc=com <--
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: b8cfca23-742f-44f0-8bb7-f93371954d40
+ :setup: Standalone instance
+ :steps:
+ 1. Configure memberof with 'memberOfEntryScope: ou=groups,dc=example,dc=com' <--
+ 2. provision a set of entry
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ memberof = MemberOfPlugin(topo.standalone)
+ memberof.enable()
+ memberof.replace('memberOfGroupAttr', 'manager')
+ memberof.replace('memberOfAllBackends', 'off')
+ memberof.replace('memberOfSkipNested', 'off')
+ memberof.replace('memberOfEntryScope', 'ou=groups,dc=example,dc=com')
+ topo.standalone.restart()
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_IF_POSSIBLE',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': "ou=People,%s" % DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_reuse_if_possible_8(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to reuse IF POSSIBLE the computed values
+ from memberof plugins.
+ Memberof plugin is enabled, but with a different 'slapimemberOfEntryScope' attr
+ it falls back to regular computation (recompute)
+ with following parameters
+ - member attribute: memberof
+ - membership attribute: 'manager'
+ - span over all backends: 'off'
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: ou=people,dc=example,dc=com <--
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 5c990df5-8aa6-44c6-a9e1-f161c3e01d1e
+ :setup: Standalone instance
+ :steps:
+ 1. Configure memberof with
+ - 'memberOfEntryScope: ou=groups,dc=example,dc=com' <--
+ - 'memberOfEntryScope: ou=people,dc=example,dc=com' <--
+ 2. provision a set of entry
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ memberof = MemberOfPlugin(topo.standalone)
+ memberof.enable()
+ memberof.replace('memberOfGroupAttr', 'manager')
+ memberof.replace('memberOfAllBackends', 'off')
+ memberof.replace('memberOfSkipNested', 'off')
+ memberof.replace('memberOfEntryScope', DEFAULT_SUFFIX)
+ memberof.replace('memberOfEntryScope', 'ou=groups,dc=example,dc=com')
+ memberof.add('memberOfEntryScope', 'ou=people,dc=example,dc=com')
+ topo.standalone.restart()
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_IF_POSSIBLE',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': "ou=People,%s" % DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_reuse_if_possible_9(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to reuse IF POSSIBLE the computed values
+ from memberof plugins.
+ Memberof plugin is enabled, but with a different 'slapimemberOfEntryScopeExcludeSubtree' attr
+ it falls back to regular computation (recompute)
+ with following parameters
+ - member attribute: memberof
+ - membership attribute: 'manager'
+ - span over all backends: 'off'
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: None
+ - ExcludeScope: ou=groups,dc=example,dc=com <--
+ - Maximum return entries: None
+
+ :id: 55ffe094-482b-4e2f-9d34-8b0a1ebd5248
+ :setup: Standalone instance
+ :steps:
+ 1. Configure memberof without 'memberOfEntryScopeExcludeSubtree' <--
+ 2. provision a set of entry
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ memberof = MemberOfPlugin(topo.standalone)
+ memberof.enable()
+ memberof.replace('memberOfGroupAttr', 'manager')
+ memberof.replace('memberOfAllBackends', 'off')
+ memberof.replace('memberOfSkipNested', 'off')
+ memberof.replace('memberOfEntryScope', DEFAULT_SUFFIX)
+ topo.standalone.restart()
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_IF_POSSIBLE',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfEntryScopeExcludeSubtree': "ou=Groups,%s" % DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+
+def test_slapi_memberof_reuse_if_possible_10(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to reuse IF POSSIBLE the computed values
+ from memberof plugins.
+ Memberof plugin is enabled, but with a different 'slapimemberOfEntryScopeExcludeSubtree' attr
+ it falls back to regular computation (recompute)
+ with following parameters
+ - member attribute: memberof
+ - membership attribute: 'manager'
+ - span over all backends: 'off'
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: ou=foo,dc=example,dc=com <--
+ - Maximum return entries: None
+
+ :id: 6c26619d-e0e2-4b3e-938b-4cab817e928f
+ :setup: Standalone instance
+ :steps:
+ 1. Configure memberof with 'memberOfEntryScopeExcludeSubtree: ou=groups,dc=example,dc=com' <--
+ 2. provision a set of entry
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ memberof = MemberOfPlugin(topo.standalone)
+ memberof.enable()
+ memberof.replace('memberOfGroupAttr', 'manager')
+ memberof.replace('memberOfAllBackends', 'off')
+ memberof.replace('memberOfSkipNested', 'off')
+ memberof.replace('memberOfEntryScope', DEFAULT_SUFFIX)
+ memberof.replace('memberOfEntryScopeExcludeSubtree', 'ou=groups,dc=example,dc=com')
+ topo.standalone.restart()
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_IF_POSSIBLE',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfEntryScopeExcludeSubtree': "ou=Foo,%s" % DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_reuse_if_possible_11(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to reuse IF POSSIBLE the computed values
+ from memberof plugins.
+ Memberof plugin is enabled, but with a different 'slapimemberOfEntryScope' attr
+ it falls back to regular computation (recompute)
+ with following parameters
+ - member attribute: memberof
+ - membership attribute: 'manager'
+ - span over all backends: 'off'
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: None
+ - ExcludeScope: ou=foo1,dc=example,dc=com <--
+ - Maximum return entries: None
+
+ :id: 4c15995e-21a9-4443-8027-a0908345be50
+ :setup: Standalone instance
+ :steps:
+ 1. Configure memberof with
+ - 'memberOfEntryScopeExcludeSubtree: ou=foo1,dc=example,dc=com' <--
+ - 'memberOfEntryScopeExcludeSubtree: ou=foo2,dc=example,dc=com' <--
+ 2. provision a set of entry
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ memberof = MemberOfPlugin(topo.standalone)
+ memberof.enable()
+ memberof.replace('memberOfGroupAttr', 'manager')
+ memberof.replace('memberOfAllBackends', 'off')
+ memberof.replace('memberOfSkipNested', 'off')
+ memberof.replace('memberOfEntryScope', DEFAULT_SUFFIX)
+ memberof.replace('memberOfEntryScopeExcludeSubtree', 'ou=foo1,dc=example,dc=com')
+ memberof.add('memberOfEntryScopeExcludeSubtree', 'ou=foo2,dc=example,dc=com')
+ topo.standalone.restart()
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_IF_POSSIBLE',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfEntryScopeExcludeSubtree': "ou=foo1,%s" % DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_reuse_only_1(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to ONLY reuse the computed values
+ from memberof plugins. As memberof plugin is not enabled, it returns
+ no memberof.
+ with following parameters
+ - membership attribute: 'manager'
+ - span over all backends: 'on'
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_ONLY <--
+ - Scope: DEFAULT_SUFFIX
+ - ExcludeScope: None
+ - Maximum return entries: None
+
+ :id: 7be9b188-2e84-4454-b6db-9e176014582a
+ :setup: Standalone instance
+ :steps:
+ 1. provision a set of entry
+ 2. configure test_slapi_memberof as described above
+ 3. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_ONLY',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+def test_slapi_memberof_reuse_only_2(topo, request, install_test_plugin):
+ """
+ Test that management hierarchy (manager) is computed with slapi_memberof
+ It requires slapi_memberof to ONLY reuse the computed values
+ from memberof plugins. As memberof plugin is enabled, it returns
+ memberof.
+ with following parameters
+ - member attribute: memberof
+ - membership attribute: 'manager'
+ - span over all backends: 'off'
+ - skip nesting membership: 'off'
+ - computation mode: MEMBEROF_REUSE_IF_POSSIBLE <--
+ - Scope: None
+ - ExcludeScope: ou=foo1,dc=example,dc=com <--
+ - Maximum return entries: None
+
+ :id: fb4f8c86-aa39-4252-90e0-36cfd7b3dd80
+ :setup: Standalone instance
+ :steps:
+ 1. Configure memberof with
+ 2. provision a set of entry
+ 3. configure test_slapi_memberof as described above
+ 4. check computed membership vs expected result
+ :expectedresults:
+ 1. Operation should succeed
+ 2. Operation should succeed
+ 3. Operation should succeed
+ 4. Operation should succeed
+
+ DIT is :
+ e_1_parent_0
+ - e_1_parent_1_0
+ -- e_1_parent_1_1_0
+ --- e_1_parent_1_1_1_0
+ --- e_2_parent_1_1_1_0
+ --- e_3_parent_1_1_1_0
+ --- e_4_parent_1_1_1_0
+ --- e_5_parent_1_1_1_0
+ -- e_2_parent_1_1_0
+ - e_2_parent_1_0
+ -- e_1_parent_2_1_0
+ -- e_2_parent_2_1_0
+ --- e_1_parent_2_2_1_0
+ -- e_3_parent_2_1_0
+ -- e_4_parent_2_1_0
+ e_2_parent_0
+ - e_1_parent_2_0
+ - e_2_parent_2_0
+ - e_3_parent_2_0
+ - e_4_parent_2_0
+ e_3_parent_0
+ - e_1_parent_3_0
+ -- e_1_parent_1_3_0
+ --- e_1_parent_1_1_3_0
+ ---- e_1_parent_1_1_1_3_0
+ """
+ memberof = MemberOfPlugin(topo.standalone)
+ memberof.enable()
+ memberof.replace('memberOfAttr', 'memberof')
+ memberof.replace('memberOfGroupAttr', 'manager')
+ memberof.replace('memberOfAllBackends', 'off')
+ memberof.replace('memberOfSkipNested', 'off')
+ memberof.replace('memberOfEntryScope', DEFAULT_SUFFIX)
+ topo.standalone.restart()
+
+ user = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+
+ # First subtree
+ e_1_parent_0 = add_entry(topo.standalone, uid="e_1_parent_0")
+
+ e_1_parent_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_1_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_2_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_3_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_3_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_4_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_4_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+ e_5_parent_1_1_1_0 = add_entry(topo.standalone, uid="e_5_parent_1_1_1_0", manager=[ensure_bytes(e_1_parent_1_1_0)])
+
+ e_2_parent_1_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_1_0", manager=[ensure_bytes(e_1_parent_1_0)])
+
+ e_2_parent_1_0 = add_entry(topo.standalone, uid="e_2_parent_1_0", manager=[ensure_bytes(e_1_parent_0)])
+
+ e_1_parent_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_2_parent_2_1_0 = add_entry(topo.standalone, uid="e_2_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_1_parent_2_2_1_0 = add_entry(topo.standalone, uid="e_1_parent_2_2_1_0", manager=[ensure_bytes(e_2_parent_2_1_0)])
+ e_3_parent_2_1_0 = add_entry(topo.standalone, uid="e_3_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+ e_4_parent_2_1_0 = add_entry(topo.standalone, uid="e_4_parent_2_1_0", manager=[ensure_bytes(e_2_parent_1_0)])
+
+ # 2nd subtree
+ e_2_parent_0 = add_entry(topo.standalone, uid="e_2_parent_0")
+
+ e_1_parent_2_0 = add_entry(topo.standalone, uid="e_1_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_2_parent_2_0 = add_entry(topo.standalone, uid="e_2_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_3_parent_2_0 = add_entry(topo.standalone, uid="e_3_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+ e_4_parent_2_0 = add_entry(topo.standalone, uid="e_4_parent_2_0", manager=[ensure_bytes(e_2_parent_0)])
+
+ # third subtree
+ e_3_parent_0 = add_entry(topo.standalone, uid="e_3_parent_0")
+
+ e_1_parent_3_0 = add_entry(topo.standalone, uid="e_1_parent_3_0", manager=[ensure_bytes(e_3_parent_0)])
+
+ e_1_parent_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_3_0", manager=[ensure_bytes(e_1_parent_3_0)])
+
+ e_1_parent_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_3_0)])
+
+ e_1_parent_1_1_1_3_0 = add_entry(topo.standalone, uid="e_1_parent_1_1_1_3_0", manager=[ensure_bytes(e_1_parent_1_1_3_0)])
+
+ dn_config = 'cn=test_slapi_memberof,cn=plugins,cn=config'
+ topo.standalone.add_s(Entry((dn_config, {'objectclass': 'top nsSlapdPlugin extensibleObject'.split(),
+ 'cn': 'test_slapi_memberof',
+ 'nsslapd-pluginPath': 'libtest_slapi_memberof-plugin',
+ 'nsslapd-pluginInitfunc': 'test_slapi_memberof_init',
+ 'nsslapd-pluginType': 'extendedop',
+ 'nsslapd-pluginEnabled': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'test_slapi_memberof-plugin',
+ 'slapimemberOfMemberDN': 'uid=test_user_11,ou=People,dc=example,dc=com',
+ 'slapimemberOfGroupAttr': 'manager',
+ 'slapimemberOfAttr': 'memberof',
+ 'slapimemberOfFlag': 'MEMBEROF_REUSE_ONLY',
+ 'slapimemberOfAllBackends': 'off',
+ 'slapimemberOfSkipNested': 'off',
+ 'slapimemberOfEntryScope': DEFAULT_SUFFIX,
+ 'slapimemberOfMaxGroup': '0',
+ 'nsslapd-pluginVersion': '2.3.2.202302131418git0e190fc3d',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginDescription': 'test_slapi_memberof extended operation plugin'})))
+ topo.standalone.restart()
+
+ # Check the first subtree
+ expected = [ e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_0, relation="manager")
+ _check_res_vs_expected("first subtree", res, expected)
+
+ # Check the second subtree
+ expected = [e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_0, relation="manager")
+ _check_res_vs_expected("second subtree", res, expected)
+
+ # Check the third subtree
+ expected = [e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_3_parent_0, relation="manager")
+ _check_res_vs_expected("third subtree", res, expected)
+
+ # check e_1_parent_1_0
+ expected = [e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_0", res, expected)
+
+ # check e_1_parent_1_1_0
+ expected = [e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_1_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_1_0", res, expected)
+
+ # check e_2_parent_1_0
+ expected = [e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_1_0", res, expected)
+
+ # check e_2_parent_2_1_0
+ expected = [e_1_parent_2_2_1_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_2_parent_2_1_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_2_parent_2_1_0", res, expected)
+
+ # Check e_1_parent_3_0
+ expected = [e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_3_0", res, expected)
+
+ # Check e_1_parent_1_3_0
+ expected = [e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_3_0
+ expected = [e_1_parent_1_1_1_3_0]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_3_0", res, expected)
+
+ # Check e_1_parent_1_1_1_3_0
+ expected = [EMPTY_RESULT]
+ res = _extop_test_slapi_member(server=topo.standalone, dn=e_1_parent_1_1_1_3_0, relation="manager")
+ _check_res_vs_expected("organisation reporting to e_1_parent_1_1_1_3_0", res, expected)
+
+ def fin():
+ entries = [e_1_parent_0, e_1_parent_1_0, e_1_parent_1_1_0, e_1_parent_1_1_1_0, e_2_parent_1_1_1_0, e_3_parent_1_1_1_0, e_4_parent_1_1_1_0, e_5_parent_1_1_1_0, e_2_parent_1_1_0, e_2_parent_1_0, e_1_parent_2_1_0, e_2_parent_2_1_0, e_1_parent_2_2_1_0, e_3_parent_2_1_0, e_4_parent_2_1_0, e_2_parent_0, e_1_parent_2_0, e_2_parent_2_0, e_3_parent_2_0, e_4_parent_2_0, e_3_parent_0, e_1_parent_3_0, e_1_parent_1_3_0, e_1_parent_1_1_3_0, e_1_parent_1_1_1_3_0]
+ for entry in entries:
+ topo.standalone.delete_s(entry)
+ topo.standalone.delete_s(dn_config)
+
+ request.addfinalizer(fin)
+
+if __name__ == "__main__":
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s -v %s" % CURRENT_FILE)
+
diff --git a/ldap/servers/slapd/main.c b/ldap/servers/slapd/main.c
index b8084faac..f24650547 100644
--- a/ldap/servers/slapd/main.c
+++ b/ldap/servers/slapd/main.c
@@ -1064,6 +1064,7 @@ main(int argc, char **argv)
plugin_print_lists();
plugin_startall(argc, argv, NULL /* specific plugin list */);
compute_plugins_started();
+ slapi_memberof_load_memberof_plugin_config();
(void) rewriters_init();
if (housekeeping_start((time_t)0, NULL) == NULL) {
return_value = 1;
@@ -1132,6 +1133,7 @@ main(int argc, char **argv)
slapd_daemon(&ports_info);
}
slapi_log_err(SLAPI_LOG_INFO, "main", "slapd stopped.\n");
+ slapi_memberof_free_memberof_plugin_config();
reslimit_cleanup();
vattr_cleanup();
sasl_map_done();
diff --git a/ldap/servers/slapd/slapi-memberof.c b/ldap/servers/slapd/slapi-memberof.c
new file mode 100644
index 000000000..6f39965bb
--- /dev/null
+++ b/ldap/servers/slapd/slapi-memberof.c
@@ -0,0 +1,1306 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (C) 2023 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* util.c -- utility functions -- functions available form libslapd */
+#include <sys/param.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include "slap.h"
+#include <sys/resource.h>
+#include <errno.h>
+
+#define MEMBEROF_CACHE_DEBUG 0
+#define MEMBEROF_CONFIG_DN "cn=MemberOf Plugin,cn=plugins,cn=config"
+#define MEMBEROF_GOUP_ATTR "memberofgroupattr"
+#define MEMBEROF_ATTR "memberofattr"
+#define MEMBEROF_INCLUDE_SCOPE "memberOfEntryScope"
+#define MEMBEROF_EXLCUDE_SCOPE "memberOfEntryScopeExcludeSubtree"
+#define MEMBEROF_ALL_BACKENDS "memberOfAllBackends"
+#define MEMBEROF_SKIP_NESTED "memberOfSkipNested"
+#define MEMBEROF_ENABLED "nsslapd-pluginEnabled"
+
+struct memberof_plugin_config {
+ char **groupattrs;
+ char *memberof_attr;
+ PRBool all_backends;
+ PRBool skip_nested;
+ PRBool enabled;
+ char **include_scope;
+ char **exclude_scope;
+};
+static struct memberof_plugin_config *memberof_config = NULL;
+
+typedef struct _sm_memberof_get_groups_data {
+ Slapi_MemberOfConfig *config;
+ Slapi_Value *memberdn_val;
+ Slapi_ValueSet **groupvals; /* list of group DN which memberdn_val is member of */
+ Slapi_ValueSet **group_norm_vals;
+ Slapi_ValueSet **nsuniqueidvals; /* equivalent to groupvals but with nsuniqueid */
+ Slapi_ValueSet **already_seen_ndn_vals;
+ PRBool use_cache;
+} sm_memberof_get_groups_data;
+
+
+/* The key to access the hash table is the normalized DN
+ * The normalized DN is stored in the value because:
+ * - It is used in slapi_valueset_find
+ * - It is used to fill the memberof_get_groups_data.group_norm_vals
+ */
+typedef struct _sm_memberof_cached_value {
+ char *key;
+ char *group_dn_val;
+ char *group_ndn_val;
+ char *nsuniqueid_val;
+ int valid;
+} sm_memberof_cached_value;
+
+struct sm_cache_stat {
+ int total_lookup;
+ int successfull_lookup;
+ int total_add;
+ int total_remove;
+ int total_enumerate;
+ int cumul_duration_lookup;
+ int cumul_duration_add;
+ int cumul_duration_remove;
+ int cumul_duration_enumerate;
+};
+static struct sm_cache_stat sm_cache_stat;
+
+
+
+static sm_memberof_cached_value *sm_ancestors_cache_lookup(Slapi_MemberOfConfig *config, const char *ndn);
+static PLHashEntry *sm_ancestors_cache_add(Slapi_MemberOfConfig *config, const void *key, void *value);
+static void sm_ancestor_hashtable_entry_free(sm_memberof_cached_value *entry);
+static void sm_cache_ancestors(Slapi_MemberOfConfig *config, Slapi_Value **member_ndn_val, sm_memberof_get_groups_data *groups);
+static int sm_memberof_compare(Slapi_MemberOfConfig *config, const void *a, const void *b);
+static void sm_merge_ancestors(Slapi_Value **member_ndn_val, sm_memberof_get_groups_data *v1, sm_memberof_get_groups_data *v2);
+static int sm_memberof_entry_in_scope(Slapi_MemberOfConfig *config, Slapi_DN *sdn);
+static int sm_memberof_get_groups_callback(Slapi_Entry *e, void *callback_data);
+static void sm_report_error_msg(Slapi_MemberOfConfig *config, char* msg);
+static int sm_entry_get_groups(Slapi_MemberOfConfig *config, Slapi_DN *member_sdn,
+ Slapi_ValueSet *groupvals, Slapi_ValueSet *nsuniqueidvals);
+static PRBool sm_compare_memberof_config(const char *memberof_attr, char **groupattrs, PRBool all_backends,
+ PRBool skip_nested, Slapi_DN **include_scope, Slapi_DN **exclude_scope, PRBool enabled_only);
+static void sm_add_ancestors_cbdata(sm_memberof_cached_value *ancestors, void *callback_data);
+static int sm_memberof_call_foreach_dn(Slapi_PBlock *pb __attribute__((unused)), Slapi_DN *sdn, Slapi_MemberOfConfig *config, char **types,
+ plugin_search_entry_callback callback, void *callback_data, int *cached, PRBool use_grp_cache);
+static int sm_memberof_get_groups_r(Slapi_MemberOfConfig *config, Slapi_DN *member_sdn, sm_memberof_get_groups_data *data);
+static PRIntn sm_memberof_hash_compare_keys(const void *v1, const void *v2);
+static PRIntn sm_memberof_hash_compare_values(const void *v1, const void *v2);
+static PLHashNumber sm_memberof_hash_fn(const void *key);
+static PLHashTable *sm_hashtable_new(int usetxn);
+static PRIntn sm_ancestor_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)));
+static void sm_ancestor_hashtable_empty(Slapi_MemberOfConfig *config, char *msg);
+
+
+#if MEMBEROF_CACHE_DEBUG
+static void
+sm_dump_cache_entry(sm_memberof_cached_value *double_check, const char *msg)
+{
+ for (size_t i = 0; double_check[i].valid; i++) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_dump_cache_entry: %s -> %s/%s\n",
+ msg ? msg : "<no key>",
+ double_check[i].group_dn_val ? double_check[i].group_dn_val : "NULL",
+ double_check[i].nsuniqueid_val ? double_check[i].nsuniqueid_val : "NULL");
+ }
+}
+#endif
+
+static sm_memberof_cached_value *
+sm_ancestors_cache_lookup(Slapi_MemberOfConfig *config, const char *ndn)
+{
+ sm_memberof_cached_value *e;
+#if defined(DEBUG)
+ long int start;
+ struct timespec tsnow;
+#endif
+
+ sm_cache_stat.total_lookup++;
+
+#if defined(DEBUG)
+ if (clock_gettime(CLOCK_REALTIME, &tsnow) != 0) {
+ start = 0;
+ } else {
+ start = tsnow.tv_nsec;
+ }
+#endif
+
+ e = (sm_memberof_cached_value *) PL_HashTableLookupConst(config->ancestors_cache, (const void *) ndn);
+
+#if defined(DEBUG)
+ if (start) {
+ if (clock_gettime(CLOCK_REALTIME, &tsnow) == 0) {
+ sm_cache_stat.cumul_duration_lookup += (tsnow.tv_nsec - start);
+ }
+ }
+#endif
+
+ if (e)
+ sm_cache_stat.successfull_lookup++;
+ return e;
+}
+
+/* allocates the plugin hashtable
+ * This hash table is used by operation and is protected from
+ * concurrent operations with the memberof_lock (if not usetxn, memberof_lock
+ * is not implemented and the hash table will be not used.
+ *
+ * The hash table contains all the DN of the entries for which the memberof
+ * attribute has been computed/updated during the current operation
+ *
+ * hash table should be empty at the beginning and end of the plugin callback
+ */
+
+static void
+sm_ancestor_hashtable_entry_free(sm_memberof_cached_value *entry)
+{
+ size_t i;
+ if (entry == NULL) {
+ return;
+ }
+
+ /* entry[] is an array. The last element of the array contains 'valid'=0 */
+ for (i = 0; entry[i].valid; i++) {
+ slapi_ch_free_string(&entry[i].group_dn_val);
+ slapi_ch_free_string(&entry[i].group_ndn_val);
+ slapi_ch_free_string(&entry[i].nsuniqueid_val);
+ }
+ /* Here we are at the ending element containing the key */
+ slapi_ch_free_string(&entry[i].key);
+}
+
+static PLHashEntry *
+sm_ancestors_cache_add(Slapi_MemberOfConfig *config, const void *key, void *value)
+{
+ PLHashEntry *e;
+#if defined(DEBUG)
+ long int start;
+ struct timespec tsnow;
+#endif
+ sm_cache_stat.total_add++;
+
+#if defined(DEBUG)
+ if (clock_gettime(CLOCK_REALTIME, &tsnow) != 0) {
+ start = 0;
+ } else {
+ start = tsnow.tv_nsec;
+ }
+#endif
+
+ e = PL_HashTableAdd(config->ancestors_cache, key, value);
+
+#if defined(DEBUG)
+ if (start) {
+ if (clock_gettime(CLOCK_REALTIME, &tsnow) == 0) {
+ sm_cache_stat.cumul_duration_add += (tsnow.tv_nsec - start);
+ }
+ }
+#endif
+ return e;
+}
+
+/*
+ * A cache value consist of an array of all dn/ndn of the groups member_ndn_val belongs to
+ * The last element of the array has 'valid=0'
+ * the firsts elements of the array has 'valid=1' and the dn/ndn of group it belong to
+ */
+static void
+sm_cache_ancestors(Slapi_MemberOfConfig *config, Slapi_Value **member_ndn_val, sm_memberof_get_groups_data *groups)
+{
+ Slapi_ValueSet *groupvals = *((sm_memberof_get_groups_data *) groups)->groupvals;
+ Slapi_ValueSet *nsuniqueidvals = *((sm_memberof_get_groups_data *) groups)->nsuniqueidvals;
+ Slapi_Value *sval_dn = NULL;
+ Slapi_Value *sval_nsuniqueid = NULL;
+ Slapi_DN *sdn = NULL;
+ const char *dn = NULL;
+ const char *nsuniqueid = NULL;
+ const char *ndn = NULL;
+ const char *key = NULL;
+ char *key_copy = NULL;
+ int hint_dn = 0;
+ int hint_nsuniqueid = 0;
+ int count = 0;
+ size_t index;
+ sm_memberof_cached_value *cache_entry;
+#if MEMBEROF_CACHE_DEBUG
+ sm_memberof_cached_value *double_check;
+#endif
+
+ if ((member_ndn_val == NULL) || (*member_ndn_val == NULL)) {
+ slapi_log_err(SLAPI_LOG_FATAL, "slapi_memberof", "sm_cache_ancestors: Fail to cache groups ancestor of unknown member\n");
+ return;
+ }
+
+ /* Allocate the cache entry and fill it */
+ count = slapi_valueset_count(groupvals);
+ if (count == 0) {
+ /* There is no group containing member_ndn_val
+ * so cache the NULL value
+ */
+ cache_entry = (sm_memberof_cached_value *) slapi_ch_calloc(2, sizeof (sm_memberof_cached_value));
+ if (!cache_entry) {
+ slapi_log_err(SLAPI_LOG_FATAL, "slapi_memberof", "sm_cache_ancestors: Fail to cache no group are ancestor of %s\n",
+ slapi_value_get_string(*member_ndn_val));
+ return;
+ }
+ index = 0;
+ cache_entry[index].key = NULL; /* only the last element (valid=0) contains the key */
+ cache_entry[index].group_dn_val = NULL;
+ cache_entry[index].group_ndn_val = NULL;
+ cache_entry[index].nsuniqueid_val = NULL;
+ cache_entry[index].valid = 1; /* this entry is valid and indicate no group contain member_ndn_val */
+#if MEMBEROF_CACHE_DEBUG
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_cache_ancestors: For %s cache %s/%s\n",
+ slapi_value_get_string(*member_ndn_val),
+ cache_entry[index].group_dn_val ? cache_entry[index].group_dn_val : "<empty>",
+ cache_entry[index].nsuniqueid_val ? cache_entry[index].nsuniqueid_val : "<empty>");
+#endif
+ index++;
+
+ } else {
+ cache_entry = (sm_memberof_cached_value *) slapi_ch_calloc(count + 1, sizeof (sm_memberof_cached_value));
+ if (!cache_entry) {
+ slapi_log_err(SLAPI_LOG_FATAL, "slapi_memberof", "sm_cache_ancestors: Fail to cache groups ancestor of %s\n",
+ slapi_value_get_string(*member_ndn_val));
+ return;
+ }
+
+ /* Store the dn/ndn into the cache_entry */
+ index = 0;
+ hint_dn = slapi_valueset_first_value(groupvals, &sval_dn);
+ hint_nsuniqueid = slapi_valueset_first_value(nsuniqueidvals, &sval_nsuniqueid);
+ while (sval_dn) {
+ /* In case of recursion the member_ndn can be present
+ * in its own ancestors. Skip it
+ */
+ if (sm_memberof_compare(groups->config, member_ndn_val, &sval_dn)) {
+ /* add this dn/ndn even if it is NULL
+ * in fact a node belonging to no group needs to be cached
+ */
+ dn = slapi_value_get_string(sval_dn);
+ nsuniqueid = slapi_value_get_string(sval_nsuniqueid);
+ sdn = slapi_sdn_new_dn_byval(dn);
+ ndn = slapi_sdn_get_ndn(sdn);
+
+ cache_entry[index].key = NULL; /* only the last element (valid=0) contains the key */
+ cache_entry[index].group_dn_val = slapi_ch_strdup(dn);
+ cache_entry[index].group_ndn_val = slapi_ch_strdup(ndn);
+ cache_entry[index].nsuniqueid_val = slapi_ch_strdup(nsuniqueid);
+ cache_entry[index].valid = 1;
+#if MEMBEROF_CACHE_DEBUG
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_cache_ancestors: %s/%s\n",
+ cache_entry[index].group_dn_val ? cache_entry[index].group_dn_val : "<empty>",
+ cache_entry[index].nsuniqueid_val ? cache_entry[index].nsuniqueid_val : "<empty>");
+#endif
+ index++;
+ slapi_sdn_free(&sdn);
+ }
+
+ hint_dn = slapi_valueset_next_value(groupvals, hint_dn, &sval_dn);
+ hint_nsuniqueid = slapi_valueset_next_value(nsuniqueidvals, hint_nsuniqueid, &sval_nsuniqueid);
+ }
+ }
+ /* This is marking the end of the cache_entry */
+ key = slapi_value_get_string(*member_ndn_val);
+ key_copy = slapi_ch_strdup(key);
+ cache_entry[index].key = key_copy;
+ cache_entry[index].group_dn_val = NULL;
+ cache_entry[index].group_ndn_val = NULL;
+ cache_entry[index].nsuniqueid_val = NULL;
+ cache_entry[index].valid = 0;
+
+ /* Cache the ancestor of member_ndn_val using the
+ * normalized DN key
+ */
+#if MEMBEROF_CACHE_DEBUG
+ sm_dump_cache_entry(cache_entry, key);
+#endif
+ if (sm_ancestors_cache_add(config, (const void*) key_copy, (void *) cache_entry) == NULL) {
+ slapi_log_err(SLAPI_LOG_FATAL, "slapi_memberof", "sm_cache_ancestors: Failed to cache ancestor of %s\n", key);
+ sm_ancestor_hashtable_entry_free(cache_entry);
+ slapi_ch_free((void**) &cache_entry);
+ return;
+ }
+#if MEMBEROF_CACHE_DEBUG
+ if ((double_check = sm_ancestors_cache_lookup(config, (const void*) key)) != NULL) {
+ sm_dump_cache_entry(double_check, "read back");
+ }
+#endif
+}
+
+/* memberof_compare()
+ *
+ * compare two attr values
+ */
+static int
+sm_memberof_compare(Slapi_MemberOfConfig *config, const void *a, const void *b)
+{
+ Slapi_Value *val1;
+ Slapi_Value *val2;
+
+ if (a == NULL && b != NULL) {
+ return 1;
+ } else if (a != NULL && b == NULL) {
+ return -1;
+ } else if (a == NULL && b == NULL) {
+ return 0;
+ }
+ val1 = *((Slapi_Value **) a);
+ val2 = *((Slapi_Value **) b);
+
+ /* We only need to provide a Slapi_Attr here for it's syntax. We
+ * already validated all grouping attributes to use the Distinguished
+ * Name syntax, so we can safely just use the first attr.
+ */
+ return slapi_attr_value_cmp_ext(config->dn_syntax_attr, val1, val2);
+}
+
+/*
+ * Add in v2 the values that are in v1
+ * If the values are already present in v2, they are skipped
+ * It does not consum the values in v1
+ */
+static void
+sm_merge_ancestors(Slapi_Value **member_ndn_val, sm_memberof_get_groups_data *v1, sm_memberof_get_groups_data *v2)
+{
+ Slapi_Value *sval_dn = 0;
+ Slapi_Value *sval_ndn = 0;
+ Slapi_Value *sval_nsuniqueid = 0;
+ Slapi_Value *sval, *sval_2;
+ Slapi_DN *val_sdn = 0;
+ int hint = 0;
+ int hint_nsuniqueid = 0;
+ Slapi_MemberOfConfig *config = ((sm_memberof_get_groups_data *) v2)->config;
+ Slapi_ValueSet *v1_groupvals = *((sm_memberof_get_groups_data *) v1)->groupvals;
+ Slapi_ValueSet *v2_groupvals = *((sm_memberof_get_groups_data *) v2)->groupvals;
+ Slapi_ValueSet *v2_group_norm_vals = *((sm_memberof_get_groups_data *) v2)->group_norm_vals;
+ Slapi_ValueSet *v1_nsuniqueidvals = *((sm_memberof_get_groups_data *) v1)->nsuniqueidvals;
+ Slapi_ValueSet *v2_nsuniqueidvals = *((sm_memberof_get_groups_data *) v2)->nsuniqueidvals;
+ int merged_cnt = 0;
+
+ hint = slapi_valueset_first_value(v1_groupvals, &sval);
+ hint_nsuniqueid = slapi_valueset_first_value(v1_nsuniqueidvals, &sval_2);
+ while (sval) {
+ if (sm_memberof_compare(config, member_ndn_val, &sval)) {
+ sval_dn = slapi_value_new_string(slapi_value_get_string(sval));
+ sval_nsuniqueid = slapi_value_new_string(slapi_value_get_string(sval_2));
+ if (sval_dn) {
+ /* Use the normalized dn from v1 to search it in v2 */
+ val_sdn = slapi_sdn_new_dn_byval(slapi_value_get_string(sval_dn));
+ sval_ndn = slapi_value_new_string(slapi_sdn_get_ndn(val_sdn));
+ if (!slapi_valueset_find(((sm_memberof_get_groups_data *) v2)->config->dn_syntax_attr,
+ v2_group_norm_vals, sval_ndn)) {
+ /* This ancestor was not already present in v2 => Add it
+ * Using slapi_valueset_add_value it consumes val
+ * so do not free sval
+ */
+#if MEMBEROF_CACHE_DEBUG
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_merge_ancestors: add %s\n", slapi_value_get_string(sval_ndn));
+#endif
+ slapi_valueset_add_value_ext(v2_groupvals, sval_dn, SLAPI_VALUE_FLAG_PASSIN);
+ slapi_valueset_add_value_ext(v2_group_norm_vals, sval_ndn, SLAPI_VALUE_FLAG_PASSIN);
+ slapi_valueset_add_value_ext(v2_nsuniqueidvals, sval_nsuniqueid, SLAPI_VALUE_FLAG_PASSIN);
+ merged_cnt++;
+ } else {
+ /* This ancestor was already present, free sval_ndn/sval_dn that will not be consumed */
+#if MEMBEROF_CACHE_DEBUG
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_merge_ancestors: skip (already present) %s\n", slapi_value_get_string(sval_ndn));
+#endif
+ slapi_value_free(&sval_dn);
+ slapi_value_free(&sval_ndn);
+ slapi_value_free(&sval_nsuniqueid);
+ }
+ slapi_sdn_free(&val_sdn);
+ }
+ }
+ hint = slapi_valueset_next_value(v1_groupvals, hint, &sval);
+ hint_nsuniqueid = slapi_valueset_next_value(v1_nsuniqueidvals, hint_nsuniqueid, &sval_2);
+ }
+}
+
+/*
+ * Return 1 if the entry is in the scope.
+ * For MODRDN the caller should check both the preop
+ * and postop entries. If we are moving out of, or
+ * into scope, we should process it.
+ */
+static int
+sm_memberof_entry_in_scope(Slapi_MemberOfConfig *config, Slapi_DN *sdn)
+{
+ if (config->entryScopeExcludeSubtrees) {
+ /* check the excludes */
+ size_t i = 0;
+ while (config->entryScopeExcludeSubtrees[i]) {
+ if (slapi_sdn_issuffix(sdn, config->entryScopeExcludeSubtrees[i])) {
+ return 0;
+ }
+ i++;
+ }
+ }
+ if (config->entryScopes) {
+ /* check the excludes */
+ size_t i = 0;
+ while (config->entryScopes[i]) {
+ if (slapi_sdn_issuffix(sdn, config->entryScopes[i])) {
+ return 1;
+ }
+ i++;
+ }
+ return 0;
+ }
+ return 1;
+}
+
+/* memberof_get_groups_callback()
+ *
+ * Callback to perform work of memberof_get_groups()
+ */
+static int
+sm_memberof_get_groups_callback(Slapi_Entry *e, void *callback_data)
+{
+ Slapi_DN *group_sdn = slapi_entry_get_sdn(e);
+ char *group_ndn = slapi_entry_get_ndn(e);
+ char *group_dn = slapi_entry_get_dn(e);
+ const char *group_nsuniqueid = slapi_entry_get_uniqueid(e);
+ Slapi_Value *group_ndn_val = 0;
+ Slapi_Value *group_dn_val = 0;
+ Slapi_Value *group_nsuniqueid_val = 0;
+ Slapi_Value *already_seen_ndn_val = 0;
+ Slapi_ValueSet *groupvals = *((sm_memberof_get_groups_data *) callback_data)->groupvals;
+ Slapi_ValueSet *group_norm_vals = *((sm_memberof_get_groups_data *) callback_data)->group_norm_vals;
+ Slapi_ValueSet *group_nsuniqueid_vals = *((sm_memberof_get_groups_data *) callback_data)->nsuniqueidvals;
+ Slapi_ValueSet *already_seen_ndn_vals = *((sm_memberof_get_groups_data *) callback_data)->already_seen_ndn_vals;
+ Slapi_MemberOfConfig *config = ((sm_memberof_get_groups_data *) callback_data)->config;
+ int rc = 0;
+
+ if (slapi_is_shutting_down()) {
+ rc = -1;
+ goto bail;
+ }
+
+ if (config->maxgroups_reached) {
+ rc = -1;
+ goto bail;
+ }
+
+ if (!groupvals || !group_norm_vals) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof",
+ "sm_memberof_get_groups_callback - NULL groupvals or group_norm_vals\n");
+ rc = -1;
+ goto bail;
+ }
+ /* get the DN of the group */
+ group_ndn_val = slapi_value_new_string(group_ndn);
+ /* group_dn is case-normalized */
+ slapi_value_set_flags(group_ndn_val, SLAPI_ATTR_FLAG_NORMALIZED_CIS);
+
+ /* check if e is the same as our original member entry */
+ if (0 == sm_memberof_compare(((sm_memberof_get_groups_data *) callback_data)->config,
+ &((sm_memberof_get_groups_data *) callback_data)->memberdn_val, &group_ndn_val)) {
+ /* A recursive group caused us to find our original
+ * entry we passed to memberof_get_groups(). We just
+ * skip processing this entry. */
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof",
+ "sm_memberof_get_groups_callback - Group recursion"
+ " detected in %s\n",
+ group_ndn);
+ slapi_value_free(&group_ndn_val);
+ ((sm_memberof_get_groups_data *) callback_data)->use_cache = PR_FALSE;
+ goto bail;
+ }
+
+ /* Have we been here before? Note that we don't loop through all of the membership_slapiattrs
+ * in config. We only need this attribute for it's syntax so the comparison can be
+ * performed. Since all of the grouping attributes are validated to use the Dinstinguished
+ * Name syntax, we can safely just use the first group_slapiattr. */
+ if (slapi_valueset_find(
+ ((sm_memberof_get_groups_data *) callback_data)->config->dn_syntax_attr, already_seen_ndn_vals, group_ndn_val)) {
+ /* we either hit a recursive grouping, or an entry is
+ * a member of a group through multiple paths. Either
+ * way, we can just skip processing this entry since we've
+ * already gone through this part of the grouping hierarchy. */
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof",
+ "sm_memberof_get_groups_callback - Possible group recursion"
+ " detected in %s\n",
+ group_ndn);
+ slapi_value_free(&group_ndn_val);
+ ((sm_memberof_get_groups_data *) callback_data)->use_cache = PR_FALSE;
+ goto bail;
+ }
+
+ /* if the group does not belong to an excluded subtree, adds it to the valueset */
+ if (sm_memberof_entry_in_scope(config, group_sdn)) {
+ /* Push group_dn_val into the valueset. This memory is now owned
+ * by the valueset. */
+ slapi_valueset_add_value_ext(group_norm_vals, group_ndn_val, SLAPI_VALUE_FLAG_PASSIN);
+
+ group_dn_val = slapi_value_new_string(group_dn);
+ slapi_valueset_add_value_ext(groupvals, group_dn_val, SLAPI_VALUE_FLAG_PASSIN);
+
+ group_nsuniqueid_val = slapi_value_new_string(group_nsuniqueid);
+ slapi_valueset_add_value_ext(group_nsuniqueid_vals, group_nsuniqueid_val, SLAPI_VALUE_FLAG_PASSIN);
+
+ /* push this ndn to detect group recursion */
+ already_seen_ndn_val = slapi_value_new_string(group_ndn);
+ slapi_valueset_add_value_ext(already_seen_ndn_vals, already_seen_ndn_val, SLAPI_VALUE_FLAG_PASSIN);
+
+ config->current_maxgroup++;
+
+ /* Check we did not hit the maxgroup */
+ if ((config->maxgroups) &&
+ (config->current_maxgroup >= config->maxgroups)) {
+ char *msg = "slapi_memberof: sm_memberof_get_groups_callback result set truncated because of maxgroups limit (from computation)";
+ sm_report_error_msg(config, msg);
+ config->maxgroups_reached = PR_TRUE;
+ rc = -1;
+ goto bail;
+ }
+ }
+ if (config->recurse && (config->maxgroups_reached == PR_FALSE)) {
+ /* now recurse to find ancestors groups of e */
+ sm_memberof_get_groups_r(((sm_memberof_get_groups_data *) callback_data)->config,
+ group_sdn, callback_data);
+ }
+
+bail:
+ return rc;
+}
+
+/* Should be called at shutdown only */
+void
+slapi_memberof_free_memberof_plugin_config()
+{
+ struct memberof_plugin_config *sav;
+ if (memberof_config == NULL) {
+ return;
+ }
+ sav = memberof_config;
+ memberof_config = NULL;
+ slapi_ch_array_free(sav->groupattrs);
+ sav->groupattrs = NULL;
+
+ slapi_ch_array_free(sav->include_scope);
+ sav->include_scope = NULL;
+
+ slapi_ch_array_free(sav->exclude_scope);
+ sav->exclude_scope = NULL;
+
+ slapi_ch_free_string(&sav->memberof_attr);
+ slapi_ch_free((void **) &sav);
+}
+
+int
+slapi_memberof_load_memberof_plugin_config()
+{
+ Slapi_PBlock *entry_pb = NULL;
+ Slapi_Entry *config_entry = NULL;
+ Slapi_DN *config_sdn = NULL;
+ int rc;
+ char *attrs[8];
+ const char *allBackends = NULL;
+ const char *skip_nested = NULL;
+ const char *enabled = NULL;
+
+ /* if already loaded, we are done */
+ if (memberof_config) {
+ return 0;
+ }
+ memberof_config = (struct memberof_plugin_config *) slapi_ch_calloc(1, sizeof (struct memberof_plugin_config));
+
+
+ /* Retrieve the config entry */
+ config_sdn = slapi_sdn_new_normdn_byref(MEMBEROF_CONFIG_DN);
+ attrs[0] = MEMBEROF_GOUP_ATTR; /* e.g. member, uniquemember,... */
+ attrs[1] = MEMBEROF_ATTR; /* e.g. memberof */
+ attrs[2] = MEMBEROF_ALL_BACKENDS;
+ attrs[3] = MEMBEROF_INCLUDE_SCOPE;
+ attrs[4] = MEMBEROF_EXLCUDE_SCOPE;
+ attrs[5] = MEMBEROF_SKIP_NESTED;
+ attrs[6] = MEMBEROF_ENABLED;
+ attrs[7] = NULL;
+ rc = slapi_search_get_entry(&entry_pb, config_sdn, attrs, &config_entry, plugin_get_default_component_id());
+ slapi_sdn_free(&config_sdn);
+
+ if (rc != LDAP_SUCCESS || config_entry == NULL) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof",
+ "get_memberof_plugin_config - Failed to retrieve configuration entry %s: %d\n",
+ MEMBEROF_CONFIG_DN, rc);
+ slapi_search_get_entry_done(&entry_pb);
+ return (-1);
+ }
+ memberof_config->groupattrs = slapi_entry_attr_get_charray(config_entry, MEMBEROF_GOUP_ATTR);
+ memberof_config->memberof_attr = slapi_entry_attr_get_charptr(config_entry, MEMBEROF_ATTR);
+ allBackends = slapi_entry_attr_get_ref(config_entry, MEMBEROF_ALL_BACKENDS);
+ if (allBackends) {
+ if (strcasecmp(allBackends, "on") == 0) {
+ memberof_config->all_backends = PR_TRUE;
+ } else {
+ memberof_config->all_backends = PR_FALSE;
+ }
+ } else {
+ memberof_config->all_backends = PR_FALSE;
+ }
+ skip_nested = slapi_entry_attr_get_ref(config_entry, MEMBEROF_SKIP_NESTED);
+ if (skip_nested) {
+ if (strcasecmp(skip_nested, "on") == 0) {
+ memberof_config->skip_nested = PR_TRUE;
+ } else {
+ memberof_config->skip_nested = PR_FALSE;
+ }
+ } else {
+ memberof_config->skip_nested = PR_FALSE;
+ }
+ enabled = slapi_entry_attr_get_ref(config_entry, MEMBEROF_ENABLED);
+ if (enabled) {
+ if (strcasecmp(enabled, "on") == 0) {
+ memberof_config->enabled = PR_TRUE;
+ } else {
+ memberof_config->enabled = PR_FALSE;
+ }
+ } else {
+ memberof_config->enabled = PR_FALSE;
+ }
+ memberof_config->include_scope = slapi_entry_attr_get_charray(config_entry, MEMBEROF_INCLUDE_SCOPE);
+ memberof_config->exclude_scope = slapi_entry_attr_get_charray(config_entry, MEMBEROF_EXLCUDE_SCOPE);
+
+ slapi_search_get_entry_done(&entry_pb);
+ return (rc);
+}
+
+static void
+sm_report_error_msg(Slapi_MemberOfConfig *config, char* msg)
+{
+ int32_t len;
+
+ if ((config->error_msg == NULL) || (config->errot_msg_lenght == 0) || (msg == NULL)) {
+ return;
+ }
+ len = strlen(msg);
+ if ((config->errot_msg_lenght - 1) < len) {
+ len = config->errot_msg_lenght - 1;
+ }
+ strncpy(config->error_msg, msg, len);
+ config->error_msg[len] = '\0';
+}
+
+static int
+sm_entry_get_groups(Slapi_MemberOfConfig *config, Slapi_DN *member_sdn, Slapi_ValueSet *groupvals, Slapi_ValueSet *nsuniqueidvals)
+{
+ Slapi_PBlock *member_pb = NULL;
+ Slapi_PBlock *group_pb = NULL;
+ char **groups_dn;
+ Slapi_Entry *member_entry = NULL;
+ Slapi_Entry *group_entry = NULL;
+ Slapi_DN *group_sdn;
+ Slapi_Value *sval;
+ const char *nsuniqueid;
+ char *attrs[2];
+ int rc = 0;
+
+ /* Retrieve the 'memberof' from the target entry */
+ attrs[0] = (char *) config->memberof_attr;
+ attrs[1] = NULL;
+ rc = slapi_search_get_entry(&member_pb, member_sdn, attrs, &member_entry, plugin_get_default_component_id());
+ if (rc != LDAP_SUCCESS || member_entry == NULL) {
+ char *msg = "slapi_memberof: fails to retrieve the target entry";
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof",
+ "sm_entry_get_groups - Failed to retrieve target entry %s: %d\n",
+ slapi_sdn_get_ndn(member_sdn), rc);
+ slapi_search_get_entry_done(&member_pb);
+ sm_report_error_msg(config, msg);
+ return (-1);
+ }
+
+ /* For each group that the target entry is memberof, retrieve its dn/nsuniqueid */
+ groups_dn = slapi_entry_attr_get_charray(member_entry, config->memberof_attr);
+ attrs[0] = "nsuniqueid";
+ attrs[1] = NULL;
+ for (size_t i = 0; groups_dn && groups_dn[i]; i++) {
+ if ((config->maxgroups > 0) && (i >= config->maxgroups)) {
+ char *msg = "slapi_memberof: result set truncated because of maxgroups limit (from memberof)";
+ sm_report_error_msg(config, msg);
+ config->maxgroups_reached = PR_TRUE;
+ rc = -1;
+ goto common;
+ }
+ group_pb = NULL;
+ group_sdn = slapi_sdn_new_dn_byval(groups_dn[i]);
+ rc = slapi_search_get_entry(&group_pb, group_sdn, attrs, &group_entry, plugin_get_default_component_id());
+ if (rc != LDAP_SUCCESS || member_entry == NULL) {
+ char *msg = "slapi_memberof: fails to retrieve a group";
+ sm_report_error_msg(config, msg);
+
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof",
+ "sm_entry_get_groups - Failed to retrieve target entry %s: %d\n",
+ slapi_sdn_get_ndn(group_sdn), rc);
+ slapi_sdn_free(&group_sdn);
+ slapi_ch_array_free(groups_dn);
+ rc = -1;
+ goto common;
+ }
+
+ /* Now we retrieve a group */
+ /* add its nsuniqueid to the valuset */
+ nsuniqueid = slapi_entry_attr_get_ref(group_entry, (const char*) "nsuniqueid");
+ if (nsuniqueid) {
+ sval = slapi_value_new_string(nsuniqueid);
+ } else {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof",
+ "sm_entry_get_groups - Failed to retrieve the nsuniqueid of the target entry %s\n",
+ slapi_sdn_get_ndn(group_sdn));
+ sval = slapi_value_new_string("unknown-nsuniqueid");
+ }
+ slapi_valueset_add_value_ext(nsuniqueidvals, sval, SLAPI_VALUE_FLAG_PASSIN);
+ /* add its dn to the valuset */
+ sval = slapi_value_new_string(slapi_sdn_get_ndn(group_sdn));
+ slapi_valueset_add_value_ext(groupvals, sval, SLAPI_VALUE_FLAG_PASSIN);
+
+
+ slapi_sdn_free(&group_sdn);
+ slapi_search_get_entry_done(&group_pb);
+ }
+
+common:
+ slapi_ch_array_free(groups_dn);
+ slapi_search_get_entry_done(&member_pb);
+ return rc;
+}
+
+static PRBool
+sm_compare_memberof_config(const char *memberof_attr, char **groupattrs, PRBool all_backends, PRBool skip_nested, Slapi_DN **include_scope, Slapi_DN **exclude_scope, PRBool enabled_only)
+{
+ int32_t cnt1, cnt2;
+
+ if ((memberof_config == NULL) || (memberof_config->enabled == PR_FALSE)) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: config not initialized or disabled\n");
+ return PR_FALSE;
+ }
+
+ if (enabled_only) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: check the plugin is enabled that is %s\n",
+ memberof_config->enabled ? "SUCCEEDS" : "FAILS");
+ if (memberof_config->enabled) {
+ return PR_TRUE;
+ } else {
+ return PR_FALSE;
+ }
+ }
+
+ /* Check direct flags */
+ if ((all_backends != memberof_config->all_backends) || (skip_nested != memberof_config->skip_nested)) {
+ /* If those flags do not match the current set of 'memberof' values is invalid */
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails (allbackend %d vs %d, skip_nested %d vs %d)\n",
+ all_backends, memberof_config->all_backends, skip_nested, memberof_config->skip_nested);
+ return PR_FALSE;
+ }
+
+ /* Check that we are dealing with the same membership attribute
+ * e.g. 'memberof'
+ */
+ if ((memberof_attr == NULL) || (memberof_config->memberof_attr == NULL) || (strcasecmp(memberof_attr, memberof_config->memberof_attr))) {
+ /* just be conservative, we should speak about the same attribute */
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof",
+ "sm_compare_memberof_config: fails memberof attribute differs (require '%s' vs config '%s')\n",
+ memberof_attr ? memberof_attr : "NULL",
+ memberof_config->memberof_attr ? memberof_config->memberof_attr : NULL);
+ return PR_FALSE;
+ }
+
+ /* Check that the membership attributes are identical to the one
+ * in the memberof config. e.g. 'member', 'uniquemember'..
+ */
+ if (groupattrs == NULL) {
+ /* This is a mandatory parameter */
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested group attributes is empty\n");
+ return PR_FALSE;
+ }
+ for (cnt1 = 0; groupattrs[cnt1]; cnt1++) {
+ if (charray_inlist(memberof_config->groupattrs, groupattrs[cnt1]) == 0) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof",
+ "sm_compare_memberof_config: fails because requested group attribute '%s' is not configured\n",
+ groupattrs[cnt1]);
+ return PR_FALSE;
+ }
+ }
+ for (cnt2 = 0; memberof_config->groupattrs && memberof_config->groupattrs[cnt2]; cnt2++);
+ if (cnt1 != cnt2) {
+ /* make sure groupattrs is not a subset of memberof_config->groupattrs */
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because number of requested group attributes differs from config\n");
+ return PR_FALSE;
+ }
+
+ /* check Include scope that is optional */
+ if (include_scope == NULL) {
+ if (memberof_config->include_scope) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested include scope is empty that differs config\n");
+ return PR_FALSE;
+ }
+ } else {
+ if (memberof_config->include_scope == NULL) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested include scope is not empty that differs config\n");
+ return PR_FALSE;
+ }
+ }
+ /* here include scopes are both NULL or both not NULL */
+ for (cnt1 = 0; include_scope && include_scope[cnt1]; cnt1++) {
+ if (charray_inlist(memberof_config->include_scope, (char *) slapi_sdn_get_ndn(include_scope[cnt1])) == 0) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested include scope (%s) is not in config\n",
+ slapi_sdn_get_ndn(include_scope[cnt1]));
+ return PR_FALSE;
+ }
+ }
+ for (cnt2 = 0; memberof_config->include_scope && memberof_config->include_scope[cnt2]; cnt2++);
+ if (cnt1 != cnt2) {
+ /* make sure include_scope is not a subset of memberof_config->include_scope */
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because number of requested included scopes differs from config\n");
+ return PR_FALSE;
+ }
+
+ /* check Exclude scope that is optional */
+ if (exclude_scope == NULL) {
+ if (memberof_config->exclude_scope) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested exclude scope is empty that differs config\n");
+ return PR_FALSE;
+ }
+ } else {
+ if (memberof_config->exclude_scope == NULL) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested exclude scope is not empty that differs config\n");
+ return PR_FALSE;
+ }
+ }
+ /* here exclude scopes are both NULL or both not NULL */
+ for (cnt1 = 0; exclude_scope && exclude_scope[cnt1]; cnt1++) {
+ if (charray_inlist(memberof_config->exclude_scope, (char *) slapi_sdn_get_ndn(exclude_scope[cnt1])) == 0) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested exclude scope (%s) is not in config\n",
+ slapi_sdn_get_ndn(exclude_scope[cnt1]));
+ return PR_FALSE;
+ }
+ }
+ for (cnt2 = 0; memberof_config->exclude_scope && memberof_config->exclude_scope[cnt2]; cnt2++);
+ if (cnt1 != cnt2) {
+ /* make sure exclude_scope is not a subset of memberof_config->exclude_scope */
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because number of requested exclude scopes differs from config\n");
+ return PR_FALSE;
+ }
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: succeeds. requested options match config\n");
+ return PR_TRUE;
+}
+
+static void
+sm_add_ancestors_cbdata(sm_memberof_cached_value *ancestors, void *callback_data)
+{
+ Slapi_Value *sval;
+ size_t val_index;
+ Slapi_ValueSet *group_norm_vals_cbdata = *((sm_memberof_get_groups_data *) callback_data)->group_norm_vals;
+ Slapi_ValueSet *group_vals_cbdata = *((sm_memberof_get_groups_data *) callback_data)->groupvals;
+ Slapi_ValueSet *nsuniqueid_vals_cbdata = *((sm_memberof_get_groups_data *) callback_data)->nsuniqueidvals;
+ Slapi_Value *memberdn_val = ((sm_memberof_get_groups_data *) callback_data)->memberdn_val;
+ int added_group;
+ PRBool empty_ancestor = PR_FALSE;
+
+ for (val_index = 0, added_group = 0; ancestors[val_index].valid; val_index++) {
+ /* For each of its ancestor (not already present) add it to callback_data */
+
+ if (ancestors[val_index].group_ndn_val == NULL) {
+ /* This is a node with no ancestor
+ * ancestors should only contains this empty valid value
+ * but just in case let the loop continue instead of a break
+ */
+ empty_ancestor = PR_TRUE;
+ continue;
+ }
+
+ sval = slapi_value_new_string(ancestors[val_index].group_ndn_val);
+ if (sval) {
+ if (!slapi_valueset_find(
+ ((sm_memberof_get_groups_data *) callback_data)->config->dn_syntax_attr, group_norm_vals_cbdata, sval)) {
+ /* This ancestor was not already present in the callback data
+ * => Add it to the callback_data
+ * Using slapi_valueset_add_value it consumes sval
+ * so do not free sval
+ */
+ slapi_valueset_add_value_ext(group_norm_vals_cbdata, sval, SLAPI_VALUE_FLAG_PASSIN);
+ sval = slapi_value_new_string(ancestors[val_index].group_dn_val);
+ slapi_valueset_add_value_ext(group_vals_cbdata, sval, SLAPI_VALUE_FLAG_PASSIN);
+ sval = slapi_value_new_string(ancestors[val_index].nsuniqueid_val);
+ slapi_valueset_add_value_ext(nsuniqueid_vals_cbdata, sval, SLAPI_VALUE_FLAG_PASSIN);
+ added_group++;
+ } else {
+ /* This ancestor was already present, free sval that will not be consumed */
+ slapi_value_free(&sval);
+ }
+ }
+ }
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof",
+ "sm_add_ancestors_cbdata: Ancestors of %s contained %ld groups. %d added. %s\n",
+ slapi_value_get_string(memberdn_val), val_index, added_group,
+ empty_ancestor ? "no ancestors" : "");
+}
+
+/*
+ * Does a callback search of "type=dn" under the db suffix that "dn" is in,
+ * unless all_backends is set, then we look at all the backends. If "dn"
+ * is a user, you'd want "type" to be "member". If "dn" is a group, you
+ * could want type to be either "member" or "memberOf" depending on the case.
+ */
+static int
+sm_memberof_call_foreach_dn(Slapi_PBlock *pb __attribute__((unused)), Slapi_DN *sdn, Slapi_MemberOfConfig *config, char **types, plugin_search_entry_callback callback, void *callback_data, int *cached, PRBool use_grp_cache)
+{
+ Slapi_PBlock *search_pb = NULL;
+ Slapi_DN *base_sdn = NULL;
+ Slapi_Backend *be = NULL;
+ char *escaped_filter_val;
+ char *filter_str = NULL;
+ char *cookie = NULL;
+ int all_backends = config->allBackends;
+ int dn_len = slapi_sdn_get_ndn_len(sdn);
+ int free_it = 0;
+ int rc = 0;
+
+ *cached = 0;
+
+ if (!sm_memberof_entry_in_scope(config, sdn)) {
+ return (rc);
+ }
+
+ /* This flags indicates memberof_call_foreach_dn is called to retrieve ancestors (groups).
+ * To improve performance, it can use a cache. (it will not in case of circular groups)
+ * When this flag is true it means no circular group are detected (so far) so we can use the cache
+ */
+ if (use_grp_cache) {
+ /* Here we will retrieve the ancestor of sdn.
+ * The key access is the normalized sdn
+ * This is done through recursive internal searches of parents
+ * If the ancestors of sdn are already cached, just use
+ * this value
+ */
+ sm_memberof_cached_value *ht_grp = NULL;
+ const char *ndn = slapi_sdn_get_ndn(sdn);
+
+ ht_grp = sm_ancestors_cache_lookup(config, (const void *) ndn);
+ if (ht_grp) {
+#if MEMBEROF_CACHE_DEBUG
+ slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_memberof_call_foreach_dn: Ancestors of %s already cached (%p)\n", ndn, ht_grp);
+#endif
+ sm_add_ancestors_cbdata(ht_grp, callback_data);
+ *cached = 1;
+ return (rc);
+ }
+ }
+#if MEMBEROF_CACHE_DEBUG
+ slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_memberof_call_foreach_dn: Ancestors of %s not cached\n", slapi_sdn_get_ndn(sdn));
+#endif
+
+ /* Escape the dn, and build the search filter. */
+ escaped_filter_val = slapi_escape_filter_value((char *) slapi_sdn_get_dn(sdn), dn_len);
+ if (escaped_filter_val) {
+ free_it = 1;
+ } else {
+ escaped_filter_val = (char *) slapi_sdn_get_dn(sdn);
+ }
+
+ for (size_t i = 0; (types[i] && (config->maxgroups_reached == PR_FALSE)); i++) {
+ /* Triggers one internal search per membership attribute.
+ * Assuming the attribute is indexed (eq), the search will
+ * bypass the evaluation of the filter (nsslapd-search-bypass-filter-test)
+ * against the candidates. This is important to bypass the filter
+ * because on large valueset (static group) it is very expensive
+ */
+ filter_str = slapi_ch_smprintf("(%s=%s%s)", types[i], config->subtree_search ? "*" : "", escaped_filter_val);
+
+ be = slapi_get_first_backend(&cookie);
+ while ((config->maxgroups_reached == PR_FALSE) && be) {
+ PRBool do_suffix_search = PR_TRUE;
+
+ if (!all_backends) {
+ be = slapi_be_select(sdn);
+ if (be == NULL) {
+ break;
+ }
+ }
+ if ((base_sdn = (Slapi_DN *) slapi_be_getsuffix(be, 0)) == NULL) {
+ if (!all_backends) {
+ break;
+ } else {
+ /* its ok, goto the next backend */
+ be = slapi_get_next_backend(cookie);
+ continue;
+ }
+ }
+
+ search_pb = slapi_pblock_new();
+ if ((config->entryScopes && config->entryScopes[0]) ||
+ (config->entryScopeExcludeSubtrees && config->entryScopeExcludeSubtrees[0])) {
+ if (sm_memberof_entry_in_scope(config, base_sdn)) {
+ /* do nothing, entry scope is spanning
+ * multiple suffixes, start at suffix */
+ } else if (config->entryScopes) {
+ for (size_t i = 0; config->entryScopes[i]; i++) {
+ if (slapi_sdn_issuffix(config->entryScopes[i], base_sdn)) {
+ /* Search each include scope */
+ slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(config->entryScopes[i]),
+ LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0,
+ plugin_get_default_component_id(), 0);
+ slapi_search_internal_callback_pb(search_pb, callback_data, 0, callback, 0);
+ /* We already did the search for this backend, don't
+ * do it again when we fall through */
+ do_suffix_search = PR_FALSE;
+ }
+ }
+ } else if (!all_backends) {
+ slapi_pblock_destroy(search_pb);
+ break;
+ } else {
+ /* its ok, goto the next backend */
+ be = slapi_get_next_backend(cookie);
+ slapi_pblock_destroy(search_pb);
+ continue;
+ }
+ }
+
+ if (do_suffix_search) {
+ slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(base_sdn),
+ LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0,
+ plugin_get_default_component_id(), 0);
+ slapi_search_internal_callback_pb(search_pb, callback_data, 0, callback, 0);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS) {
+ slapi_pblock_destroy(search_pb);
+ break;
+ }
+ }
+
+ if (!all_backends) {
+ slapi_pblock_destroy(search_pb);
+ break;
+ }
+
+ be = slapi_get_next_backend(cookie);
+ slapi_pblock_destroy(search_pb);
+ }
+ slapi_ch_free((void **) &cookie);
+ slapi_ch_free_string(&filter_str);
+ }
+
+ if (free_it) {
+ slapi_ch_free_string(&escaped_filter_val);
+ }
+ return rc;
+}
+
+static int
+sm_memberof_get_groups_r(Slapi_MemberOfConfig *config, Slapi_DN *member_sdn, sm_memberof_get_groups_data *data)
+{
+ Slapi_ValueSet *groupvals = slapi_valueset_new();
+ Slapi_ValueSet *group_norm_vals = slapi_valueset_new();
+ Slapi_ValueSet *nsuniqueidvals = slapi_valueset_new();
+ Slapi_Value *member_ndn_val =
+ slapi_value_new_string(slapi_sdn_get_ndn(member_sdn));
+ int rc;
+ int cached = 0;
+
+ slapi_value_set_flags(member_ndn_val, SLAPI_ATTR_FLAG_NORMALIZED_CIS);
+
+ sm_memberof_get_groups_data member_data = {config, member_ndn_val, &groupvals, &group_norm_vals, &nsuniqueidvals, data->already_seen_ndn_vals, data->use_cache};
+
+ /* Search for any grouping attributes that point to memberdn.
+ * For each match, add it to the list, recurse and do same search */
+#if MEMBEROF_CACHE_DEBUG
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_memberof_get_groups_r: Ancestors of %s\n", slapi_sdn_get_dn(member_sdn));
+#endif
+ rc = sm_memberof_call_foreach_dn(NULL, member_sdn, config, config->groupattrs,
+ sm_memberof_get_groups_callback, &member_data,
+ &cached, member_data.use_cache);
+
+ sm_merge_ancestors(&member_ndn_val, &member_data, data);
+ if (!cached && member_data.use_cache)
+ sm_cache_ancestors(config, &member_ndn_val, &member_data);
+
+ slapi_value_free(&member_ndn_val);
+ slapi_valueset_free(groupvals);
+ slapi_valueset_free(group_norm_vals);
+ slapi_valueset_free(nsuniqueidvals);
+
+
+ return rc;
+}
+
+static PRIntn
+sm_memberof_hash_compare_keys(const void *v1, const void *v2)
+{
+ PRIntn rc;
+ if (0 == strcasecmp((const char *) v1, (const char *) v2)) {
+ rc = 1;
+ } else {
+ rc = 0;
+ }
+ return rc;
+}
+
+static PRIntn
+sm_memberof_hash_compare_values(const void *v1, const void *v2)
+{
+ PRIntn rc;
+ if ((char *) v1 == (char *) v2) {
+ rc = 1;
+ } else {
+ rc = 0;
+ }
+ return rc;
+}
+
+/*
+ * Hashing function using Bernstein's method
+ */
+static PLHashNumber
+sm_memberof_hash_fn(const void *key)
+{
+ PLHashNumber hash = 5381;
+ unsigned char *x = (unsigned char *) key;
+ int c;
+
+ while ((c = *x++)) {
+ hash = ((hash << 5) + hash) ^ c;
+ }
+ return hash;
+}
+
+/* allocates the plugin hashtable
+ * This hash table is used by operation and is protected from
+ * concurrent operations with the memberof_lock (if not usetxn, memberof_lock
+ * is not implemented and the hash table will be not used.
+ *
+ * The hash table contains all the DN of the entries for which the memberof
+ * attribute has been computed/updated during the current operation
+ *
+ * hash table should be empty at the beginning and end of the plugin callback
+ */
+static PLHashTable *
+sm_hashtable_new(int usetxn)
+{
+ if (!usetxn) {
+ return NULL;
+ }
+
+ return PL_NewHashTable(1000,
+ sm_memberof_hash_fn,
+ sm_memberof_hash_compare_keys,
+ sm_memberof_hash_compare_values, NULL, NULL);
+}
+
+/* this function called for each hash node during hash destruction */
+static PRIntn
+sm_ancestor_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)))
+{
+ sm_memberof_cached_value *group_ancestor_array;
+
+ if (he == NULL) {
+ return HT_ENUMERATE_NEXT;
+ }
+ group_ancestor_array = (sm_memberof_cached_value *) he->value;
+ sm_ancestor_hashtable_entry_free(group_ancestor_array);
+ slapi_ch_free((void **) &group_ancestor_array);
+
+ return HT_ENUMERATE_REMOVE;
+}
+
+static void
+sm_ancestor_hashtable_empty(Slapi_MemberOfConfig *config, char *msg)
+{
+ if (config->ancestors_cache) {
+ PL_HashTableEnumerateEntries(config->ancestors_cache, sm_ancestor_hashtable_remove, msg);
+ }
+}
+
+int
+slapi_memberof(Slapi_MemberOfConfig *config, Slapi_DN *member_sdn, Slapi_MemberOfResult *result)
+{
+ Slapi_ValueSet *groupvals;
+ Slapi_ValueSet *nsuniqueidvals;
+ Slapi_ValueSet *group_norm_vals;
+ Slapi_ValueSet *already_seen_ndn_vals;
+ Slapi_Value *memberdn_val;
+ Slapi_Attr *membership_slapiattrs;
+ int32_t rc = 0;
+
+ if (config == NULL || member_sdn == NULL || result == NULL) {
+ return -1;
+ }
+ config->maxgroups_reached = PR_FALSE;
+ config->current_maxgroup = 0;
+ if (config->error_msg) {
+ memset(config->error_msg, 0, config->errot_msg_lenght);
+ strcpy(config->error_msg, "no error msg");
+ }
+ groupvals = slapi_valueset_new();
+ nsuniqueidvals = slapi_valueset_new();
+ if (config->flag == MEMBEROF_REUSE_ONLY) {
+ if (sm_compare_memberof_config(NULL, NULL, PR_FALSE, PR_FALSE,
+ NULL, NULL, PR_TRUE)) {
+ /* Whatever the configuration of memberof plugin as long
+ * as it is enabled, return the groups referenced in the target entry
+ */
+ rc = sm_entry_get_groups(config, member_sdn, groupvals, nsuniqueidvals);
+ } else {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "memberof plugin is not enabled, with MEMBEROF_REUSE_ONLY return empty result");
+ }
+ } else if ((config->flag == MEMBEROF_REUSE_IF_POSSIBLE) &&
+ sm_compare_memberof_config(config->memberof_attr,
+ config->groupattrs,
+ config->allBackends,
+ !config->recurse,
+ config->entryScopes,
+ config->entryScopeExcludeSubtrees, PR_FALSE)) {
+ /* If the configuration of memberof plugin match the requested config
+ * (and the plugin is enabled), return the groups referenced in the target entry
+ */
+ rc = sm_entry_get_groups(config, member_sdn, groupvals, nsuniqueidvals);
+ } else {
+ /* This is RECOMPUTE mode or memberof plugin config does not satisfy
+ * the requested config, then recompute the membership
+ */
+ group_norm_vals = slapi_valueset_new();
+ already_seen_ndn_vals = slapi_valueset_new();
+ memberdn_val = slapi_value_new_string(slapi_sdn_get_ndn(member_sdn));
+ membership_slapiattrs = slapi_attr_new();
+ slapi_attr_init(membership_slapiattrs, config->groupattrs[0]);
+ config->memberof_attr = "memberof"; /* used to check if the shortcut of memberof plugin is possible */
+ config->dn_syntax_attr = membership_slapiattrs; /* all groupattrs are DN syntax, get the syntax from any of them */
+ config->maxgroups_reached = PR_FALSE;
+
+ slapi_value_set_flags(memberdn_val, SLAPI_ATTR_FLAG_NORMALIZED_CIS);
+
+ config->ancestors_cache = sm_hashtable_new(1);
+ sm_memberof_get_groups_data data = {config, memberdn_val, &groupvals, &group_norm_vals, &nsuniqueidvals, &already_seen_ndn_vals, PR_TRUE};
+
+ rc = sm_memberof_get_groups_r(config, member_sdn, &data);
+
+ slapi_attr_free(&membership_slapiattrs);
+ slapi_value_free(&memberdn_val);
+ slapi_valueset_free(group_norm_vals);
+ slapi_valueset_free(already_seen_ndn_vals);
+
+ if (config->ancestors_cache) {
+ sm_ancestor_hashtable_empty(config, "memberof_free_config empty group_ancestors_hashtable");
+ PL_HashTableDestroy(config->ancestors_cache);
+ config->ancestors_cache = NULL;
+ }
+ }
+
+ result->dn_vals = groupvals;
+ result->nsuniqueid_vals = nsuniqueidvals;
+
+ return rc;
+}
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index 79d04e418..d9d697a49 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -34,6 +34,7 @@ extern "C" {
#include "prprf.h"
#include "nspr.h"
#include <syslog.h>
+#include <plhash.h>
#ifdef __GNUC__
#define __ATTRIBUTE__(x) __attribute__(x)
@@ -8444,6 +8445,40 @@ int32_t slapi_search_get_entry(Slapi_PBlock **pb, Slapi_DN *dn, char **attrs, Sl
*/
void slapi_search_get_entry_done(Slapi_PBlock **pb);
+/* Those definitions are used to implement slapi_memberof() */
+typedef enum {
+ MEMBEROF_REUSE_ONLY,
+ MEMBEROF_REUSE_IF_POSSIBLE,
+ MEMBEROF_RECOMPUTE
+} memberof_flag_t;
+
+typedef struct _slapi_memberofresult {
+ Slapi_ValueSet *nsuniqueid_vals;
+ Slapi_ValueSet *dn_vals;
+ PRBool maxgroups_reached; /* flag is true if the number of groups hit the max limit */
+} Slapi_MemberOfResult;
+
+typedef struct _slapi_memberofconfig
+{
+ char **groupattrs;
+ PRBool subtree_search;
+ int allBackends;
+ Slapi_DN **entryScopes;
+ Slapi_DN **entryScopeExcludeSubtrees;
+ PRBool recurse;
+ int maxgroups;
+ memberof_flag_t flag;
+ char *error_msg;
+ int errot_msg_lenght;
+ int entryScopeCount; /* private to slapi_memberof */
+ int entryExcludeScopeCount; /* private to slapi_memberof */
+ PRBool maxgroups_reached; /* private to slapi_memberof */
+ const char *memberof_attr; /* private to slapi_memberof */
+ Slapi_Attr *dn_syntax_attr; /* private to slapi_memberof */
+ PLHashTable *ancestors_cache; /* private to slapi_memberof */
+ int current_maxgroup; /* private to slapi_memberof */
+} Slapi_MemberOfConfig;
+
#ifdef __cplusplus
}
#endif
diff --git a/ldap/servers/slapd/slapi-private.h b/ldap/servers/slapd/slapi-private.h
index bbf443dfc..17eedc2de 100644
--- a/ldap/servers/slapd/slapi-private.h
+++ b/ldap/servers/slapd/slapi-private.h
@@ -1258,6 +1258,11 @@ int mkdir_p(char *dir, unsigned int mode);
const char *ldif_getline_ro( const char **next);
void dup_ldif_line(struct berval *copy, const char *line, const char *endline);
+/* slapi-memberof.c */
+int slapi_memberof(Slapi_MemberOfConfig *config, Slapi_DN *member_sdn, Slapi_MemberOfResult *result);
+void slapi_memberof_free_memberof_plugin_config(void);
+int slapi_memberof_load_memberof_plugin_config(void);
+
/* lenstr stuff */
typedef struct _lenstr
diff --git a/ldap/servers/slapd/test-plugins/test_slapi_memberof.c b/ldap/servers/slapd/test-plugins/test_slapi_memberof.c
new file mode 100644
index 000000000..56626866b
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/test_slapi_memberof.c
@@ -0,0 +1,476 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (C) 2007 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+/**
+ * Distributed Numeric Assignment plug-in
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include "portable.h"
+#include "slap.h"
+#include "nspr.h"
+#include "slapi-private.h"
+#include "slapi-plugin.h"
+#include "prclist.h"
+
+#include <sys/stat.h>
+
+
+
+#define TEST_SLAPI_MEMBEROF_FEATURE_DESC "test slapi_memberof"
+#define TEST_SLAPI_MEMBEROF_EXOP_FEATURE_DESC "test slapi_memberof Extension Request"
+#define TEST_SLAPI_MEMBEROF_PLUGIN_DESC "test slapi_memberof plugin"
+#define TEST_SLAPI_MEMBEROF_EXOP_DESC "test slapi_memberof extop plugin"
+
+
+#define TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM "test-slapi_memberof-plugin"
+
+#define TEST_SLAPI_MEMBEROF_MEMBER_DN "slapimemberOfMemberDN"
+#define TEST_SLAPI_MEMBEROF_GROUP_ATTR "slapimemberOfGroupAttr"
+#define TEST_SLAPI_MEMBEROF_ATTR "slapimemberOfAttr"
+#define TEST_SLAPI_MEMBEROF_BACKEND_ATTR "slapimemberOfAllBackends"
+#define TEST_SLAPI_MEMBEROF_ENTRY_SCOPE_ATTR "slapimemberOfEntryScope"
+#define TEST_SLAPI_MEMBEROF_ENTRY_SCOPE_EXCLUDE_SUBTREE "slapimemberOfEntryScopeExcludeSubtree"
+#define TEST_SLAPI_MEMBEROF_SKIP_NESTED_ATTR "slapimemberOfSkipNested"
+#define TEST_SLAPI_MEMBEROF_MAXGROUP "slapimemberOfMaxGroup"
+#define TEST_SLAPI_MEMBEROF_FLAG "slapimemberOfFlag"
+
+#define MEMBEROF_RECOMPUTE_STR "MEMBEROF_RECOMPUTE"
+#define MEMBEROF_REUSE_IF_POSSIBLE_STR "MEMBEROF_REUSE_IF_POSSIBLE"
+#define MEMBEROF_REUSE_ONLY_STR "MEMBEROF_REUSE_ONLY"
+
+static Slapi_PluginDesc pdesc = {TEST_SLAPI_MEMBEROF_FEATURE_DESC,
+ "389 Project - test plugin",
+ "RHDS 9.3",
+ TEST_SLAPI_MEMBEROF_PLUGIN_DESC};
+
+static Slapi_PluginDesc exop_pdesc = {TEST_SLAPI_MEMBEROF_EXOP_FEATURE_DESC,
+ "389 Project - test plugin",
+ "RHDS 9.3",
+ TEST_SLAPI_MEMBEROF_EXOP_DESC};
+
+typedef struct test_slapi_memberof_config
+{
+ char *member_dn;
+ Slapi_DN *sdn_member_dn;
+ char **groupattrs;
+ char *memberof_attr;
+ int32_t maxgroup;
+ memberof_flag_t flag;
+ PRBool allBackends;
+ PRBool skip_nested;
+ char **entryScopes;
+ Slapi_DN **sdn_entryScopes;
+ char **entryScopeExcludeSubtrees;
+ Slapi_DN **sdn_entryScopeExcludeSubtrees;
+} Test_Slapi_MemberOf_Config;
+static Test_Slapi_MemberOf_Config theConfig = {0};
+static void *_PluginID = NULL;
+
+#define TEST_SLAPI_MEMBEROF_EXOP_REQUEST_OID "2.3.4.5.113730.6.7.1"
+#define TEST_SLAPI_MEMBEROF_EXOP_RESPONSE_OID "2.3.4.5.113730.6.7.2"
+static char *test_slapi_memberof_exop_oid_list[] = {
+ TEST_SLAPI_MEMBEROF_EXOP_REQUEST_OID,
+ NULL};
+
+
+int test_slapi_memberof_init(Slapi_PBlock *pb);
+static int test_slapi_memberof_exop_init(Slapi_PBlock *pb);
+static int test_slapi_memberof_extend_exop(Slapi_PBlock *pb);
+static int test_slapi_memberof_start(Slapi_PBlock *pb);
+static int test_slapi_memberof_close(Slapi_PBlock *pb __attribute__((unused)));
+
+
+/**
+ * Plugin identity mgmt
+ */
+void
+setPluginID(void *pluginID)
+{
+ _PluginID = pluginID;
+}
+
+void *
+getPluginID(void)
+{
+ return _PluginID;
+}
+
+
+/*
+ test_slapi_memberof_init plugin init function
+*/
+int
+test_slapi_memberof_init(Slapi_PBlock *pb)
+{
+ int status = SLAPI_PLUGIN_SUCCESS;
+ char *plugin_identity = NULL;
+
+ slapi_log_err(SLAPI_LOG_NOTICE, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "--> test_slapi_memberof_init\n");
+
+ /**
+ * Store the plugin identity for later use.
+ * Used for internal operations
+ */
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
+ PR_ASSERT(plugin_identity);
+ setPluginID(plugin_identity);
+
+ if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *)test_slapi_memberof_start) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *)test_slapi_memberof_close) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc) != 0) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_init - Failed to register plugin\n");
+ status = SLAPI_PLUGIN_FAILURE;
+ }
+
+ if ((status == SLAPI_PLUGIN_SUCCESS) &&
+ /* the range extension extended operation */
+ slapi_register_plugin("extendedop", /* op type */
+ 1, /* Enabled */
+ "test_slapi_memberof_init", /* this function desc */
+ test_slapi_memberof_exop_init, /* init func for exop */
+ TEST_SLAPI_MEMBEROF_EXOP_DESC, /* plugin desc */
+ NULL, /* ? */
+ plugin_identity /* access control */
+ )) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_init - Failed to register plugin\n");
+ status = SLAPI_PLUGIN_FAILURE;
+ }
+
+
+ slapi_log_err(SLAPI_LOG_NOTICE, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "<-- test_slapi_memberof_init\n");
+ return status;
+}
+
+
+
+
+static int
+test_slapi_memberof_exop_init(Slapi_PBlock *pb)
+{
+ int status = SLAPI_PLUGIN_SUCCESS;
+
+ if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&exop_pdesc) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_OIDLIST,
+ (void *)test_slapi_memberof_exop_oid_list) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_FN,
+ (void *)test_slapi_memberof_extend_exop) != 0) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_exop_init - Failed to register plugin\n");
+ status = SLAPI_PLUGIN_FAILURE;
+ }
+
+ return status;
+}
+
+static int
+test_slapi_memberof_start(Slapi_PBlock *pb)
+{
+ Slapi_Entry *config_e = NULL; /* entry containing plugin config */
+ char **groupattrs = NULL;
+ char *memberof_attr = NULL;
+ char *member_dn = NULL;
+ int maxgroup = 0;
+ const char *allBackends = NULL;
+ const char *skip_nested = NULL;
+ char **entryScopes = NULL;
+ char **entryScopeExcludeSubtrees = NULL;
+ char *flag;
+ size_t i;
+
+ if (slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &config_e) != 0) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_start - Missing config entry\n");
+ return SLAPI_PLUGIN_FAILURE;
+ }
+ groupattrs = slapi_entry_attr_get_charray(config_e, TEST_SLAPI_MEMBEROF_GROUP_ATTR);
+ memberof_attr = slapi_entry_attr_get_charptr(config_e, TEST_SLAPI_MEMBEROF_ATTR);
+ member_dn = slapi_entry_attr_get_charptr(config_e, TEST_SLAPI_MEMBEROF_MEMBER_DN);
+ allBackends = slapi_entry_attr_get_ref(config_e, TEST_SLAPI_MEMBEROF_BACKEND_ATTR);
+ skip_nested = slapi_entry_attr_get_ref(config_e, TEST_SLAPI_MEMBEROF_SKIP_NESTED_ATTR);
+ entryScopes = slapi_entry_attr_get_charray(config_e, TEST_SLAPI_MEMBEROF_ENTRY_SCOPE_ATTR);
+ entryScopeExcludeSubtrees = slapi_entry_attr_get_charray(config_e, TEST_SLAPI_MEMBEROF_ENTRY_SCOPE_EXCLUDE_SUBTREE);
+ maxgroup = slapi_entry_attr_get_int(config_e, TEST_SLAPI_MEMBEROF_MAXGROUP);
+ flag = slapi_entry_attr_get_charptr(config_e, TEST_SLAPI_MEMBEROF_FLAG);
+
+ theConfig.groupattrs = groupattrs;
+ theConfig.member_dn = member_dn;
+ theConfig.sdn_member_dn = slapi_sdn_new_dn_byval(member_dn);
+ theConfig.memberof_attr = memberof_attr;
+ if (skip_nested) {
+ if (strcasecmp(skip_nested, "on") == 0) {
+ theConfig.skip_nested = PR_TRUE;
+ } else {
+ theConfig.skip_nested = PR_FALSE;
+ }
+ } else {
+ theConfig.skip_nested = PR_FALSE;
+ }
+
+ if (allBackends) {
+ if (strcasecmp(allBackends, "on") == 0) {
+ theConfig.allBackends = PR_TRUE;
+ } else {
+ theConfig.allBackends = PR_FALSE;
+ }
+ } else {
+ theConfig.allBackends = PR_FALSE;
+ }
+ theConfig.entryScopes = entryScopes;
+ for (i = 0; entryScopes && entryScopes[i]; i++);
+ theConfig.sdn_entryScopes = (Slapi_DN **) slapi_ch_calloc(sizeof(Slapi_DN *), i + 1);
+ for (i = 0; entryScopes && entryScopes[i]; i++) {
+ theConfig.sdn_entryScopes[i] = slapi_sdn_new_dn_byval((const char *)entryScopes[i]);
+ }
+
+ theConfig.entryScopeExcludeSubtrees = entryScopeExcludeSubtrees;
+ for (i = 0; entryScopeExcludeSubtrees && entryScopeExcludeSubtrees[i]; i++);
+ theConfig.sdn_entryScopeExcludeSubtrees = (Slapi_DN **) slapi_ch_calloc(sizeof(Slapi_DN *), i + 1);
+ for (i = 0; entryScopeExcludeSubtrees && entryScopeExcludeSubtrees[i]; i++) {
+ theConfig.sdn_entryScopeExcludeSubtrees[i] = slapi_sdn_new_dn_byval((const char *)entryScopeExcludeSubtrees[i]);
+ }
+ theConfig.maxgroup = maxgroup;
+
+ /* By default we are recomputing the membership MEMBEROF_RECOMPUTE */
+ if (flag == NULL) {
+ theConfig.flag = MEMBEROF_RECOMPUTE;
+ } else if (strcasecmp(flag, MEMBEROF_RECOMPUTE_STR) == 0) {
+ theConfig.flag = MEMBEROF_RECOMPUTE;
+ } else if (strcasecmp(flag, MEMBEROF_REUSE_IF_POSSIBLE_STR) == 0) {
+ theConfig.flag = MEMBEROF_REUSE_IF_POSSIBLE;
+ } else if (strcasecmp(flag, MEMBEROF_REUSE_ONLY_STR) == 0) {
+ theConfig.flag = MEMBEROF_REUSE_ONLY;
+ } else {
+ theConfig.flag = MEMBEROF_RECOMPUTE;
+ }
+
+ return SLAPI_PLUGIN_SUCCESS;
+}
+
+static int
+test_slapi_memberof_close(Slapi_PBlock *pb __attribute__((unused)))
+{
+ return SLAPI_PLUGIN_SUCCESS;
+}
+
+/****************************************************
+ * Test Slapi_memberof Extended Operation
+ ***************************************************/
+static int
+test_slapi_memberof_extend_exop(Slapi_PBlock *pb)
+{
+ char *oid = NULL;
+ int ret = SLAPI_PLUGIN_EXTENDED_NOT_HANDLED;
+ int i;
+ int idx = 0;
+ int count;
+ char error_buffer[1024] = {0};
+ int32_t error_buffer_size;
+ Slapi_MemberOfConfig config = {0};
+ Slapi_MemberOfResult groupvals = {0};
+ Slapi_DN *target_sdn;
+ BerElement *req_bere = NULL;
+ struct berval *reqdata = NULL;
+ char *req_dn = NULL;
+ BerElement *respber = NULL;
+ struct berval *respdata = NULL;
+ Slapi_Value *v;
+ struct berval **returned_bervals = NULL;
+ char **returned_array = NULL;
+
+ error_buffer_size = sizeof(error_buffer);
+
+ if (!slapi_plugin_running(pb)) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - plugin not started\n");
+ return ret;
+ }
+
+ slapi_log_err(SLAPI_LOG_NOTICE, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "--> test_slapi_memberof_extend_exop\n");
+ /* Fetch the request OID */
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_OID, &oid);
+ if (!oid) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - Unable to retrieve request OID.\n");
+ return ret;
+ }
+
+ /* decode the exop */
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &reqdata);
+ if (BV_HAS_DATA(reqdata)) {
+ req_bere = ber_init(reqdata);
+ if (req_bere == NULL) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - Failed to decode/init the DN from the request.\n");
+ return ret;
+ }
+ ber_scanf(req_bere, "a", &req_dn);
+ ber_free(req_bere, 1);
+ req_bere = NULL;
+ }
+
+ if (req_dn) {
+ slapi_log_err(SLAPI_LOG_NOTICE, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "--> Target entry is %s\n", req_dn);
+ } else {
+ slapi_log_err(SLAPI_LOG_NOTICE, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "--> Target entry is (fallback) %s\n", theConfig.member_dn);
+ }
+
+ /* Make sure the request OID is correct. */
+ if (strcmp(oid, TEST_SLAPI_MEMBEROF_EXOP_REQUEST_OID) != 0) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - Received incorrect request OID.\n");
+ return ret;
+ }
+
+ /* Checking the incoming attributes */
+ if (theConfig.member_dn == NULL) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - missing %s attribute (e.g. 'uid=user0,ou=people,dc=example,dc=com').\n",
+ TEST_SLAPI_MEMBEROF_MEMBER_DN);
+ strncpy(error_buffer, "missing slapimemberOfMemberDN", error_buffer_size - 1);
+ goto skip_it;
+ }
+ if (theConfig.memberof_attr == NULL) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - missing %s attribute (e.g. 'memberof').\n",
+ TEST_SLAPI_MEMBEROF_ATTR);
+ strncpy(error_buffer, "missing slapimemberOfAttr", error_buffer_size - 1);
+ goto skip_it;
+ }
+ if ((theConfig.groupattrs == NULL) || (theConfig.groupattrs[0] == NULL)) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - missing %s attribute (e.g. 'member' or 'manager').\n",
+ TEST_SLAPI_MEMBEROF_GROUP_ATTR);
+ strncpy(error_buffer, "missing slapimemberOfGroupAttr", error_buffer_size - 1);
+ goto skip_it;
+ }
+ if ((theConfig.entryScopes == NULL) || (theConfig.entryScopes[0] == NULL)) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - missing %s attribute (e.g. 'dc=example,dc=com').\n",
+ TEST_SLAPI_MEMBEROF_ENTRY_SCOPE_ATTR);
+ strncpy(error_buffer, "missing slapimemberOfEntryScope", error_buffer_size - 1);
+ goto skip_it;
+ }
+
+ config.memberof_attr = (const char *) theConfig.memberof_attr;
+ config.groupattrs = theConfig.groupattrs;
+ config.allBackends = theConfig.allBackends;
+ config.recurse = (! theConfig.skip_nested);
+
+ config.entryScopes = theConfig.sdn_entryScopes;
+ config.entryScopeExcludeSubtrees = theConfig.sdn_entryScopeExcludeSubtrees;
+ config.maxgroups = theConfig.maxgroup;
+ config.flag = theConfig.flag;
+ config.error_msg = error_buffer;
+ config.errot_msg_lenght = error_buffer_size;
+ config.subtree_search = PR_FALSE;
+ if (req_dn) {
+ target_sdn = slapi_sdn_new_dn_byval(req_dn);
+ } else {
+ target_sdn = theConfig.sdn_member_dn;
+ }
+ ret = slapi_memberof(&config, target_sdn, &groupvals);
+ if (req_dn) {
+ slapi_sdn_free(&target_sdn);
+ }
+ slapi_log_err(SLAPI_LOG_NOTICE, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - slapi_memberof -> %d).\n",
+ ret);
+ /* Just fo tracing purpose log the memberships in error logs */
+ for (i = slapi_valueset_first_value(groupvals.dn_vals, &v);
+ i != -1;
+ i = slapi_valueset_next_value(groupvals.dn_vals, i, &v)) {
+ slapi_log_err(SLAPI_LOG_NOTICE, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - slapi_memberof found %s.\n",
+ slapi_value_get_string(v));
+ }
+
+
+skip_it:
+ if ((respber = ber_alloc()) == NULL) {
+ ret = LDAP_NO_MEMORY;
+ goto free_and_return;
+ }
+ count = slapi_valueset_count(groupvals.dn_vals);
+ if (count) {
+ returned_bervals = (struct berval **)slapi_ch_malloc(sizeof(struct berval *) * (count + 1));
+ returned_bervals[count] = NULL;
+ for (i = slapi_valueset_first_value(groupvals.dn_vals, &v), idx = 0;
+ i != -1;
+ i = slapi_valueset_next_value(groupvals.dn_vals, i, &v), idx++) {
+ returned_bervals[idx] = (struct berval *)slapi_ch_malloc(sizeof(struct berval));
+ returned_bervals[idx]->bv_val = slapi_ch_strdup(slapi_value_get_string(v));
+ returned_bervals[idx]->bv_len = strlen(slapi_value_get_string(v));
+ }
+ returned_array = (char **) slapi_ch_malloc(sizeof(char *) * (count + 1));
+ returned_array[count] = NULL;
+ for (i = slapi_valueset_first_value(groupvals.dn_vals, &v), idx = 0;
+ i != -1;
+ i = slapi_valueset_next_value(groupvals.dn_vals, i, &v), idx++) {
+ returned_array[idx] = slapi_ch_strdup(slapi_value_get_string(v));
+ }
+ if (LBER_ERROR == (ber_printf(respber, "[V]", returned_bervals))) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - Unable to encode exop response.\n");
+ ber_free(respber, 1);
+ ret = LDAP_ENCODING_ERROR;
+ goto free_and_return;
+ }
+ } else {
+ if (LBER_ERROR == (ber_printf(respber, "{s}", error_buffer))) {
+ slapi_log_err(SLAPI_LOG_ERR, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "test_slapi_memberof_extend_exop - Unable to encode exop response.\n");
+ ber_free(respber, 1);
+ ret = LDAP_ENCODING_ERROR;
+ goto free_and_return;
+ }
+ }
+ ber_flatten(respber, &respdata);
+ ber_free(respber, 1);
+
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, TEST_SLAPI_MEMBEROF_EXOP_RESPONSE_OID);
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, respdata);
+
+ /* send the response ourselves */
+ slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
+ ret = SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
+ ber_bvfree(respdata);
+
+free_and_return:
+
+ if (returned_bervals)
+ ber_bvecfree(returned_bervals);
+ slapi_log_err(SLAPI_LOG_NOTICE, TEST_SLAPI_MEMBEROF_PLUGIN_SUBSYSTEM,
+ "<-- test_slapi_memberof_extend_exop\n");
+
+ return ret;
+}
+
--
2.41.0