New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

serverless-iam-roles-per-function

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

serverless-iam-roles-per-function - npm Package Compare versions

Comparing version 0.1.2 to 0.1.3

11

CHANGELOG.md

@@ -5,2 +5,13 @@ # Change Log

<a name="0.1.3"></a>
## [0.1.3](https://github.com/functionalone/serverless-iam-roles-per-function/compare/v0.1.2...v0.1.3) (2018-02-20)
### Features
* new configuration to control default inherit or override behaviour ([542175f](https://github.com/functionalone/serverless-iam-roles-per-function/commit/542175f))
* support custom role names via the property: iamRoleStatementsName ([93cd015](https://github.com/functionalone/serverless-iam-roles-per-function/commit/93cd015)), closes [#2](https://github.com/functionalone/serverless-iam-roles-per-function/issues/2)
<a name="0.1.2"></a>

@@ -7,0 +18,0 @@ ## [0.1.2](https://github.com/functionalone/serverless-iam-roles-per-function/compare/v0.1.1...v0.1.2) (2018-02-07)

1

dist/lib/index.d.ts

@@ -8,2 +8,3 @@ declare class ServerlessIamPerFunctionPlugin {

awsPackagePlugin: any;
defaultInherit: boolean;
/**

@@ -10,0 +11,0 @@ *

34

dist/lib/index.js
"use strict";
const _ = require("lodash");
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
}
const lodash_1 = __importDefault(require("lodash"));
class ServerlessIamPerFunctionPlugin {

@@ -15,2 +18,3 @@ /**

};
this.defaultInherit = lodash_1.default.get(this.serverless.service, "custom.serverless-iam-roles-per-function.defaultInherit", false);
}

@@ -36,6 +40,15 @@ validateStatements(statements) {

const fnJoin = roleName['Fn::Join'];
if (!_.isArray(fnJoin) || fnJoin.length !== 2 || !_.isArray(fnJoin[1]) || fnJoin[1].length < 2) {
if (!lodash_1.default.isArray(fnJoin) || fnJoin.length !== 2 || !lodash_1.default.isArray(fnJoin[1]) || fnJoin[1].length < 2) {
throw new this.serverless.classes.Error("Global Role Name is not in exepcted format. Got name: " + JSON.stringify(roleName));
}
fnJoin[1].splice(2, 0, functionName);
let length = 0; //calculate the expected length. Sum the lenght of each part
for (const part of fnJoin[1]) {
length += part.length;
}
length += (fnJoin[1].length - 1); //take into account the dashes between parts
if (length > 64) {
throw new this.serverless.classes.Error(`auto generated role name for function: ${functionName} is too long (over 64 chars).
Try setting a custom role name using the property: iamRoleStatementsName.`);
}
return roleName;

@@ -46,4 +59,4 @@ }

const functionResource = this.serverless.service.provider.compiledCloudFormationTemplate.Resources[functionResourceName];
if (_.isEmpty(functionResource) || _.isEmpty(functionResource.Properties) || _.isEmpty(functionResource.Properties.Role) ||
!_.isArray(functionResource.Properties.Role["Fn::GetAtt"]) || !_.isArray(functionResource.DependsOn)) {
if (lodash_1.default.isEmpty(functionResource) || lodash_1.default.isEmpty(functionResource.Properties) || lodash_1.default.isEmpty(functionResource.Properties.Role) ||
!lodash_1.default.isArray(functionResource.Properties.Role["Fn::GetAtt"]) || !lodash_1.default.isArray(functionResource.DependsOn)) {
throw new this.serverless.classes.Error("Function Resource is not in exepcted format. For function name: " + functionName);

@@ -60,3 +73,3 @@ }

const functionObject = this.serverless.service.getFunction(functionName);
if (_.isEmpty(functionObject.iamRoleStatements)) {
if (lodash_1.default.isEmpty(functionObject.iamRoleStatements)) {
return;

@@ -71,3 +84,3 @@ }

const globalIamRole = this.serverless.service.provider.compiledCloudFormationTemplate.Resources[globalRoleName];
const functionIamRole = _.cloneDeep(globalIamRole);
const functionIamRole = lodash_1.default.cloneDeep(globalIamRole);
//remove the statements

@@ -88,3 +101,3 @@ const policyStatements = [];

//set vpc if needed
if (!_.isEmpty(functionObject.vpc) || !_.isEmpty(this.serverless.service.provider.vpc)) {
if (!lodash_1.default.isEmpty(functionObject.vpc) || !lodash_1.default.isEmpty(this.serverless.service.provider.vpc)) {
functionIamRole.Properties.ManagedPolicyArns = [

@@ -94,3 +107,4 @@ 'arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole',

}
if (functionObject.iamRoleStatementsInherit && !_.isEmpty(this.serverless.service.provider.iamRoleStatements)) {
if ((functionObject.iamRoleStatementsInherit || (this.defaultInherit && functionObject.iamRoleStatementsInherit !== false))
&& !lodash_1.default.isEmpty(this.serverless.service.provider.iamRoleStatements)) {
for (const s of this.serverless.service.provider.iamRoleStatements) {

@@ -104,3 +118,3 @@ policyStatements.push(s);

}
functionIamRole.Properties.RoleName = this.getFunctionRoleName(functionName);
functionIamRole.Properties.RoleName = functionObject.iamRoleStatementsName || this.getFunctionRoleName(functionName);
const roleResourceName = this.serverless.providers.aws.naming.getNormalizedFunctionName(functionName) + globalRoleName;

@@ -112,3 +126,3 @@ this.serverless.service.provider.compiledCloudFormationTemplate.Resources[roleResourceName] = functionIamRole;

const allFunctions = this.serverless.service.getAllFunctions();
if (_.isEmpty(allFunctions)) {
if (lodash_1.default.isEmpty(allFunctions)) {
return;

@@ -115,0 +129,0 @@ }

{
"name": "serverless-iam-roles-per-function",
"private": false,
"version": "0.1.2",
"version": "0.1.3",
"engines": {

@@ -35,3 +35,3 @@ "node": ">=6.10.0"

"dependencies": {
"lodash": "^4.17.4"
"lodash": "^4.17.5"
},

@@ -44,3 +44,3 @@ "devDependencies": {

"chai": "^4.1.2",
"mocha": "^5.0.0",
"mocha": "^5.0.1",
"rimraf": "^2.6.2",

@@ -50,3 +50,3 @@ "serverless": "^1.26.0",

"tslint": "^5.6.0",
"typescript": "^2.6.1"
"typescript": "^2.7.2"
},

@@ -53,0 +53,0 @@ "files": [

@@ -28,2 +28,3 @@ # Serverless IAM Roles Per Function Plugin

handler: handler.get
iamRoleStatementsName: my-custom-role-name #optional custom role name setting instead of the default generated one
iamRoleStatements:

@@ -36,3 +37,3 @@ - Effect: "Allow"

func2:
handler: handler.put
handler: handler.put
iamRoleStatements:

@@ -48,3 +49,3 @@ - Effect: "Allow"

By deafault, function level `iamRoleStatements` override the provider level definition. It is also possible to inherit the provider level definition by specifying the option `iamRoleStatementsInherit`:
By deafault, function level `iamRoleStatements` override the provider level definition. It is also possible to inherit the provider level definition by specifying the option `iamRoleStatementsInherit: true`:

@@ -73,3 +74,16 @@ ```yaml

If you wish to change the default behaviour to `inherit` instead of `override` it is possible to specify the following custom configuration:
```yaml
custom:
serverless-iam-roles-per-function:
defaultInherit: true
```
## More Info
**Introduction post**:
[Serverless Framework: Defining Per-Function IAM Roles](https://medium.com/@glicht/serverless-framework-defining-per-function-iam-roles-c678fa09f46d)
**Note**: Servless Framework provides support for defining custom IAM roles on a per function level through the use of the `role` property and creating CloudFormation resources, as documented [here](https://serverless.com/framework/docs/providers/aws/guide/iam#custom-iam-roles). This plugin doesn't support defining both the `role` property and `iamRoleStatements` at the function level.

@@ -76,0 +90,0 @@

@@ -1,2 +0,2 @@

import * as _ from 'lodash';
import _ from 'lodash';

@@ -9,2 +9,3 @@ class ServerlessIamPerFunctionPlugin {

awsPackagePlugin: any;
defaultInherit: boolean;

@@ -22,2 +23,3 @@ /**

};
this.defaultInherit = _.get(this.serverless.service, "custom.serverless-iam-roles-per-function.defaultInherit", false);
}

@@ -49,2 +51,11 @@

fnJoin[1].splice(2, 0, functionName);
let length=0; //calculate the expected length. Sum the lenght of each part
for (const part of fnJoin[1]) {
length += part.length;
}
length += (fnJoin[1].length - 1); //take into account the dashes between parts
if(length > 64) { //aws limits to 64 chars the role name
throw new this.serverless.classes.Error(`auto generated role name for function: ${functionName} is too long (over 64 chars).
Try setting a custom role name using the property: iamRoleStatementsName.`);
}
return roleName;

@@ -101,3 +112,4 @@ }

}
if(functionObject.iamRoleStatementsInherit && !_.isEmpty(this.serverless.service.provider.iamRoleStatements)) { //add global statements
if((functionObject.iamRoleStatementsInherit || (this.defaultInherit && functionObject.iamRoleStatementsInherit !== false))
&& !_.isEmpty(this.serverless.service.provider.iamRoleStatements)) { //add global statements
for (const s of this.serverless.service.provider.iamRoleStatements) {

@@ -111,3 +123,3 @@ policyStatements.push(s);

}
functionIamRole.Properties.RoleName = this.getFunctionRoleName(functionName);
functionIamRole.Properties.RoleName = functionObject.iamRoleStatementsName || this.getFunctionRoleName(functionName);
const roleResourceName = this.serverless.providers.aws.naming.getNormalizedFunctionName(functionName) + globalRoleName;

@@ -114,0 +126,0 @@ this.serverless.service.provider.compiledCloudFormationTemplate.Resources[roleResourceName] = functionIamRole;

@@ -6,77 +6,132 @@ // tslint:disable:no-var-requires

const funcWithIamTemplate = require('../../src/test/funcs-with-iam.json');
import _ from 'lodash';
describe('plugin tests', () => {
let serverless: any;
let plugin: any;
function assertFunctionRoleName(name: string, roleNameObj: any) {
assert.isArray(roleNameObj['Fn::Join']);
assert.isTrue(roleNameObj['Fn::Join'][1].indexOf(name) >= 0, 'role name contains function name');
}
beforeEach(() => {
serverless = new Serverless();
Object.assign(serverless.service, funcWithIamTemplate);
serverless.pluginManager.loadAllPlugins();
plugin = new Plugin(serverless);
});
describe('defaultInherit not set', () => {
let serverless: any;
let plugin: any;
describe('#constructor()', () => {
it('should initialize the plugin', () => {
assert.instanceOf(plugin, Plugin);
});
});
beforeEach(() => {
serverless = new Serverless();
Object.assign(serverless.service, funcWithIamTemplate);
serverless.pluginManager.loadAllPlugins();
plugin = new Plugin(serverless);
});
describe('#validateStatements', () => {
it('should validate valid statement', () => {
const statements = [{
Effect: "Allow",
Action: [
'xray:PutTelemetryRecords',
'xray:PutTraceSegments',
],
Resource: "*",
}];
assert.doesNotThrow(() => {plugin.validateStatements(statements);});
describe('#constructor()', () => {
it('should initialize the plugin', () => {
assert.instanceOf(plugin, Plugin);
});
it('defaultInherit shuuld be false', () => {
assert.isFalse(plugin.defaultInherit);
});
});
it('should throw an error for invalid statement', () => {
const statements = [{ //missing effect
Action: [
'xray:PutTelemetryRecords',
'xray:PutTraceSegments',
],
Resource: "*",
}];
assert.throws(() => {plugin.validateStatements(statements);});
describe('#validateStatements', () => {
it('should validate valid statement', () => {
const statements = [{
Effect: "Allow",
Action: [
'xray:PutTelemetryRecords',
'xray:PutTraceSegments',
],
Resource: "*",
}];
assert.doesNotThrow(() => {plugin.validateStatements(statements);});
});
it('should throw an error for invalid statement', () => {
const statements = [{ //missing effect
Action: [
'xray:PutTelemetryRecords',
'xray:PutTraceSegments',
],
Resource: "*",
}];
assert.throws(() => {plugin.validateStatements(statements);});
});
});
});
describe('#getFunctionRoleName', () => {
it('should return a name with the function name', () => {
const name = 'test-name';
const roleName = plugin.getFunctionRoleName(name);
assertFunctionRoleName(name, roleName);
});
function assertFunctionRoleName(name: string, roleNameObj: any) {
assert.isArray(roleNameObj['Fn::Join']);
assert.isTrue(roleNameObj['Fn::Join'][1].indexOf(name) >= 0, 'role name contains function name');
}
it('should throw an error on long name', () => {
assert.throws(() => {plugin.getFunctionRoleName('long-long-long-long-long-long-long-long-long-long-long-name');});
});
});
describe('#getFunctionRoleName', () => {
it('should return a name with the function name', () => {
const name = 'test-name';
const roleName = plugin.getFunctionRoleName(name);
assertFunctionRoleName(name, roleName);
describe('#createRolesPerFunction', () => {
it('should create role per function', () => {
plugin.createRolesPerFunction();
const helloRole = serverless.service.provider.compiledCloudFormationTemplate.Resources.HelloIamRoleLambdaExecution;
assert.isNotEmpty(helloRole);
assertFunctionRoleName('hello', helloRole.Properties.RoleName);
//check depends and role is set properlly
const helloFunctionResource = serverless.service.provider.compiledCloudFormationTemplate.Resources.HelloLambdaFunction;
assert.isTrue(helloFunctionResource.DependsOn.indexOf('HelloIamRoleLambdaExecution') >= 0, 'function resource depends on role');
assert.equal(helloFunctionResource.Properties.Role["Fn::GetAtt"][0], 'HelloIamRoleLambdaExecution', "function resource role is set properly");
const helloInheritRole = serverless.service.provider.compiledCloudFormationTemplate.Resources.HelloInheritIamRoleLambdaExecution;
assertFunctionRoleName('helloInherit', helloInheritRole.Properties.RoleName);
const statements: any[] = helloInheritRole.Properties.Policies[0].PolicyDocument.Statement;
assert.isObject(statements.find((s) => s.Action[0] === "xray:PutTelemetryRecords"), 'global statements imported upon inherit');
assert.isObject(statements.find((s) => s.Action[0] === "dynamodb:GetItem"), 'per function statements imported upon inherit');
});
});
});
describe('#createRolesPerFunction', () => {
it('should create role per function', () => {
plugin.createRolesPerFunction();
const helloRole = serverless.service.provider.compiledCloudFormationTemplate.Resources.HelloIamRoleLambdaExecution;
assert.isNotEmpty(helloRole);
assertFunctionRoleName('hello', helloRole.Properties.RoleName);
//check depends and role is set properlly
const helloFunctionResource = serverless.service.provider.compiledCloudFormationTemplate.Resources.HelloLambdaFunction;
assert.isTrue(helloFunctionResource.DependsOn.indexOf('HelloIamRoleLambdaExecution') >= 0, 'function resource depends on role');
assert.equal(helloFunctionResource.Properties.Role["Fn::GetAtt"][0], 'HelloIamRoleLambdaExecution', "function resource role is set properly");
const helloInheritRole = serverless.service.provider.compiledCloudFormationTemplate.Resources.HelloInheritIamRoleLambdaExecution;
assertFunctionRoleName('helloInherit', helloInheritRole.Properties.RoleName);
const statements: any[] = helloInheritRole.Properties.Policies[0].PolicyDocument.Statement;
assert.isObject(statements.find((s) => s.Action[0] === "xray:PutTelemetryRecords"), 'global statements imported upon inherit');
assert.isObject(statements.find((s) => s.Action[0] === "dynamodb:GetItem"), 'per function statements imported upon inherit');
describe('defaultInherit set', () => {
let serverless: any;
let plugin: any;
beforeEach(() => {
serverless = new Serverless();
const funcWithIamTemplateMod = _.set(_.cloneDeep(funcWithIamTemplate), "custom.serverless-iam-roles-per-function.defaultInherit", true);
//change helloInherit to false for testing
funcWithIamTemplateMod.functions.helloInherit.iamRoleStatementsInherit = false;
Object.assign(serverless.service, funcWithIamTemplateMod);
serverless.pluginManager.loadAllPlugins();
plugin = new Plugin(serverless);
});
describe('#constructor()', () => {
it('defaultInherit shuuld be true', () => {
assert.isTrue(plugin.defaultInherit);
});
});
describe('#createRolesPerFunction', () => {
it('should create role per function', () => {
plugin.createRolesPerFunction();
const helloRole = serverless.service.provider.compiledCloudFormationTemplate.Resources.HelloIamRoleLambdaExecution;
assert.isNotEmpty(helloRole);
assertFunctionRoleName('hello', helloRole.Properties.RoleName);
//check depends and role is set properlly
const helloFunctionResource = serverless.service.provider.compiledCloudFormationTemplate.Resources.HelloLambdaFunction;
assert.isTrue(helloFunctionResource.DependsOn.indexOf('HelloIamRoleLambdaExecution') >= 0, 'function resource depends on role');
assert.equal(helloFunctionResource.Properties.Role["Fn::GetAtt"][0], 'HelloIamRoleLambdaExecution', "function resource role is set properly");
let statements: any[] = helloRole.Properties.Policies[0].PolicyDocument.Statement;
assert.isObject(statements.find((s) => s.Action[0] === "xray:PutTelemetryRecords"), 'global statements imported as defaultInherit is set');
assert.isObject(statements.find((s) => s.Action[0] === "dynamodb:GetItem"), 'per function statements imported upon inherit');
const helloInheritRole = serverless.service.provider.compiledCloudFormationTemplate.Resources.HelloInheritIamRoleLambdaExecution;
assertFunctionRoleName('helloInherit', helloInheritRole.Properties.RoleName);
statements = helloInheritRole.Properties.Policies[0].PolicyDocument.Statement;
assert.isObject(statements.find((s) => s.Action[0] === "dynamodb:GetItem"), 'per function statements imported');
assert.isTrue(statements.find((s) => s.Action[0] === "xray:PutTelemetryRecords") === undefined,
'global statements not imported as iamRoleStatementsInherit is false');
});
});
});
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc