class HerokuConfig::AwsKey

Constants

MAX_KEYS

Check if there are 2 keys, cannot rotate if there are 2 keys already. Raise error if there are 2 keys. Returns false if not at max limit

Public Class Methods

new(options, access_key_id) click to toggle source
# 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

Public Instance Methods

create_access_key(user_name) click to toggle source

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>>
# 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) click to toggle source
# 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.access_key_metadata
  # 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) click to toggle source
# 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
rotate() click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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

Private Instance Methods

check_max_keys_limit!(user_name) click to toggle source
# File lib/heroku_config/aws_key.rb, line 118
def check_max_keys_limit!(user_name)
  resp = iam.list_access_keys(user_name: user_name)
  return false if resp.access_key_metadata.size < MAX_KEYS # not at max limit

  puts "ERROR: There are already 2 access keys for user: #{user_name.color(:green)}".color(:red)
  raise MaxKeysError
end