diff --git a/src/commands/profiles/create.js b/src/commands/profiles/create.js index 16ce12aaa..03663d801 100644 --- a/src/commands/profiles/create.js +++ b/src/commands/profiles/create.js @@ -230,7 +230,7 @@ class ProfilesCreate extends BaseCommand { this.logger.debug(error); throw new TwilioCliError('Could not create an API Key.'); } - + await this.removeKeytarKeysByProfileId(this.profileId); this.userConfig.addProfile(this.profileId, this.accountSid, this.region, apiKey.sid, apiKey.secret); const configSavedMessage = await this.configFile.save(this.userConfig); @@ -239,6 +239,17 @@ class ProfilesCreate extends BaseCommand { ); this.logger.info(configSavedMessage); } + + async removeKeytarKeysByProfileId(profileId) { + if (this.userConfig.projects.find((p) => p.id === profileId)) { + const removed = await this.secureStorage.removeCredentials(profileId); + if (removed === true) { + this.logger.info('Deleted key from keytar.'); + } else { + this.logger.warn(`Could not delete ${profileId} key from keytar.`); + } + } + } } ProfilesCreate.aliases = ['profiles:add', 'login']; diff --git a/test/commands/profiles/create.test.js b/test/commands/profiles/create.test.js index 3c7d22127..33142d9ad 100644 --- a/test/commands/profiles/create.test.js +++ b/test/commands/profiles/create.test.js @@ -11,9 +11,13 @@ const helpMessages = require('../../../src/services/messaging/help-messages'); describe('commands', () => { describe('profiles', () => { describe('create', () => { - const createTest = (commandArgs = [], profileId = 'default') => + const createTest = (commandArgs = [], { profileId = 'default', addProjects = [], removeCred = true } = {}) => test .twilioFakeProfile(ConfigData) + .do((ctx) => { + ctx.userConfig = new ConfigData(); + addProjects.forEach((project) => ctx.userConfig.addProject(project, constants.FAKE_ACCOUNT_SID)); + }) .twilioCliEnv(Config) .twilioCreateCommand(ProfilesCreate, commandArgs) .stdout() @@ -35,6 +39,11 @@ describe('commands', () => { overwrite: true, }); ctx.testCmd.inquirer.prompt = fakePrompt; + }) + .do((ctx) => { + ctx.testCmd.secureStorage.removeCredentials = () => { + return removeCred; + }; }); const mockSuccess = (api) => { @@ -65,6 +74,36 @@ describe('commands', () => { ); }); + createTest([], { profileId: 'profile1', addProjects: ['profile1'] }) + .nock('https://api.twilio.com', mockSuccess) + .do((ctx) => ctx.testCmd.run()) + .it('runs profiles:create with existing profile in Projects', (ctx) => { + expect(ctx.stdout).to.equal(''); + expect(ctx.stderr).to.contain(helpMessages.AUTH_TOKEN_NOT_SAVED); + expect(ctx.stderr).to.contain('Saved profile1.'); + expect(ctx.stderr).to.contain('Deleted key from keytar.'); + expect(ctx.stderr).to.contain('configuration saved'); + expect(ctx.stderr).to.contain(`Created API Key ${constants.FAKE_API_KEY} and stored the secret in Config.`); + expect(ctx.stderr).to.contain( + `See: https://www.twilio.com/console/runtime/api-keys/${constants.FAKE_API_KEY}`, + ); + }); + + createTest([], { profileId: 'profile1', addProjects: ['profile1'], removeCred: false }) + .nock('https://api.twilio.com', mockSuccess) + .do((ctx) => ctx.testCmd.run()) + .it('runs profiles:create with existing profile in Projects with Keytar remove failed', (ctx) => { + expect(ctx.stdout).to.equal(''); + expect(ctx.stderr).to.contain(helpMessages.AUTH_TOKEN_NOT_SAVED); + expect(ctx.stderr).to.contain('Saved profile1.'); + expect(ctx.stderr).to.contain('Could not delete profile1 key from keytar.'); + expect(ctx.stderr).to.contain('configuration saved'); + expect(ctx.stderr).to.contain(`Created API Key ${constants.FAKE_API_KEY} and stored the secret in Config.`); + expect(ctx.stderr).to.contain( + `See: https://www.twilio.com/console/runtime/api-keys/${constants.FAKE_API_KEY}`, + ); + }); + createTest() .do((ctx) => { sinon.stub(os, 'hostname').returns('some_super_long_fake_hostname');