Google Workspace域管理员通过Apps Script管理用户Drive文件权限
解决方案:域管理员通过服务账户批量转移Drive文件所有权
核心问题分析
你的代码中存在两个关键问题:
- 调用
Drive.Files.get()时使用的是脚本自身权限,而非服务账户模拟目标用户后的授权,导致无法访问用户私有文件; - 服务账户的**域范围委派(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(); }
三、关键说明
- 所有权限制:Google Drive仅允许将所有权转移给同域用户,跨域转移会失败;
- 文件夹处理:脚本中默认不转移文件夹所有权(Drive文件夹所有权转移需特殊配置,建议递归转移文件夹内的所有文件);
- 分页逻辑:自动处理Drive API的分页限制,确保获取用户所有Drive项目;
- 权限清理:转移所有权后,原所有者默认保留编辑权限,脚本会先删除该权限再添加仅查看权限。
四、使用方法
直接调用主函数并传入参数:
transferDriveOwnership('原所有者邮箱@你的域.com', '目标用户邮箱@你的域.com');
内容的提问来源于stack exchange,提问作者Francisco Cortes
相关产品推荐
相关产品推荐

