Puter 文件共享权限审计指南:用 puter.fs.getShares() 查看谁能访问你的文件与目录

Puter 文件共享权限审计指南:用 puter.fs.getShares() 查看谁能访问你的文件与目录 Puter 文件共享权限审计指南用 puter.fs.getShares() 查看谁能访问你的文件与目录【免费下载链接】puter The Internet Computer! Free, Open-Source, and Self-Hostable.项目地址: https://gitcode.com/GitHub_Trending/pu/puterputer.fs.getShares()是 Puter 文件系统FS模块中用于“反向审计共享关系”的核心方法给定一个你拥有或拥有manage权限的文件或目录它会返回一份完整的授权清单告诉你谁能到达它、以什么模式到达、授权来自哪里。本文以 getShares.md 为骨架结合puter-jsSDK 与后端ShareController的实现细节完整讲解其语法、返回结构、邀请pending语义、继承授权行为并提供可直接运行的实战示例帮助你精确掌握 Puter 共享模型的边界与审计能力。适用前提谁可以调用、能查到什么getShares()用于列出“谁能访问某个文件或目录”调用者必须满足以下条件之一是该文件或目录的所有者拥有该对象的manage权限例如你被授予了对某个共享文件夹的manage权限。只有具备上述身份你才能读到该对象的共享关系清单。应用App能分享什么越权是设计上被禁止的Puter 的共享模型强调“App 永远不会获得超过用户授予它的权限”。文档中明确了 App 可触达的边界App 可以分享自己的 AppData以及用户明确授予它的文件分享的权限级别不超过 App 自身持有的级别——例如一个只有read权限的 App最多只能再授予read无法授予write或manage用户拥有但从未交给 App 的文件App 无法触达listShared()见 listShared.md对 App 只展示它能触达的那部分共享关系App 创建的共享归属于背后的用户同时携带issuedByApp标记便于所有者通过getShares()区分“用户本人创建”与“App 代为创建”。你能看到的共享由“谁授予”决定getShares()返回的清单不仅包含你自己授予的共享还包含任何持有该对象manage权限的人所授予的共享。这正是所有者查看“我信任的人又转授给了谁”的途径。语法与参数两种等价调用形式puter.fs.getShares(path); puter.fs.getShares(options);从 SDK 源码operations/getShares.js看该操作在defineOperation中声明了位置参数[path]并在请求构造时二选一写入查询参数优先使用uid否则把path交给getAbsolutePathForApp解析为绝对路径最终请求GET /share/shares?...。参数说明参数类型必需说明pathString二者必居其一文件或目录的路径。若为相对路径将相对 App 的根目录解析SDK 通过getAbsolutePathForApp完成归一化若在调用位置参数形式时path为第一个位置参数options.pathString仅以 options 作为唯一入参时必需与位置参数语义相同options.uidString二者必居其一以对象的 UID 定位条目可替代path使用若同时不提供uid与path后端会直接拒绝。见后端实现 ShareController.tsGET /share/shares校验后若target为空会抛出400 one of uid or path is requiredlegacyCode: bad_request。此外当以路径定位时后端会用expandTildePath处理~起始的路径。返回值结构逐个共享对象的字段getShares()返回一个Promise解析为一个共享对象数组。前端映射发生在 shareUtil.js 的toShare函数中它把服务端下发的行记录归一化为 SDK 公开的Share结构类型定义见 types.js。下表字段与该映射一一对应字段类型含义uidString这条共享记录的 UIDmodeString授予的访问模式read/write/manage等pathString被共享条目的路径entryUidString被共享条目本身目录项的 UID服务端字段为uid_entry或entryUidisDirBoolean被共享对象是否为目录服务端字段为is_dir或isDirissuerString | null这条共享由谁发出holderString | null持有访问权限的人邀请pending共享时为nullinheritedFromString | null该访问权限所继承自的“共享祖先”路径共享落在对象自身时值为nullissuedByAppString | null请求该共享的 App 的 UID由用户直接创建时值为nullmodifiedNumber修改时间戳sizeNumber | null大小用于文件浏览器渲染注toShare还会尽力携带name、type、thumbnail、owner等展示字段原因是共享清单背后没有可 stat 的 fsentry服务端直接在行记录中携带文件浏览器渲染所需的信息这些字段在其它场景下可能缺失。当记录处于pending状态服务端status pending或pending true时还会追加pending: true与recipientEmail。issuedByApp区分“人”与“App”的授权issuedByApp为 App 的 UID表示这条共享是某个 App 代替其背后的用户发出的若由用户直接操作创建则为null。这一字段让所有者在审计时可以一眼区分两类来源——这正是文档中“App 创建的共享归属用户但携带来源标记”的设计落地。继承授权与邀请两个必须理解的行为inheritedFrom 与“在祖先上托管”若某条访问权限不是直接落在当前条目上而是来自其某个共享祖先目录则该记录的inheritedFrom字段会给出那个祖先的路径若共享直接落在当前条目自身则为null。关键管理约束有两层当你不是所有者时inheritedFrom与path一样会被掩码处理防止非所有者探测完整的目录结构从父目录继承来的访问权限托管在那个父目录上——因此你无法在这里当前条目上撤除它因为该授权记录并不存在于当前条目。要收回继承授权必须到授权实际所在的祖先目录上操作。invitations面向未确认邮箱的待认领共享返回清单同时包含邀请invitations——即发送到某个邮箱地址、但该邮箱还没有对应已确认账户的共享这类记录携带pending: trueholder为null目标地址存放在recipientEmail中在收件人确认该邮箱之前它们不授予任何实际权限如需在认领前取消调用puter.fs.unshare()即可。错误与隐私行为不会确认“对象存在”如果调用者完全看不到该条目既非所有者也无manage权限getShares()的拒绝方式与请求一个不存在的文件完全相同——也就是说该 API 不会通过错误差异来向无权者确认对象是否存在避免信息泄露。完整实战示例示例一查看谁能访问一个文件先写入文件、用邮箱发起共享再列出所有共享并逐条打印持有者、模式与发出者html body script srchttps://js.puter.com/v2//script script (async () { await puter.fs.write(report.txt, Quarterly numbers); await puter.fs.share(report.txt, friendexample.com, read); const shares await puter.fs.getShares(report.txt); for (const share of shares) { puter.print(${share.holder}: ${share.mode} (from ${share.issuer})br); } })() /script /body /html输出将包含直接授予friendexample.com的共享若该邮箱账户未确认这条记录会以pending形式出现holder为null、recipientEmail为目标地址。示例二撤销所有人的访问配合puter.fs.share()与puter.fs.unshare()实现“一键清理”const shares await puter.fs.getShares(report.txt); for (const share of shares) { await puter.fs.unshare(report.txt, share.holder); }需要留意unshare对每个被撤对象的语义在 SDK 中有所体现见 operations/unshare.js——它把撤销请求发往POST /share/revoke并在响应后清空相关条目缓存item:path与readdir:dirname见 shareUtil.js 的 invalidateShareCache。无法对holder为null的待认领邀请直接用上面的循环撤销应针对邀请取消场景单独处理。示例三按来源过滤——区分人授予与 App 授予结合issuedByApp做更精细的审计展示const shares await puter.fs.getShares(/Documents/project); for (const share of shares) { if (share.issuedByApp) { puter.print(App ${share.issuedByApp} 授予了 ${share.holder} ${share.mode}由 ${share.issuer} 发出); } else { puter.print(${share.issuer} 直接授予了 ${share.holder} ${share.mode}); } }底层实现从 SDK 到后端的一条链路getShares()的实现与源码证据可以归纳为一条清晰链路SDK 层operations/getShares.js声明位置参数path用URLSearchParams构造uid或path查询串向GET /share/shares发起请求再把响应体{ items: [...] }逐项经toShare映射为Share[]返回。后端路由层ShareController.tsGET /share/shares装饰器声明了subdomain: api、requireVerified: true要求已验证邮箱与rateLimit: SHARE_LIST_LIMIT。处理器要求uid与path二选一随后调用共享服务层的listSharesOf(actor, target)最后同样经统一的#toClientShare转换后返回{ items }信封。服务层listSharesOf承担实际查询逻辑相关测试如 ShareService.test.ts、ShareConsistency.test.ts对其按权限遍历与一致性进行了专门验证。与它对照的两个“宏观视角”接口也值得了解实现于同一 ShareController.tsGET /share/shared-by-me分页列出调用者分享出去的全部内容支持appUid过滤到某个 App 或none用户本人创建适用于调用者不知道要问哪个条目的场景而GET /share/shares一次只能针对一个条目GET /share/shared-by-me/apps列出持有调用者所建共享的 App 及计数是查询appUid的入口。SDK 侧对应的两种“自己视角”能力见 listSharedByMe.md 与 listShared.md。边界与注意事项小结身份要求必须是所有者或持有manage权限否则按“文件不存在”拒绝相对路径非绝对路径会相对 App 根目录解析SDK 层或处理~后端expandTildePath范围覆盖结果包含所有manage持有者转授的共享不只你自己继承 vs 直授inheritedFrom标明来源继承授权在祖先上托管不能在当前条目撤除邀请语义pending: true、holder: null、地址在recipientEmail认领前不授任何权限可用unshare()提前取消App 边界App 只能分享自身 AppData 与被用户授予的文件且授予级别不超过自身所持级别共享归用户所有并携带issuedByApp来源标记。相关 APIputer.fs.share()— 授予访问权限puter.fs.unshare()— 撤回访问权限puter.fs.listSharedByMe()— 分页查看自己分享出去的内容puter.fs.listShared()— 查看共享给我的内容【免费下载链接】puter The Internet Computer! Free, Open-Source, and Self-Hostable.项目地址: https://gitcode.com/GitHub_Trending/pu/puter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考