---
description: Pipeline for parsing Salesforce Logout (REST) logs
processors:
- set:
    field: event.ingested
    value: '{{_ingest.timestamp}}'
- remove:
    field:
      - message
    ignore_missing: true

- set:
    field: salesforce.access_mode
    value: rest
    ignore_failure: true

- date:
    field: json.TIMESTAMP_DERIVED
    target_field: "@timestamp"
    formats:
    - ISO8601
    ignore_failure: true

- rename: 
    field: json.SESSION_TYPE
    target_field: salesforce.logout.session_type
    ignore_missing: true
- rename: 
    field: json.EVENT_TYPE
    target_field: salesforce.logout.event_type
    ignore_missing: true
- rename: 
    field: json.SESSION_LEVEL
    target_field: salesforce.logout.session_level
    ignore_missing: true
- rename: 
    field: json.BROWSER_TYPE
    target_field: salesforce.logout.browser_type
    ignore_missing: true
- rename: 
    field: json.PLATFORM_TYPE
    target_field: salesforce.logout.platform_type
    ignore_missing: true
- rename: 
    field: json.RESOLUTION_TYPE
    target_field: salesforce.logout.resolution_type
    ignore_missing: true
- rename: 
    field: json.APP_TYPE
    target_field: salesforce.logout.app_type
    ignore_missing: true
- rename: 
    field: json.CLIENT_VERSION
    target_field: salesforce.logout.client_version
    ignore_missing: true
- rename: 
    field: json.API_TYPE
    target_field: salesforce.logout.api_type
    ignore_missing: true
- rename: 
    field: json.API_VERSION
    target_field: salesforce.logout.api_version
    ignore_missing: true
- rename: 
    field: json.USER_INITIATED_LOGOUT
    target_field: salesforce.logout.user_initiated_logout
    ignore_missing: true
- rename: 
    field: json.LOGIN_KEY
    target_field: salesforce.logout.login_key
    ignore_missing: true
- rename: 
    field: json.USER_ID_DERIVED
    target_field: salesforce.logout.user_id_derived
    ignore_missing: true
- rename: 
    field: json.ORGANIZATION_ID
    target_field: salesforce.logout.organization_by_id
    ignore_missing: true

#######################
## ECS Event Mapping ##
#######################

- set:
    field: event.type
    value: "info"
- set:
    field: event.kind
    value: "event"
- set:
    field: event.action
    value: "logout"
- set:
    field: event.category
    value: "authentication"
- set:
    field: event.dataset
    value: "salesforce.logout"
- set:
    field: event.module
    value: "salesforce"
- rename:
    field: json.REQUEST_ID
    target_field: event.code
    ignore_missing: true

######################
## ECS User Mapping ##
######################

- rename: 
    field: json.USER_TYPE
    target_field: user.roles
    ignore_missing: true
- rename: 
    field: json.USER_ID
    target_field: user.id
    ignore_missing: true

########################
## ECS Source Mapping ##
########################

# A Salesforce internal IP (such as a login from Salesforce Workbench or AppExchange) is shown as “Salesforce.com IP”
- rename:
    field: json.CLIENT_IP
    target_field: source.ip
    ignore_missing: true
    if: 'ctx?.json?.CLIENT_IP != "Salesforce.com IP" && ctx?.json?.CLIENT_IP != ""'

############################
## ECS Source.Geo Mapping ##
############################

- geoip:
    field: source.ip
    target_field: source.geo
    ignore_missing: true

############################
## ECS Related.ip Mapping ##
############################

- append:
    field: related.ip
    value: "{{{source.ip}}}"
    if: ctx?.source?.ip != null
    allow_duplicates: false
    ignore_failure: true

#############
## Cleanup ##
#############

- script:
    description: Drops null/empty values recursively
    lang: painless
    source: |
        boolean dropEmptyFields(Object object) {
            if (object == null || object == "") {
                return true;
            } else if (object instanceof Map) {
                ((Map) object).values().removeIf(value -> dropEmptyFields(value));
                return (((Map) object).size() == 0);
            } else if (object instanceof List) {
                ((List) object).removeIf(value -> dropEmptyFields(value));
                return (((List) object).length == 0);
            }
            return false;
        }
        dropEmptyFields(ctx);
- remove:
    field:
      - json
    ignore_missing: true
on_failure:
- set:
    field: error.message
    value: '{{_ingest.on_failure_message}}'
