def to_statement(self, parameter_values): """ With the given values for each parameter, this method will return a policy statement that can be used directly with IAM. :param dict parameter_values: Dict containing values for each parameter defined in the template :return dict: Dictionary containing policy statement :raises InvalidParameterValues: If parameter values is not a valid dictionary or does not contain values for all parameters :raises InsufficientParameterValues: If the parameter values don't have values for all required parameters """ missing = self.missing_parameter_values(parameter_values) if len(missing) > 0: # str() of elements of list to prevent any `u` prefix from being displayed in user-facing error message raise InsufficientParameterValues("Following required parameters of template '{}' don't have values: {}" .format(self.name, [str(m) for m in missing])) # Select only necessary parameter_values. this is to prevent malicious or accidental # injection of values for parameters not intended in the template. This is important because "Ref" resolution # will substitute any references for which a value is provided. necessary_parameter_values = {name: value for name, value in parameter_values.items() if name in self.parameters} # Only "Ref" is supported supported_intrinsics = { RefAction.intrinsic_name: RefAction() } resolver = IntrinsicsResolver(necessary_parameter_values, supported_intrinsics) definition_copy = copy.deepcopy(self.definition) return resolver.resolve_parameter_refs(definition_copy)
def to_statement(self, parameter_values): """ With the given values for each parameter, this method will return a policy statement that can be used directly with IAM. :param dict parameter_values: Dict containing values for each parameter defined in the template :return dict: Dictionary containing policy statement :raises InvalidParameterValues: If parameter values is not a valid dictionary or does not contain values for all parameters :raises InsufficientParameterValues: If the parameter values don't have values for all required parameters """ missing = self.missing_parameter_values(parameter_values) if len(missing) > 0: # str() of elements of list to prevent any `u` prefix from being displayed in user-facing error message raise InsufficientParameterValues( "Following required parameters of template '{}' don't have values: {}" .format(self.name, [str(m) for m in missing])) # Select only necessary parameter_values. this is to prevent malicious or accidental # injection of values for parameters not intended in the template. This is important because "Ref" resolution # will substitute any references for which a value is provided. necessary_parameter_values = { name: value for name, value in parameter_values.items() if name in self.parameters } # Only "Ref" is supported supported_intrinsics = {RefAction.intrinsic_name: RefAction()} resolver = IntrinsicsResolver(necessary_parameter_values, supported_intrinsics) definition_copy = copy.deepcopy(self.definition) return resolver.resolve_parameter_refs(definition_copy)
def test_short_circuit_on_empty_parameters(self): resolver = IntrinsicsResolver({}) resolver._try_resolve_parameter_refs = Mock() # Mock other methods to detect any actual calls input = {"Ref": "foo"} expected = {"Ref": "foo"} self.assertEqual(resolver.resolve_parameter_refs(input), expected) resolver._try_resolve_parameter_refs.assert_not_called()
def test_short_circuit_on_empty_parameters(self): resolver = IntrinsicsResolver({}) resolver._try_resolve_parameter_refs = Mock() # Mock other methods to detect any actual calls input = {"Ref": "foo"} expected = {"Ref": "foo"} self.assertEqual(resolver.resolve_parameter_refs(input), expected) resolver._try_resolve_parameter_refs.assert_not_called()
class TestParameterReferenceResolution(TestCase): def setUp(self): self.parameter_values = { "param1": "value1", "param2": "value2", "param3": "value3" } self.resolver = IntrinsicsResolver(self.parameter_values) def test_must_resolve_top_level_direct_refs(self): input = { "key1": { "Ref": "param1" }, "key2": { "Ref": "param2" }, "key3": { "a": "b" } } expected = { "key1": self.parameter_values["param1"], "key2": self.parameter_values["param2"], "key3": { "a": "b" }, } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_resolve_nested_refs(self): input = { "key1": { "sub1": { "sub2": { "sub3": { "Ref": "param1" }, "list": [1, "b", { "Ref": "param2" }] } } } } expected = { "key1": { "sub1": { "sub2": { "sub3": self.parameter_values["param1"], "list": [1, "b", self.parameter_values["param2"]] } } } } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_resolve_direct_refs(self): input = {"Ref": "param1"} expected = self.parameter_values["param1"] output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_resolve_array_refs(self): input = ["foo", 1, 2, {"Ref": "param1"}] expected = ["foo", 1, 2, self.parameter_values["param1"]] output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_skip_unknown_refs(self): input = {"key1": {"Ref": "someresource"}, "key2": {"Ref": "param1"}} expected = { "key1": { "Ref": "someresource" }, "key2": self.parameter_values["param1"] } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_resolve_inside_sub_strings(self): input = { "Fn::Sub": "prefix ${param1} ${param2} ${param3} ${param1} suffix" } expected = {"Fn::Sub": "prefix value1 value2 value3 value1 suffix"} output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_skip_over_sub_literals(self): input = {"Fn::Sub": "prefix ${!MustNotBeReplaced} suffix"} expected = {"Fn::Sub": "prefix ${!MustNotBeReplaced} suffix"} output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_resolve_refs_inside_other_intrinsics(self): input = { "key1": { "Fn::Join": ["-", [{ "Ref": "param1" }, "some other value"]] } } expected = { "key1": { "Fn::Join": ["-", [self.parameter_values["param1"], "some other value"]] } } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_skip_invalid_values_for_ref(self): input = {"Ref": ["ref cannot have list value"]} expected = {"Ref": ["ref cannot have list value"]} output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_skip_invalid_values_for_sub(self): input = { # Invalid Sub resource, must never be parsed, and must not error out "Fn::Sub": [{ "a": "b" }] } expected = {"Fn::Sub": [{"a": "b"}]} output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_throw_on_empty_parameters(self): with self.assertRaises(TypeError): IntrinsicsResolver(None).resolve_parameter_refs({}) def test_throw_on_non_dict_parameters(self): with self.assertRaises(TypeError): IntrinsicsResolver([1, 2, 3]).resolve_parameter_refs({}) def test_short_circuit_on_empty_parameters(self): resolver = IntrinsicsResolver({}) resolver._try_resolve_parameter_refs = Mock( ) # Mock other methods to detect any actual calls input = {"Ref": "foo"} expected = {"Ref": "foo"} self.assertEqual(resolver.resolve_parameter_refs(input), expected) resolver._try_resolve_parameter_refs.assert_not_called()
class TestParameterReferenceResolution(TestCase): def setUp(self): self.parameter_values = { "param1": "value1", "param2": "value2", "param3": "value3" } self.resolver = IntrinsicsResolver(self.parameter_values) def test_must_resolve_top_level_direct_refs(self): input = { "key1": { "Ref": "param1" }, "key2": { "Ref": "param2" }, "key3": { "a": "b" } } expected = { "key1": self.parameter_values["param1"], "key2": self.parameter_values["param2"], "key3": { "a": "b" } } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_resolve_nested_refs(self): input = { "key1": { "sub1": { "sub2": { "sub3": { "Ref": "param1" }, "list": [1, "b", {"Ref": "param2"}] } } } } expected = { "key1": { "sub1": { "sub2": { "sub3": self.parameter_values["param1"], "list": [1, "b", self.parameter_values["param2"]] } } } } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_resolve_direct_refs(self): input = {"Ref": "param1"} expected = self.parameter_values["param1"] output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_resolve_array_refs(self): input = ["foo", 1, 2, {"Ref": "param1"}] expected = ["foo", 1, 2, self.parameter_values["param1"]] output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_skip_unknown_refs(self): input = { "key1": { "Ref": "someresource" }, "key2": { "Ref": "param1" } } expected = { "key1": { "Ref": "someresource" }, "key2": self.parameter_values["param1"] } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_resolve_inside_sub_strings(self): input = { "Fn::Sub": "prefix ${param1} ${param2} ${param3} ${param1} suffix" } expected = { "Fn::Sub": "prefix value1 value2 value3 value1 suffix" } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_skip_over_sub_literals(self): input = { "Fn::Sub": "prefix ${!MustNotBeReplaced} suffix" } expected = { "Fn::Sub": "prefix ${!MustNotBeReplaced} suffix" } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_must_resolve_refs_inside_other_intrinsics(self): input = { "key1": { "Fn::Join": ["-", [{"Ref": "param1"}, "some other value"]] } } expected = { "key1": { "Fn::Join": ["-", [self.parameter_values["param1"], "some other value"]] } } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_skip_invalid_values_for_ref(self): input = { "Ref": ["ref cannot have list value"] } expected = { "Ref": ["ref cannot have list value"] } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_skip_invalid_values_for_sub(self): input = { # Invalid Sub resource, must never be parsed, and must not error out "Fn::Sub": [{"a": "b"}] } expected = { "Fn::Sub": [{"a": "b"}] } output = self.resolver.resolve_parameter_refs(input) self.assertEqual(output, expected) def test_throw_on_empty_parameters(self): with self.assertRaises(TypeError): IntrinsicsResolver(None).resolve_parameter_refs({}) def test_throw_on_non_dict_parameters(self): with self.assertRaises(TypeError): IntrinsicsResolver([1,2,3]).resolve_parameter_refs({}) def test_short_circuit_on_empty_parameters(self): resolver = IntrinsicsResolver({}) resolver._try_resolve_parameter_refs = Mock() # Mock other methods to detect any actual calls input = {"Ref": "foo"} expected = {"Ref": "foo"} self.assertEqual(resolver.resolve_parameter_refs(input), expected) resolver._try_resolve_parameter_refs.assert_not_called()