| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- # --- BEGIN COPYRIGHT BLOCK ---
- # Copyright (C) 2015 Red Hat, Inc.
- # All rights reserved.
- #
- # License: GPL (version 3 or any later version).
- # See LICENSE for details.
- # --- END COPYRIGHT BLOCK ---
- #
- import os
- import sys
- import time
- import ldap
- import logging
- import pytest
- from lib389 import DirSrv, Entry, tools, tasks
- from lib389.tools import DirSrvTools
- from lib389._constants import *
- from lib389.properties import *
- from lib389.tasks import *
- from lib389.utils import *
- log = logging.getLogger(__name__)
- # Assuming DEFAULT_SUFFIX is "dc=example,dc=com", otherwise it does not work... :(
- SUBTREE_CONTAINER = 'cn=nsPwPolicyContainer,' + DEFAULT_SUFFIX
- SUBTREE_PWPDN = 'cn=nsPwPolicyEntry,' + DEFAULT_SUFFIX
- SUBTREE_PWP = 'cn=cn\3DnsPwPolicyEntry\2C' + DEFAULT_SUFFIX_ESCAPED + ',' + SUBTREE_CONTAINER
- SUBTREE_COS_TMPLDN = 'cn=nsPwTemplateEntry,' + DEFAULT_SUFFIX
- SUBTREE_COS_TMPL = 'cn=cn\3DnsPwTemplateEntry\2C' + DEFAULT_SUFFIX_ESCAPED + ',' + SUBTREE_CONTAINER
- SUBTREE_COS_DEF = 'cn=nsPwPolicy_CoS,' + DEFAULT_SUFFIX
- USER1_DN = 'uid=user1,' + DEFAULT_SUFFIX
- USER2_DN = 'uid=user2,' + DEFAULT_SUFFIX
- USER3_DN = 'uid=user3,' + DEFAULT_SUFFIX
- USER_PW = 'password'
- class TopologyStandalone(object):
- def __init__(self, standalone):
- standalone.open()
- self.standalone = standalone
- @pytest.fixture(scope="module")
- def topology(request):
- # Creating standalone instance ...
- standalone = DirSrv(verbose=False)
- args_instance[SER_HOST] = HOST_STANDALONE
- args_instance[SER_PORT] = PORT_STANDALONE
- args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
- args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
- args_standalone = args_instance.copy()
- standalone.allocate(args_standalone)
- instance_standalone = standalone.exists()
- if instance_standalone:
- standalone.delete()
- standalone.create()
- standalone.open()
- # Delete each instance in the end
- def fin():
- standalone.delete()
- request.addfinalizer(fin)
- return TopologyStandalone(standalone)
- def days_to_secs(days):
- # Value of 60 * 60 * 24
- return days * 86400
- # Values are in days
- def set_global_pwpolicy(topology, min_=1, max_=10, warn=3):
- log.info(" +++++ Enable global password policy +++++\n")
- # Enable password policy
- try:
- topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local', 'on')])
- except ldap.LDAPError as e:
- log.error('Failed to set pwpolicy-local: error ' + e.message['desc'])
- assert False
- # Convert our values to seconds
- min_secs = days_to_secs(min_)
- max_secs = days_to_secs(max_)
- warn_secs = days_to_secs(warn)
- log.info(" Set global password Min Age -- %s day\n"% min_)
- try:
- topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMinAge', '%s' % min_secs)])
- except ldap.LDAPError as e:
- log.error('Failed to set passwordMinAge: error ' + e.message['desc'])
- assert False
- log.info(" Set global password Max Age -- %s days\n" % max_)
- try:
- topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMaxAge', '%s' % max_secs)])
- except ldap.LDAPError as e:
- log.error('Failed to set passwordMaxAge: error ' + e.message['desc'])
- assert False
- log.info(" Set global password Warning -- %s days\n" % warn)
- try:
- topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordWarning', '%s' % warn_secs)])
- except ldap.LDAPError as e:
- log.error('Failed to set passwordWarning: error ' + e.message['desc'])
- assert False
- def set_subtree_pwpolicy(topology, min_=2, max_=20, warn=6):
- log.info(" +++++ Enable subtree level password policy +++++\n")
- # Convert our values to seconds
- min_secs = days_to_secs(min_)
- max_secs = days_to_secs(max_)
- warn_secs = days_to_secs(warn)
- log.info(" Add the container")
- try:
- topology.standalone.add_s(Entry((SUBTREE_CONTAINER, {'objectclass': 'top nsContainer'.split(),
- 'cn': 'nsPwPolicyContainer'})))
- except ldap.LDAPError as e:
- log.error('Failed to add subtree container: error ' + e.message['desc'])
- #assert False
- try:
- # Purge the old policy
- topology.standalone.delete_s(SUBTREE_PWP)
- except:
- pass
- log.info(" Add the password policy subentry {passwordMustChange: on, passwordMinAge: %s, passwordMaxAge: %s, passwordWarning: %s}" % (min_, max_, warn))
- try:
- topology.standalone.add_s(Entry((SUBTREE_PWP, {'objectclass': 'top ldapsubentry passwordpolicy'.split(),
- 'cn': SUBTREE_PWPDN,
- 'passwordMustChange': 'on',
- 'passwordExp': 'on',
- 'passwordMinAge': '%s' % min_secs,
- 'passwordMaxAge': '%s' % max_secs,
- 'passwordWarning': '%s' % warn_secs,
- 'passwordChange': 'on',
- 'passwordStorageScheme': 'clear'})))
- except ldap.LDAPError as e:
- log.error('Failed to add passwordpolicy: error ' + e.message['desc'])
- assert False
- log.info(" Add the COS template")
- try:
- topology.standalone.add_s(Entry((SUBTREE_COS_TMPL, {'objectclass': 'top ldapsubentry costemplate extensibleObject'.split(),
- 'cn': SUBTREE_PWPDN,
- 'cosPriority': '1',
- 'cn': SUBTREE_COS_TMPLDN,
- 'pwdpolicysubentry': SUBTREE_PWP})))
- except ldap.LDAPError as e:
- log.error('Failed to add COS template: error ' + e.message['desc'])
- #assert False
- log.info(" Add the COS definition")
- try:
- topology.standalone.add_s(Entry((SUBTREE_COS_DEF, {'objectclass': 'top ldapsubentry cosSuperDefinition cosPointerDefinition'.split(),
- 'cn': SUBTREE_PWPDN,
- 'costemplatedn': SUBTREE_COS_TMPL,
- 'cosAttribute': 'pwdpolicysubentry default operational-default'})))
- except ldap.LDAPError as e:
- log.error('Failed to add COS def: error ' + e.message['desc'])
- #assert False
- time.sleep(1)
- def update_passwd(topology, user, passwd, newpasswd):
- log.info(" Bind as {%s,%s}" % (user, passwd))
- topology.standalone.simple_bind_s(user, passwd)
- try:
- topology.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', newpasswd)])
- except ldap.LDAPError as e:
- log.fatal('test_ticket548: Failed to update the password ' + cpw + ' of user ' + user + ': error ' + e.message['desc'])
- assert False
- time.sleep(1)
- def check_shadow_attr_value(entry, attr_type, expected, dn):
- if entry.hasAttr(attr_type):
- actual = entry.getValue(attr_type)
- if int(actual) == expected:
- log.info('%s of entry %s has expected value %s' % (attr_type, dn, actual))
- assert True
- else:
- log.fatal('%s %s of entry %s does not have expected value %s' % (attr_type, actual, dn, expected))
- assert False
- else:
- log.fatal('entry %s does not have %s attr' % (dn, attr_type))
- assert False
- def test_ticket548_test_with_no_policy(topology):
- """
- Check shadowAccount under no password policy
- """
- log.info("Case 1. No password policy")
- log.info("Bind as %s" % DN_DM)
- topology.standalone.simple_bind_s(DN_DM, PASSWORD)
- log.info('Add an entry' + USER1_DN)
- try:
- topology.standalone.add_s(Entry((USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
- 'sn': '1',
- 'cn': 'user 1',
- 'uid': 'user1',
- 'givenname': 'user',
- 'mail': 'user1@' + DEFAULT_SUFFIX,
- 'userpassword': USER_PW})))
- except ldap.LDAPError as e:
- log.fatal('test_ticket548: Failed to add user' + USER1_DN + ': error ' + e.message['desc'])
- assert False
- edate = int(time.time() / (60 * 60 * 24))
- log.info('Search entry %s' % USER1_DN)
- log.info("Bind as %s" % USER1_DN)
- topology.standalone.simple_bind_s(USER1_DN, USER_PW)
- entry = topology.standalone.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)", ['shadowLastChange'])
- check_shadow_attr_value(entry, 'shadowLastChange', edate, USER1_DN)
- log.info("Check shadowAccount with no policy was successfully verified.")
- def test_ticket548_test_global_policy(topology):
- """
- Check shadowAccount with global password policy
- """
- log.info("Case 2. Check shadowAccount with global password policy")
- log.info("Bind as %s" % DN_DM)
- topology.standalone.simple_bind_s(DN_DM, PASSWORD)
- set_global_pwpolicy(topology)
- log.info('Add an entry' + USER2_DN)
- try:
- topology.standalone.add_s(Entry((USER2_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
- 'sn': '2',
- 'cn': 'user 2',
- 'uid': 'user2',
- 'givenname': 'user',
- 'mail': 'user2@' + DEFAULT_SUFFIX,
- 'userpassword': USER_PW})))
- except ldap.LDAPError as e:
- log.fatal('test_ticket548: Failed to add user' + USER2_DN + ': error ' + e.message['desc'])
- assert False
- edate = int(time.time() / (60 * 60 * 24))
- log.info("Bind as %s" % USER1_DN)
- topology.standalone.simple_bind_s(USER1_DN, USER_PW)
- log.info('Search entry %s' % USER1_DN)
- entry = topology.standalone.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)")
- check_shadow_attr_value(entry, 'shadowLastChange', edate, USER1_DN)
- # passwordMinAge -- 1 day
- check_shadow_attr_value(entry, 'shadowMin', 1, USER1_DN)
- # passwordMaxAge -- 10 days
- check_shadow_attr_value(entry, 'shadowMax', 10, USER1_DN)
- # passwordWarning -- 3 days
- check_shadow_attr_value(entry, 'shadowWarning', 3, USER1_DN)
- log.info("Bind as %s" % USER2_DN)
- topology.standalone.simple_bind_s(USER2_DN, USER_PW)
- log.info('Search entry %s' % USER2_DN)
- entry = topology.standalone.getEntry(USER2_DN, ldap.SCOPE_BASE, "(objectclass=*)")
- check_shadow_attr_value(entry, 'shadowLastChange', edate, USER2_DN)
- # passwordMinAge -- 1 day
- check_shadow_attr_value(entry, 'shadowMin', 1, USER2_DN)
- # passwordMaxAge -- 10 days
- check_shadow_attr_value(entry, 'shadowMax', 10, USER2_DN)
- # passwordWarning -- 3 days
- check_shadow_attr_value(entry, 'shadowWarning', 3, USER2_DN)
- # Bind as DM again, change policy
- log.info("Bind as %s" % DN_DM)
- topology.standalone.simple_bind_s(DN_DM, PASSWORD)
- set_global_pwpolicy(topology, 3, 30, 9)
- # change the user password, then check again.
- log.info("Bind as %s" % USER2_DN)
- topology.standalone.simple_bind_s(USER2_DN, USER_PW)
- newpasswd = USER_PW + '2'
- update_passwd(topology, USER2_DN, USER_PW, newpasswd)
- log.info("Re-bind as %s with new password" % USER2_DN)
- topology.standalone.simple_bind_s(USER2_DN, newpasswd)
- ## This tests if we update the shadow values on password change.
- log.info('Search entry %s' % USER2_DN)
- entry = topology.standalone.getEntry(USER2_DN, ldap.SCOPE_BASE, "(objectclass=*)")
- # passwordMinAge -- 1 day
- check_shadow_attr_value(entry, 'shadowMin', 3, USER2_DN)
- # passwordMaxAge -- 10 days
- check_shadow_attr_value(entry, 'shadowMax', 30, USER2_DN)
- # passwordWarning -- 3 days
- check_shadow_attr_value(entry, 'shadowWarning', 9, USER2_DN)
- log.info("Check shadowAccount with global policy was successfully verified.")
- def test_ticket548_test_subtree_policy(topology):
- """
- Check shadowAccount with subtree level password policy
- """
- log.info("Case 3. Check shadowAccount with subtree level password policy")
- log.info("Bind as %s" % DN_DM)
- topology.standalone.simple_bind_s(DN_DM, PASSWORD)
- # Check the global policy values
- set_subtree_pwpolicy(topology, 2, 20, 6)
- log.info('Add an entry' + USER3_DN)
- try:
- topology.standalone.add_s(Entry((USER3_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
- 'sn': '3',
- 'cn': 'user 3',
- 'uid': 'user3',
- 'givenname': 'user',
- 'mail': 'user3@' + DEFAULT_SUFFIX,
- 'userpassword': USER_PW})))
- except ldap.LDAPError as e:
- log.fatal('test_ticket548: Failed to add user' + USER3_DN + ': error ' + e.message['desc'])
- assert False
- log.info('Search entry %s' % USER3_DN)
- entry0 = topology.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
- log.info('Expecting shadowLastChange 0 since passwordMustChange is on')
- check_shadow_attr_value(entry0, 'shadowLastChange', 0, USER3_DN)
- # passwordMinAge -- 2 day
- check_shadow_attr_value(entry0, 'shadowMin', 2, USER3_DN)
- # passwordMaxAge -- 20 days
- check_shadow_attr_value(entry0, 'shadowMax', 20, USER3_DN)
- # passwordWarning -- 6 days
- check_shadow_attr_value(entry0, 'shadowWarning', 6, USER3_DN)
- log.info("Bind as %s" % USER3_DN)
- topology.standalone.simple_bind_s(USER3_DN, USER_PW)
- log.info('Search entry %s' % USER3_DN)
- try:
- entry1 = topology.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
- except ldap.UNWILLING_TO_PERFORM:
- log.info('test_ticket548: Search by' + USER3_DN + ' failed by UNWILLING_TO_PERFORM as expected')
- except ldap.LDAPError as e:
- log.fatal('test_ticket548: Failed to serch user' + USER3_DN + ' by self: error ' + e.message['desc'])
- assert False
- log.info("Bind as %s and updating the password with a new one" % USER3_DN)
- topology.standalone.simple_bind_s(USER3_DN, USER_PW)
- # Bind as DM again, change policy
- log.info("Bind as %s" % DN_DM)
- topology.standalone.simple_bind_s(DN_DM, PASSWORD)
- set_subtree_pwpolicy(topology, 4, 40, 12)
- newpasswd = USER_PW + '0'
- update_passwd(topology, USER3_DN, USER_PW, newpasswd)
- log.info("Re-bind as %s with new password" % USER3_DN)
- topology.standalone.simple_bind_s(USER3_DN, newpasswd)
- try:
- entry2 = topology.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
- except ldap.LDAPError as e:
- log.fatal('test_ticket548: Failed to serch user' + USER3_DN + ' by self: error ' + e.message['desc'])
- assert False
- edate = int(time.time() / (60 * 60 * 24))
- log.info('Expecting shadowLastChange %d once userPassword is updated', edate)
- check_shadow_attr_value(entry2, 'shadowLastChange', edate, USER3_DN)
- log.info('Search entry %s' % USER3_DN)
- entry = topology.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
- check_shadow_attr_value(entry, 'shadowLastChange', edate, USER3_DN)
- # passwordMinAge -- 1 day
- check_shadow_attr_value(entry, 'shadowMin', 4, USER3_DN)
- # passwordMaxAge -- 10 days
- check_shadow_attr_value(entry, 'shadowMax', 40, USER3_DN)
- # passwordWarning -- 3 days
- check_shadow_attr_value(entry, 'shadowWarning', 12, USER3_DN)
- log.info("Check shadowAccount with subtree level policy was successfully verified.")
- if __name__ == '__main__':
- # Run isolated
- # -s for DEBUG mode
- CURRENT_FILE = os.path.realpath(__file__)
- pytest.main("-s %s" % CURRENT_FILE)
|