Google 驱动器 API - 具有全域权限的服务帐户可以列出共享驱动器,但不能列出它们的内容

Google Drive API - Service account w/ domain-wide authority can list shared drives but not their contents

注意:我 不是 使用云客户端库,而是通过 Powershell 使用 Google 驱动器 API 和HTTP,going against the guides recommendations.


我正在尝试让 Powershell 脚本使用 Google 驱动器 API (v3) 执行简单的文件上传。我当前的 GCP 项目包括一个服务帐户,该帐户已被授予具有以下范围的全域权限:

按照“Preparing to make an authorized API call”指南,我成功创建了一个 JWT,使用所述 JWT 获取访问令牌,并使用所述访问令牌与API。我还确认该服务帐户能够成功“模拟”我们工作区中的用户帐户(使用指南“其他声明”部分中的 sub 参数以上)。

我面临的问题是关于权限的。我从 GET https://www.googleapis.com/drive/v3/drives 得到了成功的响应,并且可以看到模拟用户有权访问的共享驱动器。但是,在请求列出其中一个共享驱动器的内容时​​,我得到了 (403) Forbidden;具体通过 GET https://www.googleapis.com/drive/v3/files?driveId=<DRIVE_ID_HERE>?includeItemsFromAllDrives=true?supportsAllDrives=true?corpora=allDrives.

我的请求参数有问题吗?我很困惑为什么拥有共享驱动器内容管理器访问权限的用户能够列出共享驱动器而不是所述驱动器中的任何内容?


编辑 1:添加了 1) 构建 JWT 和访问令牌以及 2) Google 驱动 API 请求

的片段
JWT 和访问令牌
$now = (Get-Date).ToUniversalTime()
$createDate = [Math]::Floor([decimal](Get-Date($now) -UFormat '%s'))
$expiryDate = [Math]::Floor([decimal](Get-Date($now.AddHours(1)) -UFormat '%s'))

$payload = [Ordered]@{
    iss = 'service_account@xyz-123.iam.gserviceaccount.com'
    sub = 'account-user@gmail.com'
    scope = 'https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.file'
    aud = 'https://oauth2.googleapis.com/token'
    iat = $createDate
    exp = $expiryDate
} | ConvertTo-Json

$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("cert.pfx", 'pw')

# link to JWT module is in OP
$jwt = New-Jwt -PayloadJson $payload -Cert $cert -Verbose

$accessTokenResponse = Invoke-WebRequest 'https://oauth2.googleapis.com/token' `
    -UseBasicParsing `
    -Method 'POST' `
    -Headers @{'Content-Type' = 'application/x-www-form-urlencoded'} `
    -Body "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=$jwt" `
    -Verbose
$accessToken = ($accessTokenResponse.Content | ConvertFrom-Json).access_token
请求drives-list
$uploadResponse = Invoke-WebRequest 'https://www.googleapis.com/drive/v3/drives' `
    -UseBasicParsing `
    -Method 'GET' `
    -Headers @{'Authorization' = "Bearer $accessToken"'} `
    -Verbose
请求files-list
# Using driveId in the response from drives-list above
$uploadResponse = Invoke-WebRequest 'https://www.googleapis.com/drive/v3/files?driveId=<DRIVE_ID_HERE>?includeItemsFromAllDrives=true?supportsAllDrives=true?corpora=allDrives' `
    -UseBasicParsing `
    -Method 'GET' `
    -Headers @{'Authorization' = "Bearer $accessToken"'} `
    -Verbose

我从文件列表请求返回的 403 响应是:

PSMessageDetails      :
Exception             : System.Net.WebException: The remote server returned an error: (403) Forbidden.
                           at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.GetResponse(WebRequest request)
                           at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()
TargetObject          : System.Net.HttpWebRequest
CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
ErrorDetails          :
InvocationInfo        : System.Management.Automation.InvocationInfo
ScriptStackTrace      : at <ScriptBlock>, C:\...\GDriveAPITest.ps1: line 80
                        at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}

编辑 2:添加了片段以确定模拟是否有效

查看 answers/comments 后,我想确认模拟是否在我现有的代码中正常工作。在上面编辑 1 的片段中,我在创建访问令牌期间在 sub 参数中传递了要模拟的帐户。此步骤在文档中有概述,under Additional claims in the HTTP/REST guide.

然后我做了 GETabout-get 以确定模拟是否正在使用访问令牌。

请求获得
$uploadResponse = Invoke-WebRequest 'https://www.googleapis.com/drive/v3/about?fields=*' `
    -UseBasicParsing `
    -Method 'GET' `
    -Headers @{'Authorization' = "Bearer $accessToken"; 'Content-Type' = 'text/plain'} `
    -Verbose
来自 about-get 的响应(在 JSON 中)
{
  "kind": "drive#about",
  "user": {
    "kind": "drive#user",
    "displayName": "Impersonated User",
    "photoLink": "https://lh3.googleusercontent.com/a/default-user=s64",
    "me": true,
    "permissionId": "<PERMISSION_ID_HERE>",
    "emailAddress": "impersonated_user@gmail.com"
  },
  "storageQuota": { ... },
  "importFormats": { ... },
  "exportFormats": { ... },
  "maxImportSizes": { ... },
  "maxUploadSize": "0000000000000",
  "appInstalled": false,
  "folderColorPalette": [ ... ],
  "teamDriveThemes": [ ... ],
  "driveThemes": [ ... ],
  "canCreateTeamDrives": true,
  "canCreateDrives": true
}

这是否确认我的模拟工作正常?

模拟的用户是共享驱动器上的 Content Manager 并且驱动器 SDK 在 org/project 中启用。我要尝试的下一个更新是确认 Drive API 端点所需的所有范围都已分配给服务帐户 JWT 令牌。更新后再来汇报。

您是否配置了 domain wide delegation?即使模拟和一切都可能是正确的,如果没有域范围的委派,服务帐户不会获得足够的权限并可能显示此错误。

答案修改: 该问题实际上与用户模拟有关,因为它没有在代码中正确执行。

在这种情况下,创建服务帐户是不够的,执行全域委派也是不够的。您还必须模拟您域中的另一个用户。

服务帐户 授予 domain-wide 权限并稍后使用模拟的主要目的是让这些帐户能够代表用户访问数据您的域,否则 服务帐户 就像另一个帐户一样,它正在尝试从云端硬盘访问自己的数据。

如果问题出现在模拟部分,您可能需要仔细检查代码中设置的 scope/s 是否与您为服务帐户授予的匹配。

因此,例如,这些范围 https://www.googleapis.com/auth/drivehttps://www.googleapis.com/auth/drive.file 也应添加到服务帐户。

但是,由于您正在尝试访问共享驱动器,以下情况也可能会导致 403

  • 您的组织未启用 Drive SDK;

  • 您模拟的用户实际上无权访问共享 and/or 无法从中检索文件;

在这种情况下,检查已授予此共享驱动器的实际用户的权限会很有用。

参考

感谢 Fernando、John 和 ale13 的评论!

我能够确认我正在从共享驱动器上的 files-list 端点收到 (403) Forbidden,因为用户帐户正在被冒充。

  • 在我的服务帐户设置中,我启用了domain-wide委派
  • 通过创建 JWT 和访问令牌,我正确设置了模拟
  • 我在我的项目中启用了驱动器 API
  • 在我的服务帐户和令牌创建中,我将 full-scope 设置为 https://www.googleapis.com/auth/drive
  • 被模拟的用户帐户是工作区的有效成员,并且已添加到共享驱动器

但是,服务帐户在其中模拟的工作区中的用户帐户未完全正确设置。我在 Shared Drive root 上的 files-list 上看到 403,因为用户帐户 not 无法访问 Shared Drive root 下的所有内容。用户帐户的访问权限已更新,端点按预期返回 200。