Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: move oauth secrets to parameter store #10413

Merged
merged 17 commits into from
May 19, 2022
Merged
2 changes: 1 addition & 1 deletion .eslint-dictionary.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,4 @@
"xcode",
"yaml",
"yyyymmddhhmmss"
]
]
18 changes: 15 additions & 3 deletions packages/amplify-category-auth/amplify-plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
{
"name": "auth",
"type": "category",
"commands": ["add", "console", "enable", "import", "push", "remove", "update", "help", "override"],
"eventHandlers": []
}
"commands": [
"add",
"console",
"enable",
"import",
"push",
"remove",
"update",
"help",
"override"
],
"eventHandlers": [
"PrePush"
]
}
Original file line number Diff line number Diff line change
@@ -1,104 +1,101 @@
const response = require('cfn-response');
const aws = require('aws-sdk');
const identity = new aws.CognitoIdentityServiceProvider();
exports.handler = (event, context, callback) => {
const ssm = new aws.SSM();

exports.handler = async(event, context) => {
try {
const userPoolId = event.ResourceProperties.userPoolId;
let responseData;
const parameter = await ssm.getParameter({
akshbhu marked this conversation as resolved.
Show resolved Hide resolved
Name: process.env['hostedUIProviderCreds'],
WithDecryption: true,
}).promise();
let hostedUIProviderMeta = JSON.parse(event.ResourceProperties.hostedUIProviderMeta);
let hostedUIProviderCreds = JSON.parse(event.ResourceProperties.hostedUIProviderCreds);
if (hostedUIProviderCreds.length === 0) {
response.send(event, context, response.SUCCESS, {});
}
let hostedUIProviderCreds = JSON.parse(parameter.Parameter.Value);
if (event.RequestType == 'Delete') {
response.send(event, context, response.SUCCESS, {});
await response.send(event, context, response.SUCCESS, {});
}
if (event.RequestType == 'Update' || event.RequestType == 'Create') {
let getRequestParams = providerName => {
let providerMetaIndex = hostedUIProviderMeta.findIndex(provider => provider.ProviderName === providerName);
let providerMeta = hostedUIProviderMeta[providerMetaIndex];
let providerCredsIndex = hostedUIProviderCreds.findIndex(provider => provider.ProviderName === providerName);
let providerCreds = hostedUIProviderCreds[providerCredsIndex];
let requestParams = {
ProviderName: providerMeta.ProviderName,
UserPoolId: userPoolId,
AttributeMapping: providerMeta.AttributeMapping,
};
if (providerMeta.ProviderName === 'SignInWithApple') {
if (providerCreds.client_id && providerCreds.team_id && providerCreds.key_id && providerCreds.private_key) {
requestParams.ProviderDetails = {
client_id: providerCreds.client_id,
team_id: providerCreds.team_id,
key_id: providerCreds.key_id,
private_key: providerCreds.private_key,
authorize_scopes: providerMeta.authorize_scopes,
};
} else {
requestParams = null;
}
const result = await identity.listIdentityProviders({ UserPoolId: userPoolId, MaxResults: 60 }).promise();
let providerList = result.Providers.map(provider => provider.ProviderName);
let providerListInParameters = hostedUIProviderMeta.map(provider => provider.ProviderName);
for (const providerMetadata of hostedUIProviderMeta){
if (providerList.includes(providerMetadata.ProviderName)) {
responseData = await updateIdentityProvider(providerMetadata.ProviderName,userPoolId, hostedUIProviderMeta, hostedUIProviderCreds);
} else {
if (providerCreds.client_id && providerCreds.client_secret) {
requestParams.ProviderDetails = {
client_id: providerCreds.client_id,
client_secret: providerCreds.client_secret,
authorize_scopes: providerMeta.authorize_scopes,
};
} else {
requestParams = null;
}
}
return requestParams;
};
let createIdentityProvider = providerName => {
let requestParams = getRequestParams(providerName);
if (!requestParams) {
return Promise.resolve();
responseData = await createIdentityProvider(providerMetadata.ProviderName,userPoolId, hostedUIProviderMeta, hostedUIProviderCreds);
}
requestParams.ProviderType = requestParams.ProviderName;
return identity.createIdentityProvider(requestParams).promise();
};
let updateIdentityProvider = providerName => {
let requestParams = getRequestParams(providerName);
if (!requestParams) {
return Promise.resolve();
for (const provider of providerList){
if (!providerListInParameters.includes(provider)) {
responseData = await deleteIdentityProvider(provider,userPoolId);
}
return identity.updateIdentityProvider(requestParams).promise();
};
let deleteIdentityProvider = providerName => {
let params = { ProviderName: providerName, UserPoolId: userPoolId };
return identity.deleteIdentityProvider(params).promise();
};
let providerPromises = [];
identity
.listIdentityProviders({ UserPoolId: userPoolId, MaxResults: 60 })
.promise()
.then(result => {
console.log(result);
let providerList = result.Providers.map(provider => provider.ProviderName);
let providerListInParameters = hostedUIProviderMeta.map(provider => provider.ProviderName);
hostedUIProviderMeta.forEach(providerMetadata => {
if (providerList.indexOf(providerMetadata.ProviderName) > -1) {
providerPromises.push(updateIdentityProvider(providerMetadata.ProviderName));
} else {
providerPromises.push(createIdentityProvider(providerMetadata.ProviderName));
}
});
providerList.forEach(provider => {
if (providerListInParameters.indexOf(provider) < 0) {
providerPromises.push(deleteIdentityProvider(provider));
}
});
return Promise.all(providerPromises);
})
.then(() => {
response.send(event, context, response.SUCCESS, {});
})
.catch(err => {
console.log(err.stack);
response.send(event, context, response.FAILED, { err });
});
console.log(responseData);
await response.send(event, context, response.SUCCESS, {});
}
} catch (err) {
console.log(err.stack);
response.send(event, context, response.FAILED, { err });
await response.send(event, context, response.FAILED, { err });
}
};

const getRequestParams = (providerName,userPoolId, hostedUIProviderMeta, hostedUIProviderCreds) => {
const providerMetaIndex = hostedUIProviderMeta.findIndex(provider => provider.ProviderName === providerName);
const providerMeta = hostedUIProviderMeta[providerMetaIndex];
const providerCredsIndex = hostedUIProviderCreds.findIndex(provider => provider.ProviderName === providerName);
const providerCreds = hostedUIProviderCreds[providerCredsIndex];
let requestParams = {
ProviderName: providerMeta.ProviderName,
UserPoolId: userPoolId,
AttributeMapping: providerMeta.AttributeMapping,
};
if (providerMeta.ProviderName === 'SignInWithApple') {
if (providerCreds.client_id && providerCreds.team_id && providerCreds.key_id && providerCreds.private_key) {
requestParams.ProviderDetails = {
client_id: providerCreds.client_id,
team_id: providerCreds.team_id,
key_id: providerCreds.key_id,
private_key: providerCreds.private_key,
authorize_scopes: providerMeta.authorize_scopes,
};
} else {
requestParams = null;
}
} else {
if (providerCreds.client_id && providerCreds.client_secret) {
requestParams.ProviderDetails = {
client_id: providerCreds.client_id,
client_secret: providerCreds.client_secret,
authorize_scopes: providerMeta.authorize_scopes,
};
} else {
requestParams = null;
}
}
return requestParams;
};

const deleteIdentityProvider = async (providerName, userPoolId) => {
let params = { ProviderName: providerName, UserPoolId: userPoolId };
return identity.deleteIdentityProvider(params).promise();
};


const createIdentityProvider = async (providerName, userPoolId, hostedUIProviderMeta, hostedUIProviderCreds) => {
let requestParams = getRequestParams(providerName, userPoolId, hostedUIProviderMeta, hostedUIProviderCreds);
if (!requestParams) {
return;
}
requestParams.ProviderType = requestParams.ProviderName;
return identity.createIdentityProvider(requestParams).promise();
};

const updateIdentityProvider = async (providerName, userPoolId, hostedUIProviderMeta, hostedUIProviderCreds) => {
let requestParams = getRequestParams(providerName, userPoolId, hostedUIProviderMeta, hostedUIProviderCreds);
if (!requestParams) {
return;
}
return identity.updateIdentityProvider(requestParams).promise();
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { executeAmplifyHeadlessCommand } from '../../../src';
import { ImportAuthRequest } from 'amplify-headless-interface';
import { messages } from '../../provider-utils/awscloudformation/assets/string-maps';
import { printer } from 'amplify-prompts';
import { stateManager } from 'amplify-cli-core';
import { messages } from '../../provider-utils/awscloudformation/assets/string-maps';
import { executeAmplifyHeadlessCommand } from '../..';

jest.mock('amplify-prompts', () => ({
printer: {
Expand Down Expand Up @@ -137,9 +137,9 @@ describe('import auth headless', () => {
input: {
command: 'import',
},
usageData : {
pushHeadlessFlow : jest.fn()
}
usageData: {
pushHeadlessFlow: jest.fn(),
},
};
});

Expand Down Expand Up @@ -189,7 +189,7 @@ describe('import auth headless', () => {
await executeAmplifyHeadlessCommand(mockContext, headlessPayloadString);

expect(printer.warn).toBeCalledWith(
'Auth has already been imported to this project and cannot be modified from the CLI. To modify, run "amplify remove auth" to unlink the imported auth resource. Then run "amplify import auth".',
'Auth has already been imported to this project and cannot be modified from the CLI. To modify, run "amplify remove auth" to un-link the imported auth resource. Then run "amplify import auth".',
);
});

Expand All @@ -209,11 +209,11 @@ describe('import auth headless', () => {

fail('should throw error');
} catch (e) {
expect(e.message).toBe(`The previously configured Cognito User Pool: '' (user-pool-123) cannot be found.`);
expect(e.message).toBe('The previously configured Cognito User Pool: \'\' (user-pool-123) cannot be found.');
}
});

it('should throw web clients not found exception ', async () => {
it('should throw web clients not found exception', async () => {
stateManager_mock.getMeta = jest.fn().mockReturnValue({
providers: {
awscloudformation: {},
Expand All @@ -239,7 +239,7 @@ describe('import auth headless', () => {
awscloudformation: {},
},
});
const INVALID_USER_POOL_ID = USER_POOL_ID + '-invalid';
const INVALID_USER_POOL_ID = `${USER_POOL_ID}-invalid`;
const invalidHeadlessPayload = {
...headlessPayload,
userPoolId: INVALID_USER_POOL_ID,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
import * as remove from '../../commands/auth/remove';
import { messages } from '../../provider-utils/awscloudformation/assets/string-maps';
import { $TSContext } from 'amplify-cli-core';
import { printer } from 'amplify-prompts';
import * as remove from '../../commands/auth/remove';
import { messages } from '../../provider-utils/awscloudformation/assets/string-maps';

jest.mock('amplify-prompts', () => ({
printer: {
info: jest.fn(),
},
}));

jest.mock('../../provider-utils/awscloudformation/auth-secret-manager/sync-oauth-secrets');

const saveCLIInputPayload_mock = jest.fn();

jest.mock('fs-extra');

jest.mock('../../provider-utils/awscloudformation/auth-inputs-manager/auth-input-state', () => {
return {
AuthInputState: jest.fn().mockImplementation(() => {
return {
isCLIInputsValid: jest.fn(),
getCLIInputPayload: jest.fn().mockImplementation(() => ({
cognitoConfig: {
userPoolGroupList: ['admin'],
},
})),
saveCLIInputPayload: saveCLIInputPayload_mock,
};
}),
};
});
jest.mock('../../provider-utils/awscloudformation/auth-inputs-manager/auth-input-state', () => ({
AuthInputState: jest.fn().mockImplementation(() => ({
isCLIInputsValid: jest.fn(),
getCLIInputPayload: jest.fn().mockImplementation(() => ({
cognitoConfig: {
userPoolGroupList: ['admin'],
},
})),
saveCLIInputPayload: saveCLIInputPayload_mock,
})),
}));

jest.mock('amplify-cli-core', () => ({
AmplifySupportedService: {
Expand Down Expand Up @@ -111,20 +109,20 @@ const mockContext = {
};
const context_stub_typed = mockContext as unknown as $TSContext;

test(`remove method should detect existing categories metadata and display warning with no userPoolGroup`, async () => {
test('remove method should detect existing categories metadata and display warning with no userPoolGroup', async () => {
await remove.run(context_stub_typed);
expect(printer.info).toBeCalledWith(warningString);
expect(context_stub_typed.amplify.removeResource).toBeCalled();
});

test(`remove method should not display warning when there is no dependency with no userPoolGroup`, async () => {
test('remove method should not display warning when there is no dependency with no userPoolGroup', async () => {
jest.clearAllMocks();
await remove.run(context_stub_typed);
expect(printer.info).not.toBeCalledWith(warningString);
expect(context_stub_typed.amplify.removeResource).toBeCalled();
});

test(`remove method should still be called even when warning displayed for existing category resource and remmoves userPool group`, async () => {
test('remove method should still be called even when warning displayed for existing category resource and remmoves userPool group', async () => {
await remove.run(context_stub_typed);
expect(saveCLIInputPayload_mock).toBeCalledWith({ cognitoConfig: { userPoolGroupList: [] } });
});
Loading