Skip to content

Commit

Permalink
Salesforce 2.1.0 - release (#2005)
Browse files Browse the repository at this point in the history
  • Loading branch information
llaszuk-r7 authored Oct 2, 2023
1 parent ded2ebf commit db4ecd2
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 232 deletions.
8 changes: 4 additions & 4 deletions plugins/salesforce/.CHECKSUM
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"spec": "376f6b481744c0bfb7c1768add79f86b",
"manifest": "2fed1eae6fef01ffe60ed61db132753f",
"setup": "1637f87ae56f374eb26e2e1fac33e4db",
"spec": "c1dec7cfceff3e96fdcfbbb845b157b8",
"manifest": "71e58227b4db0b3e8df068c994e3b2c7",
"setup": "7f5033d434b3c7de40f80b08c8129187",
"schemas": [
{
"identifier": "advanced_search/schema.py",
Expand Down Expand Up @@ -41,7 +41,7 @@
},
{
"identifier": "monitor_users/schema.py",
"hash": "37bee6489cb79302a472590502be24a1"
"hash": "eddcd73207775d3a825400dc78274f07"
}
]
}
2 changes: 1 addition & 1 deletion plugins/salesforce/bin/komand_salesforce
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ from sys import argv

Name = "Salesforce"
Vendor = "rapid7"
Version = "2.0.2"
Version = "2.1.0"
Description = "The Salesforce plugin allows you to search, update, and manage salesforce records"


Expand Down
1 change: 1 addition & 0 deletions plugins/salesforce/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ _This plugin does not contain any troubleshooting information._

# Version History

* 2.1.0 - Implemented token auto-refresh on expiration for continuous sessions | Task Monitor Users: add flag `remove_duplicates` for duplicated events | Task Monitor Users: removed formatting of task output and cleaning null
* 2.0.2 - Task Monitor Users: query improvement | Handle exception related with grant type
* 2.0.1 - Add extra logs register
* 2.0.0 - Code refactor | Update plugin to be cloud enabled | Add new task Monitor Users
Expand Down
111 changes: 8 additions & 103 deletions plugins/salesforce/komand_salesforce/tasks/monitor_users/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,110 +38,15 @@ def __init__(self):
class MonitorUsersOutput(insightconnect_plugin_runtime.Output):
schema = json.loads("""
{
"type": "array",
"title": "Users",
"description": "Information about users, their login history and changes made to user data",
"items": {
"$ref": "#/definitions/userData"
"type": "array",
"title": "Users",
"description": "Information about users, their login history and changes made to user data",
"items": {
"type": "object"
},
"definitions": {
"userData": {
"type": "object",
"title": "userData",
"properties": {
"alias": {
"type": "string",
"title": "Alias",
"description": "The user’s alias",
"order": 6
},
"application": {
"type": "string",
"title": "Application",
"description": "The application used to access the organization",
"order": 14
},
"browser": {
"type": "string",
"title": "Browser",
"description": "The current browser version",
"order": 15
},
"dataType": {
"type": "string",
"title": "Data Type",
"description": "Type of the data",
"order": 1
},
"email": {
"type": "string",
"title": "Email",
"description": "The user’s email address",
"order": 5
},
"firstName": {
"type": "string",
"title": "First Name",
"description": "The user’s first name",
"order": 3
},
"id": {
"type": "string",
"title": "ID",
"description": "The ID of the user",
"order": 2
},
"isActive": {
"type": "boolean",
"title": "Is Active",
"description": "Indicates whether the user has access to log in (true) or not (false)",
"order": 7
},
"lastName": {
"type": "string",
"title": "Last Name",
"description": "The user’s last name",
"order": 4
},
"loginTime": {
"type": "string",
"title": "Login Time",
"description": "The time of user login. Time zone is based on GMT",
"order": 8
},
"loginType": {
"type": "string",
"title": "Login Type",
"description": "The type of login used to access the session",
"order": 10
},
"loginUrl": {
"type": "string",
"title": "Login URL",
"description": "URL from which the login request is coming",
"order": 11
},
"sourceIp": {
"type": "string",
"title": "Source IP",
"description": "IP address of the machine from which the login request is coming. The address can be an IPv4 or IPv6 address",
"order": 12
},
"status": {
"type": "string",
"title": "Status",
"description": "Displays the status of the attempted login. Status is either success or a reason for failure",
"order": 13
},
"userId": {
"type": "string",
"title": "User ID",
"description": "ID of the user logging in",
"order": 9
}
}
}
}
"required": [
"users"
]
}
""")

Expand Down
37 changes: 33 additions & 4 deletions plugins/salesforce/komand_salesforce/tasks/monitor_users/task.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import json

import insightconnect_plugin_runtime
from .schema import MonitorUsersInput, MonitorUsersOutput, MonitorUsersState, Component

# Custom imports below
from datetime import datetime, timedelta, timezone
from insightconnect_plugin_runtime.exceptions import PluginException
from komand_salesforce.util.exceptions import ApiException
from komand_salesforce.util.helpers import clean, convert_to_camel_case


class MonitorUsers(insightconnect_plugin_runtime.Task):
Expand All @@ -19,6 +19,7 @@ class MonitorUsers(insightconnect_plugin_runtime.Task):
NEXT_USER_COLLECTION_TIMESTAMP = "next_user_collection_timestamp"
NEXT_USER_LOGIN_COLLECTION_TIMESTAMP = "next_user_login_collection_timestamp"
LAST_USER_LOGIN_COLLECTION_TIMESTAMP = "last_user_login_collection_timestamp"
REMOVE_DUPLICATES = "remove_duplicates"

def __init__(self):
super(self.__class__, self).__init__(
Expand All @@ -38,6 +39,7 @@ def run(self, params={}, state={}): # noqa: C901
get_users = False
get_user_login_history = False

remove_duplicates = state.pop(self.REMOVE_DUPLICATES, True) # true as a default
users_next_page_id = state.get(self.USERS_NEXT_PAGE_ID)
state.pop(self.USERS_NEXT_PAGE_ID, None)
user_login_next_page_id = state.get(self.USER_LOGIN_NEXT_PAGE_ID)
Expand Down Expand Up @@ -111,6 +113,7 @@ def run(self, params={}, state={}): # noqa: C901
self.UPDATED_USERS_QUERY.format(user_ids=concatenated_ids), None
).get("records", [])

self.logger.info(f"{len(updated_users)} updated users added to output")
records.extend(self.add_data_type_field(updated_users, "User Update"))

if get_users:
Expand All @@ -119,6 +122,8 @@ def run(self, params={}, state={}): # noqa: C901
if users_next_page_id:
state[self.USERS_NEXT_PAGE_ID] = users_next_page_id
has_more_pages = True

self.logger.info(f"{len(response.get('records'))} internal users added to output")
records.extend(self.add_data_type_field(response.get("records", []), "User"))

if get_user_login_history:
Expand All @@ -133,13 +138,37 @@ def run(self, params={}, state={}): # noqa: C901
if user_login_next_page_id:
state[self.USER_LOGIN_NEXT_PAGE_ID] = user_login_next_page_id
has_more_pages = True

self.logger.info(f"{len(response.get('records'))} users login history added to output")
records.extend(self.add_data_type_field(response.get("records", []), "User Login"))
return convert_to_camel_case(clean(records)), state, has_more_pages, 200, None

if remove_duplicates is True:
records = self.remove_duplicates(records)

return records, state, has_more_pages, 200, None
except ApiException as error:
return [], state, False, error.status_code, error
except Exception as error:
return [], state, False, 500, PluginException(preset=PluginException.Preset.UNKNOWN, data=error)

def remove_duplicates(self, records: list) -> list:
"""
Remove duplicate entries from the provided list of records.
Args:
records (list): A list containing the records to be de-duplicated.
Returns:
list: A list containing only the unique records from the input list.
"""
unique_records = {json.dumps(event, sort_keys=True): event for event in records}
unique_records = list(unique_records.values())
if len(records) != len(unique_records):
self.logger.info(
f"Removed {len(records) - len(unique_records)} duplicate from a total of {len(records)} duplicate records."
)
return unique_records

@staticmethod
def get_current_time() -> datetime:
return datetime.now(timezone.utc)
Expand All @@ -155,5 +184,5 @@ def convert_to_datetime(timestamp: str) -> datetime:
@staticmethod
def add_data_type_field(records: list, field_value: str) -> list:
for record in records:
record["dataType"] = field_value
record["DataType"] = field_value
return records
42 changes: 39 additions & 3 deletions plugins/salesforce/komand_salesforce/util/api.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import requests
import time
from insightconnect_plugin_runtime.exceptions import PluginException
from functools import wraps
from json.decoder import JSONDecodeError
from logging import Logger
from komand_salesforce.util.exceptions import ApiException
from typing import Callable, Any

import requests
from insightconnect_plugin_runtime.exceptions import PluginException
from komand_salesforce.util.endpoints import (
PARAMETERIZED_SEARCH_ENDPOINT,
QUERY_ENDPOINT,
Expand All @@ -14,6 +16,7 @@
SOBJECT_RECORD_FIELD_ENDPOINT,
SOBJECT_UPDATED_USERS,
)
from komand_salesforce.util.exceptions import ApiException


def rate_limiting(max_tries: int):
Expand Down Expand Up @@ -43,6 +46,38 @@ def _wrapper(*args, **kwargs):
return _decorate


def refresh_token(max_tries: int) -> Callable:
"""
Decorator to refresh the token if expired and retry the function.
Args:
- max_tries (int): Maximum number of attempts to refresh the token and retry.
Returns:
- Callable: Wrapped function that retries on token expiry.
"""

def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(self, *args, **kwargs) -> Any:
for _ in range(max_tries):
try:
return func(self, *args, **kwargs)
except ApiException as error:
cause = PluginException.causes.get(PluginException.Preset.INVALID_CREDENTIALS)
if error.cause == cause and "Session expired or invalid" in error.data:
self.logger.info("Token expired, renewing token...")
self.token = None
self.instance_url = None
else:
raise
return func(self, *args, **kwargs)

return wrapper

return decorator


class SalesforceAPI:
RETRY_LIMIT = 5

Expand Down Expand Up @@ -190,6 +225,7 @@ def _get_version(instance_url: str) -> str:
return instance_url

@rate_limiting(10)
@refresh_token(1)
def _make_request(self, method: str, url: str, params: dict = {}, json: dict = {}): # noqa: C901
access_token, instance_url = self._get_token(
self._client_id, self._client_secret, self._username, self._password, self._security_token
Expand Down
4 changes: 2 additions & 2 deletions plugins/salesforce/plugin.spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ products: [insightconnect]
name: salesforce
title: Salesforce
description: The Salesforce plugin allows you to search, update, and manage salesforce records
version: 2.0.2
version: 2.1.0
connection_version: 2
vendor: rapid7
support: community
Expand Down Expand Up @@ -377,6 +377,6 @@ tasks:
users:
title: Users
description: Information about users, their login history and which users have been updated
type: "[]userData"
type: "[]object"
required: true
example: []
2 changes: 1 addition & 1 deletion plugins/salesforce/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@


setup(name="salesforce-rapid7-plugin",
version="2.0.2",
version="2.1.0",
description="The Salesforce plugin allows you to search, update, and manage salesforce records",
author="rapid7",
author_email="",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,28 @@
"type": "User",
"url": "/services/data/v58.0/sobjects/User/005Hn00000H35JtIAJ"
},
"id": "005Hn00000H35JtIAJ",
"firstName": "Example",
"lastName": "User",
"email": "user@example.com",
"alias": "exam",
"isActive": true,
"dataType": "User"
"Id": "005Hn00000H35JtIAJ",
"FirstName": "Example",
"LastName": "User",
"Email": "user@example.com",
"Alias": "exam",
"IsActive": true,
"DataType": "User"
},
{
"attributes": {
"type": "LoginHistory",
"url": "/services/data/v58.0/sobjects/LoginHistory/0YaHn0000EUyGkcKQF"
},
"loginTime": "2023-07-23T16:20:13.000+0000",
"userId": "005Hn00000H35JtIAJ",
"loginType": "Application",
"loginUrl": "example.salesforce.com",
"sourceIp": "198.51.100.1",
"status": "Success",
"application": "Browser",
"browser": "Chrome 115",
"dataType": "User Login"
"LoginTime": "2023-07-23T16:20:13.000+0000",
"UserId": "005Hn00000H35JtIAJ",
"LoginType": "Application",
"LoginUrl": "example.salesforce.com",
"SourceIp": "198.51.100.1",
"Status": "Success",
"Application": "Browser",
"Browser": "Chrome 115",
"DataType": "User Login"
}
]
}
Loading

0 comments on commit db4ecd2

Please sign in to comment.