Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.

merge credential parsing bits in #5

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions sls-action/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ inputs:
description: 'The serverless yaml file'
required: true
default: 'serverless.yml'
credentials:
description: 'The login credentials for deploying'
required: true
runs:
using: 'node12'
main: 'lib/main.js'
1,053 changes: 796 additions & 257 deletions sls-action/package-lock.json

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions sls-action/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,17 @@
],
"license": "MIT",
"dependencies": {
"@actions/core": "^1.0.0",
"@actions/exec": "^1.0.0",
"serverless": "^1.56.1"
"@actions/core": "^1.2.0",
"@actions/exec": "^1.0.1",
"actions-secret-parser": "^1.0.2"
},
"devDependencies": {
"@types/jest": "^24.0.22",
"@types/node": "^12.12.6",
"jest": "^24.9.0",
"jest-circus": "^24.9.0",
"ts-jest": "^24.1.0",
"typescript": "^3.7.2"
"typescript": "^3.7.2",
"serverless": "^1.56.1"
}
}
59 changes: 59 additions & 0 deletions sls-action/src/constants/authenticationType.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { AuthenticationTypeConst, AuthenticationTypeUtil } from './authenticationType'

test('parses as aws auth type', async() => {
const authType = AuthenticationTypeUtil.FromCredentials(JSON.stringify(
{
secretAccessKey: 'I am super secret',
accessKeyId: 'I am an access id',
}
));

expect(authType).toEqual(AuthenticationTypeConst.AWS);
});

test('parses as azure auth type', async() => {
const authType = AuthenticationTypeUtil.FromCredentials(JSON.stringify(
{
appId: 'I am an application id',
password: 'I am super secret',
tenant: 'What tenant id',
subscriptionId: 'The sub has been identified',
}
));

expect(authType).toEqual(AuthenticationTypeConst.Azure);
});

test.each([
[JSON.stringify({
appId: 'I am an application id',
tenant: 'What tenant id',
subscriptionId: 'The sub has been identified',
})],
[JSON.stringify({
password: 'I am super secret',
tenant: 'What tenant id',
subscriptionId: 'The sub has been identified',
})],
[JSON.stringify({
appId: 'I am an application id',
password: 'I am super secret',
subscriptionId: 'The sub has been identified',
})],
[JSON.stringify({
appId: 'I am an application id',
password: 'I am super secret',
tenant: 'What tenant id',
})],
[JSON.stringify({
secretAccessKey: 'I am super secret',
})],
[JSON.stringify({
accessKeyId: 'I am an access id',
})],
])(
'missing property throws an error', (credentials) => {
expect(() => {
AuthenticationTypeUtil.FromCredentials(credentials)
}).toThrowError();
});
20 changes: 20 additions & 0 deletions sls-action/src/constants/authenticationType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export enum AuthenticationTypeConst {
AWS = 1,
Azure
}

export class AuthenticationTypeUtil {
public static FromCredentials(credentials:string) : AuthenticationTypeConst {
const creds = JSON.parse(credentials);

if(creds.appId && creds.password && creds.tenant && creds.subscriptionId){
return AuthenticationTypeConst.Azure;
}

if(creds.accessKeyId && creds.secretAccessKey){
return AuthenticationTypeConst.AWS;
}

throw new Error('Invalid credentials provided');
}
}
34 changes: 34 additions & 0 deletions sls-action/src/credentialParser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { CredentialParser } from './CredentialParser';
import { AuthenticationTypeConst } from './constants/authenticationType'

test('aws credentials can be parsed', async () => {
const credentials = {
accessKeyId: 'I am an access id',
secretAccessKey: 'I am super secret',
}

const credentialParser = new CredentialParser(JSON.stringify(credentials));

credentialParser.setLoginVariables();

expect(process.env.AWS_ACCESS_KEY_ID).toEqual(credentials.accessKeyId);
expect(process.env.AWS_SECRET_ACCESS_KEY).toEqual(credentials.secretAccessKey);
});

test('azure credentials can be parsed', async () => {
const credentials = {
appId: 'I am an application id',
password: 'I am super secret',
tenant: 'What tenant id',
subscriptionId: 'The sub has been identified',
}

const credentialParser = new CredentialParser(JSON.stringify(credentials));

credentialParser.setLoginVariables();

expect(process.env.AZURE_CLIENT_ID).toEqual(credentials.appId);
expect(process.env.AZURE_CLIENT_SECRET).toEqual(credentials.password);
expect(process.env.AZURE_TENANT_ID).toEqual(credentials.tenant);
expect(process.env.AZURE_SUBSCRIPTION_ID).toEqual(credentials.subscriptionId);
});
46 changes: 46 additions & 0 deletions sls-action/src/credentialParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as core from '@actions/core';
import { AuthenticationTypeConst, AuthenticationTypeUtil } from './constants/authenticationType';
import { FormatType, SecretParser } from 'actions-secret-parser';

export class CredentialParser {
authType: AuthenticationTypeConst;
credentials: string;

constructor(credentials: string) {
this.credentials = credentials;
this.authType = AuthenticationTypeUtil.FromCredentials(credentials);
}

setLoginVariables() {
let secrets = new SecretParser(this.credentials, FormatType.JSON);
switch (this.authType) {
case AuthenticationTypeConst.AWS : {
this.setAWSSecrets(secrets);
break;
}
case AuthenticationTypeConst.Azure : {
this.setAzureServicePrincipal(secrets);
break;
}
default : {
throw new Error('Invalid Authentication Type')
}
}
}

setAWSSecrets(secrets:SecretParser) {
process.env.AWS_ACCESS_KEY_ID = secrets.getSecret('$.accessKeyId', false);
process.env.AWS_SECRET_ACCESS_KEY = secrets.getSecret('$.secretAccessKey', true);

core.info('AWS Login settings configured.');
}

setAzureServicePrincipal(secrets:SecretParser) {
process.env.AZURE_SUBSCRIPTION_ID = secrets.getSecret('$.subscriptionId', false);
process.env.AZURE_TENANT_ID = secrets.getSecret('$.tenant', false);
process.env.AZURE_CLIENT_ID = secrets.getSecret('$.appId', false);
process.env.AZURE_CLIENT_SECRET = secrets.getSecret('$.password', true);

core.info('Azure Login settings configured.');
}
}
21 changes: 21 additions & 0 deletions sls-action/src/initializeServerless.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { execSync, IExecSyncResult } from './utility';
import * as core from '@actions/core';

export class InitializeServerless {
public static async run() {
throwIfError(execSync("npm", "--version"));
const response = execSync("npm", 'install serverless');
core.info('Serverless framework installed');
return response;
}
}

function throwIfError(resultOfToolExecution: IExecSyncResult, errormsg?: string) {
if (resultOfToolExecution.code != 0) {
core.error("Error Code: [" + resultOfToolExecution.code + "]");
if (errormsg) {
core.error("Error: " + errormsg);
}
throw resultOfToolExecution;
}
}
12 changes: 11 additions & 1 deletion sls-action/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as core from '@actions/core';
import { SlsCli } from './slsCli';
import { SlsOptions } from './slsOptions';
import { CredentialParser } from './CredentialParser';
import { InitializeServerless } from './initializeServerless';

async function run() {
try {
Expand All @@ -10,11 +12,19 @@ async function run() {
const yamlFile = core.getInput('yamlFile');
core.debug(`yamlFile=${yamlFile}`);

const creds = core.getInput('credentials', { required: true });

const slsOptions: SlsOptions = {
command: 'version',
yamlFile: yamlFile
yamlFile: yamlFile,
credentials: creds,
}

const credentialParser = new CredentialParser(creds);
credentialParser.setLoginVariables();

InitializeServerless.run();

const output = await SlsCli.run(slsOptions);
console.log(`serverless stdout:\n\n${output.stdout}`);

Expand Down
5 changes: 3 additions & 2 deletions sls-action/src/slsCli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { SlsOptions } from '../src/slsOptions';
test('sls cli can be invoked', async () => {
jest.setTimeout(15000);
const slsOptions: SlsOptions = {
command: 'version',
yamlFile: undefined
command: '--version',
yamlFile: undefined,
credentials: ''
}

const output = await SlsCli.run(slsOptions);
Expand Down
34 changes: 13 additions & 21 deletions sls-action/src/slsCli.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
import * as exec from '@actions/exec';
import * as core from '@actions/core';
import { SlsOptions } from './slsOptions';
import { execSync, IExecSyncResult } from './utility';

export class SlsCli {

public static async run(slsOptions: SlsOptions) {
// TODO: sniff test params; possibly put isValid method on SlsOptions

let output: string = '';
let errOutput: string ='';
throwIfError(execSync("npx", "--version"));
const response = execSync("npx", `sls ${slsOptions.command}`);
return response;
}
}

const execOptions = {
listeners: {
stdout: (data: Buffer) => {
output += data.toString();
},
stderr: (data: Buffer) => {
errOutput += data.toString();
}
function throwIfError(resultOfToolExecution: IExecSyncResult, errormsg?: string) {
if (resultOfToolExecution.code != 0) {
core.error("Error Code: [" + resultOfToolExecution.code + "]");
if (errormsg) {
core.error("Error: " + errormsg);
}
};

await exec.exec("npx", ["sls", slsOptions.command], execOptions);
return {
stdout: output,
stderr: errOutput
}
throw resultOfToolExecution;
}
}
1 change: 1 addition & 0 deletions sls-action/src/slsOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface SlsOptions {
command: string;
yamlFile?: string | undefined;
credentials: string;
}
Loading