diff --git a/changelog/54713.added b/changelog/54713.added new file mode 100644 index 00000000000..174564b4162 --- /dev/null +++ b/changelog/54713.added @@ -0,0 +1 @@ +Added Windows Event Viewer support diff --git a/doc/ref/modules/all/index.rst b/doc/ref/modules/all/index.rst index 1f7cf1f37d5..4a90f638916 100644 --- a/doc/ref/modules/all/index.rst +++ b/doc/ref/modules/all/index.rst @@ -510,7 +510,7 @@ execution modules win_dism win_dns_client win_dsc - win_event_viewer + win_event win_file win_firewall win_groupadd diff --git a/doc/ref/modules/all/salt.modules.win_event.rst b/doc/ref/modules/all/salt.modules.win_event.rst new file mode 100644 index 00000000000..826326b68cb --- /dev/null +++ b/doc/ref/modules/all/salt.modules.win_event.rst @@ -0,0 +1,6 @@ +====================== +salt.modules.win_event +====================== + +.. automodule:: salt.modules.win_event + :members: diff --git a/doc/ref/modules/all/salt.modules.win_event_viewer.rst b/doc/ref/modules/all/salt.modules.win_event_viewer.rst deleted file mode 100644 index fb5d171124f..00000000000 --- a/doc/ref/modules/all/salt.modules.win_event_viewer.rst +++ /dev/null @@ -1,6 +0,0 @@ -============================= -salt.modules.win_event_viewer -============================= - -.. automodule:: salt.modules.win_event_viewer - :members: diff --git a/salt/modules/win_event.py b/salt/modules/win_event.py index 5347fba5221..fbe690714c6 100644 --- a/salt/modules/win_event.py +++ b/salt/modules/win_event.py @@ -3,43 +3,41 @@ A module for working with the Windows Event log system. """ # https://docs.microsoft.com/en-us/windows/win32/eventlog/event-logging -from __future__ import absolute_import - -# Import Python libs import collections import logging + import xmltodict -# Import Salt Libs import salt.utils.platform import salt.utils.stringutils from salt.exceptions import CommandExecutionError -# Import Third Party Libs try: + import pywintypes import win32evtlog import win32evtlogutil import winerror - import pywintypes + IMPORT_STATUS = True except ImportError: IMPORT_STATUS = False # keys of all the parts of a Event supported by the API -EVENT_PARTS = ("closingRecordNumber", - "computerName", - "data", - "eventCategory", - "eventID", - "eventType", - "recordNumber", - "reserved", - "reservedFlags", - "sid", - "sourceName", - "stringInserts", - "timeGenerated", - "timeWritten", +EVENT_PARTS = ( + "closingRecordNumber", + "computerName", + "data", + "eventCategory", + "eventID", + "eventType", + "recordNumber", + "reserved", + "reservedFlags", + "sid", + "sourceName", + "stringInserts", + "timeGenerated", + "timeWritten", ) EVENT_TYPES = { @@ -58,14 +56,17 @@ EVENT_TYPES = { } # keys time -TIME_PARTS = ("year", - "month", - "day", - "hour", - "minute", - "second", +TIME_PARTS = ( + "year", + "month", + "day", + "hour", + "minute", + "second", +) +TimeTuple = collections.namedtuple( + "TimeTuple", "year, month, day, hour, minute, second" ) -TimeTuple = collections.namedtuple("TimeTuple", "year, month, day, hour, minute, second") log = logging.getLogger(__name__) @@ -78,7 +79,7 @@ def __virtual__(): """ if not salt.utils.platform.is_windows(): - return False, "win_event: Most be on Windows" + return False, "win_event: Must be on Windows" if not IMPORT_STATUS: return False, "win_event: Missing PyWin32" return __virtualname__ @@ -93,8 +94,11 @@ def _to_bytes(data, encoding="utf-8", encode_keys=False): to. Args: + data (object): The string object to encode + encoding(str): The encoding type + encode_keys(bool): If false key strings will not be turned into bytes Returns: @@ -135,13 +139,16 @@ def _raw_time(time): Will make a pywintypes.datetime into a TimeTuple. Args: + time (ob): A datetime object Returns: TimeTuple: A TimeTuple """ - return TimeTuple._make((time.year, time.month, time.day, time.hour, time.minute, time.second)) + return TimeTuple( + time.year, time.month, time.day, time.hour, time.minute, time.second + ) def _make_event_dict(event): @@ -149,6 +156,7 @@ def _make_event_dict(event): Will make a PyEventLogRecord into a dictionary Args: + event (PyEventLogRecord): An event to convert to a dictionary Returns: @@ -158,7 +166,9 @@ def _make_event_dict(event): event_dict = {} for event_part in EVENT_PARTS: # get object value and add it to the event dict - event_dict[event_part] = getattr(event, event_part[0].upper() + event_part[1:], None) + event_dict[event_part] = getattr( + event, event_part[0].upper() + event_part[1:], None + ) # format items event_dict["eventID"] = winerror.HRESULT_CODE(event_dict["eventID"]) @@ -175,6 +185,7 @@ def _get_handle(log_name): Will try to open a PyHANDLE to the Event System Args: + log_name (str): The name of the log to open Returns: @@ -185,9 +196,9 @@ def _get_handle(log_name): # "log close" can fail if this is not done try: return win32evtlog.OpenEventLog(None, log_name) - except pywintypes.error: + except pywintypes.error as exc: raise FileNotFoundError( - "{0} log can not be found or access was denied!".format(log_name) + "Failed to open log: {}\nError: {}".format(log_name, exc.strerror) ) @@ -196,6 +207,7 @@ def _close_handle(handle): Will close the handle to the event log Args: + handle (PyHANDLE): The handle to the event log to close """ @@ -208,6 +220,7 @@ def _event_generator(log_name): Get all log events one by one. Events are not ordered Args: + log_name(str): The name of the log to retrieve Yields: @@ -230,11 +243,12 @@ def _event_generator(log_name): _close_handle(handle) -def _event_generator_sorted(log_name): +def _event_generator_with_time(log_name): """ Sorts the results of the event generator Args: + log_name (str): The name of the log to retrieve Yields: @@ -254,33 +268,48 @@ def _event_generator_sorted(log_name): def _event_generator_filter(log_name, all_requirements=True, **kwargs): """ - Will find events that meet the requirements in the filter + Will find events that meet the requirements in the filter. Can be any item + in the return for the event. + Args: + log_name (str): The name of the log to retrieve + all_requirements (bool): Should the results match all requirements. ``True`` matches all requirements. ``False`` matches any requirement. Kwargs: - Can be any item in the return for the event. Common kwargs are: + eventID (int): The event ID number + eventType (int): The event type number. Valid options and their corresponding meaning are: + - 0 : Success - 1 : Error - 2 : Warning - 4 : Information - 8 : Audit Success - 10 : Audit Failure + year (int): The year + month (int): The month + day (int): The day of the month + hour (int): The hour + minute (int): The minute + second (int): The second + eventCategory (int): The event category number + sid (sid): The SID of the user that created the event + sourceName (str): The name of the event source Yields: @@ -288,7 +317,7 @@ def _event_generator_filter(log_name, all_requirements=True, **kwargs): CLI Example: - .. code-block::python + .. code-block:: python # Return all events from the Security log with an ID of 1100 _event_generator_filter("Security", eventID=1100) @@ -300,7 +329,7 @@ def _event_generator_filter(log_name, all_requirements=True, **kwargs): _event_generator_filter("System", eventType=1, sourceName="Service Control Manager", data="netprofm") """ - for event, info in _event_generator_sorted(log_name): + for event, info in _event_generator_with_time(log_name): if all_requirements: # all keys need to match each other for key in kwargs: @@ -317,7 +346,7 @@ def _event_generator_filter(log_name, all_requirements=True, **kwargs): log.trace( "utf-8: Does %s == %s", repr(kwargs[key]), - repr(info[key].decode("utf-8")) + repr(info[key].decode("utf-8")), ) if kwargs[key] != info[key].decode("utf-8"): # try utf-16 and strip null bytes @@ -325,9 +354,11 @@ def _event_generator_filter(log_name, all_requirements=True, **kwargs): log.trace( "utf-16: Does %s == %s", repr(kwargs[key]), - repr(info[key].decode("utf-16").strip("\x00")) + repr(info[key].decode("utf-16").strip("\x00")), ) - if kwargs[key] != info[key].decode("utf-16").strip("\x00"): + if kwargs[key] != info[key].decode("utf-16").strip( + "\x00" + ): break except UnicodeDecodeError: log.trace("Failed to decode (utf-16): %s", info[key]) @@ -355,7 +386,7 @@ def _event_generator_filter(log_name, all_requirements=True, **kwargs): log.trace( "utf-8: Does %s == %s", repr(kwargs[key]), - repr(info[key].decode("utf-8")) + repr(info[key].decode("utf-8")), ) if kwargs[key] == info[key].decode("utf-8"): yield info @@ -366,7 +397,7 @@ def _event_generator_filter(log_name, all_requirements=True, **kwargs): log.trace( "utf-16: Does %s == %s", repr(kwargs[key]), - repr(info[key].decode("utf-16").strip("\x00")) + repr(info[key].decode("utf-16").strip("\x00")), ) if kwargs[key] == info[key].decode("utf-16").strip("\x00"): yield info @@ -388,6 +419,7 @@ def get(log_name): ``Applications`` log, can take a long time. Args: + log_name(str): The name of the log to retrieve. Returns @@ -395,7 +427,7 @@ def get(log_name): CLI Example: - .. code-block::bash + .. code-block:: bash salt '*' win_event.get Application """ @@ -420,11 +452,16 @@ def query(log_name, query_text=None, records=20, latest=True, raw=False): put spaces between comparison operators. For example: ``this >= that``. Args: + log_name (str): The name of the log to query + query_text (str): The filter to apply to the log + records (int): The number of records to return + latest (bool): ``True`` will return the newest events. ``False`` will return the oldest events. Default is ``True`` + raw (bool): ``True`` will return the raw xml results. ``False`` will return the xml converted to a dictionary. Default is ``False`` @@ -433,7 +470,7 @@ def query(log_name, query_text=None, records=20, latest=True, raw=False): CLI Example: - .. code-block::bash + .. code-block:: bash # Return the 20 most recent events from the Application log with an event ID of 22 salt '*' win_event.query Application "*[System[(EventID=22)]]" @@ -467,12 +504,7 @@ def query(log_name, query_text=None, records=20, latest=True, raw=False): if not latest: direction = win32evtlog.EvtQueryForwardDirection - results = win32evtlog.EvtQuery( - log_name, - direction, - query_text, - None - ) + results = win32evtlog.EvtQuery(log_name, direction, query_text, None) event_list = [] for evt in win32evtlog.EvtNext(results, records): @@ -485,68 +517,52 @@ def query(log_name, query_text=None, records=20, latest=True, raw=False): return event_list -def get_sorted(log_name): - """ - Make a list of events sorted by date. - - .. warning:: - Running this command on a log with thousands of events, such as the - ``Applications`` log, can take a long time. - - Args: - log_name (str): The name of the log to retrieve - - Returns: - dict: A dictionary of events - - CLI Example: - - .. code-block::bash - - # This command can take a long time - salt "*" win_event.get_sorted Application - """ - - event_info = {event_part: collections.defaultdict(list) for event_part in EVENT_PARTS + TIME_PARTS} - for event, info in _event_generator_sorted(log_name): - for part in info: - event_info[part][info.get(part)].append(event) - - return event_info - - def get_filtered(log_name, all_requirements=True, **kwargs): """ Will find events that match the fields and values specified in the kwargs. + Kwargs can be any item in the return for the event. .. warning:: Running this command on a log with thousands of events, such as the ``Applications`` log, can take a long time. Args: + log_name (str): The name of the log to retrieve + all_requirements (bool): ``True`` matches all requirements. ``False`` matches any requirement. Default is ``True`` Kwargs: - Can be any item in the return for the event. Common kwargs are: + eventID (int): The event ID number + eventType (int): The event type number. Valid options and their corresponding meaning are: + - 0 : Success - 1 : Error - 2 : Warning - 4 : Information - 8 : Audit Success - 10 : Audit Failure + year (int): The year + month (int): The month + day (int): The day of the month + hour (int): The hour + minute (int): The minute + second (int): The second + eventCategory (int): The event category number + sid (sid): The SID of the user that created the event + sourceName (str): The name of the event source Returns: @@ -554,7 +570,7 @@ def get_filtered(log_name, all_requirements=True, **kwargs): CLI Example: - .. code-block::bash + .. code-block:: bash # Return all events from the Security log with an ID of 1100 salt "*" win_event.get_filtered Security eventID=1100 @@ -581,7 +597,7 @@ def get_log_names(): CLI Example: - .. code-block::bash + .. code-block:: bash salt "*" win_event.get_log_names """ @@ -605,18 +621,26 @@ def add( Adds an event to the application event log. Args: + log_name (str): The name of the application or source + event_id (int): The event ID + event_category (int): The event category + event_type (str): The event category. Must be one of: + - Success - Error - Warning - Information - AuditSuccess - AuditFailure + event_strings (list): A list of strings + event_data (bytes): Event data. Strings will be converted to bytes + event_sid (sid): The SID for the event Raises: @@ -681,7 +705,7 @@ def add( ) -def clear_log(log_name): +def clear(log_name, backup=None): """ Clears the specified event log. @@ -689,17 +713,20 @@ def clear_log(log_name): A clear log event will be added to the log after it is cleared. Args: + log_name (str): The name of the log to clear + backup (str): Path to backup file + CLI Example: - .. code-block::bash + .. code-block:: bash - salt "*" win_event.clear_log Application + salt "*" win_event.clear Application """ handle = _get_handle(log_name) - win32evtlog.ClearEventLog(handle, log_name) + win32evtlog.ClearEventLog(handle, backup) _close_handle(handle) @@ -708,6 +735,7 @@ def count(log_name): Gets the number of events in the specified. Args: + log_name (str): The name of the log Returns: @@ -715,7 +743,7 @@ def count(log_name): CLI Example: - .. code-block::bash + .. code-block:: bash salt "*" win_event.count Application """ diff --git a/tests/pytests/functional/modules/test_win_event.py b/tests/pytests/functional/modules/test_win_event.py deleted file mode 100644 index 1ea26f8a886..00000000000 --- a/tests/pytests/functional/modules/test_win_event.py +++ /dev/null @@ -1,15 +0,0 @@ -import pytest - -pytestmark = [ - pytest.mark.windows_whitelisted, -] - - -@pytest.fixture(scope="module") -def win_event(modules): - return modules.win_event - - -def test_get(win_event): - events = win_event.get() - assert events == {} diff --git a/tests/pytests/unit/modules/test_win_event.py b/tests/pytests/unit/modules/test_win_event.py index 2bcd2a8b803..5177e7de644 100644 --- a/tests/pytests/unit/modules/test_win_event.py +++ b/tests/pytests/unit/modules/test_win_event.py @@ -1,43 +1,52 @@ import datetime import pytest + import salt.modules.win_event as win_event pytestmark = [ pytest.mark.windows_whitelisted, pytest.mark.skip_unless_on_windows, - pytest.mark.destructive_test, ] +@pytest.fixture(scope="function") +def application_events(): + # This deletes the contents of the Application event log + win_event.clear("Application") + win_event.add("Application", 2011, event_type="Information") + win_event.add("Application", 2011, event_type="Information") + win_event.add("Application", 2011, event_type="Information") + win_event.add("Application", 2011, event_type="Information") + win_event.add("Application", 2020, event_type="Warning") + win_event.add("Application", 2020, event_type="Warning") + yield + # This deletes the contents of the Application event log + win_event.clear("Application") + + def test__to_bytes_utf8(): - data = {'key1': 'item1', - 'key2': [1, 2, 'item2'], - 'key3': 45, - 45: str} + data = {"key1": "item1", "key2": [1, 2, "item2"], "key3": 45, 45: str} - new_data = win_event._to_bytes(data, 'utf-8', False) + new_data = win_event._to_bytes(data, "utf-8", False) - assert 'key1' in new_data + assert "key1" in new_data - assert new_data['key1'] == 'item1'.encode('utf-8') - assert new_data['key2'][2] == 'item2'.encode('utf-8') + assert new_data["key1"] == b"item1" + assert new_data["key2"][2] == b"item2" def test__to_bytes_cp1252(): - data = {'key1': 'item1', - 'key2': [1, 2, 'item2'], - 'key3': 45, - 45: str} + data = {"key1": "item1", "key2": [1, 2, "item2"], "key3": 45, 45: str} - new_data = win_event._to_bytes(data, 'CP1252', True) + new_data = win_event._to_bytes(data, "CP1252", True) - assert b'key1' in new_data - assert b'key2' in new_data - assert b'key3' in new_data + assert b"key1" in new_data + assert b"key2" in new_data + assert b"key3" in new_data - assert new_data['key1'.encode('CP1252')] == 'item1'.encode('CP1252') - assert new_data['key2'.encode('CP1252')][2] == 'item2'.encode('CP1252') + assert new_data["key1".encode("CP1252")] == "item1".encode("CP1252") + assert new_data["key2".encode("CP1252")][2] == "item2".encode("CP1252") def test__raw_time(): @@ -45,34 +54,109 @@ def test__raw_time(): assert raw_time == (2019, 7, 2, 10, 8, 19) -def test_count(): +@pytest.mark.destructive_test +def test_count(application_events): """ Test win_event.count """ - ret = win_event.count("System") - assert ret > 0 + ret = win_event.count("Application") + assert ret == 6 -def test_security(): - ret = win_event.get("Security") - print(len(ret)) - assert len(ret) > 0 +@pytest.mark.destructive_test +def test_get(application_events): + ret = win_event.get("Application") + assert len(ret) == 6 -def test_windows_powershell(): - ret = win_event.get("Windows PowerShell") - print(len(ret)) - assert len(ret) > 0 +@pytest.mark.destructive_test +def test_query(application_events): + ret = win_event.query("Application") + assert len(ret) == 6 -def test_operational(): - ret = win_event.get("Operational") - print(len(ret)) - assert len(ret) > 0 +@pytest.mark.destructive_test +def test_query_records(application_events): + ret = win_event.query("Application", records=3) + for item in ret: + assert isinstance(item, dict) + assert len(ret) == 3 -def test_query(): +@pytest.mark.destructive_test +def test_query_raw(application_events): + ret = win_event.query("Application", raw=True) + for item in ret: + assert isinstance(item, str) + assert len(ret) == 6 - ret = win_event.query("Microsoft-Windows-TerminalServices-LocalSessionManager/Operational", 22) - print(len(ret)) - assert len(ret) > 0 + +@pytest.mark.destructive_test +def test_query_level(application_events): + ret = win_event.query("Application", "*[System[(Level=3)]]") + assert len(ret) == 2 + + +@pytest.mark.destructive_test +def test_query_level_eventid(application_events): + ret = win_event.query( + "Application", "*[System[(Level=4 or Level=0) and (EventID=2011)]]" + ) + assert len(ret) == 4 + + +@pytest.mark.destructive_test +def test_query_last_hour(application_events): + ret = win_event.query( + "Application", "*[System[TimeCreated[timediff(@SystemTime) <= 3600000]]]" + ) + assert len(ret) == 6 + + +@pytest.mark.destructive_test +def test_get_filtered(application_events): + ret = win_event.get_filtered("Application") + assert len(ret) == 6 + + +@pytest.mark.destructive_test +def test_get_filtered_event_id(application_events): + ret = win_event.get_filtered("Application", eventID=2011) + assert len(ret) == 4 + + +@pytest.mark.destructive_test +def test_get_filtered_event_type(application_events): + ret = win_event.get_filtered("Application", eventType=2) + assert len(ret) == 2 + + +@pytest.mark.destructive_test +def test_get_filtered_year(application_events): + year = datetime.datetime.now().year + ret = win_event.get_filtered("Application", year=year) + assert len(ret) == 6 + + +@pytest.mark.destructive_test +def test_get_filtered_year_none(application_events): + year = 1999 + ret = win_event.get_filtered("Application", year=year) + assert len(ret) == 0 + + +@pytest.mark.destructive_test +def test_clear(application_events): + assert win_event.count("Application") == 6 + win_event.clear("Application") + assert win_event.count("Application") == 0 + + +@pytest.mark.destructive_test +def test_clear_backup(application_events, tmp_path): + assert win_event.count("Application") == 6 + backup_log = tmp_path / "test.bak" + assert not backup_log.exists() + win_event.clear("Application", str(backup_log)) + assert backup_log.exists() + assert win_event.count("Application") == 0 diff --git a/tests/unit/modules/test_win_event_viewer.py b/tests/unit/modules/test_win_event_viewer.py deleted file mode 100644 index 8d8e89f6818..00000000000 --- a/tests/unit/modules/test_win_event_viewer.py +++ /dev/null @@ -1,585 +0,0 @@ -# -*- coding: utf-8 -*- -''' -test of win_event_viewer -''' - -from __future__ import absolute_import - -# Import Salt Libs -import salt.utils.platform -import salt.modules.win_event_viewer as win_event_viewer - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.helpers import destructiveTest -from tests.support.mock import ( - patch, - NO_MOCK, - NO_MOCK_REASON -) - -# Import Third Party Libs -try: - import win32evtlog - import win32evtlogutil - import pywintypes -except ImportError: - pass - - -MAX_EVENT_LOOK_UP = 2000 - - -class MockTime(object): - def __init__(self, year=2000, month=1, day=1, hour=1, minute=1, second=1): - self.year = year - self.month = month - self.day = day - self.hour = hour - self.minute = minute - self.second = second - - -class MockEvent(object): - def __init__(self, - closing_record_number=0, - computer_name='PC', - data=bytes(), - event_category=117, - event_id=101, - event_type=4, - record_number=0, - reserved_data=4, - reserved_flags=0, - sid=None, - source_name=0, - string_inserts=("cat", "m"), - time_generated=None, - time_written=None): - - self.ClosingRecordNumber = closing_record_number - self.ComputerName = computer_name - self.Data = data - self.EventCategory = event_category - self.EventID = event_id - self.EventType = event_type - self.RecordNumber = record_number - # 'reserved' is a build in function - self.Reserved = reserved_data - self.ReservedFlags = reserved_flags - self.Sid = sid - self.SourceName = source_name - self.StringInserts = string_inserts - - if time_generated is None: - time_generated = MockTime() - self.TimeGenerated = time_generated - - if time_written is None: - time_written = MockTime() - self.TimeWritten = time_written - - -MOCK_EVENT_1 = MockEvent() -MOCK_EVENT_2 = MockEvent(event_category=404, time_generated=MockTime(2019, 4, 6, 2, 3, 12)) -MOCK_EVENT_3 = MockEvent(reserved_data=2, string_inserts=("fail...", "error...")) -MOCK_EVENT_4 = MockEvent(event_category=301, event_id=9) -MOCK_EVENT_5 = MockEvent(event_category=300, event_id=5) -MOCK_EVENT_6 = MockEvent(computer_name='sky', - time_generated=MockTime(1997, 8, 29, 2, 14, 0), - string_inserts=("'I am a live!'", "launching...")) - -EVENTS = ( - MOCK_EVENT_1, - MOCK_EVENT_2, - MOCK_EVENT_3, - MOCK_EVENT_4, - MOCK_EVENT_5, - MOCK_EVENT_6, -) - - -class MockHandler(object): - def __init__(self, events=None): - if events is None: - events = [] - self.events = events - self.generator = self.get_generator() - - def __len__(self): - return len(self.events) - - def get_generator(self): - for event in self.events: - yield [event] - - def read(self): - try: - return next(self.generator) - except StopIteration: - return [] - - -def mock_read_event_log(handler, flag, start): - assert isinstance(flag, int) and flag == abs(flag) - assert isinstance(start, int) and start == abs(start) - return handler.read() - - -def mock_get_number_of_event_log_records(handler): - return len(handler) - - -@skipIf(not salt.utils.platform.is_windows(), "Windows is required") -class WinEventViewerSimpleTestCase(TestCase, LoaderModuleMockMixin): - ''' - Test cases for salt.states.win_iis - ''' - - def setup_loader_modules(self): - return {win_event_viewer: {}} - - def test__str_to_bytes(self): - data = {'key1': 'item1', - 'key2': [1, 2, 'item2'], - 'key3': 45, - 45: str} - - new_data = win_event_viewer._change_str_to_bytes(data, 'utf-8', False) - - self.assertTrue('key1' in new_data) - - self.assertEqual(new_data['key1'], 'item1'.encode('utf-8')) - self.assertEqual(new_data['key2'][2], 'item2'.encode('utf-8')) - - def test_2__str_to_bytes(self): - data = {'key1': 'item1', - 'key2': [1, 2, 'item2'], - 'key3': 45, - 45: str} - - new_data = win_event_viewer._change_str_to_bytes(data, 'CP1252', True) - - self.assertTrue('key1'.encode('CP1252') in new_data) - self.assertTrue('key2'.encode('CP1252') in new_data) - self.assertTrue('key3'.encode('CP1252') in new_data) - - self.assertEqual(new_data['key1'.encode('CP1252')], 'item1'.encode('CP1252')) - self.assertEqual(new_data['key2'.encode('CP1252')][2], 'item2'.encode('CP1252')) - - def test__get_raw_time(self): - mock_time = MockTime(2019, 7, 2, 10, 8, 19) - raw_time = win_event_viewer._get_raw_time(mock_time) - self.assertEqual(raw_time, (2019, 7, 2, 10, 8, 19)) - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -@skipIf(not salt.utils.platform.is_windows(), "Windows is required") -class WinEventViewerMockTestCase(TestCase, LoaderModuleMockMixin): - ''' - Test cases for salt.states.win_iis - ''' - - def setup_loader_modules(self): - return {win_event_viewer: {}} - - def test__get_event_handler(self): - with patch.object(win32evtlog, 'OpenEventLog', return_value=MockHandler()) as open_event_log: - ret = win_event_viewer._get_event_handler("System", None) - open_event_log.called_once_with("System", None) - self.assertIsInstance(ret, MockHandler) - - def test_fail__get_event_handler(self): - with patch.object(win32evtlog, 'OpenEventLog', side_effect=pywintypes.error()) as open_event_log: - open_event_log.called_once_with("System", None) - self.assertRaises(FileNotFoundError, win_event_viewer._get_event_handler, "System") - - @staticmethod - def test__close_event_handler(): - with patch.object(win32evtlog, 'CloseEventLog', return_value=None) as close_event_log: - handler = MockHandler() - win_event_viewer._close_event_handler(MockHandler()) - close_event_log.called_once_with(handler) - - def test_get_event_generator(self): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'ReadEventLog', wraps=mock_read_event_log) as ReadEventLog: - with patch.object(win32evtlog, - 'GetNumberOfEventLogRecords', - wraps=mock_get_number_of_event_log_records) as GetNumberOfEventLogRecords: - ret = tuple(win_event_viewer.get_event_generator("System", target_computer=None, raw=False)) - - self.assertIsInstance(ret, tuple) - self.assertTrue(all([isinstance(event, dict)for event in ret])) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - self.assertEqual(ReadEventLog.call_count, len(handler)) - self.assertEqual(GetNumberOfEventLogRecords.call_count, len(handler) + 1) - - def test_raw_get_event_generator(self): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'ReadEventLog', wraps=mock_read_event_log) as ReadEventLog: - with patch.object(win32evtlog, - 'GetNumberOfEventLogRecords', - wraps=mock_get_number_of_event_log_records) as GetNumberOfEventLogRecords: - ret = tuple(win_event_viewer.get_event_generator("System", target_computer=None, raw=True)) - - self.assertIsInstance(ret, tuple) - self.assertTrue(all([isinstance(event, MockEvent) for event in ret])) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - self.assertEqual(ReadEventLog.call_count, len(handler)) - self.assertEqual(GetNumberOfEventLogRecords.call_count, len(handler) + 1) - - def test_get_events(self): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'ReadEventLog', wraps=mock_read_event_log) as ReadEventLog: - with patch.object(win32evtlog, - 'GetNumberOfEventLogRecords', - wraps=mock_get_number_of_event_log_records) as GetNumberOfEventLogRecords: - ret = win_event_viewer.get_events("System", target_computer=None, raw=False) - - self.assertIsInstance(ret, tuple) - self.assertTrue(all([isinstance(event, dict)for event in ret])) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - self.assertEqual(ReadEventLog.call_count, len(handler)) - self.assertEqual(GetNumberOfEventLogRecords.call_count, len(handler) + 1) - - def test_raw_get_events(self): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'ReadEventLog', wraps=mock_read_event_log) as ReadEventLog: - with patch.object(win32evtlog, - 'GetNumberOfEventLogRecords', - wraps=mock_get_number_of_event_log_records) as GetNumberOfEventLogRecords: - ret = win_event_viewer.get_events("System", target_computer=None, raw=True) - - self.assertIsInstance(ret, tuple) - self.assertTrue(all([isinstance(event, MockEvent) for event in ret])) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - self.assertEqual(ReadEventLog.call_count, len(handler)) - self.assertEqual(GetNumberOfEventLogRecords.call_count, len(handler) + 1) - - def test_get_event_sorted_by_info_generator(self): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'ReadEventLog', wraps=mock_read_event_log) as ReadEventLog: - with patch.object(win32evtlog, - 'GetNumberOfEventLogRecords', - wraps=mock_get_number_of_event_log_records) as GetNumberOfEventLogRecords: - ret = tuple(win_event_viewer.get_event_sorted_by_info_generator("System", target_computer=None)) - - self.assertIsInstance(ret, tuple) - self.assertTrue(all([isinstance(event_info, tuple) for event_info in ret])) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - self.assertEqual(ReadEventLog.call_count, len(handler)) - self.assertEqual(GetNumberOfEventLogRecords.call_count, len(handler) + 1) - - self.assertEqual(ret[1][1]['eventCategory'], 404) - self.assertEqual(ret[2][1]['stringInserts'], (b'fail...', b'error...')) - self.assertEqual(ret[4][1]['eventID'], 5) - self.assertEqual(ret[5][1]['computerName'], b'sky') - self.assertEqual(ret[5][1]['timeGenerated'], (1997, 8, 29, 2, 14, 0)) - - def test_get_events_sorted_by_info(self): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'ReadEventLog', wraps=mock_read_event_log) as ReadEventLog: - with patch.object(win32evtlog, - 'GetNumberOfEventLogRecords', - wraps=mock_get_number_of_event_log_records) as GetNumberOfEventLogRecords: - ret = win_event_viewer.get_events_sorted_by_info("System", target_computer=None) - - self.assertIsInstance(ret, dict) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - self.assertEqual(ReadEventLog.call_count, len(handler)) - self.assertEqual(GetNumberOfEventLogRecords.call_count, len(handler) + 1) - - self.assertEqual(ret['eventID'][5], [{'closingRecordNumber': 0, - 'computerName': b'PC', - 'data': b'', - 'eventCategory': 300, - 'eventID': 5, - 'eventType': 4, - 'recordNumber': 0, - 'reserved': 4, - 'reservedFlags': 0, - 'sid': None, - 'sourceName': 0, - 'stringInserts': (b'cat', b'm'), - 'timeGenerated': (2000, 1, 1, 1, 1, 1), - 'timeWritten': (2000, 1, 1, 1, 1, 1)}]) - - self.assertEqual(ret['computerName'][b'sky'], - [{'closingRecordNumber': 0, - 'computerName': b'sky', - 'data': b'', - 'eventCategory': 117, - 'eventID': 101, - 'eventType': 4, - 'recordNumber': 0, - 'reserved': 4, - 'reservedFlags': 0, - 'sid': None, - 'sourceName': 0, - 'stringInserts': (b"'I am a live!'", b'launching...'), - 'timeGenerated': (1997, 8, 29, 2, 14, 0), - 'timeWritten': (2000, 1, 1, 1, 1, 1)}]) - - def test_get_event_filter_generator(self): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'ReadEventLog', wraps=mock_read_event_log) as ReadEventLog: - with patch.object(win32evtlog, - 'GetNumberOfEventLogRecords', - wraps=mock_get_number_of_event_log_records) as GetNumberOfEventLogRecords: - ret = tuple(win_event_viewer.get_event_filter_generator("System", - target_computer=None, - all_requirements=True, - eventID=101, - sid=None)) - self.assertIsInstance(ret, tuple) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - self.assertEqual(ReadEventLog.call_count, len(handler)) - self.assertEqual(GetNumberOfEventLogRecords.call_count, len(handler) + 1) - - self.assertEqual(len(ret), 4) - - def test_all_get_event_filter_generator(self): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'ReadEventLog', wraps=mock_read_event_log) as ReadEventLog: - with patch.object(win32evtlog, - 'GetNumberOfEventLogRecords', - wraps=mock_get_number_of_event_log_records) as GetNumberOfEventLogRecords: - ret = tuple(win_event_viewer.get_event_filter_generator("System", - target_computer=None, - all_requirements=False, - eventID=101, - sid=None)) - self.assertIsInstance(ret, tuple) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - self.assertEqual(ReadEventLog.call_count, len(handler)) - self.assertEqual(GetNumberOfEventLogRecords.call_count, len(handler) + 1) - - self.assertEqual(len(ret), 6) - - def test_get_events_filter(self): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'ReadEventLog', wraps=mock_read_event_log) as ReadEventLog: - with patch.object(win32evtlog, - 'GetNumberOfEventLogRecords', - wraps=mock_get_number_of_event_log_records) as GetNumberOfEventLogRecords: - ret = tuple(win_event_viewer.get_events_filter("System", - target_computer=None, - all_requirements=True, - eventID=101, - sid=None)) - self.assertIsInstance(ret, tuple) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - self.assertEqual(ReadEventLog.call_count, len(handler)) - self.assertEqual(GetNumberOfEventLogRecords.call_count, len(handler) + 1) - - self.assertEqual(len(ret), 4) - - def test_all_get_events_filter(self): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'ReadEventLog', wraps=mock_read_event_log) as ReadEventLog: - with patch.object(win32evtlog, - 'GetNumberOfEventLogRecords', - wraps=mock_get_number_of_event_log_records) as GetNumberOfEventLogRecords: - ret = tuple(win_event_viewer.get_events_filter("System", - target_computer=None, - all_requirements=False, - eventID=101, - sid=None)) - self.assertIsInstance(ret, tuple) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - self.assertEqual(ReadEventLog.call_count, len(handler)) - self.assertEqual(GetNumberOfEventLogRecords.call_count, len(handler) + 1) - - self.assertEqual(len(ret), 6) - - @staticmethod - def test_clear_log(): - with patch.object(win32evtlogutil, 'ReportEvent', return_value=None) as ReportEvent: - win_event_viewer.log_event('salt', 117, eventType=1) - ReportEvent.assert_called_once_with('salt', 117, eventType=1) - - @staticmethod - def test_log_event(): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'ClearEventLog', return_value=None) as ClearEventLog: - win_event_viewer.clear_log('System', target_computer=None) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - ClearEventLog.assert_called_once_with(handler, 'System') - - @staticmethod - def test_get_number_of_events(): - handler = MockHandler(EVENTS) - - with patch.object(win_event_viewer, "_get_event_handler", return_value=handler) as _get_event_handler: - with patch.object(win_event_viewer, "_close_event_handler", return_value=None) as _close_event_handler: - with patch.object(win32evtlog, 'GetNumberOfEventLogRecords', - return_value=None) as GetNumberOfEventLogRecords: - win_event_viewer.get_number_of_events('System', target_computer=None) - - _get_event_handler.assert_called_once_with('System', None) - _close_event_handler.assert_called_once_with(handler) - - GetNumberOfEventLogRecords.assert_called_once_with(handler) - - -@destructiveTest -@skipIf(not salt.utils.platform.is_windows(), "Windows is required") -class WinEventViewerDestructiveTestCase(TestCase, LoaderModuleMockMixin): - ''' - Test cases for salt.states.win_iis - All test are destructive because their actions will add events to the security log. - Even if the method is only reading a log or getting the size of a log. - DONT!!!!! add a "clear log" test for security reasons!!!!! - ''' - - def setup_loader_modules(self): - return {win_event_viewer: {}} - - def test_get_event_generator(self): - for number, event in enumerate(win_event_viewer.get_event_generator("System", - target_computer=None, - raw=False)): - - self.assertIsInstance(event, dict) - for event_part in win_event_viewer.EVENT_PARTS: - self.assertTrue(event_part in event) - - if number == MAX_EVENT_LOOK_UP: - break - - def test_raw_get_event_generator(self): - # TODO: type(event) does not work as of 7/3/2019. - # its a upstream problem and python will crash if you call it - # when this upstream problem is no more add a self.assertIsInstance pls - - for number, event in enumerate(win_event_viewer.get_event_generator("System", - target_computer=None, - raw=True)): - - for event_part in win_event_viewer.EVENT_PARTS: - self.assertTrue(hasattr(event, event_part[0].upper() + event_part[1:])) - - if number == MAX_EVENT_LOOK_UP: - break - - def test_get_event_sorted_by_info_generator(self): - for number, ret in enumerate(win_event_viewer.get_event_sorted_by_info_generator("Application", - target_computer=None)): - event, event_info = ret[0], ret[1] - for event_part in win_event_viewer.EVENT_PARTS: - self.assertEqual(event[event_part], event_info[event_part]) - - for event_part in win_event_viewer.TIME_PARTS: - self.assertTrue(event_part in event_info) - - if number == MAX_EVENT_LOOK_UP: - break - - def test_get_event_filter_generator(self): - for number, event in enumerate(win_event_viewer.get_event_filter_generator("System", - target_computer=None, - all_requirements=True, - hour=3, - eventID=37)): - - self.assertEqual(event['timeGenerated'][3], 3) - self.assertEqual(event['eventID'], 37) - - if number == MAX_EVENT_LOOK_UP: - break - - def test_all_get_event_filter_generator(self): - for number, event in enumerate(win_event_viewer.get_event_filter_generator("System", - target_computer=None, - all_requirements=False, - hour=3, - eventID=37)): - - self.assertTrue(event['timeGenerated'][3] == 3 or event['eventID'] == 37) - - if number == MAX_EVENT_LOOK_UP: - break - - @staticmethod - def test_log_event(): - ''' - Does not check for event because - * the log can be slow to update - * the log can be cleared at anytime - * I dont want to add a flaky test - ''' - - win_event_viewer.log_event('salt_test', event_id=117) - - def test_get_number_of_events(self): - event_count = win_event_viewer.get_number_of_events("Application", target_computer=None) - self.assertIsInstance(event_count, int) - self.assertEqual(event_count, abs(event_count))