From 2741a6db134ad40662cfa0233c4542d2d4148997 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Tue, 3 Oct 2017 17:22:37 -0400 Subject: [PATCH] Ticket 49389 - unable to retrieve specific cosAttribute when subtree password policy is configured Bug Description: If indirect cos is being used and a subtree password policy is added, th orignal COS attributes aren't always returned. The issue is that when the subtree password policy attribute was encountered during the virtual attribute processing it set a flag that said the attribute was operational (which is correct for the password policy attr: pwdpolicysubentry). However, this flag was accidentally carried over to the following virtual attributes that were being processed. Which caused those attributes to be seen as operational which is why it was no longer being returned to the client. Fix Description: Reset the prop flags before processing the next COS attribute https://pagure.io/389-ds-base/issue/49389 Reviewed by: firstyear(Thanks!) (cherry picked from commit 0953e6011368bc29300990e9493ac13e5aba9586) --- dirsrvtests/tests/suites/cos/__init__.py | 0 dirsrvtests/tests/suites/cos/indirect_cos_test.py | 191 ++++++++++++++++++++++ ldap/servers/plugins/cos/cos_cache.c | 68 ++++---- 3 files changed, 223 insertions(+), 36 deletions(-) create mode 100644 dirsrvtests/tests/suites/cos/__init__.py create mode 100644 dirsrvtests/tests/suites/cos/indirect_cos_test.py diff --git a/dirsrvtests/tests/suites/cos/__init__.py b/dirsrvtests/tests/suites/cos/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dirsrvtests/tests/suites/cos/indirect_cos_test.py b/dirsrvtests/tests/suites/cos/indirect_cos_test.py new file mode 100644 index 0000000..1aac6b8 --- /dev/null +++ b/dirsrvtests/tests/suites/cos/indirect_cos_test.py @@ -0,0 +1,191 @@ +import logging +import pytest +import os +import ldap +import time +import subprocess + +from lib389 import Entry +from lib389.idm.user import UserAccounts +from lib389.topologies import topology_st as topo +from lib389._constants import (DEFAULT_SUFFIX, DN_DM, PASSWORD, HOST_STANDALONE, + SERVERID_STANDALONE, PORT_STANDALONE) + + +DEBUGGING = os.getenv("DEBUGGING", default=False) +if DEBUGGING: + logging.getLogger(__name__).setLevel(logging.DEBUG) +else: + logging.getLogger(__name__).setLevel(logging.INFO) +log = logging.getLogger(__name__) + +TEST_USER_DN = "uid=test_user,ou=people,dc=example,dc=com" +OU_PEOPLE = 'ou=people,{}'.format(DEFAULT_SUFFIX) + +PW_POLICY_CONT_PEOPLE = 'cn="cn=nsPwPolicyEntry,' \ + 'ou=people,dc=example,dc=com",' \ + 'cn=nsPwPolicyContainer,ou=people,dc=example,dc=com' + +PW_POLICY_CONT_PEOPLE2 = 'cn="cn=nsPwPolicyEntry,' \ + 'dc=example,dc=com",' \ + 'cn=nsPwPolicyContainerdc=example,dc=com' + + +def check_user(inst): + """Search the test user and make sure it has the execpted attrs + """ + try: + entries = inst.search_s('dc=example,dc=com', ldap.SCOPE_SUBTREE, "uid=test_user") + log.debug('user: \n' + str(entries[0])) + assert entries[0].hasAttr('ou'), "Entry is missing ou cos attribute" + assert entries[0].hasAttr('x-department'), "Entry is missing description cos attribute" + assert entries[0].hasAttr('x-en-ou'), "Entry is missing givenname cos attribute" + except ldap.LDAPError as e: + log.fatal('Failed to search for user: ' + str(e)) + raise e + + +def setup_subtree_policy(topo): + """Set up subtree password policy + """ + try: + topo.standalone.modify_s("cn=config", [(ldap.MOD_REPLACE, + 'nsslapd-pwpolicy-local', + 'on')]) + except ldap.LDAPError as e: + log.error('Failed to set fine-grained policy: error {}'.format( + e.message['desc'])) + raise e + + log.info('Create password policy for subtree {}'.format(OU_PEOPLE)) + try: + subprocess.call(['%s/ns-newpwpolicy.pl' % topo.standalone.get_sbin_dir(), + '-D', DN_DM, '-w', PASSWORD, + '-p', str(PORT_STANDALONE), '-h', HOST_STANDALONE, + '-S', DEFAULT_SUFFIX, '-Z', SERVERID_STANDALONE]) + except subprocess.CalledProcessError as e: + log.error('Failed to create pw policy policy for {}: error {}'.format( + OU_PEOPLE, e.message['desc'])) + raise e + + log.info('Add pwdpolicysubentry attribute to {}'.format(OU_PEOPLE)) + try: + topo.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_REPLACE, + 'pwdpolicysubentry', + PW_POLICY_CONT_PEOPLE2)]) + except ldap.LDAPError as e: + log.error('Failed to pwdpolicysubentry pw policy ' + 'policy for {}: error {}'.format(OU_PEOPLE, e.message['desc'])) + raise e + time.sleep(1) + + +def setup_indirect_cos(topo): + """Setup indirect COS definition and template + """ + cosDef = Entry(('cn=cosDefinition,dc=example,dc=com', + {'objectclass': ['top', 'ldapsubentry', + 'cossuperdefinition', + 'cosIndirectDefinition'], + 'cosAttribute': ['ou merge-schemes', + 'x-department merge-schemes', + 'x-en-ou merge-schemes'], + 'cosIndirectSpecifier': 'seeAlso', + 'cn': 'cosDefinition'})) + + cosTemplate = Entry(('cn=cosTemplate,dc=example,dc=com', + {'objectclass': ['top', + 'extensibleObject', + 'cosTemplate'], + 'ou': 'My COS Org', + 'x-department': 'My COS x-department', + 'x-en-ou': 'my COS x-en-ou', + 'cn': 'cosTemplate'})) + try: + topo.standalone.add_s(cosDef) + topo.standalone.add_s(cosTemplate) + except ldap.LDAPError as e: + log.fatal('Failed to add cos: error ' + str(e)) + raise e + time.sleep(1) + + +@pytest.fixture(scope="module") +def setup(topo, request): + """Add schema, and test user + """ + log.info('Add custom schema...') + try: + ATTR_1 = ("( 1.3.6.1.4.1.409.389.2.189 NAME 'x-department' " + + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'user defined' )") + ATTR_2 = ("( 1.3.6.1.4.1.409.389.2.187 NAME 'x-en-ou' " + + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'user defined' )") + OC = ("( xPerson-oid NAME 'xPerson' DESC '' SUP person STRUCTURAL MAY " + + "( x-department $ x-en-ou ) X-ORIGIN 'user defined' )") + topo.standalone.modify_s("cn=schema", [(ldap.MOD_ADD, 'attributeTypes', ATTR_1), + (ldap.MOD_ADD, 'attributeTypes', ATTR_2), + (ldap.MOD_ADD, 'objectClasses', OC)]) + except ldap.LDAPError as e: + log.fatal('Failed to add custom schema') + raise e + time.sleep(1) + + log.info('Add test user...') + users = UserAccounts(topo.standalone, DEFAULT_SUFFIX) + + user_properties = { + 'uid': 'test_user', + 'cn': 'test user', + 'sn': 'user', + 'uidNumber': '1000', + 'gidNumber': '2000', + 'homeDirectory': '/home/test_user', + 'seeAlso': 'cn=cosTemplate,dc=example,dc=com' + } + users.create(properties=user_properties) + try: + topo.standalone.modify_s(TEST_USER_DN, [(ldap.MOD_ADD, + 'objectclass', + 'xPerson')]) + except ldap.LDAPError as e: + log.fatal('Failed to add objectclass to user') + raise e + + # Setup COS + log.info("Setup indirect COS...") + setup_indirect_cos(topo) + + +def test_indirect_cos(topo, setup): + """Test indirect cos + + :id: 890d5929-7d52-4a56-956e-129611b4649a + :setup: standalone + :steps: + 1. Test cos is working for test user + 2. Add subtree password policy + 3. Test cos is working for test user + :expectedresults: + 1. User has expected cos attrs + 2. Substree password policy setup is successful + 3 User still has expected cos attrs + """ + + # Step 1 - Search user and see if the COS attrs are included + log.info('Checking user...') + check_user(topo.standalone) + + # Step 2 - Add subtree password policy (Second COS - operational attribute) + setup_subtree_policy(topo) + + # Step 3 - Check user again now hat we have a mix of vattrs + log.info('Checking user...') + check_user(topo.standalone) + + +if __name__ == '__main__': + # Run isolated + # -s for DEBUG mode + CURRENT_FILE = os.path.realpath(__file__) + pytest.main("-s %s" % CURRENT_FILE) + diff --git a/ldap/servers/plugins/cos/cos_cache.c b/ldap/servers/plugins/cos/cos_cache.c index 66c6c7f..87d4890 100644 --- a/ldap/servers/plugins/cos/cos_cache.c +++ b/ldap/servers/plugins/cos/cos_cache.c @@ -2190,48 +2190,44 @@ bail: static int cos_cache_vattr_types(vattr_sp_handle *handle,Slapi_Entry *e, vattr_type_list_context *type_context,int flags) { - int ret = 0; - int index = 0; - cosCache *pCache; - char *lastattr = "thisisfakeforcos"; - int props = 0; - - slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "--> cos_cache_vattr_types\n"); - - if(cos_cache_getref((cos_cache **)&pCache) < 1) - { - /* problems we are hosed */ - slapi_log_err(SLAPI_LOG_PLUGIN, COS_PLUGIN_SUBSYSTEM, "cos_cache_vattr_types - Failed to get class of service reference\n"); - goto bail; - } - - while(index < pCache->attrCount ) - { - if(slapi_utf8casecmp( - (unsigned char *)pCache->ppAttrIndex[index]->pAttrName, - (unsigned char *)lastattr)) - { - lastattr = pCache->ppAttrIndex[index]->pAttrName; + int ret = 0; + int index = 0; + cosCache *pCache; + char *lastattr = "thisisfakeforcos"; - if(1 == cos_cache_query_attr(pCache, NULL, e, lastattr, NULL, NULL, - NULL, &props, NULL)) - { - /* entry contains this attr */ - vattr_type_thang thang = {0}; + slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "--> cos_cache_vattr_types\n"); - thang.type_name = lastattr; - thang.type_flags = props; + if (cos_cache_getref((cos_cache **)&pCache) < 1) { + /* problems we are hosed */ + slapi_log_err(SLAPI_LOG_PLUGIN, COS_PLUGIN_SUBSYSTEM, "cos_cache_vattr_types - Failed to get class of service reference\n"); + goto bail; + } - slapi_vattrspi_add_type(type_context,&thang,0); - } - } - index++; - } - cos_cache_release(pCache); + while (index < pCache->attrCount) { + int props = 0; + if (slapi_utf8casecmp( + (unsigned char *)pCache->ppAttrIndex[index]->pAttrName, + (unsigned char *)lastattr)) { + lastattr = pCache->ppAttrIndex[index]->pAttrName; + + if (1 == cos_cache_query_attr(pCache, NULL, e, lastattr, NULL, NULL, + NULL, &props, NULL)) { + /* entry contains this attr */ + vattr_type_thang thang = {0}; + + thang.type_name = lastattr; + thang.type_flags = props; + + slapi_vattrspi_add_type(type_context, &thang, 0); + } + } + index++; + } + cos_cache_release(pCache); bail: -slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "<-- cos_cache_vattr_types\n"); + slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "<-- cos_cache_vattr_types\n"); return ret; } -- 2.9.5