Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Custom policies IAM Policies for Lambda and Containers #8068

Merged
merged 27 commits into from
Oct 4, 2021
Merged
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
fc5fef8
Custom policy implementation
luhanamz Aug 12, 2021
85f7eea
feat: add custom policies file to function and API container
luhanamz Aug 16, 2021
5ae975a
feat: changes for first PR
luhanamz Aug 16, 2021
c6ceb2f
feat: Some changes according to the PR comments
luhanamz Aug 17, 2021
8ddd755
feat: replace env to current env in the resource when checkout and ad…
luhanamz Aug 20, 2021
8966a7d
feat: e2e test and replacing env
luhanamz Aug 26, 2021
83e4155
feat: Minor changes for env replacement
luhanamz Aug 27, 2021
048b1e5
feat: remove changing env between env
luhanamz Aug 27, 2021
a2cd9fa
feat: Add cloudform type for type safety, move validation to provider…
luhanamz Aug 27, 2021
b7038fe
feat: remove some unused function and import, change regex for resource
luhanamz Aug 30, 2021
1f7efc7
feat: Some changes according to the PR comment
luhanamz Aug 30, 2021
c3f4d05
feat: changes according to PR comments
luhanamz Aug 31, 2021
41f242d
feat: remove unused import
luhanamz Aug 31, 2021
1bb1a42
feat: remove previous unused code
luhanamz Aug 31, 2021
28dab7d
feat: Changes according to PR comments
luhanamz Aug 31, 2021
bb85696
feat: some changes according to PR comments
luhanamz Sep 1, 2021
cac4100
feat: work on PR comments
luhanamz Sep 3, 2021
d1da5e8
feat: rebase for conflict
luhanamz Sep 3, 2021
5f9bb10
feat: rebase for failure of hooksmanager test failed
luhanamz Sep 4, 2021
0aa1933
feat: unit test
luhanamz Sep 7, 2021
8a8268c
feat: fix fail test
luhanamz Sep 7, 2021
453b8b2
feat: change default template of custom policies
luhanamz Sep 8, 2021
d9ed2e4
feat: fix failed test
luhanamz Sep 8, 2021
b7dda25
feat: PR comments
luhanamz Sep 9, 2021
c0c01d5
feat: pr comments
luhanamz Sep 9, 2021
64096b9
feat: fix failed test
luhanamz Sep 9, 2021
22c178e
feat: PR comments from ED
luhanamz Sep 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Custom policy implementation
  • Loading branch information
luhanamz committed Sep 4, 2021
commit fc5fef89e9cb35c53b43da3502332493ee9a5d81
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { JSONUtilities, pathManager } from 'amplify-cli-core';
import fs from 'fs-extra';
import path from 'path';
import uuid from 'uuid';
@@ -96,6 +97,8 @@ export const addResource = async (

}

await addCustomPoliciesFileForContainer(category, resourceName);

context.print.success(`Successfully added resource ${resourceName} locally.`);
context.print.info('');
context.print.success('Next steps:');
@@ -116,6 +119,25 @@ export const addResource = async (
return resourceName;
};

export async function addCustomPoliciesFileForContainer(categoryName, resourceName)
{
const destDir = pathManager.getBackendDirPath();
const fileName = "custom-iam-policy-documents.json";
const addCustomPoliciesPath = path.join(
destDir,
categoryName,
resourceName,
fileName
)
const defaultCustomPolicies = {
"policies": [
{
}
]
}
JSONUtilities.writeJson(addCustomPoliciesPath, defaultCustomPolicies);
}

const getResourceDependencies = async ({
restrictAccess,
dependsOn,
Original file line number Diff line number Diff line change
@@ -112,6 +112,8 @@ export async function addFunctionResource(

await createFunctionResources(context, completeParams);

await addCustomPoliciesFileForFunction(context, completeParams);

if (!completeParams.skipEdit) {
await openEditor(context, category, completeParams.resourceName, completeParams.functionTemplate);
}
@@ -131,6 +133,25 @@ export async function addFunctionResource(
return completeParams.resourceName;
}

export async function addCustomPoliciesFileForFunction(context: $TSContext, parameters: FunctionParameters | FunctionTriggerParameters)
{
const destDir = pathManager.getBackendDirPath();
const fileName = "custom-iam-policy-documents.json";
const addCustomPoliciesPath = path.join(
destDir,
categoryName,
parameters.resourceName,
fileName
)
const defaultCustomPolicies = {
"policies": [
{
}
]
}
JSONUtilities.writeJson(addCustomPoliciesPath, defaultCustomPolicies);
}

export async function addLayerResource(
context: $TSContext,
service: ServiceName,
9 changes: 9 additions & 0 deletions packages/amplify-cli-core/src/customPoliciesType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type CustomIAMPolicy = {
Action: string[];
Effect: string;
Resource: string[];
}

export type CustomIAMPolicies = {
policies: CustomIAMPolicy[];
}
4 changes: 4 additions & 0 deletions packages/amplify-cli-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@ export * from './cliGetCategories';
export * from './cliRemoveResourcePrompt';
export * from './cliViewAPI';
export * from './hooks';
export * from './customPoliciesType'

// Temporary types until we can finish full type definition across the whole CLI

@@ -179,6 +180,9 @@ export type $TSMeta = any;
// Use it for all file content read from team-provider-info.json
export type $TSTeamProviderInfo = any;

//Use it for all file content read from custom-policies.json
export type $TSCustomPolicies = any;

// Use it for all object initializer usages: {}
export type $TSObject = Record<string, $TSAny>;

5 changes: 5 additions & 0 deletions packages/amplify-cli-core/src/state-manager/pathManager.ts
Original file line number Diff line number Diff line change
@@ -47,6 +47,8 @@ export const PathConstants = {
CLIJsonWithEnvironmentFileName: (env: string) => `cli.${env}.json`,

CfnFileName: (resourceName: string) => `${resourceName}-awscloudformation-template.json`,

CustomPolicies: 'custom-iam-policy-documents.json',
};

export class PathManager {
@@ -148,6 +150,9 @@ export class PathManager {

getDotAWSDirPath = (): string => path.normalize(path.join(homedir(), PathConstants.DotAWSDirName));

getCustomPoliciesPath = (projectPath: string): string =>
path.join(projectPath, PathConstants.CustomPolicies);

getAWSCredentialsFilePath = (): string => path.normalize(path.join(this.getDotAWSDirPath(), PathConstants.AWSCredentials));

getAWSConfigFilePath = (): string => path.normalize(path.join(this.getDotAWSDirPath(), PathConstants.AWSConfig));
17 changes: 17 additions & 0 deletions packages/amplify-cli-core/src/state-manager/stateManager.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import _ from 'lodash';
<<<<<<< HEAD
import { $TSAny, $TSMeta, $TSTeamProviderInfo, DeploymentSecrets, HooksConfig, PathConstants } from '..';
import { SecretFileMode } from '../cliConstants';
import { JSONUtilities } from '../jsonUtilities';
import { HydrateTags, ReadTags, Tag } from '../tags';
=======
>>>>>>> d36fae7f6 (Custom policy implementation)
import { pathManager } from './pathManager';
import { $TSMeta, $TSTeamProviderInfo, $TSCustomPolicies, $TSAny, DeploymentSecrets } from '..';
import { JSONUtilities } from '../jsonUtilities';
import _ from 'lodash';
import { SecretFileMode } from '../cliConstants';

export type GetOptions<T> = {
throwIfNotExist?: boolean;
@@ -76,6 +83,16 @@ export class StateManager {
return this.getData<$TSTeamProviderInfo>(filePath, mergedOptions);
};

getCustomPolicies = (projectPath: string): $TSCustomPolicies => {
const filePath = pathManager.getCustomPoliciesPath(projectPath);

if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile) {
return undefined;
}
const data = JSONUtilities.readJson<$TSCustomPolicies>(filePath);
return data;
};

localEnvInfoExists = (projectPath?: string): boolean => this.doesExist(pathManager.getLocalEnvFilePath, projectPath);

getLocalEnvInfo = (projectPath?: string, options?: GetOptions<$TSAny>): $TSAny => {
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { pathManager, readCFNTemplate, writeCFNTemplate } from 'amplify-cli-core';
import { stateManager, pathManager, readCFNTemplate, writeCFNTemplate, CustomIAMPolicies } from 'amplify-cli-core';
import * as path from 'path';
import * as fs from 'fs-extra';
import { ProviderName as providerName } from '../constants';
import { prePushCfnTemplateModifier } from './pre-push-cfn-modifier';
import { Template } from 'cloudform-types';

const buildDir = 'build';

@@ -24,3 +26,85 @@ export async function preProcessCFNTemplate(filePath: string): Promise<string> {
await writeCFNTemplate(cfnTemplate, newPath, { templateFormat });
return newPath;
}

export async function writeCustomPoliciesToCFNTemplate(
resourceDir,
cfnFile,
category,
filePath
) {
const { templateFormat, cfnTemplate } = await readCFNTemplate(path.join(resourceDir, cfnFile));
const customPolicies = await stateManager.getCustomPolicies(resourceDir);
const customPath = pathManager.getCustomPoliciesPath(resourceDir);
if(category === 'function' && fs.existsSync(customPath)) {
await addCustomPoliciesToCFNTemplateForFunction(customPolicies, cfnTemplate, filePath, {templateFormat} );
}
if(category === 'api' && fs.existsSync(customPath)) {
await addCustomPoliciesToCFNTemplateForContainer(customPolicies, cfnTemplate, filePath, {templateFormat});
}

}

export async function addCustomPoliciesToCFNTemplateForFunction(
customPolicies: CustomIAMPolicies,
cfnTemplate: Template,
filePath: string,
{templateFormat}
) {
if(customPolicies.policies === undefined || customPolicies.policies === null
|| customPolicies.policies.length === 0 || Object.keys(customPolicies.policies[0]).length === 0) {
return;
}
const customlambdaexecutionpolicy = {
"DependsOn": ["LambdaExecutionRole"],
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "custom-lambda-execution-policy",
"Roles": [{ "Ref": "LambdaExecutionRole" }],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
]
}
}
};
for (const customPolicy of customPolicies.policies) {
customlambdaexecutionpolicy.Properties.PolicyDocument.Statement.push(customPolicy);
}
cfnTemplate.Resources["CustomLambdaExecutionPolicy"] = customlambdaexecutionpolicy;
await writeCFNTemplate(cfnTemplate, filePath, { templateFormat });
}

export async function addCustomPoliciesToCFNTemplateForContainer(
customPolicies: CustomIAMPolicies,
cfnTemplate: Template,
filePath: string,
{templateFormat}
) {
if(customPolicies.policies === undefined || customPolicies.policies === null
|| customPolicies.policies.length === 0 || Object.keys(customPolicies.policies[0]).length === 0) {
return;
}
const roleName = cfnTemplate.Resources.TaskDefinition.Properties.ExecutionRoleArn["Fn::GetAtt"][0];
const customexecutionpolicyforcontainer = {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
],
"Version": "2012-10-17"
},
"PolicyName": "CustomExecutionPolicyForContainer",
"Roles": [
{
"Ref": roleName
}
]
}
};
for (const customPolicy of customPolicies.policies) {
customexecutionpolicyforcontainer.Properties.PolicyDocument.Statement.push(customPolicy);
}
cfnTemplate.Resources["CustomExecutionPolicyForContainer"] = customexecutionpolicyforcontainer;
await writeCFNTemplate(cfnTemplate, filePath, { templateFormat })
}
Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@ import { fileLogger } from './utils/aws-logger';
import { APIGW_AUTH_STACK_LOGICAL_ID, loadApiWithPrivacyParams } from './utils/consolidate-apigw-policies';
import { createEnvLevelConstructs } from './utils/env-level-constructs';
import { NETWORK_STACK_LOGICAL_ID } from './network/stack';
import { preProcessCFNTemplate } from './pre-push-cfn-processor/cfn-pre-processor';
import { preProcessCFNTemplate, writeCustomPoliciesToCFNTemplate } from './pre-push-cfn-processor/cfn-pre-processor';
import { AUTH_TRIGGER_STACK, AUTH_TRIGGER_TEMPLATE } from './utils/upload-auth-trigger-template';
import { ensureValidFunctionModelDependencies } from './utils/remove-dependent-function';
import { legacyLayerMigration, postPushLambdaLayerCleanup, prePushLambdaLayerPrompt } from './lambdaLayerInvocations';
@@ -759,6 +759,7 @@ async function updateS3Templates(context: $TSContext, resourcesToBeUpdated: $TSA
const { resourceDir, cfnFiles } = getCfnFiles(category, resourceName);

for (const cfnFile of cfnFiles) {
await writeCustomPoliciesToCFNTemplate(resourceDir, cfnFile, category, path.join(resourceDir, cfnFile));
const transformedCFNPath = await preProcessCFNTemplate(path.join(resourceDir, cfnFile));
promises.push(uploadTemplateToS3(context, transformedCFNPath, category, resourceName, amplifyMeta));
}