权限 API
权限是在运行 deno 命令时从 CLI 授予的。用户代码通常会假设它自己的一组所需权限,但在执行期间,无法保证授予的权限集与之匹配。
在某些情况下,确保容错程序需要一种在运行时与权限系统交互的方法。
权限描述符 跳转到标题
在 CLI 上,/foo/bar 的读取权限表示为 --allow-read=/foo/bar。在运行时 JS 中,它表示为以下内容
const desc = { name: "read", path: "/foo/bar" } as const;
其他示例
// Global write permission.
const desc1 = { name: "write" } as const;
// Write permission to `$PWD/foo/bar`.
const desc2 = { name: "write", path: "foo/bar" } as const;
// Global net permission.
const desc3 = { name: "net" } as const;
// Net permission to 127.0.0.1:8000.
const desc4 = { name: "net", host: "127.0.0.1:8000" } as const;
// High-resolution time permission.
const desc5 = { name: "hrtime" } as const;
⚠️ 请参阅
PermissionDescriptor以获取 API 参考中的更多详细信息。
⚠️ 在 1.30 及更高版本中,所有下面描述的 API 都有同步 API 对应项(例如
Deno.permissions.querySync)。
查询权限 跳转到标题
通过描述符检查是否授予了权限。
// deno run --allow-read=/foo main.ts
const desc1 = { name: "read", path: "/foo" } as const;
console.log(await Deno.permissions.query(desc1));
// PermissionStatus { state: "granted", partial: false }
const desc2 = { name: "read", path: "/foo/bar" } as const;
console.log(await Deno.permissions.query(desc2));
// PermissionStatus { state: "granted", partial: false }
const desc3 = { name: "read", path: "/bar" } as const;
console.log(await Deno.permissions.query(desc3));
// PermissionStatus { state: "prompt", partial: false }
如果使用 --deny-read 标志限制了一些文件路径,则结果将包含 partial: true,表示并非所有子路径都授予了权限
// deno run --allow-read=/foo --deny-read=/foo/bar main.ts
const desc1 = { name: "read", path: "/foo" } as const;
console.log(await Deno.permissions.query(desc1));
// PermissionStatus { state: "granted", partial: true }
const desc2 = { name: "read", path: "/foo/bar" } as const;
console.log(await Deno.permissions.query(desc2));
// PermissionStatus { state: "denied", partial: false }
const desc3 = { name: "read", path: "/bar" } as const;
console.log(await Deno.permissions.query(desc3));
// PermissionStatus { state: "prompt", partial: false }
权限状态 跳转到标题
权限状态可以是“已授予”、“提示”或“拒绝”。从 CLI 授予的权限将查询为 { state: "granted" }。那些未授予的权限默认查询为 { state: "prompt" },而 { state: "denied" } 保留给那些被明确拒绝的权限。这将在 请求权限 中出现。
权限强度 跳转到标题
对 查询权限 中第二个查询结果的直观理解是,授予了对 /foo 的读取访问权限,而 /foo/bar 在 /foo 内,因此允许读取 /foo/bar。这确实如此,除非 CLI 授予的权限对查询的权限是部分的(作为使用 --deny-* 标志的结果)。
我们也可以说 desc1 比 desc2 更强。这意味着对于任何一组 CLI 授予的权限
- 如果
desc1查询为{ state: "granted", partial: false },那么desc2也必须如此。 - 如果
desc2查询为{ state: "denied", partial: false },那么desc1也必须如此。
更多示例
const desc1 = { name: "write" } as const;
// is stronger than
const desc2 = { name: "write", path: "/foo" } as const;
const desc3 = { name: "net", host: "127.0.0.1" } as const;
// is stronger than
const desc4 = { name: "net", host: "127.0.0.1:8000" } as const;
请求权限 跳转到标题
通过 CLI 提示从用户那里请求未授予的权限。
// deno run main.ts
const desc1 = { name: "read", path: "/foo" } as const;
const status1 = await Deno.permissions.request(desc1);
// ⚠️ Deno requests read access to "/foo". Grant? [y/n (y = yes allow, n = no deny)] y
console.log(status1);
// PermissionStatus { state: "granted", partial: false }
const desc2 = { name: "read", path: "/bar" } as const;
const status2 = await Deno.permissions.request(desc2);
// ⚠️ Deno requests read access to "/bar". Grant? [y/n (y = yes allow, n = no deny)] n
console.log(status2);
// PermissionStatus { state: "denied", partial: false }
如果当前权限状态为“提示”,则用户终端将出现一个提示,询问他们是否希望授予该请求。对 desc1 的请求已授予,因此返回其新状态,并且执行将继续,就像在 CLI 上指定了 --allow-read=/foo 一样。对 desc2 的请求被拒绝,因此其权限状态从“提示”降级为“拒绝”。
如果当前权限状态已经是“已授予”或“拒绝”,则该请求将表现得像一个查询,并且只返回当前状态。这可以防止对已授予的权限和之前拒绝的请求进行提示。
撤销权限 跳转到标题
将权限从“granted”降级到“prompt”。
// deno run --allow-read=/foo main.ts
const desc = { name: "read", path: "/foo" } as const;
console.log(await Deno.permissions.revoke(desc));
// PermissionStatus { state: "prompt", partial: false }
当您尝试撤销一个在 CLI 上部分授予的权限时会发生什么?
// deno run --allow-read=/foo main.ts
const desc = { name: "read", path: "/foo/bar" } as const;
console.log(await Deno.permissions.revoke(desc));
// PermissionStatus { state: "prompt", partial: false }
const cliDesc = { name: "read", path: "/foo" } as const;
console.log(await Deno.permissions.revoke(cliDesc));
// PermissionStatus { state: "prompt", partial: false }
CLI 授予的权限,它隐含了被撤销的权限,也被撤销了。
为了理解这种行为,想象一下 Deno 存储了一组内部的显式授予的权限描述符。在 CLI 上指定 --allow-read=/foo,/bar 会将这组初始化为
[
{ name: "read", path: "/foo" },
{ name: "read", path: "/bar" },
];
授予运行时请求 { name: "write", path: "/foo" } 会将这组更新为
[
{ name: "read", path: "/foo" },
{ name: "read", path: "/bar" },
{ name: "write", path: "/foo" },
];
Deno 的权限撤销算法通过从这组中删除所有比参数权限描述符更强的元素来工作。
Deno 不允许“碎片化”的权限状态,其中一些强权限被授予,但排除了由它隐含的弱权限。这样的系统在您考虑更广泛的用例和 "denied" 状态时会变得越来越复杂和不可预测。这是一个为了安全而牺牲粒度的权衡。