googleapis / python-analytics-data

This library has moved to https://github.com/googleapis/google-cloud-python/tree/main/packages/google-analytics-data
Apache License 2.0
159 stars 36 forks source link

Credentials subject and Domain-wide delegation access issue for BetaAnalyticsDataClient #341

Closed jroakes closed 1 year ago

jroakes commented 1 year ago

Environment details

Steps to reproduce

  1. Using quickstart.py code supplied in this repo;
  2. Using the domain-wide delegation scopes below in Workspace Admin;
  3. Oauth subject email has Administrator access to property;
  4. API access granted for GCP project;
  5. Using service account that currently operates normally for GA and GSC using subject delegation.

Domain-wide Delegation Scopes

https://www.googleapis.com/auth/webmasters.readonly
https://www.googleapis.com/auth/analytics.readonly
https://www.googleapis.com/auth/analytics
openid

Code example

from google.analytics.data_v1beta import BetaAnalyticsDataClient
from google.oauth2 import service_account
from google.analytics.data_v1beta.types import (
    DateRange,
    Dimension,
    Metric,
    RunReportRequest,
)

SERVICE_ACCOUNT_CREDENTIALS = "credentials/path-to-service-account-json.json"
SERVICE_ACCOUNT_SUBJECT = "user@domain.com"

def sample_run_report(property_id="<property-id>"):
    """Runs a simple report on a Google Analytics 4 property."""
    # TODO(developer): Uncomment this variable and replace with your
    #  Google Analytics 4 property ID before running the sample.
    # property_id = "YOUR-GA4-PROPERTY-ID"

    # [START analyticsdata_json_credentials_initialize]
    # TODO(developer): Uncomment this variable and replace with a valid path to
    #  the credentials.json file for your service account downloaded from the
    #  Cloud Console.
    # credentials_json_path = "/path/to/credentials.json"

    # Explicitly use service account credentials by specifying
    # the private key file.
    credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_CREDENTIALS, subject=SERVICE_ACCOUNT_SUBJECT)
    client = BetaAnalyticsDataClient(credentials=credentials)

    # [END analyticsdata_json_credentials_initialize]

    # [START analyticsdata_json_credentials_run_report]
    request = RunReportRequest(
        property=f"properties/{property_id}",
        dimensions=[Dimension(name="city")],
        metrics=[Metric(name="activeUsers")],
        date_ranges=[DateRange(start_date="2020-03-31", end_date="today")],
    )
    response = client.run_report(request)
    # [END analyticsdata_json_credentials_run_report]

    print("Report result:")
    for row in response.rows:
        print(row.dimension_values[0].value, row.metric_values[0].value)

sample_run_report(property_id="XXXXXXXXXX")

Stack trace

_InactiveRpcError                         Traceback (most recent call last)
/opt/tljh/user/lib/python3.8/site-packages/google/api_core/grpc_helpers.py in error_remapped_callable(*args, **kwargs)
     64         try:
---> 65             return callable_(*args, **kwargs)
     66         except grpc.RpcError as exc:

/opt/tljh/user/lib/python3.8/site-packages/grpc/_channel.py in __call__(self, request, timeout, metadata, credentials, wait_for_ready, compression)
    945                                       wait_for_ready, compression)
--> 946         return _end_unary_response_blocking(state, call, False, None)
    947 

/opt/tljh/user/lib/python3.8/site-packages/grpc/_channel.py in _end_unary_response_blocking(state, call, with_call, deadline)
    848     else:
--> 849         raise _InactiveRpcError(state)
    850 

_InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
    status = StatusCode.PERMISSION_DENIED
    details = "User does not have sufficient permissions for this property. To learn more about Property ID, see https://developers.google.com/analytics/devguides/reporting/data/v1/property-id."
    debug_error_string = "UNKNOWN:Error received from peer ipv4:172.253.63.95:443 {created_time:"2023-02-09T13:29:52.563206159+00:00", grpc_status:7, grpc_message:"User does not have sufficient permissions for this property. To learn more about Property ID, see https://developers.google.com/analytics/devguides/reporting/data/v1/property-id."}"
>

The above exception was the direct cause of the following exception:

PermissionDenied                          Traceback (most recent call last)
/tmp/ipykernel_30595/3858193892.py in <module>
     46 
     47 
---> 48 sample_run_report(property_id="281603923")

/tmp/ipykernel_30595/3858193892.py in sample_run_report(property_id)
     38         date_ranges=[DateRange(start_date="2020-03-31", end_date="today")],
     39     )
---> 40     response = client.run_report(request)
     41     # [END analyticsdata_json_credentials_run_report]
     42 

/opt/tljh/user/lib/python3.8/site-packages/google/analytics/data_v1beta/services/beta_analytics_data/client.py in run_report(self, request, retry, timeout, metadata)
    491 
    492         # Send the request.
--> 493         response = rpc(
    494             request,
    495             retry=retry,

/opt/tljh/user/lib/python3.8/site-packages/google/api_core/gapic_v1/method.py in __call__(self, timeout, retry, *args, **kwargs)
    111             kwargs["metadata"] = metadata
    112 
--> 113         return wrapped_func(*args, **kwargs)
    114 
    115 

/opt/tljh/user/lib/python3.8/site-packages/google/api_core/grpc_helpers.py in error_remapped_callable(*args, **kwargs)
     65             return callable_(*args, **kwargs)
     66         except grpc.RpcError as exc:
---> 67             raise exceptions.from_grpc_error(exc) from exc
     68 
     69     return error_remapped_callable

PermissionDenied: 403 User does not have sufficient permissions for this property. To learn more about Property ID, see https://developers.google.com/analytics/devguides/reporting/data/v1/property-id.

I am unable to provide delegated access to properties using the same subject delegation that works in the GA3 API. I have searched to see if there is another scope for delegation in our Workspace account relative to this beta version. It is my understanding that https://www.googleapis.com/auth/analytics.readonly should provide this. We use a centralized Google Workspace email as a user on multiple GA4 properties to make reporting more efficient. Currently, we can't figure out how to unblock these permission denied errors using subject delgation, using code provided as example.

jroakes commented 1 year ago

Leaving an update here for others:

Error was present with:

google-auth 1.35.0
google-api-core 1.34.0

Resolution:

  1. Updated to: installed google-api-core-2.11.0 google-auth-2.16.0
  2. Included Scopes in credentials.
    credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_CREDENTIALS, subject=SERVICE_ACCOUNT_SUBJECT)

to

SCOPES = ['https://www.googleapis.com/auth/analytics.readonly']
credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_CREDENTIALS, 
                                                                        scopes=SCOPES,
                                                                        subject=SERVICE_ACCOUNT_SUBJECT)

step 1 seemed to be the main issue. received error even after updating step 2.

Running: pip install -U google-api-core[grpc] solved the error.

parthea commented 1 year ago

Thanks for providing an update. I'm glad that the issue is resolved. I'm going to close this issue but please feel free to open a new issue if you have additional questions or encounter issues.

joejoinerr commented 1 year ago

Hey @parthea this is going to reappear because the dependency is not set correctly.

https://github.com/googleapis/python-analytics-data/blob/9f18c1f09b95e4351adad3674b4d67a39a63482e/setup.py#L39

If it's google-api-core 1.34.0 causing the problem, that dependency will need to be reviewed to fix this.