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

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