diff --git a/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2.py b/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2.py index 9abc59ddb2eb..8eae698d72b9 100644 --- a/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2.py +++ b/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2.py @@ -1,10 +1,11 @@ +import demistomock as demisto # noqa: F401 +from CommonServerPython import * # noqa: F401 + import enum import http from collections.abc import Callable from typing import Any, TypeVar -import demistomock as demisto # noqa: F401 -from CommonServerPython import * # noqa: F401 BASE_URL = 'https://api.umbrella.com' @@ -83,17 +84,39 @@ def login(self) -> None: Log in to the API using the API key and API secret. The access token is stored in the headers of the request. """ - response = self._http_request( - method='POST', - url_suffix=Client.AUTH_SUFFIX, - auth=(self.api_key, self.api_secret), - ) + access_token = self.get_access_token() + self._headers['Authorization'] = f'Bearer {access_token}' - try: - access_token = response['access_token'] - self._headers['Authorization'] = f'Bearer {access_token}' - except Exception as e: - raise DemistoException(f'Failed logging in: {response}') from e + def get_access_token(self): + """ + Get an access token that was previously created if it is still valid, else, generate a new access token from + the API key and secret. + """ + # Check if there is an existing valid access token + integration_context = get_integration_context() + if integration_context.get('access_token') and integration_context.get('expiry_time') > date_to_timestamp(datetime.now()): + return integration_context.get('access_token') + else: + try: + res = self._http_request( + method='POST', + url_suffix=Client.AUTH_SUFFIX, + headers={'Content-Type': 'application/x-www-form-urlencoded'}, + auth=(self.api_key, self.api_secret), + data={'grant_type': 'client_credentials'} + ) + if res.get('access_token'): + expiry_time = date_to_timestamp(datetime.now(), date_format='%Y-%m-%dT%H:%M:%S') + expiry_time += res.get('expires_in', 0) * 1000 - 10 + context = { + 'access_token': res.get('access_token'), + 'expiry_time': expiry_time + } + set_integration_context(context) + return res.get('access_token') + except Exception as e: + return_error(f'Error occurred while creating an access token. Please check the instance configuration.' + f'\n\n{e.args[0]}') def _get_destination_payload( self, diff --git a/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2.yml b/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2.yml index 77601f3559c7..41c154ea74ad 100644 --- a/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2.yml +++ b/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2.yml @@ -29,27 +29,18 @@ script: - name: destination_list_id description: The ID of the destination list. Destination lists can be fetched with the `umbrella-destination-lists-list` command. required: true - isArray: false - name: destination_ids description: Comma-separated list of destination IDs to be retrieved from a list of destinations. - required: false isArray: true - name: destinations description: Comma-separated list of destinations to retrieve, a destination may be a domain, URL, or IP address. - required: false isArray: true - name: page description: Page number of paginated results. Minimum 1; Default 1. - required: false - isArray: false - name: page_size description: The number of items per page. Minimum 1; Maximum 100; Default 50. - required: false - isArray: false - name: limit description: The number of items per page. Minimum 1. - required: false - isArray: false defaultValue: '50' outputs: - type: String @@ -73,15 +64,11 @@ script: - name: destination_list_id description: The ID of the destination list. Destination lists can be fetched with the `umbrella-destination-lists-list` command. required: true - isArray: false - name: destinations description: Comma-separated list of destinations. A destination may be a URL, IPv4, CIDR or fully qualified domain name. required: true - isArray: false - name: comment description: A comment about all the inserted destinations. - required: false - isArray: false defaultValue: Added from XSOAR outputs: - type: Number @@ -126,7 +113,6 @@ script: - name: destination_list_id description: The ID of the destination list. Destination lists can be fetched with the `umbrella-destination-lists-list` command. required: true - isArray: false - name: destination_ids description: Comma-separated list of destination IDs. Destinations can be fetched with the `umbrella-destination-list` command. required: true @@ -173,20 +159,12 @@ script: arguments: - name: destination_list_id description: The ID of the destination list to retrieve. - required: false - isArray: false - name: page description: Page number of paginated results. Minimum 1; Default 1. - required: false - isArray: false - name: page_size description: The number of items per page. Minimum 1; Maximum 100; Default 50. - required: false - isArray: false - name: limit description: The maximum number of records to retrieve. Minimum 1. - required: false - isArray: false defaultValue: '50' outputs: - type: Number @@ -242,8 +220,6 @@ script: arguments: - name: bundle_type description: The type of the Umbrella policy associated with the destination list. If the field is not specified, the default value is 'DNS'. - required: false - isArray: false auto: PREDEFINED predefined: - 'DNS' @@ -251,7 +227,6 @@ script: - name: access description: 'The type of access for the destination list. Valid values are "allow" or "block". Accepted types for destination list with the access "allow" are: DOMAIN, IPv4 and CIDR. Accepted types for destination list with the access "block" are: URL and DOMAIN.' required: true - isArray: false auto: PREDEFINED predefined: - 'allow' @@ -259,7 +234,6 @@ script: - name: is_global description: Specifies whether the destination list is a global destination list. There is only one default destination list of type 'allow' or 'block' for an organization. required: true - isArray: false auto: PREDEFINED predefined: - 'True' @@ -267,15 +241,11 @@ script: - name: name description: The name of the destination list. required: true - isArray: false - name: destinations description: Comma-separated list of destinations. A destination may be a URL, IPv4, CIDR or fully qualified domain name. - required: false isArray: true - name: destinations_comment description: A comment about all the inserted destinations. - required: false - isArray: false defaultValue: Added from XSOAR outputs: - type: Number @@ -320,11 +290,9 @@ script: - name: destination_list_id description: The ID of the destination list. Destination lists can be fetched with the `umbrella-destination-lists-list` command. required: true - isArray: false - name: name description: The name of the destination list. required: true - isArray: false outputs: - type: Number contextPath: Umbrella.DestinationLists.id @@ -368,7 +336,6 @@ script: - name: destination_list_id description: The ID of the destination list. Destination lists can be fetched with the `umbrella-destination-lists-list` command. required: true - isArray: false - arguments: - description: Organization ID. name: orgId diff --git a/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2_test.py b/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2_test.py index fb8439a00df7..6596eb1bd967 100644 --- a/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2_test.py +++ b/Packs/Cisco-umbrella-cloud-security/Integrations/CiscoUmbrellaCloudSecurityv2/CiscoUmbrellaCloudSecurityv2_test.py @@ -59,7 +59,7 @@ def test_list_destinations_command(requests_mock, mock_client): - A destination list ID When: - - list_destinations_command + - Running the umbrella-destinations-list command. Then: - Ensure that the CommandResults outputs_prefix is correct. @@ -111,7 +111,7 @@ def test_list_destinations_command_fetch_destinations(requests_mock, mock_client - A destination list ID When: - - list_destinations_command + - Running the umbrella-destinations-list command. Then: - Ensure that the CommandResults outputs_prefix is correct. @@ -168,7 +168,7 @@ def test_add_destinations_command(requests_mock, mock_client): - A destination list ID and destinations When: - - add_destinations_command + - Running the umbrella-destination-add command. Then: - Ensure that the CommandResults raw_response is correct. @@ -206,7 +206,7 @@ def test_delete_destination_command(requests_mock, mock_client): - A destination list ID and destination IDs When: - - delete_destination_command + - Running the umbrella-destination-delete command. Then: - Ensure that the CommandResults readable_output is correct. @@ -243,7 +243,7 @@ def test_list_destination_lists_command(requests_mock, mock_client): - A destination list ID When: - - list_destination_lists_command + - Running the umbrella-destination-lists-list command. Then: - Ensure that the CommandResults outputs_prefix is correct. @@ -280,7 +280,7 @@ def test_list_destination_lists_command_list_request(requests_mock, mock_client) - Nothing When: - - list_destination_lists_command + - Running the umbrella-destination-lists-list command. Then: - Ensure that the CommandResults outputs_prefix is correct. @@ -314,7 +314,7 @@ def test_create_destination_list_command(requests_mock, mock_client): for a new destination list When: - - create_destination_list_command + - Running the umbrella-destination-list-create command. Then: - Ensure that the CommandResults outputs_prefix is correct. @@ -356,7 +356,7 @@ def test_update_destination_list_command(requests_mock, mock_client): - A destination list ID and a new name When: - - update_destination_list_command + - Running the umbrella-destination-list-update command. Then: - Ensure that the CommandResults outputs_prefix is correct. @@ -418,3 +418,29 @@ def test_delete_destination_list_command(requests_mock, mock_client): assert command_results.readable_output == expected_readable_output assert command_results.raw_response == response + + +def test_get_access_token(requests_mock, mock_client): + """ + Scenario: + - Test the flow of getting an access token + When: + - Running the get_access_token method. + Then: + - Ensure that an access token is returned. + """ + + response = { + "token_type": "bearer", + "access_token": "Pichu", + "expires_in": 3600 + } + requests_mock.post( + url=f'{CiscoUmbrellaCloudSecurityv2.BASE_URL}/auth/v2/token', + json=response + ) + + access_token = CiscoUmbrellaCloudSecurityv2.Client.get_access_token(mock_client) + + assert access_token == response.get('access_token') + diff --git a/Packs/Cisco-umbrella-cloud-security/ReleaseNotes/2_0_9.md b/Packs/Cisco-umbrella-cloud-security/ReleaseNotes/2_0_9.md new file mode 100644 index 000000000000..d43362467191 --- /dev/null +++ b/Packs/Cisco-umbrella-cloud-security/ReleaseNotes/2_0_9.md @@ -0,0 +1,6 @@ + +#### Integrations + +##### Cisco Umbrella Cloud Security v2 + +- Improved implementation of retrieving the *Access Token* in the authentication process. diff --git a/Packs/Cisco-umbrella-cloud-security/pack_metadata.json b/Packs/Cisco-umbrella-cloud-security/pack_metadata.json index 26375fc39992..ed744f5f0bb9 100644 --- a/Packs/Cisco-umbrella-cloud-security/pack_metadata.json +++ b/Packs/Cisco-umbrella-cloud-security/pack_metadata.json @@ -2,7 +2,7 @@ "name": "Cisco Umbrella cloud security", "description": "Basic integration with Cisco Umbrella that allows you to add domains to destination lists (e.g. global block / allow)", "support": "xsoar", - "currentVersion": "2.0.8", + "currentVersion": "2.0.9", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex", "email": "", @@ -18,4 +18,4 @@ "marketplacev2" ], "certification": "certified" -} \ No newline at end of file +}