def lock(self): """Set the entry dn to nsDisabledRole and ensure it exists""" current_status = self.status() if current_status["state"] == RoleState.DIRECTLY_LOCKED: raise ValueError( f"Role is already {current_status['state'].describe()}") inst = self._instance mapping_trees = MappingTrees(inst) root_suffix = "" root_suffix = mapping_trees.get_root_suffix_by_entry(self.dn) if root_suffix: managed_roles = ManagedRoles(inst, root_suffix) managed_role = managed_roles.ensure_state( properties={"cn": "nsManagedDisabledRole"}) nested_roles = NestedRoles(inst, root_suffix) try: disabled_role = nested_roles.get("nsDisabledRole") except ldap.NO_SUCH_OBJECT: # We don't use "ensure_state" because we want to preserve the existing attributes disabled_role = nested_roles.create(properties={ "cn": "nsDisabledRole", "nsRoleDN": managed_role.dn }) disabled_role.add("nsRoleDN", self.dn) inact_containers = nsContainers(inst, basedn=root_suffix) inact_container = inact_containers.ensure_state( properties={'cn': 'nsAccountInactivationTmp'}) cos_templates = CosTemplates(inst, inact_container.dn) cos_templates.ensure_state( properties={ 'cosPriority': '1', 'nsAccountLock': 'true', 'cn': f'{disabled_role.dn}' }) cos_classic_defs = CosClassicDefinitions(inst, root_suffix) cos_classic_defs.ensure_state( properties={ 'cosAttribute': 'nsAccountLock operational', 'cosSpecifier': 'nsRole', 'cosTemplateDn': inact_container.dn, 'cn': 'nsAccountInactivation_cos' })
def status(self): """Check if role is locked in nsDisabledRole (directly or indirectly) :returns: a dict """ inst = self._instance disabled_roles = {} try: mapping_trees = MappingTrees(inst) root_suffix = mapping_trees.get_root_suffix_by_entry(self.dn) roles = Roles(inst, root_suffix) disabled_roles = roles.get_disabled_roles() nested_roles = NestedRoles(inst, root_suffix) disabled_role = nested_roles.get("nsDisabledRole") inact_containers = nsContainers(inst, basedn=root_suffix) inact_container = inact_containers.get('nsAccountInactivationTmp') cos_templates = CosTemplates(inst, inact_container.dn) cos_template = cos_templates.get(f'{disabled_role.dn}') cos_template.present('cosPriority', '1') cos_template.present('nsAccountLock', 'true') cos_classic_defs = CosClassicDefinitions(inst, root_suffix) cos_classic_def = cos_classic_defs.get('nsAccountInactivation_cos') cos_classic_def.present('cosAttribute', 'nsAccountLock operational') cos_classic_def.present('cosTemplateDn', inact_container.dn) cos_classic_def.present('cosSpecifier', 'nsRole') except ldap.NO_SUCH_OBJECT: return self._format_status_message(RoleState.PROBABLY_ACTIVATED) for role, parent in disabled_roles.items(): if str.lower(self.dn) == str.lower(role.dn): if parent is None: return self._format_status_message( RoleState.DIRECTLY_LOCKED) else: return self._format_status_message( RoleState.INDIRECTLY_LOCKED, parent) return self._format_status_message(RoleState.ACTIVATED)
def unlock(self): """Remove the entry dn from nsDisabledRole if it exists""" inst = self._instance current_status = self.status() if current_status["state"] == RoleState.ACTIVATED: raise ValueError("Role is already active") mapping_trees = MappingTrees(inst) root_suffix = mapping_trees.get_root_suffix_by_entry(self.dn) roles = NestedRoles(inst, root_suffix) try: disabled_role = roles.get("nsDisabledRole") # Still we want to ensure that it is not locked directly too disabled_role.ensure_removed("nsRoleDN", self.dn) except ldap.NO_SUCH_OBJECT: pass # Notify if it's locked indirectly if current_status["state"] == RoleState.INDIRECTLY_LOCKED: raise ValueError( f"Role is {current_status['state'].describe(current_status['role_dn'])}. Please, deal with it separately" )
def status(self): """Check if account is locked by Account Policy plugin or nsAccountLock (directly or indirectly) :returns: a dict in a format - {"status": status, "params": activity_data, "calc_time": epoch_time} """ inst = self._instance # Fetch Account Policy data if its enabled plugin = AccountPolicyPlugin(inst) state_attr = "" alt_state_attr = "" limit = "" spec_attr = "" limit_attr = "" process_account_policy = False try: process_account_policy = plugin.status() except IndexError: self._log.debug( "The bound user doesn't have rights to access Account Policy settings. Not checking." ) if process_account_policy: config_dn = plugin.get_attr_val_utf8("nsslapd-pluginarg0") config = AccountPolicyConfig(inst, config_dn) config_settings = config.get_attrs_vals_utf8([ "stateattrname", "altstateattrname", "specattrname", "limitattrname" ]) state_attr = self._dict_get_with_ignore_indexerror( config_settings, "stateattrname") alt_state_attr = self._dict_get_with_ignore_indexerror( config_settings, "altstateattrname") spec_attr = self._dict_get_with_ignore_indexerror( config_settings, "specattrname") limit_attr = self._dict_get_with_ignore_indexerror( config_settings, "limitattrname") mapping_trees = MappingTrees(inst) root_suffix = mapping_trees.get_root_suffix_by_entry(self.dn) cos_entries = CosTemplates(inst, root_suffix) accpol_entry_dn = "" for cos in cos_entries.list(): if cos.present(spec_attr): accpol_entry_dn = cos.get_attr_val_utf8_l(spec_attr) if accpol_entry_dn: accpol_entry = AccountPolicyEntry(inst, accpol_entry_dn) else: accpol_entry = config limit = accpol_entry.get_attr_val_utf8_l(limit_attr) # Fetch account data account_data = self.get_attrs_vals_utf8([ "createTimestamp", "modifyTimeStamp", "nsAccountLock", state_attr ]) last_login_time = self._dict_get_with_ignore_indexerror( account_data, state_attr) if not last_login_time: last_login_time = self._dict_get_with_ignore_indexerror( account_data, alt_state_attr) create_time = self._dict_get_with_ignore_indexerror( account_data, "createTimestamp") modify_time = self._dict_get_with_ignore_indexerror( account_data, "modifyTimeStamp") acct_roles = self.get_attr_vals_utf8_l("nsRole") mapping_trees = MappingTrees(inst) root_suffix = "" try: root_suffix = mapping_trees.get_root_suffix_by_entry(self.dn) except ldap.NO_SUCH_OBJECT: self._log.debug( "The bound user doesn't have rights to access disabled roles settings. Not checking." ) if root_suffix: roles = Roles(inst, root_suffix) try: disabled_roles = roles.get_disabled_roles() # Locked indirectly through a role locked_indirectly_role_dn = "" for role in acct_roles: if str.lower(role) in [ str.lower(role.dn) for role in disabled_roles.keys() ]: locked_indirectly_role_dn = role if locked_indirectly_role_dn: return self._format_status_message( AccountState.INDIRECTLY_LOCKED, create_time, modify_time, last_login_time, limit, locked_indirectly_role_dn) except ldap.NO_SUCH_OBJECT: pass # Locked directly if self._dict_get_with_ignore_indexerror(account_data, "nsAccountLock") == "true": return self._format_status_message(AccountState.DIRECTLY_LOCKED, create_time, modify_time, last_login_time, limit) # Locked indirectly through Account Policy plugin if process_account_policy and last_login_time: # Now check the Account Policy Plugin inactivity limits remaining_time = float(limit) - (time.mktime( time.gmtime()) - gentime_to_posix_time(last_login_time)) if remaining_time <= 0: return self._format_status_message( AccountState.INACTIVITY_LIMIT_EXCEEDED, create_time, modify_time, last_login_time, limit) # All checks are passed - we are active return self._format_status_message(AccountState.ACTIVATED, create_time, modify_time, last_login_time, limit)