Skip to content

Commit

Permalink
Merge pull request #90 from servian/reorder_flags
Browse files Browse the repository at this point in the history
Reorder flags
  • Loading branch information
tristanmorgan authored Nov 8, 2022
2 parents cfb18ad + 347f8f1 commit 2ecd3fe
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 49 deletions.
32 changes: 17 additions & 15 deletions lib/awskeyring_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,15 @@ def list_role
end

desc 'env ACCOUNT', I18n.t('env_desc')
method_option :force, type: :boolean, aliases: '-f', desc: I18n.t('method_option.force'), default: false
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
method_option :unset, type: :boolean, aliases: '-u', desc: I18n.t('method_option.unset'), default: false
method_option :force, type: :boolean, aliases: '-f', desc: I18n.t('method_option.force'), default: false
# Print Env vars
def env(account = nil)
if options[:unset]
put_env_string(account: nil, key: nil, secret: nil, token: nil)
else
if $stdout.isatty && !options[:force]
warn I18n.t('message.ttyblock')
exit 1
end
output_safe(options[:force])
account = ask_check(
existing: account, message: I18n.t('message.account'),
validator: Awskeyring.method(:account_exists),
Expand All @@ -125,14 +122,11 @@ def env(account = nil)
end

desc 'json ACCOUNT', I18n.t('json_desc')
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
method_option :force, type: :boolean, aliases: '-f', desc: I18n.t('method_option.force'), default: false
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
# Print JSON for use with credential_process
def json(account) # rubocop:disable Metrics/AbcSize
if $stdout.isatty && !options[:force]
warn I18n.t('message.ttyblock')
exit 1
end
output_safe(options[:force])
account = ask_check(
existing: account, message: I18n.t('message.account'), validator: Awskeyring.method(:account_exists),
limited_to: Awskeyring.list_account_names
Expand Down Expand Up @@ -184,8 +178,8 @@ def import(account = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSiz
end

desc 'exec ACCOUNT command...', I18n.t('exec_desc')
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
method_option 'no-bundle', type: :boolean, aliases: '-b', desc: I18n.t('method_option.nobundle'), default: false
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
# execute an external command with env set
def exec(account, *command) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
if command.empty?
Expand All @@ -211,9 +205,9 @@ def exec(account, *command) # rubocop:disable Metrics/MethodLength, Metrics/AbcS

desc 'add ACCOUNT', I18n.t('add_desc')
method_option :key, type: :string, aliases: '-k', desc: I18n.t('method_option.key')
method_option :secret, type: :string, aliases: '-s', desc: I18n.t('method_option.secret')
method_option :mfa, type: :string, aliases: '-m', desc: I18n.t('method_option.mfa')
method_option 'no-remote', type: :boolean, aliases: '-r', desc: I18n.t('method_option.noremote'), default: false
method_option :secret, type: :string, aliases: '-s', desc: I18n.t('method_option.secret')
# Add an Account
def add(account = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
account = ask_check(
Expand Down Expand Up @@ -242,8 +236,8 @@ def add(account = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize

desc 'update ACCOUNT', I18n.t('update_desc')
method_option :key, type: :string, aliases: '-k', desc: I18n.t('method_option.key')
method_option :secret, type: :string, aliases: '-s', desc: I18n.t('method_option.secret')
method_option 'no-remote', type: :boolean, aliases: '-r', desc: I18n.t('method_option.noremote'), default: false
method_option :secret, type: :string, aliases: '-s', desc: I18n.t('method_option.secret')
# Update an Account
def update(account = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
account = ask_check(
Expand Down Expand Up @@ -398,10 +392,10 @@ def token(account = nil, role = nil, code = nil) # rubocop:disable Metrics/AbcSi
end

desc 'console ACCOUNT', I18n.t('console_desc')
method_option :path, type: :string, aliases: '-p', desc: I18n.t('method_option.path')
method_option :browser, type: :string, aliases: '-b', desc: I18n.t('method_option.browser')
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
method_option 'no-open', type: :boolean, aliases: '-o', desc: I18n.t('method_option.noopen'), default: false
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
method_option :path, type: :string, aliases: '-p', desc: I18n.t('method_option.path')
# Open the AWS Console
def console(account = nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
account = ask_check(
Expand Down Expand Up @@ -550,6 +544,14 @@ def age_check_and_get(account:, no_token:)
cred
end

# warn if output is unsafe unless forced
def output_safe(force)
return if force || !$stdout.isatty

warn I18n.t('message.ttyblock')
exit 1
end

# print exports from map
def put_env_string(cred)
env_var = Awskeyring::Awsapi.get_env_array(cred)
Expand Down
71 changes: 56 additions & 15 deletions man/awskeyring.5
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ Adds an ACCOUNT to the keyring
\-k, \-\-key=KEY: AWS account key id\.
.
.br
\-s, \-\-secret=SECRET: AWS account secret\.
.
.br
\-m, \-\-mfa=MFA: AWS virtual mfa arn\.
.
.br
\-r, \-\-no\-remote: Do not validate with remote api\.
.
.br
\-s, \-\-secret=SECRET: AWS account secret\.
.
.TP
add\-role ROLE:
.
Expand All @@ -69,16 +69,16 @@ Open the AWS Console for the ACCOUNT
.br
.
.IP
\-p, \-\-path=PATH: The service PATH to open\.
\-b, \-\-browser=BROWSER: Specify an alternative browser\.
.
.br
\-b, \-\-browser=BROWSER: Specify an alternative browser\.
\-o, \-\-no\-open: Do not open the url\.
.
.br
\-n, \-\-no\-token: Do not use saved token\.
.
.br
\-o, \-\-no\-open: Do not open the url\.
\-p, \-\-path=PATH: The service PATH to open\.
.
.TP
env ACCOUNT:
Expand All @@ -89,13 +89,13 @@ Outputs bourne shell environment exports for an ACCOUNT
.br
.
.IP
\-n, \-\-no\-token: Do not use saved token\.
\-f, \-\-force: Force output to a tty\.
.
.br
\-u, \-\-unset, \-\-no\-unset: Unset environment variables\.
\-n, \-\-no\-token: Do not use saved token\.
.
.br
\-f, \-\-force: Force output to a tty\.
\-u, \-\-unset, \-\-no\-unset: Unset environment variables\.
.
.TP
exec ACCOUNT command\.\.\.:
Expand All @@ -106,10 +106,10 @@ Execute a COMMAND with the environment set for an ACCOUNT
.br
.
.IP
\-n, \-\-no\-token: Do not use saved token\.
\-b, \-\-no\-bundle: Unset Bundler environment variables\.
.
.br
\-b, \-\-no\-bundle: Unset Bundler environment variables\.
\-n, \-\-no\-token: Do not use saved token\.
.
.TP
help [COMMAND]:
Expand Down Expand Up @@ -148,10 +148,10 @@ Outputs AWS CLI compatible JSON for an ACCOUNT
.br
.
.IP
\-n, \-\-no\-token: Do not use saved token\.
\-f, \-\-force: Force output to a tty\.
.
.br
\-f, \-\-force: Force output to a tty\.
\-n, \-\-no\-token: Do not use saved token\.
.
.TP
list:
Expand Down Expand Up @@ -220,10 +220,10 @@ Updates an ACCOUNT in the keyring
\-k, \-\-key=KEY: AWS account key id\.
.
.br
\-s, \-\-secret=SECRET: AWS account secret\.
\-r, \-\-no\-remote: Do not validate with remote api\.
.
.br
\-r, \-\-no\-remote: Do not validate with remote api\.
\-s, \-\-secret=SECRET: AWS account secret\.
.
.SH "ENVIRONMENT"
The AWS_DEFAULT_REGION environment variable will be used for AWS API calls where specified or fall back to us\-east\-1 when not\.
Expand Down Expand Up @@ -296,6 +296,47 @@ complete \-C /usr/local/bin/awskeyring awskeyring
.
.IP "" 0
.
.SH "CONFIGURATION"
A Configuration file is stored in the users home directory at \fB~/\.awskeyring\fR as a JSON formatted file\. Most of the fields have a default value except the awskeyring field\.
.
.IP "" 4
.
.nf

{
"awskeyring": "awskeyring",
"browser": ["FireFox", "Google Chrome", "Safari"],
"console": ["ec2/v2", "cloudwatch", "iam"],
"keyage": 90
}
.
.fi
.
.IP "" 0
.
.IP "1." 4
The first field is the Keychain that your keys will be saved in\.
.
.br

.
.IP "2." 4
A list of your browsers to use the console command with\.
.
.br

.
.IP "3." 4
The next is the list of AWS Console pages autocomplete will present\.
.
.br

.
.IP "4." 4
The last field is the warning threshold for key age\.
.
.IP "" 0
.
.SH "HISTORY"
The motivation of this application is to provide a local secure store of AWS credentials using specifically in the macOS Keychain, to have them easily accessed from the Terminal, and to provide useful functions like assuming roles and opening the AWS Console from the cli\. It then expanded to include autocomplete and a desire to have an almost complete test coverage to prevent regressions in its functionality\. For Enterprise environments there are better suited tools to use like HashiCorp Vault \fIhttps://vaultproject\.io/\fR\.
.
Expand Down
43 changes: 31 additions & 12 deletions man/awskeyring.5.ronn
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ The commands are as follows:
Adds an ACCOUNT to the keyring<br>

-k, --key=KEY: AWS account key id.<br>
-s, --secret=SECRET: AWS account secret.<br>
-m, --mfa=MFA: AWS virtual mfa arn.<br>
-r, --no-remote: Do not validate with remote api.
-r, --no-remote: Do not validate with remote api.<br>
-s, --secret=SECRET: AWS account secret.

* add-role ROLE:

Expand All @@ -39,25 +39,25 @@ The commands are as follows:

Open the AWS Console for the ACCOUNT<br>

-p, --path=PATH: The service PATH to open.<br>
-b, --browser=BROWSER: Specify an alternative browser.<br>
-o, --no-open: Do not open the url.<br>
-n, --no-token: Do not use saved token.<br>
-o, --no-open: Do not open the url.
-p, --path=PATH: The service PATH to open.

* env ACCOUNT:

Outputs bourne shell environment exports for an ACCOUNT<br>

-f, --force: Force output to a tty.<br>
-n, --no-token: Do not use saved token.<br>
-u, --unset, --no-unset: Unset environment variables.<br>
-f, --force: Force output to a tty.
-u, --unset, --no-unset: Unset environment variables.

* exec ACCOUNT command...:

Execute a COMMAND with the environment set for an ACCOUNT<br>

-n, --no-token: Do not use saved token.<br>
-b, --no-bundle: Unset Bundler environment variables.
-b, --no-bundle: Unset Bundler environment variables.<br>
-n, --no-token: Do not use saved token.

* help [COMMAND]:

Expand All @@ -79,8 +79,8 @@ The commands are as follows:

Outputs AWS CLI compatible JSON for an ACCOUNT<br>

-n, --no-token: Do not use saved token.<br>
-f, --force: Force output to a tty.
-f, --force: Force output to a tty.<br>
-n, --no-token: Do not use saved token.

* list:

Expand Down Expand Up @@ -120,8 +120,8 @@ The commands are as follows:
Updates an ACCOUNT in the keyring<br>

-k, --key=KEY: AWS account key id.<br>
-s, --secret=SECRET: AWS account secret.<br>
-r, --no-remote: Do not validate with remote api.
-r, --no-remote: Do not validate with remote api.<br>
-s, --secret=SECRET: AWS account secret.

## ENVIRONMENT

Expand Down Expand Up @@ -154,6 +154,25 @@ Autocomplete is enabled in your current shell with the following command...

complete -C /usr/local/bin/awskeyring awskeyring

## CONFIGURATION

A Configuration file is stored in the users home directory at `~/.awskeyring` as a JSON formatted file.
Most of the fields have a default value except the awskeyring field.

```json
{
"awskeyring": "awskeyring",
"browser": ["FireFox", "Google Chrome", "Safari"],
"console": ["ec2/v2", "cloudwatch", "iam"],
"keyage": 90
}
```

1. The first field is the Keychain that your keys will be saved in.<br>
2. A list of your browsers to use the console command with.<br>
3. The next is the list of AWS Console pages autocomplete will present.<br>
4. The last field is the warning threshold for key age.

## HISTORY

The motivation of this application is to provide a local secure store of AWS
Expand Down
12 changes: 5 additions & 7 deletions spec/lib/awskeyring_command_exceptional_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
context 'when everything raises an exception' do
let(:iam_client) { instance_double(Aws::IAM::Client) }
let(:sts_client) { instance_double(Aws::STS::Client) }
let(:test_tty) { instance_double(StringIO) }
let(:test_tty) { instance_double(IO) }

before do
allow(Awskeyring).to receive(:get_valid_creds).and_return(
Expand Down Expand Up @@ -41,6 +41,10 @@
allow(test_tty).to receive(:write)
end

after do
$stdout = STDOUT # rubocop:disable RSpec/ExpectOutput
end

it 'fails to run an external command' do
expect do
described_class.start(%w[exec test test-exec with params])
Expand All @@ -66,25 +70,20 @@
end

it 'blocks showing JSON creds on console' do
old = $stdout
$stdout = test_tty # rubocop:disable RSpec/ExpectOutput
expect do
described_class.start(%w[json test])
end.to raise_error(SystemExit).and output(/Output suppressed to a tty, --force to override/).to_stderr
$stdout = old # rubocop:disable RSpec/ExpectOutput
end

it 'blocks showing creds on console' do
old = $stdout
$stdout = test_tty # rubocop:disable RSpec/ExpectOutput
expect do
described_class.start(%w[env test])
end.to raise_error(SystemExit).and output(/Output suppressed to a tty, --force to override/).to_stderr
$stdout = old # rubocop:disable RSpec/ExpectOutput
end

it 'allows showing creds on console' do
old = $stdout
$stdout = test_tty # rubocop:disable RSpec/ExpectOutput
expect { described_class.start(%w[env test --force]) }
.to output(%(export AWS_ACCOUNT_NAME="test"
Expand All @@ -96,7 +95,6 @@
unset AWS_SECURITY_TOKEN
unset AWS_SESSION_TOKEN
)).to_stdout
$stdout = old # rubocop:disable RSpec/ExpectOutput
end
end
end

0 comments on commit 2ecd3fe

Please sign in to comment.