CiscoWebexEventCollector
Cisco Webex Event Collector fetches Events and Admin Audit Events and Security Audit Events.
Analytics & SIEM · Cisco Webex Teams
Details
| ID | CiscoWebexEventCollector |
|---|---|
| Provider | Cisco Systems |
| Category | Analytics & SIEM |
| From Version | 6.12.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Cisco Webex Event Collector fetches Events and Admin Audit Events and Security Audit Events.
This integration was integrated and tested with version 1 of CiscoWebex API.
Configure Cisco Webex Event Collector on Cortex XSOAR
- Navigate to Settings > Automation & Feed Integrations.
- Search for Cisco Webex Event Collector.
-
Click Add instance to create and configure a new integration instance.
Parameter Description Required URL True Admin APP Client ID Admin APP Client ID and Secret. True Admin APP Client Secret True Admin APP Redirect URI True Admin Org Id True Compliance Officer Client ID Compliance Officer Client ID and Secret. True Compliance Officer Client Secret True Compliance Officer Redirect URI True Trust any certificate (not secure) False Use system proxy settings False Fetch security audit events To fetch security audit events, the Pro Pack must be installed on the Webex instance, and Allow user authentication data must be enabled. See the note below. False Maximum number of events per fetch False
4.
1. Run the cisco-webex-oauth-start command with the user argument - you will be prompted to sign in to Cisco Webex with your username and password. (make sure you sign in with the same user as you defined in the user argument admin or compliance officer). You will then be redirected to the redirect URI you defined in the application. The URL will contain a query parameter called code. The value of this query parameter will be used in the next command.
2. Run the cisco-webex-oauth-complete command with the user and code arguments The user argument should be set to the same value as in the previous command (admin or compliance officer). The code argument should be set to the value returned in the code query parameter from the previous command.
3. Run the cisco-webex-oauth-test command with the user argument. The user argument should be set to the same value as in the previous command (admin or compliance officer) to ensure connectivity to Cisco Webex.
Note: To fetch security audit events, the Pro Pack must be installed on the Webex instance. Additionally, the Allow user authentication data setting must be enabled:
Go to Management > Organization Settings.
In the User authentication data section, toggle Allow user authentication data ON.
For more details, refer to the [official documentation.](https://help.webex.com/en-us/article/pf66vg/Log-and-analyze-user-sign-ins-and-sign-outs)
Commands
You can execute these commands from the Cortex XSOAR 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.
cisco-webex-oauth-start
Use this command to start the authorization process. In order to authorize the instance, first run the command, and complete the process in the URL that is returned. You will then be redirected to the callback URL where you will copy the authorization code found in the query parameter code, and paste that value in the command !cisco-webex-oauth-complete as an argument to finish the process.
Base Command
cisco-webex-oauth-start
Input
| Argument Name | Description | Required |
|---|---|---|
| user | The user to start authorization. Possible values are: admin, compliance_officer. | Required |
Context Output
There is no context output for this command.
cisco-webex-oauth-complete
Use this command to complete the authorization process. After copying the authorization code found in the query parameter code of the callback URL, paste the value in the command as an argument to finish the process.
Base Command
cisco-webex-oauth-complete
Input
| Argument Name | Description | Required |
|---|---|---|
| user | The user to complete authorization. Possible values are: admin, compliance_officer. | Required |
| code | The authorization code retrieved from the callback URL according to the documentation. | Required |
Context Output
There is no context output for this command.
cisco-webex-oauth-test
Use this command to complete the authorization process. After copying the authorization code found in the query parameter code of the callback URL, paste the value in the command as an argument to finish the process.
Base Command
cisco-webex-oauth-test
Input
| Argument Name | Description | Required |
|---|---|---|
| user | The user to complete authorization. Possible values are: admin, compliance_officer. | Required |
Context Output
There is no context output for this command.
cisco-webex-get-admin-audit-events
Gets admin audit events from Cisco Webex.
Base Command
cisco-webex-get-admin-audit-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 number of events to return. | Optional |
| since_datetime | Date in ISO format (2023-10-01T20:33:22.123Z) to return events from. | Optional |
Human Readable Output
Admin Audit Events
| _time | actorId | actorOrgId | created | data | id | source_log_type |
|---|---|---|---|---|---|---|
| 2023-11-02T09:33:26.408Z | 444444 | 222222 | 2023-11-02T09:33:26.408Z | actorOrgName: panw targetName: panw operationType: CREATE eventDescription: An org setting was created or updated. actorName: admin@example.com actorEmail: admin@example.com settingKey: release_migration settingName: release_migration settingValue: “MIGRATED” trackingId: 111111 previousValue: Null targetType: ORG targetId: 222222 actorUserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/2.2.2.2 Safari/537.36 eventCategory: ORG_SETTINGS actorIp: 1.1.1.1 targetOrgId: 222222 actionText: admin@example.com has modified the value of setting release_migration for ORG “panw”. New value = “MIGRATED”, Previous value = Null. entityType: ORG targetOrgName: panw |
333333 | Admin Audit Events |
Context Output
There is no context output for this command.
cisco-webex-get-security-audit-events
Gets security audit events from Cisco Webex.
Base Command
cisco-webex-get-security-audit-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 number of events to return. | Optional |
| since_datetime | Date in ISO format (2023-10-01T20:33:22.123Z) to return events from. | Optional |
Human Readable Output
Security Audit Events
| _time | actorId | actorOrgId | created | data | id | source_log_type |
|---|---|---|---|---|---|---|
| 2023-12-19T07:01:26.486Z | 444444 | 222222 | 2023-12-19T07:01:26.486Z | actorOrgName: panw eventDescription: A user attempted logging in actorName: admin@example.com actorEmail: admin@example.com authenticationMethod: Non-Interactive trackingId: 123456 eventStatus: SUCCESS actorOauthClient: 111111 actorUserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 eventCategory: LOGINS actorIp: 1.1.1.1 actorClientName: Developer Portal actionText: admin@example.com attempted logging into panw using client (Developer Portal) and Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36. Login status: SUCCESS. failedReason: |
333333 | Security Audit Events |
Context Output
There is no context output for this command.
cisco-webex-get-compliance-officer-events
Gets events from Cisco Webex.
Base Command
cisco-webex-get-compliance-officer-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 number of events to return. | Optional |
| since_datetime | Date in ISO format (2023-10-01T20:33:22.123Z) to return events from. | Optional |
Human Readable Output
Events
| _time | actorId | created | data | id | resource | source_log_type | type |
|---|---|---|---|---|---|---|---|
| 2023-11-05T13:33:46.417Z | 222222 | 2023-11-05T13:33:46.417Z | id: 333333 roomId: 444444 roomType: group personId: 222222 personEmail: jsmith@example.com personDisplayName: Kfir Testman personOrgId: 555555 isModerator: false isMonitor: false isRoomHidden: false created: 2023-11-05T13:33:46.417Z |
111111 | memberships | Events | created |
Context Output
There is no context output for this command.
Configuration parameters
base_url— URL (required)admin_credentials— Admin APP Client ID (required)admin_app_redirect_uri— Admin APP Redirect URI (required)admin_org_id— Admin Org Id (required)compliance_officer_credentials— Compliance Officer Client ID (required)compliance_officer_redirect_uri— Compliance Officer Redirect URI (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settingsfetch_security_audit_events— Fetch security audit eventsmax_fetch— Maximum number of events per fetch
Commands (6)
-
cisco-webex-get-admin-audit-eventsGets admin audit events from Cisco Webex.
-
cisco-webex-get-compliance-officer-eventsGets events from Cisco Webex.
-
cisco-webex-get-security-audit-eventsGets security audit events from Cisco Webex.
-
cisco-webex-oauth-completeUse this command to complete the authorization process. After copying the authorization code found in the query parameter `code` of the callback URL, paste the value in the command as an argument to finish the process.
-
cisco-webex-oauth-startUse this command to start the authorization process. In order to authorize the instance, first run the command, and complete the process in the URL that is returned. You will then be redirected to the callback URL where you will copy the authorization code found in the query parameter `code`, and paste that value in the command `!cisco-webex-oauth-complete` as an argument to finish the process.
-
cisco-webex-oauth-testUse this command to complete the authorization process. After copying the authorization code found in the query parameter `code` of the callback URL, paste the value in the command as an argument to finish the process.
import json import datetime import pytest import requests_mock from freezegun import freeze_time """ UTILS """ def util_load_json(path: str) -> dict: with open(path) as f: return json.load(f) def util_load_text(path: str) -> str: with open(path) as f: return f.read() def mock_get_access_token(): return { "access_token": "123456", "expires_in": 1111, "refresh_token": "123456", "refresh_token_expires_in": 2222, } def mocked_admin_client(): from CiscoWebexEventCollector import AdminClient class MockAdminClient(AdminClient): def get_access_token(self): return "" return MockAdminClient( "https://url.com", False, False, "1", "1", "https://redirect.com", "admin_scope", "1", ) def mocked_compliance_officer_client(): from CiscoWebexEventCollector import ComplianceOfficerClient class MockComplianceOfficerClient(ComplianceOfficerClient): def get_access_token(self): return "" return MockComplianceOfficerClient( "https://url.com", False, False, "1", "1", "https://redirect.com", "co_scope", ) """ TEST HELPER FUNCTION """ def mock_set_integration_context(context: dict = None) -> dict | None: return context def test_remove_integration_context_for_user(mocker): import CiscoWebexEventCollector mock_integration_context = {"test_user1": {"context_key": "context_value"}, "test_user2": {"context_key": "context_value"}} mocker.patch.object(CiscoWebexEventCollector, "get_integration_context", return_value=mock_integration_context) mock_context = mocker.patch("CiscoWebexEventCollector.set_integration_context", side_effect=mock_set_integration_context) assert CiscoWebexEventCollector.get_integration_context() == mock_integration_context CiscoWebexEventCollector.remove_integration_context_for_user("test_user1") assert mock_context.call_args.args[0] == {"test_user1": {}, "test_user2": {"context_key": "context_value"}} CiscoWebexEventCollector.remove_integration_context_for_user("test_user2") assert mock_context.call_args.args[0] == {"test_user1": {}, "test_user2": {}} @freeze_time("2023-12-20 13:40:00 UTC") def test_create_last_run(): """ Given: - An expected `last_run` dict. When: - create_last_run function is running. Then: - Validates that the function creates a dict with the expected items. """ from CiscoWebexEventCollector import create_last_run expected_result = { "admin_audits": {"since_datetime": "2023-12-13T13:40:00.000Z", "next_url": ""}, "security_audits": {"since_datetime": "2023-12-13T13:40:00.000Z", "next_url": ""}, "compliance_officer_events": {"since_datetime": "2023-12-13T13:40:00.000Z", "next_url": ""}, } assert create_last_run() == expected_result @freeze_time("2023-12-20 13:40:00 UTC") def test_date_time_to_iso_format(): """ Given: - A datetime object with freeze time set to '2023-12-20 13:40:00'. When: - date_time_to_iso_format function is running. Then: - Validates that the function returns a string is ISO format as expected. """ from CiscoWebexEventCollector import date_time_to_iso_format assert date_time_to_iso_format(datetime.datetime.utcnow()) == "2023-12-20T13:40:00.000Z" def test_add_fields_to_events(): """ Given: - lists of events of the following types. 1. Admin Audit Events. 2. Admin Audit Events. 3. Events. When: - add_fields_to_events function is running. Then: - Validates that the function adds the fields as expected. """ from CiscoWebexEventCollector import add_fields_to_events, COMMAND_FUNCTION_TO_EVENT_TYPE admin_audits = util_load_json("test_data/admin_audits.json").get("items") security_audits = util_load_json("test_data/security_audits.json").get("items") compliance_officer_events = util_load_json("test_data/events.json").get("items") assert not any(key in admin_audits[0] for key in ("_time", "source_log_type")) assert not any(key in security_audits[0] for key in ("_time", "source_log_type")) assert not any(key in compliance_officer_events[0] for key in ("_time", "source_log_type")) add_fields_to_events(admin_audits, "Admin Audit Events") add_fields_to_events(security_audits, "Security Audit Events") add_fields_to_events(compliance_officer_events, "Events") assert admin_audits[0]["_time"] == admin_audits[0]["created"] assert admin_audits[0]["source_log_type"] == COMMAND_FUNCTION_TO_EVENT_TYPE.get("get_admin_audits") assert security_audits[0]["_time"] == security_audits[0]["created"] assert security_audits[0]["source_log_type"] == COMMAND_FUNCTION_TO_EVENT_TYPE.get("get_security_audits") assert compliance_officer_events[0]["_time"] == compliance_officer_events[0]["created"] assert compliance_officer_events[0]["source_log_type"] == COMMAND_FUNCTION_TO_EVENT_TYPE.get("get_compliance_officer_events") @pytest.mark.parametrize( "latest_datetime_previous_fetch, expected_datetime", [ ("2023-12-04T07:40:06.680Z", "2023-12-04T07:40:06.691Z"), ("2023-12-04T07:40:06.695Z", "2023-12-04T07:40:06.696Z"), ], ) def test_increase_datetime_for_next_fetch(latest_datetime_previous_fetch, expected_datetime): """ Given: - A list of events and a string represents a datetime from the previous fetch. 1. the datetime from the previous fetch is earlier than the latest event in the list of events. 2. the datetime from the previous fetch is later than the latest event in the list of events. When: - increase_datetime_for_next_fetch function is running. Then: - Validates that the function returns the latest event time + a timedelta of 1 millisecond. """ from CiscoWebexEventCollector import increase_datetime_for_next_fetch events = util_load_json("test_data/events.json").get("items") assert increase_datetime_for_next_fetch(events, latest_datetime_previous_fetch) == expected_datetime """ TEST COMMAND FUNCTION """ @pytest.mark.parametrize( "client, expected_url", [ ( mocked_admin_client(), "https://url.com/authorize?response_type=code&scope=admin_scope&client_id=1" "&redirect_uri=https%3A%2F%2Fredirect.com", ), ( mocked_compliance_officer_client(), "https://url.com/authorize?response_type=code&scope=co_scope&client_id=1&redirect_uri=https%3A%2F%2Fredirect.com", ), ], ) def test_oauth_start(client, expected_url): """ Given: - An AdminClient and a ComplianceOfficerClient. When: - oauth_start function is running. Then: - Validates that the expected URL is in the result. """ from CiscoWebexEventCollector import oauth_start results = oauth_start(client) assert expected_url in results.readable_output @pytest.mark.parametrize("client", [mocked_admin_client(), mocked_compliance_officer_client()]) def test_oauth_complete(client): """ Given: - An AdminClient and a ComplianceOfficerClient. When: - oauth_complete function is running. Then: - Validates that the expected text (`Logged in successfully.`) is in the result. """ from CiscoWebexEventCollector import oauth_complete with requests_mock.Mocker() as m: m.post("https://url.com/access_token", json=mock_get_access_token()) results = oauth_complete(client, {"code": "123456"}) assert "Authorization completed successfully." in results.readable_output @pytest.mark.parametrize("client", [mocked_admin_client(), mocked_compliance_officer_client()]) def test_oauth_test(client): """ Given: - An AdminClient and a ComplianceOfficerClient. When: - oauth_test function is running. Then: - Validates that the expected text (`### Test succeeded!`) is in the result. """ from CiscoWebexEventCollector import oauth_test with requests_mock.Mocker() as m: m.get("https://url.com/adminAudit/events", text=util_load_text("test_data/admin_audits.json")) m.get("https://url.com/events", text=util_load_text("test_data/events.json")) result = oauth_test(client) assert result.readable_output == "```✅ Success!```" @pytest.mark.parametrize( "command_function, args", [ (mocked_admin_client().get_admin_audits, {}), (mocked_admin_client().get_security_audits, {}), (mocked_compliance_officer_client().get_compliance_officer_events, {}), ], ) def test_get_events_command(command_function, args): """ Given: - Three types of events to fetch. When: - get_events_command function is running. 1. with event type `Admin audits` 2. with event type `Security audits` 3. with event type `Events` Then: - Validates that the function works as expected. """ from CiscoWebexEventCollector import get_events_command, COMMAND_FUNCTION_TO_EVENT_TYPE with requests_mock.Mocker() as m: m.get("https://url.com/adminAudit/events", text=util_load_text("test_data/admin_audits.json")) m.get("https://url.com/admin/securityAudit/events", text=util_load_text("test_data/security_audits.json")) m.get("https://url.com/events", text=util_load_text("test_data/events.json")) command_results, events = get_events_command(command_function, args) assert len(events) > 0 assert COMMAND_FUNCTION_TO_EVENT_TYPE.get(command_function.__name__) in command_results.readable_output @freeze_time("2023-12-20 13:40:00 UTC") def test_fetch_events(): """ Given: - An AdminClient and a ComplianceOfficerClient. When: - fetch_events function is running. Then: - Validates that the function returns 1. A list of events and a dict with fetch data including a `next_url` link. 2. The second interval of fetch_events uses the `next_url` link from the previous fetch, and returns an empty list of events and a dict with fetch data including a `next_url` link set to an empty string. """ from CiscoWebexEventCollector import create_last_run, fetch_events with requests_mock.Mocker() as m: m.get( "https://url.com/adminAudit/events?orgId=1&from=2023-12-13T13%3A40%3A00.000Z&to=2023-12-20T13%3A40%3A00.000Z&max=1", text=util_load_text("test_data/admin_audits.json"), headers={"Link": '<https://url.com/adminAudit/events?nexturl=true>; rel="next"'}, ) m.get( "https://url.com/admin/securityAudit/events?orgId=1&startTime=2023-12-13T13%3A40%3A00.000Z&" "endTime=2023-12-20T13%3A40%3A00.000Z&max=1", text=util_load_text("test_data/security_audits.json"), headers={"Link": '<https://url.com/securityAudit/events?nexturl=true>; rel="next"'}, ) m.get( "https://url.com/events?from=2023-12-13T13%3A40%3A00.000Z&to=2023-12-20T13%3A40%3A00.000Z&max=1", text=util_load_text("test_data/events.json"), headers={"Link": '<https://url.com/events?nexturl=true>; rel="next"'}, ) events, next_run = fetch_events(mocked_admin_client(), mocked_compliance_officer_client(), create_last_run(), 1, True) assert len(events) > 0 assert next_run == { "admin_audits": { "next_url": "https://url.com/adminAudit/events?nexturl=true", "since_datetime": "2023-12-20T09:33:26.409Z", }, "compliance_officer_events": { "next_url": "https://url.com/events?nexturl=true", "since_datetime": "2023-12-13T13:40:00.001Z", }, "security_audits": { "next_url": "https://url.com/securityAudit/events?nexturl=true", "since_datetime": "2023-12-19T07:01:26.487Z", }, } with requests_mock.Mocker() as m: m.get("https://url.com/adminAudit/events?nexturl=true", text=util_load_text("test_data/no_events.json")) m.get("https://url.com/securityAudit/events?nexturl=true", text=util_load_text("test_data/no_events.json")) m.get("https://url.com/events?nexturl=true", text=util_load_text("test_data/no_events.json")) events, next_run = fetch_events(mocked_admin_client(), mocked_compliance_officer_client(), next_run, 1, True) assert len(events) == 0 assert next_run == { "admin_audits": {"next_url": "", "since_datetime": "2023-12-20T09:33:26.409Z"}, "compliance_officer_events": {"next_url": "", "since_datetime": "2023-12-13T13:40:00.001Z"}, "security_audits": {"next_url": "", "since_datetime": "2023-12-19T07:01:26.487Z"}, }