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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc910795f877ae9b6a82106d0abb6aa98a1ac41e3b23e995effe1ea5dc8e8ffd
4
- data.tar.gz: 8e6651afc8b0320b9ed9e3d7b32274d6cf473ef36f7d8bf393f4e0115072aed4
3
+ metadata.gz: 155240a1418a0de58a8d3bd88fe97d607cb5f68ea6655f4b3e82ad6628683dad
4
+ data.tar.gz: 99224076df4e631f41e7aef1f0b8a713ff458887943303dbfd691af4e5ddd875
5
5
  SHA512:
6
- metadata.gz: 56112939c5730e6e72812b0e85845018ae762dddc1e8df8c9308c72a8458c010ce6898c1cd957d91f22a2dc3bffcc5504d933c03e711073adea294e218043e9a
7
- data.tar.gz: 60b525d9b832477409eaaa8b59ef4cb34d8ffac239b9ab26011214a8a8e182d1cc9a304ab35ca3ca4b7b128377c019eb6337bac468ed899bfd067fdac677ada0
6
+ metadata.gz: c8cfc74bf58e2886e28c3ab58f3d04955a57dbbcc4bffe9f450a472651df70f2ab3e1585dc21b1118a1f14e5ae8a1703076f56ff5215f19d623c3420c1883b65
7
+ data.tar.gz: 6c0c9f2f7b5916067b016bac45a3088d349298aff18e6b90e554e7ce2791476d8a5865800e0bbe334d2b07146f8a14b6cd68312a82af43ad6aedc5ffde585106
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ![Awskeyring](https://raw.githubusercontent.com/tristanmorgan/awskeyring/main/awskeyring-144.png)
4
4
 
5
- * [![Build Status](https://app.travis-ci.com/tristanmorgan/awskeyring.svg?branch=main)](https://app.travis-ci.com/github/tristanmorgan/awskeyring)
5
+ * ![Build Passing](https://github.com/tristanmorgan/awskeyring/actions/workflows/ruby.yml/badge.svg)
6
6
  * [![Gem Version](https://img.shields.io/gem/v/awskeyring)](https://badge.fury.io/rb/awskeyring)
7
7
  * [![license MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)
8
8
  * [![All Downloads](https://img.shields.io/gem/dt/awskeyring)](https://rubygems.org/gems/awskeyring)
data/exe/awskeyring CHANGED
@@ -11,6 +11,6 @@ end
11
11
  begin
12
12
  AwskeyringCommand.start
13
13
  rescue Keychain::UserCancelledError => e
14
- warn e.to_s
14
+ warn e
15
15
  exit 1
16
16
  end
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
@@ -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.to_s
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
- params.each_key do |param_name|
133
- AWS_ENV_VARS.each do |var_name|
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.to_s
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
@@ -6,7 +6,7 @@ require 'json'
6
6
  # Version const and query of latest.
7
7
  module Awskeyring
8
8
  # The Gem's version number
9
- VERSION = '1.11.0'
9
+ VERSION = '1.12.1'
10
10
  # The Gem's homepage
11
11
  HOMEPAGE = 'https://github.com/tristanmorgan/awskeyring'
12
12
 
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
@@ -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
- puts Awskeyring.list_account_names.join("\n")
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, *command) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
185
- if command.empty?
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, command.join(' '))
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.to_s
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.to_s
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" "February 2023" "" ""
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.11.0
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: 2023-02-21 00:00:00.000000000 Z
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.11.0
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.11.0
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: []