Blame SOURCES/0011-Ticket-49064-RFE-allow-to-enable-MemberOf-plugin-in-.patch

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