Skip to content

Commit

Permalink
feat: Define IAM Permissions Boundary for Project (#7502) (ref #4618)
Browse files Browse the repository at this point in the history
* fix(amplify-provider-awscloudformation): fix tests failing due to system-config-manager.js (#7053)

* feat: s3 sse by default

* chore: fix init push issue

* chore: cleanup

* test: whole lotta tests

* test: update nondeterministic test

* fix: serialize modifiers and improve test error handling

* fix: add parameterization to ResourceModifier

* fix: add type to sig

* test: update test with new modifier structure

* test: fix test

* feat: add permission boundary to IAM roles

* fix: update iam role modifier

* test: add e2e test for perm bound

* test: add unit tests for perm bound modifier

* fix: fix regex

* feat: switch to env-specific config

* chore: dumping env perm bound changes

* feat: fixup env-specific config and add headless support

* chore: cleaning up things

* test: more unit tests and e2e test

* test: small test tweaks

* chore: reverting some unintentional linting changes

* fix: add update to env help text

* test: add mock

* chore: address PR comments

* chore: use module var instead of global var

* chore: rename permission boundary -> permissions boundary

* fix: merge tpi instead of overwrite

* chore: remove newline

* fix: load creds for new env when checking policy

* fix: test fixes

* test: fix unit tests

* test: fix profile selection

* fix: change permissions boundary success text

Co-authored-by: Raj Rajhans <me@rajrajhans.com>
  • Loading branch information
edwardfoyle and rajrajhans authored Jun 10, 2021
1 parent 4f8b5b6 commit 08f7a3c
Show file tree
Hide file tree
Showing 26 changed files with 757 additions and 45 deletions.
70 changes: 50 additions & 20 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1183,30 +1183,38 @@ jobs:
environment:
TEST_SUITE: src/__tests__/layer-2.test.ts
CLI_REGION: eu-central-1
iam-permissions-boundary-amplify_e2e_tests:
working_directory: ~/repo
docker: *ref_1
resource_class: large
steps: *ref_4
environment:
TEST_SUITE: src/__tests__/iam-permissions-boundary.test.ts
CLI_REGION: ap-northeast-1
function_5-amplify_e2e_tests:
working_directory: ~/repo
docker: *ref_1
resource_class: large
steps: *ref_4
environment:
TEST_SUITE: src/__tests__/function_5.test.ts
CLI_REGION: ap-northeast-1
CLI_REGION: ap-southeast-1
configure-project-amplify_e2e_tests:
working_directory: ~/repo
docker: *ref_1
resource_class: large
steps: *ref_4
environment:
TEST_SUITE: src/__tests__/configure-project.test.ts
CLI_REGION: ap-southeast-1
CLI_REGION: ap-southeast-2
api_4-amplify_e2e_tests:
working_directory: ~/repo
docker: *ref_1
resource_class: large
steps: *ref_4
environment:
TEST_SUITE: src/__tests__/api_4.test.ts
CLI_REGION: ap-southeast-2
CLI_REGION: us-east-2
schema-iterative-update-4-amplify_e2e_tests_pkg_linux:
working_directory: ~/repo
docker: *ref_1
Expand Down Expand Up @@ -1877,6 +1885,16 @@ jobs:
TEST_SUITE: src/__tests__/layer-2.test.ts
CLI_REGION: eu-central-1
steps: *ref_5
iam-permissions-boundary-amplify_e2e_tests_pkg_linux:
working_directory: ~/repo
docker: *ref_1
resource_class: large
environment:
AMPLIFY_DIR: /home/circleci/repo/out
AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux
TEST_SUITE: src/__tests__/iam-permissions-boundary.test.ts
CLI_REGION: ap-northeast-1
steps: *ref_5
function_5-amplify_e2e_tests_pkg_linux:
working_directory: ~/repo
docker: *ref_1
Expand All @@ -1885,7 +1903,7 @@ jobs:
AMPLIFY_DIR: /home/circleci/repo/out
AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux
TEST_SUITE: src/__tests__/function_5.test.ts
CLI_REGION: ap-northeast-1
CLI_REGION: ap-southeast-1
steps: *ref_5
configure-project-amplify_e2e_tests_pkg_linux:
working_directory: ~/repo
Expand All @@ -1895,7 +1913,7 @@ jobs:
AMPLIFY_DIR: /home/circleci/repo/out
AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux
TEST_SUITE: src/__tests__/configure-project.test.ts
CLI_REGION: ap-southeast-1
CLI_REGION: ap-southeast-2
steps: *ref_5
api_4-amplify_e2e_tests_pkg_linux:
working_directory: ~/repo
Expand All @@ -1905,7 +1923,7 @@ jobs:
AMPLIFY_DIR: /home/circleci/repo/out
AMPLIFY_PATH: /home/circleci/repo/out/amplify-pkg-linux
TEST_SUITE: src/__tests__/api_4.test.ts
CLI_REGION: ap-southeast-2
CLI_REGION: us-east-2
steps: *ref_5
workflows:
version: 2
Expand Down Expand Up @@ -2005,10 +2023,10 @@ workflows:
- master
- done_with_node_e2e_tests:
requires:
- schema-key-amplify_e2e_tests
- analytics-amplify_e2e_tests
- notifications-amplify_e2e_tests
- schema-iterative-update-locking-amplify_e2e_tests
- api_4-amplify_e2e_tests
- schema-auth-10-amplify_e2e_tests
- hosting-amplify_e2e_tests
- tags-amplify_e2e_tests
Expand All @@ -2024,21 +2042,21 @@ workflows:
- containers-api-amplify_e2e_tests
- interactions-amplify_e2e_tests
- datastore-modelgen-amplify_e2e_tests
- function_5-amplify_e2e_tests
- iam-permissions-boundary-amplify_e2e_tests
- schema-iterative-update-2-amplify_e2e_tests
- schema-data-access-patterns-amplify_e2e_tests
- init-special-case-amplify_e2e_tests
- configure-project-amplify_e2e_tests
- function_5-amplify_e2e_tests
- feature-flags-amplify_e2e_tests
- schema-versioned-amplify_e2e_tests
- plugin-amplify_e2e_tests
- api_4-amplify_e2e_tests
- configure-project-amplify_e2e_tests
- done_with_pkg_linux_e2e_tests:
requires:
- schema-key-amplify_e2e_tests_pkg_linux
- analytics-amplify_e2e_tests_pkg_linux
- notifications-amplify_e2e_tests_pkg_linux
- schema-iterative-update-locking-amplify_e2e_tests_pkg_linux
- api_4-amplify_e2e_tests_pkg_linux
- schema-auth-10-amplify_e2e_tests_pkg_linux
- hosting-amplify_e2e_tests_pkg_linux
- tags-amplify_e2e_tests_pkg_linux
Expand All @@ -2054,15 +2072,15 @@ workflows:
- containers-api-amplify_e2e_tests_pkg_linux
- interactions-amplify_e2e_tests_pkg_linux
- datastore-modelgen-amplify_e2e_tests_pkg_linux
- function_5-amplify_e2e_tests_pkg_linux
- iam-permissions-boundary-amplify_e2e_tests_pkg_linux
- schema-iterative-update-2-amplify_e2e_tests_pkg_linux
- schema-data-access-patterns-amplify_e2e_tests_pkg_linux
- init-special-case-amplify_e2e_tests_pkg_linux
- configure-project-amplify_e2e_tests_pkg_linux
- function_5-amplify_e2e_tests_pkg_linux
- feature-flags-amplify_e2e_tests_pkg_linux
- schema-versioned-amplify_e2e_tests_pkg_linux
- plugin-amplify_e2e_tests_pkg_linux
- api_4-amplify_e2e_tests_pkg_linux
- configure-project-amplify_e2e_tests_pkg_linux
- amplify_migration_tests_latest:
context:
- amplify-ecr-image-pull
Expand Down Expand Up @@ -2269,6 +2287,12 @@ workflows:
filters: *ref_9
requires:
- function_2-amplify_e2e_tests
- api_4-amplify_e2e_tests:
context: *ref_7
post-steps: *ref_8
filters: *ref_9
requires:
- schema-key-amplify_e2e_tests
- api_2-amplify_e2e_tests:
context: *ref_7
post-steps: *ref_8
Expand Down Expand Up @@ -2503,7 +2527,7 @@ workflows:
filters: *ref_9
requires:
- migration-api-key-migration1-amplify_e2e_tests
- function_5-amplify_e2e_tests:
- iam-permissions-boundary-amplify_e2e_tests:
context: *ref_7
post-steps: *ref_8
filters: *ref_9
Expand Down Expand Up @@ -2563,7 +2587,7 @@ workflows:
filters: *ref_9
requires:
- layer-amplify_e2e_tests
- configure-project-amplify_e2e_tests:
- function_5-amplify_e2e_tests:
context: *ref_7
post-steps: *ref_8
filters: *ref_9
Expand Down Expand Up @@ -2623,7 +2647,7 @@ workflows:
filters: *ref_9
requires:
- auth_3-amplify_e2e_tests
- api_4-amplify_e2e_tests:
- configure-project-amplify_e2e_tests:
context: *ref_7
post-steps: *ref_8
filters: *ref_9
Expand Down Expand Up @@ -2701,6 +2725,12 @@ workflows:
filters: *ref_12
requires:
- function_2-amplify_e2e_tests_pkg_linux
- api_4-amplify_e2e_tests_pkg_linux:
context: *ref_10
post-steps: *ref_11
filters: *ref_12
requires:
- schema-key-amplify_e2e_tests_pkg_linux
- api_2-amplify_e2e_tests_pkg_linux:
context: *ref_10
post-steps: *ref_11
Expand Down Expand Up @@ -2951,7 +2981,7 @@ workflows:
filters: *ref_12
requires:
- migration-api-key-migration1-amplify_e2e_tests_pkg_linux
- function_5-amplify_e2e_tests_pkg_linux:
- iam-permissions-boundary-amplify_e2e_tests_pkg_linux:
context: *ref_10
post-steps: *ref_11
filters: *ref_12
Expand Down Expand Up @@ -3015,7 +3045,7 @@ workflows:
filters: *ref_12
requires:
- layer-amplify_e2e_tests_pkg_linux
- configure-project-amplify_e2e_tests_pkg_linux:
- function_5-amplify_e2e_tests_pkg_linux:
context: *ref_10
post-steps: *ref_11
filters: *ref_12
Expand Down Expand Up @@ -3079,7 +3109,7 @@ workflows:
filters: *ref_12
requires:
- auth_3-amplify_e2e_tests_pkg_linux
- api_4-amplify_e2e_tests_pkg_linux:
- configure-project-amplify_e2e_tests_pkg_linux:
context: *ref_10
post-steps: *ref_11
filters: *ref_12
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { getPermissionsBoundaryArn, setPermissionsBoundaryArn } from '..';
import { stateManager } from '../state-manager';

jest.mock('../state-manager');

const testEnv = 'testEnv';

const objKey = 'PermissionsBoundaryPolicyArn';

const stateManager_mock = stateManager as jest.Mocked<typeof stateManager>;
stateManager_mock.getLocalEnvInfo.mockReturnValue({
envName: testEnv,
});

const testArn = 'testArn';

const tpi_stub = {
[testEnv]: {
awscloudformation: {
[objKey]: testArn,
},
},
};

describe('get permissions boundary arn', () => {
beforeEach(jest.clearAllMocks);
it('gets arn from team provider info file', () => {
stateManager_mock.getTeamProviderInfo.mockReturnValueOnce(tpi_stub);
expect(getPermissionsBoundaryArn()).toEqual(testArn);
});

it('gets arn from preInitTeamProviderInfo', () => {
// setup
setPermissionsBoundaryArn(testArn, testEnv, tpi_stub);

// test
expect(getPermissionsBoundaryArn()).toEqual(testArn);

// reset
setPermissionsBoundaryArn(testArn, testEnv);
});

it('returns undefined if no value found', () => {
expect(getPermissionsBoundaryArn()).toBeUndefined();
});
});

describe('set permissions boundary arn', () => {
beforeEach(jest.clearAllMocks);
it('sets the ARN value in tpi file if specified', () => {
stateManager_mock.getTeamProviderInfo.mockReturnValueOnce({});
setPermissionsBoundaryArn(testArn);
expect(stateManager_mock.setTeamProviderInfo.mock.calls[0][1][testEnv].awscloudformation[objKey]).toEqual(testArn);
});

it('sets the ARN for the specified env', () => {
stateManager_mock.getTeamProviderInfo.mockReturnValueOnce({});
setPermissionsBoundaryArn(testArn, 'otherenv');
expect(stateManager_mock.setTeamProviderInfo.mock.calls[0][1].otherenv.awscloudformation[objKey]).toEqual(testArn);
});

it('removes the ARN value if not specified', () => {
stateManager_mock.getTeamProviderInfo.mockReturnValueOnce(tpi_stub);
setPermissionsBoundaryArn();
expect(stateManager_mock.setTeamProviderInfo.mock.calls[0][1][testEnv].awscloudformation).toBeDefined();
expect(stateManager_mock.setTeamProviderInfo.mock.calls[0][1][testEnv].awscloudformation[objKey]).toBeUndefined();
});

it('if tpi object specified, sets arn in object and sets global preInitTeamProviderInfo', () => {
const tpi: Record<string, any> = {};
setPermissionsBoundaryArn(testArn, undefined, tpi);
expect(tpi[testEnv].awscloudformation[objKey]).toEqual(testArn);
expect(getPermissionsBoundaryArn()).toEqual(testArn);
delete (global as any).preInitTeamProviderInfo;
});
});
3 changes: 2 additions & 1 deletion packages/amplify-cli-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './cliContext';
export * from './cliContextEnvironmentProvider';
export * from './cliEnvironmentProvider';
export * from './feature-flags';
export * from './permissionsBoundaryState';
export * from './jsonUtilities';
export * from './jsonValidationError';
export * from './serviceSelection';
Expand Down Expand Up @@ -184,7 +185,7 @@ interface AmplifyToolkit {
getResourceStatus: (category?: $TSAny, resourceName?: $TSAny, providerName?: $TSAny, filteredResources?: $TSAny) => $TSAny;
getResourceOutputs: () => $TSAny;
getWhen: () => $TSAny;
inputValidation: (input: $TSAny) => $TSAny;
inputValidation: (input: $TSAny) => (value: $TSAny) => boolean | string;
listCategories: () => $TSAny;
makeId: (n?: number) => string;
openEditor: (context: $TSContext, target: string, waitToContinue?: boolean) => Promise<void>;
Expand Down
56 changes: 56 additions & 0 deletions packages/amplify-cli-core/src/permissionsBoundaryState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { stateManager } from './state-manager';
import _ from 'lodash';
import { $TSObject } from '.';

let preInitTeamProviderInfo: any;

export const getPermissionsBoundaryArn = (env?: string): string | undefined => {
try {
const tpi = preInitTeamProviderInfo ?? stateManager.getTeamProviderInfo();
// if the pre init team-provider-info only has one env (which should always be the case), default to that one
if (preInitTeamProviderInfo && Object.keys(preInitTeamProviderInfo).length === 1 && !env) {
env = Object.keys(preInitTeamProviderInfo)[0];
}
return _.get(tpi, teamProviderInfoObjectPath(env)) as string | undefined;
} catch {
// uninitialized project
return undefined;
}
};

/**
* Stores the permissions boundary ARN in team-provider-info
* If teamProviderInfo is not specified, the file is read, updated and written back to disk
* If teamProviderInfo is specified, then this function assumes that the env is not initialized
* In this case, the teamProviderInfo object is updated but not written to disk. Instead "preInitTeamProviderInfo" is set
* so that subsequent calls to getPermissionsBoundaryArn will return the permissions boundary arn of the pre-initialized env
* @param arn The permissions boundary arn. If undefined or empty, the permissions boundary is removed
* @param env The Amplify env to update. If not specified, defaults to the current checked out environment
* @param teamProviderInfo The team-provider-info object to update
*/
export const setPermissionsBoundaryArn = (arn?: string, env?: string, teamProviderInfo?: $TSObject): void => {
let tpiGetter = () => stateManager.getTeamProviderInfo();
let tpiSetter = (tpi: $TSObject) => {
stateManager.setTeamProviderInfo(undefined, tpi);
preInitTeamProviderInfo = undefined;
};
if (teamProviderInfo) {
tpiGetter = () => teamProviderInfo;
tpiSetter = (tpi: $TSObject) => {
preInitTeamProviderInfo = tpi;
};
}
const tpi = tpiGetter();
if (!arn) {
_.unset(tpi, teamProviderInfoObjectPath(env));
} else {
_.set(tpi, teamProviderInfoObjectPath(env), arn);
}
tpiSetter(tpi);
};

const teamProviderInfoObjectPath = (env?: string) => [
env || (stateManager.getLocalEnvInfo().envName as string),
'awscloudformation',
'PermissionsBoundaryPolicyArn',
];
4 changes: 4 additions & 0 deletions packages/amplify-cli/src/commands/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ function displayHelp(context) {
name: 'import --name <env-name> --config <provider-configs> [--awsInfo <aws-configs>]',
description: 'Imports an already existing Amplify project environment stack to your local backend',
},
{
name: 'update [--permissions-boundary <IAM Policy ARN>]',
description: 'Update the environment configuration',
},
{
name: 'remove <env-name>',
description: 'Removes an environment from the Amplify project',
Expand Down
6 changes: 6 additions & 0 deletions packages/amplify-cli/src/commands/env/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { $TSContext } from 'amplify-cli-core';
import { executeProviderCommand } from '../../extensions/amplify-helpers/get-provider-plugins';

export const run = async (context: $TSContext) => {
await executeProviderCommand(context, 'updateEnv');
};
Loading

0 comments on commit 08f7a3c

Please sign in to comment.