OneLogin Event Collector

Simple customer authentication and streamlined workforce identity operations.

Analytics & SIEM · OneLogin

Details

IDOneLogin Event Collector
ProviderOneIdentity
CategoryAnalytics & SIEM
From Version6.8.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesXSIAM

README

Simple customer authentication and streamlined workforce identity operations.

Configure OneLogin Event Collector in Cortex

Parameter Description Required
Your server URL   True
Client Id The client ID. True
Client Secret The client secret. True
The maximum number of events per fetch   False
First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)   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.

onelogin-get-events


Manual command to fetch events from OneLogin and display them.

Base Command

onelogin-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 Number of results to return. Maximum is 2000. Default is 10. Optional
cursor A string pointing at the next page of results. The cursor can be found within the response_metadata field, as part of the raw response of the OneLogin Events API call. Optional
since Occurrence time of the least recent event to include (inclusive). Default is 3 days. Optional
until Occurrence time of the most recent event to include (inclusive). Optional
event_type_id A comma-separated list of type IDs of events to include. Optional

Context Output

There is no context output for this command.

Configuration parameters

  • url — Your server URL (required)
  • credentials — Client Id (required)
  • limit — The maximum number of events per fetch
  • since — First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (1)

  • onelogin-get-events

    Manual command to fetch events and display them.

import json

import demistomock as demisto
import pytest
from OneLoginEventCollector import Client, check_response, prepare_query_params
from requests import Session

CORE_URL = "https://api.onelogin.com"


def util_load_json(path):
    with open(path, encoding="utf-8") as f:
        return json.loads(f.read())


MOCK_EVENTS = util_load_json("test_data/mock_events.json")
MOCK_EVENT_TYPES = util_load_json("test_data/mock_event_types.json")


class MockResponse:
    def __init__(self, data: list):
        self.ok = True
        self.status_code = 200
        self.data = {"data": [self.create_mock_entry(**e) for e in data]}

    def create_mock_entry(self, **kwargs) -> dict:
        return kwargs

    def json(self):
        return self.data


@pytest.mark.parametrize(
    "params, last_run, expected_params",
    [
        ({"limit": "1"}, {}, {"limit": 1}),
        ({"since": "2022-08-01T09:00:00Z"}, {"since": "2022-09-01T09:00:00Z"}, {"limit": 1000, "since": "2022-09-01T09:00:00Z"}),
        (
            {"since": "2022-08-01T09:00:00Z", "after_cursor": "param_cursor"},
            {"after_cursor": "last_run_cursor"},
            {"limit": 1000, "since": "2022-08-01T09:00:00Z", "after_cursor": "last_run_cursor"},
        ),
        ({"after_cursor": "param_cursor"}, {}, {"limit": 1000, "after_cursor": "param_cursor"}),
    ],
)
def test_onelogin_events_params_good(params, last_run, expected_params):
    """
    Given:
        - Various dictionary values.
    When:
        - preparing the parameters.
    Then:
        - Make sure they are parsed correctly.
    """
    query_params = prepare_query_params(params, last_run)

    if query_params["since"]:
        assert (
            query_params["since"] == "2022-09-01T09:00:00Z"
            if "since" in last_run
            else query_params["since"] == "2022-08-01T09:00:00Z"
        )

    if query_params["after_cursor"]:
        assert (
            query_params["after_cursor"] == "last_run_cursor"
            if "after_cursor" in last_run
            else query_params["after_cursor"] == "param_cursor"
        )

    assert expected_params.items() <= prepare_query_params(params, last_run).items()


@pytest.mark.parametrize("params", [{"limit": "hello"}, {"since": "hello"}, {"until": "hello"}])
def test_onelogin_events_params_bad(params):
    """
    Given:
        - Various dictionary bad values.
    When:
        - preparing the parameters.
    Then:
        - Make sure an Exception is raised.
    """
    with pytest.raises(ValueError):
        prepare_query_params(params)


@pytest.mark.parametrize(
    "response, should_fail",
    [({"status": {"error": False}}, False), ({"status": {"error": True, "code": 400}}, True), (MOCK_EVENTS, False)],
)
def test_onelogin_check_response(mocker, response, should_fail):
    """
    Given:
        - An OneLogin API response.
    When:
        - Running the check_response to verify the response isn't an error.
    Then:
        - Make sure the method raises an exception if the response is an error.
    """
    mocker.patch.object(demisto, "error")

    if should_fail:
        with pytest.raises(Exception):
            check_response(response)
    else:
        check_response(response)


def test_test_module(mocker):
    """
    Given:
        - test-module call
    When:
        - A response with an OK status_code is retrieved from the API call.
    Then:
        - Make sure 'ok' is returned.
    """
    from OneLoginEventCollector import test_module_command

    mocker.patch.object(Session, "request", return_value=MockResponse([]))
    assert test_module_command(Client(base_url="", headers={}), {}) == "ok"


def test_fetch_events(requests_mock):
    """
    Given:
        - fetch-events call, where first_id = 2 in LastRun obj.
    When:
        - Four events with ids 1, 2, 3 and 4 are retrieved from the API.
    Then:
        - Make sure only events 2, 3 and 4 are returned (1 should not).
    """
    from OneLoginEventCollector import fetch_events_command

    last_run = {"first_id": 2}

    requests_mock.post(f"{CORE_URL}/auth/oauth2/v2/token", json={"access_token": "token"})
    requests_mock.get(f"{CORE_URL}/api/1/events/types", json=MOCK_EVENT_TYPES)
    requests_mock.get(f"{CORE_URL}/api/1/events", json=MOCK_EVENTS)

    events, _ = fetch_events_command(Client(base_url=CORE_URL, headers={}), params={"limit": 3}, last_run=last_run)

    assert len(events) == 3
    assert events[0].get("id") == 2


def test_fetch_events_with_iterations(requests_mock):
    """
    Given:
        - fetch-events command execution.
    When:
        - Limit parameter value is 10.
        - A single /events API call retrieves 4 events.
    Then:
        - Make sure the logs API is called 3 times.
    """
    from OneLoginEventCollector import fetch_events_command

    requests_mock.post(f"{CORE_URL}/auth/oauth2/v2/token", json={"access_token": "token"})
    requests_mock.get(f"{CORE_URL}/api/1/events/types", json=MOCK_EVENT_TYPES)
    mock_request = requests_mock.get(f"{CORE_URL}/api/1/events", json=MOCK_EVENTS)

    events, _ = fetch_events_command(Client(base_url=CORE_URL, headers={}), params={"limit": 10}, last_run={})

    assert len(events) == 10
    assert mock_request.call_count == 3


def test_fetch_events_with_last_event_ids(requests_mock):
    """
    Given:
        - fetch-events call, where first_id = 2 in LastRun obj.
    When:
        - Four events with ids 1, 2, 3 and 4 are retrieved from the API.
    Then:
        - Make sure only events 2, 3 and 4 are returned (1 should not).
    """
    from OneLoginEventCollector import fetch_events_command

    last_run = {"last_event_ids": [1, 2]}

    requests_mock.post(f"{CORE_URL}/auth/oauth2/v2/token", json={"access_token": "token"})
    requests_mock.get(f"{CORE_URL}/api/1/events/types", json=MOCK_EVENT_TYPES)
    requests_mock.get(f"{CORE_URL}/api/1/events", json=MOCK_EVENTS)

    events, _ = fetch_events_command(Client(base_url=CORE_URL, headers={}), params={"limit": 2}, last_run=last_run)

    assert len(events) == 2
    assert events[0].get("id") == 3


def test_get_events(mocker, requests_mock):
    """
    Given:
        - onelogin-get-events call
    When:
        - Four events with ids 1, 2, 3 and 4 are retrieved from the API.
    Then:
        - Make sure all of the events are returned as part of the CommandResult.
    """
    from OneLoginEventCollector import get_events_command

    response = MOCK_EVENTS
    requests_mock.get(f"{CORE_URL}/api/1/events", json=response)

    mocker.patch.object(Client, "get_access_token_request")
    mocker.patch.object(Client, "get_event_types_request")
    _, results = get_events_command(Client(base_url=CORE_URL, headers={}), args={})

    assert len(results.raw_response.get("data", [])) == 4
    assert results.raw_response == response


@pytest.mark.parametrize("last_run, call_count", [({"event_types": {"1": "TYPE"}}, 0), ({}, 1)])
def test_get_event_types_from_last_run(requests_mock, last_run, call_count):
    """
    Given:
        - LasRun object.
    When:
        - Trying tp get the events type using the client.get_event_types_from_last_run method.
    Then:
        - Verify a request is sent only if there are no event types in the LastRun.
    """

    request_mocker = requests_mock.get(f"{CORE_URL}/api/1/events/types", json={})
    client = Client(base_url=CORE_URL, headers={})
    client.get_event_types_from_last_run(last_run)

    assert request_mocker.call_count == call_count


@pytest.mark.parametrize(
    "event, call_count, expected_name",
    [
        ({"event_type_id": 1}, 0, "EVENT_TYPE_NUMBER_1"),
        ({"event_type_id": 3}, 1, "EVENT_TYPE_NUMBER_3"),
        ({"event_type_id": 5}, 1, ""),
    ],
)
def test_convert_type_id_to_name(requests_mock, event, call_count, expected_name):
    """
    Given:
        - An event from the API response.
        - Dict of event type names by IDs.
    When:
        - Trying to convert the event type id to its name.
    Then:
        - Verify the returned type name is as expected.
        - Verify An API request is called only if didn't find the type ID in the given types dict.
        - Verify LastRun object is updated with the new event types dict if a request was called.
    """

    request_mock = requests_mock.get(f"{CORE_URL}/api/1/events/types", json=MOCK_EVENT_TYPES)
    client = Client(base_url=CORE_URL, headers={})

    event_types = {"1": "EVENT_TYPE_NUMBER_1", "2": "EVENT_TYPE_NUMBER_2"}
    last_run = {"event_types": event_types}
    event_type_name = client.convert_type_id_to_name(event, event_types, last_run)

    assert event_type_name == expected_name
    assert request_mock.call_count == call_count
    if expected_name:
        assert str(event["event_type_id"]) in last_run["event_types"]