lib/ruby_conversations/aws_credential_provider.rb



# frozen_string_literal: true

require 'aws-sdk-core'
require_relative 'errors'

module RubyConversations
  # Manages AWS credentials with automatic refresh capability
  class AwsCredentialProvider
    class << self
      def instance
        @instance ||= new
      end
    end

    def initialize
      refresh_credentials!
    end

    def access_key_id
      refresh_if_expired!
      @credentials&.access_key_id
    end

    def secret_access_key
      refresh_if_expired!
      @credentials&.secret_access_key
    end

    def session_token
      refresh_if_expired!
      @credentials&.session_token
    end

    attr_reader :expiration

    def refresh_credentials!
      fetch_and_set_credentials
    end

    def refresh_if_expired!
      return unless expired?

      refresh_credentials!
    end

    private

    def fetch_and_set_credentials
      if use_mock_credentials?
        set_mock_credentials
      else
        fetch_and_set_real_credentials
      end
    end

    def use_mock_credentials?
      ENV['RAILS_ENV'] == 'test'
    end

    def set_mock_credentials
      @credentials = Aws::Credentials.new(
        'mock_access_key_id',
        'mock_secret_access_key',
        'mock_session_token'
      )
      @expiration = Time.now + 3600 # 1 hour
    end

    def fetch_and_set_real_credentials
      ecs_credentials = Aws::CredentialProviderChain.new.resolve
      raise ConfigurationError, 'Could not resolve AWS credentials' if ecs_credentials.nil?

      refresh_if_supported(ecs_credentials)
      assign_credentials(ecs_credentials)
    end

    def refresh_if_supported(credentials)
      credentials.refresh! if credentials.respond_to?(:refresh!)
    end

    def assign_credentials(ecs_credentials)
      if ecs_credentials.respond_to?(:credentials)
        @credentials = ecs_credentials.credentials
        @expiration = ecs_credentials.expiration if ecs_credentials.respond_to?(:expiration)
      else
        @credentials = ecs_credentials
        @expiration = nil
      end
    end

    def expired?
      return false if @expiration.nil?
      return true if @credentials.nil?

      # Refresh if we're within 5 minutes of expiration
      @expiration < Time.now + 300
    end
  end
end