ZoomEventCollector
This is the Zoom event collector integration for Cortex XSIAM.
Analytics & SIEM · Zoom
Details
| ID | ZoomEventCollector |
|---|---|
| Provider | Zoom |
| Category | Analytics & SIEM |
| From Version | 6.8.0 |
| Docker Image | demisto/auth-utils:1.0.0.10133006 |
| Supported Modules | Agentix XSIAM EDR Cortex Cloud Cloud Runtime Security |
README
This is the Zoom event collector integration for Cortex XSIAM.
This integration was integrated and tested with version 2.0.0 of Zoom
This is the default integration for this content pack when configured by the Data Onboarder in Cortex XSIAM.
Configure Zoom Event Collector in Cortex
| Parameter | Required |
|---|---|
| Server URL (e.g., ‘https://api.zoom.us/v2/’) | True |
| Account ID (OAuth) | True |
| Client ID (OAuth) | True |
| Client Secret (OAuth) | True |
| First fetch time (<number> <time unit>, e.g., 12 hours, 7 days) - within the last six months | False |
| Trust any certificate (not secure) | False |
| Use system proxy settings | False |
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
zoom-get-events
Gets events from Zoom.
Base Command
zoom-get-events
Input
| Argument Name | Description | Required |
|---|---|---|
| should_push_events | If true, the command will create events, otherwise it will only display them. Possible values are: true, false. Default is false. | Required |
| limit | Maximum results to return. The maximum is 300. | Optional |
Context Output
There is no context output for this command.
Command Example
!zoom-get-events should_push_events=true limit=1
Human Readable Output
operationlogs Events
| action | category_type | operation_detail | operator | time |
|---|---|---|---|---|
| Delete | User | Delete User example@example.com | example@example.com | 2023-01-16T09:51:59Z |
activities Events
| client_type | ip_address | time | type | version | |
|---|---|---|---|---|---|
| Browser | example@example.com | 8.8.8.8 | 2023-01-19T14:44:23Z | Sign in | - |
Rate Limits
To prevent abuse and ensure service stability, all API requests are rate limited. Rate limits specify the maximum number of API calls that can be made in a minute period. The exact number of calls that your application can make per minute varies based on company plan.
For more information, please refer to the Zoom API documentation on Rate limits by account type.
Configuration parameters
url— Server URL (e.g., 'https://api.zoom.us/v2/') (required)account_id— Account ID (OAuth) (required)credentials— Client ID (OAuth) (required)first_fetch— First fetch time (<number> <time unit>, e.g., 12 hours, 7 days) - within the last six monthsinsecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (1)
-
zoom-get-eventsGets events from Zoom.
import json from datetime import UTC, datetime import demistomock as demisto # noqa: F401 import pytest from CommonServerPython import DemistoException from freezegun import freeze_time BASE_URL = "https://api.zoom.us/v2/" MAX_RECORDS_PER_PAGE = 300 def util_load_json(path): with open(path, encoding="utf-8") as f: return json.loads(f.read()) @pytest.mark.parametrize( "first_fetch_time, expected_result, expect_error", [ ("3 days", "First fetch timestamp: 2023-03-29 00:00:00", False), ("6 months", "First fetch timestamp: 2022-10-02 00:00:00", False), ( "7 months", "The First fetch time should fall within the last six months." " Please provide a valid date within the last six months.", True, ), ], ) @freeze_time("2023-04-01 00:00:00") def test_main(first_fetch_time, expected_result, expect_error, mocker): """ Given: - 'first_fetch_time': A string representing the first fetch time to be used in the command. - 'expected_result': The expected result of the command function. - 'expect_error': A boolean indicating whether an error is expected to be raised. When: - Running the 'main' function. Then: If 'expect_error' is True: Checks that a 'DemistoException' is raised with a message that matches the 'expected_result'. If 'expect_error' is False: Checks that the output of the command function matches the 'expected_result'. Checks that the 'demisto.results' function was called once with the value 'ok'. Checks that the 'demisto.info' function was called with the appropriate output. """ from ZoomEventCollector import main params = { "url": BASE_URL, "credentials": {"identifier": "test_id", "password": "test_secret"}, "account_id": "test_account", "first_fetch": first_fetch_time, } args = {} mocker.patch.object(demisto, "params", return_value=params) mocker.patch.object(demisto, "args", return_value=args) mocker.patch.object(demisto, "command", return_value="test-module") mocker.patch.object(demisto, "results") mocker.patch("ZoomEventCollector.Client.get_oauth_token", return_value=("token", None)) mocker.patch( "ZoomEventCollector.Client.search_events", return_value={"from": "2023-03-31", "to": "2023-04-01", "page_size": 300, "next_page_token": "", "operation_logs": []}, ) mocker_info = mocker.patch.object(demisto, "info") mock_return_error = mocker.patch("ZoomEventCollector.return_error") if expect_error: main() assert mock_return_error.called mock_return_error.assert_called_with(f"Failed to execute test-module command.\nError:\n{expected_result!s}") else: main() output = mocker_info.call_args[0][0] assert output == expected_result assert demisto.results.call_count == 1 assert demisto.results.call_args[0][0] == "ok" assert mock_return_error.call_count == 0 @freeze_time("2023-03-30 00:00:00") def test_fetch_events(mocker): """ Tests the fetch_events function Given: - first_fetch_time When: - Running the 'fetch_events' function. Then: - Validates that the function generates the correct API requests with the expected parameters. - Validates that the function returns the expected events and next_run timestamps. """ from ZoomEventCollector import Client, fetch_events first_fetch_time = datetime(2023, 3, 1).replace(tzinfo=UTC) http_request_mocker = mocker.patch.object( Client, "error_handled_http_request", side_effect=[ util_load_json("test_data/fetch_events_operationlogs.json").get("fetch_events_month_before"), util_load_json("test_data/fetch_events_operationlogs.json").get("fetch_events"), util_load_json("test_data/fetch_events_activities.json").get("fetch_events_month_before"), util_load_json("test_data/fetch_events_activities.json").get("fetch_events"), ], ) mocker.patch("ZoomEventCollector.Client.get_oauth_token", return_value=("token", None)) mocker.patch.object(Client, "generate_oauth_token") client = Client(base_url=BASE_URL) next_run, events = fetch_events(client, last_run={}, first_fetch_time=datetime(2023, 2, 1).replace(tzinfo=UTC)) mock_events = util_load_json("test_data/zoom_fetch_events.json") assert http_request_mocker.call_args_list[0][1].get("params") == {"page_size": 300, "from": "2023-02-01", "to": "2023-03-01"} assert http_request_mocker.call_args_list[1][1].get("params") == {"page_size": 300, "from": "2023-03-02", "to": "2023-03-30"} assert http_request_mocker.call_args_list[2][1].get("params") == {"page_size": 300, "from": "2023-02-01", "to": "2023-03-01"} assert http_request_mocker.call_args_list[3][1].get("params") == {"page_size": 300, "from": "2023-03-02", "to": "2023-03-30"} assert http_request_mocker.call_args_list[0][1].get("url_suffix") == "report/operationlogs" assert http_request_mocker.call_args_list[2][1].get("url_suffix") == "report/activities" assert events == mock_events assert next_run == {"activities": "2023-03-29T11:38:50Z", "operationlogs": "2023-03-21T08:22:09Z"} # assert no new results when given the last_run: mocker.patch.object( Client, "error_handled_http_request", side_effect=[ util_load_json("test_data/fetch_events_operationlogs.json").get("fetch_events"), util_load_json("test_data/fetch_events_activities.json").get("fetch_events"), ], ) next_run, events = fetch_events( client, last_run={"activities": "2023-03-29T11:38:50Z", "operationlogs": "2023-03-21T08:22:09Z"}, first_fetch_time=first_fetch_time, ) assert events == [] assert next_run == {"activities": "2023-03-29T11:38:50Z", "operationlogs": "2023-03-21T08:22:09Z"} @freeze_time("2023-03-30 00:00:00") def test_fetch_events_with_last_run(mocker): """ Tests the fetch_events function Given: - last run object - the first request returns next_page_token *NOTE* The test simulates a situation where next token is returned, in reality there is no reason why next_token would be returned if it is not over 300 results When: - Running the 'fetch_events' function. Then: - Ensure the events are returned as expected and the pagination is working as expected """ from ZoomEventCollector import Client, fetch_events first_fetch_time = datetime(2023, 3, 1).replace(tzinfo=UTC) http_request_mocker = mocker.patch.object( Client, "error_handled_http_request", side_effect=[ util_load_json("test_data/fetch_events_operationlogs.json").get("fetch_events_with_token"), util_load_json("test_data/fetch_events_operationlogs.json").get("fetch_events_with_token_next"), util_load_json("test_data/fetch_events_activities.json").get("fetch_events_with_token"), util_load_json("test_data/fetch_events_activities.json").get("fetch_events_with_token_next"), ], ) mocker.patch("ZoomEventCollector.Client.get_oauth_token", return_value=("token", None)) mocker.patch.object(Client, "generate_oauth_token") client = Client(base_url=BASE_URL) next_run, events = fetch_events( client, last_run={"activities": "2023-03-21T08:14:27Z", "operationlogs": "2023-03-20T16:37:58Z"}, first_fetch_time=first_fetch_time, ) mock_events = util_load_json("test_data/zoom_fetch_events_with_token.json") assert http_request_mocker.call_args_list[0][1].get("params") == {"page_size": 300, "from": "2023-03-20", "to": "2023-03-30"} assert http_request_mocker.call_args_list[1][1].get("params") == { "page_size": 300, "from": "2023-03-20", "to": "2023-03-30", "next_page_token": "next_token_operationlogs", } assert http_request_mocker.call_args_list[2][1].get("params") == {"page_size": 300, "from": "2023-03-21", "to": "2023-03-30"} assert http_request_mocker.call_args_list[3][1].get("params") == { "page_size": 300, "from": "2023-03-21", "to": "2023-03-30", "next_page_token": "next_token_activities", } assert http_request_mocker.call_args_list[0][1].get("url_suffix") == "report/operationlogs" assert http_request_mocker.call_args_list[2][1].get("url_suffix") == "report/activities" assert events == mock_events assert next_run == {"activities": "2023-03-29T11:38:50Z", "operationlogs": "2023-03-21T08:22:09Z"} @freeze_time("2023-03-30 00:00:00") def test_get_events_command(mocker): """ Tests the get-events command function. Given: - NetBox client and limit of events to fetch When: - Running the 'get_events' function. Then: - Checks that the events returned by the 'get_events' function match the expected events. - Checks that an exception is raised when the limit exceeds the maximum number of records per page. """ from ZoomEventCollector import Client, get_events http_request_mocker = mocker.patch.object( Client, "error_handled_http_request", side_effect=[ util_load_json("test_data/get_events_operationlogs.json"), util_load_json("test_data/get_events_activities.json"), ], ) mocker.patch("ZoomEventCollector.Client.get_oauth_token", return_value=("token", None)) mocker.patch.object(Client, "generate_oauth_token") client = Client(base_url=BASE_URL) events, results = get_events(client, limit=2, first_fetch_time=datetime(2023, 3, 1).replace(tzinfo=UTC)) mock_events = util_load_json("test_data/zoom_get_events.json") assert http_request_mocker.call_args_list[0][1].get("params") == {"page_size": 2, "from": "2023-03-01", "to": "2023-03-30"} assert http_request_mocker.call_args_list[1][1].get("params") == {"page_size": 2, "from": "2023-03-01", "to": "2023-03-30"} assert http_request_mocker.call_args_list[0][1].get("url_suffix") == "report/operationlogs" assert http_request_mocker.call_args_list[1][1].get("url_suffix") == "report/activities" assert events == mock_events # Test limit > MAX_RECORDS_PER_PAGE with pytest.raises(DemistoException) as e: get_events(client, limit=MAX_RECORDS_PER_PAGE + 1, first_fetch_time=datetime(2023, 3, 1).replace(tzinfo=UTC)) assert ( e.value.message == f"The requested limit ({MAX_RECORDS_PER_PAGE + 1}) exceeds the maximum number of " f"records per page ({MAX_RECORDS_PER_PAGE}). Please reduce the limit and try again." ) @pytest.mark.parametrize( "input_date, expected_output", [ (datetime(2021, 12, 3), datetime(2022, 1, 3)), (datetime(2022, 1, 31), datetime(2022, 2, 28)), (datetime(2022, 2, 28), datetime(2022, 3, 28)), (datetime(2020, 2, 29), datetime(2020, 3, 29)), ], ) def test_get_next_month(input_date: datetime, expected_output: datetime): """ Given - input_date: A datetime object representing the input date. expected_output: A datetime object representing the date of the next month on the same day. When - The function get_next_month is called with the input_date. Then - Validate that the output of get_next_month is equal to expected_output. """ from ZoomEventCollector import get_next_month assert get_next_month(input_date) == expected_output