Skip to content

[COST-7476] Fall back to credential-based auth when storage account key access is disabled#6062

Open
bacciotti wants to merge 6 commits into
mainfrom
COST-7476-key-auth-fallback
Open

[COST-7476] Fall back to credential-based auth when storage account key access is disabled#6062
bacciotti wants to merge 6 commits into
mainfrom
COST-7476-key-auth-fallback

Conversation

@bacciotti
Copy link
Copy Markdown
Contributor

Jira Ticket

COST-7476

Description

This change fixes a silent failure in AzureClientFactory.blob_service_client when a storage account has "Allow storage account key access" disabled at the storage account level.

Root cause

The existing fallback to credential-based auth only triggers when listKeys fails (i.e. the service principal lacks Microsoft.Storage/storageAccounts/listKeys/action). However, if the service principal has Storage Account Contributor, listKeys succeeds and returns keys — but any attempt to use those keys is rejected by the storage account with KeyBasedAuthenticationNotPermitted. The fallback never kicks in, and the integration silently fails with a 403 during blob listing.

This means that having more permission (Storage Account Contributor) paradoxically breaks the integration when key access is disabled, because the code never reaches the token-based fallback.

Fix

After creating the key-based BlobServiceClient, immediately call get_account_information() to validate that key-based authentication is actually permitted. If it raises HttpResponseError with KeyBasedAuthenticationNotPermitted, the code falls back to the credential-based BlobServiceClient — the same path already used when listKeys is denied.

Also replaces httpError.message (which can be None) with str(httpError) for safer string matching.

Testing

  1. Checkout branch

  2. Run the Azure client tests:

    pipenv run tox -- providers.test.azure.test_client
    
    • test_blob_service_client_key_auth_not_permitted — new test covering the fixed scenario
    • test_blob_service_client_none_message — new test covering the None message edge case
    • All existing tests should continue to pass
  3. To verify end-to-end: configure an Azure integration where the storage account has "Allow storage account key access" disabled and the service principal has Storage Account Contributor + Storage Blob Data Reader. Data should now ingest successfully without needing to remove Storage Account Contributor.

Release Notes

  • proposed release note
* [COST-7476](https://redhat.atlassian.net/browse/COST-7476) Fix Azure integration failure when storage account key access is disabled but service principal has Storage Account Contributor role

Made with Cursor

@github-actions github-actions Bot added the smokes-required Label to show that smokes tests should be run against these changes. label May 14, 2026
…disabled

When a storage account has 'Allow storage account key access' disabled,
listKeys succeeds but any subsequent use of the key is rejected with
KeyBasedAuthenticationNotPermitted. The previous fallback only triggered
when listKeys itself failed, leaving integrations with Storage Account
Contributor broken silently.

This change validates the key-based client immediately after creation via
get_account_information(). If key-based auth is rejected, the code falls
back to credential-based BlobServiceClient — the same path used when
listKeys is denied outright.

Also replaces httpError.message with str(httpError) to safely handle
cases where the message attribute is None.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the Azure provider's client to support fallback to credential-based authentication when key-based authentication is disabled on a storage account. It also improves error handling by using the string representation of HttpResponseError to avoid potential TypeError issues and includes new unit tests for these scenarios. Feedback suggests avoiding hardcoded endpoint suffixes to support multi-cloud environments and refactoring the exception handler to store the error string in a variable for better readability.

Comment thread koku/providers/azure/client.py
Comment thread koku/providers/azure/client.py
@bacciotti bacciotti force-pushed the COST-7476-key-auth-fallback branch from 0a92937 to 7a933de Compare May 14, 2026 19:12
@bacciotti bacciotti added the azure-smoke-tests pr_check will run azure + ocp on azure smoke tests, used when changes affect azure only. label May 14, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>
@bacciotti bacciotti marked this pull request as ready for review May 14, 2026 19:16
@bacciotti bacciotti requested review from a team as code owners May 14, 2026 19:16
bacciotti and others added 2 commits May 15, 2026 09:12
@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.4%. Comparing base (ed4c0b1) to head (4a1a75c).

Additional details and impacted files
@@          Coverage Diff          @@
##            main   #6062   +/-   ##
=====================================
  Coverage   94.4%   94.4%           
=====================================
  Files        362     362           
  Lines      32101   32105    +4     
  Branches    3538    3538           
=====================================
+ Hits       30292   30296    +4     
  Misses      1175    1175           
  Partials     634     634           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@lcouzens lcouzens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a couple of clarifying questions

except HttpResponseError as httpError:
err = "does not have authorization to perform action 'Microsoft.Storage/storageAccounts/listKeys/action'"
if err not in str(httpError):
error_msg = str(httpError)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we obfuscated these errors before because they would occasionally leak sensitive information into our logs. I could be miss remembering here but we should also check or add in some logic that can recognise sensitive content and obfuscate it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i didnt find any obfuscation pattern... Also the original code already logged httpError.message in the same warning. For these errors we check here the messages don't contain secrets, just ids and metadata.

)
return BlobServiceClient.from_connection_string(connect_str)
client = BlobServiceClient.from_connection_string(connect_str)
client.get_account_information()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my understanding, this is the real line that fixes thingss correct? Essentially this check would cause a error to be raised for the customer and enable the flow to fall back to account access keys?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct, without it, the error only surfaces later during blob listing, outside where the fallback can trigger

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

azure-smoke-tests pr_check will run azure + ocp on azure smoke tests, used when changes affect azure only. smokes-required Label to show that smokes tests should be run against these changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants