RedLens834
RedLens834

Reputation: 231

Google Cloud Storage with Service account - 403 Forbidden

I'm implementing a cron fetching automatically my application installation stats on Google Play Store. So, I need to download a file from Google Cloud Storage and parse it. I made this code :

// Set scopes to authorize full control of Cloud Storage

$scopes = array("https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/devstorage.full_control");


// Create new Google Client

$client = new Google_Client();


// Set path to ServiceAccount's credentials and link them to Google Client

putenv('GOOGLE_APPLICATION_CREDENTIALS=../../libs/GoogleAPICredentials/client_credentials_CPTS.json');
$client->useApplicationDefaultCredentials();
$client->setScopes($scopes);


// Create new Google Service Storage object to handle our repository

$service = new Google_Service_Storage($client);


// Get content of GCS repository

$request = $service->objects->listObjects("MY_BUCKET");


// Settings folders, files, buckets, names

$sourceBucket = "pubsite_prod_rev_BUCKET_IDENTIFIER";
$sourceObject = "installs_MY_APP_IDENTIFIER_" . $date->format("Ym") . "_overview.csv";
$localFile = "../files/temp/installs_MY_APP_IDENTIFIER_" . $date->format("Ym") . "_overview.csv";
$fullSrcObj = "stats/installs/" . $sourceObject;
$destinationBucket = "MY_BUCKET";
$destinationObject = $sourceObject;


// Create new Google Service Storage StorageObject object to handle a single object (file)

$postBody = new Google_Service_Storage_StorageObject($client);


try {
    // Copy stats app .CSV file from orignal repository (Google Play Store) to our bucket

    $response = $service->objects->copy($sourceBucket, $fullSrcObj, $destinationBucket, $destinationObject, $postBody);


    // Get the new object

    try {
        $object = $service->objects->get($destinationBucket, $destinationObject);
    }
    catch (Google_Service_Exception $e) {
        if ($e->getCode() == 404)
            echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">". $e->getMessage() ."</span></b><br><br>");
        throw $e;
    }


    // Download the new object on the MY_COMPAGNY server

    $uri = sprintf('https://storage.googleapis.com/%s/%s?alt=media&generation=%s', $destinationBucket, $destinationObject, $object->generation);
    $http = $client->authorize();
    $response = $http->get($uri);

    if ($response->getStatusCode() != 200) {
        echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">An unhandled error append !</span></b><br><br>");
        die();
    }


    // Save it into local file on MY_COMPAGNY server

    file_put_contents($localFile, $response->getBody());


    // Convert .CSV into php array

    $csv = array_map('str_getcsv', file($localFile));


    // Delete local file

    unlink($localFile);


    // Remove last line of CSV (Array) --> Empty - And remove first line of CSV (Array) --> Title line

    array_shift($csv);
    array_pop($csv);


    // Insert each line into MY_COMPAGNY Database

    $success = false;
    foreach ($csv as $row) {
        $success = insertAppInstallData(filter_var($row[0], FILTER_SANITIZE_STRING), (int)filter_var($row[$columnTotal], FILTER_SANITIZE_NUMBER_INT), (int)filter_var($row[$columnCurrent], FILTER_SANITIZE_NUMBER_INT))?true:  $success;
    }
    if (!$success)
        echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">DataBase insertion failure</span></b><br><br>");

}
catch (Google_Service_Exception $E) {
    echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">".$E->getMessage()." - ".$E->getTraceAsString()."</span></b><br><br>");
}

With an user account, it's work well but with a Service account, I have a 403 error, Forbidden on the call to $service->objects->copy().

{ 
   "error": 
   { 
        "errors": [ 
        { 
            "domain": "global", 
            "reason": "forbidden", 
            "message": "Forbidden" 
        }], 
        "code": 403, 
        "message": "Forbidden" 
    } 
}

I checks twice if there are good credentials, IAM authorizations, OAuth client, ... and it still dosent work.

I'm not on a Google Engine, I running on a personal Engine.

Someone have an idea ?

Have a good day and Happy New Year !

Ben.

Upvotes: 2

Views: 3997

Answers (1)

Morfinismo
Morfinismo

Reputation: 5253

The reason you are getting error 403 is because you are missing the domain wide delegation and the user impersonation step. Please refer to the documentation here to understand how you can access user data with service accounts.

The process is like this:

$scopes = array("https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/devstorage.full_control");

$client = new Google_Client();
$client->useApplicationDefaultCredentials();    
$client->addScope($scopes); //Set scopes to client object
$client->setSubject("[email protected]"); // replace with the user account

You are able to make it work with a user account because the user already has access to the resource but not the service account, hence the error 403 forbidden. Unfortunately, the domain wide delegation process and user impersonation only works with G Suite accounts( formerly Google Apps ). Another alternative would be to share the resources with the service account but that is something that I haven´t explored yet and not sure if it is possible. You can also refer to the PHP Client Lib documentation here. I hope this information is helpful.

Upvotes: 2

Related Questions