From d67a2344e3310035d050f7e4ac7f7bc941cc88e6 Mon Sep 17 00:00:00 2001 From: Sergio Garcia Date: Tue, 10 Dec 2024 11:06:48 -0400 Subject: [PATCH] fix(gcp): make sure default project is active (#6097) (cherry picked from commit 48946fa4f7ce414dddcb1a4dbe039365a4cd2a9b) --- prowler/providers/gcp/gcp_provider.py | 6 +- tests/providers/gcp/gcp_provider_test.py | 423 +++++++++++++++-------- 2 files changed, 282 insertions(+), 147 deletions(-) diff --git a/prowler/providers/gcp/gcp_provider.py b/prowler/providers/gcp/gcp_provider.py index 1365d3586d0..4fff9652121 100644 --- a/prowler/providers/gcp/gcp_provider.py +++ b/prowler/providers/gcp/gcp_provider.py @@ -181,8 +181,6 @@ def __init__( message="No Project IDs can be accessed via Google Credentials.", ) if project_ids: - if self._default_project_id not in project_ids: - self._default_project_id = project_ids[0] for input_project in project_ids: for ( accessible_project_id, @@ -203,6 +201,10 @@ def __init__( self._projects[project_id] = project self._project_ids.append(project_id) + # Change default project if not in active projects + if self._project_ids and self._default_project_id not in self._project_ids: + self._default_project_id = self._project_ids[0] + # Remove excluded projects if any input if excluded_project_ids: for excluded_project in excluded_project_ids: diff --git a/tests/providers/gcp/gcp_provider_test.py b/tests/providers/gcp/gcp_provider_test.py index 9c748219358..9916a6902bc 100644 --- a/tests/providers/gcp/gcp_provider_test.py +++ b/tests/providers/gcp/gcp_provider_test.py @@ -51,18 +51,23 @@ def test_gcp_provider(self): execute=MagicMock(return_value={"projects": projects}) ) - with patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.setup_session", - return_value=(None, "test-project"), - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", - return_value=projects, - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", - return_value=None, - ), patch( - "prowler.providers.gcp.gcp_provider.discovery.build", - return_value=mocked_service, + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.setup_session", + return_value=(None, "test-project"), + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", + return_value=projects, + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", + return_value=None, + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), ): gcp_provider = GcpProvider( project_id, @@ -119,18 +124,23 @@ def test_is_project_matching(self): mocked_service.projects.list.return_value = MagicMock( execute=MagicMock(return_value={"projects": projects}) ) - with patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.setup_session", - return_value=(None, None), - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", - return_value=projects, - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", - return_value=None, - ), patch( - "prowler.providers.gcp.gcp_provider.discovery.build", - return_value=mocked_service, + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.setup_session", + return_value=(None, None), + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", + return_value=projects, + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", + return_value=None, + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), ): gcp_provider = GcpProvider( arguments.organization_id, @@ -193,21 +203,27 @@ def test_setup_session_with_credentials_file_no_impersonate(self): mocked_service.projects.list.return_value = MagicMock( execute=MagicMock(return_value={"projects": projects}) ) - with patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", - return_value=projects, - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", - return_value=None, - ), patch( - "os.path.abspath", - return_value="test_credentials_file", - ), patch( - "prowler.providers.gcp.gcp_provider.default", - return_value=(mocked_credentials, MagicMock()), - ), patch( - "prowler.providers.gcp.gcp_provider.discovery.build", - return_value=mocked_service, + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", + return_value=projects, + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", + return_value=None, + ), + patch( + "os.path.abspath", + return_value="test_credentials_file", + ), + patch( + "prowler.providers.gcp.gcp_provider.default", + return_value=(mocked_credentials, MagicMock()), + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), ): gcp_provider = GcpProvider( arguments.organization_id, @@ -257,21 +273,27 @@ def test_setup_session_with_credentials_file_and_impersonate(self): mocked_service.projects.list.return_value = MagicMock( execute=MagicMock(return_value={"projects": projects}) ) - with patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", - return_value=projects, - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", - return_value=None, - ), patch( - "os.path.abspath", - return_value="test_credentials_file", - ), patch( - "prowler.providers.gcp.gcp_provider.default", - return_value=(mocked_credentials, MagicMock()), - ), patch( - "prowler.providers.gcp.gcp_provider.discovery.build", - return_value=mocked_service, + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", + return_value=projects, + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", + return_value=None, + ), + patch( + "os.path.abspath", + return_value="test_credentials_file", + ), + patch( + "prowler.providers.gcp.gcp_provider.default", + return_value=(mocked_credentials, MagicMock()), + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), ): gcp_provider = GcpProvider( arguments.organization_id, @@ -334,21 +356,27 @@ def test_setup_session_with_organization_id(self): mocked_service.projects.list.return_value = MagicMock( execute=MagicMock(return_value={"projects": projects}) ) - with patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", - return_value=projects, - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", - return_value=None, - ), patch( - "os.path.abspath", - return_value="test_credentials_file", - ), patch( - "prowler.providers.gcp.gcp_provider.default", - return_value=(mocked_credentials, MagicMock()), - ), patch( - "prowler.providers.gcp.gcp_provider.discovery.build", - return_value=mocked_service, + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", + return_value=projects, + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", + return_value=None, + ), + patch( + "os.path.abspath", + return_value="test_credentials_file", + ), + patch( + "prowler.providers.gcp.gcp_provider.default", + return_value=(mocked_credentials, MagicMock()), + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), ): gcp_provider = GcpProvider( arguments.organization_id, @@ -401,21 +429,27 @@ def test_setup_session_with_inactive_project(self): mocked_service.projects.list.return_value = MagicMock( execute=MagicMock(return_value={"projects": projects}) ) - with patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", - return_value=projects, - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", - return_value=None, - ), patch( - "os.path.abspath", - return_value="test_credentials_file", - ), patch( - "prowler.providers.gcp.gcp_provider.default", - return_value=(mocked_credentials, MagicMock()), - ), patch( - "prowler.providers.gcp.gcp_provider.discovery.build", - return_value=mocked_service, + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", + return_value=projects, + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", + return_value=None, + ), + patch( + "os.path.abspath", + return_value="test_credentials_file", + ), + patch( + "prowler.providers.gcp.gcp_provider.default", + return_value=(mocked_credentials, MagicMock()), + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), ): with pytest.raises(Exception) as e: GcpProvider( @@ -433,6 +467,81 @@ def test_setup_session_with_inactive_project(self): ) assert e.type == GCPNoAccesibleProjectsError + def test_setup_session_with_inactive_default_project(self): + mocked_credentials = MagicMock() + + mocked_credentials.refresh.return_value = None + mocked_credentials._service_account_email = "test-service-account-email" + + arguments = Namespace() + arguments.project_id = ["default_project", "active_project"] + arguments.excluded_project_id = [] + arguments.organization_id = None + arguments.list_project_id = False + arguments.credentials_file = "test_credentials_file" + arguments.impersonate_service_account = "" + arguments.config_file = default_config_file_path + arguments.fixer_config = default_fixer_config_file_path + + projects = { + "default_project": GCPProject( + number="55555555", + id="default_project", + name="default_project", + labels={"test": "value"}, + lifecycle_state="DELETE_REQUESTED", + ), + "active_project": GCPProject( + number="12345678", + id="active_project", + name="active_project", + labels={"test": "value"}, + lifecycle_state="ACTIVE", + ), + } + + mocked_service = MagicMock() + + mocked_service.projects.list.return_value = MagicMock( + execute=MagicMock(return_value={"projects": projects}) + ) + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", + return_value=projects, + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", + return_value=None, + ), + patch( + "os.path.abspath", + return_value="test_credentials_file", + ), + patch( + "prowler.providers.gcp.gcp_provider.default", + return_value=(mocked_credentials, "default_project"), + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), + ): + gcp_provider = GcpProvider( + arguments.organization_id, + arguments.project_id, + arguments.excluded_project_id, + arguments.credentials_file, + arguments.impersonate_service_account, + arguments.list_project_id, + arguments.config_file, + arguments.fixer_config, + client_id=None, + client_secret=None, + refresh_token=None, + ) + assert gcp_provider.default_project_id == "active_project" + def test_print_credentials_default_options(self, capsys): mocked_credentials = MagicMock() @@ -464,21 +573,27 @@ def test_print_credentials_default_options(self, capsys): mocked_service.projects.list.return_value = MagicMock( execute=MagicMock(return_value={"projects": projects}) ) - with patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", - return_value=projects, - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", - return_value=None, - ), patch( - "os.path.abspath", - return_value="test_credentials_file", - ), patch( - "prowler.providers.gcp.gcp_provider.default", - return_value=(mocked_credentials, MagicMock()), - ), patch( - "prowler.providers.gcp.gcp_provider.discovery.build", - return_value=mocked_service, + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", + return_value=projects, + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", + return_value=None, + ), + patch( + "os.path.abspath", + return_value="test_credentials_file", + ), + patch( + "prowler.providers.gcp.gcp_provider.default", + return_value=(mocked_credentials, MagicMock()), + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), ): gcp_provider = GcpProvider( arguments.organization_id, @@ -535,21 +650,27 @@ def test_print_credentials_impersonated_service_account(self, capsys): mocked_service.projects.list.return_value = MagicMock( execute=MagicMock(return_value={"projects": projects}) ) - with patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", - return_value=projects, - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", - return_value=None, - ), patch( - "os.path.abspath", - return_value="test_credentials_file", - ), patch( - "prowler.providers.gcp.gcp_provider.default", - return_value=(mocked_credentials, MagicMock()), - ), patch( - "prowler.providers.gcp.gcp_provider.discovery.build", - return_value=mocked_service, + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", + return_value=projects, + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", + return_value=None, + ), + patch( + "os.path.abspath", + return_value="test_credentials_file", + ), + patch( + "prowler.providers.gcp.gcp_provider.default", + return_value=(mocked_credentials, MagicMock()), + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), ): gcp_provider = GcpProvider( arguments.organization_id, @@ -614,21 +735,27 @@ def test_print_credentials_excluded_project_ids(self, capsys): execute=MagicMock(return_value={"projects": projects}) ) - with patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", - return_value=projects, - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", - return_value=None, - ), patch( - "os.path.abspath", - return_value="test_credentials_file", - ), patch( - "prowler.providers.gcp.gcp_provider.default", - return_value=(mocked_credentials, MagicMock()), - ), patch( - "prowler.providers.gcp.gcp_provider.discovery.build", - return_value=mocked_service, + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.get_projects", + return_value=projects, + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.update_projects_with_organizations", + return_value=None, + ), + patch( + "os.path.abspath", + return_value="test_credentials_file", + ), + patch( + "prowler.providers.gcp.gcp_provider.default", + return_value=(mocked_credentials, MagicMock()), + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), ): gcp_provider = GcpProvider( arguments.organization_id, @@ -698,12 +825,15 @@ def test_test_connection_valid_project_id(self): execute=MagicMock(return_value={"projectId": project_id}) ) - with patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.setup_session", - return_value=(None, project_id), - ), patch( - "prowler.providers.gcp.gcp_provider.discovery.build", - return_value=mocked_service, + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.setup_session", + return_value=(None, project_id), + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), ): output = GcpProvider.test_connection( client_id="test-client-id", @@ -730,16 +860,19 @@ def test_test_connection_invalid_project_id(self): execute=MagicMock(return_value={"projects": projects}) ) - with patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.setup_session", - return_value=(None, "test-valid-project"), - ), patch( - "prowler.providers.gcp.gcp_provider.discovery.build", - return_value=mocked_service, - ), patch( - "prowler.providers.gcp.gcp_provider.GcpProvider.validate_project_id" - ) as mock_validate_project_id: - + with ( + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.setup_session", + return_value=(None, "test-valid-project"), + ), + patch( + "prowler.providers.gcp.gcp_provider.discovery.build", + return_value=mocked_service, + ), + patch( + "prowler.providers.gcp.gcp_provider.GcpProvider.validate_project_id" + ) as mock_validate_project_id, + ): mock_validate_project_id.side_effect = GCPInvalidProviderIdError( "Invalid project ID" )