GSuiteAuditor

G Suite Auditor is an integration that receives Audit logs from G Suite's different applications - admin, drive, calender, and more.

IT Services · GsuiteAuditor

Details

IDGSuiteAuditor
ProviderGoogle
CategoryIT Services
From Version5.5.0
Docker Imagedemisto/googleapi-python3:1.0.0.115338
Supported ModulesAgentix XSIAM

README

G Suite Auditor is an integration that receives audit logs from G Suite’s different applications - admin, drive, calendar, and more.

Required Permissions

In order to use the integration you will need:

  • Admin email address.
  • Service Account with access to the following scope - https://www.googleapis.com/auth/admin.reports.audit.readonly .

For more information, see the integrations description.

For more information about the integration arguments

Google’s lag time information

This integration relies on Google’s audit logs which are prone to some data delays. Some results may be partial if data had not arrived due to lag times. The lag time changes depending on the used audit log (from near real-time up to 3 days).
For more information - https://support.google.com/a/answer/7061566?hl=en

Configure G Suite Auditor in Cortex

Parameter Required
Admin email True
User’s Service Account JSON True
Use system proxy settings False
Trust any certificate (not secure) 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.

gsuite-activity-search


Retrieves a list of activities for a specific customer’s account and application.

Base Command

gsuite-activity-search

Input

Argument Name Description Required
user_key Profile ID or the user email for which the data should be filtered. Can be ‘all’ for all information, or ‘userKey’ for a user’s unique Google Workspace profile ID or their primary email address. Optional
application_name Application name for which the events are to be retrieved. Possible values are: access_transparency, admin, calendar, chat, drive, gcp, gplus, groups, groups_enterprise, jamboard, login, meet, mobile, rules, saml, token, user_accounts, context_aware_access, chrome, data_studio, keep. Required
event_name The name of the event being queried. For a list of event names for each application, see the integration documentation. Optional
filters A comma-separated list of event parameters and relational operators. For example-
‘API_CLIENT_NAME==111,API_SCOPES=aaa’.
The relevant ‘event name’ argument must be supplied to use this argument.
Optional
org_unit_id ID of the organizational unit to report on. Activity records will be shown only for users who belong to the specified organizational unit. Optional
group_id Comma-separated group IDs on which user activities are filtered.
The response will contain activities for only those users who are a part of
at least one of the group IDs mentioned here. For example: “id:abc123,id:xyz456”.
To retrieve a group ID, use the ‘gsuite-get-group’ command in the ‘G Suite Admin’ pack.
Optional
actor_ip_address IP address of the host where the event was performed. Optional
start_time The beginning of the time range shown in the report. For example - 2010-10-28T10:26:35.000Z. Optional
end_time The end of the time range shown in the report. For example - 2010-10-28T10:26:35.000Z. Optional
max_results Maximum number of results to return. Default is 50. Optional
page_token The token to specify the next page. Optional
admin_email Email address of the G Suite domain admin. The request is preformed based on this user’s permissions. Optional

Context Output

Path Type Description
GSuite.ActivitySearch.items.id.time Date Time the activity occurred.
GSuite.ActivitySearch.items.id.uniqueQualifier String Unique qualifier if multiple events have the same time.
GSuite.ActivitySearch.items.id.applicationName String Application name to which the event belongs.
GSuite.ActivitySearch.items.id.customerId String The unique identifier for a Google Workspace account.
GSuite.ActivitySearch.items.actor.callerType String The type of actor.
GSuite.ActivitySearch.items.actor.key String Can be the consumer_key of the requestor for OAuth 2LO API requests or an identifier for robot accounts.
GSuite.ActivitySearch.items.events.type String Type of event.
GSuite.ActivitySearch.items.events.name String Name of the event.
GSuite.ActivitySearch.items.events.parameters.name String The name of the parameter.
GSuite.ActivitySearch.items.events.parameters.value String String value of the parameter.
GSuite.ActivitySearch.items.actor.email String The primary email address of the actor.
GSuite.ActivitySearch.items.actor.profileId String The unique Google Workspace profile ID of the actor.
GSuite.ActivitySearch.items.ipAddress String IP address of the user performing the action.
GSuite.ActivitySearch.items.events.parameters.boolValue Boolean Boolean value of the parameter.
GSuite.ActivitySearch.items.events.parameters.multiValue String String values of the parameter.
GSuite.PageToken.ActivitySearch.nextPageToken String Token to specify the next page in the list.

Command Example

!gsuite-activity-search application_name=admin max_results=2

Context Example

{
    "GSuite": {
        "ActivitySearch": [
            {
                "actor": {
                    "callerType": "KEY",
                    "key": "SYSTEM"
                },
                "events": [
                    {
                        "name": "USER_LICENSE_REVOKE",
                        "parameters": [
                            {
                                "name": "USER_EMAIL",
                                "value": "user@email.com"
                            },
                            {
                                "name": "PRODUCT_NAME",
                                "value": "Google Workspace"
                            },
                            {
                                "name": "OLD_VALUE",
                                "value": "G Suite Business"
                            }
                        ],
                        "type": "LICENSES_SETTINGS"
                    }
                ],
                "id": {
                    "applicationName": "admin",
                    "customerId": "11111",
                    "time": "2021-07-27T02:47:20.894Z",
                    "uniqueQualifier": "-7168880636905757919"
                }
            },
            {
                "actor": {
                    "callerType": "USER",
                    "email": "admin@email.com",
                    "profileId": "103020731686044834269"
                },
                "events": [
                    {
                        "name": "DELETE_USER",
                        "parameters": [
                            {
                                "name": "USER_EMAIL",
                                "value": "user@email.com"
                            }
                        ],
                        "type": "USER_SETTINGS"
                    }
                ],
                "id": {
                    "applicationName": "admin",
                    "customerId": "1111",
                    "time": "2021-07-27T01:47:40.585Z",
                    "uniqueQualifier": "-4797090398870165525"
                },
                "ipAddress": " "
            }
        ],
        "PageToken": {
            "ActivitySearch": {
                "nextPageToken": "A:1627350460585000:-4797090398870165525:207535951991:C02f0zfqw"
            }
        }
    }
}

Human Readable Output

Next Page Token: A:1627350460585000:-4797090398870165525:207535951991:C02f0zfqw

Total Retrieved Activities: 2

Time Application Name Email ProfileId IpAddress Events
2021-07-27T02:47:20.894Z admin       {‘type’: ‘LICENSES_SETTINGS’, ‘name’: ‘USER_LICENSE_REVOKE’, ‘parameters’: [{‘name’: ‘USER_EMAIL’, ‘value’: ‘user@email.com’}, {‘name’: ‘PRODUCT_NAME’, ‘value’: ‘Google Workspace’}, {‘name’: ‘OLD_VALUE’, ‘value’: ‘G Suite Business’}]}
2021-07-27T01:47:40.585Z admin admin@email.com 103020731686044834269 - {‘type’: ‘USER_SETTINGS’, ‘name’: ‘DELETE_USER’, ‘parameters’: [{‘name’: ‘USER_EMAIL’, ‘value’: ‘user@email.com’}]}

Configuration parameters

  • credentials — Admin email
  • proxy — Use system proxy settings
  • insecure — Trust any certificate (not secure)

Commands (1)

  • gsuite-activity-search

    Retrieves a list of activities for a specific customer's account and application.

# import demistomock as demisto

from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import
from CommonServerUserPython import *  # noqa
from GSuiteApiModule import *  # noqa: E402

""" IMPORTS """

from typing import Any

import urllib3

# Disable insecure warnings
urllib3.disable_warnings()

ADMIN_EMAIL = None

""" CONSTANTS """

OUTPUT_PREFIX: dict[str, str] = {
    "ACTIVITY_LIST": "GSuite.ActivitySearch",
    "ACTIVITY_LIST_PAGE_TOKEN": "GSuite.PageToken.ActivitySearch",
}

REQ_URL = "https://admin.googleapis.com/"
URL_SUFFIX = "admin/reports/v1/activity/users/{}/applications/{}"
SCOPE = ["https://www.googleapis.com/auth/admin.reports.audit.readonly"]

DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"  # ISO8601 format with UTC, default in XSOAR
DATE_MILISEC_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"

""" HELPER FUNCTIONS """


def is_email_valid(email: str) -> bool:
    """
    Validates provided email is valid or not.

    :param email: email string.
    :return:  True if email is in valid format.
    """

    return bool(re.match(emailRegex, email))


def prepare_args_for_activities_list(args: dict[str, str]) -> dict[str, str]:
    """
    Prepares arguments for gsuite-activity-search command.

    :param args: Command arguments.

    :return: Prepared arguments.
    """

    return GSuiteClient.remove_empty_entities(
        {
            "eventName": args.get("event_name"),
            "filters": args.get("filters"),
            "orgUnitId": args.get("org_unit_id"),
            "groupIdFilter": args.get("group_id"),
            "actorIpAddress": args.get("actor_ip_address"),
            "startTime": args.get("start_time"),
            "endTime": args.get("end_time"),
            "maxResults": GSuiteClient.validate_get_int(
                args.get("max_results"), "The argument max_results must be a positive integer."
            ),
            "pageToken": args.get("page_token"),
        }
    )


def prepare_readable_items(items: list[dict[str, Any]]) -> list[dict[str, Any]]:
    readable_items = [
        {
            "Time": item.get("id", {}).get("time"),
            "Application Name": item.get("id", {}).get("applicationName"),
            "Email": item.get("actor", {}).get("email"),
            "ProfileId": item.get("actor", {}).get("profileId"),
            "IpAddress": item.get("ipAddress", ""),
            "Events": item["events"],
        }
        for item in items
    ]

    return GSuiteClient.remove_empty_entities(readable_items)


def prepare_output_for_activities_list(response: dict[str, Any]) -> dict[str, Any]:
    """
    prepares context output for gsuite-activity-search.

    :param response: API response.

    :return: output dictionary.
    """
    output_items = [
        {"id": item["id"], "actor": item["actor"], "ipAddress": item.get("ipAddress", []), "events": item["events"]}
        for item in response.get("items", [])
    ]

    return {
        "GSuite.ActivitySearch": GSuiteClient.remove_empty_entities(output_items),
        "GSuite.PageToken.ActivitySearch": {"nextPageToken": response["nextPageToken"]}
        if response.get("nextPageToken", "")
        else {},
    }


def prepare_gsuite_client(params: dict) -> GSuiteClient:
    user_service_account = params.get("credentials", {}).get("password")
    service_account_dict = GSuiteClient.safe_load_non_strict_json(user_service_account)
    verify_certificate = not params.get("insecure", False)
    proxy = params.get("proxy", False)

    headers = {"Content-Type": "application/json"}

    # prepare client class object
    gsuite_client = GSuiteClient(service_account_dict, base_url=REQ_URL, verify=verify_certificate, proxy=proxy, headers=headers)
    return gsuite_client


@logger
def test_module(client: GSuiteClient) -> str:
    """
    Performs test connectivity by valid http response

    :param client: client object which is used to get response from api.

    :return: raise ValueError if any error occurred during connection
    :raises DemistoException: If there is any other issues while making the http call.
    """
    with GSuiteClient.http_exception_handler():
        if ADMIN_EMAIL:
            suffix = URL_SUFFIX.format("all", "admin")
            client.http_request(url_suffix=suffix, method="GET", params={"max_results": "1"})
        else:
            return_results("Please insert Admin Email parameter for the test to run")
    return "ok"


""" COMMAND FUNCTIONS """


def activities_list_command(client: GSuiteClient, args: dict[str, Any]) -> CommandResults:
    """
    Prints all activities in the G Suite instance.

    :param client: client object which is used to get response from api
    :param args: command arguments.

    :return: CommandResults object with context and human-readable.
    """
    arguments = prepare_args_for_activities_list(args)
    response = client.http_request(
        url_suffix=URL_SUFFIX.format(args.get("user_key", "all"), args.get("application_name")), params=arguments
    )

    # Readable Output
    readable_items = prepare_readable_items(response.get("items", []))

    readable_output = f'### Next Page Token: {response["nextPageToken"]}\n' if response.get("nextPageToken") else ""
    readable_output += (
        tableToMarkdown(
            "Total Retrieved {}: {}".format("Activities", len(response.get("items", []))),
            readable_items,
            ["Time", "Application Name", "Email", "ProfileId", "IpAddress", "Events"],
        )
        if response.get("items")
        else "No activities found for the given argument(s). "
    )
    # Context
    context_outputs = prepare_output_for_activities_list(response)

    return CommandResults(outputs=context_outputs, readable_output=readable_output, raw_response=response)


""" MAIN FUNCTION """


def main() -> None:
    """main function, parses params and runs command functions

    :return:
    :rtype:
    """

    command = demisto.command()
    demisto.info(f"Command being called is {command}")

    try:
        global ADMIN_EMAIL
        params = demisto.params()
        gsuite_client = prepare_gsuite_client(params)

        # Trim the arguments
        args = GSuiteClient.strip_dict(demisto.args())

        ADMIN_EMAIL = args.get("admin_email") if args.get("admin_email") else params.get("credentials", {}).get("identifier")
        # Validation of ADMIN_EMAIL
        if ADMIN_EMAIL and not is_email_valid(ADMIN_EMAIL):
            raise ValueError("Invalid value of argument/parameter Admin Email.")

        gsuite_client.set_authorized_http(scopes=SCOPE, subject=ADMIN_EMAIL)
        # This is the call made when pressing the integration Test button.
        if command == "test-module":
            result = test_module(gsuite_client)
            return_results(result)

        elif command == "gsuite-activity-search":
            return_results(activities_list_command(gsuite_client, args))

        # Log exceptions
    except Exception as e:
        return_error(f"Error: {e!s}")


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()