11# from ..config import iamPolicyAttributes, arnServiceMap, awsAccount
2+ from collections import defaultdict
23import iam_check .config as config
34import enum
45import logging
@@ -81,10 +82,66 @@ def __str__(self) -> str:
8182 obj ["proposed_unknown" ] = json .loads (str (self .proposed_unknown ))
8283 return json .dumps (obj , indent = 4 )
8384
85+ def generatePolicyForLambda (self , resource , permissions_policies ):
86+ action = self .getValue (f"{ resource } .action" )
87+ function_name = self .getValue (f"{ resource } .function_name" )
88+ principal = self .getValue (f"{ resource } .principal" )
89+ source_arn = None
90+ source_account = None
91+ principal_org_id = None
92+ try :
93+ source_arn = self .getValue (f"{ resource } .source_arn" )
94+ except KeyError :
95+ pass
96+ try :
97+ source_account = self .getValue (f"{ resource } .source_account" )
98+ except KeyError :
99+ pass
100+ try :
101+ principal_org_id = self .getValue (f"{ resource } .principal_org_id" )
102+ except KeyError :
103+ pass
104+
105+ policy_statement = {"Effect" : "Allow" , "Action" : action }
106+ if "amazonaws" in principal :
107+ # this is a permission to a service
108+ policy_statement ["Principal" ] = {"Service" : principal }
109+ else :
110+ # otherwise this is a permission to an account
111+ policy_statement ["Principal" ] = {"AWS" : principal }
112+
113+ # just the function name
114+ policy_statement ["Resource" ] = f"arn::lambda:::function:{ function_name } "
115+ # Ref to the supported arguments
116+ # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission
117+
118+ if source_arn is not None :
119+ condition = policy_statement .get ("Condition" , {})
120+ condition ["ArnLike" ] = {"AWS:SourceArn" : source_arn }
121+ policy_statement ["Condition" ] = condition
122+
123+ if source_account is not None :
124+ condition = policy_statement .get ("Condition" , {})
125+ condition ["StringEquals" ] = {"AWS:SourceAccount" : source_account }
126+ policy_statement ["Condition" ] = condition
127+
128+ if principal_org_id is not None :
129+ condition = policy_statement .get ("Condition" , {})
130+ if "StringEquals" not in condition :
131+ condition ["StringEquals" ] = {}
132+ condition ["StringEquals" ]["AWS:PrincipalOrgID" ] = principal_org_id
133+ policy_statement ["Condition" ] = condition
134+
135+ permissions_policies [f"{ resource } .policy" ].append (policy_statement )
136+ return permissions_policies
137+
84138 def findPolicies (self ):
85139 logging .debug ("generating a list of policies in plan" )
86140 policies = {}
87141 resources = self .listResources ()
142+ # used to collect all policy statements related to a resource. Currently, only applicable to aws_lambda_permission
143+ # where each aws_lambda_permission is mapped to a statement.
144+ permissions_policies = defaultdict (list )
88145 for r in resources :
89146 resourceType = r .split ("." )[- 2 ]
90147 if resourceType not in config .iamPolicyAttributes :
@@ -93,6 +150,11 @@ def findPolicies(self):
93150 attributes = config .iamPolicyAttributes [resourceType ]
94151 if isinstance (attributes , str ):
95152 attributes = [attributes ]
153+ if resourceType == "aws_lambda_permission" :
154+ permissions_policies = self .generatePolicyForLambda (
155+ r , permissions_policies
156+ )
157+ continue
96158 for attribute in attributes :
97159 # check if attribute is a base one or inside a block
98160 if "." not in attribute :
@@ -134,6 +196,10 @@ def findPolicies(self):
134196 policies [ref ] = policy
135197 else :
136198 LOGGER .info (f"No policy found at: { ref } " )
199+ for key , statements in permissions_policies .items ():
200+ policy_document = {"Version" : "2012-10-17" , "Statement" : statements }
201+ policies [key ] = json .dumps (policy_document )
202+
137203 for ref in policies .keys ():
138204 LOGGER .debug (f"found policy at { ref } " )
139205
0 commit comments