AWSApiModule

AWS Client class, provides generic Infrastructure to all AWS integrations.

python · ApiModules

Details

IDAWSApiModule
Languagepython
From Version5.5.0
Docker Imagedemisto/boto3py3:1.0.0.110635
Tagsinfra server
from unittest.mock import MagicMock

import pytest
from AWSApiModule import *
from pytest import raises  # noqa: PT013

VALIDATE_CASES = [
    {
        "aws_default_region": "test",
        "aws_role_arn": "test",
        "aws_role_session_name": "test",
        "aws_access_key_id": "test",
        "aws_secret_access_key": "test",
    },
    {
        "aws_default_region": "region test",
        "aws_role_arn": None,
        "aws_role_session_name": None,
        "aws_access_key_id": None,
        "aws_secret_access_key": None,
    },
    {
        "aws_default_region": "region test",
        "aws_role_arn": None,
        "aws_role_session_name": None,
        "aws_access_key_id": "test",
        "aws_secret_access_key": "test",
    },
]


VALIDATE_CASES_MISSING_PARAMS = [
    (
        {
            "aws_default_region": "region test",
            "aws_role_arn": None,
            "aws_role_session_name": None,
            "aws_access_key_id": None,
            "aws_secret_access_key": "secret key test",
        },
        "You must provide Access Key id and Secret key id to configure the instance with credentials.",
    ),
    (
        {
            "aws_default_region": None,
            "aws_role_arn": None,
            "aws_role_session_name": None,
            "aws_access_key_id": "access key test",
            "aws_secret_access_key": None,
        },
        "You must specify AWS default region.",
    ),
    (
        {
            "aws_default_region": "region test",
            "aws_role_arn": "example",
            "aws_role_session_name": None,
            "aws_access_key_id": None,
            "aws_secret_access_key": None,
        },
        "Role session name is required when using role ARN.",
    ),
]


@pytest.mark.parametrize("params, raised_message", VALIDATE_CASES_MISSING_PARAMS)
def test_validate_params_with_missing_values(mocker, params, raised_message):
    """
    Given
    - Different missing configuration parameters
    When
    - Before creating AWSClient object
    Then
    - Validates that exception is thrown properly with each missing parameter
    """

    with raises(DemistoException) as exception:
        validate_params(**params)

    assert raised_message == str(exception.value)


@pytest.mark.parametrize("params", VALIDATE_CASES)
def test_validate_params(mocker, params):
    """
    Given
    - Different valid configuration parameters
    When
    - Before creating AWSClient object
    Then
    - Validates that all parameters are given for each authentication method
    """
    validate_params(**params)


def test_get_timeout():
    """
    Given
    - Different valid values for timeout and retries parameters
    When
    - before setting aws config instance
    Then
    - validates the logic of setting read_timeout and connect_timeout values
    """
    (read, connect) = AWSClient.get_timeout(None)
    assert read == 60
    assert connect == 10
    (read, connect) = AWSClient.get_timeout("100")
    assert read == 100
    assert connect == 10
    (read, connect) = AWSClient.get_timeout("200,2")
    assert read == 200
    assert connect == 2
    (read, connect) = AWSClient.get_timeout(60)
    assert read == 60
    assert connect == 10
    (read, connect) = AWSClient.get_timeout("60, 10")  # testing for unicode variable
    assert read == 60
    assert connect == 10


def test_AWSClient_with_session_token():
    """
    Given
        - Creates new class instance object with aws session token
    When
        - After creating the client instance
    Then
        - Checks if class object contains aws_session_token argument and creates a session
    """

    aws_client_args = {
        "aws_default_region": "us-east-1",
        "aws_role_arn": None,
        "aws_role_session_name": None,
        "aws_role_session_duration": None,
        "aws_role_policy": None,
        "aws_access_key_id": "test_access_key",
        "aws_secret_access_key": "test_secret_key",
        "aws_session_token": "test_sts_token",
        "verify_certificate": False,
        "timeout": 60,
        "retries": 3,
    }

    client = AWSClient(**aws_client_args)

    if client:
        assert client.aws_session_token
        assert client.aws_access_key_id
        assert client.aws_secret_access_key

        try:
            session = client.aws_session("s3")
            assert session
        except Exception:
            print("failed to create session:" + Exception)  # noqa: T201


def test_AWSClient_without_session_token():
    """
    Given
        - Creates new class instance object with aws session token
    When
        - After creating the client instance
    Then
        - Checks if class object contains aws_session_token argument and creates a session
    """
    # Purposfully leaving out aws_session_token to test optional argument in class instance
    aws_client_args = {
        "aws_default_region": "us-east-1",
        "aws_role_arn": None,
        "aws_role_session_name": None,
        "aws_role_session_duration": None,
        "aws_role_policy": None,
        "aws_access_key_id": "test_access_key",
        "aws_secret_access_key": "test_secret_key",
        "verify_certificate": False,
        "timeout": 60,
        "retries": 3,
    }

    client = AWSClient(**aws_client_args)

    if client and client.aws_session_token is None:
        assert client.aws_access_key_id
        assert client.aws_secret_access_key

        try:
            session = client.aws_session("s3")
            assert session
        except Exception:
            print("failed to create session:" + Exception)  # noqa: T201


@pytest.mark.parametrize(
    "secret_key, session_token, expected",
    [
        ("secret_key@@@session_token", None, ("secret_key", "session_token")),
        ("test1", None, ("test1", None)),
        ("test1", "test2", ("test1", "test2")),
        ("test1@@@test2", "test3", ("test1@@@test2", "test3")),
        ("", None, ("", None)),
        (None, "", (None, "")),
        (None, None, (None, None)),
    ],
)
def test_extract_session_from_secret(secret_key, session_token, expected):
    """
    Given
    - Secret key and session token

    When
    - Calling the extract_session_from_secret function

    Then
    - Check that the function returns the expected secret key and session token
    """
    result = extract_session_from_secret(secret_key, session_token)

    assert result == expected


@pytest.mark.parametrize(
    "params, args, expected_assume_roles_args",
    [
        (
            {
                "aws_default_region": "us-east-1",
                "aws_role_arn": None,
                "aws_role_session_name": None,
                "aws_access_key_id": "test_access_key",
                "aws_role_session_duration": None,
            },
            {"role_arn": "role_arn_arg", "role_session_name": "role_session_name_arg"},
            {"RoleArn": "role_arn_arg", "RoleSessionName": "role_session_name_arg"},
        ),
        (
            {
                "aws_default_region": "us-east-1",
                "aws_role_arn": "role_arn_param",
                "aws_role_session_name": "role_session_name_param",
                "aws_access_key_id": "test_access_key",
                "aws_role_session_duration": None,
            },
            {},
            {"RoleArn": "role_arn_param", "RoleSessionName": "role_session_name_param"},
        ),
        (
            {
                "aws_default_region": "us-east-1",
                "aws_role_arn": "role_arn_param",
                "aws_role_session_name": "role_session_name_param",
                "aws_access_key_id": "test_access_key",
                "aws_role_session_duration": None,
            },
            {"role_arn": "role_arn_arg", "role_session_name": "role_session_name_arg"},
            {"RoleArn": "role_arn_arg", "RoleSessionName": "role_session_name_arg"},
        ),
        (
            {
                "aws_default_region": "us-east-1",
                "aws_role_arn": "role_arn_param",
                "aws_role_session_name": "role_session_name_param",
                "aws_access_key_id": "test_access_key",
                "aws_role_session_duration": "",
            },
            {"role_arn": "role_arn_arg", "role_session_name": "role_session_name_arg"},
            {"RoleArn": "role_arn_arg", "RoleSessionName": "role_session_name_arg"},
        ),
    ],
)
def test_aws_session(mocker, params, args, expected_assume_roles_args):
    """
    Given
    - Case A: role arn and role session name provided only from demisto.args()
    - Case B: role arn and role session name provided only from demisto.params()
    - Case C: role arn and role session name provided from both demisto.params() and demisto.args()
    - Case D: role arn, role session name and aws role session duration which is empty
              provided from both demisto.params() and demisto.args()

    When
    - Calling the aws_session method

    Then
    - Case A: make sure the role arn and role session name are taken from command arguments
    - Case B: make sure the role arn and role session name are taken from integration parameters
    - Case C: make sure the role arn and role session name are taken from command arguments
    - Case D: make sure the role arn and role session name are taken from command arguments and no exception is raised
              when sending empty aws role session
    """
    params.update(
        {
            "aws_role_policy": None,
            "aws_secret_access_key": "test_secret_key",
            "verify_certificate": False,
            "timeout": 60,
            "retries": 3,
        }
    )

    sts_client_mock = boto3.client("sts", region_name=params["aws_default_region"])
    assume_client_mock = mocker.patch.object(
        sts_client_mock,
        "assume_role",
        return_value={"Credentials": {"AccessKeyId": "1", "SecretAccessKey": "2", "SessionToken": "3"}},
    )

    mocker.patch(
        "AWSApiModule.boto3.client", side_effect=[sts_client_mock, boto3.client("ec2", region_name=params["aws_default_region"])]
    )
    aws_client = AWSClient(**params)
    aws_client.aws_session(service="ec2", **args)

    assert assume_client_mock.call_args_list[0].kwargs == expected_assume_roles_args


@pytest.mark.parametrize("sts_regional_endpoint", ["legacy", "regional", ""])
def test_sts_regional_endpoint_param(mocker, sts_regional_endpoint):
    """
    Given
        - Configuration param to set in the 'AWS_STS_REGIONAL_ENDPOINTS' variable.
    When
        - After creating the AWS client instance.
    Then
        - Verify the environment variable was sets correctly.
    """
    params = {
        "aws_default_region": "us-east-1",
        "aws_role_arn": "role_arn_param",
        "aws_role_session_name": "role_session_name_param",
        "aws_access_key_id": "test_access_key",
        "aws_role_session_duration": None,
        "aws_role_policy": None,
        "aws_secret_access_key": "test_secret_key",
        "verify_certificate": False,
        "timeout": 60,
        "retries": 3,
    }

    mocker.patch.object(demisto, "params", return_value={"sts_regional_endpoint": sts_regional_endpoint})
    os.environ["AWS_STS_REGIONAL_ENDPOINTS"] = ""
    AWSClient(**params)
    assert os.environ["AWS_STS_REGIONAL_ENDPOINTS"] == sts_regional_endpoint


@pytest.mark.parametrize(
    "params, region, expected_sts_endpoint_url",
    [
        (
            {
                "aws_default_region": "us-west-1",
                "aws_role_arn": "role_arn_param",
                "aws_role_session_name": "role_session_name_param",
                "aws_access_key_id": "test_access_key",
                "aws_role_session_duration": None,
                "sts_endpoint_url": None,
                "aws_role_policy": None,
                "aws_secret_access_key": "test_secret_key",
                "verify_certificate": False,
                "timeout": 60,
                "retries": 3,
            },
            "us-east-1",
            None,
        ),
        (
            {
                "aws_default_region": "us-gov-west-1",
                "aws_role_arn": "role_arn_param",
                "aws_role_session_name": "role_session_name_param",
                "aws_access_key_id": "test_access_key",
                "aws_role_session_duration": None,
                "sts_endpoint_url": None,
                "aws_role_policy": None,
                "aws_secret_access_key": "test_secret_key",
                "verify_certificate": False,
                "timeout": 60,
                "retries": 3,
            },
            "us-gov-east-1",
            "https://sts.us-gov-east-1.amazonaws.com",
        ),
        (
            {
                "aws_default_region": "us-gov-east-1",
                "aws_role_arn": "role_arn_param",
                "aws_role_session_name": "role_session_name_param",
                "aws_access_key_id": "test_access_key",
                "aws_role_session_duration": None,
                "sts_endpoint_url": None,
                "aws_role_policy": None,
                "aws_secret_access_key": "test_secret_key",
                "verify_certificate": False,
                "timeout": 60,
                "retries": 3,
            },
            "us-gov-east-1",
            "https://sts.us-gov-east-1.amazonaws.com",
        ),
    ],
)
def test_aws_session_sts_endpoint_url(mocker, params, region, expected_sts_endpoint_url):
    """
    Given
    - A region parameter and its expected sts_endpoint_url.

    When
    - Calling the aws_session method with the specified region.

    Then
    - Verify that the sts_endpoint_url is set correctly based on the region.
    """
    sts_client_mock = MagicMock()
    mocker.patch.object(
        sts_client_mock,
        "assume_role",
        return_value={"Credentials": {"AccessKeyId": "1", "SecretAccessKey": "2", "SessionToken": "3"}},
    )
    boto3_client_mock = mocker.patch("AWSApiModule.boto3.client")
    boto3_client_mock.side_effect = [MagicMock(), MagicMock()]
    aws_client = AWSClient(**params)
    aws_client.aws_session(service="ec2", region=region)
    assert aws_client.sts_endpoint_url == expected_sts_endpoint_url
    sts_call_args = boto3_client_mock.call_args_list[0]
    assert sts_call_args[1] == {
        "service_name": "sts",
        "region_name": region if region else params["aws_default_region"],
        "aws_access_key_id": params["aws_access_key_id"],
        "aws_secret_access_key": params["aws_secret_access_key"],
        "verify": params["verify_certificate"],
        "config": aws_client.config,
        "endpoint_url": expected_sts_endpoint_url,
    }


@pytest.mark.parametrize(
    "sts_region, region, expected_sts_region, aws_access_key_id, aws_secret_access_key",
    [
        ("eu-west-1", "us-west-2", "eu-west-1", "test_access_key", "test_secret_key"),
        (None, "us-west-2", "us-west-2", "test_access_key", "test_secret_key"),
        (None, None, "us-east-1", "test_access_key", "test_secret_key"),
        ("eu-west-1", "us-west-2", "eu-west-1", None, None),
    ],
)
def test_aws_session_sts_region(mocker, sts_region, region, expected_sts_region, aws_access_key_id, aws_secret_access_key):
    """
    Given
    - Case A: sts_region is explicitly provided.
    - Case B: sts_region is not provided, but region is passed to aws_session.
    - Case C: Neither sts_region nor region is provided.
    - Case D: sts_region is provided, but no aws_access_key_id is provided (Role ARN login).

    When
    - Calling the aws_session method.

    Then
    - Case A: Verify that the sts client is created with the sts_region.
    - Case B: Verify that the sts client is created with the region passed to aws_session.
    - Case C: Verify that the sts client is created with the aws_default_region.
    - Case D: Verify that the sts client is created with the sts_region.
    """
    params = {
        "aws_default_region": "us-east-1",
        "aws_role_arn": "role_arn_param",
        "aws_role_session_name": "role_session_name_param",
        "aws_role_session_duration": None,
        "sts_endpoint_url": None,
        "aws_role_policy": None,
        "verify_certificate": False,
        "timeout": 60,
        "retries": 3,
        "sts_region": sts_region,
        "aws_access_key_id": aws_access_key_id,
        "aws_secret_access_key": aws_secret_access_key,
    }

    sts_client_mock = MagicMock()
    mocker.patch.object(
        sts_client_mock,
        "assume_role",
        return_value={"Credentials": {"AccessKeyId": "1", "SecretAccessKey": "2", "SessionToken": "3"}},
    )
    boto3_client_mock = mocker.patch("AWSApiModule.boto3.client")
    boto3_client_mock.side_effect = [MagicMock(), MagicMock()]
    aws_client = AWSClient(**params)
    aws_client.aws_session(service="ec2", region=region)

    sts_call_args = boto3_client_mock.call_args_list[0]
    assert sts_call_args[1]["region_name"] == expected_sts_region