Blame SOURCES/0055-Ticket-49523-memberof-schema-violation-error-message.patch

b045b9
From 60198729ba59f673aae2ae1db1d9668b674ad429 Mon Sep 17 00:00:00 2001
b045b9
From: Thierry Bordaz <tbordaz@redhat.com>
b045b9
Date: Fri, 5 Jan 2018 15:31:44 +0100
b045b9
Subject: [PATCH] Ticket 49523 - memberof: schema violation error message is
b045b9
 confusing as memberof will likely repair target entry
b045b9
b045b9
Bug Description:
b045b9
	When memberof is enabled it adds 'memberof' attribute to members entries.
b045b9
	If a member entry has not the appropriate objectclass to support 'memberof' attribute an ERR is logged.
b045b9
b045b9
	ERR - oc_check_allowed_sv - Entry "cn=user_1,ou=People,dc=example,dc=com" -- attribute "memberOf" not allowed
b045b9
b045b9
	This is confusing because memberof will catch this violation and may try to repair it.
b045b9
	So although this message is alarming, the target entry may finally have the 'memberof' attribute.
b045b9
b045b9
	This is especially confusing since https://pagure.io/389-ds-base/issue/48985 where the repair operation
b045b9
	is done by default (if schema is violated)
b045b9
b045b9
	We can not (and should not) eliminate the schema violation message.
b045b9
	But memberof should log a additional warning (beside the schema violation msg) stating it repaired the violation.
b045b9
b045b9
Fix Description:
b045b9
b045b9
	Add a warning message upon repair operation
b045b9
		ERR - oc_check_allowed_sv - Entry "<entry_dn>" -- attribute "memberOf" not allowed
b045b9
		WARN - memberof-plugin - Entry <entry_dn> - schema violation caught - repair operation succeeded
b045b9
b045b9
https://pagure.io/389-ds-base/issue/49523
b045b9
b045b9
Reviewed by: Mark Reynolds
b045b9
b045b9
Platforms tested: F26
b045b9
b045b9
Flag Day: no
b045b9
b045b9
Doc impact: no
b045b9
---
b045b9
 dirsrvtests/tests/tickets/ticket49523_test.py | 154 ++++++++++++++++++++++++++
b045b9
 ldap/servers/plugins/memberof/memberof.c      |   8 +-
b045b9
 2 files changed, 161 insertions(+), 1 deletion(-)
b045b9
 create mode 100644 dirsrvtests/tests/tickets/ticket49523_test.py
b045b9
b045b9
diff --git a/dirsrvtests/tests/tickets/ticket49523_test.py b/dirsrvtests/tests/tickets/ticket49523_test.py
b045b9
new file mode 100644
b045b9
index 000000000..c3296ef07
b045b9
--- /dev/null
b045b9
+++ b/dirsrvtests/tests/tickets/ticket49523_test.py
b045b9
@@ -0,0 +1,154 @@
b045b9
+import logging
b045b9
+import pytest
b045b9
+import os
b045b9
+import ldap
b045b9
+import time
b045b9
+import re
b045b9
+from lib389.plugins import MemberOfPlugin
b045b9
+from lib389._constants import *
b045b9
+from lib389.topologies import topology_st as topo
b045b9
+from lib389 import Entry
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
+
b045b9
+USER_CN='user_'
b045b9
+GROUP_CN='group_'
b045b9
+def _user_get_dn(no):
b045b9
+    cn = '%s%d' % (USER_CN, no)
b045b9
+    dn = 'cn=%s,ou=people,%s' % (cn, SUFFIX)
b045b9
+    return (cn, dn)
b045b9
+
b045b9
+def add_user(server, no, desc='dummy', sleep=True):
b045b9
+    (cn, dn) = _user_get_dn(no)
b045b9
+    log.fatal('Adding user (%s): ' % dn)
b045b9
+    server.add_s(Entry((dn, {'objectclass': ['top', 'person'],
b045b9
+                             'cn': [cn],
b045b9
+                             'description': [desc],
b045b9
+                             'sn': [cn],
b045b9
+                             'description': ['add on that host']})))
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
+def pattern_accesslog(server, log_pattern):
b045b9
+    file_obj = open(server.accesslog, "r")
b045b9
+
b045b9
+    found = False
b045b9
+    # Use a while true iteration because 'for line in file: hit a
b045b9
+    while True:
b045b9
+        line = file_obj.readline()
b045b9
+        found = log_pattern.search(line)
b045b9
+        if ((line == '') or (found)):
b045b9
+            break
b045b9
+
b045b9
+    return found
b045b9
+
b045b9
+def pattern_errorlog(server, log_pattern):
b045b9
+    file_obj = open(server.errlog, "r")
b045b9
+
b045b9
+    found = None
b045b9
+    # Use a while true iteration because 'for line in file: hit a
b045b9
+    while True:
b045b9
+        line = file_obj.readline()
b045b9
+        found = log_pattern.search(line)
b045b9
+        server.log.fatal("%s --> %s" % (line, found))
b045b9
+        if ((line == '') or (found)):
b045b9
+            break
b045b9
+
b045b9
+    return found
b045b9
+        
b045b9
+def test_ticket49523(topo):
b045b9
+    """Specify a test case purpose or name here
b045b9
+
b045b9
+    :id: e2af0aaa-447e-4e85-a5ce-57ae66260d0b
b045b9
+    :setup: Fill in set up configuration here
b045b9
+    :steps:
b045b9
+        1. Fill in test case steps here
b045b9
+        2. And indent them like this (RST format requirement)
b045b9
+    :expectedresults:
b045b9
+        1. Fill in the result that is expected
b045b9
+        2. For each test step
b045b9
+    """
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
+    inst = topo.standalone
b045b9
+    memberof = MemberOfPlugin(inst)
b045b9
+    memberof.enable()
b045b9
+    memberof.set_autoaddoc('nsMemberOf')
b045b9
+    inst.restart()
b045b9
+    
b045b9
+    # Step 2
b045b9
+    for i in range(10):
b045b9
+        add_user(inst, i, desc='add user')
b045b9
+    
b045b9
+    add_group(inst, 1)
b045b9
+    
b045b9
+    group_parent_dn = 'ou=groups,%s' % (SUFFIX)
b045b9
+    group_rdn = 'cn=%s%d' % (GROUP_CN, 1)
b045b9
+    group_dn  = '%s,%s' % (group_rdn, group_parent_dn)
b045b9
+    (member_cn, member_dn) = _user_get_dn(1)
b045b9
+    update_member(inst, member_dn, group_dn, ldap.MOD_ADD, sleep=False)
b045b9
+    
b045b9
+    _find_memberof(inst, member_dn, group_dn, find_result=True)
b045b9
+    
b045b9
+    pattern = ".*oc_check_allowed_sv - Entry.*cn=%s.* -- attribute.*not allowed.*" % member_cn
b045b9
+    log.fatal("pattern = %s" % pattern)
b045b9
+    regex = re.compile(pattern)
b045b9
+    assert pattern_errorlog(inst, regex)
b045b9
+    
b045b9
+    regex = re.compile(".*schema violation caught - repair operation.*")
b045b9
+    assert pattern_errorlog(inst, regex)
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 44b52edbb..fcfa7817d 100644
b045b9
--- a/ldap/servers/plugins/memberof/memberof.c
b045b9
+++ b/ldap/servers/plugins/memberof/memberof.c
b045b9
@@ -3236,8 +3236,14 @@ memberof_add_memberof_attr(LDAPMod **mods, const char *dn, char *add_oc)
b045b9
                  */
b045b9
                 break;
b045b9
             }
b045b9
-            if (memberof_add_objectclass(add_oc, dn)) {
b045b9
+            rc = memberof_add_objectclass(add_oc, dn);
b045b9
+            slapi_log_err(SLAPI_LOG_WARNING, MEMBEROF_PLUGIN_SUBSYSTEM,
b045b9
+                    "Entry %s - schema violation caught - repair operation %s\n",
b045b9
+                    dn ? dn : "unknown",
b045b9
+                    rc ? "failed" : "succeeded");
b045b9
+            if (rc) {
b045b9
                 /* Failed to add objectclass */
b045b9
+                rc = LDAP_OBJECT_CLASS_VIOLATION;
b045b9
                 break;
b045b9
             }
b045b9
             added_oc = 1;
b045b9
-- 
b045b9
2.13.6
b045b9