def GetResources(client, api_id): response_get_resources = client.get_resources(restApiId=api_id) resources = response_get_resources['items'] resources_dict = {} for resource in resources: #print(resource) resource_id = resource['id'] resource_path = resource['path'] methods = [] if resource_path != '/': # avoid root path, which don't have methods resource_methods = resource['resourceMethods'] for method in resource_methods: methods.append(method) resource = resource_path.split('/') length = len(resource) if length == 3: resource = resource[-1].replace('{', '') resource = resource.replace('}', '') elif length == 4: resource = resource[-2].replace('{', '') + '_' + resource[-1] resource = resource.replace('}', '') elif length == 2 and resource[-1] != '': resource = resource[-1] if type(resource) == str: temp_lst = [resource_id, methods] resources_dict[resource] = temp_lst logger.generatedDebug('API Resource Dictionary', json.dumps(resources_dict)) return resources_dict
def GetFeatureStageFuncs(stg_var_name_lst): # get stage name stg_name = input('Enter new feature stage sprint number: ') try: stg_name = int(stg_name) except: logger.inputError('Stage sprint number must be numeric') stg_name = 'feature-sprint' + str(stg_name) logger.inputTrace('Stage Name', stg_name) print ('List of stage variables:') length = len(stg_var_name_lst) for l in range(length): print(str(l+1)+'.'+ stg_var_name_lst[l], end=" ") print('') stg_vars_get = input('Enter method name/number-responsible party, separate by comma, or type all for all:') stg_vars_get = stg_vars_get.replace(" ", "") logger.inputTrace('Method Selection', stg_vars_get) if stg_vars_get.lower() == 'all': l = len(stg_var_name_lst) stg_vars_get = [] for i in range(l): stg_vars_get.append(str(i+1)) else: stg_vars_get = stg_vars_get.split(',') stg_vars_updated = {} if stg_vars_get[0].lower() != 'all': for var in stg_vars_get: methodName = '' if var.isnumeric(): methodNum = int(var) - 1 methodName = stg_var_name_lst[methodNum] elif '_' in var: var = var.lower() methodName = var.split('_') methodName = var.replace(methodName[-1], methodName[-1].upper()) if methodName not in stg_var_name_lst: logger.inputError('Method name you enter is not a known method') os.abort() else: logger.inputError('Cannot identify the method number/name') lambdaName = stg_name + '-api-' + methodName.lower().replace('_','-') stg_vars_updated[methodName] = lambdaName else: for var in stg_var_name_lst: methodName = var lambdaName = stg_name + '-api-' + methodName.lower().replace('_','-').replace('-id', '_id') stg_vars_updated[methodName] = lambdaName logger.generatedDebug('Feature Functions', json.dumps(stg_vars_updated)) return (stg_vars_updated)
def GetSourceArn(api_id, function_name, method, resource_path): source_arn = function_name.replace('lambda', 'execute-api') temp = api_id + '/*/' source_arn = source_arn.replace('function:', temp) source_arn = source_arn.split('${')[0] source_arn = source_arn + method resource_path = re.sub("\{\w*}",'*', resource_path) # logger.runTrace('Regex resource path', resource_path) source_arn = source_arn + resource_path # print(source_arn) logger.generatedDebug('source arn', source_arn) return (source_arn)
def GetFunctionName(client_api, api_id, resource_id, http_method): logger.runTrace('for resource_method', resource_id + '_' + http_method) response = client_api.get_integration( restApiId = api_id, resourceId = resource_id, httpMethod = http_method ) logger.runTrace('get integration', json.dumps(response)) function_name ='' # avoid errors with option methods if response['type'] == 'AWS': function_name = response['uri'].rsplit('/', 1)[0] function_name = function_name.split('arn:')[2] function_name = 'arn:' + function_name logger.generatedDebug('Raw Function Name', function_name) return(function_name)
def GetResourcesDict(resources, client_api, api_id): resources_dict = {} for resource in resources: #print(resource) resource_id = resource['id'] resource_path = resource['path'] methods = [] # this gives a dict {resource_id:[[method, statement_id, resource_path],...]} if resource_path != '/': # avoid root path, which don't have methods resource_methods = resource['resourceMethods'] for method in resource_methods: method_statement_id = [] method_statement_id.append(method) #T generate pesudo statement id based on the method statement_id = resource_path.replace('/{', '-') statement_id = statement_id.replace('}/', '-') statement_id = statement_id.replace('}', '-') statement_id = statement_id.replace('_', '-') statement_id = statement_id.replace('/', '') statement_id = statement_id + '-' + method.lower() method_statement_id.append(statement_id) method_statement_id.append(resource_path) methods.append(method_statement_id) resources_dict[resource_id] = methods logger.generatedDebug('Resource Dictionary', json.dumps(resources_dict)) for id, methods in resources_dict.items(): value_sets = [] for method in methods: if method[0] != 'OPTIONS': value_set = [] method_function_pair = {} function_name = GetFunctionName(client_api, api_id, id, method[0]) method_function_pair[method[0]] = function_name value_set.append(method_function_pair) value_set.append(method[1]) source_arn = GetSourceArn(api_id, function_name, method[0], method[2]) value_set.append(source_arn) value_sets.append(value_set) logger.runTrace('value set', json.dumps(value_set)) resources_dict[id] = value_sets return (resources_dict)
def GetDevProdStageFunc(stg_var_name_lst): dev_funcs = [] for name in stg_var_name_lst: method = name.split('_')[-1] val = name.replace('_' + method, '-' + method.lower()) dev_funcs.append('dev-' + val) # print(val) prod_funcs = [] for name in stg_var_name_lst: method = name.split('_')[-1] val = name.replace('_' + method, '-' + method.lower()) prod_funcs.append('prod-' + val) dev_prod_funcs = list(zip(dev_funcs, prod_funcs)) dev_prod_stg_funcs = dict(zip(stg_var_name_lst, dev_prod_funcs)) logger.generatedDebug('Development and Production Stage Functions:', dev_prod_stg_funcs) return (dev_prod_stg_funcs)
def PutOPTIONSMethod(client, api_id, resource_id): logger.runTrace('Create OPTIONS Method for resource', resource_id) response_put_option = client.put_method( restApiId=api_id, resourceId=resource_id, httpMethod='OPTIONS', authorizationType= 'NONE' #NONE for open access, COGNITO_USER_POOLS for cognito ) logger.generatedDebug('OPTIONS Method created', json.dumps(response_put_option)) response_put_integration = client.put_integration( restApiId=api_id, resourceId=resource_id, httpMethod='OPTIONS', type='MOCK', requestTemplates={'application/json': '{"statusCode": 200}' } #this is needed so options will return 200 ) logger.generatedDebug('OPTIONS Integration set type', 'MOCK') response_put_method_response = client.put_method_response( restApiId=api_id, resourceId=resource_id, httpMethod='OPTIONS', statusCode='200') logger.generatedDebug('OPTIONS Method Response set status Code', '200')
logger.inputError('Cannot identify the method number/name') lambdaName = branch + '-' + methodName.lower().replace('_','-') if branch not in ['dev', 'prod']: partyName = 'api' lambdaName = branch + '-' + partyName + '-' + methodName.lower().replace('_','-') lambdaName = lambdaName.replace('_','-') lambdaName = lambdaName.replace('-id', '_id') stg_vars_updated[methodName] = lambdaName for sv, ln in stg_vars_updated.items(): logger.generatedDebug('Stage Variables', sv) logger.generatedDebug('Lambda Function Name', ln) # all none updated stg_vars connect to dev functions ln = ln.replace('_','-') ln = ln.replace('-id', '_id') stg_vars[sv] = ln # generate stage description description = input('Enter description for the new stage: ') logger.inputTrace('Description', description) if description == '': description = stg_name + 'for Key Management app api development.' #LOG stage variables and description logger.generatedDebug('Stage Description',
def main(): status_code_pattern = { '200': 'Default', '201': 'Created', '204': 'No Content', '400': 'Bad Request', '404': 'Not Found', '403': 'Forbidden', '405': 'Not Allowed', '500': 'Server Error' } for status_code, pattern in status_code_pattern.items(): if pattern.lower() != 'default': status_code_pattern[ status_code] = '.*' + status_code_pattern[status_code] + '.*' logger.generatedDebug('Status Code Keywords', json.dumps(status_code_pattern)) responseTemplates = { 'application/json': '#set($inputRoot = $input.path(\'$\'))\n$input.json("$")\n#if($inputRoot.toString().contains("Created"))\n#set($context.responseOverride.status = 201)\n#end\n#if($inputRoot.toString().contains("No Content"))\n#set($context.responseOverride.status = 204)\n#end\n#if($inputRoot.toString().contains("Bad Request"))\n#set($context.responseOverride.status = 400)\n#end\n#if($inputRoot.toString().contains("Forbidden"))\n#set($context.responseOverride.status = 403)\n#end\n#if($inputRoot.toString().contains("Not Found"))\n#set($context.responseOverride.status = 404)\n#end\n#if($inputRoot.toString().contains("Not Allowed"))\n#set($context.responseOverride.status = 405)\n#end\n#if($inputRoot.toString().contains("Server Error"))\n#set($context.responseOverride.status = 500)\n#end' } #* set logger logger.setLogger('api_gateway_cors_enable.log') #* get boto3 client client = boto3.client('apigateway') #* get api key with default to prod api_id = GetAPIId() #* get a dict of resources for the API {resource_name:resource_id,[methods]} resources_dict = GetResources(client, api_id) #* if resource does not have OPTIONS method, add OPTIONS to resource for each for resource in resources_dict: resource_id = resources_dict[resource][0] methods = resources_dict[resource][1] if 'OPTIONS' not in methods: methods.append('OPTIONS') PutOPTIONSMethod(client, api_id, resource_id) resources_dict[resource][1] = methods #* set Response Headers response_headers = [ 'X-Requested-With', 'Access-Control-Allow-Headers', 'Access-Control-Allow-Origin', 'Access-Control-Allow-Methods' ] resp_headers_methods = [] resp_headers_integration = [] for header in response_headers: resp_headers_methods.append('method.response.header.' + header) resp_headers_integration.append('integration.response.header.' + header) print('Method Headers:') print(resp_headers_methods) print('Integration Headers:') print(resp_headers_integration) XRW_val = "'*'" ACAH_val = "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-requested-with'" ACAO_val = "'https://www.2edusite.com'" #"'*'" headers_vals = (XRW_val, ACAH_val, ACAO_val) method_respPara = {} for header in resp_headers_methods: method_respPara[header] = True print('Method Parameters: ') print(method_respPara) PutCORSResponds(client, api_id, resources_dict, method_respPara, resp_headers_methods, headers_vals, status_code_pattern, responseTemplates)
def PutCORSResponds(client, api_id, resources_dict, method_respPara, resp_headers_methods, headers_vals, status_code_pattern, responseTemplates): for resource in resources_dict: logger.runTrace('for resource', resource) resource_id = resources_dict[resource][0] logger.runTrace('resource', resource) methods = resources_dict[resource][1] methods_str = ','.join(methods) methods_str = "'" + methods_str + "'" logger.runTrace('with methods', methods_str) integration_mapping = [] for header_val in headers_vals: integration_mapping.append(header_val) integration_mapping.append(methods_str) logger.generatedDebug('Integration Mapping', json.dumps(integration_mapping)) integration_respPara = {} integration_respPara = dict( zip(resp_headers_methods, integration_mapping)) logger.generatedDebug('Integration Parameters', json.dumps(integration_respPara)) for method in methods: logger.runTrace('for method', method) #* Get status codes response_get_method = client.get_method(restApiId=api_id, resourceId=resource_id, httpMethod=method) logger.generatedDebug('Method Detail', json.dumps(response_get_method)) method_responses = response_get_method['methodResponses'] logger.runTrace('Method Responses', json.dumps(method_responses)) status_codes = list(method_responses.keys()) logger.runTrace('Status Codes', json.dumps(status_codes)) #* if status codes not include 200 add 200, as long as it is not OPTIONS if '200' not in status_codes and method != 'OPTIONS': status_codes.append('200') for status_code in status_codes: logger.runTrace('for status code', status_code) logger.runTrace('pattern', status_code_pattern[status_code]) #! delete Method Response Headers if there is one try: response_delete_method = client.delete_method_response( restApiId=api_id, resourceId=resource_id, httpMethod=method, statusCode=status_code) logger.runTrace('Method deletion', json.dumps(response_delete_method)) except: logger.runTrace('Method deletion failed', 'Method does not exist') #! delete Integration Response Headers if there is one try: response_delete_integration = client.delete_integration_response( restApiId=api_id, resourceId=resource_id, httpMethod=method, statusCode=status_code) logger.runTrace('Integration deletion', json.dumps(response_delete_integration)) except: logger.runTrace('Integration deletion failed', 'Integration does not exist') #* put Method Response Headers response_put_method = client.put_method_response( restApiId=api_id, resourceId=resource_id, httpMethod=method, statusCode=status_code, responseParameters=method_respPara) print(response_put_method) logger.createInfo('Method Response', json.dumps(response_put_method)) #* put Integration Responses Header Mappings if status_code_pattern[status_code].lower() == 'default': response_put_integration = client.put_integration_response( restApiId=api_id, resourceId=resource_id, httpMethod=method, statusCode=status_code, responseParameters=integration_respPara, responseTemplates=responseTemplates) else: response_put_integration = client.put_integration_response( restApiId=api_id, resourceId=resource_id, httpMethod=method, statusCode=status_code, selectionPattern=status_code_pattern[status_code], responseParameters=integration_respPara) print(response_put_integration)
def main(): #constant stg_var_name_lst = ['keybundle_GET', 'keybundle_id_DELETE', 'keybundle_id_GET', 'keybundle_id_PUT', 'keyholder_GET', 'keyholder_POST', 'keyholder_id_DELETE', 'keyholder_id_GET', 'keyholder_id_PUT', 'property_GET', 'property_POST', 'property_id_DELETE', 'property_id_GET', 'property_id_PUT', 'property_id_keybundle_GET', 'property_id_keybundle_POST' ] #set logger logger.setLogger('api_gateway_lambda_stage_variable_enable.log') #set client for api client_api = boto3.client('apigateway') #get API api_id = GetAPIId() #get feature stage isFeature = input('Is this for the feature stage?(y/n) ') logger.inputTrace('Is Feature', isFeature) if isFeature.lower() == 'y' or isFeature.lower() == 'yes': feature_funcs = GetFeatureStageFuncs(stg_var_name_lst) isFeature = True else: isFeature = False response_get_resources = client_api.get_resources( restApiId=api_id ) resources = response_get_resources['items'] logger.generatedDebug('Resources of API', json.dumps(resources)) # this gives {resource_id:[{method:functionName}, statement_id, source_arn],[]...} resources_dict = GetResourcesDict(resources, client_api, api_id) logger.generatedDebug('Resource Dictionary with Function Names', json.dumps(resources_dict)) # get boto3 clinet for lambda client_lambda = boto3.client('lambda') # lambda add permission isPandD= input('Is this also for the production and development stages?(y/n) ') logger.inputTrace('Is production and development', isPandD) function_name = '' if isPandD.lower() == 'y' or isPandD.lower() == 'yes': dev_prod_funcs = GetDevProdStageFunc(stg_var_name_lst) for method_set in resources_dict.values(): # print(method_set) for method in method_set: # print(method) logger.runTrace('for method', json.dumps(method)) for k in method[0].values(): function_name = k # print(function_name) logger.runTrace('with stage variable', json.dumps(function_name)) statement_id = method[1] source_arn = method[2] function_name_part = function_name.split('${stageVariables.') # print(function_name_part) function_name_part[1] = function_name_part[1].replace('}', '') # print(dev_prod_funcs.keys()) if function_name_part[1] in dev_prod_funcs.keys(): for val in dev_prod_funcs[function_name_part[1]]: function_name = function_name_part[0] + val.replace('id_','id-') logger.runTrace('invoke function', function_name) try: LambdaRemovePermission(client_lambda, function_name, statement_id) except: logger.runTrace('Permission Removal', 'permission not exist') try: LambdaAddPermission(client_lambda, api_id, function_name, statement_id, source_arn) except: print('AddPermission failed for', function_name) else: print('Development and Production permission successfully added!') if isFeature: for method_set in resources_dict.values(): # print(method_set) for method in method_set: logger.runTrace('for method', json.dumps(method)) for k in method[0].values(): function_name = k # logger.runTrace('with stage variable', json.dumps(function_name)) statement_id = method[1] source_arn = method[2] function_name_part = function_name.split('${stageVariables.') function_name_part[1] = function_name_part[1].replace('}', '') # print(function_name_part) # print(feature_funcs.keys()) if function_name_part[1] in feature_funcs.keys(): function_name = function_name_part[0] + feature_funcs[function_name_part[1]].replace('-id','_id') print(function_name) logger.runTrace('invoke function', function_name) try: LambdaRemovePermission(client_lambda, function_name, statement_id) except: logger.runTrace('Permission Removal', 'permission not exist') try: LambdaAddPermission(client_lambda, api_id, function_name, statement_id, source_arn) except: print('AddPermission failed for', function_name) else: print('Feature permission successfully added!')