共用檔案、資料夾和雲端硬碟

每個 Google 雲端硬碟檔案、資料夾和共用雲端硬碟都有相關聯的permissions資源。每個資源都會識別特定type (user、group、domain、anyone) 和role (owner、organizer、fileOrganizer、writer、commenter、reader) 的權限。舉例來說,某個檔案可能具有一項權限,可授予特定使用者 (type=user) 唯讀存取權 (role=reader),而另一項權限則可授予特定群組 (type=group) 的成員在檔案中新增註解的權限 (role=commenter)。

如需角色完整清單和各角色允許的作業,請參閱「角色和權限」。

權限的傳播方式

權限會從上層資料夾向下傳播至所有子項:

  • 預設為沿用:所有子檔案和子資料夾都會自動沿用上層資料夾的權限。
  • 無法減少子項目的權限:您無法移除或減少子項目的沿用權限。變更必須在上層資料夾進行,或是資料夾必須使用存取限制設定。
  • 可擴展至子項:子項可授予較寬鬆的角色,例如在使用者擁有 role=reader 的資料夾中,授予檔案 role=writer。
  • 移動時重新評估:將項目移至新的上層資料夾時,系統會重新評估並將新上層資料夾的權限套用至該項目及其子項。

檔案連結和存取控管

與特定使用者或群組共用檔案或資料夾時,存取項目的網址不會變更,系統也不會為每位使用者產生專屬連結。而是根據 fileId 產生單一固定連結。

Google 雲端硬碟會評估項目的 ACL,藉此控管存取權。使用者嘗試開啟連結時,雲端硬碟會根據存取控制清單 (ACL) 驗證已通過驗證的身分。如果權限遭到撤銷或到期,系統會將使用者從 ACL 中移除。如果使用者再次嘗試造訪連結,雲端硬碟會拒絕存取。

瞭解檔案功能

permissions 資源會定義「誰」有存取權 (存取控制清單),但不會直接指出目前使用者是否能在應用程式的 UI 中執行特定動作。

而是包含布林值 capabilities 欄位 (例如 canComment、canShare 或 canDelete) 的集合,Google Drive API 會根據使用者的角色和項目設定動態計算這些欄位。files 資源

取得檔案功能

在算繪應用程式的 UI 時,請檢查 files.capabilities,而不是直接剖析權限:

  • 使用 fields=capabilities 呼叫 files.get 方法。詳情請參閱「傳回特定欄位」。
  • 使用傳回的布林值標記,在介面中啟用或停用對應的動作。舉例來說,如果 canComment 是 false,則停用留言功能。

共用雲端硬碟資源的適用情境

下表列出在不同位置和項目類型之間共用雲端硬碟資源時,所需的角色和條件:

位置 項目 必要的角色 主要限制
我的雲端硬碟 檔案或資料夾 owner 或 writer ownerwritersCanShare=false
如要設定資料夾的限期存取,必須具備 reader 權限 (請參閱「設定到期日」一文)。
共用雲端硬碟 檔案 organizer、fileOrganizer 或 writer writersCanShare 一律會視為 true。
共用雲端硬碟 資料夾 organizer 如果 sharingFoldersRequiresOrganizerPermission 為 false,fileOrganizer 也可以分享。
共用雲端硬碟 會員制 organizer 僅適用於 user 或 group (不適用於網域)。

管理權限

下表列出 permissions 資源可用的方法:

方法 API 端點 重要參數 參考資料
建立 POST https://www.googleapis.com/drive/v3/files/{fileId}/permissions role、type、emailAddress或domain permissions.create
取得 GET https://www.googleapis.com/drive/v3/files/{fileId}/permissions/{permissionId} fields permissions.get
清單 GET https://www.googleapis.com/drive/v3/files/{fileId}/permissions pageSize、supportsAllDrives、pageToken permissions.list
更新 PATCH https://www.googleapis.com/drive/v3/files/{fileId}/permissions/{permissionId} role、allowFileDiscovery permissions.update
刪除 DELETE https://www.googleapis.com/drive/v3/files/{fileId}/permissions/{permissionId} supportsAllDrives permissions.delete

建立權限

如要共用檔案、資料夾或共用雲端硬碟,請對 permissions 資源呼叫 create 方法,並使用 fileId。建立權限會在項目中新增 ACL 項目,並傳回指派的 permissionId。

在要求主體中提供下列欄位:

  • role:要授予的存取層級 (例如 reader、commenter 或 writer)。如需完整清單,請參閱「角色和權限」。
  • type:受讓人範圍 (user、group、domain 或 anyone)。
  • 受讓人 ID (視 type 而定):
    • emailAddress:type 為 user 或 group 時為必要屬性。
    • domain:type 為 domain 時為必填。

以下程式碼範例說明如何建立權限。回應會傳回 permissions 資源的執行個體,包括指派的 permissionId。

要求

POST https://www.googleapis.com/drive/v3/files/FILE_ID/permissions
{
  "role": "commenter",
  "type": "user",
  "emailAddress": "alex@altostrat.com"
}

回應

{
  "kind": "drive#permission",
  "id": "PERMISSION_ID",
  "type": "user",
  "role": "commenter"
}

與目標對象分享

目標對象是一種使用者群組 (例如部門或團隊),您可以建議使用者在共用項目時挑選這類群組。您可以鼓勵使用者與特定或部分目標對象 (而非整個機構) 共用項目。目標對象可協助您提升資料安全性和隱私,並讓使用者更輕鬆地適當共用資料。

如要與目標對象共用,請設定 type=domain 並將 domain 設為 <TARGET_AUDIENCE_ID>.audience.googledomains.com。如要瞭解如何在 Google 管理控制台中尋找或建立目標對象,請參閱「關於目標對象」。

如要瞭解使用者與目標對象的互動情形,請參閱連結分享的使用者體驗。

取得權限

如要取得權限,請使用 fileId 和 permissionId 路徑參數,呼叫 permissions 資源的 get 方法。如果您不知道權限 ID,請先列出所有權限。

列出權限

如要列出檔案、資料夾或共用雲端硬碟的權限,請使用必要的 fileId 路徑參數,對 permissions 資源呼叫 list 方法。

您可以加入下列任一選用查詢參數,分頁或篩選回應:

  • pageSize (選用):每頁要傳回的權限數量上限。 如果未針對共用雲端硬碟中的檔案設定,最多會傳回 100 個結果。如果未針對不在共用雲端硬碟中的檔案設定,系統會傳回整個清單。

  • pageToken (選用):先前 list 呼叫傳來的頁面符記,用於擷取後續頁面。

  • supportsAllDrives (選用):要求存取權的應用程式是否同時支援「我的雲端硬碟」和共用雲端硬碟。

  • useDomainAdminAccess (選用):設為 true,以網域管理員身分發出要求。如果 fileId 參數參照共用雲端硬碟,且要求者是共用雲端硬碟所屬網域的管理員,系統就會授予要求者存取權。詳情請參閱「以網域管理員身分管理共用雲端硬碟」。

  • includePermissionsForView (選用):要在回覆中加入的其他檢視權限。僅支援 published。

  • fields (選用):要在回應中傳回的特定欄位。根據預設,list 只會傳回 id、type、kind 和 role。如要傳回其他欄位 (例如 permissionDetails),請使用這個參數指定。詳情請參閱「傳回特定欄位」。

判斷角色來源

如要變更檔案或資料夾的角色,您必須知道角色的來源。 如果是共用雲端硬碟,角色的來源可能是共用雲端硬碟的成員資格、資料夾的角色或檔案的角色。

如要判斷共用雲端硬碟或該硬碟中項目的角色來源,請使用 fileId 和 permissionId 路徑參數,以及設為 permissionDetails 欄位的 fields 參數,對 permissions 資源呼叫 get 方法。

如要尋找 permissionId,請在 permissions 資源上使用 list 方法,並搭配 fileId 路徑參數。如要擷取 list 要求中的 permissionDetails 欄位,請將 fields 參數設為 permissions/permissionDetails。

這個欄位會列舉使用者、群組或網域的所有沿用和直接檔案權限。

下列程式碼範例說明如何判斷角色來源。回應會傳回 permissions 資源的 permissionDetails。「inheritedFrom」欄位提供沿用權限的項目 ID。

要求

GET https://www.googleapis.com/drive/v3/files/FILE_ID/permissions/PERMISSION_ID?fields=permissionDetails&supportsAllDrives=true

回應

{
  "permissionDetails": [
    {
      "permissionType": "member",
      "role": "commenter",
      "inheritedFrom": "INHERITED_FROM_ID",
      "inherited": true
    },
    {
      "permissionType": "file",
      "role": "writer",
      "inherited": false
    }
  ]
}

更新權限

如要更新檔案或資料夾的權限,可以變更指派的角色。如要進一步瞭解如何找出角色來源,請參閱「判斷角色來源」。

  1. 在 permissions 資源上呼叫 update 方法,並將 fileId 路徑參數設為相關聯的檔案、資料夾或共用雲端硬碟,以及將 permissionId 路徑參數設為要變更的權限。如要尋找 permissionId,請使用 permissions 資源的 list 方法和 fileId 路徑參數。

  2. 在要求中,找出新的 role。

即使使用者或群組已是成員,您還是可以授予共用雲端硬碟中個別檔案或資料夾的權限。舉例來說,假設 Alex 是共用雲端硬碟的成員,role=commenter 不過,您的應用程式可以授予 Alex role=writer 共用雲端硬碟中檔案的存取權。在本例中,由於新角色比透過成員資格授予的角色權限更寬鬆,因此新權限會成為檔案或資料夾的有效角色。

您可以透過修補程式語意套用更新,也就是對資源進行部分修改。您必須在要求中明確設定要修改的欄位。要求中未包含的欄位會保留現有值。詳情請參閱「處理部分資源」。

除了變更角色,當權限為 type 時,您也可以修改項目的可探索性。domainanyone如要讓共用檔案可供搜尋或不列出,請在修補程式要求中加入 allowFileDiscovery 布林值欄位。設為 true 後,即使指定目標對象未取得直接連結,項目仍會顯示在搜尋結果中。您不需要刪除並重新建立權限,即可變更這項設定。

下列程式碼範例說明如何將檔案或資料夾的權限從 commenter 變更為 writer。回應會傳回 permissions 資源的例項。

要求

PATCH https://www.googleapis.com/drive/v3/files/FILE_ID/permissions/PERMISSION_ID
{
  "role": "writer"
}

回應

{
  "kind": "drive#permission",
  "id": "PERMISSION_ID",
  "type": "user",
  "role": "writer"
}

使用批次要求更新多項權限

系統不支援對相同檔案、資料夾或共用雲端硬碟同時進行權限修改。無論您是修改同一位或不同收件者的權限,以及要求是來自單一應用程式或多位使用者,所有變更作業 (例如更新或刪除) 都會受到這項限制。

雲端硬碟會評估並更新項目的權限,做為單一 ACL。同時進行作業會導致競爭狀況,也就是「最後寫入者勝出」,這可能會無聲無息地覆寫權限變更,或觸發 sharingRateLimitExceeded 錯誤。

為避免衝突,請依序對同一項目執行權限變更,或使用批次要求,在單一要求中修改多項權限。

以下範例說明如何使用用戶端程式庫,以批次方式修改權限。

Java

drive/snippets/drive_v3/src/main/java/ShareFile.java
import com.google.api.client.googleapis.batch.BatchRequest;
import com.google.api.client.googleapis.batch.json.JsonBatchCallback;
import com.google.api.client.googleapis.json.GoogleJsonError;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.Permission;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/* Class to demonstrate use-case of modify permissions. */
public class ShareFile {

  /**
   * Batch permission modification.
   * realFileId file Id.
   * realUser User Id.
   * realDomain Domain of the user ID.
   *
   * @return list of modified permissions if successful, {@code null} otherwise.
   * @throws IOException if service account credentials file not found.
   */
  public static List<String> shareFile(String realFileId, String realUser, String realDomain)
      throws IOException {
        /* Load pre-authorized user credentials from the environment.
         TODO(developer) - See https://developers.google.com/identity for
         guides on implementing OAuth2 for your application.application*/
    GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()
        .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));
    HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(
        credentials);

    // Build a new authorized API client service.
    Drive service = new Drive.Builder(new NetHttpTransport(),
        GsonFactory.getDefaultInstance(),
        requestInitializer)
        .setApplicationName("Drive samples")
        .build();

    final List<String> ids = new ArrayList<String>();


    JsonBatchCallback<Permission> callback = new JsonBatchCallback<Permission>() {
      @Override
      public void onFailure(GoogleJsonError e,
                            HttpHeaders responseHeaders)
          throws IOException {
        // Handle error
        System.err.println(e.getMessage());
      }

      @Override
      public void onSuccess(Permission permission,
                            HttpHeaders responseHeaders)
          throws IOException {
        System.out.println("Permission ID: " + permission.getId());

        ids.add(permission.getId());

      }
    };
    BatchRequest batch = service.batch();
    Permission userPermission = new Permission()
        .setType("user")
        .setRole("writer");

    userPermission.setEmailAddress(realUser);
    try {
      service.permissions().create(realFileId, userPermission)
          .setFields("id")
          .queue(batch, callback);

      Permission domainPermission = new Permission()
          .setType("domain")
          .setRole("reader");

      domainPermission.setDomain(realDomain);

      service.permissions().create(realFileId, domainPermission)
          .setFields("id")
          .queue(batch, callback);

      batch.execute();

      return ids;
    } catch (GoogleJsonResponseException e) {
      // TODO(developer) - handle error appropriately
      System.err.println("Unable to modify permission: " + e.getDetails());
      throw e;
    }
  }
}

Python

drive/snippets/drive-v3/file_snippet/share_file.py
import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError


def share_file(real_file_id, real_user, real_domain):
  """Batch permission modification.
  Args:
      real_file_id: file Id
      real_user: User ID
      real_domain: Domain of the user ID
  Prints modified permissions

  Load pre-authorized user credentials from the environment.
  TODO(developer) - See https://developers.google.com/identity
  for guides on implementing OAuth2 for the application.
  """
  creds, _ = google.auth.default()

  try:
    # create drive api client
    service = build("drive", "v3", credentials=creds)
    ids = []
    file_id = real_file_id

    def callback(request_id, response, exception):
      if exception:
        # Handle error
        print(exception)
      else:
        print(f"Request_Id: {request_id}")
        print(f'Permission Id: {response.get("id")}')
        ids.append(response.get("id"))

    # pylint: disable=maybe-no-member
    batch = service.new_batch_http_request(callback=callback)
    user_permission = {
        "type": "user",
        "role": "writer",
        "emailAddress": "user@example.com",
    }
    batch.add(
        service.permissions().create(
            fileId=file_id,
            body=user_permission,
            fields="id",
        )
    )
    domain_permission = {
        "type": "domain",
        "role": "reader",
        "domain": "example.com",
    }
    domain_permission["domain"] = real_domain
    batch.add(
        service.permissions().create(
            fileId=file_id,
            body=domain_permission,
            fields="id",
        )
    )
    batch.execute()

  except HttpError as error:
    print(f"An error occurred: {error}")
    ids = None

  return ids


if __name__ == "__main__":
  share_file(
      real_file_id="1dUiRSoAQKkM3a4nTPeNQWgiuau1KdQ_l",
      real_user="gduser1@workspacesamples.dev",
      real_domain="workspacesamples.dev",
  )

Node.js

drive/snippets/drive_v3/file_snippets/share_file.js
import {GoogleAuth} from 'google-auth-library';
import {google} from 'googleapis';

/**
 * Shares a file with a user and a domain.
 * @param {string} fileId The ID of the file to share.
 * @param {string} targetUserEmail The email address of the user to share with.
 * @param {string} targetDomainName The domain to share with.
 * @return {Promise<Array<string>>} A promise that resolves to an array of permission IDs.
 */
async function shareFile(fileId, targetUserEmail, targetDomainName) {
  // Authenticate with Google and get an authorized client.
  // TODO (developer): Use an appropriate auth mechanism for your app.
  const auth = new GoogleAuth({
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Drive API client (v3).
  const service = google.drive({version: 'v3', auth});

  /** @type {Array<string>} */
  const permissionIds = [];

  // The permissions to create.
  const permissions = [
    {
      type: 'user',
      role: 'writer',
      emailAddress: targetUserEmail, // e.g., 'user@partner.com'
    },
    {
      type: 'domain',
      role: 'writer',
      domain: targetDomainName, // e.g., 'example.com'
    },
  ];

  // Iterate through the permissions and create them one by one.
  for (const permission of permissions) {
    const result = await service.permissions.create({
      requestBody: permission,
      fileId,
      fields: 'id',
    });

    if (result.data.id) {
      permissionIds.push(result.data.id);
      console.log(`Inserted permission id: ${result.data.id}`);
    } else {
      throw new Error('Failed to create permission');
    }
  }
  return permissionIds;
}

PHP

drive/snippets/drive_v3/src/DriveShareFile.php
<?php
use Google\Client;
use Google\Service\Drive;
function shareFile()
{
    try {
        $client = new Client();
        $client->useApplicationDefaultCredentials();
        $client->addScope(Drive::DRIVE);
        $driveService = new Drive($client);
        $realFileId = readline("Enter File Id: ");
        $realUser = readline("Enter user email address: ");
        $realDomain = readline("Enter domain name: ");
        $ids = array();
            $fileId = '1sTWaJ_j7PkjzaBWtNc3IzovK5hQf21FbOw9yLeeLPNQ';
            $fileId = $realFileId;
            $driveService->getClient()->setUseBatch(true);
            try {
                $batch = $driveService->createBatch();

                $userPermission = new Drive\Permission(array(
                    'type' => 'user',
                    'role' => 'writer',
                    'emailAddress' => 'user@example.com'
                ));
                $userPermission['emailAddress'] = $realUser;
                $request = $driveService->permissions->create(
                    $fileId, $userPermission, array('fields' => 'id'));
                $batch->add($request, 'user');
                $domainPermission = new Drive\Permission(array(
                    'type' => 'domain',
                    'role' => 'reader',
                    'domain' => 'example.com'
                ));
                $userPermission['domain'] = $realDomain;
                $request = $driveService->permissions->create(
                    $fileId, $domainPermission, array('fields' => 'id'));
                $batch->add($request, 'domain');
                $results = $batch->execute();

                foreach ($results as $result) {
                    if ($result instanceof Google_Service_Exception) {
                        // Handle error
                        printf($result);
                    } else {
                        printf("Permission ID: %s\n", $result->id);
                        array_push($ids, $result->id);
                    }
                }
            } finally {
                $driveService->getClient()->setUseBatch(false);
            }
            return $ids;
    } catch(Exception $e) {
        echo "Error Message: ".$e;
    }

}

.NET

drive/snippets/drive_v3/DriveV3Snippets/ShareFile.cs
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v3;
using Google.Apis.Drive.v3.Data;
using Google.Apis.Requests;
using Google.Apis.Services;

namespace DriveV3Snippets
{
    // Class to demonstrate use-case of Drive modify permissions.
    public class ShareFile
    {
        /// <summary>
        /// Batch permission modification.
        /// </summary>
        /// <param name="realFileId">File id.</param>
        /// <param name="realUser">User id.</param>
        /// <param name="realDomain">Domain id.</param>
        /// <returns>list of modified permissions, null otherwise.</returns>
        public static IList<String> DriveShareFile(string realFileId, string realUser, string realDomain)
        {
            try
            {
                /* Load pre-authorized user credentials from the environment.
                 TODO(developer) - See https://developers.google.com/identity for
                 guides on implementing OAuth2 for your application. */
                GoogleCredential credential = GoogleCredential.GetApplicationDefault()
                    .CreateScoped(DriveService.Scope.Drive);

                // Create Drive API service.
                var service = new DriveService(new BaseClientService.Initializer
                {
                    HttpClientInitializer = credential,
                    ApplicationName = "Drive API Snippets"
                });

                var ids = new List<String>();
                var batch = new BatchRequest(service);
                BatchRequest.OnResponse<Permission> callback = delegate(
                    Permission permission,
                    RequestError error,
                    int index,
                    HttpResponseMessage message)
                {
                    if (error != null)
                    {
                        // Handle error
                        Console.WriteLine(error.Message);
                    }
                    else
                    {
                        Console.WriteLine("Permission ID: " + permission.Id);
                    }
                };
                Permission userPermission = new Permission()
                {
                    Type = "user",
                    Role = "writer",
                    EmailAddress = realUser
                };

                var request = service.Permissions.Create(userPermission, realFileId);
                request.Fields = "id";
                batch.Queue(request, callback);

                Permission domainPermission = new Permission()
                {
                    Type = "domain",
                    Role = "reader",
                    Domain = realDomain
                };
                request = service.Permissions.Create(domainPermission, realFileId);
                request.Fields = "id";
                batch.Queue(request, callback);
                var task = batch.ExecuteAsync();
                task.Wait();
                return ids;
            }
            catch (Exception e)
            {
                // TODO(developer) - handle error appropriately
                if (e is AggregateException)
                {
                    Console.WriteLine("Credential Not found");
                }
                else
                {
                    throw;
                }
            }
            return null;
        }
    }
}

刪除權限

如要撤銷檔案或資料夾的存取權,請使用 fileId 和 permissionId 路徑參數,呼叫 permissions 資源的 delete 方法。

您無法直接撤銷子項目的沿用權限,請改為更新或刪除上層資料夾的權限 (或使用存取限制設定)。

請注意,從上層項目移除使用者的存取權,只會撤銷從該上層項目繼承的權限。如果使用者也獲得子項目的直接權限,則該直接存取權會保留。如要確認權限已移除,請使用 fileId 呼叫 list。

設定到期日

如要授予檔案或資料夾的臨時存取權,請在呼叫 create 或 update 方法時,設定 expirationTime 欄位 (RFC 3339 日期時間)。

有效期限有下列限制:

  • 只能針對 user 和 group 權限設定,不能針對 domain 或 anyone 權限設定。
  • 時間必須設在未來,最長為一年。
  • 如果是資料夾,只有 reader 角色才能取得臨時存取權。