awskeyring 1.11.0 → 1.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/exe/awskeyring +1 -1
- data/i18n/en.yml +1 -0
- data/lib/awskeyring/awsapi.rb +32 -5
- data/lib/awskeyring/version.rb +1 -1
- data/lib/awskeyring.rb +14 -0
- data/lib/awskeyring_command.rb +42 -14
- data/man/awskeyring.5 +4 -1
- 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: 155240a1418a0de58a8d3bd88fe97d607cb5f68ea6655f4b3e82ad6628683dad
|
4
|
+
data.tar.gz: 99224076df4e631f41e7aef1f0b8a713ff458887943303dbfd691af4e5ddd875
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8cfc74bf58e2886e28c3ab58f3d04955a57dbbcc4bffe9f450a472651df70f2ab3e1585dc21b1118a1f14e5ae8a1703076f56ff5215f19d623c3420c1883b65
|
7
|
+
data.tar.gz: 6c0c9f2f7b5916067b016bac45a3088d349298aff18e6b90e554e7ce2791476d8a5865800e0bbe334d2b07146f8a14b6cd68312a82af43ad6aedc5ffde585106
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|

|
4
4
|
|
5
|
-
*
|
5
|
+
* 
|
6
6
|
* [](https://badge.fury.io/rb/awskeyring)
|
7
7
|
* [](https://opensource.org/licenses/MIT)
|
8
8
|
* [](https://rubygems.org/gems/awskeyring)
|
data/exe/awskeyring
CHANGED
data/i18n/en.yml
CHANGED
@@ -5,6 +5,7 @@ en:
|
|
5
5
|
add_role_desc: Adds a ROLE to the keyring
|
6
6
|
awskeyring_desc: Autocompletion for bourne shells
|
7
7
|
console_desc: Open the AWS Console for the ACCOUNT
|
8
|
+
decode_desc: Decode an account id from a KEY
|
8
9
|
default_desc: Run default help or initialise if needed.
|
9
10
|
env_desc: Outputs bourne shell environment exports for an ACCOUNT
|
10
11
|
exec_desc: Execute a COMMAND with the environment set for an ACCOUNT
|
data/lib/awskeyring/awsapi.rb
CHANGED
@@ -25,6 +25,7 @@ module Awskeyring
|
|
25
25
|
# AWS Env vars
|
26
26
|
AWS_ENV_VARS = %w[
|
27
27
|
AWS_ACCOUNT_NAME
|
28
|
+
AWS_ACCOUNT_ID
|
28
29
|
AWS_ACCESS_KEY_ID
|
29
30
|
AWS_ACCESS_KEY
|
30
31
|
AWS_CREDENTIAL_EXPIRATION
|
@@ -85,7 +86,7 @@ module Awskeyring
|
|
85
86
|
)
|
86
87
|
end
|
87
88
|
rescue Aws::STS::Errors::AccessDenied => e
|
88
|
-
warn e
|
89
|
+
warn e
|
89
90
|
exit 1
|
90
91
|
end
|
91
92
|
|
@@ -123,14 +124,16 @@ module Awskeyring
|
|
123
124
|
# [String] secret The aws_secret_access_key
|
124
125
|
# [String] token The aws_session_token
|
125
126
|
# @return [Hash] env_var hash
|
126
|
-
def self.get_env_array(params = {})
|
127
|
+
def self.get_env_array(params = {}) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
|
127
128
|
env_var = {}
|
128
129
|
env_var['AWS_DEFAULT_REGION'] = 'us-east-1' unless region
|
129
130
|
|
130
131
|
params[:expiration] = Time.at(params[:expiry]).iso8601 unless params[:expiry].nil?
|
132
|
+
params[:account_name] = params.delete(:account)
|
133
|
+
params[:account_id] = get_account_id(key: params[:key]) unless params[:key].nil?
|
131
134
|
|
132
|
-
|
133
|
-
|
135
|
+
AWS_ENV_VARS.each do |var_name|
|
136
|
+
params.each_key do |param_name|
|
134
137
|
if var_name.include?(param_name.to_s.upcase) && !params[param_name].nil?
|
135
138
|
env_var[var_name] = params[param_name]
|
136
139
|
end
|
@@ -151,7 +154,7 @@ module Awskeyring
|
|
151
154
|
sts = Aws::STS::Client.new(access_key_id: key, secret_access_key: secret, session_token: token)
|
152
155
|
sts.get_caller_identity
|
153
156
|
rescue Aws::Errors::ServiceError => e
|
154
|
-
warn e
|
157
|
+
warn e
|
155
158
|
exit 1
|
156
159
|
end
|
157
160
|
true
|
@@ -229,6 +232,30 @@ module Awskeyring
|
|
229
232
|
region || Aws.shared_config.region(profile: 'default')
|
230
233
|
end
|
231
234
|
|
235
|
+
# Get the account number from an access key
|
236
|
+
#
|
237
|
+
# @param [String] key The aws_access_key_id
|
238
|
+
# @return [String] Account number
|
239
|
+
def self.get_account_id(key:)
|
240
|
+
padded_no = key[3..12]
|
241
|
+
mask = (2 << 39) - 1
|
242
|
+
decimal = (decode(padded_no) >> 4) & mask
|
243
|
+
decimal.to_s.rjust(12, '0')
|
244
|
+
end
|
245
|
+
|
246
|
+
# base32 decode function
|
247
|
+
# returns 0 on failure
|
248
|
+
private_class_method def self.decode(str)
|
249
|
+
aws_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
|
250
|
+
bytes = str.bytes
|
251
|
+
bytes.inject do |m, o|
|
252
|
+
i = aws_table.index(o.chr)
|
253
|
+
return 0 if i.nil?
|
254
|
+
|
255
|
+
(m << 5) + i
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
232
259
|
# Rotates the AWS access keys
|
233
260
|
#
|
234
261
|
# @param [String] key The aws_access_key_id
|
data/lib/awskeyring/version.rb
CHANGED
data/lib/awskeyring.rb
CHANGED
@@ -4,6 +4,7 @@ require 'i18n'
|
|
4
4
|
require 'json'
|
5
5
|
require 'keychain'
|
6
6
|
require 'awskeyring/validate'
|
7
|
+
require 'awskeyring/awsapi'
|
7
8
|
|
8
9
|
# Awskeyring Module,
|
9
10
|
# gives you an interface to access keychains and items.
|
@@ -195,6 +196,19 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
|
|
195
196
|
(items + tokens).uniq.sort
|
196
197
|
end
|
197
198
|
|
199
|
+
# Return a list account item names plus account ids
|
200
|
+
def self.list_account_names_plus # rubocop:disable Metrics/AbcSize
|
201
|
+
list_items.concat(list_tokens).map do |elem|
|
202
|
+
account_id = Awskeyring::Awsapi.get_account_id(key: elem.attributes[:account])
|
203
|
+
account_name = if elem.attributes[:label].start_with?(ACCOUNT_PREFIX)
|
204
|
+
elem.attributes[:label][(ACCOUNT_PREFIX.length)..]
|
205
|
+
else
|
206
|
+
elem.attributes[:label][(SESSION_KEY_PREFIX.length)..]
|
207
|
+
end
|
208
|
+
"#{account_name}\t#{account_id}"
|
209
|
+
end.uniq.sort
|
210
|
+
end
|
211
|
+
|
198
212
|
# Return a list role item names
|
199
213
|
def self.list_role_names
|
200
214
|
list_roles.map { |elem| elem.attributes[:label][(ROLE_PREFIX.length)..] }.sort
|
data/lib/awskeyring_command.rb
CHANGED
@@ -77,13 +77,18 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
77
77
|
end
|
78
78
|
|
79
79
|
desc 'list', I18n.t('list_desc')
|
80
|
+
method_option :detail, type: :boolean, aliases: '-d', desc: I18n.t('method_option.detail'), default: false
|
80
81
|
# list the accounts
|
81
82
|
def list
|
82
83
|
if Awskeyring.list_account_names.empty?
|
83
84
|
warn I18n.t('message.missing_account', bin: File.basename($PROGRAM_NAME))
|
84
85
|
exit 1
|
85
86
|
end
|
86
|
-
|
87
|
+
if options[:detail]
|
88
|
+
puts Awskeyring.list_account_names_plus.join("\n")
|
89
|
+
else
|
90
|
+
puts Awskeyring.list_account_names.join("\n")
|
91
|
+
end
|
87
92
|
end
|
88
93
|
|
89
94
|
desc 'list-role', I18n.t('list_role_desc')
|
@@ -181,8 +186,8 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
181
186
|
method_option 'no-bundle', type: :boolean, aliases: '-b', desc: I18n.t('method_option.nobundle'), default: false
|
182
187
|
method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
|
183
188
|
# execute an external command with env set
|
184
|
-
def exec(account, *
|
185
|
-
if
|
189
|
+
def exec(account, *exec) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
190
|
+
if exec.empty?
|
186
191
|
warn I18n.t('message.exec')
|
187
192
|
exit 1
|
188
193
|
end
|
@@ -194,11 +199,11 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
194
199
|
env_vars = Awskeyring::Awsapi.get_env_array(cred)
|
195
200
|
unbundle if options['no-bundle']
|
196
201
|
begin
|
197
|
-
pid = Process.spawn(env_vars,
|
202
|
+
pid = Process.spawn(env_vars, exec.join(' '))
|
198
203
|
Process.wait pid
|
199
204
|
exit 1 if Process.last_status.exitstatus.positive?
|
200
205
|
rescue Errno::ENOENT => e
|
201
|
-
warn e
|
206
|
+
warn e
|
202
207
|
exit 1
|
203
208
|
end
|
204
209
|
end
|
@@ -330,7 +335,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
330
335
|
key_message: I18n.t('message.rotate', account: account)
|
331
336
|
)
|
332
337
|
rescue Aws::Errors::ServiceError => e
|
333
|
-
warn e
|
338
|
+
warn e
|
334
339
|
exit 1
|
335
340
|
end
|
336
341
|
|
@@ -426,6 +431,16 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
426
431
|
end
|
427
432
|
end
|
428
433
|
|
434
|
+
desc 'decode KEY', I18n.t('decode_desc'), hide: true
|
435
|
+
# decode account numbers
|
436
|
+
def decode(key = nil)
|
437
|
+
key = ask_check(
|
438
|
+
existing: key, message: I18n.t('message.key'), validator: Awskeyring::Validate.method(:access_key)
|
439
|
+
)
|
440
|
+
|
441
|
+
puts Awskeyring::Awsapi.get_account_id(key: key)
|
442
|
+
end
|
443
|
+
|
429
444
|
desc "#{File.basename($PROGRAM_NAME)} CURR PREV", I18n.t('awskeyring_desc'), hide: true
|
430
445
|
map File.basename($PROGRAM_NAME) => :autocomplete
|
431
446
|
# autocomplete
|
@@ -441,7 +456,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
441
456
|
|
442
457
|
comp_lines = comp_line[0..(comp_point_str.to_i)].split
|
443
458
|
|
444
|
-
comp_type, sub_cmd = comp_type(comp_lines: comp_lines, prev: prev)
|
459
|
+
comp_type, sub_cmd = comp_type(comp_lines: comp_lines, prev: prev, curr: curr)
|
445
460
|
list = fetch_auto_resp(comp_type, sub_cmd)
|
446
461
|
puts list.select { |elem| elem.start_with?(curr) }.sort!.join("\n")
|
447
462
|
end
|
@@ -458,7 +473,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
458
473
|
end
|
459
474
|
|
460
475
|
# determine the type of completion needed
|
461
|
-
def comp_type(comp_lines:, prev:)
|
476
|
+
def comp_type(comp_lines:, prev:, curr:)
|
462
477
|
sub_cmd = sub_command(comp_lines)
|
463
478
|
comp_idx = comp_lines.rindex(prev)
|
464
479
|
|
@@ -469,19 +484,19 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
469
484
|
comp_type = :browser_type
|
470
485
|
else
|
471
486
|
comp_type = :command
|
472
|
-
comp_type = param_type(comp_idx, sub_cmd) unless sub_cmd.empty?
|
487
|
+
comp_type = param_type(comp_idx, sub_cmd, curr) unless sub_cmd.empty?
|
473
488
|
end
|
474
489
|
|
475
490
|
[comp_type, sub_cmd]
|
476
491
|
end
|
477
492
|
|
478
493
|
# check params for named params or fall back to flags
|
479
|
-
def param_type(comp_idx, sub_cmd)
|
480
|
-
types = %i[opt req]
|
494
|
+
def param_type(comp_idx, sub_cmd, curr)
|
495
|
+
types = %i[opt req rest]
|
481
496
|
param_list = method(sub_cmd).parameters.select { |elem| types.include? elem[0] }
|
482
497
|
if comp_idx.zero?
|
483
498
|
:command
|
484
|
-
elsif comp_idx > param_list.length
|
499
|
+
elsif comp_idx > param_list.length || curr.start_with?('-')
|
485
500
|
:flag
|
486
501
|
else
|
487
502
|
param_list[comp_idx - 1][1]
|
@@ -500,7 +515,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
500
515
|
end
|
501
516
|
|
502
517
|
# given a type return the right list for completions
|
503
|
-
def fetch_auto_resp(comp_type, sub_cmd)
|
518
|
+
def fetch_auto_resp(comp_type, sub_cmd) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
504
519
|
case comp_type
|
505
520
|
when :command
|
506
521
|
list_commands
|
@@ -514,6 +529,8 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
514
529
|
Awskeyring.list_token_names
|
515
530
|
when :browser_type
|
516
531
|
Awskeyring.list_browsers
|
532
|
+
when :exec
|
533
|
+
list_exec
|
517
534
|
else
|
518
535
|
list_arguments(command: sub_cmd)
|
519
536
|
end
|
@@ -522,7 +539,18 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
|
|
522
539
|
# list command names
|
523
540
|
def list_commands
|
524
541
|
commands = self.class.all_commands.keys.map { |elem| elem.tr('_', '-') }
|
525
|
-
commands.reject! { |elem| %w[autocomplete default].include?(elem) }
|
542
|
+
commands.reject! { |elem| %w[autocomplete default decode].include?(elem) }
|
543
|
+
end
|
544
|
+
|
545
|
+
# list executables
|
546
|
+
def list_exec
|
547
|
+
list = []
|
548
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
549
|
+
list.concat(Dir.children(File.expand_path(path)))
|
550
|
+
rescue Errno::ENOENT
|
551
|
+
next
|
552
|
+
end
|
553
|
+
list.flatten
|
526
554
|
end
|
527
555
|
|
528
556
|
# list flags for a command
|
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" "June 2024" "" ""
|
5
5
|
.
|
6
6
|
.SH "NAME"
|
7
7
|
\fBAwskeyring\fR \- is a small tool to manage AWS account keys in the macOS Keychain
|
@@ -159,6 +159,9 @@ list:
|
|
159
159
|
.IP
|
160
160
|
Prints a list of accounts in the keyring
|
161
161
|
.
|
162
|
+
.IP
|
163
|
+
\-d, \-\-detail, \-\-no\-detail: Show more detail\.
|
164
|
+
.
|
162
165
|
.TP
|
163
166
|
list\-role:
|
164
167
|
.
|
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.12.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tristan Morgan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-06-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-iam
|
@@ -92,9 +92,9 @@ licenses:
|
|
92
92
|
metadata:
|
93
93
|
bug_tracker_uri: https://github.com/tristanmorgan/awskeyring/issues
|
94
94
|
changelog_uri: https://github.com/tristanmorgan/awskeyring/blob/main/CHANGELOG.md
|
95
|
-
documentation_uri: https://rubydoc.info/gems/awskeyring/1.
|
95
|
+
documentation_uri: https://rubydoc.info/gems/awskeyring/1.12.1
|
96
96
|
rubygems_mfa_required: 'true'
|
97
|
-
source_code_uri: https://github.com/tristanmorgan/awskeyring/tree/v1.
|
97
|
+
source_code_uri: https://github.com/tristanmorgan/awskeyring/tree/v1.12.1
|
98
98
|
wiki_uri: https://github.com/tristanmorgan/awskeyring/wiki
|
99
99
|
post_install_message:
|
100
100
|
rdoc_options: []
|