awskeyring 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 045e119cd7be746d03d7b12c73d804958cc5331b
4
- data.tar.gz: 1e7bc87c08887ed8625a80d629d5822ca60a3d08
3
+ metadata.gz: c6053b960d27395acf21d82a71883cdcf5788959
4
+ data.tar.gz: 0f8efce2feb5af0a87d7be239a85733448eee90d
5
5
  SHA512:
6
- metadata.gz: 92b6df41a05aef2d2c4246b86daad7a6fdc4c8092192e05341167e6b707663ca73d9babbcc8ef871644743d40b2e84207f985eb4cfc140296a8297c18afebe94
7
- data.tar.gz: 4c32c86b6ed103a50e4218e1f6a258e0bbe29ddd79de7470a56b149567fa2a66ca5515540092714c57cde7c98987a211d7ac87dfed17c11c682039db92455728
6
+ metadata.gz: 8449e554b6d4543d851d4caf488807965e5a5ee2cb656a4eb5535662254bc273ca457f76b1a8b0f435ecd771d7b5fb1cd6a8f1521fd6cca5c6d09b70f13246a1
7
+ data.tar.gz: 71d5d0e493329b3b6dc3c203175c79baf3a5caba618885863e9bc358c934f9d5215161fbe0a609b8c7cbc69d8f02aa6615960c6b1fde36e2c78f4fc37ea1332d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Change Log
2
2
 
3
+ ## [v0.5.0](https://github.com/vibrato/awskeyring/tree/v0.5.0) (2018-09-10)
4
+ [Full Changelog](https://github.com/vibrato/awskeyring/compare/v0.4.0...v0.5.0)
5
+
6
+ **Implemented enhancements:**
7
+
8
+ - Separate update account from add account. [\#28](https://github.com/vibrato/awskeyring/pull/28) ([tristanmorgan](https://github.com/tristanmorgan))
9
+
10
+ **Merged pull requests:**
11
+
12
+ - Refactor [\#27](https://github.com/vibrato/awskeyring/pull/27) ([tristanmorgan](https://github.com/tristanmorgan))
13
+
3
14
  ## [v0.4.0](https://github.com/vibrato/awskeyring/tree/v0.4.0) (2018-08-21)
4
15
  [Full Changelog](https://github.com/vibrato/awskeyring/compare/v0.3.1...v0.4.0)
5
16
 
@@ -73,7 +84,7 @@
73
84
  ## [v0.0.5](https://github.com/vibrato/awskeyring/tree/v0.0.5) (2018-02-15)
74
85
  [Full Changelog](https://github.com/vibrato/awskeyring/compare/v0.0.4...v0.0.5)
75
86
 
76
- **Closed issues:**
87
+ **Fixed bugs:**
77
88
 
78
89
  - Issue on add [\#7](https://github.com/vibrato/awskeyring/issues/7)
79
90
 
data/README.md CHANGED
@@ -67,6 +67,7 @@ The CLI is using [Thor](http://whatisthor.com) with help provided interactively.
67
67
  awskeyring remove-token ACCOUNT # Removes a token for ACCOUNT from the keyring
68
68
  awskeyring rotate ACCOUNT # Rotate access keys for an ACCOUNT
69
69
  awskeyring token ACCOUNT [ROLE] [MFA] # Create an STS Token from a ROLE or an MFA code
70
+ awskeyring update ACCOUNT # Updates an ACCOUNT in the keyring
70
71
 
71
72
  and autocomplete that can be installed with:
72
73
 
data/Rakefile CHANGED
@@ -2,6 +2,7 @@ require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
3
  require 'rubocop/rake_task'
4
4
  require 'github_changelog_generator/task'
5
+ require 'yard'
5
6
 
6
7
  GitHubChangelogGenerator::RakeTask.new :changelog do |config|
7
8
  config.future_release = "v#{Awskeyring::VERSION}"
@@ -29,4 +30,9 @@ task :filemode do
29
30
  print "\n"
30
31
  end
31
32
 
32
- task default: %i[filemode rubocop spec]
33
+ YARD::Rake::YardocTask.new do |t|
34
+ t.options = ['--fail-on-warning', '--no-progress']
35
+ t.stats_options = ['--list-undoc']
36
+ end
37
+
38
+ task default: %i[filemode rubocop spec yard]
data/awskeyring.gemspec CHANGED
@@ -29,4 +29,5 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency 'rake'
30
30
  spec.add_development_dependency 'rspec'
31
31
  spec.add_development_dependency 'rubocop'
32
+ spec.add_development_dependency 'yard'
32
33
  end
data/i18n/en.yml CHANGED
@@ -32,6 +32,8 @@ en:
32
32
  desc: Rotate access keys for an ACCOUNT
33
33
  token:
34
34
  desc: Create an STS Token from a ROLE or an MFA code
35
+ update:
36
+ desc: Updates an ACCOUNT in the keyring
35
37
  method_option:
36
38
  arn: 'AWS role arn.'
37
39
  code: 'Virtual mfa CODE.'
@@ -40,11 +42,11 @@ en:
40
42
  keychain: 'Name of KEYCHAIN to initialise.'
41
43
  local: 'Only validate locally.'
42
44
  mfa: 'AWS virtual mfa arn.'
45
+ noopen: 'Do not open the url.'
43
46
  notoken: 'Do not use saved token.'
44
47
  path: 'The service PATH to open.'
45
48
  role: 'The ROLE to assume.'
46
49
  secret: 'AWS account secret.'
47
- update: 'Update existing.'
48
50
  message:
49
51
  keychain: 'Name for new keychain (default: awskeyring)'
50
52
  account: 'account name'
@@ -17,7 +17,9 @@ module Awskeyring
17
17
  }]
18
18
  }.to_json.freeze
19
19
 
20
+ # Twelve hours in seconds
20
21
  TWELVE_HOUR = (60 * 60 * 12)
22
+ # One hour in seconds
21
23
  ONE_HOUR = (60 * 60 * 1)
22
24
  # Days in seconds
23
25
  ONE_DAY = (24 * 60 * 60)
@@ -100,7 +102,6 @@ module Awskeyring
100
102
  #
101
103
  # @param [String] key The aws_access_key_id
102
104
  # @param [String] secret The aws_secret_access_key
103
- # @param [String] token The aws_session_token
104
105
  def self.verify_cred(key:, secret:)
105
106
  begin
106
107
  ENV['AWS_DEFAULT_REGION'] = 'us-east-1' unless region
@@ -8,6 +8,7 @@ module Awskeyring
8
8
  # @param [String] account_name the associated account name.
9
9
  def self.account_name(account_name)
10
10
  raise 'Invalid Account Name' unless account_name =~ /\S+/
11
+
11
12
  account_name
12
13
  end
13
14
 
@@ -16,6 +17,7 @@ module Awskeyring
16
17
  # @param [String] aws_access_key The aws_access_key_id
17
18
  def self.access_key(aws_access_key)
18
19
  raise 'Invalid Access Key' unless aws_access_key =~ /\AAKIA[A-Z0-9]{12,16}\z/
20
+
19
21
  aws_access_key
20
22
  end
21
23
 
@@ -24,6 +26,7 @@ module Awskeyring
24
26
  # @param [String] aws_secret_access_key The aws_secret_access_key
25
27
  def self.secret_access_key(aws_secret_access_key)
26
28
  raise 'Secret Access Key is not 40 chars' if aws_secret_access_key.length != 40
29
+
27
30
  aws_secret_access_key
28
31
  end
29
32
 
@@ -32,6 +35,7 @@ module Awskeyring
32
35
  # @param [String] mfa_arn The users MFA arn
33
36
  def self.mfa_arn(mfa_arn)
34
37
  raise 'Invalid MFA ARN' unless mfa_arn =~ %r(\Aarn:aws:iam::[0-9]{12}:mfa\/\S*\z)
38
+
35
39
  mfa_arn
36
40
  end
37
41
 
@@ -40,6 +44,7 @@ module Awskeyring
40
44
  # @param [String] role_name
41
45
  def self.role_name(role_name)
42
46
  raise 'Invalid Role Name' unless role_name =~ /\S+/
47
+
43
48
  role_name
44
49
  end
45
50
 
@@ -48,6 +53,7 @@ module Awskeyring
48
53
  # @param [String] role_arn The role arn
49
54
  def self.role_arn(role_arn)
50
55
  raise 'Invalid Role ARN' unless role_arn =~ %r(\Aarn:aws:iam::[0-9]{12}:role\/\S*\z)
56
+
51
57
  role_arn
52
58
  end
53
59
 
@@ -56,6 +62,7 @@ module Awskeyring
56
62
  # @param [String] mfa_code The mfa code
57
63
  def self.mfa_code(mfa_code)
58
64
  raise 'Invalid MFA CODE' unless mfa_code =~ /\A\d{6}\z/
65
+
59
66
  mfa_code
60
67
  end
61
68
  end
@@ -1,4 +1,4 @@
1
1
  module Awskeyring
2
2
  # The Gems version number
3
- VERSION = '0.4.0'.freeze
3
+ VERSION = '0.5.0'.freeze
4
4
  end
data/lib/awskeyring.rb CHANGED
@@ -128,7 +128,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
128
128
  end
129
129
 
130
130
  # Return a session token pair of items by name
131
- private_class_method def self.get_pair(account:)
131
+ private_class_method def self.get_token_pair(account:)
132
132
  session_key = all_items.where(label: SESSION_KEY_PREFIX + account).first
133
133
  session_token = all_items.where(label: SESSION_TOKEN_PREFIX + account).first if session_key
134
134
  [session_key, session_token]
@@ -145,11 +145,11 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
145
145
  end
146
146
 
147
147
  # Return a session token if available or a static key
148
- private_class_method def self.get_valid_item_pair(account:)
149
- session_key, session_token = get_pair(account: account)
148
+ private_class_method def self.get_valid_item_pair(account:, no_token: false)
149
+ session_key, session_token = get_token_pair(account: account)
150
150
  session_key, session_token = delete_expired(key: session_key, token: session_token) if session_key
151
151
 
152
- if session_key && session_token
152
+ if session_key && session_token && !no_token
153
153
  puts I18n.t('message.temporary')
154
154
  return session_key, session_token
155
155
  end
@@ -164,33 +164,16 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
164
164
 
165
165
  # Return valid creds for account
166
166
  def self.get_valid_creds(account:, no_token: false)
167
- if no_token
168
- cred = get_item(account: account)
169
- temp_cred = nil
170
- else
171
- cred, temp_cred = get_valid_item_pair(account: account)
172
- end
167
+ cred, temp_cred = get_valid_item_pair(account: account, no_token: no_token)
173
168
  token = temp_cred.password unless temp_cred.nil?
174
169
  expiry = temp_cred.attributes[:account].to_i unless temp_cred.nil?
175
170
  {
176
171
  account: account,
177
- key: cred.attributes[:account],
178
- secret: cred.password,
179
- token: token,
180
172
  expiry: expiry,
181
- updated: cred.attributes[:updated_at]
182
- }
183
- end
184
-
185
- # Return a hash for account (skip tokens)
186
- def self.get_account_hash(account:)
187
- cred = get_item(account: account)
188
- return unless cred
189
- {
190
- account: account,
191
173
  key: cred.attributes[:account],
174
+ mfa: no_token ? cred.attributes[:comment] : nil,
192
175
  secret: cred.password,
193
- mfa: cred.attributes[:comment],
176
+ token: token,
194
177
  updated: cred.attributes[:updated_at]
195
178
  }
196
179
  end
@@ -215,6 +198,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
215
198
  # Delete session token items
216
199
  private_class_method def self.delete_pair(key:, token:, message:)
217
200
  return unless key
201
+
218
202
  puts message if message
219
203
  token.delete if token
220
204
  key.delete
@@ -222,7 +206,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
222
206
 
223
207
  # Delete a session token
224
208
  def self.delete_token(account:, message:)
225
- session_key, session_token = get_pair(account: account)
209
+ session_key, session_token = get_token_pair(account: account)
226
210
  delete_pair(key: session_key, token: session_token, message: message)
227
211
  end
228
212
 
@@ -231,6 +215,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
231
215
  delete_token(account: account, message: I18n.t('message.delexpired'))
232
216
  cred = get_item(account: account)
233
217
  return unless cred
218
+
234
219
  puts message if message
235
220
  cred.delete
236
221
  end
@@ -239,6 +224,7 @@ module Awskeyring # rubocop:disable Metrics/ModuleLength
239
224
  def self.delete_role(role_name:, message:)
240
225
  role = get_role(role_name: role_name)
241
226
  return unless role
227
+
242
228
  puts message if message
243
229
  role.delete
244
230
  end
@@ -21,6 +21,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
21
21
  map ['rmr'] => :remove_role
22
22
  map ['rmt'] => :remove_token
23
23
  map ['rot'] => :rotate
24
+ map ['up'] => :update
24
25
 
25
26
  desc '--version, -v', I18n.t('__version.desc')
26
27
  # print the version number
@@ -68,8 +69,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
68
69
  account = ask_check(
69
70
  existing: account, message: I18n.t('message.account'), validator: Awskeyring::Validate.method(:account_name)
70
71
  )
71
- cred = Awskeyring.get_valid_creds(account: account, no_token: options['no-token'])
72
- age_check(account, cred[:updated])
72
+ cred = age_check_and_get(account: account, no_token: options['no-token'])
73
73
  put_env_string(
74
74
  account: cred[:account],
75
75
  key: cred[:key],
@@ -85,8 +85,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
85
85
  account = ask_check(
86
86
  existing: account, message: I18n.t('message.account'), validator: Awskeyring::Validate.method(:account_name)
87
87
  )
88
- cred = Awskeyring.get_valid_creds(account: account, no_token: options['no-token'])
89
- age_check(account, cred[:updated])
88
+ cred = age_check_and_get(account: account, no_token: options['no-token'])
90
89
  expiry = Time.at(cred[:expiry]) unless cred[:expiry].nil?
91
90
  puts Awskeyring::Awsapi.get_cred_json(
92
91
  key: cred[:key],
@@ -100,8 +99,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
100
99
  method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
101
100
  # execute an external command with env set
102
101
  def exec(account, *command)
103
- cred = Awskeyring.get_valid_creds(account: account, no_token: options['no-token'])
104
- age_check(account, cred[:updated])
102
+ cred = age_check_and_get(account: account, no_token: options['no-token'])
105
103
  env_vars = env_vars(
106
104
  account: cred[:account],
107
105
  key: cred[:key],
@@ -117,7 +115,6 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
117
115
  method_option :secret, type: :string, aliases: '-s', desc: I18n.t('method_option.secret')
118
116
  method_option :mfa, type: :string, aliases: '-m', desc: I18n.t('method_option.mfa')
119
117
  method_option :local, type: :boolean, aliases: '-l', desc: I18n.t('method_option.local'), default: false
120
- method_option :update, type: :boolean, aliases: '-u', desc: I18n.t('method_option.update'), default: false
121
118
  # Add an Account
122
119
  def add(account = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
123
120
  account = ask_check(
@@ -130,28 +127,43 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
130
127
  existing: options[:secret], message: I18n.t('message.secret'),
131
128
  secure: true, validator: Awskeyring::Validate.method(:secret_access_key)
132
129
  )
133
- if options[:update]
134
- Awskeyring::Awsapi.verify_cred(key: key, secret: secret) unless options[:local]
135
- Awskeyring.update_account(
136
- account: account,
137
- key: key,
138
- secret: secret
139
- )
140
- puts I18n.t('message.upaccount', account: account)
141
- else
142
- mfa = ask_check(
143
- existing: options[:mfa], message: I18n.t('message.mfa'),
144
- optional: true, validator: Awskeyring::Validate.method(:mfa_arn)
145
- )
146
- Awskeyring::Awsapi.verify_cred(key: key, secret: secret) unless options[:local]
147
- Awskeyring.add_account(
148
- account: account,
149
- key: key,
150
- secret: secret,
151
- mfa: mfa
152
- )
153
- puts I18n.t('message.addaccount', account: account)
154
- end
130
+ mfa = ask_check(
131
+ existing: options[:mfa], message: I18n.t('message.mfa'),
132
+ optional: true, validator: Awskeyring::Validate.method(:mfa_arn)
133
+ )
134
+ Awskeyring::Awsapi.verify_cred(key: key, secret: secret) unless options[:local]
135
+ Awskeyring.add_account(
136
+ account: account,
137
+ key: key,
138
+ secret: secret,
139
+ mfa: mfa
140
+ )
141
+ puts I18n.t('message.addaccount', account: account)
142
+ end
143
+
144
+ desc 'update ACCOUNT', I18n.t('update.desc')
145
+ method_option :key, type: :string, aliases: '-k', desc: I18n.t('method_option.key')
146
+ method_option :secret, type: :string, aliases: '-s', desc: I18n.t('method_option.secret')
147
+ method_option :local, type: :boolean, aliases: '-l', desc: I18n.t('method_option.local'), default: false
148
+ # Update an Account
149
+ def update(account = nil) # rubocop:disable Metrics/MethodLength
150
+ account = ask_check(
151
+ existing: account, message: I18n.t('message.account'), validator: Awskeyring::Validate.method(:account_name)
152
+ )
153
+ key = ask_check(
154
+ existing: options[:key], message: I18n.t('message.key'), validator: Awskeyring::Validate.method(:access_key)
155
+ )
156
+ secret = ask_check(
157
+ existing: options[:secret], message: I18n.t('message.secret'),
158
+ secure: true, validator: Awskeyring::Validate.method(:secret_access_key)
159
+ )
160
+ Awskeyring::Awsapi.verify_cred(key: key, secret: secret) unless options[:local]
161
+ Awskeyring.update_account(
162
+ account: account,
163
+ key: key,
164
+ secret: secret
165
+ )
166
+ puts I18n.t('message.upaccount', account: account)
155
167
  end
156
168
 
157
169
  map 'add-role' => :add_role
@@ -214,13 +226,13 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
214
226
  account = ask_check(
215
227
  existing: account, message: I18n.t('message.account'), validator: Awskeyring::Validate.method(:account_name)
216
228
  )
217
- item_hash = Awskeyring.get_account_hash(account: account)
229
+ cred = Awskeyring.get_valid_creds(account: account, no_token: true)
218
230
 
219
231
  begin
220
232
  new_key = Awskeyring::Awsapi.rotate(
221
- account: item_hash[:account],
222
- key: item_hash[:key],
223
- secret: item_hash[:secret],
233
+ account: cred[:account],
234
+ key: cred[:key],
235
+ secret: cred[:secret],
224
236
  key_message: I18n.t('message.rotate', account: account)
225
237
  )
226
238
  rescue Aws::Errors::ServiceError => err
@@ -263,8 +275,7 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
263
275
  duration ||= Awskeyring::Awsapi::TWELVE_HOUR.to_s if code
264
276
  duration ||= Awskeyring::Awsapi::ONE_HOUR.to_s
265
277
 
266
- item_hash = Awskeyring.get_account_hash(account: account)
267
- age_check(account, item_hash[:updated])
278
+ item_hash = age_check_and_get(account: account, no_token: true)
268
279
  role_arn = Awskeyring.get_role_arn(role_name: role) if role
269
280
 
270
281
  begin
@@ -298,13 +309,13 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
298
309
  desc 'console ACCOUNT', I18n.t('console.desc')
299
310
  method_option :path, type: :string, aliases: '-p', desc: I18n.t('method_option.path')
300
311
  method_option 'no-token', type: :boolean, aliases: '-n', desc: I18n.t('method_option.notoken'), default: false
312
+ method_option 'no-open', type: :boolean, aliases: '-o', desc: I18n.t('method_option.noopen'), default: false
301
313
  # Open the AWS Console
302
314
  def console(account = nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
303
315
  account = ask_check(
304
316
  existing: account, message: I18n.t('message.account'), validator: Awskeyring::Validate.method(:account_name)
305
317
  )
306
- cred = Awskeyring.get_valid_creds(account: account, no_token: options['no-token'])
307
- age_check(account, cred[:updated])
318
+ cred = age_check_and_get(account: account, no_token: options['no-token'])
308
319
 
309
320
  path = options[:path] || 'console'
310
321
 
@@ -321,8 +332,12 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
321
332
  exit 1
322
333
  end
323
334
 
324
- pid = Process.spawn("open \"#{login_url}\"")
325
- Process.wait pid
335
+ if options['no-open']
336
+ puts login_url
337
+ else
338
+ pid = Process.spawn("open \"#{login_url}\"")
339
+ Process.wait pid
340
+ end
326
341
  end
327
342
 
328
343
  desc 'awskeyring CURR PREV', I18n.t('awskeyring.desc'), hide: true
@@ -348,10 +363,14 @@ class AwskeyringCommand < Thor # rubocop:disable Metrics/ClassLength
348
363
 
349
364
  private
350
365
 
351
- def age_check(account, updated)
366
+ def age_check_and_get(account:, no_token:)
367
+ cred = Awskeyring.get_valid_creds(account: account, no_token: no_token)
368
+
352
369
  maxage = Awskeyring.prefs[:keyage] || Awskeyring::DEFAULT_KEY_AGE
353
- age = (Time.new - updated).div Awskeyring::Awsapi::ONE_DAY
370
+ age = (Time.new - cred[:updated]).div Awskeyring::Awsapi::ONE_DAY
354
371
  warn I18n.t('message.age_check', account: account, age: age) unless age < maxage
372
+
373
+ cred
355
374
  end
356
375
 
357
376
  def print_auto_resp(curr, len)
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: 0.4.0
4
+ version: 0.5.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: 2018-08-21 00:00:00.000000000 Z
11
+ date: 2018-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-iam
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - ">="
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: yard
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
153
167
  description: Manages AWS credentials in the macOS keychain
154
168
  email:
155
169
  - tristan@vibrato.com.au