Skip to content

Commit

Permalink
Merge pull request #78 from servian/default-action
Browse files Browse the repository at this point in the history
Default action and package name.
  • Loading branch information
tristanmorgan authored Apr 20, 2021
2 parents 49c5945 + 4d12ac8 commit c12ed81
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 62 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ more details on this config option.

The CLI is using [Thor](http://whatisthor.com) with help provided interactively.

Commands:
Awskeyring commands:
awskeyring --version, -v # Prints the version
awskeyring add ACCOUNT # Adds an ACCOUNT to the keyring
awskeyring add-role ROLE # Adds a ROLE to the keyring
Expand Down
55 changes: 19 additions & 36 deletions i18n/en.yml
Original file line number Diff line number Diff line change
@@ -1,41 +1,24 @@
---
en:
__version:
desc: Prints the version
add:
desc: Adds an ACCOUNT to the keyring
add_role:
desc: Adds a ROLE to the keyring
awskeyring:
desc: Autocompletion for bourne shells
console:
desc: Open the AWS Console for the ACCOUNT
env:
desc: Outputs bourne shell environment exports for an ACCOUNT
exec:
desc: Execute a COMMAND with the environment set for an ACCOUNT
import:
desc: Import an ACCOUNT to the keyring from ~/.aws/credentials
initialise:
desc: Initialises a new KEYCHAIN
json:
desc: Outputs AWS CLI compatible JSON for an ACCOUNT
list:
desc: Prints a list of accounts in the keyring
list_role:
desc: Prints a list of roles in the keyring
remove:
desc: Removes an ACCOUNT from the keyring
remove_role:
desc: Removes a ROLE from the keyring
remove_token:
desc: Removes a token for ACCOUNT from the keyring
rotate:
desc: Rotate access keys for an ACCOUNT
token:
desc: Create an STS Token from a ROLE or an MFA code
update:
desc: Updates an ACCOUNT in the keyring
__version_desc: Prints the version
add_desc: Adds an ACCOUNT to the keyring
add_role_desc: Adds a ROLE to the keyring
awskeyring_desc: Autocompletion for bourne shells
console_desc: Open the AWS Console for the ACCOUNT
default_desc: Run default help or initialise if needed.
env_desc: Outputs bourne shell environment exports for an ACCOUNT
exec_desc: Execute a COMMAND with the environment set for an ACCOUNT
import_desc: Import an ACCOUNT to the keyring from ~/.aws/credentials
initialise_desc: Initialises a new KEYCHAIN
json_desc: Outputs AWS CLI compatible JSON for an ACCOUNT
list_desc: Prints a list of accounts in the keyring
list_role_desc: Prints a list of roles in the keyring
remove_desc: Removes an ACCOUNT from the keyring
remove_role_desc: Removes a ROLE from the keyring
remove_token_desc: Removes a token for ACCOUNT from the keyring
rotate_desc: Rotate access keys for an ACCOUNT
token_desc: Create an STS Token from a ROLE or an MFA code
update_desc: Updates an ACCOUNT in the keyring
method_option:
arn: 'AWS role arn.'
code: 'Virtual mfa CODE.'
Expand Down
51 changes: 32 additions & 19 deletions lib/awskeyring_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

# AWSkeyring command line interface.
class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
package_name 'Awskeyring'
I18n.load_path = Dir.glob(File.join(File.realpath(__dir__), '..', 'i18n', '*.{yml,yaml}'))
I18n.backend.load_translations

Expand All @@ -27,13 +28,24 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
map 'rot' => :rotate
map 'tok' => :token
map 'up' => :update
default_command :default

# default to returning an error on failure.
def self.exit_on_failure?
true
end

desc '--version, -v', I18n.t('__version.desc')
desc 'default', I18n.t('default_desc'), hide: true
# default command to run
def default
if Awskeyring.prefs.empty?
invoke :initialise
else
invoke :help
end
end

desc '--version, -v', I18n.t('__version_desc')
method_option 'no-remote', type: :boolean, aliases: '-r', desc: I18n.t('method_option.noremote'), default: false
# print the version number
def __version
Expand All @@ -44,7 +56,7 @@ def __version
puts "Homepage #{Awskeyring::HOMEPAGE}"
end

desc 'initialise', I18n.t('initialise.desc')
desc 'initialise', I18n.t('initialise_desc')
method_option :keychain, type: :string, aliases: '-n', desc: I18n.t('method_option.keychain')
# initialise the keychain
def initialise
Expand All @@ -69,7 +81,7 @@ def initialise
puts I18n.t('message.addkeychain', keychain: keychain, exec_name: exec_name)
end

desc 'list', I18n.t('list.desc')
desc 'list', I18n.t('list_desc')
# list the accounts
def list
if Awskeyring.list_account_names.empty?
Expand All @@ -80,7 +92,7 @@ def list
end

map 'list-role' => :list_role
desc 'list-role', I18n.t('list_role.desc')
desc 'list-role', I18n.t('list_role_desc')
method_option 'detail', type: :boolean, aliases: '-d', desc: I18n.t('method_option.detail'), default: false
# List roles
def list_role
Expand All @@ -95,7 +107,7 @@ def list_role
end
end

desc 'env ACCOUNT', I18n.t('env.desc')
desc 'env ACCOUNT', I18n.t('env_desc')
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
# Print Env vars
Expand All @@ -113,7 +125,7 @@ def env(account = nil)
end
end

desc 'json ACCOUNT', I18n.t('json.desc')
desc 'json ACCOUNT', I18n.t('json_desc')
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)
Expand All @@ -130,7 +142,7 @@ def json(account)
)
end

desc 'import ACCOUNT', I18n.t('import.desc')
desc 'import ACCOUNT', I18n.t('import_desc')
method_option 'no-remote', type: :boolean, aliases: '-r', desc: I18n.t('method_option.noremote'), default: false
# Import an Account
def import(account = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
Expand Down Expand Up @@ -166,7 +178,7 @@ def import(account = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSiz
end
end

desc 'exec ACCOUNT command...', I18n.t('exec.desc')
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
# execute an external command with env set
Expand All @@ -188,7 +200,7 @@ def exec(account, *command)
end
end

desc 'add ACCOUNT', I18n.t('add.desc')
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')
Expand Down Expand Up @@ -219,7 +231,7 @@ def add(account = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
puts I18n.t('message.addaccount', account: account)
end

desc 'update ACCOUNT', I18n.t('update.desc')
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
Expand Down Expand Up @@ -247,7 +259,7 @@ def update(account = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSiz
end

map 'add-role' => :add_role
desc 'add-role ROLE', I18n.t('add_role.desc')
desc 'add-role ROLE', I18n.t('add_role_desc')
method_option :arn, type: :string, aliases: '-a', desc: I18n.t('method_option.arn')
# Add a role
def add_role(role = nil)
Expand All @@ -267,7 +279,7 @@ def add_role(role = nil)
puts I18n.t('message.addrole', role: role)
end

desc 'remove ACCOUNT', I18n.t('remove.desc')
desc 'remove ACCOUNT', I18n.t('remove_desc')
# Remove an account
def remove(account = nil)
account = ask_check(
Expand All @@ -277,7 +289,7 @@ def remove(account = nil)
Awskeyring.delete_account(account: account, message: I18n.t('message.delaccount', account: account))
end

desc 'remove-token ACCOUNT', I18n.t('remove_token.desc')
desc 'remove-token ACCOUNT', I18n.t('remove_token_desc')
# remove a session token
def remove_token(account = nil)
account = ask_check(
Expand All @@ -288,7 +300,7 @@ def remove_token(account = nil)
end

map 'remove-role' => :remove_role
desc 'remove-role ROLE', I18n.t('remove_role.desc')
desc 'remove-role ROLE', I18n.t('remove_role_desc')
# remove a role
def remove_role(role = nil)
role = ask_check(
Expand All @@ -298,7 +310,7 @@ def remove_role(role = nil)
Awskeyring.delete_role(role_name: role, message: I18n.t('message.delrole', role: role))
end

desc 'rotate ACCOUNT', I18n.t('rotate.desc')
desc 'rotate ACCOUNT', I18n.t('rotate_desc')
# rotate Account keys
def rotate(account = nil) # rubocop:disable Metrics/MethodLength
account = ask_check(
Expand Down Expand Up @@ -330,7 +342,7 @@ def rotate(account = nil) # rubocop:disable Metrics/MethodLength
puts I18n.t('message.upaccount', account: account)
end

desc 'token ACCOUNT [ROLE] [MFA]', I18n.t('token.desc')
desc 'token ACCOUNT [ROLE] [MFA]', I18n.t('token_desc')
method_option :role, type: :string, aliases: '-r', desc: I18n.t('method_option.role')
method_option :code, type: :string, aliases: '-c', desc: I18n.t('method_option.code')
method_option :duration, type: :string, aliases: '-d', desc: I18n.t('method_option.duration')
Expand Down Expand Up @@ -385,7 +397,7 @@ def token(account = nil, role = nil, code = nil) # rubocop:disable Metrics/AbcSi
puts I18n.t('message.addtoken', account: account, time: Time.at(new_creds[:expiry].to_i))
end

desc 'console ACCOUNT', I18n.t('console.desc')
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
Expand Down Expand Up @@ -424,7 +436,7 @@ def console(account = nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLeng
end
end

desc 'awskeyring CURR PREV', I18n.t('awskeyring.desc'), hide: true
desc "#{File.basename($PROGRAM_NAME)} CURR PREV", I18n.t('awskeyring_desc'), hide: true
map File.basename($PROGRAM_NAME) => :autocomplete
# autocomplete
def autocomplete(curr, prev)
Expand Down Expand Up @@ -507,7 +519,8 @@ def print_auto_resp(curr, len, sub_cmd) # rubocop:disable Metrics/MethodLength,
end

def list_commands
self.class.all_commands.keys.map { |elem| elem.tr('_', '-') }.reject! { |elem| elem == 'autocomplete' }
commands = self.class.all_commands.keys.map { |elem| elem.tr('_', '-') }
commands.reject! { |elem| %w[autocomplete default].include?(elem) }
end

def list_arguments(command:)
Expand Down
30 changes: 28 additions & 2 deletions man/awskeyring.5
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "AWSKEYRING" "5" "March 2021" "" ""
.TH "AWSKEYRING" "5" "April 2021" "" ""
.
.SH "NAME"
\fBAwskeyring\fR \- is a small tool to manage AWS account keys in the macOS Keychain
Expand Down Expand Up @@ -267,8 +267,34 @@ awskeyring env personal\-aws
.
.IP "" 0
.
.P
To open the AWS Console (web page) with your default browser simply run\.\.\.
.
.IP "" 4
.
.nf

awskeyring console personal\-aws
.
.fi
.
.IP "" 0
.
.P
Autocomplete is enabled in your current shell with the following command\.\.\.
.
.IP "" 4
.
.nf

complete \-C /usr/local/bin/awskeyring awskeyring
.
.fi
.
.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\. For Enterprise environments there are better suited tools to use like HashiCorp Vault \fIhttps://vaultproject\.io/\fR\.
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\.
.
.SH "SECURITY"
If you believe you have found a security issue in Awskeyring, please responsibly disclose by contacting me at \fItristan\.morgan@servian\.com\fR\. Awskeyring is a Ruby script and as such Ruby is whitelisted to access your "awskeyring" keychain\. Use a strong password and keep the unlock time short\.
Expand Down
11 changes: 10 additions & 1 deletion man/awskeyring.5.ronn
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,21 @@ Now your keys are stored safely in the macOS keychain. To print environment vari

awskeyring env personal-aws

To open the AWS Console (web page) with your default browser simply run...

awskeyring console personal-aws

Autocomplete is enabled in your current shell with the following command...

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

## 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.
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](https://vaultproject.io/).

Expand Down
14 changes: 11 additions & 3 deletions spec/lib/awskeyring_command_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@
end

it 'outputs help text' do
expect { described_class.start([]) }
.to output(/^ \w+ --version, -v\s+# Prints the version/).to_stdout
expect { described_class.start(%w[help]) }
.to output(/Commands:/).to_stdout
.to output(/Awskeyring commands:/).to_stdout
end

it 'returns the version number' do
Expand Down Expand Up @@ -57,6 +55,11 @@
expect { described_class.start(%w[initialise]) }
.to output(/Add accounts to your test keychain with:/).to_stdout
end

it 'initialises the keychain by default' do
expect { described_class.start([]) }
.to output(/Add accounts to your test keychain with:/).to_stdout
end
end

context 'when no accounts or roles are set' do
Expand All @@ -66,6 +69,11 @@
allow(Awskeyring).to receive(:prefs).and_return('{"awskeyring": "awskeyringtest"}')
end

it 'outputs help text by default' do
expect { described_class.start([]) }
.to output(/^ \w+ --version, -v\s+# Prints the version/).to_stdout
end

it 'tells you that you must add accounts' do
expect { described_class.start(%w[list]) }.to raise_error
.and output(/No accounts added, run `\w+ add` to add./).to_stderr
Expand Down

0 comments on commit c12ed81

Please sign in to comment.