6

When attempting to transfer ownership I get the following error:

"domain": "global",
"reason": "invalidSharingRequest",
"message": "Bad Request. User message: \"You can't change the owner of this item.\""

Here is my code:

use Google_Client; 
use Google_Service_Drive;
use Google_Service_Drive_DriveFile;
use Google_Service_Drive_Permission;

public function uploadDocument() {

 $FOLDER_ID = 'my_folder_ID_string';

 $client = new Google_Client();
 $client->setAuthConfig(base_path('service_account_credentials.json'));
 $client->setScopes(array('https://www.googleapis.com/auth/drive'));

 $service = new Google_Service_Drive($client);

 $fileMetadata = new Google_Service_Drive_DriveFile(array(
                                                     'name' => 'Test.pdf',
                                                     'parents' => [$FOLDER_ID]
                                                    ));

 $content = file_get_contents(public_path('tmp/Test.pdf'));

 $file = $service->files->create($fileMetadata, array(
                                                    'data' => $content,
                                                    'mimeType' => 'application/pdf',
                                                    'uploadType' => 'multipart',
                                                    'fields' => 'id'
                                                     ));

  // Transfer Ownership
  $newPermission = new Google_Service_Drive_Permission();
  $newPermission->setRole('owner');
  $newPermission->setType('user');
  $newPermission->setEmailAddress('email@gmail.com');
  $optParams = array('transferOwnership' => 'true');

  $service->permissions->create($file->id, $newPermission, $optParams);

}

The folder is successfully uploaded to Google Drive in the shared folder (owner is 'email@gmail.com', service account is 'editor'), however the owner of the uploaded file is the service account and the editor is 'email@gmail.com'.

1 Answers1

7

You cannot change the owner of a file located on a shared drive

As per documentation:

Files within a shared drive are owned by the shared drive, not individual users.

Consequently, there is no need to "take away" the ownership from the service account.

You can make give users any other role, e.g. organizer which would allow the user to move the file out of the shared drive and thus become the owner of the file.\

UPDATE

If the files are uploaded by the service account to a shared folder on the user's Drive rather than to a shared Drive, the situation is different.

In specific:

  • For GSuite users the transfer of ownership outside of the domain is not allowed and will result in the error Ownership can only be transferred to another user in the same organization as the current owner..
  • A service account is not considered as a domain user and thus subjected to the same restrictions.
  • For consumer users the transfer of ownership from the service account to the user is allowed, but restricted: Only for documents of Google mimeType the owner can be changed.
  • Due to the above mentioned restrictions of transferring ownership from the service account to the user, it is better to avoid the problem by using impersonation.
  • Impersonation means that the a service account acts on behalf of a user (e.g. you) and when he uploads files to your Drive, it is equivalent to when you upload files yourself - no transfer of ownership is necessary, no explicit sharing of a folder with the service account is necessary.
  • To set-up impersonation you need to
    • Enable domain-wide delgation for the specific service account in your GCP console.

    • Authorize all the scopes the service account needs in your Admin console by going on Security > API Controls > Domain wide delegation.

    • Modify your code by adding the line

      $client->setSubject($EmailOfUserToImpersonate);

  • Mind that unfortunately domain-wide delegation is only possible for domains - that is GSuite accounts.
  • For consumer (gmail) accounts it is not possible to transfer ownership for a non-Google mimeType from the service account to the user.
  • As a workaround the user can make a copy of the file uploaded by the service account - subsequently the service account can delete the original file.
ziganotschka
  • 25,866
  • 2
  • 16
  • 33
  • If my understanding of OP's question is correct, I think that OP tries to upload a PDF file to Google Drive using the service account, and tries to transfer the owner of the uploaded file. When I ran OP's script, it seems that the file is not uploaded to the shared Drive. If my understanding is correct, in the current stage, the owner of the files except for the Google Docs cannot be transferred using the service account. On the other hand, the owner of Google Docs files can be transferred. It seems that this is the current specification. I think that this might be the reason of OP's issue. – Tanaike Sep 07 '20 at 23:44
  • @Tanaike: It is true that the OPs code does not include `supportsAllDrives` which is necessary to upload the file to a shared Drive, thank you for pointing it out. – ziganotschka Sep 08 '20 at 06:25
  • @spaghetti_code: Could you confirm that you want to upload first a file with a service account to the shared drive (in this case changing ownership is not necessary)? Or do you want to upload the file to the service account's drive (this is what is gonna happen if you don't set `'supportsAllDrives' => true`) and then change ownership? – ziganotschka Sep 08 '20 at 06:28
  • I'm using a service account to upload the file to My Drive (not Shared Drive). Within My Drive, I've created a Folder which is owned by the Google Drive user, otherwise it would be uploaded to the service's accounts drive and I wouldn't be able to see it in the Google Drive user's My Drive. I want to change ownership because after a while the service accounts drive will fill up and even if I delete the files from the Google Drive user's account it will still be in the service account's drive. – spaghetti_code Sep 08 '20 at 06:57
  • I understood your issue now and will update my answer, thanks @Tanaike for helping with clarification. – ziganotschka Sep 08 '20 at 07:22
  • Would this solution work if you don't have a GSuite account? – spaghetti_code Sep 08 '20 at 08:21
  • 1
    Sorry, I was not aware of the fact that you do not have a GSuite account. Domain-wide delegation only works if you have a domain, so unfortunately not without a GSuite account. Neither can you create a shared drive without a GSuite domain... Why do you not upload the files directly authenticating as yourself instead of using a service account? – ziganotschka Sep 08 '20 at 08:26
  • I'm trying the machine to machine way.. Otherwise, if the user changes the email password, someone would have to change the environment settings. It works well using the node.js client api but with php it doesn't seem to be working. – spaghetti_code Sep 08 '20 at 08:30
  • I am surpised that it works well in other languages given that a service account does not belong to your domain and thus trying to transfer ownership of a file from a service account to you should result in the error message `Ownership can only be transferred to another user in the same organization as the current owner.` See also [here](https://support.google.com/drive/thread/4709066?hl=en). Can you provide a node.js code that allows you to overcome this issue? – ziganotschka Sep 08 '20 at 09:57
  • // Transfer ownership await drive.permissions.create({ fileId: newSheet.data.spreadsheetId, transferOwnership: 'true', resource: { role: 'owner', type: 'user', emailAddress: 'email@gmail.com' } }); – spaghetti_code Sep 08 '20 at 10:06
  • 1
    Are you trying to create the permission in both cases for the same mimeType of file? In my experience for GSuite accounts it is not possible at all to transfer ownership from the service account to the user, while for consumer account I just verified that it is possible, but only for Google mimeTypes. Thereby the programming language does not play a role. What plays a role that in the node.js sample you are transfering ownership for a Google Spreadsheet, while in the php snippet you are trying to do so for a pdf file (non-Google mimeType!) – ziganotschka Sep 08 '20 at 12:19
  • 1
    That makes sense, I believe that answers the question as I've also tried getting the permission ID of the file and updating the permission but I get an error: "The resource body includes fields which are not directly writable." – spaghetti_code Sep 08 '20 at 12:50
  • Try to perform the same request in PHP with a Google mimeType document - provided all other parameters will be the same like in your node.js request - it should work. – ziganotschka Sep 08 '20 at 13:10
  • @ziganotschka Thank you for replying. I saw your updated answer. I think that your detail answer it should be upvoted. – Tanaike Sep 08 '20 at 22:36
  • @ziganotschka Welcome. Thank you for your always support, too. – Tanaike Sep 09 '20 at 22:30