|
|
081b2d |
From 4af03a6a2a59684950d887d42c6e9d8b027d71f5 Mon Sep 17 00:00:00 2001
|
|
|
081b2d |
From: Thierry Bordaz <tbordaz@redhat.com>
|
|
|
081b2d |
Date: Mon, 16 Oct 2017 11:21:51 +0200
|
|
|
081b2d |
Subject: [PATCH] Ticket 49064 - RFE allow to enable MemberOf plugin in
|
|
|
081b2d |
dedicated consumer
|
|
|
081b2d |
|
|
|
081b2d |
Bug Description:
|
|
|
081b2d |
memberof triggers some internal updates to add/del 'memberof' values.
|
|
|
081b2d |
on a readonly consumer, those updates selects a REFERRAL_ON_UPDATE backend
|
|
|
081b2d |
and that is not followed by internal updates.
|
|
|
081b2d |
At the end of the day, the update is rejected and if memberof plugin is enabled
|
|
|
081b2d |
replication will stuck on that rejected update
|
|
|
081b2d |
|
|
|
081b2d |
Fix Description:
|
|
|
081b2d |
internal updates from memberof need to bypassing referrals.
|
|
|
081b2d |
So they flag internal updates SLAPI_OP_FLAG_BYPASS_REFERRALS, so that mtn_get_be
|
|
|
081b2d |
(mapping tree selection) will not return the referrals.
|
|
|
081b2d |
|
|
|
081b2d |
https://pagure.io/389-ds-base/issue/49064
|
|
|
081b2d |
|
|
|
081b2d |
Reviewed by: Ludwig Krispenz, William Brown (thanks a LOT !)
|
|
|
081b2d |
|
|
|
081b2d |
Platforms tested: F23 (all tickets + basic suite)
|
|
|
081b2d |
|
|
|
081b2d |
Flag Day: no
|
|
|
081b2d |
|
|
|
081b2d |
Doc impact: no
|
|
|
081b2d |
---
|
|
|
081b2d |
dirsrvtests/tests/tickets/ticket49064_test.py | 259 ++++++++++++++++++++++++++
|
|
|
081b2d |
ldap/servers/plugins/memberof/memberof.c | 6 +-
|
|
|
081b2d |
2 files changed, 262 insertions(+), 3 deletions(-)
|
|
|
081b2d |
create mode 100644 dirsrvtests/tests/tickets/ticket49064_test.py
|
|
|
081b2d |
|
|
|
081b2d |
diff --git a/dirsrvtests/tests/tickets/ticket49064_test.py b/dirsrvtests/tests/tickets/ticket49064_test.py
|
|
|
081b2d |
new file mode 100644
|
|
|
081b2d |
index 000000000..b4b6de4b9
|
|
|
081b2d |
--- /dev/null
|
|
|
081b2d |
+++ b/dirsrvtests/tests/tickets/ticket49064_test.py
|
|
|
081b2d |
@@ -0,0 +1,259 @@
|
|
|
081b2d |
+import logging
|
|
|
081b2d |
+import pytest
|
|
|
081b2d |
+import os
|
|
|
081b2d |
+import time
|
|
|
081b2d |
+import ldap
|
|
|
081b2d |
+import subprocess
|
|
|
081b2d |
+from lib389.utils import ds_is_older
|
|
|
081b2d |
+from lib389.topologies import topology_m1h1c1 as topo
|
|
|
081b2d |
+from lib389._constants import *
|
|
|
081b2d |
+from lib389 import Entry
|
|
|
081b2d |
+
|
|
|
081b2d |
+# Skip on older versions
|
|
|
081b2d |
+pytestmark = pytest.mark.skipif(ds_is_older('1.3.7'), reason="Not implemented")
|
|
|
081b2d |
+
|
|
|
081b2d |
+USER_CN='user_'
|
|
|
081b2d |
+GROUP_CN='group_'
|
|
|
081b2d |
+FIXUP_FILTER = '(objectClass=*)'
|
|
|
081b2d |
+FIXUP_CMD = 'fixup-memberof.pl'
|
|
|
081b2d |
+
|
|
|
081b2d |
+DEBUGGING = os.getenv("DEBUGGING", default=False)
|
|
|
081b2d |
+if DEBUGGING:
|
|
|
081b2d |
+ logging.getLogger(__name__).setLevel(logging.DEBUG)
|
|
|
081b2d |
+else:
|
|
|
081b2d |
+ logging.getLogger(__name__).setLevel(logging.INFO)
|
|
|
081b2d |
+log = logging.getLogger(__name__)
|
|
|
081b2d |
+
|
|
|
081b2d |
+def memberof_fixup_task(server):
|
|
|
081b2d |
+ sbin_dir = server.get_sbin_dir()
|
|
|
081b2d |
+ memof_task = os.path.join(sbin_dir, FIXUP_CMD)
|
|
|
081b2d |
+ try:
|
|
|
081b2d |
+ output = subprocess.check_output(
|
|
|
081b2d |
+ [memof_task, '-D', DN_DM, '-w', PASSWORD, '-b', SUFFIX, '-Z', SERVERID_CONSUMER_1, '-f', FIXUP_FILTER])
|
|
|
081b2d |
+ except subprocess.CalledProcessError as err:
|
|
|
081b2d |
+ output = err.output
|
|
|
081b2d |
+ log.info('output: {}'.format(output))
|
|
|
081b2d |
+ expected = "Successfully added task entry"
|
|
|
081b2d |
+ assert expected in output
|
|
|
081b2d |
+
|
|
|
081b2d |
+def config_memberof(server):
|
|
|
081b2d |
+
|
|
|
081b2d |
+ server.plugins.enable(name=PLUGIN_MEMBER_OF)
|
|
|
081b2d |
+ MEMBEROF_PLUGIN_DN = ('cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config')
|
|
|
081b2d |
+ server.modify_s(MEMBEROF_PLUGIN_DN, [(ldap.MOD_REPLACE,
|
|
|
081b2d |
+ 'memberOfAllBackends',
|
|
|
081b2d |
+ 'on'),
|
|
|
081b2d |
+ (ldap.MOD_REPLACE, 'memberOfAutoAddOC', 'nsMemberOf')])
|
|
|
081b2d |
+ # Configure fractional to prevent total init to send memberof
|
|
|
081b2d |
+ ents = server.agreement.list(suffix=DEFAULT_SUFFIX)
|
|
|
081b2d |
+ for ent in ents:
|
|
|
081b2d |
+ log.info('update %s to add nsDS5ReplicatedAttributeListTotal' % ent.dn)
|
|
|
081b2d |
+ server.modify_s(ent.dn,
|
|
|
081b2d |
+ [(ldap.MOD_REPLACE,
|
|
|
081b2d |
+ 'nsDS5ReplicatedAttributeListTotal',
|
|
|
081b2d |
+ '(objectclass=*) $ EXCLUDE '),
|
|
|
081b2d |
+ (ldap.MOD_REPLACE,
|
|
|
081b2d |
+ 'nsDS5ReplicatedAttributeList',
|
|
|
081b2d |
+ '(objectclass=*) $ EXCLUDE memberOf')])
|
|
|
081b2d |
+
|
|
|
081b2d |
+
|
|
|
081b2d |
+def send_updates_now(server):
|
|
|
081b2d |
+
|
|
|
081b2d |
+ ents = server.agreement.list(suffix=DEFAULT_SUFFIX)
|
|
|
081b2d |
+ for ent in ents:
|
|
|
081b2d |
+ server.agreement.pause(ent.dn)
|
|
|
081b2d |
+ server.agreement.resume(ent.dn)
|
|
|
081b2d |
+
|
|
|
081b2d |
+def add_user(server, no, desc='dummy', sleep=True):
|
|
|
081b2d |
+ cn = '%s%d' % (USER_CN, no)
|
|
|
081b2d |
+ dn = 'cn=%s,ou=people,%s' % (cn, SUFFIX)
|
|
|
081b2d |
+ log.fatal('Adding user (%s): ' % dn)
|
|
|
081b2d |
+ server.add_s(Entry((dn, {'objectclass': ['top', 'person', 'inetuser'],
|
|
|
081b2d |
+ 'sn': ['_%s' % cn],
|
|
|
081b2d |
+ 'description': [desc]})))
|
|
|
081b2d |
+ if sleep:
|
|
|
081b2d |
+ time.sleep(2)
|
|
|
081b2d |
+
|
|
|
081b2d |
+def add_group(server, nr, sleep=True):
|
|
|
081b2d |
+ cn = '%s%d' % (GROUP_CN, nr)
|
|
|
081b2d |
+ dn = 'cn=%s,ou=groups,%s' % (cn, SUFFIX)
|
|
|
081b2d |
+ server.add_s(Entry((dn, {'objectclass': ['top', 'groupofnames'],
|
|
|
081b2d |
+ 'description': 'group %d' % nr})))
|
|
|
081b2d |
+ if sleep:
|
|
|
081b2d |
+ time.sleep(2)
|
|
|
081b2d |
+
|
|
|
081b2d |
+def update_member(server, member_dn, group_dn, op, sleep=True):
|
|
|
081b2d |
+ mod = [(op, 'member', member_dn)]
|
|
|
081b2d |
+ server.modify_s(group_dn, mod)
|
|
|
081b2d |
+ if sleep:
|
|
|
081b2d |
+ time.sleep(2)
|
|
|
081b2d |
+
|
|
|
081b2d |
+def _find_memberof(server, member_dn, group_dn, find_result=True):
|
|
|
081b2d |
+ ent = server.getEntry(member_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
|
|
|
081b2d |
+ found = False
|
|
|
081b2d |
+ if ent.hasAttr('memberof'):
|
|
|
081b2d |
+
|
|
|
081b2d |
+ for val in ent.getValues('memberof'):
|
|
|
081b2d |
+ server.log.info("!!!!!!! %s: memberof->%s" % (member_dn, val))
|
|
|
081b2d |
+ server.log.info("!!!!!!! %s" % (val))
|
|
|
081b2d |
+ server.log.info("!!!!!!! %s" % (group_dn))
|
|
|
081b2d |
+ if val.lower() == group_dn.lower():
|
|
|
081b2d |
+ found = True
|
|
|
081b2d |
+ break
|
|
|
081b2d |
+
|
|
|
081b2d |
+ if find_result:
|
|
|
081b2d |
+ assert (found)
|
|
|
081b2d |
+ else:
|
|
|
081b2d |
+ assert (not found)
|
|
|
081b2d |
+
|
|
|
081b2d |
+
|
|
|
081b2d |
+def test_ticket49064(topo):
|
|
|
081b2d |
+ """Specify a test case purpose or name here
|
|
|
081b2d |
+
|
|
|
081b2d |
+ :id: 60c11636-55a1-4704-9e09-2c6bcc828de4
|
|
|
081b2d |
+ :setup: 1 Master - 1 Hub - 1 Consumer
|
|
|
081b2d |
+ :steps:
|
|
|
081b2d |
+ 1. Configure replication to EXCLUDE memberof
|
|
|
081b2d |
+ 2. Enable memberof plugin
|
|
|
081b2d |
+ 3. Create users/groups
|
|
|
081b2d |
+ 4. make user_1 member of group_1
|
|
|
081b2d |
+ 5. Checks that user_1 is memberof group_1 on M,H,C
|
|
|
081b2d |
+ 6. make group_1 member of group_2 (nest group)
|
|
|
081b2d |
+ 7. Checks that user_1 is memberof group_1 and group_2 on M,H,C
|
|
|
081b2d |
+ 8. Check group_1 is memberof group_2 on M,H,C
|
|
|
081b2d |
+ 9. remove group_1 from group_2
|
|
|
081b2d |
+ 10. Check group_1 and user_1 are NOT memberof group_2 on M,H,C
|
|
|
081b2d |
+ 11. remove user_1 from group_1
|
|
|
081b2d |
+ 12. Check user_1 is NOT memberof group_1 and group_2 on M,H,C
|
|
|
081b2d |
+ 13. Disable memberof on C1
|
|
|
081b2d |
+ 14. make user_1 member of group_1
|
|
|
081b2d |
+ 15. Checks that user is memberof group_1 on M,H but not on C
|
|
|
081b2d |
+ 16. Enable memberof on C1
|
|
|
081b2d |
+ 17. Checks that user is memberof group_1 on M,H but not on C
|
|
|
081b2d |
+ 18. Run memberof fixup task
|
|
|
081b2d |
+ 19. Checks that user is memberof group_1 on M,H,C
|
|
|
081b2d |
+
|
|
|
081b2d |
+
|
|
|
081b2d |
+ :expectedresults:
|
|
|
081b2d |
+ no assert for membership check
|
|
|
081b2d |
+ """
|
|
|
081b2d |
+
|
|
|
081b2d |
+
|
|
|
081b2d |
+ M1 = topo.ms["master1"]
|
|
|
081b2d |
+ H1 = topo.hs["hub1"]
|
|
|
081b2d |
+ C1 = topo.cs["consumer1"]
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 1 & 2
|
|
|
081b2d |
+ M1.config.enable_log('audit')
|
|
|
081b2d |
+ config_memberof(M1)
|
|
|
081b2d |
+ M1.restart()
|
|
|
081b2d |
+
|
|
|
081b2d |
+ H1.config.enable_log('audit')
|
|
|
081b2d |
+ config_memberof(H1)
|
|
|
081b2d |
+ H1.restart()
|
|
|
081b2d |
+
|
|
|
081b2d |
+ C1.config.enable_log('audit')
|
|
|
081b2d |
+ config_memberof(C1)
|
|
|
081b2d |
+ C1.restart()
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 3
|
|
|
081b2d |
+ for i in range(10):
|
|
|
081b2d |
+ add_user(M1, i, desc='add on m1')
|
|
|
081b2d |
+ for i in range(3):
|
|
|
081b2d |
+ add_group(M1, i)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 4
|
|
|
081b2d |
+ member_dn = 'cn=%s%d,ou=people,%s' % (USER_CN, 1, SUFFIX)
|
|
|
081b2d |
+ group_dn = 'cn=%s%d,ou=groups,%s' % (GROUP_CN, 1, SUFFIX)
|
|
|
081b2d |
+ update_member(M1, member_dn, group_dn, ldap.MOD_ADD, sleep=True)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 5
|
|
|
081b2d |
+ for i in [M1, H1, C1]:
|
|
|
081b2d |
+ _find_memberof(i, member_dn, group_dn, find_result=True)
|
|
|
081b2d |
+
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 6
|
|
|
081b2d |
+ user_dn = 'cn=%s%d,ou=people,%s' % (USER_CN, 1, SUFFIX)
|
|
|
081b2d |
+ grp1_dn = 'cn=%s%d,ou=groups,%s' % (GROUP_CN, 1, SUFFIX)
|
|
|
081b2d |
+ grp2_dn = 'cn=%s%d,ou=groups,%s' % (GROUP_CN, 2, SUFFIX)
|
|
|
081b2d |
+ update_member(M1, grp1_dn, grp2_dn, ldap.MOD_ADD, sleep=True)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 7
|
|
|
081b2d |
+ for i in [grp1_dn, grp2_dn]:
|
|
|
081b2d |
+ for inst in [M1, H1, C1]:
|
|
|
081b2d |
+ _find_memberof(inst, user_dn, i, find_result=True)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 8
|
|
|
081b2d |
+ for i in [M1, H1, C1]:
|
|
|
081b2d |
+ _find_memberof(i, grp1_dn, grp2_dn, find_result=True)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 9
|
|
|
081b2d |
+ user_dn = 'cn=%s%d,ou=people,%s' % (USER_CN, 1, SUFFIX)
|
|
|
081b2d |
+ grp1_dn = 'cn=%s%d,ou=groups,%s' % (GROUP_CN, 1, SUFFIX)
|
|
|
081b2d |
+ grp2_dn = 'cn=%s%d,ou=groups,%s' % (GROUP_CN, 2, SUFFIX)
|
|
|
081b2d |
+ update_member(M1, grp1_dn, grp2_dn, ldap.MOD_DELETE, sleep=True)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 10
|
|
|
081b2d |
+ for inst in [M1, H1, C1]:
|
|
|
081b2d |
+ for i in [grp1_dn, user_dn]:
|
|
|
081b2d |
+ _find_memberof(inst, i, grp2_dn, find_result=False)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 11
|
|
|
081b2d |
+ member_dn = 'cn=%s%d,ou=people,%s' % (USER_CN, 1, SUFFIX)
|
|
|
081b2d |
+ group_dn = 'cn=%s%d,ou=groups,%s' % (GROUP_CN, 1, SUFFIX)
|
|
|
081b2d |
+ update_member(M1, member_dn, group_dn, ldap.MOD_DELETE, sleep=True)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 12
|
|
|
081b2d |
+ for inst in [M1, H1, C1]:
|
|
|
081b2d |
+ for grp in [grp1_dn, grp2_dn]:
|
|
|
081b2d |
+ _find_memberof(inst, member_dn, grp, find_result=False)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 13
|
|
|
081b2d |
+ C1.plugins.disable(name=PLUGIN_MEMBER_OF)
|
|
|
081b2d |
+ C1.restart()
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 14
|
|
|
081b2d |
+ member_dn = 'cn=%s%d,ou=people,%s' % (USER_CN, 1, SUFFIX)
|
|
|
081b2d |
+ group_dn = 'cn=%s%d,ou=groups,%s' % (GROUP_CN, 1, SUFFIX)
|
|
|
081b2d |
+ update_member(M1, member_dn, group_dn, ldap.MOD_ADD, sleep=True)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 15
|
|
|
081b2d |
+ for i in [M1, H1]:
|
|
|
081b2d |
+ _find_memberof(i, member_dn, group_dn, find_result=True)
|
|
|
081b2d |
+ _find_memberof(C1, member_dn, group_dn, find_result=False)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 16
|
|
|
081b2d |
+ C1.plugins.enable(name=PLUGIN_MEMBER_OF)
|
|
|
081b2d |
+ C1.restart()
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 17
|
|
|
081b2d |
+ for i in [M1, H1]:
|
|
|
081b2d |
+ _find_memberof(i, member_dn, group_dn, find_result=True)
|
|
|
081b2d |
+ _find_memberof(C1, member_dn, group_dn, find_result=False)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 18
|
|
|
081b2d |
+ memberof_fixup_task(C1)
|
|
|
081b2d |
+ time.sleep(5)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # Step 19
|
|
|
081b2d |
+ for i in [M1, H1, C1]:
|
|
|
081b2d |
+ _find_memberof(i, member_dn, group_dn, find_result=True)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # If you need any test suite initialization,
|
|
|
081b2d |
+ # please, write additional fixture for that (including finalizer).
|
|
|
081b2d |
+ # Topology for suites are predefined in lib389/topologies.py.
|
|
|
081b2d |
+
|
|
|
081b2d |
+ # If you need host, port or any other data about instance,
|
|
|
081b2d |
+ # Please, use the instance object attributes for that (for example, topo.ms["master1"].serverid)
|
|
|
081b2d |
+
|
|
|
081b2d |
+ if DEBUGGING:
|
|
|
081b2d |
+ # Add debugging steps(if any)...
|
|
|
081b2d |
+ pass
|
|
|
081b2d |
+
|
|
|
081b2d |
+
|
|
|
081b2d |
+if __name__ == '__main__':
|
|
|
081b2d |
+ # Run isolated
|
|
|
081b2d |
+ # -s for DEBUG mode
|
|
|
081b2d |
+ CURRENT_FILE = os.path.realpath(__file__)
|
|
|
081b2d |
+ pytest.main("-s %s" % CURRENT_FILE)
|
|
|
081b2d |
+
|
|
|
081b2d |
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
|
|
|
081b2d |
index bae242c81..44b52edbb 100644
|
|
|
081b2d |
--- a/ldap/servers/plugins/memberof/memberof.c
|
|
|
081b2d |
+++ b/ldap/servers/plugins/memberof/memberof.c
|
|
|
081b2d |
@@ -609,7 +609,7 @@ memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data)
|
|
|
081b2d |
slapi_modify_internal_set_pb_ext(
|
|
|
081b2d |
mod_pb, slapi_entry_get_sdn(e),
|
|
|
081b2d |
mods, 0, 0,
|
|
|
081b2d |
- memberof_get_plugin_id(), 0);
|
|
|
081b2d |
+ memberof_get_plugin_id(), SLAPI_OP_FLAG_BYPASS_REFERRALS);
|
|
|
081b2d |
|
|
|
081b2d |
slapi_modify_internal_pb(mod_pb);
|
|
|
081b2d |
|
|
|
081b2d |
@@ -3224,7 +3224,7 @@ memberof_add_memberof_attr(LDAPMod **mods, const char *dn, char *add_oc)
|
|
|
081b2d |
mod_pb = slapi_pblock_new();
|
|
|
081b2d |
slapi_modify_internal_set_pb(
|
|
|
081b2d |
mod_pb, dn, mods, 0, 0,
|
|
|
081b2d |
- memberof_get_plugin_id(), 0);
|
|
|
081b2d |
+ memberof_get_plugin_id(), SLAPI_OP_FLAG_BYPASS_REFERRALS);
|
|
|
081b2d |
slapi_modify_internal_pb(mod_pb);
|
|
|
081b2d |
|
|
|
081b2d |
slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
|
|
|
081b2d |
@@ -3279,7 +3279,7 @@ memberof_add_objectclass(char *auto_add_oc, const char *dn)
|
|
|
081b2d |
|
|
|
081b2d |
slapi_modify_internal_set_pb(
|
|
|
081b2d |
mod_pb, dn, mods, 0, 0,
|
|
|
081b2d |
- memberof_get_plugin_id(), 0);
|
|
|
081b2d |
+ memberof_get_plugin_id(), SLAPI_OP_FLAG_BYPASS_REFERRALS);
|
|
|
081b2d |
slapi_modify_internal_pb(mod_pb);
|
|
|
081b2d |
|
|
|
081b2d |
slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
|
|
|
081b2d |
--
|
|
|
081b2d |
2.13.6
|
|
|
081b2d |
|