Azure / azure-storage-ruby

Microsoft Azure Storage Library for Ruby
http://azure.github.io/azure-storage-ruby/
82 stars 158 forks source link

How to use Azure Managed Identity for authentication and authorization for Ruby On Rails Active Storage? #233

Open avivansh opened 8 months ago

avivansh commented 8 months ago

Current setup

azure:
  service: AzureStorage
  storage_account_name: <%= VaultService.get_secret("AZURE-STORAGE::AZURE-STORAGE-ACCOUNT-NAME") %>
  storage_access_key: <%= VaultService.get_secret("AZURE-STORAGE::AZURE-STORAGE-ACCOUNT-ACCESS-KEY") %>
  container: <%= VaultService.get_secret('AZURE-ATTACHMENTS-BUCKET-NAME') %>

Given the implementation of accessing Azure Storage using Access Token. Link

require "azure/storage/common"

access_token = <your initial access token>

# Creating an instance of `Azure::Storage::Common::Core::TokenCredential`
token_credential = Azure::Storage::Common::Core::TokenCredential.new access_token
token_signer = Azure::Storage::Common::Core::Auth::TokenSigner.new token_credential
blob_token_client = Azure::Storage::Blob::BlobService.new(storage_account_name: <your_account_name>, signer: token_signer)

Given the implementation of Active Storage for Ruby on Rails. It uses azure-storage-blob gem under the hood. link

    def initialize(storage_account_name:, storage_access_key:, container:, public: false, **options)
      @client = Azure::Storage::Blob::BlobService.create(storage_account_name: storage_account_name, storage_access_key: storage_access_key, **options)
      @signer = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(storage_account_name, storage_access_key)
      @container = container
      @public = public
    end

New Setup, config/storage.yml

azure:
  service: AzureStorage
  storage_account_name: <%= VaultService.get_secret("AZURE-STORAGE::AZURE-STORAGE-ACCOUNT-NAME") %>
  container: <%= VaultService.get_secret('AZURE-ATTACHMENTS-BUCKET-NAME') %>

Monkey patched to use the above information to use active storage using managed identity

module ActiveStorage
  class Service::AzureStorageService < Service
    def initialize(storage_account_name:, container:, public: false, **options)
      access_token = AzureAd::ManagedIdentityTokenProvider.new('https://storage.azure.com/', client_id: ENV['AKS_MANAGED_IDENTITY_ID']).get_authentication_header.split(' ').last
      # Creating an instance of `Azure::Storage::Common::Core::TokenCredential`
      token_credential = ::Azure::Storage::Common::Core::TokenCredential.new access_token
      token_signer = ::Azure::Storage::Common::Core::Auth::TokenSigner.new token_credential
      @client = Azure::Storage::Blob::BlobService.create(storage_account_name: storage_account_name, signer: token_signer, **options)
      user_delegation_key = @client.get_user_delegation_key(DateTime.now - 1.minute, DateTime.now + 6.day + 23.hours)
      @signer = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(storage_account_name: storage_account_name, user_delegation_key: user_delegation_key )
      @container = container
      @public = public
    rescue StandardError => e
      raise e unless Rake.respond_to?(:application) && (!Rake.application.top_level_tasks.exclude?('assets:precompile') || !Rake.application.top_level_tasks.exclude?('source_map:upload_source_map'))
    end
  end
end

Is this approach correct? (I am yet to test this.). Also, is there any other approach on how to achieve this? I have posted the question on stackoverflow as well.