awskeyring 1.8.4 → 1.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +18 -18
- data/Rakefile +3 -3
- data/i18n/en.yml +1 -1
- data/lib/awskeyring/awsapi.rb +1 -1
- data/lib/awskeyring/credential_provider.rb +29 -0
- data/lib/awskeyring/version.rb +1 -1
- data/lib/awskeyring.rb +15 -3
- data/lib/awskeyring_command.rb +46 -20
- data/man/awskeyring.5 +3 -3
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6461179e25d74d7a488cc8dd25db6cb3f3965c177f0dd5cdc8fa440f7c0bf1e3
|
4
|
+
data.tar.gz: 30ac24cde26e0b407a89b218e61ecfd5a8f3ed2852d1da3e3339fa0ef82d17c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c11cee874dbaa962b252a637ebffb6623fbc0bf05cafaab9e32bbf34b8243dcf0a53e91f1ca6b3f1e4a2e65f3112981cae169334fa89817a866b8efe48a650dd
|
7
|
+
data.tar.gz: e4bbba7de171b98f88a1e3ea9232ab54c09a785712cddf979f232539ec867676bc0e33fe3b763eaf8217feecb3728aad11436326795f30560034ceeb2ac2d769
|
data/README.md
CHANGED
@@ -62,24 +62,24 @@ more details on this config option.
|
|
62
62
|
The CLI is using [Thor](http://whatisthor.com) with help provided interactively.
|
63
63
|
|
64
64
|
Awskeyring commands:
|
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
|
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
|
|
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,8 +48,9 @@ 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
|
|
data/i18n/en.yml
CHANGED
@@ -17,7 +17,7 @@ en:
|
|
17
17
|
remove_role_desc: Removes a ROLE from the keyring
|
18
18
|
remove_token_desc: Removes a token for ACCOUNT from the keyring
|
19
19
|
rotate_desc: Rotate access keys for an ACCOUNT
|
20
|
-
token_desc: Create an STS Token from a ROLE or an
|
20
|
+
token_desc: Create an STS Token from a ROLE or an mfa CODE
|
21
21
|
update_desc: Updates an ACCOUNT in the keyring
|
22
22
|
method_option:
|
23
23
|
arn: 'AWS role arn.'
|
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)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aws-sdk-core'
|
4
|
+
require 'awskeyring'
|
5
|
+
|
6
|
+
module Awskeyring
|
7
|
+
# Provide a credential provider for use as a library, eg.
|
8
|
+
# require 'awskeyring/credential_provider'
|
9
|
+
# client = Aws::STS::Client.new(
|
10
|
+
# credentials: Awskeyring::CredentialProvider.new("company-acc")
|
11
|
+
# )
|
12
|
+
class CredentialProvider
|
13
|
+
include Aws::CredentialProvider
|
14
|
+
|
15
|
+
attr_accessor :account
|
16
|
+
|
17
|
+
def initialize(account)
|
18
|
+
@account = account
|
19
|
+
end
|
20
|
+
|
21
|
+
# returns a new Aws::Credentials object
|
22
|
+
def credentials
|
23
|
+
cred = Awskeyring.get_valid_creds(account: account)
|
24
|
+
Aws::Credentials.new(cred[:key],
|
25
|
+
cred[:secret],
|
26
|
+
cred[:token])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/awskeyring/version.rb
CHANGED
data/lib/awskeyring.rb
CHANGED
@@ -52,6 +52,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
|
|
52
52
|
prefs = {
|
53
53
|
awskeyring: awskeyring,
|
54
54
|
keyage: DEFAULT_KEY_AGE,
|
55
|
+
browser: DEFAULT_BROWSER_LIST,
|
55
56
|
console: DEFAULT_CONSOLE_LIST
|
56
57
|
}
|
57
58
|
File.new(Awskeyring::PREFS_FILE, 'w').write JSON.dump(prefs)
|
@@ -97,6 +98,17 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
|
|
97
98
|
all_items.where(account: account).first
|
98
99
|
end
|
99
100
|
|
101
|
+
# return item that matches a prefix if only one.
|
102
|
+
def self.solo_select(list, prefix)
|
103
|
+
return prefix if list.include?(prefix)
|
104
|
+
|
105
|
+
list.select! { |elem| elem.start_with?(prefix) }
|
106
|
+
|
107
|
+
return list.first if list.length == 1
|
108
|
+
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
|
100
112
|
# Add an account item
|
101
113
|
#
|
102
114
|
# @param [String] account The account name to create
|
@@ -317,7 +329,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
|
|
317
329
|
# @param [String] account_name the associated account name.
|
318
330
|
def self.account_exists(account_name)
|
319
331
|
Awskeyring::Validate.account_name(account_name)
|
320
|
-
raise 'Account does not exist' unless
|
332
|
+
raise 'Account does not exist' unless (account_name = solo_select(list_account_names, account_name))
|
321
333
|
|
322
334
|
account_name
|
323
335
|
end
|
@@ -347,7 +359,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
|
|
347
359
|
# @param [String] role_name the associated role name.
|
348
360
|
def self.role_exists(role_name)
|
349
361
|
Awskeyring::Validate.role_name(role_name)
|
350
|
-
raise 'Role does not exist' unless
|
362
|
+
raise 'Role does not exist' unless (role_name = solo_select(list_role_names, role_name))
|
351
363
|
|
352
364
|
role_name
|
353
365
|
end
|
@@ -367,7 +379,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
|
|
367
379
|
# @param [String] token_name the associated account name.
|
368
380
|
def self.token_exists(token_name)
|
369
381
|
Awskeyring::Validate.account_name(token_name)
|
370
|
-
raise 'Token does not exist' unless
|
382
|
+
raise 'Token does not exist' unless (token_name = solo_select(list_token_names, token_name))
|
371
383
|
|
372
384
|
token_name
|
373
385
|
end
|
data/lib/awskeyring_command.rb
CHANGED
@@ -16,7 +16,6 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
16
16
|
I18n.backend.load_translations
|
17
17
|
|
18
18
|
map %w[--version -v] => :__version
|
19
|
-
map %w[--help -h] => :help
|
20
19
|
map 'adr' => :add_role
|
21
20
|
map 'assume-role' => :token
|
22
21
|
map 'ls' => :list
|
@@ -125,7 +124,8 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
125
124
|
# Print JSON for use with credential_process
|
126
125
|
def json(account)
|
127
126
|
account = ask_check(
|
128
|
-
existing: account, message: I18n.t('message.account'), validator: Awskeyring.method(:account_exists)
|
127
|
+
existing: account, message: I18n.t('message.account'), validator: Awskeyring.method(:account_exists),
|
128
|
+
limited_to: Awskeyring.list_account_names
|
129
129
|
)
|
130
130
|
cred = age_check_and_get(account: account, no_token: options['no-token'])
|
131
131
|
expiry = Time.at(cred[:expiry]) unless cred[:expiry].nil?
|
@@ -177,11 +177,15 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
177
177
|
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
|
178
178
|
method_option 'no-bundle', type: :boolean, aliases: '-b', desc: I18n.t('method_option.nobundle'), default: false
|
179
179
|
# execute an external command with env set
|
180
|
-
def exec(account, *command)
|
180
|
+
def exec(account, *command) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
181
181
|
if command.empty?
|
182
182
|
warn I18n.t('message.exec')
|
183
183
|
exit 1
|
184
184
|
end
|
185
|
+
account = ask_check(
|
186
|
+
existing: account, message: I18n.t('message.account'), validator: Awskeyring.method(:account_exists),
|
187
|
+
limited_to: Awskeyring.list_account_names
|
188
|
+
)
|
185
189
|
cred = age_check_and_get(account: account, no_token: options['no-token'])
|
186
190
|
env_vars = Awskeyring::Awsapi.get_env_array(cred)
|
187
191
|
unbundle if options['no-bundle']
|
@@ -335,7 +339,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
335
339
|
puts I18n.t('message.upaccount', account: account)
|
336
340
|
end
|
337
341
|
|
338
|
-
desc 'token ACCOUNT [ROLE] [
|
342
|
+
desc 'token ACCOUNT [ROLE] [CODE]', I18n.t('token_desc')
|
339
343
|
method_option :code, type: :string, aliases: '-c', desc: I18n.t('method_option.code')
|
340
344
|
method_option :duration, type: :string, aliases: '-d', desc: I18n.t('method_option.duration')
|
341
345
|
# generate a sessiopn token
|
@@ -430,7 +434,8 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
430
434
|
desc "#{File.basename($PROGRAM_NAME)} CURR PREV", I18n.t('awskeyring_desc'), hide: true
|
431
435
|
map File.basename($PROGRAM_NAME) => :autocomplete
|
432
436
|
# autocomplete
|
433
|
-
def autocomplete(curr, prev)
|
437
|
+
def autocomplete(curr, prev = nil)
|
438
|
+
curr, prev = fix_args(curr, prev)
|
434
439
|
comp_line = ENV['COMP_LINE']
|
435
440
|
comp_point_str = ENV['COMP_POINT']
|
436
441
|
unless comp_line && comp_point_str
|
@@ -448,16 +453,16 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
448
453
|
|
449
454
|
private
|
450
455
|
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
cred
|
456
|
+
# when a double dash is parsed it is dropped from the args but we need it
|
457
|
+
def fix_args(curr, prev)
|
458
|
+
if prev.nil?
|
459
|
+
[ARGV[1], ARGV[2]]
|
460
|
+
else
|
461
|
+
[curr, prev]
|
462
|
+
end
|
459
463
|
end
|
460
464
|
|
465
|
+
# determine the type of completion needed
|
461
466
|
def comp_type(comp_lines:, prev:)
|
462
467
|
sub_cmd = sub_command(comp_lines)
|
463
468
|
comp_idx = comp_lines.rindex(prev)
|
@@ -475,8 +480,10 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
475
480
|
[comp_type, sub_cmd]
|
476
481
|
end
|
477
482
|
|
483
|
+
# check params for named params or fall back to flags
|
478
484
|
def param_type(comp_idx, sub_cmd)
|
479
|
-
|
485
|
+
types = %i[opt req]
|
486
|
+
param_list = method(sub_cmd).parameters.select { |elem| types.include? elem[0] }
|
480
487
|
if comp_idx.zero?
|
481
488
|
:command
|
482
489
|
elsif comp_idx > param_list.length
|
@@ -486,18 +493,18 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
486
493
|
end
|
487
494
|
end
|
488
495
|
|
496
|
+
# catch the command from prefixes and aliases
|
489
497
|
def sub_command(comp_lines)
|
490
|
-
return '' if comp_lines.
|
498
|
+
return '' if comp_lines.length < 2
|
491
499
|
|
492
|
-
sub_cmd = comp_lines[1]
|
500
|
+
sub_cmd = comp_lines[1]
|
493
501
|
|
494
|
-
|
502
|
+
return self.class.map[sub_cmd].to_s if self.class.map.key? sub_cmd
|
495
503
|
|
496
|
-
|
497
|
-
|
498
|
-
self.class.map[sub_cmd].to_s
|
504
|
+
(Awskeyring.solo_select(list_commands, sub_cmd) || '').tr('-', '_')
|
499
505
|
end
|
500
506
|
|
507
|
+
# given a type return the right list for completions
|
501
508
|
def fetch_auto_resp(comp_type, sub_cmd)
|
502
509
|
case comp_type
|
503
510
|
when :command
|
@@ -517,11 +524,13 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
517
524
|
end
|
518
525
|
end
|
519
526
|
|
527
|
+
# list command names
|
520
528
|
def list_commands
|
521
529
|
commands = self.class.all_commands.keys.map { |elem| elem.tr('_', '-') }
|
522
530
|
commands.reject! { |elem| %w[autocomplete default].include?(elem) }
|
523
531
|
end
|
524
532
|
|
533
|
+
# list flags for a command
|
525
534
|
def list_arguments(command:)
|
526
535
|
options = self.class.all_commands[command].options.values
|
527
536
|
exit 1 if options.empty?
|
@@ -530,18 +539,32 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
530
539
|
options.map(&:switch_name)
|
531
540
|
end
|
532
541
|
|
542
|
+
# add warning about old keys
|
543
|
+
def age_check_and_get(account:, no_token:)
|
544
|
+
cred = Awskeyring.get_valid_creds(account: account, no_token: no_token)
|
545
|
+
|
546
|
+
maxage = Awskeyring.key_age
|
547
|
+
age = (Time.new - cred[:updated]).div Awskeyring::Awsapi::ONE_DAY
|
548
|
+
warn I18n.t('message.age_check', account: account, age: age) unless age < maxage
|
549
|
+
|
550
|
+
cred
|
551
|
+
end
|
552
|
+
|
553
|
+
# print exports from map
|
533
554
|
def put_env_string(cred)
|
534
555
|
env_var = Awskeyring::Awsapi.get_env_array(cred)
|
535
556
|
env_var.each { |var, value| puts "export #{var}=\"#{value}\"" }
|
536
557
|
Awskeyring::Awsapi::AWS_ENV_VARS.each { |key| puts "unset #{key}" unless env_var.key?(key) }
|
537
558
|
end
|
538
559
|
|
560
|
+
# select duration for sts token types
|
539
561
|
def default_duration(duration, role, code)
|
540
562
|
duration ||= Awskeyring::Awsapi::ONE_HOUR.to_s if role
|
541
563
|
duration ||= Awskeyring::Awsapi::TWELVE_HOUR.to_s if code
|
542
564
|
duration || Awskeyring::Awsapi::ONE_HOUR.to_s
|
543
565
|
end
|
544
566
|
|
567
|
+
# ask and validate input values.
|
545
568
|
def ask_check(existing:, message:, flags: nil, validator: nil, limited_to: nil) # rubocop:disable Metrics/MethodLength
|
546
569
|
retries ||= 3
|
547
570
|
begin
|
@@ -562,10 +585,12 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
562
585
|
value
|
563
586
|
end
|
564
587
|
|
588
|
+
# ask for somthinng if its missing.
|
565
589
|
def ask_missing(existing:, message:, secure: false, optional: false, limited_to: nil)
|
566
590
|
existing || ask(message: message, secure: secure, optional: optional, limited_to: limited_to).strip
|
567
591
|
end
|
568
592
|
|
593
|
+
# ask in different ways
|
569
594
|
def ask(message:, secure: false, optional: false, limited_to: nil)
|
570
595
|
if secure
|
571
596
|
Awskeyring::Input.read_secret("#{message.rjust(20)}: ")
|
@@ -578,6 +603,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
578
603
|
end
|
579
604
|
end
|
580
605
|
|
606
|
+
# undo Bundler env vars
|
581
607
|
def unbundle
|
582
608
|
to_delete = ENV.keys.select { |elem| elem.start_with?('BUNDLER_ORIG_') }
|
583
609
|
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" "September 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,10 +189,10 @@ 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
|
.
|
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.2
|
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-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-iam
|
@@ -82,6 +82,7 @@ files:
|
|
82
82
|
- i18n/en.yml
|
83
83
|
- lib/awskeyring.rb
|
84
84
|
- lib/awskeyring/awsapi.rb
|
85
|
+
- lib/awskeyring/credential_provider.rb
|
85
86
|
- lib/awskeyring/input.rb
|
86
87
|
- lib/awskeyring/validate.rb
|
87
88
|
- lib/awskeyring/version.rb
|
@@ -93,8 +94,8 @@ licenses:
|
|
93
94
|
metadata:
|
94
95
|
bug_tracker_uri: https://github.com/servian/awskeyring/issues
|
95
96
|
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.
|
97
|
+
documentation_uri: https://rubydoc.info/gems/awskeyring/1.9.2
|
98
|
+
source_code_uri: https://github.com/servian/awskeyring/tree/v1.9.2
|
98
99
|
wiki_uri: https://github.com/servian/awskeyring/wiki
|
99
100
|
post_install_message:
|
100
101
|
rdoc_options: []
|