Skip to content

Commit b8add7e

Browse files
committed
Add awareness of quota, and exit
1 parent 5ee9253 commit b8add7e

File tree

3 files changed

+112
-14
lines changed

3 files changed

+112
-14
lines changed

lib/aws_rotate_keys/cli.rb

+29-4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@ def initialize(iam: Aws::IAM::Client.new,
2222
end
2323

2424
def call
25+
log "Reading key quota..."
26+
quota = access_key_quota
27+
28+
log "Reading existing keys..."
29+
access_keys = aws_access_keys
30+
31+
if quota <= access_keys.size
32+
log "Key set is already at quota limit of #{quota}:"
33+
log_keylist(access_keys)
34+
raise "You must manually delete a key or use one of the command-line overrides"
35+
end
36+
end
37+
2538
log "Creating access key..."
2639
new_key = create_access_key
2740

@@ -34,7 +47,7 @@ def call
3447
write_aws_credentials_file(new_key)
3548

3649
log "Deleting your oldest access key..."
37-
delete_oldest_access_key
50+
delete_oldest_access_key(access_keys)
3851

3952
log aws_environment_variables_warning_message if aws_environment_variables?
4053

@@ -63,14 +76,26 @@ def write_aws_credentials_file(access_key)
6376
end
6477
end
6578

66-
def delete_oldest_access_key
79+
def access_key_quota
80+
ret = @iam.get_account_summary.summary_map["AccessKeysPerUserQuota"]
81+
end
82+
83+
def aws_access_keys
6784
list_access_keys_response = iam.list_access_keys
68-
access_keys = list_access_keys_response.access_key_metadata
85+
list_access_keys_response.access_key_metadata
86+
end
6987

70-
oldest_access_key = access_keys.sort_by(&:create_date).first
88+
def delete_oldest_access_key(access_key_list)
89+
oldest_access_key = access_key_list.min_by(&:create_date)
7190
iam.delete_access_key(access_key_id: oldest_access_key.access_key_id)
7291
end
7392

93+
def log_keylist(access_keys)
94+
access_keys.each do |k|
95+
log " #{k['create_date']} #{k['access_key_id']} #{k['status']}"
96+
end
97+
end
98+
7499
def log(msg)
75100
stdout.puts msg
76101
end

spec/aws_rotate_keys_spec.rb

+23-10
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,21 @@
77
NEW_SECRET = "SECRET123".freeze
88

99
class IAMDouble
10+
def initialize
11+
@keys = [
12+
Aws::IAM::Types::AccessKeyMetadata.new(
13+
access_key_id: OLD_KEY_ID,
14+
create_date: Time.new(2017, 1, 1)
15+
)
16+
]
17+
end
18+
1019
def create_access_key
20+
@keys << Aws::IAM::Types::AccessKeyMetadata.new(
21+
access_key_id: NEW_KEY_ID,
22+
create_date: Time.new(2017, 2, 1)
23+
)
24+
1125
Aws::IAM::Types::CreateAccessKeyResponse.new(
1226
access_key: Aws::IAM::Types::AccessKey.new(
1327
access_key_id: NEW_KEY_ID,
@@ -18,16 +32,15 @@ def create_access_key
1832

1933
def list_access_keys
2034
Aws::IAM::Types::ListAccessKeysResponse.new(
21-
access_key_metadata: [
22-
Aws::IAM::Types::AccessKeyMetadata.new(
23-
access_key_id: NEW_KEY_ID,
24-
create_date: Time.new(2017, 2, 1)
25-
),
26-
Aws::IAM::Types::AccessKeyMetadata.new(
27-
access_key_id: OLD_KEY_ID,
28-
create_date: Time.new(2017, 1, 1)
29-
)
30-
]
35+
access_key_metadata: @keys
36+
)
37+
end
38+
39+
def get_account_summary
40+
Aws::IAM::Types::GetAccountSummaryResponse.new(
41+
summary_map: {
42+
"AccessKeysPerUserQuota" => 2
43+
}
3144
)
3245
end
3346

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
require "spec_helper"
2+
require "myio"
3+
4+
describe AwsRotateKeys do
5+
ACTIVE_KEY_ID = "ACTKEEEY".freeze
6+
INACTIVE_KEY_ID = "INACTKEY".freeze
7+
8+
class IAMAnotherDouble
9+
def initialize
10+
@keys = [
11+
Aws::IAM::Types::AccessKeyMetadata.new(
12+
access_key_id: INACTIVE_KEY_ID,
13+
status: "Inactive",
14+
create_date: Time.new(2017, 1, 1)
15+
),
16+
Aws::IAM::Types::AccessKeyMetadata.new(
17+
access_key_id: ACTIVE_KEY_ID,
18+
status: "Active",
19+
create_date: Time.new(2017, 2, 1)
20+
)
21+
]
22+
end
23+
24+
def list_access_keys
25+
Aws::IAM::Types::ListAccessKeysResponse.new(
26+
access_key_metadata: @keys
27+
)
28+
end
29+
30+
def get_account_summary
31+
Aws::IAM::Types::GetAccountSummaryResponse.new(
32+
summary_map: {
33+
"AccessKeysPerUserQuota" => 2
34+
}
35+
)
36+
end
37+
end
38+
39+
let(:iam_double) { IAMAnotherDouble.new }
40+
let(:credentials_path) { "./spec/tmp/aws/credentials" }
41+
42+
def rotate_keys(args = {})
43+
AwsRotateKeys::CLI.call(
44+
{
45+
iam: iam_double,
46+
credentials_path: credentials_path
47+
}.merge(args)
48+
)
49+
end
50+
51+
context "when at quota and no override" do
52+
it "raises an error" do
53+
stdout = MyIO.new
54+
expected_err = "You must manually delete a key or use one of the command-line overrides"
55+
expect { rotate_keys(stdout: stdout) }.to raise_error(RuntimeError, expected_err)
56+
expect(stdout.to_s).to include "Key set is already at quota limit"
57+
end
58+
end
59+
60+
end

0 commit comments

Comments
 (0)