mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
feat(azure): add provider id validation inside test_connection (#5391)
Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
This commit is contained in:
@@ -32,6 +32,7 @@ from prowler.providers.azure.exceptions.exceptions import (
|
||||
AzureGetTokenIdentityError,
|
||||
AzureHTTPResponseError,
|
||||
AzureInteractiveBrowserCredentialError,
|
||||
AzureInvalidAccountCredentialsError,
|
||||
AzureNoAuthenticationMethodError,
|
||||
AzureNoSubscriptionsError,
|
||||
AzureNotTenantIdButClientIdAndClienSecret,
|
||||
@@ -539,6 +540,7 @@ class AzureProvider(Provider):
|
||||
raise_on_exception=True,
|
||||
client_id=None,
|
||||
client_secret=None,
|
||||
provider_id=None,
|
||||
) -> Connection:
|
||||
"""Test connection to Azure subscription.
|
||||
|
||||
@@ -554,6 +556,7 @@ class AzureProvider(Provider):
|
||||
raise_on_exception (bool): Flag indicating whether to raise an exception if the connection fails.
|
||||
client_id (str): The Azure client ID.
|
||||
client_secret (str): The Azure client secret.
|
||||
provider_id (str): The provider ID, in this case it's the Azure subscription ID.
|
||||
|
||||
Returns:
|
||||
bool: True if the connection is successful, False otherwise.
|
||||
@@ -610,10 +613,20 @@ class AzureProvider(Provider):
|
||||
# Create a SubscriptionClient
|
||||
subscription_client = SubscriptionClient(credentials)
|
||||
|
||||
# Get info from the first subscription
|
||||
subscription = next(subscription_client.subscriptions.list())
|
||||
# Get info from the subscriptions
|
||||
available_subscriptions = []
|
||||
for subscription in subscription_client.subscriptions.list():
|
||||
available_subscriptions.append(subscription)
|
||||
|
||||
logger.info(f"Connected to Azure subscription: {subscription.display_name}")
|
||||
if provider_id and provider_id not in [
|
||||
sub.subscription_id for sub in available_subscriptions
|
||||
]:
|
||||
raise AzureInvalidAccountCredentialsError(
|
||||
file=os.path.basename(__file__),
|
||||
message="The provided credentials are not valid for the specified Azure subscription.",
|
||||
)
|
||||
|
||||
logger.info("Azure provider: Connection to Azure subscription successful")
|
||||
|
||||
return Connection(is_connected=True)
|
||||
# Exceptions from validate_arguments
|
||||
@@ -697,6 +710,12 @@ class AzureProvider(Provider):
|
||||
if raise_on_exception:
|
||||
raise client_secret_error
|
||||
return Connection(error=client_secret_error)
|
||||
# Exceptions from provider_id validation
|
||||
except AzureInvalidAccountCredentialsError as invalid_credentials_error:
|
||||
logger.error(str(invalid_credentials_error))
|
||||
if raise_on_exception:
|
||||
raise invalid_credentials_error
|
||||
return Connection(error=invalid_credentials_error)
|
||||
# Exceptions from SubscriptionClient
|
||||
except HttpResponseError as http_response_error:
|
||||
logger.error(
|
||||
|
||||
@@ -97,6 +97,10 @@ class AzureBaseException(ProwlerException):
|
||||
"message": "The provided tenant ID and client ID do not belong to the provided client secret",
|
||||
"remediation": "Check the tenant ID and client ID and ensure they belong to the provided client secret.",
|
||||
},
|
||||
(1937, "AzureInvalidAccountCredentialsError"): {
|
||||
"message": "The provided provider_id does not match with the available subscriptions",
|
||||
"remediation": "Check the provider_id and ensure it is a valid subscription for the given credentials.",
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, code, file=None, original_exception=None, message=None):
|
||||
@@ -279,3 +283,10 @@ class AzureTenantIdAndClientIdNotBelongingToClientSecretError(AzureCredentialsEr
|
||||
super().__init__(
|
||||
1936, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class AzureInvalidAccountCredentialsError(AzureBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
1937, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@ from prowler.providers.azure.azure_provider import AzureProvider
|
||||
from prowler.providers.azure.exceptions.exceptions import (
|
||||
AzureBrowserAuthNoTenantIDError,
|
||||
AzureHTTPResponseError,
|
||||
AzureInvalidAccountCredentialsError,
|
||||
AzureNoAuthenticationMethodError,
|
||||
AzureTenantIDNoBrowserAuthError,
|
||||
)
|
||||
@@ -280,6 +281,103 @@ class TestAzureProvider:
|
||||
assert test_connection.is_connected
|
||||
assert test_connection.error is None
|
||||
|
||||
def test_test_connection_provider_validation(self):
|
||||
with patch(
|
||||
"prowler.providers.azure.azure_provider.DefaultAzureCredential"
|
||||
) as mock_default_credential, patch(
|
||||
"prowler.providers.azure.azure_provider.AzureProvider.setup_session"
|
||||
) as mock_setup_session, patch(
|
||||
"prowler.providers.azure.azure_provider.SubscriptionClient"
|
||||
) as mock_resource_client, patch(
|
||||
"prowler.providers.azure.azure_provider.AzureProvider.validate_static_credentials"
|
||||
) as mock_validate_static_credentials:
|
||||
|
||||
# Mock the return value of DefaultAzureCredential
|
||||
mock_default_credential.return_value = {
|
||||
"client_id": str(uuid4()),
|
||||
"client_secret": str(uuid4()),
|
||||
"tenant_id": str(uuid4()),
|
||||
}
|
||||
|
||||
# Mock setup_session to return a mocked session object
|
||||
mock_session = MagicMock()
|
||||
mock_setup_session.return_value = mock_session
|
||||
|
||||
# Mock ValidateStaticCredentials to avoid real API calls
|
||||
mock_validate_static_credentials.return_value = None
|
||||
|
||||
# Mock ResourceManagementClient to avoid real API calls
|
||||
mock_subscription = MagicMock()
|
||||
mock_subscription.subscription_id = "test_provider_id"
|
||||
mock_return_value = MagicMock()
|
||||
mock_return_value.subscriptions.list.return_value = [mock_subscription]
|
||||
mock_resource_client.return_value = mock_return_value
|
||||
|
||||
test_connection = AzureProvider.test_connection(
|
||||
browser_auth=False,
|
||||
tenant_id=str(uuid4()),
|
||||
region="AzureCloud",
|
||||
raise_on_exception=False,
|
||||
client_id=str(uuid4()),
|
||||
client_secret=str(uuid4()),
|
||||
provider_id="test_provider_id",
|
||||
)
|
||||
|
||||
assert isinstance(test_connection, Connection)
|
||||
assert test_connection.is_connected
|
||||
assert test_connection.error is None
|
||||
|
||||
def test_test_connection_provider_validation_error(self):
|
||||
with patch(
|
||||
"prowler.providers.azure.azure_provider.DefaultAzureCredential"
|
||||
) as mock_default_credential, patch(
|
||||
"prowler.providers.azure.azure_provider.AzureProvider.setup_session"
|
||||
) as mock_setup_session, patch(
|
||||
"prowler.providers.azure.azure_provider.SubscriptionClient"
|
||||
) as mock_resource_client, patch(
|
||||
"prowler.providers.azure.azure_provider.AzureProvider.validate_static_credentials"
|
||||
) as mock_validate_static_credentials:
|
||||
|
||||
# Mock the return value of DefaultAzureCredential
|
||||
mock_default_credential.return_value = {
|
||||
"client_id": str(uuid4()),
|
||||
"client_secret": str(uuid4()),
|
||||
"tenant_id": str(uuid4()),
|
||||
}
|
||||
|
||||
# Mock setup_session to return a mocked session object
|
||||
mock_session = MagicMock()
|
||||
mock_setup_session.return_value = mock_session
|
||||
|
||||
# Mock ValidateStaticCredentials to avoid real API calls
|
||||
mock_validate_static_credentials.return_value = None
|
||||
|
||||
# Mock ResourceManagementClient to avoid real API calls
|
||||
mock_subscription = MagicMock()
|
||||
mock_subscription.subscription_id = "test_invalid_provider_id"
|
||||
mock_return_value = MagicMock()
|
||||
mock_return_value.subscriptions.list.return_value = [mock_subscription]
|
||||
mock_resource_client.return_value = mock_return_value
|
||||
|
||||
test_connection = AzureProvider.test_connection(
|
||||
browser_auth=False,
|
||||
tenant_id=str(uuid4()),
|
||||
region="AzureCloud",
|
||||
raise_on_exception=False,
|
||||
client_id=str(uuid4()),
|
||||
client_secret=str(uuid4()),
|
||||
provider_id="test_provider_id",
|
||||
)
|
||||
|
||||
assert test_connection.error is not None
|
||||
assert isinstance(
|
||||
test_connection.error, AzureInvalidAccountCredentialsError
|
||||
)
|
||||
assert (
|
||||
"The provided credentials are not valid for the specified Azure subscription."
|
||||
in test_connection.error.args[0]
|
||||
)
|
||||
|
||||
def test_test_connection_with_ClientAuthenticationError(self):
|
||||
with pytest.raises(AzureHTTPResponseError) as exception:
|
||||
tenant_id = str(uuid4())
|
||||
|
||||
Reference in New Issue
Block a user