Class: HerokuConfig::AwsKey

Inherits:
Base
  • Object
show all
Includes:
AwsKeyNameMap, AwsServices
Defined in:
lib/heroku_config/aws_key.rb

Defined Under Namespace

Classes: AccessKeyNotFound, MaxKeysError

Instance Method Summary collapse

Methods included from AwsKeyNameMap

#id_key_name, #secret_key_name

Methods included from AwsServices

#iam, #sts

Methods inherited from Base

#config

Constructor Details

#initialize(options, access_key_id) ⇒ AwsKey

Returns a new instance of AwsKey.



8
9
10
11
# File 'lib/heroku_config/aws_key.rb', line 8

def initialize(options, access_key_id)
  @options, @access_key_id = options, access_key_id
  @app = options[:app]
end

Instance Method Details

#create_access_key(user_name) ⇒ Object

Returns:

#<struct Aws::IAM::Types::AccessKey
  user_name="tung",
  access_key_id="AKIAXZ6ODJLQUU6O3FD2",
  status="Active",
  secret_access_key="8eEnLLdR7gQE9fkFiDVuemi3qPf3mBMXxEXAMPLE",
  create_date=2019-08-13 21:14:35 UTC>>


103
104
105
106
107
108
109
110
111
# File 'lib/heroku_config/aws_key.rb', line 103

def create_access_key(user_name)
  resp = iam.create_access_key(
    user_name: user_name,
  )
  access_key = resp.access_key
  key, secret = access_key.access_key_id, access_key.secret_access_key
  puts "Created new access key: #{key}"
  [key, secret]
end

#delete_old_access_key(user_name) ⇒ Object



74
75
76
77
78
79
80
81
82
83
# File 'lib/heroku_config/aws_key.rb', line 74

def delete_old_access_key(user_name)
  resp = iam.list_access_keys(user_name: user_name)
  access_keys = resp.
  # Important: Only delete if there are keys 2.
  return if access_keys.size <= 1

  old_key = access_keys.sort_by(&:create_date).first
  iam.delete_access_key(user_name: user_name, access_key_id: old_key.access_key_id)
  puts "Old access key deleted: #{old_key.access_key_id}"
end

#get_user_name(quiet_error: true) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/heroku_config/aws_key.rb', line 31

def get_user_name(quiet_error: true)
  return "fakeuser" if @options[:noop]

  begin
    resp = iam.get_access_key_last_used(
      access_key_id: @access_key_id,
    )
    resp.user_name
  rescue Aws::IAM::Errors::AccessDenied => e # "obscure" error if access key is not found also
    puts "#{e.class} #{e.message}".color(:red)
    puts <<~EOL
      Are you sure the access key exists?
      You can try running the following with an admin user to see if the key exists:

          aws iam get-access-key-last-used --access-key-id #{@access_key_id}

    EOL
    @options[:cli] ? exit(1) : raise(AccessKeyNotFound)
  end
end

#rotateObject



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/heroku_config/aws_key.rb', line 13

def rotate
  user_name = get_user_name

  message = "Updating access key for user: #{user_name}"
  message = "NOOP: #{message}" if ENV['HEROKU_CONFIG_TEST']
  puts message.color(:green)
  return false if @options[:noop]

  check_max_keys_limit!(user_name)
  new_key, new_secret = create_access_key(user_name)
  wait_until_usable(new_key, new_secret)

  update_heroku_config(new_key, new_secret)
  delete_old_access_key(user_name)

  true
end

#update_heroku_config(new_key, new_secret) ⇒ Object



85
86
87
88
89
90
91
92
# File 'lib/heroku_config/aws_key.rb', line 85

def update_heroku_config(new_key, new_secret)
  out = config.set(
    id_key_name => new_key,
    secret_key_name => new_secret,
  )
  puts "Setting heroku config variables"
  puts out
end

#wait_until_usable(key, secret) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/heroku_config/aws_key.rb', line 52

def wait_until_usable(key, secret)
  puts "Checking if new AWS key is usable yet."
  delay, retries = 5, 0
  sts = Aws::STS::Client.new(
    access_key_id: key,
    secret_access_key: secret,
  )
  begin
    sts.get_caller_identity
    puts "Confirmed that new AWS key is usable."
    true
  rescue Aws::STS::Errors::InvalidClientTokenId => e
    puts "#{e.class}: #{e.message}"
    retries += 1
    if retries <= 20
      puts "New IAM key not usable yet. Delaying for #{delay} seconds and retrying..."
      sleep delay
      retry
    end
  end
end