diff options
Diffstat (limited to 'docs')
-rw-r--r-- | docs/examples/permissions.md | 28 | ||||
-rw-r--r-- | docs/runtime/compiler_apis.md | 6 | ||||
-rw-r--r-- | docs/runtime/permission_apis.md | 189 | ||||
-rw-r--r-- | docs/toc.json | 2 |
4 files changed, 193 insertions, 32 deletions
diff --git a/docs/examples/permissions.md b/docs/examples/permissions.md deleted file mode 100644 index 78dfcc59d..000000000 --- a/docs/examples/permissions.md +++ /dev/null @@ -1,28 +0,0 @@ -## Inspecting and revoking permissions - -> This program makes use of an unstable Deno feature. Learn more about -> [unstable features](../runtime/stability.md). - -Sometimes a program may want to revoke previously granted permissions. When a -program, at a later stage, needs those permissions, it will fail. - -```ts -// lookup a permission -const status = await Deno.permissions.query({ name: "write" }); -if (status.state !== "granted") { - throw new Error("need write permission"); -} - -const log = await Deno.open("request.log", { write: true, append: true }); - -// revoke some permissions -await Deno.permissions.revoke({ name: "read" }); -await Deno.permissions.revoke({ name: "write" }); - -// use the log file -const encoder = new TextEncoder(); -await log.write(encoder.encode("hello\n")); - -// this will fail. -await Deno.remove("request.log"); -``` diff --git a/docs/runtime/compiler_apis.md b/docs/runtime/compiler_apis.md index d9a49a01c..3424c2b5f 100644 --- a/docs/runtime/compiler_apis.md +++ b/docs/runtime/compiler_apis.md @@ -1,7 +1,7 @@ -## Compiler API +## Compiler APIs -> This is an unstable Deno feature. Learn more about -> [unstable features](./stability.md). +> This API is unstable. Learn more about +> [unstable features](../runtime/stability.md). Deno supports runtime access to the built-in TypeScript compiler. There are three methods in the `Deno` namespace that provide this access. diff --git a/docs/runtime/permission_apis.md b/docs/runtime/permission_apis.md new file mode 100644 index 000000000..a76f0c0d4 --- /dev/null +++ b/docs/runtime/permission_apis.md @@ -0,0 +1,189 @@ +## Permission APIs + +> This API is unstable. Learn more about +> [unstable features](../runtime/stability.md). + +Permissions are granted from the CLI when running the `deno` command. User code +will often assume its own set of required permissions, but there is no guarantee +during execution that the set of _granted_ permissions will align with this. + +In some cases, ensuring a fault-tolerant program requires a way to interact with +the permission system at runtime. + +### Permission descriptors + +On the CLI, read permission for `/foo/bar` is represented as +`--allow-read=/foo/bar`. In runtime JS, it is represented as the following: + +```ts +const desc = { name: "read", path: "/foo/bar" }; +``` + +Other examples: + +```ts +// Global write permission. +const desc1 = { name: "write" }; + +// Write permission to `$PWD/foo/bar`. +const desc2 = { name: "write", path: "foo/bar" }; + +// Global net permission. +const desc3 = { name: "net" }; + +// Net permission to 127.0.0.1:8000. +const desc4 = { name: "net", url: "127.0.0.1:8000" }; + +// High-resolution time permission. +const desc5 = { name: "hrtime" }; +``` + +### Query permissions + +Check, by descriptor, if a permission is granted or not. + +```ts +// deno run --unstable --allow-read=/foo main.ts + +const desc1 = { name: "read", path: "/foo" }; +console.log(await Deno.permissions.query(desc1)); +// PermissionStatus { state: "granted" } + +const desc2 = { name: "read", path: "/foo/bar" }; +console.log(await Deno.permissions.query(desc2)); +// PermissionStatus { state: "granted" } + +const desc3 = { name: "read", path: "/bar" }; +console.log(await Deno.permissions.query(desc3)); +// PermissionStatus { state: "prompt" } +``` + +### Permission states + +A permission state can be either "granted", "prompt" or "denied". Permissions +which have been granted from the CLI will query to `{ state: "granted" }`. Those +which have not been granted query to `{ state: "prompt" }` by default, while +`{ state: "denied" }` reserved for those which have been explicitly refused. +This will come up in [Request permissions](#request-permissions). + +### Permission strength + +The intuitive understanding behind the result of the second query in +[Query permissions](#query-permissions) is that read access was granted to +`/foo` and `/foo/bar` is within `/foo` so `/foo/bar` is allowed to be read. + +We can also say that `desc1` is +_[stronger than](https://www.w3.org/TR/permissions/#ref-for-permissiondescriptor-stronger-than)_ +`desc2`. This means that for any set of CLI-granted permissions: + +1. If `desc1` queries to `{ state: "granted" }` then so must `desc2`. +2. If `desc2` queries to `{ state: "denied" }` then so must `desc1`. + +More examples: + +```ts +const desc1 = { name: "write" }; +// is stronger than +const desc2 = { name: "write", path: "/foo" }; + +const desc3 = { name: "net" }; +// is stronger than +const desc4 = { name: "net", url: "127.0.0.1:8000" }; +``` + +### Request permissions + +Request an ungranted permission from the user via CLI prompt. + +```ts +// deno run --unstable main.ts + +const desc1 = { name: "read", path: "/foo" }; +const status1 = await Deno.permissions.request(desc1); +// ⚠️ Deno requests read access to "/foo". Grant? [g/d (g = grant, d = deny)] g +console.log(status1); +// PermissionStatus { state: "granted" } + +const desc2 = { name: "read", path: "/bar" }; +const status2 = await Deno.permissions.request(desc2); +// ⚠️ Deno requests read access to "/bar". Grant? [g/d (g = grant, d = deny)] d +console.log(status2); +// PermissionStatus { state: "denied" } +``` + +If the current permission state is "prompt", a prompt will appear on the user's +terminal asking them if they would like to grant the request. The request for +`desc1` was granted so its new status is returned and execution will continue as +if `--allow-read=/foo` was specified on the CLI. The request for `desc2` was +denied so its permission state is downgraded from "prompt" to "denied". + +If the current permission state is already either "granted" or "denied", the +request will behave like a query and just return the current status. This +prevents prompts both for already granted permissions and previously denied +requests. + +### Revoke permissions + +Downgrade a permission from "granted" to "prompt". + +```ts +// deno run --unstable --allow-read=/foo main.ts + +const desc = { name: "read", path: "/foo" }; +console.log(await Deno.permissions.revoke(desc)); +// PermissionStatus { state: "prompt" } +``` + +However, what happens when you try to revoke a permission which is _partial_ to +one granted on the CLI? + +```ts +// deno run --unstable --allow-read=/foo main.ts + +const desc = { name: "read", path: "/foo/bar" }; +console.log(await Deno.permissions.revoke(desc)); +// PermissionStatus { state: "granted" } +``` + +It was not revoked. + +To understand this behaviour, imagine that Deno stores an internal set of +_explicitly granted permission descriptors_. Specifying `--allow-read=/foo,/bar` +on the CLI initializes this set to: + +```ts +[ + { name: "read", path: "/foo" }, + { name: "read", path: "/bar" }, +]; +``` + +Granting a runtime request for `{ name: "write", path: "/foo" }` updates the set +to: + +```ts +[ + { name: "read", path: "/foo" }, + { name: "read", path: "/bar" }, + { name: "write", path: "/foo" }, +]; +``` + +Deno's permission revocation algorithm works by removing every element from this +set which the argument permission descriptor is _stronger than_. So to ensure +`desc` is not longer granted, pass an argument descriptor _stronger than_ +whichever _explicitly granted permission descriptor_ is _stronger than_ `desc`. + +```ts +// deno run --unstable --allow-read=/foo main.ts + +const desc = { name: "read", path: "/foo/bar" }; +console.log(await Deno.permissions.revoke(desc)); // Insufficient. +// PermissionStatus { state: "granted" } + +const strongDesc = { name: "read", path: "/foo" }; +await Deno.permissions.revoke(strongDesc); // Good. + +console.log(await Deno.permissions.query(desc)); +// PermissionStatus { state: "prompt" } +``` diff --git a/docs/toc.json b/docs/toc.json index 0e5cd0153..2df5be811 100644 --- a/docs/toc.json +++ b/docs/toc.json @@ -19,6 +19,7 @@ "children": { "stability": "Stability", "program_lifecycle": "Program lifecycle", + "permission_apis": "Permission APIs", "compiler_apis": "Compiler APIs", "workers": "Workers" } @@ -75,7 +76,6 @@ "file_server": "File server", "tcp_echo": "TCP echo server", "subprocess": "Creating a subprocess", - "permissions": "Inspecting and revoking permissions", "os_signals": "OS Signals", "file_system_events": "File system events", "testing_if_main": "Checking if file is main" |