diff --git a/prowler/providers/m365/lib/powershell/m365_powershell.py b/prowler/providers/m365/lib/powershell/m365_powershell.py index 3ba0dd7320..49326cd38b 100644 --- a/prowler/providers/m365/lib/powershell/m365_powershell.py +++ b/prowler/providers/m365/lib/powershell/m365_powershell.py @@ -107,18 +107,22 @@ class M365PowerShell(PowerShellSession): scopes=["https://graph.microsoft.com/.default"], ) - if "access_token" in result: - # Validate user credentials belong to tenant - user_domain = credentials.user.split("@")[1] - if not credentials.provider_id.endswith(user_domain): - raise M365UserNotBelongingToTenantError( - file=os.path.basename(__file__), - message="The provided M365 User does not belong to the specified tenant.", - ) - return True - else: + if result is None: return False + if "access_token" not in result: + return False + + # Validate user credentials belong to tenant + user_domain = credentials.user.split("@")[1] + if not credentials.provider_id.endswith(user_domain): + raise M365UserNotBelongingToTenantError( + file=os.path.basename(__file__), + message="The provided M365 User does not belong to the specified tenant.", + ) + + return True + def connect_microsoft_teams(self) -> dict: """ Connect to Microsoft Teams Module PowerShell Module. diff --git a/tests/providers/m365/lib/powershell/m365_powershell_test.py b/tests/providers/m365/lib/powershell/m365_powershell_test.py index f1e577f35f..56cc28ef33 100644 --- a/tests/providers/m365/lib/powershell/m365_powershell_test.py +++ b/tests/providers/m365/lib/powershell/m365_powershell_test.py @@ -172,6 +172,43 @@ class Testm365PowerShell: ) session.close() + @patch("subprocess.Popen") + @patch("msal.ConfidentialClientApplication") + def test_test_credentials_auth_failure(self, mock_msal, mock_popen): + mock_process = MagicMock() + mock_popen.return_value = mock_process + mock_msal_instance = MagicMock() + mock_msal.return_value = mock_msal_instance + mock_msal_instance.acquire_token_by_username_password.return_value = None + + credentials = M365Credentials( + user="test@contoso.onmicrosoft.com", + passwd="test_password", + client_id="test_client_id", + client_secret="test_client_secret", + tenant_id="test_tenant_id", + provider_id="contoso.onmicrosoft.com", + ) + session = M365PowerShell(credentials) + + session.execute = MagicMock() + session.process.stdin.write = MagicMock() + session.read_output = MagicMock(return_value="decrypted_password") + + assert session.test_credentials(credentials) is False + + mock_msal.assert_called_once_with( + client_id="test_client_id", + client_credential="test_client_secret", + authority="https://login.microsoftonline.com/test_tenant_id", + ) + mock_msal_instance.acquire_token_by_username_password.assert_called_once_with( + username="test@contoso.onmicrosoft.com", + password="decrypted_password", + scopes=["https://graph.microsoft.com/.default"], + ) + session.close() + @patch("subprocess.Popen") def test_remove_ansi(self, mock_popen): credentials = M365Credentials(user="test@example.com", passwd="test_password") diff --git a/tests/providers/m365/m365_provider_test.py b/tests/providers/m365/m365_provider_test.py index 67d67fa4aa..80222441ed 100644 --- a/tests/providers/m365/m365_provider_test.py +++ b/tests/providers/m365/m365_provider_test.py @@ -21,7 +21,10 @@ from prowler.providers.m365.exceptions.exceptions import ( M365HTTPResponseError, M365MissingEnvironmentCredentialsError, M365NoAuthenticationMethodError, + M365NotValidClientIdError, + M365NotValidClientSecretError, M365NotValidEncryptedPasswordError, + M365NotValidTenantIdError, M365NotValidUserError, M365UserNotBelongingToTenantError, ) @@ -504,3 +507,78 @@ class TestM365Provider: "The provided M365 User does not belong to the specified tenant." in str(exception.value) ) + + def test_validate_static_credentials_invalid_tenant_id(self): + with pytest.raises(M365NotValidTenantIdError) as exception: + M365Provider.validate_static_credentials( + tenant_id="invalid-tenant-id", + client_id="12345678-1234-5678-1234-567812345678", + client_secret="test_secret", + user="test@example.com", + encrypted_password="test_password", + ) + assert "The provided M365 Tenant ID is not valid." in str(exception.value) + + def test_validate_static_credentials_missing_client_id(self): + with pytest.raises(M365NotValidClientIdError) as exception: + M365Provider.validate_static_credentials( + tenant_id="12345678-1234-5678-1234-567812345678", + client_id="", + client_secret="test_secret", + user="test@example.com", + encrypted_password="test_password", + ) + assert "The provided M365 Client ID is not valid." in str(exception.value) + + def test_validate_static_credentials_missing_client_secret(self): + with pytest.raises(M365NotValidClientSecretError) as exception: + M365Provider.validate_static_credentials( + tenant_id="12345678-1234-5678-1234-567812345678", + client_id="12345678-1234-5678-1234-567812345678", + client_secret="", + user="test@example.com", + encrypted_password="test_password", + ) + assert "The provided M365 Client Secret is not valid." in str(exception.value) + + def test_validate_static_credentials_missing_user(self): + with pytest.raises(M365NotValidUserError) as exception: + M365Provider.validate_static_credentials( + tenant_id="12345678-1234-5678-1234-567812345678", + client_id="12345678-1234-5678-1234-567812345678", + client_secret="test_secret", + user="", + encrypted_password="test_password", + ) + assert "The provided M365 User is not valid." in str(exception.value) + + def test_validate_static_credentials_missing_encrypted_password(self): + with pytest.raises(M365NotValidEncryptedPasswordError) as exception: + M365Provider.validate_static_credentials( + tenant_id="12345678-1234-5678-1234-567812345678", + client_id="12345678-1234-5678-1234-567812345678", + client_secret="test_secret", + user="test@example.com", + encrypted_password="", + ) + assert "The provided M365 Encrypted Password is not valid." in str( + exception.value + ) + + def test_validate_arguments_missing_env_credentials(self): + with pytest.raises(M365MissingEnvironmentCredentialsError) as exception: + M365Provider.validate_arguments( + az_cli_auth=False, + sp_env_auth=False, + env_auth=True, + browser_auth=False, + tenant_id=None, + client_id="test_client_id", + client_secret="test_secret", + user=None, + encrypted_password=None, + ) + assert ( + "M365 provider requires AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID, M365_USER and M365_ENCRYPTED_PASSWORD environment variables to be set when using --env-auth" + in str(exception.value) + )