From f6a3c5140c85f53bfcc2c7a7494b74f9f302580b Mon Sep 17 00:00:00 2001 From: Brett Andrews Date: Wed, 12 Jun 2019 10:02:01 -0700 Subject: [PATCH 1/5] chore: update PR template (#967) --- .github/PULL_REQUEST_TEMPLATE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 04e46e2b4d..b50fa29073 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -9,6 +9,8 @@ - [ ] Write/update tests - [ ] `make pr` passes - [ ] Update documentation +- [ ] Verify transformed template deploys and application functions as expected +- [ ] Add/update example to `examples/2016-10-31` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. From 42a6eea098deb93ae9bc107d81998ab5d10295ad Mon Sep 17 00:00:00 2001 From: Parimal Deshmukh Date: Fri, 21 Jun 2019 02:04:08 +0000 Subject: [PATCH 2/5] Added support for intrinsic if to be used in policies --- samtranslator/model/function_policies.py | 79 ++++++++++++- tests/model/test_function_policies.py | 144 +++++++++++++++++++++++ 2 files changed, 221 insertions(+), 2 deletions(-) diff --git a/samtranslator/model/function_policies.py b/samtranslator/model/function_policies.py index c62e94d19d..181fd7e778 100644 --- a/samtranslator/model/function_policies.py +++ b/samtranslator/model/function_policies.py @@ -4,6 +4,7 @@ from six import string_types from samtranslator.model.intrinsics import is_instrinsic +from samtranslator.model.exceptions import InvalidTemplateException PolicyEntry = namedtuple("PolicyEntry", "data type") @@ -114,8 +115,16 @@ def _get_type(self, policy): # Must handle intrinsic functions. Policy could be a primitive type or an intrinsic function - # Managed policies are either string or an intrinsic function that resolves to a string - if isinstance(policy, string_types) or is_instrinsic(policy): + # Managed policies are of type string + if isinstance(policy, string_types): + return PolicyTypes.MANAGED_POLICY + + # Handle the special case for 'if' intrinsic function + if self._is_intrinsic_if(policy): + return self._get_type_from_intrinsic_if(policy) + + # Intrinsic functions are treated as managed policies by default + if is_instrinsic(policy): return PolicyTypes.MANAGED_POLICY # Policy statement is a dictionary with the key "Statement" in it @@ -143,6 +152,72 @@ def _is_policy_template(self, policy): len(policy) == 1 and \ self._policy_template_processor.has(list(policy.keys())[0]) is True + def _is_intrinsic_if(self, policy): + """ + Is the given policy data an intrinsic if? Intrinsic function 'if' is a dictionary with single + key - if + + :param policy: Input value to check if it is an intrinsic if + :return: True, if yes + """ + if policy is not None \ + and isinstance(policy, dict) \ + and len(policy) == 1: + + key = list(policy.keys())[0] + return key.startswith("Fn::If") + + return False + + def _is_intrinsic_no_value(selfs, policy): + """ + Is the given policy data an intrinsic Ref: AWS::NoValue? Intrinsic function is a dictionary with single + key - Ref and value - AWS::NoValue + + :param policy: Input value to check if it is an intrinsic if + :return: True, if yes + """ + if policy is not None \ + and isinstance(policy, dict) \ + and len(policy) == 1: + + key = list(policy.keys())[0] + return key == "Ref" and policy["Ref"] == "AWS::NoValue" + + return False + + + def _get_type_from_intrinsic_if(self, policy): + """ + Returns the type of the given policy assuming that it is an intrinsic if function + + :param policy: Input value to get type from + :return: PolicyTypes: Type of the given policy. PolicyTypes.UNKNOWN, if type could not be inferred + """ + if not self._is_intrinsic_if(policy): + return PolicyTypes.UNKNOWN + + intrinsic_if_value = policy["Fn::If"] + + if not len(intrinsic_if_value) == 3: + raise InvalidTemplateException("Unexpected number of arguments to intrinsic function If") + + if_data = intrinsic_if_value[1] + else_data = intrinsic_if_value[2] + + if_data_type = self._get_type(if_data) + else_data_type = self._get_type(else_data) + + if if_data_type == else_data_type: + return if_data_type + + if self._is_intrinsic_no_value(if_data): + return else_data_type + + if self._is_intrinsic_no_value(else_data): + return if_data_type + + raise InvalidTemplateException("Could not resolve type of policy in intrinsic function If") class PolicyTypes(Enum): """ diff --git a/tests/model/test_function_policies.py b/tests/model/test_function_policies.py index 371ea70b18..9574b706ca 100644 --- a/tests/model/test_function_policies.py +++ b/tests/model/test_function_policies.py @@ -2,6 +2,7 @@ from unittest import TestCase from samtranslator.model.function_policies import FunctionPolicies, PolicyTypes, PolicyEntry +from samtranslator.model.exceptions import InvalidTemplateException class TestFunctionPolicies(TestCase): @@ -333,3 +334,146 @@ def test_is_policy_template_must_return_false_without_the_processor(self): self.assertFalse(function_policies_obj._is_policy_template(policy)) self.policy_template_processor_mock.has.assert_not_called() + + def test_is_intrinsic_if_must_return_true_for_if(self): + policy = { + "Fn::If": "some value" + } + + self.assertTrue(self.function_policies._is_intrinsic_if(policy)) + + def test_is_intrinsic_if_must_return_false_for_others(self): + too_many_keys = { + "Fn::If": "some value", + "Fn::And": "other value" + } + + not_if = { + "Fn::Or": "some value" + } + + self.assertFalse(self.function_policies._is_intrinsic_if(too_many_keys)) + self.assertFalse(self.function_policies._is_intrinsic_if(not_if)) + self.assertFalse(self.function_policies._is_intrinsic_if(None)) + + def test_is_intrinsic_no_value_must_return_true_for_no_value(self): + policy = { + "Ref": "AWS::NoValue" + } + + self.assertTrue(self.function_policies._is_intrinsic_no_value(policy)) + + def test_is_intrinsic_no_value_must_return_false_for_other_value(self): + bad_key = { + "sRefs": "AWS::NoValue" + } + + bad_value = { + "Ref": "SWA::NoValue" + } + + too_many_keys = { + "Ref": "AWS::NoValue", + "feR": "SWA::NoValue" + } + + self.assertFalse(self.function_policies._is_intrinsic_no_value(bad_key)) + self.assertFalse(self.function_policies._is_intrinsic_no_value(bad_value)) + self.assertFalse(self.function_policies._is_intrinsic_no_value(None)) + self.assertFalse(self.function_policies._is_intrinsic_no_value(too_many_keys)) + + def test_get_type_with_intrinsic_if_must_return_managed_policy_type(self): + managed_policy = { + "Fn::If": ["SomeCondition", "some managed policy arn", "other managed policy arn"] + } + + no_value_if = { + "Fn::If": ["SomeCondition", {"Ref": "AWS::NoValue"}, "other managed policy arn"] + } + + no_value_else = { + "Fn::If": ["SomeCondition", "other managed policy arn", {"Ref": "AWS::NoValue"}] + } + + expected_managed_policy = PolicyTypes.MANAGED_POLICY + + self.assertTrue(expected_managed_policy, self.function_policies._get_type(managed_policy)) + self.assertTrue(expected_managed_policy, self.function_policies._get_type(no_value_if)) + self.assertTrue(expected_managed_policy, self.function_policies._get_type(no_value_else)) + + def test_get_type_with_intrinsic_if_must_return_policy_statement_type(self): + policy_statement = { + "Fn::If": ["SomeCondition", {"Statement": "then statement"}, {"Statement": "else statement"}] + } + + no_value_if = { + "Fn::If": ["SomeCondition", {"Ref": "AWS::NoValue"}, {"Statement": "else statement"}] + } + + no_value_else = { + "Fn::If": ["SomeCondition", {"Statement": "then statement"}, {"Ref": "AWS::NoValue"}] + } + expected_managed_policy = PolicyTypes.POLICY_STATEMENT + + self.assertTrue(expected_managed_policy, self.function_policies._get_type(policy_statement)) + self.assertTrue(expected_managed_policy, self.function_policies._get_type(no_value_if)) + self.assertTrue(expected_managed_policy, self.function_policies._get_type(no_value_else)) + + def test_get_type_with_intrinsic_if_must_return_policy_template_type(self): + policy_template = { + "Fn::If": [ "SomeCondition", + {"template_name_one": { "Param1": "foo"}}, + {"template_name_one": { "Param1": "foo"}} + ] + } + no_value_if = { + "Fn::If": [ "SomeCondition", + {"Ref": "AWS::NoValue"}, + {"template_name_one": { "Param1": "foo"}} + ] + } + no_value_else = { + "Fn::If": [ "SomeCondition", + {"template_name_one": { "Param1": "foo"}}, + {"Ref": "AWS::NoValue"} + ] + } + + expected_managed_policy = PolicyTypes.POLICY_TEMPLATE + self.policy_template_processor_mock.has.return_value = True + function_policies = FunctionPolicies({}, self.policy_template_processor_mock) + + self.assertTrue(expected_managed_policy, function_policies._get_type(policy_template)) + self.assertTrue(expected_managed_policy, function_policies._get_type(no_value_if)) + self.assertTrue(expected_managed_policy, function_policies._get_type(no_value_else)) + + def test_get_type_with_intrinsic_if_must_raise_exception_for_bad_policy(self): + policy_too_few_values = { + "Fn::If": ["condition", "then"] + } + + policy_too_many_values = { + "Fn::If": ["condition", "then", "else", "extra"] + } + + self.assertRaises(InvalidTemplateException, self.function_policies._get_type, policy_too_few_values) + self.assertRaises(InvalidTemplateException, self.function_policies._get_type, policy_too_many_values) + + def test_get_type_with_intrinsic_if_must_raise_exception_for_different_policy_types(self): + policy_one = { + "Fn::If": ["condition", "then", {"Statement": "else"}] + } + policy_two = { + "Fn::If": ["condition", {"Statement": "then"}, "else"] + } + + self.assertRaises(InvalidTemplateException, self.function_policies._get_type, policy_one) + self.assertRaises(InvalidTemplateException, self.function_policies._get_type, policy_two) + + def test_get_type_from_intrinsic_if_must_return_unknown_if_not_intrinsic_if(self): + policy = { + "Not::If": "some value" + } + expected_managed_policy = PolicyTypes.UNKNOWN + + self.assertEquals(expected_managed_policy, self.function_policies._get_type_from_intrinsic_if(policy)) \ No newline at end of file From b108014d44ca9c116295ae30cb22412d9c6df75e Mon Sep 17 00:00:00 2001 From: Parimal Deshmukh Date: Fri, 21 Jun 2019 02:13:29 +0000 Subject: [PATCH 3/5] flake8 fixes --- samtranslator/model/function_policies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samtranslator/model/function_policies.py b/samtranslator/model/function_policies.py index 181fd7e778..d5e91b059a 100644 --- a/samtranslator/model/function_policies.py +++ b/samtranslator/model/function_policies.py @@ -186,7 +186,6 @@ def _is_intrinsic_no_value(selfs, policy): return False - def _get_type_from_intrinsic_if(self, policy): """ Returns the type of the given policy assuming that it is an intrinsic if function @@ -219,6 +218,7 @@ def _get_type_from_intrinsic_if(self, policy): raise InvalidTemplateException("Could not resolve type of policy in intrinsic function If") + class PolicyTypes(Enum): """ Enum of different policy types supported by SAM & this plugin From 8dd8a3490096c137cd38c72a1480747ca08470b3 Mon Sep 17 00:00:00 2001 From: Parimal Deshmukh Date: Tue, 23 Jul 2019 23:26:38 +0000 Subject: [PATCH 4/5] Added intrinsic if support in policy template plugin and sam_resource model. Added end to end tests for intrinsic if. Addressed pr comments. --- samtranslator/model/function_policies.py | 50 +------ samtranslator/model/intrinsics.py | 32 +++++ samtranslator/model/sam_resources.py | 33 ++++- .../policies/policy_templates_plugin.py | 67 +++++++--- tests/model/test_function_policies.py | 29 ++-- ...ction_with_conditional_managed_policy.yaml | 17 +++ ...ional_managed_policy_and_ref_no_value.yaml | 17 +++ ...tion_with_conditional_policy_template.yaml | 19 +++ ...onal_policy_template_and_ref_no_value.yaml | 18 +++ ...ction_with_conditional_managed_policy.json | 71 ++++++++++ ...ional_managed_policy_and_ref_no_value.json | 71 ++++++++++ ...tion_with_conditional_policy_template.json | 126 ++++++++++++++++++ ...onal_policy_template_and_ref_no_value.json | 91 +++++++++++++ ...ction_with_conditional_managed_policy.json | 71 ++++++++++ ...ional_managed_policy_and_ref_no_value.json | 71 ++++++++++ ...tion_with_conditional_policy_template.json | 126 ++++++++++++++++++ ...onal_policy_template_and_ref_no_value.json | 91 +++++++++++++ ...ction_with_conditional_managed_policy.json | 71 ++++++++++ ...ional_managed_policy_and_ref_no_value.json | 71 ++++++++++ ...tion_with_conditional_policy_template.json | 126 ++++++++++++++++++ ...onal_policy_template_and_ref_no_value.json | 91 +++++++++++++ tests/translator/test_translator.py | 4 + 22 files changed, 1281 insertions(+), 82 deletions(-) create mode 100644 tests/translator/input/function_with_conditional_managed_policy.yaml create mode 100644 tests/translator/input/function_with_conditional_managed_policy_and_ref_no_value.yaml create mode 100644 tests/translator/input/function_with_conditional_policy_template.yaml create mode 100644 tests/translator/input/function_with_conditional_policy_template_and_ref_no_value.yaml create mode 100644 tests/translator/output/aws-cn/function_with_conditional_managed_policy.json create mode 100644 tests/translator/output/aws-cn/function_with_conditional_managed_policy_and_ref_no_value.json create mode 100644 tests/translator/output/aws-cn/function_with_conditional_policy_template.json create mode 100644 tests/translator/output/aws-cn/function_with_conditional_policy_template_and_ref_no_value.json create mode 100644 tests/translator/output/aws-us-gov/function_with_conditional_managed_policy.json create mode 100644 tests/translator/output/aws-us-gov/function_with_conditional_managed_policy_and_ref_no_value.json create mode 100644 tests/translator/output/aws-us-gov/function_with_conditional_policy_template.json create mode 100644 tests/translator/output/aws-us-gov/function_with_conditional_policy_template_and_ref_no_value.json create mode 100644 tests/translator/output/function_with_conditional_managed_policy.json create mode 100644 tests/translator/output/function_with_conditional_managed_policy_and_ref_no_value.json create mode 100644 tests/translator/output/function_with_conditional_policy_template.json create mode 100644 tests/translator/output/function_with_conditional_policy_template_and_ref_no_value.json diff --git a/samtranslator/model/function_policies.py b/samtranslator/model/function_policies.py index d5e91b059a..843366e388 100644 --- a/samtranslator/model/function_policies.py +++ b/samtranslator/model/function_policies.py @@ -3,7 +3,7 @@ from six import string_types -from samtranslator.model.intrinsics import is_instrinsic +from samtranslator.model.intrinsics import is_instrinsic, is_intrinsic_if, is_intrinsic_no_value from samtranslator.model.exceptions import InvalidTemplateException PolicyEntry = namedtuple("PolicyEntry", "data type") @@ -120,7 +120,7 @@ def _get_type(self, policy): return PolicyTypes.MANAGED_POLICY # Handle the special case for 'if' intrinsic function - if self._is_intrinsic_if(policy): + if is_intrinsic_if(policy): return self._get_type_from_intrinsic_if(policy) # Intrinsic functions are treated as managed policies by default @@ -152,40 +152,6 @@ def _is_policy_template(self, policy): len(policy) == 1 and \ self._policy_template_processor.has(list(policy.keys())[0]) is True - def _is_intrinsic_if(self, policy): - """ - Is the given policy data an intrinsic if? Intrinsic function 'if' is a dictionary with single - key - if - - :param policy: Input value to check if it is an intrinsic if - :return: True, if yes - """ - if policy is not None \ - and isinstance(policy, dict) \ - and len(policy) == 1: - - key = list(policy.keys())[0] - return key.startswith("Fn::If") - - return False - - def _is_intrinsic_no_value(selfs, policy): - """ - Is the given policy data an intrinsic Ref: AWS::NoValue? Intrinsic function is a dictionary with single - key - Ref and value - AWS::NoValue - - :param policy: Input value to check if it is an intrinsic if - :return: True, if yes - """ - if policy is not None \ - and isinstance(policy, dict) \ - and len(policy) == 1: - - key = list(policy.keys())[0] - return key == "Ref" and policy["Ref"] == "AWS::NoValue" - - return False - def _get_type_from_intrinsic_if(self, policy): """ Returns the type of the given policy assuming that it is an intrinsic if function @@ -193,13 +159,10 @@ def _get_type_from_intrinsic_if(self, policy): :param policy: Input value to get type from :return: PolicyTypes: Type of the given policy. PolicyTypes.UNKNOWN, if type could not be inferred """ - if not self._is_intrinsic_if(policy): - return PolicyTypes.UNKNOWN - intrinsic_if_value = policy["Fn::If"] if not len(intrinsic_if_value) == 3: - raise InvalidTemplateException("Unexpected number of arguments to intrinsic function If") + raise InvalidTemplateException("Fn::If requires 3 arguments") if_data = intrinsic_if_value[1] else_data = intrinsic_if_value[2] @@ -210,13 +173,14 @@ def _get_type_from_intrinsic_if(self, policy): if if_data_type == else_data_type: return if_data_type - if self._is_intrinsic_no_value(if_data): + if is_intrinsic_no_value(if_data): return else_data_type - if self._is_intrinsic_no_value(else_data): + if is_intrinsic_no_value(else_data): return if_data_type - raise InvalidTemplateException("Could not resolve type of policy in intrinsic function If") + raise InvalidTemplateException("Different policy types within the same Fn::If statement is unsupported. " + "Separate different policy types into different Fn::If statements") class PolicyTypes(Enum): diff --git a/samtranslator/model/intrinsics.py b/samtranslator/model/intrinsics.py index 720540b90e..6b5c1c535b 100644 --- a/samtranslator/model/intrinsics.py +++ b/samtranslator/model/intrinsics.py @@ -138,3 +138,35 @@ def is_instrinsic(input): return key == "Ref" or key == "Condition" or key.startswith("Fn::") return False + + +def is_intrinsic_if(policy): + """ + Is the given policy data an intrinsic if? Intrinsic function 'if' is a dictionary with single + key - if + + :param policy: Input value to check if it is an intrinsic if + :return: True, if yes + """ + + if not is_instrinsic(policy): + return False + + key = list(policy.keys())[0] + return key == "Fn::If" + + +def is_intrinsic_no_value(policy): + """ + Is the given policy data an intrinsic Ref: AWS::NoValue? Intrinsic function is a dictionary with single + key - Ref and value - AWS::NoValue + + :param policy: Input value to check if it is an intrinsic if + :return: True, if yes + """ + + if not is_instrinsic(policy): + return False + + key = list(policy.keys())[0] + return key == "Ref" and policy["Ref"] == "AWS::NoValue" diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index 3e6ae61c26..56a29ce087 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -22,6 +22,7 @@ from samtranslator.model.types import dict_of, is_str, is_type, list_of, one_of, any_type from samtranslator.translator import logical_id_generator from samtranslator.translator.arn_generator import ArnGenerator +from samtranslator.model.intrinsics import is_intrinsic_if, is_intrinsic_no_value class SamFunction(SamResourceMacro): @@ -210,10 +211,34 @@ def _construct_role(self, managed_policy_map): if policy_entry.type is PolicyTypes.POLICY_STATEMENT: - policy_documents.append({ - 'PolicyName': execution_role.logical_id + 'Policy' + str(index), - 'PolicyDocument': policy_entry.data - }) + if is_intrinsic_if(policy_entry.data): + + intrinsic_if = policy_entry.data + then_statement = intrinsic_if["Fn::If"][1] + else_statement = intrinsic_if["Fn::If"][2] + + if not is_intrinsic_no_value(then_statement): + then_statement = { + 'PolicyName': execution_role.logical_id + 'Policy' + str(index), + 'PolicyDocument': then_statement + } + intrinsic_if["Fn::If"][1] = then_statement + + if not is_intrinsic_no_value(else_statement): + else_statement = { + 'PolicyName': execution_role.logical_id + 'Policy' + str(index), + 'PolicyDocument': else_statement + } + intrinsic_if["Fn::If"][2] = else_statement + + policy_documents.append(intrinsic_if) + + else: + policy_documents.append({ + 'PolicyName': execution_role.logical_id + 'Policy' + str(index), + 'PolicyDocument': policy_entry.data + }) + elif policy_entry.type is PolicyTypes.MANAGED_POLICY: # There are three options: diff --git a/samtranslator/plugins/policies/policy_templates_plugin.py b/samtranslator/plugins/policies/policy_templates_plugin.py index 9ae515ff2c..50aded6478 100644 --- a/samtranslator/plugins/policies/policy_templates_plugin.py +++ b/samtranslator/plugins/policies/policy_templates_plugin.py @@ -2,6 +2,7 @@ from samtranslator.model.function_policies import FunctionPolicies, PolicyTypes from samtranslator.model.exceptions import InvalidResourceException from samtranslator.policy_template_processor.exceptions import InsufficientParameterValues, InvalidParameterValues +from samtranslator.model.intrinsics import is_intrinsic_if, is_intrinsic_no_value class PolicyTemplatesForFunctionPlugin(BasePlugin): @@ -55,28 +56,60 @@ def on_before_transform_resource(self, logical_id, resource_type, resource_prope result.append(policy_entry.data) continue - # We are processing policy templates. We know they have a particular structure: - # {"templateName": { parameter_values_dict }} - template_data = policy_entry.data - template_name = list(template_data.keys())[0] - template_parameters = list(template_data.values())[0] - - try: - - # 'convert' will return a list of policy statements - result.append(self._policy_template_processor.convert(template_name, template_parameters)) + if is_intrinsic_if(policy_entry.data): + # If policy is an intrinsic if, we need to process each sub-statement separately + processed_intrinsic_if = self._process_intrinsic_if_policy_template(logical_id, policy_entry) + result.append(processed_intrinsic_if) + continue - except InsufficientParameterValues as ex: - # Exception's message will give lot of specific details - raise InvalidResourceException(logical_id, str(ex)) - except InvalidParameterValues: - raise InvalidResourceException(logical_id, - "Must specify valid parameter values for policy template '{}'" - .format(template_name)) + converted_policy = self._process_policy_template(logical_id, policy_entry.data) + result.append(converted_policy) # Save the modified policies list to the input resource_properties[FunctionPolicies.POLICIES_PROPERTY_NAME] = result + def _process_intrinsic_if_policy_template(self, logical_id, policy_entry): + intrinsic_if = policy_entry.data + then_statement = intrinsic_if["Fn::If"][1] + else_statement = intrinsic_if["Fn::If"][2] + + processed_then_statement = then_statement \ + if is_intrinsic_no_value(then_statement) \ + else self._process_policy_template(logical_id, then_statement) + + processed_else_statement = else_statement \ + if is_intrinsic_no_value(else_statement) \ + else self._process_policy_template(logical_id, else_statement) + + processed_intrinsic_if = { + "Fn::If": [ + policy_entry.data["Fn::If"][0], + processed_then_statement, + processed_else_statement + ] + } + + return processed_intrinsic_if + + def _process_policy_template(self, logical_id, template_data): + + # We are processing policy templates. We know they have a particular structure: + # {"templateName": { parameter_values_dict }} + template_name = list(template_data.keys())[0] + template_parameters = list(template_data.values())[0] + try: + + # 'convert' will return a list of policy statements + return self._policy_template_processor.convert(template_name, template_parameters) + + except InsufficientParameterValues as ex: + # Exception's message will give lot of specific details + raise InvalidResourceException(logical_id, str(ex)) + except InvalidParameterValues: + raise InvalidResourceException(logical_id, + "Must specify valid parameter values for policy template '{}'" + .format(template_name)) + def _is_supported(self, resource_type): """ Is this resource supported by this plugin? diff --git a/tests/model/test_function_policies.py b/tests/model/test_function_policies.py index 9574b706ca..a0a82b4bd9 100644 --- a/tests/model/test_function_policies.py +++ b/tests/model/test_function_policies.py @@ -3,6 +3,7 @@ from samtranslator.model.function_policies import FunctionPolicies, PolicyTypes, PolicyEntry from samtranslator.model.exceptions import InvalidTemplateException +from samtranslator.model.intrinsics import is_intrinsic_if, is_intrinsic_no_value class TestFunctionPolicies(TestCase): @@ -340,7 +341,7 @@ def test_is_intrinsic_if_must_return_true_for_if(self): "Fn::If": "some value" } - self.assertTrue(self.function_policies._is_intrinsic_if(policy)) + self.assertTrue(is_intrinsic_if(policy)) def test_is_intrinsic_if_must_return_false_for_others(self): too_many_keys = { @@ -352,16 +353,16 @@ def test_is_intrinsic_if_must_return_false_for_others(self): "Fn::Or": "some value" } - self.assertFalse(self.function_policies._is_intrinsic_if(too_many_keys)) - self.assertFalse(self.function_policies._is_intrinsic_if(not_if)) - self.assertFalse(self.function_policies._is_intrinsic_if(None)) + self.assertFalse(is_intrinsic_if(too_many_keys)) + self.assertFalse(is_intrinsic_if(not_if)) + self.assertFalse(is_intrinsic_if(None)) def test_is_intrinsic_no_value_must_return_true_for_no_value(self): policy = { "Ref": "AWS::NoValue" } - self.assertTrue(self.function_policies._is_intrinsic_no_value(policy)) + self.assertTrue(is_intrinsic_no_value(policy)) def test_is_intrinsic_no_value_must_return_false_for_other_value(self): bad_key = { @@ -377,10 +378,10 @@ def test_is_intrinsic_no_value_must_return_false_for_other_value(self): "feR": "SWA::NoValue" } - self.assertFalse(self.function_policies._is_intrinsic_no_value(bad_key)) - self.assertFalse(self.function_policies._is_intrinsic_no_value(bad_value)) - self.assertFalse(self.function_policies._is_intrinsic_no_value(None)) - self.assertFalse(self.function_policies._is_intrinsic_no_value(too_many_keys)) + self.assertFalse(is_intrinsic_no_value(bad_key)) + self.assertFalse(is_intrinsic_no_value(bad_value)) + self.assertFalse(is_intrinsic_no_value(None)) + self.assertFalse(is_intrinsic_no_value(too_many_keys)) def test_get_type_with_intrinsic_if_must_return_managed_policy_type(self): managed_policy = { @@ -468,12 +469,4 @@ def test_get_type_with_intrinsic_if_must_raise_exception_for_different_policy_ty } self.assertRaises(InvalidTemplateException, self.function_policies._get_type, policy_one) - self.assertRaises(InvalidTemplateException, self.function_policies._get_type, policy_two) - - def test_get_type_from_intrinsic_if_must_return_unknown_if_not_intrinsic_if(self): - policy = { - "Not::If": "some value" - } - expected_managed_policy = PolicyTypes.UNKNOWN - - self.assertEquals(expected_managed_policy, self.function_policies._get_type_from_intrinsic_if(policy)) \ No newline at end of file + self.assertRaises(InvalidTemplateException, self.function_policies._get_type, policy_two) \ No newline at end of file diff --git a/tests/translator/input/function_with_conditional_managed_policy.yaml b/tests/translator/input/function_with_conditional_managed_policy.yaml new file mode 100644 index 0000000000..6bfed9d071 --- /dev/null +++ b/tests/translator/input/function_with_conditional_managed_policy.yaml @@ -0,0 +1,17 @@ +Conditions: + DummyCondition: + !Equals ["", ""] + +Resources: + FunctionWithConditionalPolicy: + Type: AWS::Serverless::Function + Properties: + Description: A function that has Fn::If in the policies property + Handler: hello.handler + Runtime: python2.7 + CodeUri: s3://sam-demo-bucket/hello.zip + Policies: + - !If + - DummyCondition + - !Sub arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess + - !Sub arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess \ No newline at end of file diff --git a/tests/translator/input/function_with_conditional_managed_policy_and_ref_no_value.yaml b/tests/translator/input/function_with_conditional_managed_policy_and_ref_no_value.yaml new file mode 100644 index 0000000000..62f9528016 --- /dev/null +++ b/tests/translator/input/function_with_conditional_managed_policy_and_ref_no_value.yaml @@ -0,0 +1,17 @@ +Conditions: + DummyCondition: + !Equals ["", ""] + +Resources: + FunctionWithConditionalPolicy: + Type: AWS::Serverless::Function + Properties: + Description: A function that has Fn::If in the policies property + Handler: hello.handler + Runtime: python2.7 + CodeUri: s3://sam-demo-bucket/hello.zip + Policies: + - !If + - DummyCondition + - !Sub arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess + - !Ref "AWS::NoValue" \ No newline at end of file diff --git a/tests/translator/input/function_with_conditional_policy_template.yaml b/tests/translator/input/function_with_conditional_policy_template.yaml new file mode 100644 index 0000000000..4b7a0b8fe9 --- /dev/null +++ b/tests/translator/input/function_with_conditional_policy_template.yaml @@ -0,0 +1,19 @@ +Conditions: + DummyCondition: + !Equals ["", ""] + +Resources: + FunctionWithConditionalPolicy: + Type: AWS::Serverless::Function + Properties: + Description: A function that has Fn::If in the policies property + Handler: hello.handler + Runtime: python2.7 + CodeUri: s3://sam-demo-bucket/hello.zip + Policies: + - !If + - DummyCondition + - AWSSecretsManagerGetSecretValuePolicy: + SecretArn: "Dummy Secret Arn" + - AWSSecretsManagerRotationPolicy: + FunctionName: "Dummy Function Name" \ No newline at end of file diff --git a/tests/translator/input/function_with_conditional_policy_template_and_ref_no_value.yaml b/tests/translator/input/function_with_conditional_policy_template_and_ref_no_value.yaml new file mode 100644 index 0000000000..432b4db2ab --- /dev/null +++ b/tests/translator/input/function_with_conditional_policy_template_and_ref_no_value.yaml @@ -0,0 +1,18 @@ +Conditions: + DummyCondition: + !Equals ["", ""] + +Resources: + FunctionWithConditionalPolicy: + Type: AWS::Serverless::Function + Properties: + Description: A function that has Fn::If in the policies property + Handler: hello.handler + Runtime: python2.7 + CodeUri: s3://sam-demo-bucket/hello.zip + Policies: + - !If + - DummyCondition + - AWSSecretsManagerGetSecretValuePolicy: + SecretArn: "Dummy Secret Arn" + - !Ref "AWS::NoValue" \ No newline at end of file diff --git a/tests/translator/output/aws-cn/function_with_conditional_managed_policy.json b/tests/translator/output/aws-cn/function_with_conditional_managed_policy.json new file mode 100644 index 0000000000..f014353300 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_conditional_managed_policy.json @@ -0,0 +1,71 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + { + "Fn::If": [ + "DummyCondition", + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" + }, + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess" + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/function_with_conditional_managed_policy_and_ref_no_value.json b/tests/translator/output/aws-cn/function_with_conditional_managed_policy_and_ref_no_value.json new file mode 100644 index 0000000000..bcd345bf35 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_conditional_managed_policy_and_ref_no_value.json @@ -0,0 +1,71 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + { + "Fn::If": [ + "DummyCondition", + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/function_with_conditional_policy_template.json b/tests/translator/output/aws-cn/function_with_conditional_policy_template.json new file mode 100644 index 0000000000..6cb4ad3aae --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_conditional_policy_template.json @@ -0,0 +1,126 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "Fn::If": [ + "DummyCondition", + { + "PolicyName": "FunctionWithConditionalPolicyRolePolicy0", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Resource": { + "Fn::Sub": [ + "${secretArn}", + { + "secretArn": "Dummy Secret Arn" + } + ] + }, + "Effect": "Allow" + } + ] + } + }, + { + "PolicyName": "FunctionWithConditionalPolicyRolePolicy0", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue", + "secretsmanager:PutSecretValue", + "secretsmanager:UpdateSecretVersionStage" + ], + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*" + }, + "Effect": "Allow", + "Condition": { + "StringEquals": { + "secretsmanager:resource/AllowRotationLambdaArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${functionName}", + { + "functionName": "Dummy Function Name" + } + ] + } + } + } + }, + { + "Action": [ + "secretsmanager:GetRandomPassword" + ], + "Resource": "*", + "Effect": "Allow" + } + ] + } + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/function_with_conditional_policy_template_and_ref_no_value.json b/tests/translator/output/aws-cn/function_with_conditional_policy_template_and_ref_no_value.json new file mode 100644 index 0000000000..39be617ed1 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_conditional_policy_template_and_ref_no_value.json @@ -0,0 +1,91 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "Fn::If": [ + "DummyCondition", + { + "PolicyName": "FunctionWithConditionalPolicyRolePolicy0", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Resource": { + "Fn::Sub": [ + "${secretArn}", + { + "secretArn": "Dummy Secret Arn" + } + ] + }, + "Effect": "Allow" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_conditional_managed_policy.json b/tests/translator/output/aws-us-gov/function_with_conditional_managed_policy.json new file mode 100644 index 0000000000..859c85a8a1 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_conditional_managed_policy.json @@ -0,0 +1,71 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + { + "Fn::If": [ + "DummyCondition", + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" + }, + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess" + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_conditional_managed_policy_and_ref_no_value.json b/tests/translator/output/aws-us-gov/function_with_conditional_managed_policy_and_ref_no_value.json new file mode 100644 index 0000000000..ddcf43ff1a --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_conditional_managed_policy_and_ref_no_value.json @@ -0,0 +1,71 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + { + "Fn::If": [ + "DummyCondition", + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_conditional_policy_template.json b/tests/translator/output/aws-us-gov/function_with_conditional_policy_template.json new file mode 100644 index 0000000000..7a9e12b898 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_conditional_policy_template.json @@ -0,0 +1,126 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "Fn::If": [ + "DummyCondition", + { + "PolicyName": "FunctionWithConditionalPolicyRolePolicy0", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Resource": { + "Fn::Sub": [ + "${secretArn}", + { + "secretArn": "Dummy Secret Arn" + } + ] + }, + "Effect": "Allow" + } + ] + } + }, + { + "PolicyName": "FunctionWithConditionalPolicyRolePolicy0", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue", + "secretsmanager:PutSecretValue", + "secretsmanager:UpdateSecretVersionStage" + ], + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*" + }, + "Effect": "Allow", + "Condition": { + "StringEquals": { + "secretsmanager:resource/AllowRotationLambdaArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${functionName}", + { + "functionName": "Dummy Function Name" + } + ] + } + } + } + }, + { + "Action": [ + "secretsmanager:GetRandomPassword" + ], + "Resource": "*", + "Effect": "Allow" + } + ] + } + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_conditional_policy_template_and_ref_no_value.json b/tests/translator/output/aws-us-gov/function_with_conditional_policy_template_and_ref_no_value.json new file mode 100644 index 0000000000..f3d43297cb --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_conditional_policy_template_and_ref_no_value.json @@ -0,0 +1,91 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "Fn::If": [ + "DummyCondition", + { + "PolicyName": "FunctionWithConditionalPolicyRolePolicy0", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Resource": { + "Fn::Sub": [ + "${secretArn}", + { + "secretArn": "Dummy Secret Arn" + } + ] + }, + "Effect": "Allow" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/function_with_conditional_managed_policy.json b/tests/translator/output/function_with_conditional_managed_policy.json new file mode 100644 index 0000000000..a7aacc2839 --- /dev/null +++ b/tests/translator/output/function_with_conditional_managed_policy.json @@ -0,0 +1,71 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + { + "Fn::If": [ + "DummyCondition", + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" + }, + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess" + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/function_with_conditional_managed_policy_and_ref_no_value.json b/tests/translator/output/function_with_conditional_managed_policy_and_ref_no_value.json new file mode 100644 index 0000000000..056fd49c55 --- /dev/null +++ b/tests/translator/output/function_with_conditional_managed_policy_and_ref_no_value.json @@ -0,0 +1,71 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + { + "Fn::If": [ + "DummyCondition", + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/function_with_conditional_policy_template.json b/tests/translator/output/function_with_conditional_policy_template.json new file mode 100644 index 0000000000..dd145ce751 --- /dev/null +++ b/tests/translator/output/function_with_conditional_policy_template.json @@ -0,0 +1,126 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "Fn::If": [ + "DummyCondition", + { + "PolicyName": "FunctionWithConditionalPolicyRolePolicy0", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Resource": { + "Fn::Sub": [ + "${secretArn}", + { + "secretArn": "Dummy Secret Arn" + } + ] + }, + "Effect": "Allow" + } + ] + } + }, + { + "PolicyName": "FunctionWithConditionalPolicyRolePolicy0", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue", + "secretsmanager:PutSecretValue", + "secretsmanager:UpdateSecretVersionStage" + ], + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*" + }, + "Effect": "Allow", + "Condition": { + "StringEquals": { + "secretsmanager:resource/AllowRotationLambdaArn": { + "Fn::Sub": [ + "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${functionName}", + { + "functionName": "Dummy Function Name" + } + ] + } + } + } + }, + { + "Action": [ + "secretsmanager:GetRandomPassword" + ], + "Resource": "*", + "Effect": "Allow" + } + ] + } + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/function_with_conditional_policy_template_and_ref_no_value.json b/tests/translator/output/function_with_conditional_policy_template_and_ref_no_value.json new file mode 100644 index 0000000000..2d6b987c25 --- /dev/null +++ b/tests/translator/output/function_with_conditional_policy_template_and_ref_no_value.json @@ -0,0 +1,91 @@ +{ + "Conditions": { + "DummyCondition": { + "Fn::Equals": [ + "", + "" + ] + } + }, + "Resources": { + "FunctionWithConditionalPolicy": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "A function that has Fn::If in the policies property", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FunctionWithConditionalPolicyRole", + "Arn" + ] + }, + "Runtime": "python2.7" + } + }, + "FunctionWithConditionalPolicyRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "Fn::If": [ + "DummyCondition", + { + "PolicyName": "FunctionWithConditionalPolicyRolePolicy0", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Resource": { + "Fn::Sub": [ + "${secretArn}", + { + "secretArn": "Dummy Secret Arn" + } + ] + }, + "Effect": "Allow" + } + ] + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 88657b15b5..5629b26dfa 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -220,6 +220,10 @@ class TestTranslatorEndToEnd(TestCase): 'function_with_permissions_boundary', 'function_with_policy_templates', 'function_with_sns_event_source_all_parameters', + 'function_with_conditional_managed_policy', + 'function_with_conditional_managed_policy_and_ref_no_value', + 'function_with_conditional_policy_template', + 'function_with_conditional_policy_template_and_ref_no_value', 'globals_for_function', 'globals_for_api', 'globals_for_simpletable', From f4101cf8b5a604ebcdcc2d852d045295f2b274ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=92=BB=20=F0=9F=92=AA=20=F0=9F=98=89=20James=20Hood?= Date: Tue, 13 Aug 2019 15:19:41 -0700 Subject: [PATCH 5/5] variable renames --- samtranslator/model/intrinsics.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/samtranslator/model/intrinsics.py b/samtranslator/model/intrinsics.py index 6b5c1c535b..11edd9dabf 100644 --- a/samtranslator/model/intrinsics.py +++ b/samtranslator/model/intrinsics.py @@ -140,33 +140,33 @@ def is_instrinsic(input): return False -def is_intrinsic_if(policy): +def is_intrinsic_if(input): """ - Is the given policy data an intrinsic if? Intrinsic function 'if' is a dictionary with single + Is the given input an intrinsic if? Intrinsic function 'if' is a dictionary with single key - if - :param policy: Input value to check if it is an intrinsic if + :param input: Input value to check if it is an intrinsic if :return: True, if yes """ - if not is_instrinsic(policy): + if not is_instrinsic(input): return False - key = list(policy.keys())[0] + key = list(input.keys())[0] return key == "Fn::If" -def is_intrinsic_no_value(policy): +def is_intrinsic_no_value(input): """ - Is the given policy data an intrinsic Ref: AWS::NoValue? Intrinsic function is a dictionary with single + Is the given input an intrinsic Ref: AWS::NoValue? Intrinsic function is a dictionary with single key - Ref and value - AWS::NoValue - :param policy: Input value to check if it is an intrinsic if + :param input: Input value to check if it is an intrinsic if :return: True, if yes """ - if not is_instrinsic(policy): + if not is_instrinsic(input): return False - key = list(policy.keys())[0] - return key == "Ref" and policy["Ref"] == "AWS::NoValue" + key = list(input.keys())[0] + return key == "Ref" and input["Ref"] == "AWS::NoValue"