Skip to content

Commit

Permalink
Revert "Revert "fix: fix policies with overridden table names (#3075)" (
Browse files Browse the repository at this point in the history
#3088)"

This reverts commit e592dba.
  • Loading branch information
palpatim committed Jan 6, 2025
1 parent e592dba commit 611edb0
Show file tree
Hide file tree
Showing 6 changed files with 342 additions and 8 deletions.
2 changes: 1 addition & 1 deletion packages/amplify-category-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
"coverageProvider": "v8",
"coverageThreshold": {
"global": {
"branches": 68,
"branches": 67,
"functions": 42,
"lines": 40
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,50 @@ $util.toJson({})",
},
"Type": "AWS::IAM::Role",
},
"PostIAMRoleDefaultPolicy04190CA0": Object {
"Properties": Object {
"PolicyDocument": Object {
"Statement": Array [
Object {
"Action": Array [
"dynamodb:BatchGetItem",
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:Query",
"dynamodb:GetItem",
"dynamodb:Scan",
"dynamodb:ConditionCheckItem",
"dynamodb:BatchWriteItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:DescribeTable",
],
"Effect": "Allow",
"Resource": Array [
Object {
"Fn::GetAtt": Array [
"PostTable",
"Arn",
],
},
Object {
"Ref": "AWS::NoValue",
},
],
},
],
"Version": "2012-10-17",
},
"PolicyName": "PostIAMRoleDefaultPolicy04190CA0",
"Roles": Array [
Object {
"Ref": "PostIAMRole83BF708F",
},
],
},
"Type": "AWS::IAM::Policy",
},
"PostTable": Object {
"DeletionPolicy": "Delete",
"Properties": Object {
Expand Down Expand Up @@ -1525,6 +1569,50 @@ Object {
},
"Type": "AWS::IAM::Role",
},
"CommentIAMRoleDefaultPolicyA8D6F6B5": Object {
"Properties": Object {
"PolicyDocument": Object {
"Statement": Array [
Object {
"Action": Array [
"dynamodb:BatchGetItem",
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:Query",
"dynamodb:GetItem",
"dynamodb:Scan",
"dynamodb:ConditionCheckItem",
"dynamodb:BatchWriteItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:DescribeTable",
],
"Effect": "Allow",
"Resource": Array [
Object {
"Fn::GetAtt": Array [
"CommentTable",
"Arn",
],
},
Object {
"Ref": "AWS::NoValue",
},
],
},
],
"Version": "2012-10-17",
},
"PolicyName": "CommentIAMRoleDefaultPolicyA8D6F6B5",
"Roles": Array [
Object {
"Ref": "CommentIAMRoleD5EC5F51",
},
],
},
"Type": "AWS::IAM::Policy",
},
"CommentTable": Object {
"DeletionPolicy": "Delete",
"Properties": Object {
Expand Down Expand Up @@ -3006,6 +3094,50 @@ $util.toJson({})",
},
"Type": "AWS::IAM::Role",
},
"PostIAMRoleDefaultPolicy04190CA0": Object {
"Properties": Object {
"PolicyDocument": Object {
"Statement": Array [
Object {
"Action": Array [
"dynamodb:BatchGetItem",
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:Query",
"dynamodb:GetItem",
"dynamodb:Scan",
"dynamodb:ConditionCheckItem",
"dynamodb:BatchWriteItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:DescribeTable",
],
"Effect": "Allow",
"Resource": Array [
Object {
"Fn::GetAtt": Array [
"PostTable",
"Arn",
],
},
Object {
"Ref": "AWS::NoValue",
},
],
},
],
"Version": "2012-10-17",
},
"PolicyName": "PostIAMRoleDefaultPolicy04190CA0",
"Roles": Array [
Object {
"Ref": "PostIAMRole83BF708F",
},
],
},
"Type": "AWS::IAM::Policy",
},
"PostTable": Object {
"DeletionPolicy": "Delete",
"Properties": Object {
Expand Down Expand Up @@ -3748,6 +3880,50 @@ Object {
},
"Type": "AWS::IAM::Role",
},
"CommentIAMRoleDefaultPolicyA8D6F6B5": Object {
"Properties": Object {
"PolicyDocument": Object {
"Statement": Array [
Object {
"Action": Array [
"dynamodb:BatchGetItem",
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:Query",
"dynamodb:GetItem",
"dynamodb:Scan",
"dynamodb:ConditionCheckItem",
"dynamodb:BatchWriteItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:DescribeTable",
],
"Effect": "Allow",
"Resource": Array [
Object {
"Fn::GetAtt": Array [
"CommentTable",
"Arn",
],
},
Object {
"Ref": "AWS::NoValue",
},
],
},
],
"Version": "2012-10-17",
},
"PolicyName": "CommentIAMRoleDefaultPolicyA8D6F6B5",
"Roles": Array [
Object {
"Ref": "CommentIAMRoleD5EC5F51",
},
],
},
"Type": "AWS::IAM::Policy",
},
"CommentTable": Object {
"DeletionPolicy": "Delete",
"Properties": Object {
Expand Down
30 changes: 29 additions & 1 deletion packages/amplify-e2e-core/src/utils/sdk-calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,35 @@ export const getAmplifyBackendJobStatus = async (jobId: string, appId: string, e
.promise();
};

export const listRolePolicies = async (roleName: string, region: string) => {
export const listRoleNamesContaining = async (searchString: string, region: string): Promise<string[]> => {
const service = new IAM({ region });

const roles: string[] = [];
let isTruncated = true;
let marker: string | undefined;

while (isTruncated) {
const params = marker ? { Marker: marker } : {};
const response = await service.listRoles(params).promise();

const matchingRoles = response.Roles.filter((role) => role.RoleName.includes(searchString));
roles.push(...matchingRoles.map((r) => r.RoleName));

isTruncated = response.IsTruncated;
marker = response.Marker;
}

return roles;
};

export const getRolePolicy = async (roleName: string, policyName: string, region: string): Promise<any> => {
const service = new IAM({ region });
const rawDocument = (await service.getRolePolicy({ PolicyName: policyName, RoleName: roleName }).promise()).PolicyDocument;
const decodedDocument = decodeURIComponent(rawDocument);
return JSON.parse(decodedDocument);
};

export const listRolePolicies = async (roleName: string, region: string): Promise<string[]> => {
const service = new IAM({ region });
return (await service.listRolePolicies({ RoleName: roleName }).promise()).PolicyNames;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import path from 'path';
import * as fs from 'fs-extra';
import {
addApiWithoutSchema,
amplifyOverrideApi,
amplifyPush,
amplifyPushOverride,
createNewProjectDir,
deleteProject,
deleteProjectDir,
getAppSyncApi,
getDDBTable,
getProjectMeta,
getRolePolicy,
initJSProjectWithProfile,
listRolePolicies,
listRoleNamesContaining,
replaceOverrideFileWithProjectInfo,
updateApiSchema,
updateSchema,
} from 'amplify-category-api-e2e-core';

describe('Override table name', () => {
let projRoot: string;
let projFolderName: string;
beforeEach(async () => {
projFolderName = 'overridename';
projRoot = await createNewProjectDir(projFolderName);
});

afterEach(async () => {
const metaFilePath = path.join(projRoot, 'amplify', '#current-cloud-backend', 'amplify-meta.json');
if (fs.existsSync(metaFilePath)) {
await deleteProject(projRoot);
}
deleteProjectDir(projRoot);
});

it('Generates correct permissions policies for DynamoDB tables with overridden names', async () => {
const now = Math.floor(Date.now() / 1000);
const modelName = `Override${now}`;

const schema = /* GraphQL */ `
type ${modelName} @model {
id: ID!
content: String
}
`;

const envName = 'integtest';
const projName = 'overridetest';
const cliInputsFilePath = path.join(projRoot, 'amplify', 'backend', 'api', `${projName}`, 'cli-inputs.json');
await initJSProjectWithProfile(projRoot, { name: projName, envName });
await addApiWithoutSchema(projRoot);

updateSchema(projRoot, projName, schema);
expect(fs.existsSync(cliInputsFilePath)).toBe(true);

await amplifyPush(projRoot);

await amplifyOverrideApi(projRoot, {});

const overriddenTableName = `OverrideTest${now}Custom`;
const overrideCode = /* TypeScript */ `
export function override(props: any) {
props.models['${modelName}'].modelDDBTable.tableName = '${overriddenTableName}';
}
`;
const destOverrideFilePath = path.join(projRoot, 'amplify', 'backend', 'api', `${projName}`, 'override.ts');
fs.writeFileSync(destOverrideFilePath, overrideCode);

await amplifyPushOverride(projRoot);

const meta = getProjectMeta(projRoot);
const region = meta.providers.awscloudformation.Region;
const { output } = meta.api.overridetest;
const { GraphQLAPIIdOutput, GraphQLAPIEndpointOutput, GraphQLAPIKeyOutput } = output;
const { graphqlApi } = await getAppSyncApi(GraphQLAPIIdOutput, region);

expect(graphqlApi).toBeDefined();
expect(graphqlApi.apiId).toEqual(GraphQLAPIIdOutput);
expect(GraphQLAPIIdOutput).toBeDefined();
expect(GraphQLAPIEndpointOutput).toBeDefined();
expect(GraphQLAPIKeyOutput).toBeDefined();

const defaultTableName = `${modelName}-${graphqlApi.apiId}-${envName}`;
const error = { message: null };
try {
const defaultTable = await getDDBTable(defaultTableName, region);
expect(defaultTable).toBeUndefined();
} catch (ex) {
Object.assign(error, ex);
}
expect(error).toBeDefined();
expect(error.message).toContain(`${defaultTableName} not found`);

const actualTable = await getDDBTable(overriddenTableName, region);
expect(actualTable).toBeDefined();

// Validate policy. The role will be created with the prefix {modelName}IAMRole. It should have 2 policies: one created by Amplify, one
// created by AppSync's CDK call during the `addDynamoDbDataSource` flow. We expect the policy statements for the latter to refer to the
// overridden table name.
const matchingRoleNames = await listRoleNamesContaining(modelName, region);
expect(matchingRoleNames).toBeDefined();
expect(matchingRoleNames.length).toEqual(1);
const roleName = matchingRoleNames[0];

const policies = await listRolePolicies(roleName, region);
expect(policies).toBeDefined();
expect(policies.length).toBe(2);

const defaultPolicy = policies.find((p) => p.startsWith(`${modelName}IAMRoleDefault`));
expect(defaultPolicy).toBeDefined();

const policyObject = await getRolePolicy(roleName, defaultPolicy, region);
expect(policyObject).toBeDefined();
expect(policyObject.Statement[0].Resource[0]).toContain(overriddenTableName);
});
});
2 changes: 1 addition & 1 deletion packages/amplify-graphql-auth-transformer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"coverageProvider": "v8",
"coverageThreshold": {
"global": {
"branches": 88,
"branches": 87,
"functions": 90,
"lines": 90
}
Expand Down
Loading

0 comments on commit 611edb0

Please sign in to comment.