awskeyring 1.8.2 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +20 -20
- data/Rakefile +4 -4
- data/i18n/en.yml +19 -37
- data/lib/awskeyring.rb +14 -3
- data/lib/awskeyring/awsapi.rb +1 -1
- data/lib/awskeyring/version.rb +1 -1
- data/lib/awskeyring_command.rb +125 -90
- data/man/awskeyring.5 +30 -7
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c40aae37a636dc9f09930e87a9c592d85b19889d96f0550875954d460bc7e9fe
|
4
|
+
data.tar.gz: 6d18d87a6301f6fe6f9cd8976da5bc80dbaf3cffe8570997b341d99426f56dfc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 621e70e88839085be03c4c0cc30c8fe1e338392f46eca028c5a4fd244eac3d9f7c8cf68458730a154837408c9bd5cdc3d1da8625989c1b7de69bd007b3d91ecc
|
7
|
+
data.tar.gz: bac9058e92069e9206a070eb6fde527aeb1254e9135742cbbc7bd23a4666b27c6ee01da09f72083acff8e6b7fb0fc995e2553d2ebee196e05fc01e5f81419c4f
|
data/README.md
CHANGED
@@ -61,25 +61,25 @@ more details on this config option.
|
|
61
61
|
|
62
62
|
The CLI is using [Thor](http://whatisthor.com) with help provided interactively.
|
63
63
|
|
64
|
-
|
65
|
-
awskeyring --version, -v
|
66
|
-
awskeyring add ACCOUNT
|
67
|
-
awskeyring add-role ROLE
|
68
|
-
awskeyring console ACCOUNT
|
69
|
-
awskeyring env ACCOUNT
|
70
|
-
awskeyring exec ACCOUNT command...
|
71
|
-
awskeyring help [COMMAND]
|
72
|
-
awskeyring import ACCOUNT
|
73
|
-
awskeyring initialise
|
74
|
-
awskeyring json ACCOUNT
|
75
|
-
awskeyring list
|
76
|
-
awskeyring list-role
|
77
|
-
awskeyring remove ACCOUNT
|
78
|
-
awskeyring remove-role ROLE
|
79
|
-
awskeyring remove-token ACCOUNT
|
80
|
-
awskeyring rotate ACCOUNT
|
81
|
-
awskeyring token ACCOUNT [ROLE] [
|
82
|
-
awskeyring update ACCOUNT
|
64
|
+
Awskeyring commands:
|
65
|
+
awskeyring --version, -v # Prints the version
|
66
|
+
awskeyring add ACCOUNT # Adds an ACCOUNT to the keyring
|
67
|
+
awskeyring add-role ROLE # Adds a ROLE to the keyring
|
68
|
+
awskeyring console ACCOUNT # Open the AWS Console for the ACCOUNT
|
69
|
+
awskeyring env ACCOUNT # Outputs bourne shell environment exports for an ACCOUNT
|
70
|
+
awskeyring exec ACCOUNT command... # Execute a COMMAND with the environment set for an ACCOUNT
|
71
|
+
awskeyring help [COMMAND] # Describe available commands or one specific command
|
72
|
+
awskeyring import ACCOUNT # Import an ACCOUNT to the keyring from ~/.aws/credentials
|
73
|
+
awskeyring initialise # Initialises a new KEYCHAIN
|
74
|
+
awskeyring json ACCOUNT # Outputs AWS CLI compatible JSON for an ACCOUNT
|
75
|
+
awskeyring list # Prints a list of accounts in the keyring
|
76
|
+
awskeyring list-role # Prints a list of roles in the keyring
|
77
|
+
awskeyring remove ACCOUNT # Removes an ACCOUNT from the keyring
|
78
|
+
awskeyring remove-role ROLE # Removes a ROLE from the keyring
|
79
|
+
awskeyring remove-token ACCOUNT # Removes a token for ACCOUNT from the keyring
|
80
|
+
awskeyring rotate ACCOUNT # Rotate access keys for an ACCOUNT
|
81
|
+
awskeyring token ACCOUNT [ROLE] [CODE] # Create an STS Token from a ROLE or an mfa CODE
|
82
|
+
awskeyring update ACCOUNT # Updates an ACCOUNT in the keyring
|
83
83
|
|
84
84
|
and autocomplete that can be installed with:
|
85
85
|
|
@@ -91,7 +91,7 @@ There are also short forms of most commands if you prefer:
|
|
91
91
|
|
92
92
|
To set your environment easily the following bash function helps:
|
93
93
|
|
94
|
-
awsenv() { eval "$(awskeyring env
|
94
|
+
awsenv() { eval "$(awskeyring env ${@:-$AWS_ACCOUNT_NAME})"; }
|
95
95
|
|
96
96
|
## Development
|
97
97
|
|
data/Rakefile
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bundler/gem_tasks'
|
4
|
-
require 'rspec/core/rake_task'
|
5
4
|
require 'rubocop/rake_task'
|
6
5
|
require 'ronn'
|
7
6
|
require 'github_changelog_generator/task'
|
@@ -49,13 +48,14 @@ end
|
|
49
48
|
desc 'generate manpage'
|
50
49
|
task :ronn do
|
51
50
|
puts 'Running Ronn...'
|
52
|
-
|
53
|
-
|
51
|
+
doc = Ronn::Document.new('man/awskeyring.5.ronn')
|
52
|
+
doc.date = Time.parse(`git show -s --format=%ad --date=short`)
|
53
|
+
File.write('man/awskeyring.5', doc.to_roff)
|
54
54
|
puts "done\n\n"
|
55
55
|
end
|
56
56
|
|
57
57
|
YARD::Rake::YardocTask.new do |t|
|
58
|
-
t.options = ['--fail-on-warning', '--no-progress']
|
58
|
+
t.options = ['--fail-on-warning', '--no-progress', '--files', '*.md']
|
59
59
|
t.stats_options = ['--list-undoc']
|
60
60
|
end
|
61
61
|
|
data/i18n/en.yml
CHANGED
@@ -1,41 +1,24 @@
|
|
1
1
|
---
|
2
2
|
en:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
desc: Outputs AWS CLI compatible JSON for an ACCOUNT
|
23
|
-
list:
|
24
|
-
desc: Prints a list of accounts in the keyring
|
25
|
-
list_role:
|
26
|
-
desc: Prints a list of roles in the keyring
|
27
|
-
remove:
|
28
|
-
desc: Removes an ACCOUNT from the keyring
|
29
|
-
remove_role:
|
30
|
-
desc: Removes a ROLE from the keyring
|
31
|
-
remove_token:
|
32
|
-
desc: Removes a token for ACCOUNT from the keyring
|
33
|
-
rotate:
|
34
|
-
desc: Rotate access keys for an ACCOUNT
|
35
|
-
token:
|
36
|
-
desc: Create an STS Token from a ROLE or an MFA code
|
37
|
-
update:
|
38
|
-
desc: Updates an ACCOUNT in the keyring
|
3
|
+
__version_desc: Prints the version
|
4
|
+
add_desc: Adds an ACCOUNT to the keyring
|
5
|
+
add_role_desc: Adds a ROLE to the keyring
|
6
|
+
awskeyring_desc: Autocompletion for bourne shells
|
7
|
+
console_desc: Open the AWS Console for the ACCOUNT
|
8
|
+
default_desc: Run default help or initialise if needed.
|
9
|
+
env_desc: Outputs bourne shell environment exports for an ACCOUNT
|
10
|
+
exec_desc: Execute a COMMAND with the environment set for an ACCOUNT
|
11
|
+
import_desc: Import an ACCOUNT to the keyring from ~/.aws/credentials
|
12
|
+
initialise_desc: Initialises a new KEYCHAIN
|
13
|
+
json_desc: Outputs AWS CLI compatible JSON for an ACCOUNT
|
14
|
+
list_desc: Prints a list of accounts in the keyring
|
15
|
+
list_role_desc: Prints a list of roles in the keyring
|
16
|
+
remove_desc: Removes an ACCOUNT from the keyring
|
17
|
+
remove_role_desc: Removes a ROLE from the keyring
|
18
|
+
remove_token_desc: Removes a token for ACCOUNT from the keyring
|
19
|
+
rotate_desc: Rotate access keys for an ACCOUNT
|
20
|
+
token_desc: Create an STS Token from a ROLE or an mfa CODE
|
21
|
+
update_desc: Updates an ACCOUNT in the keyring
|
39
22
|
method_option:
|
40
23
|
arn: 'AWS role arn.'
|
41
24
|
code: 'Virtual mfa CODE.'
|
@@ -50,7 +33,6 @@ en:
|
|
50
33
|
noremote: 'Do not validate with remote api.'
|
51
34
|
path: 'The service PATH to open.'
|
52
35
|
browser: 'Specify an alternative browser.'
|
53
|
-
role: 'The ROLE to assume.'
|
54
36
|
secret: 'AWS account secret.'
|
55
37
|
unset: 'Unset environment variables.'
|
56
38
|
message:
|
data/lib/awskeyring.rb
CHANGED
@@ -97,6 +97,17 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
|
|
97
97
|
all_items.where(account: account).first
|
98
98
|
end
|
99
99
|
|
100
|
+
# return item that matches a prefix if only one.
|
101
|
+
def self.solo_select(list, prefix)
|
102
|
+
return prefix if list.include?(prefix)
|
103
|
+
|
104
|
+
list.select! { |elem| elem.start_with?(prefix) }
|
105
|
+
|
106
|
+
return list.first if list.length == 1
|
107
|
+
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
100
111
|
# Add an account item
|
101
112
|
#
|
102
113
|
# @param [String] account The account name to create
|
@@ -317,7 +328,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
|
|
317
328
|
# @param [String] account_name the associated account name.
|
318
329
|
def self.account_exists(account_name)
|
319
330
|
Awskeyring::Validate.account_name(account_name)
|
320
|
-
raise 'Account does not exist' unless
|
331
|
+
raise 'Account does not exist' unless (account_name = solo_select(list_account_names, account_name))
|
321
332
|
|
322
333
|
account_name
|
323
334
|
end
|
@@ -347,7 +358,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
|
|
347
358
|
# @param [String] role_name the associated role name.
|
348
359
|
def self.role_exists(role_name)
|
349
360
|
Awskeyring::Validate.role_name(role_name)
|
350
|
-
raise 'Role does not exist' unless
|
361
|
+
raise 'Role does not exist' unless (role_name = solo_select(list_role_names, role_name))
|
351
362
|
|
352
363
|
role_name
|
353
364
|
end
|
@@ -367,7 +378,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
|
|
367
378
|
# @param [String] token_name the associated account name.
|
368
379
|
def self.token_exists(token_name)
|
369
380
|
Awskeyring::Validate.account_name(token_name)
|
370
|
-
raise 'Token does not exist' unless
|
381
|
+
raise 'Token does not exist' unless (token_name = solo_select(list_token_names, token_name))
|
371
382
|
|
372
383
|
token_name
|
373
384
|
end
|
data/lib/awskeyring/awsapi.rb
CHANGED
@@ -209,7 +209,7 @@ module Awskeyring
|
|
209
209
|
# Get the signin token param
|
210
210
|
private_class_method def self.token_param(session_json:)
|
211
211
|
get_signin_token_url = AWS_SIGNIN_URL + '?Action=getSigninToken' \
|
212
|
-
|
212
|
+
'&Session=' + CGI.escape(session_json)
|
213
213
|
|
214
214
|
uri = URI(get_signin_token_url)
|
215
215
|
request = Net::HTTP.new(uri.host, uri.port)
|
data/lib/awskeyring/version.rb
CHANGED
data/lib/awskeyring_command.rb
CHANGED
@@ -11,29 +11,36 @@ require 'awskeyring/version'
|
|
11
11
|
|
12
12
|
# AWSkeyring command line interface.
|
13
13
|
class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
14
|
+
package_name 'Awskeyring'
|
14
15
|
I18n.load_path = Dir.glob(File.join(File.realpath(__dir__), '..', 'i18n', '*.{yml,yaml}'))
|
15
16
|
I18n.backend.load_translations
|
16
17
|
|
17
18
|
map %w[--version -v] => :__version
|
18
|
-
map %w[--help -h] => :help
|
19
|
-
map 'init' => :initialise
|
20
19
|
map 'adr' => :add_role
|
21
|
-
map '
|
20
|
+
map 'assume-role' => :token
|
22
21
|
map 'ls' => :list
|
23
22
|
map 'lsr' => :list_role
|
24
23
|
map 'rm' => :remove
|
25
24
|
map 'rmr' => :remove_role
|
26
25
|
map 'rmt' => :remove_token
|
27
|
-
|
28
|
-
map 'tok' => :token
|
29
|
-
map 'up' => :update
|
26
|
+
default_command :default
|
30
27
|
|
31
28
|
# default to returning an error on failure.
|
32
29
|
def self.exit_on_failure?
|
33
30
|
true
|
34
31
|
end
|
35
32
|
|
36
|
-
desc '
|
33
|
+
desc 'default', I18n.t('default_desc'), hide: true
|
34
|
+
# default command to run
|
35
|
+
def default
|
36
|
+
if Awskeyring.prefs.empty?
|
37
|
+
invoke :initialise
|
38
|
+
else
|
39
|
+
invoke :help
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc '--version, -v', I18n.t('__version_desc')
|
37
44
|
method_option 'no-remote', type: :boolean, aliases: '-r', desc: I18n.t('method_option.noremote'), default: false
|
38
45
|
# print the version number
|
39
46
|
def __version
|
@@ -44,7 +51,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
44
51
|
puts "Homepage #{Awskeyring::HOMEPAGE}"
|
45
52
|
end
|
46
53
|
|
47
|
-
desc 'initialise', I18n.t('
|
54
|
+
desc 'initialise', I18n.t('initialise_desc')
|
48
55
|
method_option :keychain, type: :string, aliases: '-n', desc: I18n.t('method_option.keychain')
|
49
56
|
# initialise the keychain
|
50
57
|
def initialise
|
@@ -69,7 +76,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
69
76
|
puts I18n.t('message.addkeychain', keychain: keychain, exec_name: exec_name)
|
70
77
|
end
|
71
78
|
|
72
|
-
desc 'list', I18n.t('
|
79
|
+
desc 'list', I18n.t('list_desc')
|
73
80
|
# list the accounts
|
74
81
|
def list
|
75
82
|
if Awskeyring.list_account_names.empty?
|
@@ -79,9 +86,8 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
79
86
|
puts Awskeyring.list_account_names.join("\n")
|
80
87
|
end
|
81
88
|
|
82
|
-
|
83
|
-
|
84
|
-
method_option 'detail', type: :boolean, aliases: '-d', desc: I18n.t('method_option.detail'), default: false
|
89
|
+
desc 'list-role', I18n.t('list_role_desc')
|
90
|
+
method_option :detail, type: :boolean, aliases: '-d', desc: I18n.t('method_option.detail'), default: false
|
85
91
|
# List roles
|
86
92
|
def list_role
|
87
93
|
if Awskeyring.list_role_names.empty?
|
@@ -95,9 +101,9 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
95
101
|
end
|
96
102
|
end
|
97
103
|
|
98
|
-
desc 'env ACCOUNT', I18n.t('
|
104
|
+
desc 'env ACCOUNT', I18n.t('env_desc')
|
99
105
|
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
|
100
|
-
method_option
|
106
|
+
method_option :unset, type: :boolean, aliases: '-u', desc: I18n.t('method_option.unset'), default: false
|
101
107
|
# Print Env vars
|
102
108
|
def env(account = nil)
|
103
109
|
if options[:unset]
|
@@ -113,7 +119,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
113
119
|
end
|
114
120
|
end
|
115
121
|
|
116
|
-
desc 'json ACCOUNT', I18n.t('
|
122
|
+
desc 'json ACCOUNT', I18n.t('json_desc')
|
117
123
|
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
|
118
124
|
# Print JSON for use with credential_process
|
119
125
|
def json(account)
|
@@ -130,7 +136,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
130
136
|
)
|
131
137
|
end
|
132
138
|
|
133
|
-
desc 'import ACCOUNT', I18n.t('
|
139
|
+
desc 'import ACCOUNT', I18n.t('import_desc')
|
134
140
|
method_option 'no-remote', type: :boolean, aliases: '-r', desc: I18n.t('method_option.noremote'), default: false
|
135
141
|
# Import an Account
|
136
142
|
def import(account = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
@@ -166,7 +172,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
166
172
|
end
|
167
173
|
end
|
168
174
|
|
169
|
-
desc 'exec ACCOUNT command...', I18n.t('
|
175
|
+
desc 'exec ACCOUNT command...', I18n.t('exec_desc')
|
170
176
|
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
|
171
177
|
method_option 'no-bundle', type: :boolean, aliases: '-b', desc: I18n.t('method_option.nobundle'), default: false
|
172
178
|
# execute an external command with env set
|
@@ -188,7 +194,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
188
194
|
end
|
189
195
|
end
|
190
196
|
|
191
|
-
desc 'add ACCOUNT', I18n.t('
|
197
|
+
desc 'add ACCOUNT', I18n.t('add_desc')
|
192
198
|
method_option :key, type: :string, aliases: '-k', desc: I18n.t('method_option.key')
|
193
199
|
method_option :secret, type: :string, aliases: '-s', desc: I18n.t('method_option.secret')
|
194
200
|
method_option :mfa, type: :string, aliases: '-m', desc: I18n.t('method_option.mfa')
|
@@ -219,7 +225,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
219
225
|
puts I18n.t('message.addaccount', account: account)
|
220
226
|
end
|
221
227
|
|
222
|
-
desc 'update ACCOUNT', I18n.t('
|
228
|
+
desc 'update ACCOUNT', I18n.t('update_desc')
|
223
229
|
method_option :key, type: :string, aliases: '-k', desc: I18n.t('method_option.key')
|
224
230
|
method_option :secret, type: :string, aliases: '-s', desc: I18n.t('method_option.secret')
|
225
231
|
method_option 'no-remote', type: :boolean, aliases: '-r', desc: I18n.t('method_option.noremote'), default: false
|
@@ -246,8 +252,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
246
252
|
puts I18n.t('message.upaccount', account: account)
|
247
253
|
end
|
248
254
|
|
249
|
-
|
250
|
-
desc 'add-role ROLE', I18n.t('add_role.desc')
|
255
|
+
desc 'add-role ROLE', I18n.t('add_role_desc')
|
251
256
|
method_option :arn, type: :string, aliases: '-a', desc: I18n.t('method_option.arn')
|
252
257
|
# Add a role
|
253
258
|
def add_role(role = nil)
|
@@ -267,7 +272,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
267
272
|
puts I18n.t('message.addrole', role: role)
|
268
273
|
end
|
269
274
|
|
270
|
-
desc 'remove ACCOUNT', I18n.t('
|
275
|
+
desc 'remove ACCOUNT', I18n.t('remove_desc')
|
271
276
|
# Remove an account
|
272
277
|
def remove(account = nil)
|
273
278
|
account = ask_check(
|
@@ -277,18 +282,17 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
277
282
|
Awskeyring.delete_account(account: account, message: I18n.t('message.delaccount', account: account))
|
278
283
|
end
|
279
284
|
|
280
|
-
desc 'remove-token ACCOUNT', I18n.t('
|
285
|
+
desc 'remove-token ACCOUNT', I18n.t('remove_token_desc')
|
281
286
|
# remove a session token
|
282
|
-
def remove_token(
|
283
|
-
|
284
|
-
existing:
|
287
|
+
def remove_token(token = nil)
|
288
|
+
token = ask_check(
|
289
|
+
existing: token, message: I18n.t('message.account'), validator: Awskeyring.method(:token_exists),
|
285
290
|
limited_to: Awskeyring.list_token_names
|
286
291
|
)
|
287
|
-
Awskeyring.delete_token(account:
|
292
|
+
Awskeyring.delete_token(account: token, message: I18n.t('message.deltoken', account: token))
|
288
293
|
end
|
289
294
|
|
290
|
-
|
291
|
-
desc 'remove-role ROLE', I18n.t('remove_role.desc')
|
295
|
+
desc 'remove-role ROLE', I18n.t('remove_role_desc')
|
292
296
|
# remove a role
|
293
297
|
def remove_role(role = nil)
|
294
298
|
role = ask_check(
|
@@ -298,7 +302,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
298
302
|
Awskeyring.delete_role(role_name: role, message: I18n.t('message.delrole', role: role))
|
299
303
|
end
|
300
304
|
|
301
|
-
desc 'rotate ACCOUNT', I18n.t('
|
305
|
+
desc 'rotate ACCOUNT', I18n.t('rotate_desc')
|
302
306
|
# rotate Account keys
|
303
307
|
def rotate(account = nil) # rubocop:disable Metrics/MethodLength
|
304
308
|
account = ask_check(
|
@@ -330,8 +334,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
330
334
|
puts I18n.t('message.upaccount', account: account)
|
331
335
|
end
|
332
336
|
|
333
|
-
desc 'token ACCOUNT [ROLE] [
|
334
|
-
method_option :role, type: :string, aliases: '-r', desc: I18n.t('method_option.role')
|
337
|
+
desc 'token ACCOUNT [ROLE] [CODE]', I18n.t('token_desc')
|
335
338
|
method_option :code, type: :string, aliases: '-c', desc: I18n.t('method_option.code')
|
336
339
|
method_option :duration, type: :string, aliases: '-d', desc: I18n.t('method_option.duration')
|
337
340
|
# generate a sessiopn token
|
@@ -342,7 +345,6 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
342
345
|
validator: Awskeyring.method(:account_exists),
|
343
346
|
limited_to: Awskeyring.list_account_names
|
344
347
|
)
|
345
|
-
role ||= options[:role]
|
346
348
|
if role
|
347
349
|
role = ask_check(
|
348
350
|
existing: role, message: I18n.t('message.role'), validator: Awskeyring.method(:role_exists),
|
@@ -385,7 +387,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
385
387
|
puts I18n.t('message.addtoken', account: account, time: Time.at(new_creds[:expiry].to_i))
|
386
388
|
end
|
387
389
|
|
388
|
-
desc 'console ACCOUNT', I18n.t('
|
390
|
+
desc 'console ACCOUNT', I18n.t('console_desc')
|
389
391
|
method_option :path, type: :string, aliases: '-p', desc: I18n.t('method_option.path')
|
390
392
|
method_option :browser, type: :string, aliases: '-b', desc: I18n.t('method_option.browser')
|
391
393
|
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
|
@@ -424,110 +426,140 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
424
426
|
end
|
425
427
|
end
|
426
428
|
|
427
|
-
desc
|
429
|
+
desc "#{File.basename($PROGRAM_NAME)} CURR PREV", I18n.t('awskeyring_desc'), hide: true
|
428
430
|
map File.basename($PROGRAM_NAME) => :autocomplete
|
429
431
|
# autocomplete
|
430
|
-
def autocomplete(curr, prev)
|
432
|
+
def autocomplete(curr, prev = nil)
|
433
|
+
curr, prev = fix_args(curr, prev)
|
431
434
|
comp_line = ENV['COMP_LINE']
|
432
|
-
|
435
|
+
comp_point_str = ENV['COMP_POINT']
|
436
|
+
unless comp_line && comp_point_str
|
433
437
|
exec_name = File.basename($PROGRAM_NAME)
|
434
438
|
warn I18n.t('message.awskeyring', path: $PROGRAM_NAME, bin: exec_name)
|
435
439
|
exit 1
|
436
440
|
end
|
437
441
|
|
438
|
-
|
439
|
-
|
442
|
+
comp_lines = comp_line[0..(comp_point_str.to_i)].split
|
443
|
+
|
444
|
+
comp_type, sub_cmd = comp_type(comp_lines: comp_lines, prev: prev)
|
445
|
+
list = fetch_auto_resp(comp_type, sub_cmd)
|
446
|
+
puts list.select { |elem| elem.start_with?(curr) }.sort!.join("\n")
|
440
447
|
end
|
441
448
|
|
442
449
|
private
|
443
450
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
cred
|
451
|
+
# when a double dash is parsed it is dropped from the args but we need it
|
452
|
+
def fix_args(curr, prev)
|
453
|
+
if prev.nil?
|
454
|
+
[ARGV[1], ARGV[2]]
|
455
|
+
else
|
456
|
+
[curr, prev]
|
457
|
+
end
|
452
458
|
end
|
453
459
|
|
454
|
-
|
455
|
-
|
456
|
-
sub_cmd = sub_command(
|
457
|
-
|
458
|
-
comp_len = 3 if curr.start_with?('-')
|
460
|
+
# determine the type of completion needed
|
461
|
+
def comp_type(comp_lines:, prev:)
|
462
|
+
sub_cmd = sub_command(comp_lines)
|
463
|
+
comp_idx = comp_lines.rindex(prev)
|
459
464
|
|
460
465
|
case prev
|
461
|
-
when 'help', File.basename($PROGRAM_NAME)
|
462
|
-
comp_len = 0
|
463
|
-
when 'remove-role', '-r', 'rmr'
|
464
|
-
comp_len = 2
|
465
466
|
when '--path', '-p'
|
466
|
-
|
467
|
-
when 'remove-token', 'rmt'
|
468
|
-
comp_len = 50
|
467
|
+
comp_type = :path_type
|
469
468
|
when '--browser', '-b'
|
470
|
-
|
469
|
+
comp_type = :browser_type
|
470
|
+
else
|
471
|
+
comp_type = :command
|
472
|
+
comp_type = param_type(comp_idx, sub_cmd) unless sub_cmd.empty?
|
471
473
|
end
|
472
474
|
|
473
|
-
[
|
475
|
+
[comp_type, sub_cmd]
|
474
476
|
end
|
475
477
|
|
478
|
+
# check params for named params or fall back to flags
|
479
|
+
def param_type(comp_idx, sub_cmd)
|
480
|
+
types = %i[opt req]
|
481
|
+
param_list = method(sub_cmd).parameters.select { |elem| types.include? elem[0] }
|
482
|
+
if comp_idx.zero?
|
483
|
+
:command
|
484
|
+
elsif comp_idx > param_list.length
|
485
|
+
:flag
|
486
|
+
else
|
487
|
+
param_list[comp_idx - 1][1]
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
# catch the command from prefixes and aliases
|
476
492
|
def sub_command(comp_lines)
|
477
|
-
return
|
493
|
+
return '' if comp_lines.length < 2
|
478
494
|
|
479
495
|
sub_cmd = comp_lines[1]
|
480
496
|
|
481
|
-
return sub_cmd if self.class.
|
482
|
-
|
483
|
-
|
484
|
-
end
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
case
|
489
|
-
when
|
490
|
-
|
491
|
-
when
|
492
|
-
|
493
|
-
when
|
494
|
-
|
495
|
-
when
|
496
|
-
|
497
|
-
when
|
498
|
-
|
499
|
-
when
|
500
|
-
|
501
|
-
when 60
|
502
|
-
list = Awskeyring.list_browsers
|
497
|
+
return self.class.map[sub_cmd].to_s if self.class.map.key? sub_cmd
|
498
|
+
|
499
|
+
(Awskeyring.solo_select(list_commands, sub_cmd) || '').tr('-', '_')
|
500
|
+
end
|
501
|
+
|
502
|
+
# given a type return the right list for completions
|
503
|
+
def fetch_auto_resp(comp_type, sub_cmd)
|
504
|
+
case comp_type
|
505
|
+
when :command
|
506
|
+
list_commands
|
507
|
+
when :account
|
508
|
+
Awskeyring.list_account_names
|
509
|
+
when :role
|
510
|
+
Awskeyring.list_role_names
|
511
|
+
when :path_type
|
512
|
+
Awskeyring.list_console_path
|
513
|
+
when :token
|
514
|
+
Awskeyring.list_token_names
|
515
|
+
when :browser_type
|
516
|
+
Awskeyring.list_browsers
|
503
517
|
else
|
504
|
-
|
518
|
+
list_arguments(command: sub_cmd)
|
505
519
|
end
|
506
|
-
puts list.select { |elem| elem.start_with?(curr) }.sort!.join("\n")
|
507
520
|
end
|
508
521
|
|
522
|
+
# list command names
|
509
523
|
def list_commands
|
510
|
-
self.class.all_commands.keys.map { |elem| elem.tr('_', '-') }
|
524
|
+
commands = self.class.all_commands.keys.map { |elem| elem.tr('_', '-') }
|
525
|
+
commands.reject! { |elem| %w[autocomplete default].include?(elem) }
|
511
526
|
end
|
512
527
|
|
528
|
+
# list flags for a command
|
513
529
|
def list_arguments(command:)
|
514
|
-
|
515
|
-
|
516
|
-
|
530
|
+
options = self.class.all_commands[command].options.values
|
531
|
+
exit 1 if options.empty?
|
532
|
+
|
533
|
+
options.map(&:aliases).flatten! +
|
534
|
+
options.map(&:switch_name)
|
535
|
+
end
|
536
|
+
|
537
|
+
# add warning about old keys
|
538
|
+
def age_check_and_get(account:, no_token:)
|
539
|
+
cred = Awskeyring.get_valid_creds(account: account, no_token: no_token)
|
540
|
+
|
541
|
+
maxage = Awskeyring.key_age
|
542
|
+
age = (Time.new - cred[:updated]).div Awskeyring::Awsapi::ONE_DAY
|
543
|
+
warn I18n.t('message.age_check', account: account, age: age) unless age < maxage
|
544
|
+
|
545
|
+
cred
|
517
546
|
end
|
518
547
|
|
548
|
+
# print exports from map
|
519
549
|
def put_env_string(cred)
|
520
550
|
env_var = Awskeyring::Awsapi.get_env_array(cred)
|
521
551
|
env_var.each { |var, value| puts "export #{var}=\"#{value}\"" }
|
522
552
|
Awskeyring::Awsapi::AWS_ENV_VARS.each { |key| puts "unset #{key}" unless env_var.key?(key) }
|
523
553
|
end
|
524
554
|
|
555
|
+
# select duration for sts token types
|
525
556
|
def default_duration(duration, role, code)
|
526
557
|
duration ||= Awskeyring::Awsapi::ONE_HOUR.to_s if role
|
527
558
|
duration ||= Awskeyring::Awsapi::TWELVE_HOUR.to_s if code
|
528
559
|
duration || Awskeyring::Awsapi::ONE_HOUR.to_s
|
529
560
|
end
|
530
561
|
|
562
|
+
# ask and validate input values.
|
531
563
|
def ask_check(existing:, message:, flags: nil, validator: nil, limited_to: nil) # rubocop:disable Metrics/MethodLength
|
532
564
|
retries ||= 3
|
533
565
|
begin
|
@@ -548,10 +580,12 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
548
580
|
value
|
549
581
|
end
|
550
582
|
|
583
|
+
# ask for somthinng if its missing.
|
551
584
|
def ask_missing(existing:, message:, secure: false, optional: false, limited_to: nil)
|
552
585
|
existing || ask(message: message, secure: secure, optional: optional, limited_to: limited_to).strip
|
553
586
|
end
|
554
587
|
|
588
|
+
# ask in different ways
|
555
589
|
def ask(message:, secure: false, optional: false, limited_to: nil)
|
556
590
|
if secure
|
557
591
|
Awskeyring::Input.read_secret("#{message.rjust(20)}: ")
|
@@ -564,6 +598,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
564
598
|
end
|
565
599
|
end
|
566
600
|
|
601
|
+
# undo Bundler env vars
|
567
602
|
def unbundle
|
568
603
|
to_delete = ENV.keys.select { |elem| elem.start_with?('BUNDLER_ORIG_') }
|
569
604
|
bundled_env = to_delete.map { |elem| elem[('BUNDLER_ORIG_'.length)..] }
|
data/man/awskeyring.5
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
.\" generated with Ronn/v0.7.3
|
2
2
|
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
3
3
|
.
|
4
|
-
.TH "AWSKEYRING" "5" "
|
4
|
+
.TH "AWSKEYRING" "5" "July 2021" "" ""
|
5
5
|
.
|
6
6
|
.SH "NAME"
|
7
7
|
\fBAwskeyring\fR \- is a small tool to manage AWS account keys in the macOS Keychain
|
@@ -189,17 +189,14 @@ rotate ACCOUNT:
|
|
189
189
|
Rotate access keys for an ACCOUNT
|
190
190
|
.
|
191
191
|
.TP
|
192
|
-
token ACCOUNT [ROLE] [
|
192
|
+
token ACCOUNT [ROLE] [CODE]:
|
193
193
|
.
|
194
194
|
.IP
|
195
|
-
Create an STS Token from a ROLE or an
|
195
|
+
Create an STS Token from a ROLE or an mfa CODE
|
196
196
|
.
|
197
197
|
.br
|
198
198
|
.
|
199
199
|
.IP
|
200
|
-
\-r, \-\-role=ROLE: The ROLE to assume\.
|
201
|
-
.
|
202
|
-
.br
|
203
200
|
\-c, \-\-code=CODE: Virtual mfa CODE\.
|
204
201
|
.
|
205
202
|
.br
|
@@ -267,8 +264,34 @@ awskeyring env personal\-aws
|
|
267
264
|
.
|
268
265
|
.IP "" 0
|
269
266
|
.
|
267
|
+
.P
|
268
|
+
To open the AWS Console (web page) with your default browser simply run\.\.\.
|
269
|
+
.
|
270
|
+
.IP "" 4
|
271
|
+
.
|
272
|
+
.nf
|
273
|
+
|
274
|
+
awskeyring console personal\-aws
|
275
|
+
.
|
276
|
+
.fi
|
277
|
+
.
|
278
|
+
.IP "" 0
|
279
|
+
.
|
280
|
+
.P
|
281
|
+
Autocomplete is enabled in your current shell with the following command\.\.\.
|
282
|
+
.
|
283
|
+
.IP "" 4
|
284
|
+
.
|
285
|
+
.nf
|
286
|
+
|
287
|
+
complete \-C /usr/local/bin/awskeyring awskeyring
|
288
|
+
.
|
289
|
+
.fi
|
290
|
+
.
|
291
|
+
.IP "" 0
|
292
|
+
.
|
270
293
|
.SH "HISTORY"
|
271
|
-
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\.
|
294
|
+
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\.
|
272
295
|
.
|
273
296
|
.SH "SECURITY"
|
274
297
|
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\.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: awskeyring
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tristan Morgan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-07-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-iam
|
@@ -93,8 +93,8 @@ licenses:
|
|
93
93
|
metadata:
|
94
94
|
bug_tracker_uri: https://github.com/servian/awskeyring/issues
|
95
95
|
changelog_uri: https://github.com/servian/awskeyring/blob/main/CHANGELOG.md
|
96
|
-
documentation_uri: https://rubydoc.info/gems/awskeyring/1.
|
97
|
-
source_code_uri: https://github.com/servian/awskeyring/tree/v1.
|
96
|
+
documentation_uri: https://rubydoc.info/gems/awskeyring/1.9.0
|
97
|
+
source_code_uri: https://github.com/servian/awskeyring/tree/v1.9.0
|
98
98
|
wiki_uri: https://github.com/servian/awskeyring/wiki
|
99
99
|
post_install_message:
|
100
100
|
rdoc_options: []
|