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

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