You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Workspace域管理员通过Apps Script管理用户Drive文件权限

解决方案:域管理员通过服务账户批量转移Drive文件所有权

核心问题分析

你的代码中存在两个关键问题:

  1. 调用Drive.Files.get()时使用的是脚本自身权限,而非服务账户模拟目标用户后的授权,导致无法访问用户私有文件;
  2. 服务账户的**域范围委派(Domain-Wide Delegation)**未正确配置,缺少操作域内用户Drive的权限。

一、完成服务账户域范围授权配置

这是实现功能的前提,必须完成以下步骤:

  • 在Google Cloud Console中找到你的服务账户,开启域范围委派,记录服务账户的客户端ID;
  • 登录Google Workspace Admin Console,进入「安全」→「API控制」→「域范围委派」,添加上述客户端ID,授权以下OAuth权限范围:
    • https://www.googleapis.com/auth/drive(全权限,满足所有权转移和权限修改需求)

二、修改后的完整脚本

使用Drive API v3(v2已弃用),所有操作通过服务账户授权的UrlFetchApp请求执行:

function transferDriveOwnership(sourceUserEmail, targetUserEmail) {
  const key = getJsonKey();
  const clientEmail = '你的服务账户邮箱@xxx.iam.gserviceaccount.com';
  
  const driveService = getDriveService_(key, sourceUserEmail, clientEmail);
  if (!driveService.hasAccess()) {
    throw new Error('服务账户授权失败:' + driveService.getLastError());
  }
  const accessToken = driveService.getAccessToken();
  
  // 获取用户所有文件和文件夹(处理分页)
  const allItems = getAllDriveItems(accessToken);
  
  // 批量处理每个项目
  allItems.forEach(item => {
    // 仅文件可转移所有权,文件夹所有权转移需特殊处理(或递归转移子文件)
    if (item.mimeType !== 'application/vnd.google-apps.folder') {
      transferOwnership(accessToken, item.id, targetUserEmail);
    }
    // 设置原所有者为仅查看者
    setViewerPermission(accessToken, item.id, sourceUserEmail);
  });
  
  Logger.log('处理完成,共操作 ' + allItems.length + ' 个项目');
}

// 递归获取所有Drive项目(含分页)
function getAllDriveItems(accessToken) {
  let allItems = [];
  let pageToken = null;
  
  do {
    const url = `https://www.googleapis.com/drive/v3/files?pageSize=1000&pageToken=${pageToken || ''}&fields=files(id,mimeType,name)&q="me" in owners`;
    const response = UrlFetchApp.fetch(url, {
      headers: { Authorization: 'Bearer ' + accessToken }
    });
    const result = JSON.parse(response.getContentText());
    allItems = allItems.concat(result.files || []);
    pageToken = result.nextPageToken;
  } while (pageToken);
  
  return allItems;
}

// 转移文件所有权(仅支持同域用户)
function transferOwnership(accessToken, fileId, targetEmail) {
  const url = `https://www.googleapis.com/drive/v3/files/${fileId}/permissions`;
  const payload = JSON.stringify({
    role: 'owner',
    type: 'user',
    emailAddress: targetEmail,
    transferOwnership: true
  });
  
  UrlFetchApp.fetch(url, {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + accessToken,
      'Content-Type': 'application/json'
    },
    payload: payload
  });
}

// 设置用户为仅查看者
function setViewerPermission(accessToken, itemId, userEmail) {
  // 先删除原所有者的现有权限(转移所有权后默认保留编辑权限)
  const permissionsUrl = `https://www.googleapis.com/drive/v3/files/${itemId}/permissions?fields=permissions(id,emailAddress,role)`;
  const permissionsResponse = UrlFetchApp.fetch(permissionsUrl, {
    headers: { Authorization: 'Bearer ' + accessToken }
  });
  const permissions = JSON.parse(permissionsResponse.getContentText()).permissions;
  
  const userPermission = permissions.find(p => p.emailAddress === userEmail);
  if (userPermission) {
    UrlFetchApp.fetch(`https://www.googleapis.com/drive/v3/files/${itemId}/permissions/${userPermission.id}`, {
      method: 'DELETE',
      headers: { Authorization: 'Bearer ' + accessToken }
    });
  }
  
  // 添加仅查看权限
  const payload = JSON.stringify({
    role: 'reader',
    type: 'user',
    emailAddress: userEmail
  });
  
  UrlFetchApp.fetch(`https://www.googleapis.com/drive/v3/files/${itemId}/permissions`, {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + accessToken,
      'Content-Type': 'application/json'
    },
    payload: payload
  });
}

// 加载服务账户JSON密钥
function getJsonKey(){
  const keyFile = DriveApp.getFileById("你的JSON密钥文件ID");
  const key = JSON.parse(keyFile.getBlob().getDataAsString()).private_key;
  return key;
}

// 创建OAuth2服务实例
function getDriveService_(key, userEmail, clientEmail) {
  return OAuth2.createService('GoogleDrive:' + userEmail)
      .setTokenUrl('https://oauth2.googleapis.com/token')
      .setPrivateKey(key)
      .setIssuer(clientEmail)
      .setSubject(userEmail)
      .setPropertyStore(PropertiesService.getUserProperties())
      .setCache(CacheService.getUserCache())
      .setScope('https://www.googleapis.com/auth/drive');
}

// 重置授权缓存(用于调试)
function resetAuth(userEmail) {
  OAuth2.createService('GoogleDrive:' + userEmail).reset();
}

三、关键说明

  1. 所有权限制:Google Drive仅允许将所有权转移给同域用户,跨域转移会失败;
  2. 文件夹处理:脚本中默认不转移文件夹所有权(Drive文件夹所有权转移需特殊配置,建议递归转移文件夹内的所有文件);
  3. 分页逻辑:自动处理Drive API的分页限制,确保获取用户所有Drive项目;
  4. 权限清理:转移所有权后,原所有者默认保留编辑权限,脚本会先删除该权限再添加仅查看权限。

四、使用方法

直接调用主函数并传入参数:

transferDriveOwnership('原所有者邮箱@你的域.com', '目标用户邮箱@你的域.com');

内容的提问来源于stack exchange,提问作者Francisco Cortes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 21:41:11