def _ldap_utilities_set_password_function(self, event, *args, **kwargs): """Function: A function that allows you to set a new password for an LDAP entry given the entry's DN""" log = logging.getLogger(__name__) try: yield StatusMessage("Starting ldap_utilities_set_password") # Instansiate helper (which gets appconfigs from file) helper = LDAPUtilitiesHelper(self.options) yield StatusMessage("Appconfig Settings OK") # Get function inputs input_ldap_dn = helper.get_function_input(kwargs, "ldap_dn") # text (required) input_ldap_new_password = helper.get_function_input(kwargs, "ldap_new_password") # text (required) yield StatusMessage("Function Inputs OK") # Instansiate LDAP Server and Connection c = helper.get_ldap_connection() try: # Bind to the connection c.bind() except: raise ValueError("Cannot connect to LDAP Server. Ensure credentials are correct") # Inform user msg = "" if helper.LDAP_IS_ACTIVE_DIRECTORY: msg = "Connected to {0}".format("Active Directory") else: msg = "Connected to {0}".format("LDAP Server") yield StatusMessage(msg) res = False try: yield StatusMessage("Attempting to change password") if helper.LDAP_IS_ACTIVE_DIRECTORY: res = c.extend.microsoft.modify_password(str(input_ldap_dn), input_ldap_new_password) else: res = c.modify(input_ldap_dn, {'userPassword': [(MODIFY_REPLACE, [input_ldap_new_password])]}) except Exception: raise ValueError("Could not change password. Check input_ldap_dn and input_ldap_new_password are valid") finally: # Unbind connection c.unbind() results = { "success": res, "user_dn": input_ldap_dn } log.info("Completed") # Produce a FunctionResult with the results yield FunctionResult(results) except Exception: yield FunctionError()
def _ldap_utilities_search_function(self, event, *args, **kwargs): """Resilient Function: entry point """ LOG = logging.getLogger(__name__) def escape_chars(str, encoding=None): """ Escape some characters in filter. Escape a set of characters in the filter string to help to mitigate against possibility of injection. This has a subset of characters escaped in ldap3 function escape_filter_chars. """ if encoding is None: encoding = get_config_parameter('DEFAULT_ENCODING') str = to_unicode(str, encoding) escaped_str = str.replace('\\', '\\5c') escaped_str = escaped_str.replace('*', '\\2a') escaped_str = escaped_str.replace('\x00', '\\00') return escaped_str def validate_params(search_params): """"Check mandatory fields Do a number of checks on input fields. """ for k in search_params: if re.match(r'^search_filter$', k): # Do some basic checks on filter. if not re.match(r'^\(.*\)$', search_params[k]): # Outer parentheses not found thus invalid filter raise ValueError("LDAP search filter invalid format") if not search_params[k].count( '(') == search_params[k].count(')'): # The count of '(' and ')' characters need to match in filter. raise LDAPInvalidFilterError( "Invalid filter because of unmatched parentheses.") def update_param_fields(search_params): """"Update %ldap_param% fields Escape some characters in search_params[search_filter]. If search_params[search_filter] hash key has %ldap_param% set in it's value, update value replacing %param% with actual escaped value from param. """ for k in search_params: if re.match('^search_filter$', k): # Escape some characters in search_filter which might cause LDAP injection. search_params[k] = escape_chars(search_params[k]) # Search for "%ldap_param% token in parameter. if re.search("%ldap_param%", search_params[k]): # Only allow "%ldap_param% in search_filter field. if re.match('^search_filter$', k): if "param" not in search_params: raise Exception( "The parameter '{}' contains string token '%ldap_param%' but the input '{}' is blank." .format(k, "ldap_search_param")) else: # Insert escaped param value in filter, need to escape any backslashes X 2 for regex. search_params[k] = re.sub( "%ldap_param%", search_params["param"].replace( '\\', '\\\\'), search_params[k]) LOG.debug(('Transformed parameter' + k + ' to ' + search_params[k])) else: raise Exception( "The string %ldap_param% not allowed in parameter '{}' " .format(k)) return search_params try: yield StatusMessage("Starting ldap_utilities_search") # Instansiate helper (which gets appconfigs from file) helper = LDAPUtilitiesHelper(self.options) yield StatusMessage("Appconfig Settings OK") # Get function inputs input_ldap_search_base = helper.get_function_input( kwargs, "ldap_search_base") # text (required) input_ldap_search_filter = self.get_textarea_param( kwargs.get("ldap_search_filter")) # textarea (required) input_ldap_search_attributes = helper.get_function_input( kwargs, "ldap_search_attributes", optional=True) # text (optional) input_ldap_search_param = helper.get_function_input( kwargs, "ldap_search_param", optional=True) # text (optional) if input_ldap_search_attributes is None: input_ldap_search_attributes = "" else: attributes = input_ldap_search_attributes.split(',') input_ldap_search_attributes = [ str(attr) for attr in attributes ] yield StatusMessage("Function Inputs OK") search_params = { 'search_base': input_ldap_search_base, 'search_filter': input_ldap_search_filter, 'search_attributes': input_ldap_search_attributes } if input_ldap_search_param: # Escape 'param' parameter. search_params.setdefault( 'param', escape_filter_chars(input_ldap_search_param)) yield StatusMessage("Validating LDAP Parameters") validate_params(search_params) search_params = update_param_fields(search_params) # Instansiate LDAP Server and Connection c = helper.get_ldap_connection() try: # Bind to the connection c.bind() except Exception as e: raise ValueError( "Cannot connect to LDAP Server. Ensure credentials are correct" ) # Inform user msg = "" if helper.LDAP_IS_ACTIVE_DIRECTORY: msg = "Connected to {0}".format("Active Directory") else: msg = "Connected to {0}".format("LDAP Server") yield StatusMessage(msg) res = False entries = [] success = False try: yield StatusMessage("Attempting to Search") res = c.search(search_params["search_base"], search_params["search_filter"], attributes=search_params["search_attributes"]) if res and len(c.entries) > 0: entries = json.loads(c.response_to_json())["entries"] LOG.info("Result contains %s entries", len(entries)) # Each entry has 'dn' and dict of 'attributes'. Move attributes to the top level for easier processing. for entry in entries: LOG.debug(json.dumps(entry)) entry.update(entry.pop("attributes", None)) yield StatusMessage("{0} entries found".format( len(entries))) success = True else: yield StatusMessage("No entries found") success = False except LDAPSocketOpenError: success = False raise ValueError("Invalid Search Base", input_ldap_search_base) except Exception as e: success = False raise ValueError( "Could not Search the LDAP Server. Ensure 'ldap_search_base' is valid", e) finally: # Unbind connection c.unbind() results = {"success": success, "entries": entries} LOG.info("Completed") LOG.debug(json.dumps(results)) # Produce a FunctionResult with the return value. yield FunctionResult(results) except Exception: yield FunctionError()
def _ldap_utilities_add_to_groups_function(self, event, *args, **kwargs): """Function: A function that allows adding multiple users to multiple groups""" log = logging.getLogger(__name__) try: yield StatusMessage("Starting ldap_utilities_add_to_groups") # Instansiate helper (which gets appconfigs from file) helper = LDAPUtilitiesHelper(self.options) yield StatusMessage("Appconfig Settings OK") # Get function inputs input_ldap_multiple_user_dn_asString = helper.get_function_input( kwargs, "ldap_multiple_user_dn" ) # text (required) [string repersentation of an array] input_ldap_multiple_group_dn_asString = helper.get_function_input( kwargs, "ldap_multiple_group_dn" ) # text (required) [string repersentation of an array] yield StatusMessage("Function Inputs OK") if not helper.LDAP_IS_ACTIVE_DIRECTORY: raise FunctionError( "This function only supports an Active Directory connection. Make sure ldap_is_active_directory is set to True in the app.config file" ) try: # Try converting input to an array input_ldap_multiple_user_dn = literal_eval( input_ldap_multiple_user_dn_asString) input_ldap_multiple_group_dn = literal_eval( input_ldap_multiple_group_dn_asString) except Exception: raise ValueError( """input_ldap_multiple_user_dn and input_ldap_multiple_group_dn must be a string repersenation of an array e.g. "['dn=Accounts Group,dc=example,dc=com', 'dn=IT Group,dc=example,dc=com']" """ ) # Instansiate LDAP Server and Connection c = helper.get_ldap_connection() try: # Bind to the connection c.bind() except Exception as err: raise ValueError( "Cannot connect to LDAP Server. Ensure credentials are correct\n Error: {0}" .format(err)) # Inform user msg = "Connected to {0}".format("Active Directory") yield StatusMessage(msg) res = False try: yield StatusMessage("Attempting to add user(s) to group(s)") # perform the removeMermbersFromGroups operation res = ad_add_members_to_groups(c, input_ldap_multiple_user_dn, input_ldap_multiple_group_dn, True) except Exception: raise ValueError("Ensure all user and group DNs exist") finally: # Unbind connection c.unbind() results = { "success": res, "users_dn": input_ldap_multiple_user_dn, "groups_dn": input_ldap_multiple_group_dn } log.info("Completed") # Produce a FunctionResult with the results yield FunctionResult(results) except Exception: yield FunctionError()
def _ldap_utilities_toggle_access_function(self, event, *args, **kwargs): """Function: A function that allows an LDAP user, with the correct privileges to enable or disable another user given their DN""" log = logging.getLogger(__name__) try: yield StatusMessage("Starting ldap_utilities_toggle_access") # Instansiate helper (which gets appconfigs from file) helper = LDAPUtilitiesHelper(self.options) yield StatusMessage("Appconfig Settings OK") # Get function inputs input_ldap_dn = helper.get_function_input(kwargs, "ldap_dn") # text (required) input_ldap_toggle_access = helper.get_function_input(kwargs, "ldap_toggle_access")["name"] # select, values: "Enable", "Disable" (required) yield StatusMessage("Function Inputs OK") if not helper.LDAP_IS_ACTIVE_DIRECTORY: raise FunctionError("This function only supports an Active Directory connection. Make sure ldap_is_active_directory is set to True in the app.config file") # Set local vars ldap_user_account_control_attribute = "userAccountControl" if (input_ldap_toggle_access.lower() == 'enable'): ldap_user_accout_control_value = 512 user_status = "Enabled" elif (input_ldap_toggle_access.lower() == 'disable'): ldap_user_accout_control_value = 514 user_status = "Disabled" else: raise ValueError("ldap_toggle_access function input must be 'Enable' or 'Disable'") # Instansiate LDAP Server and Connection c = helper.get_ldap_connection() try: # Bind to the connection c.bind() except: raise ValueError("Cannot connect to LDAP Server. Ensure credentials are correct") # Inform user msg = "" if helper.LDAP_IS_ACTIVE_DIRECTORY: msg = "Connected to {0}".format("Active Directory") else: msg = "Connected to {0}".format("LDAP Server") yield StatusMessage(msg) res = False try: yield StatusMessage("Attempting to {0} {1}".format(input_ldap_toggle_access, input_ldap_dn)) # perform the Modify operation res = c.modify(input_ldap_dn, {ldap_user_account_control_attribute: [(MODIFY_REPLACE, [ldap_user_accout_control_value])]}) except Exception: raise ValueError("Could not toggle access for this user. Ensue ldap_dn is valid") finally: # Unbind connection c.unbind() results = { "success": res, "user_dn": input_ldap_dn, "user_status": user_status } log.info("Completed") # Produce a FunctionResult with the results yield FunctionResult(results) except Exception: yield FunctionError()
def _ldap_utilities_update_function(self, event, *args, **kwargs): """Function: A function that updates the attribute of a DN with a new value""" log = logging.getLogger(__name__) try: yield StatusMessage("Starting ldap_utilities_update") # Instansiate helper (which gets appconfigs from file) helper = LDAPUtilitiesHelper(self.options) yield StatusMessage("Appconfig Settings OK") # Get function inputs input_ldap_dn = helper.get_function_input( kwargs, "ldap_dn") # text (required) input_ldap_attribute_name = helper.get_function_input( kwargs, "ldap_attribute_name") # text (required) input_ldap_attribute_values_asString = helper.get_function_input( kwargs, "ldap_attribute_values" ) # text (required) [string repersentation of an array] yield StatusMessage("Function Inputs OK") try: # Try converting input to an array input_ldap_attribute_values = literal_eval( input_ldap_attribute_values_asString) except Exception: raise ValueError( """input_ldap_attribute_values must be a string repersenation of an array e.g. "['stringValue1, 1234, 'stringValue2']" """ ) # Instansiate LDAP Server and Connection c = helper.get_ldap_connection() try: # Bind to the connection c.bind() except: raise ValueError( "Cannot connect to LDAP Server. Ensure credentials are correct" ) # Inform user msg = "" if helper.LDAP_IS_ACTIVE_DIRECTORY: msg = "Connected to {0}".format("Active Directory") else: msg = "Connected to {0}".format("LDAP Server") yield StatusMessage(msg) res = False try: yield StatusMessage("Attempting to update {0}".format( input_ldap_attribute_name)) # perform the Modify operation res = c.modify( input_ldap_dn, { input_ldap_attribute_name: [(MODIFY_REPLACE, input_ldap_attribute_values)] }) except Exception: raise ValueError( "Failed to update. Ensure 'ldap_dn' is valid and the update meets your LDAP CONSTRAINTS" ) finally: # Unbind connection c.unbind() results = { "success": res, "attribute_name": input_ldap_attribute_name, "attribute_values": input_ldap_attribute_values, "user_dn": input_ldap_dn } log.info("Completed") # Produce a FunctionResult with the results yield FunctionResult(results) except Exception: yield FunctionError()
def _ldap_utilities_search_function(self, event, *args, **kwargs): """Resilient Function: entry point """ log = logging.getLogger(__name__) def replace_ldap_param(ldap_param_value=None, ldap_search_filter=""): re_pattern = "%ldap_param%" # if "ldap_param" in ldap_search_filter: if re.search(re_pattern, ldap_search_filter) is not None: if ldap_param_value is None: raise ValueError( "The LDAP Search Filter '{0}' contains the key '%ldap_param%' but no value has been given for ldap_search_param." .format(ldap_search_filter)) else: # Insert escaped param value in filter, need to escape any backslashes X 2 for regex. ldap_search_filter = re.sub( re_pattern, ldap_param_value.replace('\\', '\\\\'), ldap_search_filter) return ldap_search_filter try: yield StatusMessage("Starting ldap_utilities_search") # Instansiate helper (which gets appconfigs from file) helper = LDAPUtilitiesHelper(self.options) yield StatusMessage("Appconfig Settings OK") # Get function inputs input_ldap_search_base = helper.get_function_input( kwargs, "ldap_search_base") # text (required) input_ldap_search_filter = self.get_textarea_param( kwargs.get("ldap_search_filter")) # textarea (required) input_ldap_search_attributes = helper.get_function_input( kwargs, "ldap_search_attributes", optional=True) # text (optional) input_ldap_search_param = helper.get_function_input( kwargs, "ldap_search_param", optional=True) # text (optional) # If search_attributes is not specified, request that ALL_ATTRIBUTES for the DN be returned if input_ldap_search_attributes is None: input_ldap_search_attributes = ALL_ATTRIBUTES else: input_ldap_search_attributes = [ str(attr) for attr in input_ldap_search_attributes.split(',') ] if input_ldap_search_param is not None: # Escape special chars from the search_param input_ldap_search_param = escape_filter_chars( input_ldap_search_param) yield StatusMessage("Function Inputs OK") input_ldap_search_filter = replace_ldap_param( input_ldap_search_param, input_ldap_search_filter) # Instansiate LDAP Server and Connection conn = helper.get_ldap_connection() try: # Bind to the connection conn.bind() except Exception as err: raise ValueError( "Cannot connect to LDAP Server. Ensure credentials are correct\n Error: {0}" .format(err)) try: # Inform user yield StatusMessage("Connected to {0}".format( "Active Directory" if helper. LDAP_IS_ACTIVE_DIRECTORY else "LDAP Server")) res = None entries = [] success = False yield StatusMessage("Attempting to Search") res = conn.search(search_base=input_ldap_search_base, search_filter=input_ldap_search_filter, attributes=input_ldap_search_attributes) if res and len(conn.entries) > 0: entries = json.loads(conn.response_to_json())["entries"] log.info("Result contains %s entries", len(entries)) # Each entry has 'dn' and dict of 'attributes'. Move attributes to the top level for easier processing. for entry in entries: entry.update(entry.pop("attributes", None)) yield StatusMessage("{0} entries found".format( len(entries))) success = True else: yield StatusMessage("No entries found") success = False except LDAPSocketOpenError: success = False raise ValueError("Invalid Search Base", input_ldap_search_base) except Exception as err: success = False raise ValueError( "Could not Search the LDAP Server. Ensure 'ldap_search_base' is valid", err) finally: # Unbind connection conn.unbind() results = {"success": success, "entries": entries} log.info("Completed") log.debug("RESULTS: %s", results) # Produce a FunctionResult with the return value. yield FunctionResult(results) except Exception: yield FunctionError()