Skip to content

Commit

Permalink
feat: handle demo mode
Browse files Browse the repository at this point in the history
  • Loading branch information
mdonnalley committed Sep 30, 2020
1 parent 7a11772 commit f4a03ca
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 15 deletions.
4 changes: 3 additions & 1 deletion messages/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@
"setAlias": "set an alias for the authenticated org",
"instanceUrl": "the login URL of the instance the org lives on",
"authorizeCommandSuccess": "Successfully authorized %s with org ID %s",
"authorizeCommandCloseBrowser": "You may now close the browser"
"authorizeCommandCloseBrowser": "You may now close the browser",
"warnAuth": "Logging in to a business or production org is not recommended on a demo or shared machine. Please run \"sfdx force:auth:logout --targetusername <your username> --noprompt\" when finished using this org, which is similar to logging out of the org in the browser.\n\nDo you want to authorize this org, %s, for use with the Salesforce CLI (y/n)?",
"noPromptAuth": "do not prompt for auth confirmation in demo mode"
}
9 changes: 9 additions & 0 deletions src/commands/auth/jwt/grant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import * as os from 'os';
import { flags, FlagsConfig, SfdxCommand } from '@salesforce/command';
import { AuthFields, AuthInfo, AuthRemover, Messages, SfdxError } from '@salesforce/core';
import { Prompts } from '../../../prompts';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-auth', 'jwt.grant');
Expand Down Expand Up @@ -48,11 +49,19 @@ export default class Grant extends SfdxCommand {
char: 'a',
description: commonMessages.getMessage('setAlias'),
}),
noprompt: flags.boolean({
char: 'p',
description: commonMessages.getMessage('noPromptAuth'),
required: false,
hidden: true,
}),
};

public async run(): Promise<AuthFields> {
let result: AuthFields = {};

if (await Prompts.shouldExitCommand(this.ux, this.flags.noprompt)) return {};

try {
const authInfo = await this.initAuthInfo();

Expand Down
11 changes: 4 additions & 7 deletions src/commands/auth/logout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import * as os from 'os';
import { flags, FlagsConfig, SfdxCommand } from '@salesforce/command';
import { AuthConfigs, AuthRemover, Global, Messages, Mode, SfdxError } from '@salesforce/core';
import { Prompts } from '../../prompts';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-auth', 'logout');
Expand Down Expand Up @@ -58,12 +59,8 @@ export default class Logout extends SfdxCommand {
}

private async shouldRunCommand(authConfigs: AuthConfigs): Promise<boolean> {
if (this.flags.noprompt || Global.getEnvironmentMode() === Mode.DEMO) {
return true;
} else {
const orgsToDelete = [[...authConfigs.keys()].join(os.EOL)];
const answer = await this.ux.prompt(messages.getMessage('logoutCommandYesNo', orgsToDelete));
return answer.toUpperCase() === 'YES' || answer.toUpperCase() === 'Y';
}
const orgsToDelete = [[...authConfigs.keys()].join(os.EOL)];
const message = messages.getMessage('logoutCommandYesNo', orgsToDelete);
return Prompts.shouldRunCommand(this.ux, this.flags.noprompt, message);
}
}
9 changes: 9 additions & 0 deletions src/commands/auth/sfdxurl/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import * as os from 'os';
import { flags, FlagsConfig, SfdxCommand } from '@salesforce/command';
import { AuthFields, AuthInfo, fs, Messages } from '@salesforce/core';
import { Prompts } from '../../../prompts';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-auth', 'sfdxurl.store');
Expand Down Expand Up @@ -37,9 +38,17 @@ export default class Store extends SfdxCommand {
char: 'a',
description: commonMessages.getMessage('setAlias'),
}),
noprompt: flags.boolean({
char: 'p',
description: commonMessages.getMessage('noPromptAuth'),
required: false,
hidden: true,
}),
};

public async run(): Promise<AuthFields> {
if (await Prompts.shouldExitCommand(this.ux, this.flags.noprompt)) return {};

const sfdxAuthUrl = await fs.readFile(this.flags.sfdxurlfile, 'utf8');
const oauth2Options = AuthInfo.parseSfdxAuthUrl(sfdxAuthUrl);
const authInfo = await AuthInfo.create({ oauth2Options });
Expand Down
39 changes: 39 additions & 0 deletions src/prompts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2020, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { Messages, Global, Mode } from '@salesforce/core';
import { UX } from '@salesforce/command';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-auth', 'messages');

export class Prompts {
public static async shouldExitCommand(ux: UX, noPrompt?: boolean, message?: string): Promise<boolean> {
if (noPrompt || Global.getEnvironmentMode() !== Mode.DEMO) {
return false;
} else {
const answer = await ux.prompt(message || messages.getMessage('warnAuth'));
return Prompts.answeredNo(answer);
}
}

public static async shouldRunCommand(ux: UX, noPrompt?: boolean, message?: string): Promise<boolean> {
if (noPrompt || Global.getEnvironmentMode() === Mode.DEMO) {
return true;
} else {
const answer = await ux.prompt(message || messages.getMessage('warnAuth'));
return Prompts.answeredYes(answer);
}
}

private static answeredYes(answer: string): boolean {
return ['YES', 'Y'].includes(answer.toUpperCase());
}

private static answeredNo(answer: string): boolean {
return !['YES', 'Y'].includes(answer.toUpperCase());
}
}
61 changes: 54 additions & 7 deletions test/commands/auth/jwt/grant.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { $$, expect, test } from '@salesforce/command/lib/test';
import { AuthFields, AuthInfo, SfdxError } from '@salesforce/core';
import { MockTestOrgData } from '@salesforce/core/lib/testSetup';
import { StubbedType, stubInterface, stubMethod } from '@salesforce/ts-sinon';
import { UX } from '@salesforce/command';

interface Options {
authInfoCreateFails?: boolean;
Expand Down Expand Up @@ -46,7 +47,6 @@ describe('auth:jwt:grant', async () => {
test
.do(async () => prepareStubs())
.stdout()
// eslint-disable-next-line prettier/prettier
.command(['auth:jwt:grant', '-u', testData.username, '-f', 'path/to/key.json', '-i', '123456', '--json'])
.it('should return auth fields', (ctx) => {
const response = JSON.parse(ctx.stdout);
Expand Down Expand Up @@ -109,7 +109,6 @@ describe('auth:jwt:grant', async () => {
test
.do(async () => prepareStubs())
.stdout()
// eslint-disable-next-line prettier/prettier
.command(['auth:jwt:grant', '-u', testData.username, '-f', 'path/to/key.json', '-i', '123456', '-s', '--json'])
.it('should set defaultusername to username when -s is provided', (ctx) => {
const response = JSON.parse(ctx.stdout);
Expand Down Expand Up @@ -158,7 +157,6 @@ describe('auth:jwt:grant', async () => {
test
.do(async () => prepareStubs())
.stdout()
// eslint-disable-next-line prettier/prettier
.command(['auth:jwt:grant', '-u', testData.username, '-f', 'path/to/key.json', '-i', '123456', '-d', '--json'])
.it('should set defaultdevhubusername to username when -d is provided', (ctx) => {
const response = JSON.parse(ctx.stdout);
Expand Down Expand Up @@ -237,7 +235,6 @@ describe('auth:jwt:grant', async () => {
test
.do(async () => prepareStubs())
.stdout()
// eslint-disable-next-line prettier/prettier
.command(['auth:jwt:grant', '-u', testData.username, '-f', 'path/to/key.json', '--json'])
.it('should throw an error when client id (-i) is not provided', (ctx) => {
const response = JSON.parse(ctx.stdout);
Expand All @@ -248,7 +245,6 @@ describe('auth:jwt:grant', async () => {
test
.do(async () => prepareStubs({ authInfoCreateFails: true }))
.stdout()
// eslint-disable-next-line prettier/prettier
.command(['auth:jwt:grant', '-u', testData.username, '-f', 'path/to/key.json', '-i', '123456INVALID', '--json'])
.it('should throw an error when client id is invalid', (ctx) => {
const response = JSON.parse(ctx.stdout);
Expand All @@ -259,7 +255,6 @@ describe('auth:jwt:grant', async () => {
test
.do(async () => prepareStubs())
.stdout()
// eslint-disable-next-line prettier/prettier
.command(['auth:jwt:grant', '-u', testData.username, '-i', '123456', '--json'])
.it('should throw an error when private key file (-f) is not provided', (ctx) => {
const response = JSON.parse(ctx.stdout);
Expand All @@ -270,11 +265,63 @@ describe('auth:jwt:grant', async () => {
test
.do(async () => prepareStubs({ existingAuth: true }))
.stdout()
// eslint-disable-next-line prettier/prettier
.command(['auth:jwt:grant', '-u', testData.username, '-f', 'path/to/key.json', '-i', '123456', '--json'])
.it('should not throw an error when the authorization already exists', (ctx) => {
const response = JSON.parse(ctx.stdout);
expect(response.status).to.equal(0);
expect(response.result).to.deep.equal(authFields);
});

test
.do(async () => {
await prepareStubs();
process.env['SFDX_ENV'] = 'demo';
$$.SANDBOX.stub(UX.prototype, 'prompt').returns(Promise.resolve('yes'));
})
.finally(() => {
delete process.env['SFDX_ENV'];
})
.stdout()
.command(['auth:jwt:grant', '-u', testData.username, '-f', 'path/to/key.json', '-i', '123456', '--json'])
.it('should auth when in demo mode (SFDX_ENV=demo) and prompt is answered with yes', (ctx) => {
const response = JSON.parse(ctx.stdout);
expect(response.status).to.equal(0);
expect(response.result).to.deep.equal(authFields);
expect(authInfoStub.save.callCount).to.equal(1);
});

test
.do(async () => {
await prepareStubs();
process.env['SFDX_ENV'] = 'demo';
$$.SANDBOX.stub(UX.prototype, 'prompt').returns(Promise.resolve('no'));
})
.finally(() => {
delete process.env['SFDX_ENV'];
})
.stdout()
.command(['auth:jwt:grant', '-u', testData.username, '-f', 'path/to/key.json', '-i', '123456', '--json'])
.it('should do nothing when in demo mode (SFDX_ENV=demo) and prompt is answered with no', (ctx) => {
const response = JSON.parse(ctx.stdout);
expect(response.status).to.equal(0);
expect(response.result).to.deep.equal({});
expect(authInfoStub.save.callCount).to.equal(0);
});

test
.do(async () => {
await prepareStubs();
process.env['SFDX_ENV'] = 'demo';
})
.finally(() => {
delete process.env['SFDX_ENV'];
})
.stdout()
.command(['auth:jwt:grant', '-u', testData.username, '-f', 'path/to/key.json', '-i', '123456', '--json', '-p'])
.it('should ignore prompt when in demo mode (SFDX_ENV=demo) and -p is provided', (ctx) => {
const response = JSON.parse(ctx.stdout);
expect(response.status).to.equal(0);
expect(response.result).to.deep.equal(authFields);
expect(authInfoStub.save.callCount).to.equal(1);
});
});
54 changes: 54 additions & 0 deletions test/commands/auth/sfdxurl/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { $$, expect, test } from '@salesforce/command/lib/test';
import { AuthFields, AuthInfo, fs } from '@salesforce/core';
import { MockTestOrgData } from '@salesforce/core/lib/testSetup';
import { StubbedType, stubInterface, stubMethod } from '@salesforce/ts-sinon';
import { UX } from '@salesforce/command';

interface Options {
authInfoCreateFails?: boolean;
Expand Down Expand Up @@ -173,4 +174,57 @@ describe('auth:sfdxurl:store', async () => {
},
]);
});

test
.do(async () => {
await prepareStubs();
process.env['SFDX_ENV'] = 'demo';
$$.SANDBOX.stub(UX.prototype, 'prompt').returns(Promise.resolve('yes'));
})
.finally(() => {
delete process.env['SFDX_ENV'];
})
.stdout()
.command(['auth:sfdxurl:store', '-f', 'path/to/key.txt', '--json'])
.it('should auth when in demo mode (SFDX_ENV=demo) and prompt is answered with yes', (ctx) => {
const response = JSON.parse(ctx.stdout);
expect(response.status).to.equal(0);
expect(response.result).to.deep.equal(authFields);
expect(authInfoStub.save.callCount).to.equal(1);
});

test
.do(async () => {
await prepareStubs();
process.env['SFDX_ENV'] = 'demo';
$$.SANDBOX.stub(UX.prototype, 'prompt').returns(Promise.resolve('no'));
})
.finally(() => {
delete process.env['SFDX_ENV'];
})
.stdout()
.command(['auth:sfdxurl:store', '-f', 'path/to/key.txt', '--json'])
.it('should do nothing when in demo mode (SFDX_ENV=demo) and prompt is answered with no', (ctx) => {
const response = JSON.parse(ctx.stdout);
expect(response.status).to.equal(0);
expect(response.result).to.deep.equal({});
expect(authInfoStub.save.callCount).to.equal(0);
});

test
.do(async () => {
await prepareStubs();
process.env['SFDX_ENV'] = 'demo';
})
.finally(() => {
delete process.env['SFDX_ENV'];
})
.stdout()
.command(['auth:sfdxurl:store', '-f', 'path/to/key.txt', '--json', '-p'])
.it('should ignore prompt when in demo mode (SFDX_ENV=demo) and -p is provided', (ctx) => {
const response = JSON.parse(ctx.stdout);
expect(response.status).to.equal(0);
expect(response.result).to.deep.equal(authFields);
expect(authInfoStub.save.callCount).to.equal(1);
});
});

0 comments on commit f4a03ca

Please sign in to comment.