Skip to content

Commit

Permalink
feat: flow to add policies to access amplify resources from Lambda (a…
Browse files Browse the repository at this point in the history
…ws-amplify#1462)

* feat: flow to add policies to access amplify resources from Lambda

* changes based on feedback

* fix variable names

* chnage var names to camel-case

* fix error messages

* fix region string issue

* fix wording for resource selection

* fix resource cyclic-dependency issues

* change JSON.parse to context.amplify.readJSON

* modify AppSync CRUD permissions
  • Loading branch information
kaustavghosh06 authored May 29, 2019
1 parent 18508d9 commit fee247c
Show file tree
Hide file tree
Showing 31 changed files with 1,330 additions and 21 deletions.
32 changes: 32 additions & 0 deletions packages/amplify-category-analytics/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,43 @@ const {
migrate,
} = require('./provider-utils/awscloudformation/service-walkthroughs/pinpoint-walkthrough');

const category = 'analytics';

function console(context) {
pinpointHelper.console(context);
}

async function getPermissionPolicies(context, resourceOpsMapping) {
const amplifyMetaFilePath = context.amplify.pathManager.getAmplifyMetaFilePath();
const amplifyMeta = context.amplify.readJsonFile(amplifyMetaFilePath);
const permissionPolicies = [];
const resourceAttributes = [];

Object.keys(resourceOpsMapping).forEach((resourceName) => {
try {
const providerController = require(`./provider-utils/${amplifyMeta[category][resourceName].providerPlugin}/index`);
if (providerController) {
const { policy, attributes } = providerController.getPermissionPolicies(
context,
amplifyMeta[category][resourceName].service,
resourceName,
resourceOpsMapping[resourceName],
);
permissionPolicies.push(policy);
resourceAttributes.push({ resourceName, attributes, category });
} else {
context.print.error(`Provider not configured for ${category}: ${resourceName}`);
}
} catch (e) {
context.print.warning(`Could not get policies for ${category}: ${resourceName}`);
throw e;
}
});
return { permissionPolicies, resourceAttributes };
}

module.exports = {
console,
migrate,
getPermissionPolicies,
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

function addResource(context, category, service) {
const serviceMetadata = context.amplify.readJsonFile(`${__dirname}/../supported-services.json`)[service];
const { defaultValuesFilename, serviceWalkthroughFilename } = serviceMetadata;
Expand All @@ -9,4 +8,18 @@ function addResource(context, category, service) {
return addWalkthrough(context, defaultValuesFilename, serviceMetadata);
}

module.exports = { addResource };
function getPermissionPolicies(context, service, resourceName, crudOptions) {
const serviceMetadata = context.amplify.readJsonFile(`${__dirname}/../supported-services.json`)[service];
const { serviceWalkthroughFilename } = serviceMetadata;
const serviceWalkthroughSrc = `${__dirname}/service-walkthroughs/${serviceWalkthroughFilename}`;
const { getIAMPolicies } = require(serviceWalkthroughSrc);

if (!getPermissionPolicies) {
context.print.info(`No policies found for ${resourceName}`);
return;
}

return getIAMPolicies(resourceName, crudOptions);
}

module.exports = { addResource, getPermissionPolicies };
Original file line number Diff line number Diff line change
Expand Up @@ -343,4 +343,56 @@ function isRefNode(node, refName) {
return false;
}

module.exports = { addWalkthrough, migrate };
function getIAMPolicies(resourceName, crudOptions) {
let policy = {};
const actions = [];

crudOptions.forEach((crudOption) => {
switch (crudOption) {
case 'create': actions.push(
'mobiletargeting:Put*',
'mobiletargeting:Create*',
'mobiletargeting:Send*',
);
break;
case 'update': actions.push('mobiletargeting:Update*');
break;
case 'read': actions.push('mobiletargeting:Get*', 'mobiletargeting:List*');
break;
case 'delete': actions.push('mobiletargeting:Delete*');
break;
default: console.log(`${crudOption} not supported`);
}
});

policy = {
Effect: 'Allow',
Action: actions,
Resource: [
{
'Fn::Join': [
'',
[
'arn:aws:mobiletargeting:',
{
Ref: `${category}${resourceName}Region`,
},
':',
{ Ref: 'AWS::AccountId' },
':apps/',
{
Ref: `${category}${resourceName}Id`,
},
],
],
},
],
};

const attributes = ['Id', 'Region'];

return { policy, attributes };
}


module.exports = { addWalkthrough, migrate, getIAMPolicies };
30 changes: 30 additions & 0 deletions packages/amplify-category-api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,38 @@ async function initEnv(context) {
});
}

async function getPermissionPolicies(context, resourceOpsMapping) {
const amplifyMetaFilePath = context.amplify.pathManager.getAmplifyMetaFilePath();
const amplifyMeta = context.amplify.readJsonFile(amplifyMetaFilePath);
const permissionPolicies = [];
const resourceAttributes = [];

Object.keys(resourceOpsMapping).forEach((resourceName) => {
try {
const providerController = require(`./provider-utils/${amplifyMeta[category][resourceName].providerPlugin}/index`);
if (providerController) {
const { policy, attributes } = providerController.getPermissionPolicies(
context,
amplifyMeta[category][resourceName].service,
resourceName,
resourceOpsMapping[resourceName],
);
permissionPolicies.push(policy);
resourceAttributes.push({ resourceName, attributes, category });
} else {
context.print.error(`Provider not configured for ${category}: ${resourceName}`);
}
} catch (e) {
context.print.warning(`Could not get policies for ${category}: ${resourceName}`);
throw e;
}
});
return { permissionPolicies, resourceAttributes };
}

module.exports = {
console,
migrate,
initEnv,
getPermissionPolicies,
};
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,20 @@ function addDatasource(context, category, datasource) {
return serviceQuestions(context, defaultValuesFilename, serviceWalkthroughFilename);
}

function getPermissionPolicies(context, service, resourceName, crudOptions) {
serviceMetadata = context.amplify.readJsonFile(`${__dirname}/../supported-services.json`)[service];
const { serviceWalkthroughFilename } = serviceMetadata;
const serviceWalkthroughSrc = `${__dirname}/service-walkthroughs/${serviceWalkthroughFilename}`;
const { getIAMPolicies } = require(serviceWalkthroughSrc);

if (!getPermissionPolicies) {
context.print.info(`No policies found for ${resourceName}`);
return;
}

return getIAMPolicies(resourceName, crudOptions);
}

module.exports = {
addResource, updateResource, console, migrateResource, addDatasource,
addResource, updateResource, console, migrateResource, addDatasource, getPermissionPolicies,
};
Original file line number Diff line number Diff line change
Expand Up @@ -637,4 +637,59 @@ function convertToCRUD(privacy) {
return privacy;
}

module.exports = { serviceWalkthrough, updateWalkthrough, migrate };

function getIAMPolicies(resourceName, crudOptions) {
let policy = {};
const actions = [];

crudOptions.forEach((crudOption) => {
switch (crudOption) {
case 'create': actions.push(
'apigateway:POST',
'apigateway:PUT',
);
break;
case 'update': actions.push('apigateway:PATCH');
break;
case 'read': actions.push(
'apigateway:GET', 'apigateway:HEAD',
'apigateway:OPTIONS',
);
break;
case 'delete': actions.push('apigateway:DELETE');
break;
default: console.log(`${crudOption} not supported`);
}
});

policy = {
Effect: 'Allow',
Action: actions,
Resource: [
{
'Fn::Join': [
'',
[
'arn:aws:apigateway:',
{
Ref: 'AWS::Region',
},
'::/restapis/',
{
Ref: `${category}${resourceName}ApiName`,
},
'/*',
],
],
},
],
};

const attributes = ['ApiName'];

return { policy, attributes };
}

module.exports = {
serviceWalkthrough, updateWalkthrough, migrate, getIAMPolicies,
};
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,52 @@ async function migrate(context) {
await context.amplify.executeProviderUtils(context, 'awscloudformation', 'compileSchema', { noConfig: true, forceCompile: true, migrate: true });
}

function getIAMPolicies(resourceName, crudOptions) {
let policy = {};
const actions = [];

crudOptions.forEach((crudOption) => {
switch (crudOption) {
case 'create': actions.push('appsync:Create*', 'appsync:StartSchemaCreation', 'appsync:GraphQL');
break;
case 'update': actions.push('appsync:Update*');
break;
case 'read': actions.push('appsync:Get*', 'appsync:List*');
break;
case 'delete': actions.push('appsync:Delete*');
break;
default: console.log(`${crudOption} not supported`);
}
});

policy = {
Effect: 'Allow',
Action: actions,
Resource: [
{
'Fn::Join': [
'',
[
'arn:aws:appsync:',
{ Ref: 'AWS::Region' },
':',
{ Ref: 'AWS::AccountId' },
':apis/',
{
Ref: `${category}${resourceName}GraphQLAPIIdOutput`,
},
],
],
},
],
};

const attributes = ['GraphQLAPIIdOutput'];

return { policy, attributes };
}


module.exports = {
serviceWalkthrough, updateWalkthrough, openConsole, migrate,
serviceWalkthrough, updateWalkthrough, openConsole, migrate, getIAMPolicies,
};
31 changes: 31 additions & 0 deletions packages/amplify-category-auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,42 @@ async function console(context) {
});
}

async function getPermissionPolicies(context, resourceOpsMapping) {
const amplifyMetaFilePath = context.amplify.pathManager.getAmplifyMetaFilePath();
const amplifyMeta = context.amplify.readJsonFile(amplifyMetaFilePath);
const permissionPolicies = [];
const resourceAttributes = [];

Object.keys(resourceOpsMapping).forEach((resourceName) => {
try {
const providerController = require(`./provider-utils/${amplifyMeta[category][resourceName].providerPlugin}/index`);
if (providerController) {
const { policy, attributes } = providerController.getPermissionPolicies(
context,
amplifyMeta[category][resourceName].service,
resourceName,
resourceOpsMapping[resourceName],
);
permissionPolicies.push(policy);
resourceAttributes.push({ resourceName, attributes, category });
} else {
context.print.error(`Provider not configured for ${category}: ${resourceName}`);
}
} catch (e) {
context.print.warning(`Could not get policies for ${category}: ${resourceName}`);
throw e;
}
});
return { permissionPolicies, resourceAttributes };
}


module.exports = {
externalAuthEnable,
checkRequirements,
add,
migrate,
initEnv,
console,
getPermissionPolicies,
};
1 change: 1 addition & 0 deletions packages/amplify-category-auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"chalk-pipe": "^1.2.0",
"eslint": "^4.19.1",
"inquirer": "^6.0.0",
"fs-extra": "^7.0.0",
"jest": "^23.5.0",
"lodash": "^4.17.10",
"opn": "^5.3.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,21 @@ async function openIdentityPoolConsole(context, region, identityPoolId) {
context.print.success(identityPoolConsoleUrl);
}

function getPermissionPolicies(context, service, resourceName, crudOptions) {
serviceMetadata = context.amplify.readJsonFile(`${__dirname}/../supported-services.json`)[service];
const { serviceWalkthroughFilename } = serviceMetadata;
const serviceWalkthroughSrc = `${__dirname}/service-walkthroughs/${serviceWalkthroughFilename}`;
const { getIAMPolicies } = require(serviceWalkthroughSrc);

if (!getPermissionPolicies) {
context.print.info(`No policies found for ${resourceName}`);
return;
}

return getIAMPolicies(resourceName, crudOptions);
}


module.exports = {
addResource,
updateResource,
Expand All @@ -526,4 +541,5 @@ module.exports = {
copyCfnTemplate,
migrate,
console,
getPermissionPolicies,
};
Loading

0 comments on commit fee247c

Please sign in to comment.