Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOARHELP-2878: Duplicate data coming in action 'list resources' #14

Merged
merged 31 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
739cd36
Commit with debugging codes and comments
ishans-crest Feb 9, 2024
df8733b
SOARHELP-2878: Handle max limit in all scenarios(last page, not last …
ishans-crest Feb 12, 2024
1094b67
Update README.md
Feb 12, 2024
4860fde
SOARHELP-2878: Bigfix code
ishans-crest Feb 12, 2024
b59ae18
Merge branch 'SOARHELP-2878' of https://github.com/splunk-soar-connec…
ishans-crest Feb 12, 2024
f87605a
SOARHELP-2878: Remove params argument, use params in query parameter
ishans-crest Feb 12, 2024
e5a255e
Revert "SOARHELP-2878: Remove params argument, use params in query pa…
ishans-crest Feb 12, 2024
d888692
Change Min Phantom Version and copyright year
ishans-crest Feb 14, 2024
c09aa24
Update README.md
Feb 14, 2024
c596e72
change version for installing in cloud instance(There is a bug in aut…
ishans-crest Feb 14, 2024
6b30dcb
Merge branch 'SOARHELP-2878' of https://github.com/splunk-soar-connec…
ishans-crest Feb 14, 2024
4425e1f
Update README.md
Feb 14, 2024
46dc16b
Change minor version and change number of resources per page to 100
ishans-crest Feb 15, 2024
7875271
Merge branch 'SOARHELP-2878' of https://github.com/splunk-soar-connec…
ishans-crest Feb 15, 2024
13745fa
Update README.md
Feb 15, 2024
3871323
remove unwanted variable
ishans-crest Mar 14, 2024
3ace7d4
Merge branch 'SOARHELP-2878' of https://github.com/splunk-soar-connec…
ishans-crest Mar 14, 2024
a6c75a8
updated error messages for test connectivity
aparekh-crest Mar 29, 2024
976865b
precommit changes
aparekh-crest Mar 29, 2024
bea0674
removed unused library
aparekh-crest Mar 29, 2024
e50832c
added documentation for sponsor account and release notes
aparekh-crest Apr 3, 2024
89c012f
Update README.md
Apr 3, 2024
5fef53e
Handle None value for items in ERS response
aparekh-crest Apr 12, 2024
fec940b
Solve lint error
aparekh-crest Apr 12, 2024
3c77e02
Handle page limit and absence of SearchResult i the response
ishans-crest Apr 15, 2024
7410419
Update debug message for 'Maximum limit reached'
ishans-crest Apr 15, 2024
830552d
Bugfix in const file and validate_integer function. Update min_phanto…
ishans-crest Apr 19, 2024
5aa5248
Update README.md
Apr 19, 2024
7cc021f
Update latest tested version
ishans-crest Apr 24, 2024
aade450
Review comments - update const variable names, make code pattern cons…
ishans-crest Apr 29, 2024
2852920
Update date of latest tested version
ishans-crest Apr 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/phantomcyber/dev-cicd-tools
rev: v1.16
rev: v1.17
hooks:
- id: org-hook
- id: package-app-dependencies
Expand Down
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright (c) 2014-2023 Splunk Inc.
Copyright (c) 2014-2024 Splunk Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -198,4 +198,4 @@
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.
2 changes: 1 addition & 1 deletion NOTICE
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Splunk SOAR Cisco ISE
Copyright (c) 2014-2023 Splunk Inc.
Copyright (c) 2014-2024 Splunk Inc.

Third-party Software Attributions:

Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
# Cisco ISE

Publisher: Splunk
Connector Version: 3.0.2
Connector Version: 3.1.0
Product Vendor: Cisco Systems
Product Name: Cisco ISE
Product Version Supported (regex): "/([2].[67])|([3].[01])/"
Minimum Product Version: 5.1.0
Minimum Product Version: 6.2.0

This app implements investigative and containment actions on a Cisco ISE device

[comment]: # " File: README.md"
[comment]: # " Copyright (c) 2014-2023 Splunk Inc."
[comment]: # " Copyright (c) 2014-2024 Splunk Inc."
[comment]: # ""
[comment]: # " SPLUNK CONFIDENTIAL - Use or disclosure of this material in whole or in part"
[comment]: # " without a valid written license from Splunk Inc. is PROHIBITED."
Expand Down Expand Up @@ -52,7 +52,9 @@ This app implements investigative and containment actions on a Cisco ISE device
- update resource
- apply policy
- create policy
3. An ISE node can assume any or all of the following personas: Administration, Policy Service, and
3. If resource is **Guest User** in resource related actions, it is required to use **Sponsor Account** credentials to access the GuestAPI, For creating sponsor account refer this document: [Set Up Admin and Sponsor Account for ERS](https://www.cisco.com/c/en/us/support/docs/security/identity-services-engine/215476-configure-ise-guest-accounts-with-rest-a.html)
4. Once you have internal user created from step #3, Add username and password in **ers_username** and **ers_password** in asset configuration respectively.
5. An ISE node can assume any or all of the following personas: Administration, Policy Service, and
Monitoring. For detailed info: [Types of
nodes](https://www.cisco.com/en/US/docs/security/ise/1.0/user_guide/ise10_dis_deploy.html#wp1123452)
- All actions can run on Administration node.
Expand All @@ -61,7 +63,7 @@ This app implements investigative and containment actions on a Cisco ISE device
node
- Actions quarantine device, unquarantine device, apply policy, clear policy, and terminate
session can run on Policy Service node
4. For create resource action, user needs to provide valid json with required fields of that
6. For create resource action, user needs to provide valid json with required fields of that
specified resource (For more details head over to [API
Reference](https://developer.cisco.com/docs/identity-services-engine/v1/#!endpoint) ). Examples
as below
Expand Down
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File: __init__.py
#
# Copyright (c) 2014-2023 Splunk Inc.
# Copyright (c) 2014-2024 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
12 changes: 6 additions & 6 deletions ciscoise.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@
"publisher": "Splunk",
"type": "network security",
"main_module": "ciscoise_connector.py",
"app_version": "3.0.2",
"app_version": "3.1.0",
"utctime_updated": "2022-03-11T04:16:56.000000Z",
"package_name": "phantom_ciscoise",
"product_vendor": "Cisco Systems",
"product_name": "Cisco ISE",
"product_version_regex": "/([2].[67])|([3].[01])/",
"min_phantom_version": "5.1.0",
"min_phantom_version": "6.2.0",
"logo": "logo_cisco.svg",
"logo_dark": "logo_cisco_dark.svg",
"license": "Copyright (c) 2014-2023 Splunk Inc.",
"license": "Copyright (c) 2014-2024 Splunk Inc.",
"python_version": "3",
"fips_compliant": true,
"latest_tested_version": [
"Cisco ISE version 3.0.0.458, 22th Feb 2022"
"Cisco ISE version 3.0.0.458, 24 Apr 2024"
],
"configuration": {
"device": {
Expand Down Expand Up @@ -1921,12 +1921,12 @@
},
{
"module": "setuptools",
"input_file": "wheels/py3/setuptools-69.0.2-py3-none-any.whl"
"input_file": "wheels/py3/setuptools-69.2.0-py3-none-any.whl"
},
{
"module": "xmltodict",
"input_file": "wheels/shared/xmltodict-0.12.0-py2.py3-none-any.whl"
}
]
}
}
}
149 changes: 104 additions & 45 deletions ciscoise_connector.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File: ciscoise_connector.py
#
# Copyright (c) 2014-2023 Splunk Inc.
# Copyright (c) 2014-2024 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,7 @@
#
# Phantom imports
import json
import sys

import phantom.app as phantom
import requests
Expand Down Expand Up @@ -49,7 +50,6 @@ class CiscoISEConnector(BaseConnector):
ACTION_ID_DELETE_POLICY = "delete_policy"

def __init__(self):

# Call the BaseConnectors init first
super(CiscoISEConnector, self).__init__()

Expand Down Expand Up @@ -116,14 +116,16 @@ def make_another_call(*args, **kwargs):

return make_another_call

def _call_ers_api(self, endpoint, action_result, data=None, allow_unknown=True, method="get", try_ha_device=False):
def _call_ers_api(self, endpoint, action_result, data=None, allow_unknown=True, method="get", try_ha_device=False, params=None):
auth_method = self._ers_auth or self._auth
if not auth_method:
return action_result.set_status(phantom.APP_ERROR, CISCOISE_ERS_CRED_MISSING), None
url = "{0}{1}".format(self._base_url, endpoint)
if try_ha_device:
url = "{0}{1}".format(self._ha_device_url, endpoint)

self.debug_print("url for calling an ERS API: {}".format(url))

ret_data = None

config = self.get_config()
Expand All @@ -140,8 +142,10 @@ def _call_ers_api(self, endpoint, action_result, data=None, allow_unknown=True,
json=data,
verify=verify,
headers=headers,
auth=auth_method
auth=auth_method,
params=params
)

except Exception as e:
self.debug_print("Exception occurred: {}".format(e))
return action_result.set_status(phantom.APP_ERROR, CISCOISE_ERROR_REST_API, e), ret_data
Expand Down Expand Up @@ -424,44 +428,43 @@ def _terminate_session(self, param):

return action_result.set_status(phantom.APP_SUCCESS, CISCOISE_SUCC_SESSION_TERMINATED)

def _paginator(self, endpoint, action_result, payload=None, limit=None):
def _paginator(self, endpoint, action_result, limit=None):

items_list = list()

if not payload:
payload = {}

page = 1
payload["size"] = DEFAULT_MAX_RESULTS
payload["page"] = page
params = {}
if limit:
params["size"] = min(DEFAULT_MAX_RESULTS, limit)
else:
params["size"] = DEFAULT_MAX_RESULTS

while True:
ret_val, items = self._call_ers_api(endpoint, action_result, data=payload)

ret_val, items = self._call_ers_api(endpoint, action_result, params=params)
if phantom.is_fail(ret_val):
self.debug_print("Call to ERS API Failed")
return None
items_from_page = items.get("SearchResult", {}).get("resources", [])

items_list.extend(items.get("SearchResult", {}).get("resources"))
items_list.extend(items_from_page)
self.debug_print("Retrieved {} records from the endpoint {}".format(len(items_from_page), endpoint))

next_page_dict = items.get("SearchResult", {}).get("nextPage")

if limit and len(items_list) >= limit:
self.debug_print("Maximum limit reached")
return items_list[:limit]

if len(items.get("SearchResult", {}).get("resources")) < DEFAULT_MAX_RESULTS:
break

if len(items_list) == items.get("SearchResult", {}).get("total"):
break

page = page + 1
payload["page"] = page

return items_list
else:
if next_page_dict is None:
ishans-crest marked this conversation as resolved.
Show resolved Hide resolved
self.debug_print("No more records left to retrieve")
return items_list
else:
endpoint = next_page_dict.get("href").replace(self._base_url, "")
self.debug_print("Next page available")

def _list_resources(self, param):

action_result = self.add_action_result(ActionResult(dict(param)))
resource = self._map_resource_type(param["resource"], action_result)
ret_val, max_result = self._validate_integers(action_result, param.get("max_results"), 'max_result')
ret_val, max_result = self._validate_integers(action_result, param.get("max_results"), 'max results')

if phantom.is_fail(ret_val):
return action_result.get_status()
Expand Down Expand Up @@ -707,30 +710,39 @@ def _test_connectivity_to_device(self, base_url, verify=True):
auth=self._auth,
verify=verify)
except Exception as e:
self.debug_print("Exception is test connectivity: {}".format(e))
return self.set_status_save_progress(phantom.APP_ERROR, CISCOISE_ERROR_TEST_CONNECTIVITY_FAILED)
return False, str(e)

if resp.status_code == 200:
return self.set_status_save_progress(phantom.APP_SUCCESS, CISCOISE_SUCC_TEST_CONNECTIVITY_PASSED)
else:
return self.set_status_save_progress(
phantom.APP_ERROR,
CISCOISE_TEST_CONNECTIVITY_FAILED_ERROR_CODE,
code=resp.status_code
)
return True, ''

def _test_connectivity(self, param):
return False, resp.text

def _test_connectivity(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
config = self.get_config()
verify = config[phantom.APP_JSON_VERIFY]
self.save_progress("Connecting to first device")
result = self._test_connectivity_to_device(self._base_url, verify)
result, message = self._test_connectivity_to_device(self._base_url, verify)

if not result:
self.save_progress("Error occurred while connecting to first device")
self.save_progress(str(message))
self.save_progress(CISCOISE_ERROR_TEST_CONNECTIVITY_FAILED_1)
action_result.set_status(phantom.APP_ERROR)
else:
self.save_progress(CISCOISE_SUCC_TEST_CONNECTIVITY_PASSED_1)
action_result.set_status(phantom.APP_SUCCESS, CISCOISE_SUCC_TEST_CONNECTIVITY_PASSED_1)

if self._ha_device:
self.save_progress("Connecting to second device")
result = self._test_connectivity_to_device(self._ha_device_url, verify)
result, message = self._test_connectivity_to_device(self._ha_device_url, verify)

return result
if not result:
self.save_progress("Error occurred while connecting to high availability device")
self.save_progress(str(message))
self.save_progress(CISCOISE_ERROR_TEST_CONNECTIVITY_FAILED_2)
else:
self.save_progress(CISCOISE_SUCC_TEST_CONNECTIVITY_PASSED_2)

return action_result.get_status()

def handle_action(self, param):

Expand Down Expand Up @@ -775,17 +787,60 @@ def handle_action(self, param):
return result


if __name__ == "__main__":
if __name__ == '__main__':

import sys
import argparse

import pudb

pudb.set_trace()

argparser = argparse.ArgumentParser()

argparser.add_argument('input_test_json', help='Input Test JSON file')
argparser.add_argument('-u', '--username', help='username', required=False)
argparser.add_argument('-p', '--password', help='password', required=False)
argparser.add_argument('-v', '--verify', action='store_true', help='verify', required=False, default=False)

args = argparser.parse_args()
session_id = None

username = args.username
password = args.password
verify = args.verify

if username is not None and password is None:

# User specified a username but not a password, so ask
import getpass
password = getpass.getpass("Password: ")

if username and password:
login_url = BaseConnector._get_phantom_base_url() + "login"
try:
print("Accessing the Login page")
r = requests.get(login_url, verify=verify, timeout=30)
csrftoken = r.cookies['csrftoken']

data = dict()
data['username'] = username
data['password'] = password
data['csrfmiddlewaretoken'] = csrftoken

headers = dict()
headers['Cookie'] = 'csrftoken=' + csrftoken
headers['Referer'] = login_url

print("Logging into Platform to get the session id")
r2 = requests.post(login_url, verify=verify, data=data, headers=headers, timeout=30)
session_id = r2.cookies['sessionid']
except Exception as e:
print("Unable to get session id from the platfrom. Error: " + str(e))
sys.exit(1)

if len(sys.argv) < 2:
print("No test json specified as input")
sys.exit(1)
sys.exit(0)

with open(sys.argv[1]) as f:
in_json = f.read()
Expand All @@ -794,6 +849,10 @@ def handle_action(self, param):

connector = CiscoISEConnector()
connector.print_progress_message = True

if session_id is not None:
in_json['user_session_token'] = session_id

ret_val = connector._handle_action(json.dumps(in_json), None)
print(json.dumps(json.loads(ret_val), indent=4))

Expand Down
Loading
Loading