def get_pwpolicy_entry(self, dn): """Get a local password policy entry :param dn: Entry DN for the local pwpolicy :type dn: str :returns: PwPolicyEntry instance """ entry = Account(self._instance, dn) if not entry.exists(): raise ValueError( 'Can not get the password policy entry because the target dn does not exist' ) # Get the parent DN dn_comps = ldap.dn.explode_dn(entry.dn) dn_comps.pop(0) parentdn = ",".join(dn_comps) # Get the parent's policies pwp_entries = PwPolicyEntries(self._instance, parentdn) policies = pwp_entries.list() for policy in policies: dn_comps = ldap.dn.explode_dn(policy.get_attr_val_utf8_l('cn')) dn_comps.pop(0) pwp_dn = ",".join(dn_comps) if pwp_dn == dn.lower(): # This DN does have a policy associated with it return policy # Did not find a policy for this entry raise ValueError("No password policy was found for this entry")
def list_policies(inst, basedn, log, args): log = log.getChild('list_policies') targetdn = args.DN[0] if args.json: result = {'type': 'list', 'items': []} else: result = "" # Verify target dn exists before getting started user_entry = Account(inst, args.DN[0]) if not user_entry.exists(): raise ValueError('The target entry dn does not exist') # User pwpolicy entry is under the container that is under the parent, # so we need to go one level up pwp_entries = PwPolicyEntries(inst, targetdn) for pwp_entry in pwp_entries.list(): dn_comps = ldap.dn.explode_dn(pwp_entry.get_attr_val_utf8_l('cn')) dn_comps.pop(0) entrydn = ",".join(dn_comps) policy_type = _get_policy_type(inst, entrydn) if args.json: result['items'].append([entrydn, policy_type]) else: result += "%s (%s)\n" % (entrydn, policy_type.lower()) if args.json: print(json.dumps(result)) else: print(result)
def list_policies(inst, basedn, log, args): log = log.getChild('list_policies') if args.DN is None: # list all the password policies for all the backends targetdns = [] backends = Backends(inst).list() for backend in backends: targetdns.append(backend.get_suffix()) else: targetdns = [args.DN] if args.json: result = {'type': 'list', 'items': []} else: result = "" for targetdn in targetdns: # Verify target dn exists before getting started user_entry = Account(inst, targetdn) if not user_entry.exists(): raise ValueError('The target entry dn does not exist') # User pwpolicy entry is under the container that is under the parent, # so we need to go one level up pwp_entries = PwPolicyEntries(inst, targetdn) pwp_manager = PwPolicyManager(inst) attr_list = list(pwp_manager.arg_to_attr.values()) for pwp_entry in pwp_entries.list(): dn_comps = ldap.dn.explode_dn(pwp_entry.get_attr_val_utf8_l('cn')) dn_comps.pop(0) entrydn = ",".join(dn_comps) policy_type = _get_policy_type(inst, entrydn) all_attrs = pwp_entry.get_attrs_vals_utf8(attr_list) attrs = {k: v for k, v in all_attrs.items() if len(v) > 0} if args.json: result['items'].append( { "dn": pwp_entry.dn, "targetdn": entrydn, "pwp_type": policy_type, "basedn": pwp_entry.get_basedn(), "attrs": attrs } ) else: result += "%s (%s)\n" % (entrydn, policy_type.lower()) if args.json: print(json.dumps(result, indent=4)) else: print(result)
def create_subtree_policy(self, dn, properties): """Creates all entries which are needed for the subtree password policy :param dn: Entry DN for the subtree pwpolicy :type dn: str :param properties: A dict with password policy settings :type properties: dict :returns: PwPolicyEntry instance """ # Verify target dn exists before getting started subtree_entry = Account(self._instance, dn) if not subtree_entry.exists(): raise ValueError('Can not create subtree password policy because the target dn does not exist') # Create the pwp container if needed pwp_containers = nsContainers(self._instance, basedn=dn) pwp_container = pwp_containers.ensure_state(properties={'cn': 'nsPwPolicyContainer'}) # Create policy entry pwp_entry = None properties['cn'] = 'cn=nsPwPolicyEntry_subtree,%s' % dn pwp_entries = PwPolicyEntries(self._instance, pwp_container.dn) pwp_entry = pwp_entries.create(properties=properties) try: # The CoS template entry (nsPwTemplateEntry) that has the pwdpolicysubentry # value pointing to the above (nsPwPolicyEntry) entry cos_template = None cos_templates = CosTemplates(self._instance, pwp_container.dn) cos_template = cos_templates.create(properties={'cosPriority': '1', 'pwdpolicysubentry': pwp_entry.dn, 'cn': 'cn=nsPwTemplateEntry,%s' % dn}) # The CoS specification entry at the subtree level cos_pointer_defs = CosPointerDefinitions(self._instance, dn) cos_pointer_defs.create(properties={'cosAttribute': 'pwdpolicysubentry default operational', 'cosTemplateDn': cos_template.dn, 'cn': 'nsPwPolicy_CoS'}) except ldap.LDAPError as e: # Something went wrong, remove what we have done if pwp_entry is not None: pwp_entry.delete() if cos_template is not None: cos_template.delete() raise e # make sure that local policies are enabled self.set_global_policy({'nsslapd-pwpolicy-local': 'on'}) return pwp_entry
def create_user_policy(self, dn, properties): """Creates all entries which are needed for the user password policy :param dn: Entry DN for the subtree pwpolicy :type dn: str :param properties: A dict with password policy settings :type properties: dict :returns: PwPolicyEntry instance """ # Verify target dn exists before getting started user_entry = Account(self._instance, dn) if not user_entry.exists(): raise ValueError( 'Can not create user password policy because the target dn does not exist' ) dn_comps = ldap.dn.explode_dn(user_entry.dn) dn_comps.pop(0) parentdn = ",".join(dn_comps) # Create the pwp container if needed pwp_containers = nsContainers(self._instance, basedn=parentdn) pwp_container = pwp_containers.ensure_state( properties={'cn': 'nsPwPolicyContainer'}) # Create policy entry properties['cn'] = 'cn=nsPwPolicyEntry_user,%s' % dn pwp_entries = PwPolicyEntries(self._instance, pwp_container.dn) pwp_entry = pwp_entries.create(properties=properties) try: # Add policy to the entry user_entry.replace('pwdpolicysubentry', pwp_entry.dn) except ldap.LDAPError as e: # failure, undo what we have done pwp_entry.delete() raise e # make sure that local policies are enabled self.set_global_policy({'nsslapd-pwpolicy-local': 'on'}) return pwp_entry
def delete_local_policy(self, dn): """Delete a local password policy entry :param dn: Entry DN for the local pwpolicy :type dn: str """ subtree = False # Verify target dn exists, and has a policy entry = Account(self._instance, dn) if not entry.exists(): raise ValueError('The target entry dn does not exist') pwp_entry = self.get_pwpolicy_entry(entry.dn) # Subtree or user policy? if self.is_subtree_policy(entry.dn): parentdn = dn subtree = True else: dn_comps = ldap.dn.explode_dn(dn) dn_comps.pop(0) parentdn = ",".join(dn_comps) # Starting deleting the policy, ignore the parts that might already have been removed pwp_container = nsContainer(self._instance, 'cn=nsPwPolicyContainer,%s' % parentdn) if subtree: try: # Delete template cos_templates = CosTemplates(self._instance, pwp_container.dn) cos_template = cos_templates.get('cn=nsPwTemplateEntry,%s' % dn) cos_template.delete() except ldap.NO_SUCH_OBJECT: # Already deleted pass try: # Delete definition cos_pointer_def = CosPointerDefinition( self._instance, 'cn=nsPwPolicy_CoS,%s' % dn) cos_pointer_def.delete() except ldap.NO_SUCH_OBJECT: # Already deleted pass else: try: # Cleanup user entry entry.remove("pwdpolicysubentry", pwp_entry.dn) except ldap.NO_SUCH_ATTRIBUTE: # Policy already removed from user pass # Remove the local policy try: pwp_entry.delete() except ldap.NO_SUCH_OBJECT: # Already deleted pass try: pwp_container.delete() except (ldap.NOT_ALLOWED_ON_NONLEAF, ldap.NO_SUCH_OBJECT): # There are other policies still using this container, no problem pass
def test_chaining_paged_search(topology): """ Test paged search through the chaining db. This would cause a SIGSEGV with paged search which could be triggered by SSSD. :id: 7b29b1f5-26cf-49fa-9fe7-ee29a1408633 :setup: Two standalones in chaining. :steps: 1. Configure chaining between the nodes 2. Do a chaining search (no page) to assert it works 3. Do a paged search through chaining. :expectedresults: 1. Success 2. Success 3. Success """ st1 = topology.ins["standalone1"] st2 = topology.ins["standalone2"] ### We setup so that st1 -> st2 # Clear all the BE in st1 bes1 = Backends(st1) for be in bes1.list(): be.delete() # Setup st1 to chain to st2 chain_plugin_1 = ChainingBackendPlugin(st1) chain_plugin_1.enable() chains = ChainingLinks(st1) chain = chains.create( properties={ 'cn': 'demochain', 'nsslapd-suffix': DEFAULT_SUFFIX, 'nsmultiplexorbinddn': '', 'nsmultiplexorcredentials': '', 'nsfarmserverurl': st2.toLDAPURL(), }) mts = MappingTrees(st1) # Due to a bug in lib389, we need to delete and recreate the mt. for mt in mts.list(): mt.delete() mts.ensure_state( properties={ 'cn': DEFAULT_SUFFIX, 'nsslapd-state': 'backend', 'nsslapd-backend': 'demochain', }) # Restart to enable st1.restart() # Get an anonymous connection. anon = Account(st1, dn='') anon_conn = anon.bind(password='') # Now do a search from st1 -> st2 accs_1 = Accounts(anon_conn, DEFAULT_SUFFIX) assert len(accs_1.list()) > 0 # Allow time to attach lldb if needed. # import time # print("🔥🔥🔥") # time.sleep(45) # Now do a *paged* search from st1 -> st2 assert len(accs_1.list(paged_search=2, paged_critical=False)) > 0
def test_binddn_tracking(topo, _create_inital): """Test Managed Entries basic functionality :id: ea2ddfd4-aaec-11ea-8416-8c16451d917b :setup: Standalone Instance :steps: 1. Set nsslapd-plugin-binddn-tracking attribute under cn=config 2. Add user 3. Managed Entry Plugin runs against managed entries upon any update without validating 4. verify creation of User Private Group with its time stamp value 5. Modify the SN attribute which is not mapped with managed entry 6. run ModRDN operation and check the User Private group 7. Check the time stamp of UPG should be changed now 8. Check the creatorsname should be user dn and internalCreatorsname should be plugin name 9. Check if a managed group entry was created :expected results: 1. Success 2. Success 3. Success 4. Success 5. Success 6. Success 7. Success 8. Success 9. Success """ config = Config(topo.standalone) # set nsslapd-plugin-binddn-tracking attribute under cn=config config.replace('nsslapd-plugin-binddn-tracking', 'on') # Add user user = UserAccounts(topo.standalone, f'cn=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() assert user.get_attr_val_utf8( 'mepManagedEntry') == f'cn=test_user_1000,cn=Groups,{DEFAULT_SUFFIX}' entry = Account(topo.standalone, f'cn=test_user_1000,cn=Groups,{DEFAULT_SUFFIX}') # Managed Entry Plugin runs against managed entries upon any update without validating # verify creation of User Private Group with its time stamp value stamp1 = entry.get_attr_val_utf8('modifyTimestamp') user.replace('sn', 'NewSN_modified') stamp2 = entry.get_attr_val_utf8('modifyTimestamp') # Modify the SN attribute which is not mapped with managed entry # Check the time stamp of UPG should not be changed assert stamp1 == stamp2 time.sleep(1) # run ModRDN operation and check the User Private group user.rename(new_rdn='uid=UserNewRDN', newsuperior='cn=Users,dc=example,dc=com') assert user.get_attr_val_utf8( 'mepManagedEntry') == f'cn=UserNewRDN,cn=Groups,{DEFAULT_SUFFIX}' entry = Account(topo.standalone, f'cn=UserNewRDN,cn=Groups,{DEFAULT_SUFFIX}') stamp3 = entry.get_attr_val_utf8('modifyTimestamp') # Check the time stamp of UPG should be changed now assert stamp2 != stamp3 time.sleep(1) user.replace('gidNumber', '1') stamp4 = entry.get_attr_val_utf8('modifyTimestamp') assert stamp4 != stamp3 # Check the creatorsname should be user dn and internalCreatorsname should be plugin name assert entry.get_attr_val_utf8('creatorsname') == 'cn=directory manager' assert entry.get_attr_val_utf8( 'internalCreatorsname') == 'cn=Managed Entries,cn=plugins,cn=config' assert entry.get_attr_val_utf8('modifiersname') == 'cn=directory manager' user.delete() config.replace('nsslapd-plugin-binddn-tracking', 'off')
def test_mentry01(topo, _create_inital): """Test Managed Entries basic functionality :id: 9b87493b-0493-46f9-8364-6099d0e5d806 :setup: Standalone Instance :steps: 1. Check the plug-in status 2. Add Template and definition entry 3. Add our org units 4. Add users with PosixAccount ObjectClass and verify creation of User Private Group 5. Disable the plug-in and check the status 6. Enable the plug-in and check the status the plug-in is disabled and creation of UPG should fail 7. Add users with PosixAccount ObjectClass and verify creation of User Private Group 8. Add users, run ModRDN operation and check the User Private group 9. Add users, run LDAPMODIFY to change the gidNumber and check the User Private group 10. Checking whether creation of User Private group fails for existing group entry 11. Checking whether adding of posixAccount objectClass to existing user creates UPG 12. Running ModRDN operation and checking the user private groups mepManagedBy attribute 13. Deleting mepManagedBy attribute and running ModRDN operation to check if it creates a new UPG 14. Change the RDN of template entry, DSA Unwilling to perform error expected 15. Change the RDN of cn=Users to cn=TestUsers and check UPG are deleted :expected results: 1. Success 2. Success 3. Success 4. Success 5. Success 6. Success 7. Success 8. Success 9. Success 10. Success 11. Success 12. Success 13. Success 14. Fail(Unwilling to perform ) 15. Success """ # Check the plug-in status mana = ManagedEntriesPlugin(topo.standalone) assert mana.status() # Add Template and definition entry org1 = OrganizationalUnits( topo.standalone, DEFAULT_SUFFIX).create(properties={'ou': 'Users'}) org2 = OrganizationalUnit(topo.standalone, f'ou=Groups,{DEFAULT_SUFFIX}') meps = MEPTemplates(topo.standalone, DEFAULT_SUFFIX) mep_template1 = meps.create( properties={ 'cn': 'UPG Template1', 'mepRDNAttr': 'cn', 'mepStaticAttr': 'objectclass: posixGroup', 'mepMappedAttr': 'cn: $uid|gidNumber: $gidNumber|description: User private group for $uid' .split('|') }) conf_mep = MEPConfigs(topo.standalone) conf_mep.create( properties={ 'cn': 'UPG Definition2', 'originScope': org1.dn, 'originFilter': 'objectclass=posixaccount', 'managedBase': org2.dn, 'managedTemplate': mep_template1.dn }) # Add users with PosixAccount ObjectClass and verify creation of User Private Group user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() assert user.get_attr_val_utf8( 'mepManagedEntry') == f'cn=test_user_1000,ou=Groups,{DEFAULT_SUFFIX}' # Disable the plug-in and check the status mana.disable() user.delete() topo.standalone.restart() # Add users with PosixAccount ObjectClass when the plug-in is disabled and creation of UPG should fail user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() assert not user.get_attr_val_utf8('mepManagedEntry') # Enable the plug-in and check the status mana.enable() user.delete() topo.standalone.restart() # Add users with PosixAccount ObjectClass and verify creation of User Private Group user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() assert user.get_attr_val_utf8( 'mepManagedEntry') == f'cn=test_user_1000,ou=Groups,{DEFAULT_SUFFIX}' # Add users, run ModRDN operation and check the User Private group # Add users, run LDAPMODIFY to change the gidNumber and check the User Private group user.rename(new_rdn='uid=UserNewRDN', newsuperior='ou=Users,dc=example,dc=com') assert user.get_attr_val_utf8( 'mepManagedEntry') == f'cn=UserNewRDN,ou=Groups,{DEFAULT_SUFFIX}' user.replace('gidNumber', '20209') entry = Account(topo.standalone, f'cn=UserNewRDN,ou=Groups,{DEFAULT_SUFFIX}') assert entry.get_attr_val_utf8('gidNumber') == '20209' user.replace_many(('sn', 'new_modified_sn'), ('gidNumber', '31309')) assert entry.get_attr_val_utf8('gidNumber') == '31309' user.delete() # Checking whether creation of User Private group fails for existing group entry Groups(topo.standalone, f'ou=Groups,{DEFAULT_SUFFIX}', rdn=None).create(properties={'cn': 'MENTRY_14'}) user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() with pytest.raises(ldap.NO_SUCH_OBJECT): entry.status() user.delete() # Checking whether adding of posixAccount objectClass to existing user creates UPG # Add Users without posixAccount objectClass users = WithObjectClass(topo.standalone, f'uid=test_test, ou=Users,{DEFAULT_SUFFIX}') user_properties1 = { 'uid': 'test_test', 'cn': 'test', 'sn': 'test', 'mail': '*****@*****.**', 'telephoneNumber': '123' } user = users.create(properties=user_properties1) assert not user.get_attr_val_utf8('mepManagedEntry') # Add posixAccount objectClass user.replace_many( ('objectclass', ['top', 'person', 'inetorgperson', 'posixAccount']), ('homeDirectory', '/home/ok'), ('uidNumber', '61603'), ('gidNumber', '61603')) assert not user.get_attr_val_utf8('mepManagedEntry') user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() entry = Account(topo.standalone, 'cn=test_user_1000,ou=Groups,dc=example,dc=com') # Add inetuser objectClass user.replace_many(('objectclass', [ 'top', 'account', 'posixaccount', 'inetOrgPerson', 'organizationalPerson', 'nsMemberOf', 'nsAccount', 'person', 'mepOriginEntry', 'inetuser' ]), ('memberOf', entry.dn)) assert entry.status() user.delete() user = UserAccounts(topo.standalone, f'ou=Users,{DEFAULT_SUFFIX}', rdn=None).create_test_user() entry = Account(topo.standalone, 'cn=test_user_1000,ou=Groups,dc=example,dc=com') # Add groupofNames objectClass user.replace_many(('objectclass', [ 'top', 'account', 'posixaccount', 'inetOrgPerson', 'organizationalPerson', 'nsMemberOf', 'nsAccount', 'person', 'mepOriginEntry', 'groupofNames' ]), ('memberOf', user.dn)) assert entry.status() # Running ModRDN operation and checking the user private groups mepManagedBy # attribute was also reset because the modrdn on the origin will do a modrdn # on checkManagedEntry to match the new rdn value of the origin entry checkManagedEntry = UserAccounts(topo.standalone, f'ou=Groups,{DEFAULT_SUFFIX}', rdn=None) check_entry = checkManagedEntry.create( properties={ 'objectclass': ['top', 'extensibleObject'], 'uid': 'CheckModRDN', 'uidNumber': '12', 'gidNumber': '12', 'homeDirectory': '/home', 'sn': 'tmp', 'cn': 'tmp', }) user.replace('mepManagedEntry', check_entry.dn) user.rename(new_rdn='uid=UserNewRDN', newsuperior='ou=Users,dc=example,dc=com') assert user.get_attr_val_utf8_l( 'mepManagedEntry' ) == f'cn=UserNewRDN,ou=Groups,{DEFAULT_SUFFIX}'.lower() # Deleting mepManagedBy attribute and running ModRDN operation to check if it creates a new UPG user.remove('mepManagedEntry', f'cn=UserNewRDN,ou=Groups,{DEFAULT_SUFFIX}') user.rename(new_rdn='uid=UserNewRDN1', newsuperior='ou=Users,dc=example,dc=com') assert user.get_attr_val_utf8( 'mepManagedEntry') == f'cn=UserNewRDN1,ou=Groups,{DEFAULT_SUFFIX}' # Change the RDN of template entry, DSA Unwilling to perform error expected mep = MEPTemplate(topo.standalone, f'cn=UPG Template,{DEFAULT_SUFFIX}') with pytest.raises(ldap.UNWILLING_TO_PERFORM): mep.rename(new_rdn='cn=UPG Template2', newsuperior='dc=example,dc=com') # Change the RDN of cn=Users to cn=TestUsers and check UPG are deleted before = user.get_attr_val_utf8('mepManagedEntry') user.rename(new_rdn='uid=Anuj', newsuperior='ou=Users,dc=example,dc=com') assert user.get_attr_val_utf8('mepManagedEntry') != before