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