Skip to content

Commit

Permalink
PAPP-35185: release notes and further documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
tapishj-splunk committed Feb 3, 2025
1 parent 5fbca98 commit d858ac8
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 13 deletions.
109 changes: 96 additions & 13 deletions ciscosecurefirewall_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def _update_state(self) -> None:
def authenicate_cloud_fmc(self, config: Dict[str, Any]) -> bool:
"""
This method updates the headers and sets the firepower host
based on the users region.
based on the users region when connecting to a cloud FMC.
"""
region = config["region"]
api_key = config["api_key"]
Expand Down Expand Up @@ -268,7 +268,6 @@ def _process_json_response(self, r: Response, action_result: ActionResult) -> Re
None,
)

# Please specify the status codes here
if 200 <= r.status_code < 399:
return RetVal(phantom.APP_SUCCESS, resp_json)

Expand Down Expand Up @@ -320,9 +319,19 @@ def _make_rest_call(
params: Dict[str, Any] = None,
auth: HTTPBasicAuth = None
) -> Tuple[bool, Any]:
"""
This method makes a REST call to the API
"""
"""Function that makes the REST call to the app.
:param method: REST method
:param endpoint: REST endpoint to be called
:param action_result: object of ActionResult class
:param json_body: JSON object
:param headers_only: wether to only return response headers
:param headers: request headers
:param first_try: if the request is eligible to be retried
:param params: request parameters
:param auth: basic auth if passed in. This is only needed with the generatetoken endpoint
:return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message),
response obtained by making an API call
"""
request_method = getattr(requests, method)
url = "https://{0}{1}".format(self.firepower_host, endpoint)
if json_body:
Expand Down Expand Up @@ -357,14 +366,14 @@ def _make_rest_call(
return self._process_response(result, action_result)

def is_cloud_deployment(self) -> bool:
"""Helper to determine if user is connecting to cloud based fmc.
Returns:
bool: If connection is to cloud basd FMC
"""
return self.fmc_type == "Cloud"

def _handle_test_connectivity(self, param: Dict[str, Any]) -> bool:
"""
Called when the user presses the test connectivity
button on the Phantom UI.
"""
# Add an action result to the App Run

action_result = ActionResult(dict(param))
self.add_action_result(action_result)

Expand All @@ -380,6 +389,15 @@ def _handle_test_connectivity(self, param: Dict[str, Any]) -> bool:
return action_result.set_status(phantom.APP_SUCCESS)

def get_network_objects_of_type(self, object_type: str, domain_uuid: str, action_result: ActionResult, name: str = None) -> bool:
"""Helper to get network objects of a particular type.
Args:
object_type (str): Network object type (Network, Host, Range)
domain_uuid (str): Domain to be queried
action_result (ActionResult): object of ActionResult class
name (str): Name of the object
Returns:
bool: If lookup was successfull
"""
url = NETWORK_OBJECTS_ENDPOINT.format(domain_id=domain_uuid, type=object_type.lower() + "s")

offset = 0
Expand Down Expand Up @@ -435,7 +453,14 @@ def _handle_list_network_objects(self, param: Dict[str, Any]) -> bool:

return action_result.set_status(phantom.APP_SUCCESS)

def get_network_object(self, domain_id: int, object_id: int) -> Tuple[bool, Dict[str, Any]]:
def get_network_object(self, domain_id: str, object_id: str) -> Tuple[bool, Dict[str, Any]]:
"""Helper to get a specfic network object.
Args:
domain_uuid (str): Domain to be queried
object_id (str): Id of the object to retrieve
Returns:
tuple: If lookup was successfull and response object
"""
url = NETWORK_OBJECT_ID_ENDPOINT.format(domain_id=domain_id, type="networks", object_id=object_id)
ret_val, response = self._make_rest_call("get", url, self)
return ret_val, response
Expand Down Expand Up @@ -505,8 +530,15 @@ def _handle_delete_network_object(self, param: Dict[str, Any]) -> bool:
return action_result.set_status(phantom.APP_SUCCESS)

def get_domain_id(self, domain_name: str) -> str:
"""Helper to get a domain_name id.
Args:
domain_name (str): Name of domain
Returns:
str: domain_id
"""
domain_name = domain_name or self.default_firepower_domain

# multitenancy on cloud achieved through seperate tenants not domains
if not domain_name or self.is_cloud_deployment():
return "default"

Expand All @@ -516,6 +548,14 @@ def get_domain_id(self, domain_name: str) -> str:
return domain["uuid"]

def list_objects(self, url: str, action_result: ActionResult, expanded: bool = False) -> Tuple[bool, list]:
"""Helper to get list any type of FMC objects (groups, policies, rules).
Args:
url (str): REST endpoint to query
action_result (ActionResult): object of ActionResult class
expanded (bool): Return detailed response of objects
Returns:
tuple: If lookup was successfull and list of objects retrieved
"""
objects = []
offset = 0
limit = 50
Expand Down Expand Up @@ -588,7 +628,14 @@ def _handle_create_network_group(self, param: Dict[str, Any]) -> bool:
summary["Message"] = f"Successfully added network group with name {group_name}"
return action_result.set_status(phantom.APP_SUCCESS)

def get_network_group(self, domain_uuid, group_id):
def get_network_group(self, domain_uuid: str, group_id: str) -> Tuple[bool, Dict[str, Any]]:
"""Helper to get a specfic network group.
Args:
domain_uuid (str): Domain to be queried
group_id (str): Id of the group to retrieve
Returns:
tuple: If lookup was successfull and response object
"""
url = NETWORK_GROUPS_ID_ENDPOINT.format(domain_id=domain_uuid, group_id=group_id)
ret_val, response = self._make_rest_call("get", url, self)
return ret_val, response
Expand Down Expand Up @@ -693,7 +740,14 @@ def _handle_create_access_policy(self, param: Dict[str, Any]) -> bool:
summary["Message"] = f"Successfully created access policy with name {name}"
return action_result.set_status(phantom.APP_SUCCESS)

def get_access_policy(self, domain_uuid, policy_id):
def get_access_policy(self, domain_uuid: str, policy_id: str) -> Tuple[bool, Dict[str, Any]]:
"""Helper to get a specfic access policy.
Args:
domain_uuid (str): Domain to be queried
policy_id (str): Id of the policy to retrieve
Returns:
tuple: If lookup was successfull and response object
"""
url = ACCESS_POLICY_ID_ENDPOINT.format(domain_id=domain_uuid, policy_id=policy_id)
ret_val, response = self._make_rest_call("get", url, self)
return ret_val, response
Expand Down Expand Up @@ -783,6 +837,13 @@ def _handle_get_access_rules(self, param: Dict[str, Any]) -> bool:
return action_result.set_status(phantom.APP_SUCCESS)

def build_network_objects_list(self, network_ids: list, dommain_uuid: str) -> Tuple[bool, Optional[list]]:
"""Helper that classifys and builds a list of network objects and groups.
Args:
network_ids (list): Ids of network objects and groups
domain_uuid (str): Domain to be queried
Returns:
tuple: If lookup was successfull and list of network objects and groups
"""
networks_objects = []
for object_id in network_ids:
ret_val, object_data = self.get_network_object(dommain_uuid, object_id)
Expand Down Expand Up @@ -839,6 +900,14 @@ def _handle_create_access_rules(self, param: Dict[str, Any]) -> bool:
return action_result.set_status(phantom.APP_SUCCESS)

def get_access_control_rule(self, domain_id: str, policy_id: str, rule_id: str) -> Tuple[bool, Dict[str, Any]]:
"""Helper to get a specfic access control rule belonging to a specfic access policy.
Args:
domain_id (str): Domain to be queried
policy_id (str): Id of the policy to retrieve
rule_id (str): Id of the rule to retrieve
Returns:
tuple: If lookup was successfull and response object
"""
url = ACCESS_RULES_ID_ENDPOINT.format(domain_id=domain_id, policy_id=policy_id, rule_id=rule_id)
ret_val, response = self._make_rest_call("get", url, self)
return ret_val, response
Expand Down Expand Up @@ -943,6 +1012,13 @@ def _handle_list_devices(self, param: Dict[str, Any]) -> bool:
return action_result.set_status(phantom.APP_SUCCESS)

def get_deployable_devices(self, domain_id: str, action_result) -> Tuple[bool, Any]:
"""Helper that gets list of devices ready for deployment.
Args:
domain_id (str): Domain to be queried
action_result (ActionResult): object of ActionResult class
Returns:
tuple: If lookup was successfull and list of devices
"""
url = GET_DEPLOYABLE_DEVICES_ENDPOINT.format(domain_id=domain_id)
device_lst = []
ret_val, deployable_devices = self.list_objects(url, action_result, True)
Expand Down Expand Up @@ -1023,6 +1099,13 @@ def _handle_get_deployment_status(self, param: Dict[str, Any]) -> bool:
return action_result.set_status(phantom.APP_SUCCESS)

def get_intrusion_policy(self, domain_uuid: str, policy_id: str) -> Tuple[int, any]:
"""Helper to get a specfic intrusion policy.
Args:
domain_uuid (str): Domain to be queried
policy_id (str): Id of the policy to retrieve
Returns:
tuple: If lookup was successfull and response object
"""
url = INTRUSION_POLICY_ID_ENDPOINT.format(domain_id=domain_uuid, policy_id=policy_id)
ret_val, response = self._make_rest_call("get", url, self)
return ret_val, response
Expand Down
31 changes: 31 additions & 0 deletions manual_readme_content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[comment]: # " File: README.md"
[comment]: # "Copyright (c) 2025 Splunk Inc."
[comment]: # ""
[comment]: # "Licensed under the Apache License, Version 2.0 (the 'License');"
[comment]: # "you may not use this file except in compliance with the License."
[comment]: # "You may obtain a copy of the License at"
[comment]: # ""
[comment]: # " http://www.apache.org/licenses/LICENSE-2.0"
[comment]: # ""
[comment]: # "Unless required by applicable law or agreed to in writing, software distributed under"
[comment]: # "the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,"
[comment]: # "either express or implied. See the License for the specific language governing permissions"
[comment]: # "and limitations under the License."
[comment]: # ""

This connector supports both cloud and on-prem delivered FMC. Below are the steps for connecting to both

## Connecting to a cloud delivered FMC

1. On Cisco Security Cloud Control navigate to User Management
2. Create a new Api Only User with an Admin role
3. Copy the Api key and enter it in the "Api key for cloud delivered FMC" input box in the SOAR Asset Settings page
4. Specfiy Cloud for the type of FMC you are connecting to
5. Specify your region in the "Region your Cisco Security Cloud Control is deployed in" input box and click Save

## Connecting to an on-prem delivered FMC

1. On the SOAR asset setting page select On-prem for the type of FMC you are connecting to
2. Specify the device ip/hostname of your on-prem FMC along with the username and password used ot login to FMC

**Note** that you can optionally specify a default firepower domain that will be queried. You an overide this domain when running an action. In addition, cloud versions of FMC only support the default domain, to achieve multi tenancy you must use seperate tenants.
27 changes: 27 additions & 0 deletions release_notes/unreleased.md
Original file line number Diff line number Diff line change
@@ -1 +1,28 @@
**Unreleased**
* Initial Cisco Secure Firewall connector with the following actions and features:
* Support for both cloud and on-prem delivered FMC
* test connectivity
* list network objects
* create network object
* update network object
* delete network object
* get network groups
* create network group
* update network group
* delete network group
* get access control policies
* create access control policy
* update access control policy
* delete access control policy
* get access control rules
* create access control rule
* update access control rule
* delete access control rules
* list intrusion policies
* create intrusion policy
* update intrusion policy
* delete intrusion policy
* list devices
* get deployable devices
* deploy devices
* get deployment status

0 comments on commit d858ac8

Please sign in to comment.