权限 API

Permission APIs

这是一个不稳定的 Deno API。 更多信息请查阅 稳定性

权限是在运行 deno 命令时从 CLI 授予的。用户代码通常会假定自己拥有一组必需的权限,但是在执行过程中不能保证 已授予 的权限集合中包含所需权限。

在某些情况下,具有容错能力的程序需要一种在运行时与权限系统进行交互的方法。

权限描述符

在 CLI 中,/foo/bar 的读取权限表示为 --allow-read=/foo/bar

在运行时,它表示为以下形式:

  1. const desc = { name: "read", path: "/foo/bar" };

其他示例:

  1. // 全局写入权限
  2. const desc1 = { name: "write" };
  3. // `$PWD/foo/bar` 的写入权限
  4. const desc2 = { name: "write", path: "foo/bar" };
  5. // 全局网络权限
  6. const desc3 = { name: "net" };
  7. // 访问 127.0.0.1:8000 的网络权限
  8. const desc4 = { name: "net", url: "127.0.0.1:8000" };
  9. // 高精度计时权限
  10. const desc5 = { name: "hrtime" };

检查权限

检查一个权限是否已被授予:

  1. // deno run --unstable --allow-read=/foo main.ts
  2. const desc1 = { name: "read", path: "/foo" };
  3. console.log(await Deno.permissions.query(desc1));
  4. // PermissionStatus { state: "granted" }
  5. const desc2 = { name: "read", path: "/foo/bar" };
  6. console.log(await Deno.permissions.query(desc2));
  7. // PermissionStatus { state: "granted" }
  8. const desc3 = { name: "read", path: "/bar" };
  9. console.log(await Deno.permissions.query(desc3));
  10. // PermissionStatus { state: "prompt" }

权限状态

权限状态可以是以下之一:

  • granted (已授予)
  • prompt(提示)
  • denied(已禁止)

从 CLI 授予的权限是 { state: "granted" },其他未授予的权限默认为 { state: "prompt" },明确禁止的权限是 { state: "denied" }

相关说明在 请求权限

权限强度

检查权限 中第二个查询的直观理解:读取权限授予到 /foo/foo/bar 包含在 /foo 中,所以允许读取 /foo/bar

我们也可以说 desc1 强于 desc2。这意味着对于任意从 CLI 授予的权限:

  • 如果 desc1 状态为 { state: "granted" },那么 desc2 也是。
  • 如果 desc2 状态为 { state: "denied" },那么 desc1 也是。

更多示例:

  1. const desc1 = { name: "write" };
  2. // 强于
  3. const desc2 = { name: "write", path: "/foo" };
  4. const desc3 = { name: "net" };
  5. // 强于
  6. const desc4 = { name: "net", url: "127.0.0.1:8000" };

请求权限

通过 CLI 提示来请求一个未授予的权限:

  1. // deno run --unstable main.ts
  2. const desc1 = { name: "read", path: "/foo" };
  3. const status1 = await Deno.permissions.request(desc1);
  4. // ⚠️ Deno requests read access to "/foo". Grant? [g/d (g = grant, d = deny)] g
  5. console.log(status1);
  6. // PermissionStatus { state: "granted" }
  7. const desc2 = { name: "read", path: "/bar" };
  8. const status2 = await Deno.permissions.request(desc2);
  9. // ⚠️ Deno requests read access to "/bar". Grant? [g/d (g = grant, d = deny)] d
  10. console.log(status2);
  11. // PermissionStatus { state: "denied" }

如果当前权限状态是 "prompt",用户终端中会出现一个提示来询问用户是否要授权。desc1 的授权请求通过,所以新的状态会返回,程序继续执行。desc2 的授权被禁止,权限状态会从 "prompt" 降级为 "denied"。

如果当前权限状态已经是 "granted" 或 "denied",请求将表现为一个检查,直接返回当前权限状态,这会阻止多余的提示。

放弃权限

将权限状态从 "granted" 降级为 "prompt":

  1. // deno run --unstable --allow-read=/foo main.ts
  2. const desc = { name: "read", path: "/foo" };
  3. console.log(await Deno.permissions.revoke(desc));
  4. // PermissionStatus { state: "prompt" }

然而,当尝试放弃 一部分 权限时,会发生什么?

  1. // deno run --unstable --allow-read=/foo main.ts
  2. const desc = { name: "read", path: "/foo/bar" };
  3. console.log(await Deno.permissions.revoke(desc));
  4. // PermissionStatus { state: "granted" }

它不会被放弃。

想象 Deno 存储了一个 明确授予的权限描述符 的内部集合。在 CLI 中指定 --allow-read=/foo,/bar 会把这一集合初始化为:

  1. [
  2. { name: "read", path: "/foo" },
  3. { name: "read", path: "/bar" },
  4. ];

一个运行时授权 { name: "write", path: "/foo" } 会更新这个集合:

  1. [
  2. { name: "read", path: "/foo" },
  3. { name: "read", path: "/bar" },
  4. { name: "write", path: "/foo" },
  5. ];

Deno 的权限撤销算法将所有不强于参数权限描述符的元素从集合中移除。

要确保 desc 不再有效,您需要传递一个参数权限描述符,它必须 强于 集合中所有 强于 desc显式授予的权限描述符

  1. // deno run --unstable --allow-read=/foo main.ts
  2. const desc = { name: "read", path: "/foo/bar" };
  3. console.log(await Deno.permissions.revoke(desc)); // 无效
  4. // PermissionStatus { state: "granted" }
  5. const strongDesc = { name: "read", path: "/foo" };
  6. await Deno.permissions.revoke(strongDesc); // 正确
  7. console.log(await Deno.permissions.query(desc));
  8. // PermissionStatus { state: "prompt" }