Skip to content

Commit d17bc09

Browse files
authored
Merge pull request #1602 from awslabs/develop
Chore: Release 1.24.0
2 parents 0a3419e + 153e06b commit d17bc09

File tree

143 files changed

+19503
-180
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+19503
-180
lines changed

Makefile

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,17 @@
1-
NAME=samtranslator27
2-
PYTHON_VERSION=2.7.14
3-
CODE_COVERAGE=95
4-
PYENV := $(shell command -v pyenv 2> /dev/null)
5-
61
target:
72
$(info ${HELP_MESSAGE})
83
@exit 0
9-
10-
# Make sure that pyenv is configured properly and that we can use it in our setup target.
11-
validation:
12-
ifndef PYENV
13-
$(error "make sure pyenv is accessible in your path, (usually by adding to PATH variable in bash_profile, zshrc, or other locations based on your platform) See: https://github.com/pyenv/pyenv#installation for the installation insructions.")
14-
endif
15-
ifndef PYENV_SHELL
16-
$(error "Add 'pyenv init' to your shell to enable shims and autocompletion, (usually by adding to your bash_profile, zshrc, or other locations based on your platform)")
17-
endif
18-
ifndef PYENV_VIRTUALENV_INIT
19-
$(error "Add 'pyenv virtualenv-init' to your shell to enable shims and autocompletion, (usually by adding to your bash_profile, zshrc, or other locations based on your platform)")
20-
endif
21-
22-
install:
23-
$(info [*] Install pyenv using https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer...)
24-
@curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer 2> /dev/null | bash
25-
26-
setup: validation
27-
$(info [*] Download and install python $(PYTHON_VERSION)...)
28-
@pyenv install $(PYTHON_VERSION)
29-
@pyenv local $(PYTHON_VERSION)
30-
$(info [*] Create virtualenv $(NAME) using python $(PYTHON_VERSION)...)
31-
@pyenv virtualenv $(PYTHON_VERSION) $(NAME)
32-
@$(MAKE) activate
33-
34-
activate:
35-
$(info [*] Activate virtualenv $(NAME)...)
36-
$(shell eval "$$(pyenv init -)" && eval "$$(pyenv virtualenv-init -)" && pyenv activate $(NAME) && pyenv local $(NAME))
374

385
init:
39-
$(info [*] Install requirements...)
40-
@pip install -r requirements/dev.txt -r requirements/base.txt
6+
pip install -e '.[dev]'
417

428
test:
43-
$(info [*] Run the unit test with minimum code coverage of $(CODE_COVERAGE)%...)
44-
@pytest --cov samtranslator --cov-report term-missing --cov-fail-under $(CODE_COVERAGE) tests
9+
pytest --cov samtranslator --cov-report term-missing --cov-fail-under 95 tests
4510

4611
black:
47-
rm -f tests/*.pyc samtranslator/*.pyc
4812
black setup.py samtranslator/* tests/* bin/*
4913

5014
black-check:
51-
rm -f tests/*.pyc samtranslator/*.pyc
5215
black --check setup.py samtranslator/* tests/* bin/*
5316

5417
# Command to run everytime you make changes to verify everything works
@@ -62,9 +25,6 @@ define HELP_MESSAGE
6225
Usage: $ make [TARGETS]
6326

6427
TARGETS
65-
install Install pyenv using the pyenv-installer.
66-
setup Download, install and activate a virtualenv for this project.
67-
activate Activate the virtual environment for this project.
6828
init Initialize and install the requirements and dev-requirements for this project.
6929
test Run the Unit tests.
7030
dev Run all development tests after a change.

samtranslator/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.23.0"
1+
__version__ = "1.24.0"

samtranslator/model/iam.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,36 @@ class IAMRole(Resource):
1818

1919

2020
class IAMRolePolicies:
21+
@classmethod
22+
def construct_assume_role_policy_for_service_principal(cls, service_principal):
23+
document = {
24+
"Version": "2012-10-17",
25+
"Statement": [
26+
{"Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": {"Service": [service_principal]},}
27+
],
28+
}
29+
return document
30+
31+
@classmethod
32+
def step_functions_start_execution_role_policy(cls, state_machine_arn, logical_id):
33+
document = {
34+
"PolicyName": logical_id + "StartExecutionPolicy",
35+
"PolicyDocument": {
36+
"Statement": [{"Action": "states:StartExecution", "Effect": "Allow", "Resource": state_machine_arn}]
37+
},
38+
}
39+
return document
40+
41+
@classmethod
42+
def stepfunctions_assume_role_policy(cls):
43+
document = {
44+
"Version": "2012-10-17",
45+
"Statement": [
46+
{"Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": {"Service": ["states.amazonaws.com"]},}
47+
],
48+
}
49+
return document
50+
2151
@classmethod
2252
def cloud_watch_log_assume_role_policy(cls):
2353
document = {
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
from enum import Enum
2+
from collections import namedtuple
3+
4+
from six import string_types
5+
6+
from samtranslator.model.intrinsics import is_intrinsic, is_intrinsic_if, is_intrinsic_no_value
7+
from samtranslator.model.exceptions import InvalidTemplateException
8+
9+
PolicyEntry = namedtuple("PolicyEntry", "data type")
10+
11+
12+
class ResourcePolicies(object):
13+
"""
14+
Class encapsulating the policies property of SAM resources. This class strictly encapsulates the data
15+
and does not take opinions on how to handle them.
16+
17+
There are three types of policies:
18+
- Policy Statements
19+
- AWS or Custom Managed Policy names/arns
20+
- Policy Templates
21+
22+
This class is capable of parsing and detecting the type of the policy. Optionally, if policy template information
23+
is provided to this class, it will detect Policy Templates too.
24+
"""
25+
26+
POLICIES_PROPERTY_NAME = "Policies"
27+
28+
def __init__(self, resource_properties, policy_template_processor=None):
29+
"""
30+
Initialize with policies data from resource's properties
31+
32+
:param dict resource_properties: Dictionary containing properties of this resource
33+
:param policy_template_processor: Optional Instance of PolicyTemplateProcessor that can conclusively detect
34+
if a given policy is a template or not. If not provided, then this class will not detect policy templates.
35+
"""
36+
37+
# This variable is required to get policies
38+
self._policy_template_processor = policy_template_processor
39+
40+
# Build the list of policies upon construction.
41+
self.policies = self._get_policies(resource_properties)
42+
43+
def get(self):
44+
"""
45+
Iterator method that "yields" the next policy entry on subsequent calls to this method.
46+
47+
:yields namedtuple("data", "type"): Yields a named tuple containing the policy data and its type
48+
"""
49+
50+
for policy_tuple in self.policies:
51+
yield policy_tuple
52+
53+
def __len__(self):
54+
return len(self.policies)
55+
56+
def _get_policies(self, resource_properties):
57+
"""
58+
Returns a list of policies from the resource properties. This method knows how to interpret and handle
59+
polymorphic nature of the policies property.
60+
61+
Policies can be one of the following:
62+
63+
* Managed policy name: string
64+
* List of managed policy names: list of strings
65+
* IAM Policy document: dict containing Statement key
66+
* List of IAM Policy documents: list of IAM Policy Document
67+
* Policy Template: dict with only one key where key is in list of supported policy template names
68+
* List of Policy Templates: list of Policy Template
69+
70+
71+
:param dict resource_properties: Dictionary of resource properties containing the policies property.
72+
It is assumed that this is already a dictionary and contains policies key.
73+
:return list of PolicyEntry: List of policies, where each item is an instance of named tuple `PolicyEntry`
74+
"""
75+
76+
policies = None
77+
78+
if self._contains_policies(resource_properties):
79+
policies = resource_properties[self.POLICIES_PROPERTY_NAME]
80+
81+
if not policies:
82+
# Policies is None or empty
83+
return []
84+
85+
if not isinstance(policies, list):
86+
# Just a single entry. Make it into a list of convenience
87+
policies = [policies]
88+
89+
result = []
90+
for policy in policies:
91+
policy_type = self._get_type(policy)
92+
entry = PolicyEntry(data=policy, type=policy_type)
93+
result.append(entry)
94+
95+
return result
96+
97+
def _contains_policies(self, resource_properties):
98+
"""
99+
Is there policies data in this resource?
100+
101+
:param dict resource_properties: Properties of the resource
102+
:return: True if we can process this resource. False, otherwise
103+
"""
104+
return (
105+
resource_properties is not None
106+
and isinstance(resource_properties, dict)
107+
and self.POLICIES_PROPERTY_NAME in resource_properties
108+
)
109+
110+
def _get_type(self, policy):
111+
"""
112+
Returns the type of the given policy
113+
114+
:param string or dict policy: Policy data
115+
:return PolicyTypes: Type of the given policy. None, if type could not be inferred
116+
"""
117+
118+
# Must handle intrinsic functions. Policy could be a primitive type or an intrinsic function
119+
120+
# Managed policies are of type string
121+
if isinstance(policy, string_types):
122+
return PolicyTypes.MANAGED_POLICY
123+
124+
# Handle the special case for 'if' intrinsic function
125+
if is_intrinsic_if(policy):
126+
return self._get_type_from_intrinsic_if(policy)
127+
128+
# Intrinsic functions are treated as managed policies by default
129+
if is_intrinsic(policy):
130+
return PolicyTypes.MANAGED_POLICY
131+
132+
# Policy statement is a dictionary with the key "Statement" in it
133+
if isinstance(policy, dict) and "Statement" in policy:
134+
return PolicyTypes.POLICY_STATEMENT
135+
136+
# This could be a policy template then.
137+
if self._is_policy_template(policy):
138+
return PolicyTypes.POLICY_TEMPLATE
139+
140+
# Nothing matches. Don't take opinions on how to handle it. Instead just set the appropriate type.
141+
return PolicyTypes.UNKNOWN
142+
143+
def _is_policy_template(self, policy):
144+
"""
145+
Is the given policy data a policy template? Policy templates is a dictionary with one key which is the name
146+
of the template.
147+
148+
:param dict policy: Policy data
149+
:return: True, if this is a policy template. False if it is not
150+
"""
151+
152+
return (
153+
self._policy_template_processor is not None
154+
and isinstance(policy, dict)
155+
and len(policy) == 1
156+
and self._policy_template_processor.has(list(policy.keys())[0]) is True
157+
)
158+
159+
def _get_type_from_intrinsic_if(self, policy):
160+
"""
161+
Returns the type of the given policy assuming that it is an intrinsic if function
162+
163+
:param policy: Input value to get type from
164+
:return: PolicyTypes: Type of the given policy. PolicyTypes.UNKNOWN, if type could not be inferred
165+
"""
166+
intrinsic_if_value = policy["Fn::If"]
167+
168+
if not len(intrinsic_if_value) == 3:
169+
raise InvalidTemplateException("Fn::If requires 3 arguments")
170+
171+
if_data = intrinsic_if_value[1]
172+
else_data = intrinsic_if_value[2]
173+
174+
if_data_type = self._get_type(if_data)
175+
else_data_type = self._get_type(else_data)
176+
177+
if if_data_type == else_data_type:
178+
return if_data_type
179+
180+
if is_intrinsic_no_value(if_data):
181+
return else_data_type
182+
183+
if is_intrinsic_no_value(else_data):
184+
return if_data_type
185+
186+
raise InvalidTemplateException(
187+
"Different policy types within the same Fn::If statement is unsupported. "
188+
"Separate different policy types into different Fn::If statements"
189+
)
190+
191+
192+
class PolicyTypes(Enum):
193+
"""
194+
Enum of different policy types supported by SAM & this plugin
195+
"""
196+
197+
MANAGED_POLICY = "managed_policy"
198+
POLICY_STATEMENT = "policy_statement"
199+
POLICY_TEMPLATE = "policy_template"
200+
UNKNOWN = "unknown"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .role_constructor import construct_role_for_resource

0 commit comments

Comments
 (0)