Skip to content
This repository was archived by the owner on Jan 27, 2025. It is now read-only.

Commit 4c8eb29

Browse files
authored
Add whoami command (#37)
* Add whoami command * Fix expired or invalid session error log * Add configured region to whoami command output * Pad start of whoami output keys * Move whoami command to new file and reformat output * Fix lint errors * Make status an alias for whoami and refactor utils * Minor adjustment to CHANGELOG * Minor adjustment to README
1 parent 59dd5c6 commit 4c8eb29

File tree

9 files changed

+84
-40
lines changed

9 files changed

+84
-40
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# awsx changelog
22

3+
## 1.2.0 (October 16, 2021)
4+
5+
- Add `whoami` command
6+
- Deprecate existing `status` command (made alias for `whoami`)
7+
38
## 1.1.3 (October 16, 2021)
49

510
- Switch to npm

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ If you don't have MFA set up on your AWS account you can enable it by following
7878

7979
#### `awsx remove-assume-role-profile [profile]`
8080

81-
### Checking status of current session
81+
### Show what AWS account and identity you're using
8282

83-
#### `awsx status`
83+
#### `awsx whoami` or `awsx status`
8484

8585
## Contributing
8686

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "awsx",
33
"description": "AWS CLI profile switcher with MFA support",
4-
"version": "1.1.3",
4+
"version": "1.2.0",
55
"author": "Neo Financial Engineering <engineering@neofinancial.com>",
66
"license": "MIT",
77
"repository": {

src/app.ts

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import inquirer from 'inquirer';
22
import chalk from 'chalk';
33
import yargs, { Argv } from 'yargs';
44
import updateNotifier from 'update-notifier';
5-
import { STS } from 'aws-sdk';
6-
import { promisify } from 'util';
75

86
import {
97
configFileCheck,
@@ -27,9 +25,12 @@ import getTemporaryCredentials, {
2725
} from './mfa-login';
2826
import exportEnvironmentVariables from './exporter';
2927
import pkg from '../package.json';
28+
import { getCurrentProfile } from './lib/profile';
29+
import { whoami } from './command/whoami';
3030

3131
const profiles = getProfileNames();
32-
let currentProfile = process.env.AWS_PROFILE || '';
32+
33+
let currentProfile = getCurrentProfile();
3334

3435
const validateMfaExpiry = (mfaExpiry: number): boolean | string => {
3536
if (mfaExpiry <= 0) {
@@ -596,30 +597,6 @@ const disableMfa = async (name?: string): Promise<void> => {
596597
console.log(chalk.green(`Disabled MFA on profile '${profileName}'`));
597598
};
598599

599-
const timedOutStatusCheck = async (): Promise<void> => {
600-
const delay = promisify(setTimeout);
601-
602-
await delay(1500);
603-
};
604-
605-
const status = async (): Promise<void> => {
606-
const sts = new STS();
607-
608-
const response = await Promise.race([sts.getCallerIdentity().promise(), timedOutStatusCheck()]);
609-
610-
if (response) {
611-
const role = response.Arn?.split(':')[5];
612-
const roleName = role?.split('/')[1];
613-
614-
console.log(chalk.green(`Role -> ${roleName}`));
615-
console.log(chalk.green(`Account -> ${response.Account}`));
616-
console.log(chalk.green(`Profile -> ${currentProfile}`));
617-
} else {
618-
console.log(chalk.red(`Session is expired or invalid`));
619-
process.exit();
620-
}
621-
};
622-
623600
const awsx = (): void => {
624601
try {
625602
configFileCheck();
@@ -878,13 +855,13 @@ const awsx = (): void => {
878855
},
879856
})
880857
.command({
881-
command: 'status',
882-
describe: 'Show the status of your current awsx session',
858+
command: ['whoami', 'status'],
859+
describe: "Show what AWS account and identity you're using",
883860
handler: async (): Promise<void> => {
884861
try {
885-
await status();
886-
} catch {
887-
console.log(chalk.red(`Session is expired or invalid`));
862+
await whoami();
863+
} catch (error) {
864+
console.log(chalk.red(error.message));
888865
}
889866
},
890867
})

src/command/whoami.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import chalk from 'chalk';
2+
import AWS, { STS, IAM } from 'aws-sdk';
3+
4+
import { getCurrentProfile } from '../lib/profile';
5+
import { timeout } from '../lib/time';
6+
7+
const assumedRole = (arn?: string): string | undefined => {
8+
return arn ? arn.split('/')[1] : undefined;
9+
};
10+
11+
const whoami = async (): Promise<void> => {
12+
const sts = new STS();
13+
const iam = new IAM();
14+
15+
const identity = await Promise.race([sts.getCallerIdentity().promise(), timeout(1500)]);
16+
17+
if (!identity) {
18+
console.log(chalk.red(`Session is expired or invalid`));
19+
process.exit();
20+
}
21+
22+
// Should be available if the identity call returns truthy, so shouldn't need a second timed-out race.
23+
const aliases = await iam.listAccountAliases().promise();
24+
25+
const whoAmI = {
26+
Account: identity.Account,
27+
Aliases: aliases?.AccountAliases,
28+
Arn: identity.Arn,
29+
AssumedRole: assumedRole(identity.Arn),
30+
Profile: getCurrentProfile(),
31+
Region: AWS.config.region,
32+
UserId: identity.UserId,
33+
};
34+
35+
const maxKeyLength = Math.max.apply(
36+
null,
37+
Object.keys(whoAmI).map((k) => k.length)
38+
);
39+
40+
for (const [key, value] of Object.entries(whoAmI)) {
41+
console.log(chalk.green(`${key.padEnd(maxKeyLength)} -> ${value}`));
42+
}
43+
};
44+
45+
export { assumedRole, whoami };

src/lib/profile.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const getCurrentProfile = (): string => {
2+
return process.env.AWS_PROFILE || '';
3+
};
4+
5+
export { getCurrentProfile };

src/lib/time.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { promisify } from 'util';
2+
3+
const timeout = promisify(setTimeout);
4+
5+
export { timeout };

src/mfa-login.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const createStsParameters = (
4545
return {
4646
DurationSeconds: configuration.sessionLengthInSeconds,
4747
SerialNumber: configuration.mfaDeviceArn,
48-
TokenCode: mfaToken
48+
TokenCode: mfaToken,
4949
};
5050
};
5151

@@ -57,7 +57,7 @@ const createTemporaryCredentials = (
5757
profileName: profileName,
5858
awsAccessKeyId: credentials.AccessKeyId,
5959
awsSecretAccessKey: credentials.SecretAccessKey,
60-
awsSessionToken: credentials.SessionToken
60+
awsSessionToken: credentials.SessionToken,
6161
};
6262
};
6363

@@ -68,9 +68,7 @@ const getTemporaryCredentials = async (
6868
): Promise<void> => {
6969
const stsParameters = createStsParameters(configuration, mfaToken);
7070

71-
const response = await createStsClient(configuration)
72-
.getSessionToken(stsParameters)
73-
.promise();
71+
const response = await createStsClient(configuration).getSessionToken(stsParameters).promise();
7472

7573
if (response.Credentials) {
7674
onLogin(createTemporaryCredentials(configuration.profileName, response.Credentials));

test/command/whoami.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { assumedRole } from '../../src/command/whoami';
2+
3+
describe('whoami', () => {
4+
test('assumedRole', () => {
5+
expect(
6+
assumedRole('arn:aws:sts::111111111111:assumed-role/foo-bar/aws-sdk-js-1111111111111')
7+
).toEqual('foo-bar');
8+
});
9+
});

0 commit comments

Comments
 (0)