class RawLogParser: def __init__(self, args_dict): self.args_dict = args_dict self.logger = CustomLogger(self.args_dict) def parse(self, line): # This regex takes a raw log and parses it into a few elements # time, server, process, and arbitrary remainder pattern = r'^(\w+ +\d+ \d+:\d+:\d+) ([a-zA-Z0-9\.]+) (\w+\[\d+\]): conn=(\d+) (.*)$' match = re.search(pattern, line) if match: return { 'time': match.group(1), 'server': match.group(2), 'process': match.group(3), 'conn': match.group(4), 'rest': match.group(5) } else: # raise Exception('Failed to parse raw line: {}'.format(line)) if self.args_dict['verbose']: self.logger.log( "ERROR: Failed to parse raw line: {}".format(line), error=True) return None
def test_default_json(self, capsys): logger = CustomLogger(TEST_CUSTOM_LOGGER_ARGS_DICT) event = {"category": "ldap", "details": "abc's"} logger.log(event) out, err = capsys.readouterr() assert out == '{"category": "ldap", "details": "abc\'s"}\n' assert err == ""
def __init__(self, conn_id, args_dict): self.conn_id = conn_id self.time = "" self.server = "" self.process = "" self.operations = {} self.tls_status = False self.file_descriptors = [] self.logger = CustomLogger(args_dict) self.authenticated_status = False self.user = ""
def test_file_with_existant_file(self): filename = "test_file_with_existant_file.txt" args_dict = { 'output_stdout': False, 'input_type': 'file', 'output_file': True, 'output_syslog': False, 'host': '0.0.0.0', 'daemonize': False, 'input_file_name': None, 'noconfig': False, 'output_file_name': filename, 'output_stderr': False, 'config': 'humanizer_settings.json', 'port': '1514', 'syslog_facility': 'LOG_LOCAL5', 'verbose': True } # Clean up to make sure we don't have an existing file try: os.remove(filename) except Exception as e: pass with open(filename, "w") as f: f.write('"hello world"\n') # Check to see if the content of the file is what we are expecting with open(filename) as f: s = f.read() assert s == '"hello world"\n' # Try to log to a file that does exist and make sure it creates it logger = CustomLogger(args_dict) assert os.path.isfile(filename) == True logger.log("hello world") assert os.path.isfile(filename) == True # Check to see if the content of the file is what we are expecting with open(filename) as f: s = f.read() assert s == '"hello world"\n"hello world"\n' # Clean up try: os.remove(filename) except Exception as e: pass
def __init__(self, args_dict): self.args_dict = args_dict if self.args_dict['host']: self.host = self.args_dict['host'] if self.args_dict['port']: self.port = self.args_dict['port'] self.logger = CustomLogger(self.args_dict) # Override a parser with appropriate args_dict optioned instance global syslog_server_parser syslog_server_parser = Parser(None, self.args_dict)
def test_stderr(self, capsys): args_dict = { 'output_stdout': False, 'output_stderr': True, 'input_type': 'file', 'output_file': False, 'output_syslog': False, 'host': '0.0.0.0', 'daemonize': False, 'input_file_name': None, 'noconfig': False, 'output_file_name': 'humanizer.log', 'config': 'humanizer_settings.json', 'port': '1514', 'syslog_facility': 'LOG_LOCAL5' } logger = CustomLogger(args_dict) logger.log("hello world") out, err = capsys.readouterr() assert out == "" assert err == '"hello world"\n'
def test_file_with_no_file(self): args_dict = { 'output_stdout': False, 'input_type': 'file', 'output_file': True, 'output_syslog': False, 'host': '0.0.0.0', 'daemonize': False, 'input_file_name': None, 'noconfig': False, 'output_file_name': None, 'output_stderr': False, 'config': 'humanizer_settings.json', 'port': '1514', 'syslog_facility': 'LOG_LOCAL5', 'verbose': True } logger = CustomLogger(args_dict) with pytest.raises(Exception) as excinfo: logger.log("hello world") assert str( excinfo.value ) == 'log_type of "file" was chosen, but no log file specified'
def __init__(self, args_dict): self.args_dict = args_dict self.logger = CustomLogger(self.args_dict)
class Connection: def __init__(self, conn_id, args_dict): self.conn_id = conn_id self.time = "" self.server = "" self.process = "" self.operations = {} self.tls_status = False self.file_descriptors = [] self.logger = CustomLogger(args_dict) self.authenticated_status = False self.user = "" def dict(self): return { "conn_id": self.conn_id, "time": self.time, "client": self.client(), "server": self.server, "tls": self.tls(), "authenticated": self.authenticated(), "user": self.user } def reconstitute(self, event_dict): combined_dict = {} combined_dict.update(self.dict()) combined_dict.update(event_dict) return combined_dict def authenticated(self): mail_regex = r'.*mail=([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)' uid_regex = r'.*uid=([a-zA-Z0-9._-]+)' # requirements: single operation where we have a BIND verb followed by an LDAP_SUCCESS for operation in self.operations.values(): for request in operation.requests: if request.get("verb") == "BIND": # Set user (even if they aren't authenticated, so we can track attempts) for detail in request.get("details"): mail_match_object = re.match(mail_regex, detail) if mail_match_object: self.user = mail_match_object.group(1) uid_match_object = re.match(uid_regex, detail) if uid_match_object: self.user = uid_match_object.group(1) # Set status if that user was successful if operation.response_verb == "RESULT" and operation.error == "LDAP_SUCCESS": self.authenticated_status = True return self.authenticated_status def tls(self): for file_descriptor in self.file_descriptors: if file_descriptor.verb == "TLS" and file_descriptor.details.startswith( "established"): self.tls_status = True return self.tls_status def closed(self): for file_descriptor in self.file_descriptors: if file_descriptor.verb == "closed": return True return False def client(self): for file_descriptor in self.file_descriptors: if file_descriptor.verb == "ACCEPT": pattern = r'from IP=(\d+\.\d+\.\d+\.\d+):' match = re.search(pattern, file_descriptor.details) if match: return match.group(1) return "" def add_operation(self, rest): # Expecting something like: # op=1 BIND dn="uid=bind-generateusers,ou=logins,dc=example" mech=SIMPLE ssf=0 # pattern = r'^op=(\d+) (.*)$' match = re.search(pattern, rest) if match: op_id = match.group(1) operation = self.operations.get(int(op_id)) # if an existing operation, update it's context if operation: operation.add_event(match.group(2)) # if a new operation, add it to our operations list else: operation = Operation(int(op_id)) operation.add_event(match.group(2)) self.operations[int(op_id)] = operation if operation.loggable(): self.logger.log(self.reconstitute(operation.dict())) else: raise Exception('Malformed operation: {}'.format(rest)) def add_file_descriptor(self, rest): # Expecting something like: # fd=34 ACCEPT from IP=192.168.1.1:56822 (IP=0.0.0.0:389) # pattern = r'^fd=(\d+) (.*)$' match = re.search(pattern, rest) if match: file_descriptor = FileDescriptor(int(match.group(1))) file_descriptor.add_event(match.group(2)) self.file_descriptors.append(file_descriptor) if file_descriptor.loggable(): self.logger.log(self.reconstitute(file_descriptor.dict())) else: raise Exception('Malformed file file_descriptor: {}'.format(rest)) # Something happened, this method's job is to update the context def add_event(self, event): self.time = event['time'] self.server = event['server'] self.process = event['process'] self.add_rest(event['rest']) def add_rest(self, rest): if rest.startswith('op'): self.add_operation(rest) elif rest.startswith('fd'): self.add_file_descriptor(rest) else: raise Exception('Unsupported option: {}'.format(rest))
def test_stdout(self, capsys): logger = CustomLogger(TEST_CUSTOM_LOGGER_ARGS_DICT) logger.log("hello world") out, err = capsys.readouterr() assert out == '"hello world"\n' assert err == ""
def test_creation(self): logger = CustomLogger(TEST_CUSTOM_LOGGER_ARGS_DICT) assert isinstance(logger, CustomLogger)