from unittest.mock import MagicMock, patch import pytest from google.oauth2.service_account import Credentials from googleapiclient.errors import HttpError from prowler.providers.googleworkspace.exceptions.exceptions import ( GoogleWorkspaceImpersonationError, GoogleWorkspaceInsufficientScopesError, GoogleWorkspaceInvalidCredentialsError, GoogleWorkspaceMissingDelegatedUserError, GoogleWorkspaceNoCredentialsError, GoogleWorkspaceSetUpIdentityError, GoogleWorkspaceSetUpSessionError, ) from prowler.providers.googleworkspace.googleworkspace_provider import ( GoogleworkspaceProvider, ) from prowler.providers.googleworkspace.models import ( GoogleWorkspaceIdentityInfo, GoogleWorkspaceSession, ) from tests.providers.googleworkspace.googleworkspace_fixtures import ( CUSTOMER_ID, DELEGATED_USER, DOMAIN, ROOT_ORG_UNIT_ID, SERVICE_ACCOUNT_CREDENTIALS, ) class TestGoogleWorkspaceProvider: def test_googleworkspace_provider_with_credentials_file(self): """Test provider initialization with credentials file""" credentials_file = "/path/to/credentials.json" delegated_user = DELEGATED_USER # Mock credentials object mock_credentials = MagicMock(spec=Credentials) with ( patch( "prowler.providers.googleworkspace.googleworkspace_provider.GoogleworkspaceProvider.setup_session", return_value=( GoogleWorkspaceSession(credentials=mock_credentials), DELEGATED_USER, ), ), patch( "prowler.providers.googleworkspace.googleworkspace_provider.GoogleworkspaceProvider.setup_identity", return_value=GoogleWorkspaceIdentityInfo( domain=DOMAIN, customer_id=CUSTOMER_ID, delegated_user=DELEGATED_USER, profile="default", ), ), ): provider = GoogleworkspaceProvider( credentials_file=credentials_file, delegated_user=delegated_user, ) assert provider._type == "googleworkspace" assert provider.session.credentials == mock_credentials assert provider.identity == GoogleWorkspaceIdentityInfo( domain=DOMAIN, customer_id=CUSTOMER_ID, delegated_user=DELEGATED_USER, profile="default", ) assert provider.domain_resource.id == CUSTOMER_ID assert provider.domain_resource.name == DOMAIN assert provider._audit_config == {} def test_googleworkspace_provider_with_credentials_content(self): """Test provider initialization with credentials content""" import json credentials_content = json.dumps(SERVICE_ACCOUNT_CREDENTIALS) delegated_user = DELEGATED_USER # Mock credentials object mock_credentials = MagicMock(spec=Credentials) with ( patch( "prowler.providers.googleworkspace.googleworkspace_provider.GoogleworkspaceProvider.setup_session", return_value=( GoogleWorkspaceSession(credentials=mock_credentials), DELEGATED_USER, ), ), patch( "prowler.providers.googleworkspace.googleworkspace_provider.GoogleworkspaceProvider.setup_identity", return_value=GoogleWorkspaceIdentityInfo( domain=DOMAIN, customer_id=CUSTOMER_ID, delegated_user=DELEGATED_USER, profile="default", ), ), ): provider = GoogleworkspaceProvider( credentials_content=credentials_content, delegated_user=delegated_user, ) assert provider._type == "googleworkspace" assert provider.identity.domain == DOMAIN assert provider.identity.customer_id == CUSTOMER_ID assert provider.identity.delegated_user == DELEGATED_USER assert provider.domain_resource.customer_id == CUSTOMER_ID def test_googleworkspace_provider_missing_delegated_user(self): """Test that missing delegated_user raises exception""" credentials_file = "/path/to/credentials.json" with pytest.raises(GoogleWorkspaceMissingDelegatedUserError): GoogleworkspaceProvider.setup_session( credentials_file=credentials_file, delegated_user=None, ) def test_googleworkspace_provider_no_credentials(self): """Test that missing credentials raises exception""" delegated_user = DELEGATED_USER with pytest.raises(GoogleWorkspaceNoCredentialsError): GoogleworkspaceProvider.setup_session( credentials_file=None, credentials_content=None, delegated_user=delegated_user, ) def test_googleworkspace_provider_test_connection_success(self): """Test successful connection test""" credentials_file = "/path/to/credentials.json" delegated_user = DELEGATED_USER mock_credentials = MagicMock(spec=Credentials) with ( patch( "prowler.providers.googleworkspace.googleworkspace_provider.GoogleworkspaceProvider.setup_session", return_value=( GoogleWorkspaceSession(credentials=mock_credentials), DELEGATED_USER, ), ), patch( "prowler.providers.googleworkspace.googleworkspace_provider.GoogleworkspaceProvider.setup_identity", return_value=GoogleWorkspaceIdentityInfo( domain=DOMAIN, customer_id=CUSTOMER_ID, delegated_user=DELEGATED_USER, profile="default", ), ), ): connection = GoogleworkspaceProvider.test_connection( credentials_file=credentials_file, delegated_user=delegated_user, ) assert connection.is_connected is True assert connection.error is None def test_googleworkspace_provider_test_connection_failure(self): """Test failed connection test""" credentials_file = "/path/to/credentials.json" delegated_user = DELEGATED_USER with patch( "prowler.providers.googleworkspace.googleworkspace_provider.GoogleworkspaceProvider.setup_session", side_effect=GoogleWorkspaceSetUpSessionError(), ): connection = GoogleworkspaceProvider.test_connection( credentials_file=credentials_file, delegated_user=delegated_user, raise_on_exception=False, ) assert connection.is_connected is False assert connection.error is not None def test_googleworkspace_provider_print_credentials(self): """Test print_credentials method""" mock_credentials = MagicMock(spec=Credentials) with ( patch( "prowler.providers.googleworkspace.googleworkspace_provider.GoogleworkspaceProvider.setup_session", return_value=( GoogleWorkspaceSession(credentials=mock_credentials), DELEGATED_USER, ), ), patch( "prowler.providers.googleworkspace.googleworkspace_provider.GoogleworkspaceProvider.setup_identity", return_value=GoogleWorkspaceIdentityInfo( domain=DOMAIN, customer_id=CUSTOMER_ID, delegated_user=DELEGATED_USER, profile="default", ), ), patch( "prowler.providers.googleworkspace.googleworkspace_provider.print_boxes" ) as mock_print_boxes, ): provider = GoogleworkspaceProvider( credentials_file="/path/to/credentials.json", delegated_user=DELEGATED_USER, ) provider.print_credentials() # Verify print_boxes was called assert mock_print_boxes.called def test_setup_session_credentials_file_invalid_json(self): """Test ValueError when credentials file has invalid format""" with patch( "prowler.providers.googleworkspace.googleworkspace_provider.service_account.Credentials.from_service_account_file", side_effect=ValueError("Invalid credentials format"), ): with pytest.raises(GoogleWorkspaceInvalidCredentialsError) as exc_info: GoogleworkspaceProvider.setup_session( credentials_file="/path/to/invalid.json", delegated_user=DELEGATED_USER, ) assert "Invalid service account credentials file" in str(exc_info.value) def test_setup_session_credentials_content_invalid_json(self): """Test JSONDecodeError when credentials content is invalid JSON""" with pytest.raises(GoogleWorkspaceInvalidCredentialsError) as exc_info: GoogleworkspaceProvider.setup_session( credentials_content="{ invalid json }", delegated_user=DELEGATED_USER, ) assert "Invalid JSON in credentials content" in str(exc_info.value) def test_setup_session_invalid_delegated_user_email(self): """Test invalid delegated user email format""" with pytest.raises(GoogleWorkspaceInvalidCredentialsError) as exc_info: GoogleworkspaceProvider.setup_session( credentials_file="/path/to/credentials.json", delegated_user="not-an-email", ) assert "Must be a valid email address" in str(exc_info.value) def test_setup_session_insufficient_scopes_403(self): """Test GoogleWorkspaceInsufficientScopesError for 403 errors""" mock_credentials = MagicMock(spec=Credentials) mock_delegated_creds = MagicMock() mock_credentials.with_subject.return_value = mock_delegated_creds # Mock HttpError with 403 status http_error = HttpError( resp=MagicMock(status=403), content=b"Forbidden", uri="test" ) with ( patch( "prowler.providers.googleworkspace.googleworkspace_provider.service_account.Credentials.from_service_account_file", return_value=mock_credentials, ), patch( "prowler.providers.googleworkspace.googleworkspace_provider.build" ) as mock_build, ): mock_service = MagicMock() mock_build.return_value = mock_service mock_service.users().get().execute.side_effect = http_error with pytest.raises(GoogleWorkspaceInsufficientScopesError) as exc_info: GoogleworkspaceProvider.setup_session( credentials_file="/path/to/creds.json", delegated_user=DELEGATED_USER, ) assert "Domain-Wide Delegation is not configured" in str(exc_info.value) def test_setup_session_impersonation_generic_error(self): """Test GoogleWorkspaceImpersonationError for other delegation errors""" mock_credentials = MagicMock(spec=Credentials) mock_delegated_creds = MagicMock() mock_credentials.with_subject.return_value = mock_delegated_creds with ( patch( "prowler.providers.googleworkspace.googleworkspace_provider.service_account.Credentials.from_service_account_file", return_value=mock_credentials, ), patch( "prowler.providers.googleworkspace.googleworkspace_provider.build" ) as mock_build, ): mock_service = MagicMock() mock_build.return_value = mock_service mock_service.users().get().execute.side_effect = Exception( "Connection error" ) with pytest.raises(GoogleWorkspaceImpersonationError) as exc_info: GoogleworkspaceProvider.setup_session( credentials_file="/path/to/creds.json", delegated_user=DELEGATED_USER, ) assert "Failed to verify delegation" in str(exc_info.value) def test_setup_identity_customer_fetch_failure(self): """Test error when fetching customer information fails""" mock_session = GoogleWorkspaceSession(credentials=MagicMock(spec=Credentials)) with patch( "prowler.providers.googleworkspace.googleworkspace_provider.build" ) as mock_build: mock_service = MagicMock() mock_build.return_value = mock_service mock_service.customers().get().execute.side_effect = Exception("API error") with pytest.raises(GoogleWorkspaceSetUpIdentityError) as exc_info: GoogleworkspaceProvider.setup_identity( session=mock_session, delegated_user=DELEGATED_USER, ) assert "Failed to fetch customer information" in str(exc_info.value) def test_setup_identity_domain_mismatch(self): """Test error when user domain is not in workspace""" mock_session = GoogleWorkspaceSession(credentials=MagicMock(spec=Credentials)) with patch( "prowler.providers.googleworkspace.googleworkspace_provider.build" ) as mock_build: mock_service = MagicMock() mock_build.return_value = mock_service mock_service.customers().get().execute.return_value = {"id": CUSTOMER_ID} mock_service.domains().list().execute.return_value = { "domains": [{"domainName": "different-company.com"}] } with pytest.raises(GoogleWorkspaceInvalidCredentialsError) as exc_info: GoogleworkspaceProvider.setup_identity( session=mock_session, delegated_user=DELEGATED_USER, ) assert "is not configured in this Google Workspace" in str(exc_info.value) def test_setup_identity_fetches_root_org_unit(self): """Test that setup_identity fetches and stores the root org unit ID""" mock_session = GoogleWorkspaceSession(credentials=MagicMock(spec=Credentials)) with patch( "prowler.providers.googleworkspace.googleworkspace_provider.build" ) as mock_build: mock_service = MagicMock() mock_build.return_value = mock_service mock_service.customers().get().execute.return_value = {"id": CUSTOMER_ID} mock_service.domains().list().execute.return_value = { "domains": [{"domainName": DOMAIN}] } mock_service.orgunits().list().execute.return_value = { "organizationUnits": [ { "orgUnitPath": "/", "orgUnitId": f"id:{ROOT_ORG_UNIT_ID}", "name": "Test Company", } ] } identity = GoogleworkspaceProvider.setup_identity( session=mock_session, delegated_user=DELEGATED_USER, ) assert identity.root_org_unit_id == ROOT_ORG_UNIT_ID assert identity.customer_id == CUSTOMER_ID def test_setup_identity_root_org_unit_fetch_failure(self): """Test that setup_identity gracefully handles root org unit fetch failure""" mock_session = GoogleWorkspaceSession(credentials=MagicMock(spec=Credentials)) with patch( "prowler.providers.googleworkspace.googleworkspace_provider.build" ) as mock_build: mock_service = MagicMock() mock_build.return_value = mock_service mock_service.customers().get().execute.return_value = {"id": CUSTOMER_ID} mock_service.domains().list().execute.return_value = { "domains": [{"domainName": DOMAIN}] } mock_service.orgunits().list().execute.side_effect = Exception( "Insufficient permissions" ) identity = GoogleworkspaceProvider.setup_identity( session=mock_session, delegated_user=DELEGATED_USER, ) assert identity.root_org_unit_id is None assert identity.customer_id == CUSTOMER_ID def test_test_connection_raises_exception_when_flag_true(self): """Test that test_connection raises exception when raise_on_exception=True""" credentials_file = "/path/to/credentials.json" delegated_user = DELEGATED_USER with patch( "prowler.providers.googleworkspace.googleworkspace_provider.GoogleworkspaceProvider.setup_session", side_effect=GoogleWorkspaceSetUpSessionError( file="test", message="Test error" ), ): with pytest.raises(GoogleWorkspaceSetUpSessionError): GoogleworkspaceProvider.test_connection( credentials_file=credentials_file, delegated_user=delegated_user, raise_on_exception=True, )