mirror of
https://github.com/denoland/deno.git
synced 2024-12-26 09:10:40 -05:00
feat: Add sync APIs for "Deno.permissions" (#17019)
This commit adds sync versions of async APIs to "Deno.permissions" namespace. Following APIs were added: - "Deno.permissions.querySync" - "Deno.permissions.requestSync" - "Deno.permissions.revokeSync"
This commit is contained in:
parent
f14ea3d4d4
commit
900929f65c
9 changed files with 284 additions and 15 deletions
|
@ -558,6 +558,21 @@ fn _090_run_permissions_request() {
|
|||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn _090_run_permissions_request_sync() {
|
||||
let args = "run --quiet run/090_run_permissions_request_sync.ts";
|
||||
use util::PtyData::*;
|
||||
util::test_pty2(args, vec![
|
||||
Output("⚠️ ️Deno requests run access to \"ls\". Run again with --allow-run to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
|
||||
Input("y\n"),
|
||||
Output("⚠️ ️Deno requests run access to \"cat\". Run again with --allow-run to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
|
||||
Input("n\n"),
|
||||
Output("granted\r\n"),
|
||||
Output("prompt\r\n"),
|
||||
Output("denied\r\n"),
|
||||
]);
|
||||
}
|
||||
|
||||
itest!(_091_use_define_for_class_fields {
|
||||
args: "run --check run/091_use_define_for_class_fields.ts",
|
||||
output: "run/091_use_define_for_class_fields.ts.out",
|
||||
|
@ -2272,6 +2287,21 @@ mod permissions {
|
|||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn _061_permissions_request_sync() {
|
||||
let args = "run --quiet run/061_permissions_request_sync.ts";
|
||||
use util::PtyData::*;
|
||||
util::test_pty2(args, vec![
|
||||
Output("⚠️ ️Deno requests read access to \"foo\". Run again with --allow-read to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)] "),
|
||||
Input("y\n"),
|
||||
Output("⚠️ ️Deno requests read access to \"bar\". Run again with --allow-read to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)]"),
|
||||
Input("n\n"),
|
||||
Output("granted\r\n"),
|
||||
Output("prompt\r\n"),
|
||||
Output("denied\r\n"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn _062_permissions_request_global() {
|
||||
let args = "run --quiet run/062_permissions_request_global.ts";
|
||||
|
@ -2285,16 +2315,39 @@ mod permissions {
|
|||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn _062_permissions_request_global_sync() {
|
||||
let args = "run --quiet run/062_permissions_request_global_sync.ts";
|
||||
use util::PtyData::*;
|
||||
util::test_pty2(args, vec![
|
||||
Output("⚠️ ️Deno requests read access. Run again with --allow-read to bypass this prompt.\r\n Allow? [y/n (y = yes allow, n = no deny)] "),
|
||||
Input("y\n"),
|
||||
Output("PermissionStatus { state: \"granted\", onchange: null }\r\n"),
|
||||
Output("PermissionStatus { state: \"granted\", onchange: null }\r\n"),
|
||||
Output("PermissionStatus { state: \"granted\", onchange: null }\r\n"),
|
||||
]);
|
||||
}
|
||||
|
||||
itest!(_063_permissions_revoke {
|
||||
args: "run --allow-read=foo,bar run/063_permissions_revoke.ts",
|
||||
output: "run/063_permissions_revoke.ts.out",
|
||||
});
|
||||
|
||||
itest!(_063_permissions_revoke_sync {
|
||||
args: "run --allow-read=foo,bar run/063_permissions_revoke_sync.ts",
|
||||
output: "run/063_permissions_revoke.ts.out",
|
||||
});
|
||||
|
||||
itest!(_064_permissions_revoke_global {
|
||||
args: "run --allow-read=foo,bar run/064_permissions_revoke_global.ts",
|
||||
output: "run/064_permissions_revoke_global.ts.out",
|
||||
});
|
||||
|
||||
itest!(_064_permissions_revoke_global_sync {
|
||||
args: "run --allow-read=foo,bar run/064_permissions_revoke_global_sync.ts",
|
||||
output: "run/064_permissions_revoke_global.ts.out",
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn _066_prompt() {
|
||||
let args = "run --quiet --unstable run/066_prompt.ts";
|
||||
|
|
8
cli/tests/testdata/run/061_permissions_request_sync.ts
vendored
Normal file
8
cli/tests/testdata/run/061_permissions_request_sync.ts
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
const status1 =
|
||||
Deno.permissions.requestSync({ name: "read", path: "foo" }).state;
|
||||
const status2 = Deno.permissions.querySync({ name: "read", path: "bar" }).state;
|
||||
const status3 =
|
||||
Deno.permissions.requestSync({ name: "read", path: "bar" }).state;
|
||||
console.log(status1);
|
||||
console.log(status2);
|
||||
console.log(status3);
|
6
cli/tests/testdata/run/062_permissions_request_global_sync.ts
vendored
Normal file
6
cli/tests/testdata/run/062_permissions_request_global_sync.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
const status1 = Deno.permissions.requestSync({ name: "read" });
|
||||
console.log(status1);
|
||||
const status2 = Deno.permissions.querySync({ name: "read", path: "foo" });
|
||||
console.log(status2);
|
||||
const status3 = Deno.permissions.querySync({ name: "read", path: "bar" });
|
||||
console.log(status3);
|
6
cli/tests/testdata/run/063_permissions_revoke_sync.ts
vendored
Normal file
6
cli/tests/testdata/run/063_permissions_revoke_sync.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
const status1 = Deno.permissions.revokeSync({ name: "read", path: "foo" });
|
||||
console.log(status1);
|
||||
const status2 = Deno.permissions.querySync({ name: "read", path: "bar" });
|
||||
console.log(status2);
|
||||
const status3 = Deno.permissions.revokeSync({ name: "read", path: "bar" });
|
||||
console.log(status3);
|
6
cli/tests/testdata/run/064_permissions_revoke_global_sync.ts
vendored
Normal file
6
cli/tests/testdata/run/064_permissions_revoke_global_sync.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
const status1 = Deno.permissions.revokeSync({ name: "read" });
|
||||
console.log(status1);
|
||||
const status2 = Deno.permissions.querySync({ name: "read", path: "foo" });
|
||||
console.log(status2);
|
||||
const status3 = Deno.permissions.querySync({ name: "read", path: "bar" });
|
||||
console.log(status3);
|
18
cli/tests/testdata/run/090_run_permissions_request_sync.ts
vendored
Normal file
18
cli/tests/testdata/run/090_run_permissions_request_sync.ts
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
const status1 =
|
||||
Deno.permissions.requestSync({ name: "run", command: "ls" }).state;
|
||||
if (status1 != "granted") {
|
||||
throw Error(`unexpected status1 ${status1}`);
|
||||
}
|
||||
const status2 =
|
||||
Deno.permissions.querySync({ name: "run", command: "cat" }).state;
|
||||
if (status2 != "prompt") {
|
||||
throw Error(`unexpected status2 ${status2}`);
|
||||
}
|
||||
const status3 =
|
||||
Deno.permissions.requestSync({ name: "run", command: "cat" }).state;
|
||||
if (status3 != "denied") {
|
||||
throw Error(`unexpected status3 ${status3}`);
|
||||
}
|
||||
console.log(status1);
|
||||
console.log(status2);
|
||||
console.log(status3);
|
|
@ -13,12 +13,25 @@ Deno.test(async function permissionInvalidName() {
|
|||
}, TypeError);
|
||||
});
|
||||
|
||||
Deno.test(function permissionInvalidNameSync() {
|
||||
assertThrows(() => {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
Deno.permissions.querySync({ name: "foo" as any });
|
||||
}, TypeError);
|
||||
});
|
||||
|
||||
Deno.test(async function permissionNetInvalidHost() {
|
||||
await assertRejects(async () => {
|
||||
await Deno.permissions.query({ name: "net", host: ":" });
|
||||
}, URIError);
|
||||
});
|
||||
|
||||
Deno.test(function permissionNetInvalidHostSync() {
|
||||
assertThrows(() => {
|
||||
Deno.permissions.querySync({ name: "net", host: ":" });
|
||||
}, URIError);
|
||||
});
|
||||
|
||||
Deno.test(async function permissionSysValidKind() {
|
||||
await Deno.permissions.query({ name: "sys", kind: "loadavg" });
|
||||
await Deno.permissions.query({ name: "sys", kind: "osRelease" });
|
||||
|
@ -30,6 +43,16 @@ Deno.test(async function permissionSysValidKind() {
|
|||
await Deno.permissions.query({ name: "sys", kind: "gid" });
|
||||
});
|
||||
|
||||
Deno.test(function permissionSysValidKindSync() {
|
||||
Deno.permissions.querySync({ name: "sys", kind: "loadavg" });
|
||||
Deno.permissions.querySync({ name: "sys", kind: "osRelease" });
|
||||
Deno.permissions.querySync({ name: "sys", kind: "networkInterfaces" });
|
||||
Deno.permissions.querySync({ name: "sys", kind: "systemMemoryInfo" });
|
||||
Deno.permissions.querySync({ name: "sys", kind: "hostname" });
|
||||
Deno.permissions.querySync({ name: "sys", kind: "uid" });
|
||||
Deno.permissions.querySync({ name: "sys", kind: "gid" });
|
||||
});
|
||||
|
||||
Deno.test(async function permissionSysInvalidKind() {
|
||||
await assertRejects(async () => {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
|
@ -37,6 +60,13 @@ Deno.test(async function permissionSysInvalidKind() {
|
|||
}, TypeError);
|
||||
});
|
||||
|
||||
Deno.test(function permissionSysInvalidKindSync() {
|
||||
assertThrows(() => {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
Deno.permissions.querySync({ name: "sys", kind: "abc" as any });
|
||||
}, TypeError);
|
||||
});
|
||||
|
||||
Deno.test(async function permissionQueryReturnsEventTarget() {
|
||||
const status = await Deno.permissions.query({ name: "hrtime" });
|
||||
assert(["granted", "denied", "prompt"].includes(status.state));
|
||||
|
@ -49,6 +79,18 @@ Deno.test(async function permissionQueryReturnsEventTarget() {
|
|||
assert(status === (await Deno.permissions.query({ name: "hrtime" })));
|
||||
});
|
||||
|
||||
Deno.test(function permissionQueryReturnsEventTargetSync() {
|
||||
const status = Deno.permissions.querySync({ name: "hrtime" });
|
||||
assert(["granted", "denied", "prompt"].includes(status.state));
|
||||
let called = false;
|
||||
status.addEventListener("change", () => {
|
||||
called = true;
|
||||
});
|
||||
status.dispatchEvent(new Event("change"));
|
||||
assert(called);
|
||||
assert(status === Deno.permissions.querySync({ name: "hrtime" }));
|
||||
});
|
||||
|
||||
Deno.test(async function permissionQueryForReadReturnsSameStatus() {
|
||||
const status1 = await Deno.permissions.query({
|
||||
name: "read",
|
||||
|
@ -61,6 +103,18 @@ Deno.test(async function permissionQueryForReadReturnsSameStatus() {
|
|||
assert(status1 === status2);
|
||||
});
|
||||
|
||||
Deno.test(function permissionQueryForReadReturnsSameStatusSync() {
|
||||
const status1 = Deno.permissions.querySync({
|
||||
name: "read",
|
||||
path: ".",
|
||||
});
|
||||
const status2 = Deno.permissions.querySync({
|
||||
name: "read",
|
||||
path: ".",
|
||||
});
|
||||
assert(status1 === status2);
|
||||
});
|
||||
|
||||
Deno.test(function permissionsIllegalConstructor() {
|
||||
assertThrows(() => new Deno.Permissions(), TypeError, "Illegal constructor.");
|
||||
assertEquals(Deno.Permissions.length, 0);
|
||||
|
@ -85,6 +139,21 @@ Deno.test(async function permissionURL() {
|
|||
await Deno.permissions.query({ name: "run", command: path });
|
||||
});
|
||||
|
||||
Deno.test(function permissionURLSync() {
|
||||
Deno.permissions.querySync({
|
||||
name: "read",
|
||||
path: new URL(".", import.meta.url),
|
||||
});
|
||||
Deno.permissions.querySync({
|
||||
name: "write",
|
||||
path: new URL(".", import.meta.url),
|
||||
});
|
||||
Deno.permissions.querySync({
|
||||
name: "run",
|
||||
command: new URL(".", import.meta.url),
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test(async function permissionDescriptorValidation() {
|
||||
for (const value of [undefined, null, {}]) {
|
||||
for (const method of ["query", "request", "revoke"]) {
|
||||
|
@ -100,6 +169,21 @@ Deno.test(async function permissionDescriptorValidation() {
|
|||
}
|
||||
});
|
||||
|
||||
Deno.test(function permissionDescriptorValidationSync() {
|
||||
for (const value of [undefined, null, {}]) {
|
||||
for (const method of ["querySync", "revokeSync", "requestSync"]) {
|
||||
assertThrows(
|
||||
() => {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
(Deno.permissions as any)[method](value as any);
|
||||
},
|
||||
TypeError,
|
||||
'"undefined" is not a valid permission name',
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/denoland/deno/issues/15894.
|
||||
Deno.test(async function permissionStatusObjectsNotEqual() {
|
||||
assert(
|
||||
|
@ -107,3 +191,10 @@ Deno.test(async function permissionStatusObjectsNotEqual() {
|
|||
await Deno.permissions.query({ name: "env", variable: "B" }),
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test(function permissionStatusObjectsNotEqualSync() {
|
||||
assert(
|
||||
Deno.permissions.querySync({ name: "env", variable: "A" }) !=
|
||||
Deno.permissions.querySync({ name: "env", variable: "B" }),
|
||||
);
|
||||
});
|
||||
|
|
63
cli/tsc/dts/lib.deno.ns.d.ts
vendored
63
cli/tsc/dts/lib.deno.ns.d.ts
vendored
|
@ -4279,6 +4279,20 @@ declare namespace Deno {
|
|||
*/
|
||||
query(desc: PermissionDescriptor): Promise<PermissionStatus>;
|
||||
|
||||
/** Returns the current status of a permission.
|
||||
*
|
||||
* Note, if the permission is already granted, `request()` will not prompt
|
||||
* the user again, therefore `querySync()` is only necessary if you are going
|
||||
* to react differently existing permissions without wanting to modify them
|
||||
* or prompt the user to modify them.
|
||||
*
|
||||
* ```ts
|
||||
* const status = Deno.permissions.querySync({ name: "read", path: "/etc" });
|
||||
* console.log(status.state);
|
||||
* ```
|
||||
*/
|
||||
querySync(desc: PermissionDescriptor): PermissionStatus;
|
||||
|
||||
/** Revokes a permission, and resolves to the state of the permission.
|
||||
*
|
||||
* ```ts
|
||||
|
@ -4290,6 +4304,17 @@ declare namespace Deno {
|
|||
*/
|
||||
revoke(desc: PermissionDescriptor): Promise<PermissionStatus>;
|
||||
|
||||
/** Revokes a permission, and returns the state of the permission.
|
||||
*
|
||||
* ```ts
|
||||
* import { assert } from "https://deno.land/std/testing/asserts.ts";
|
||||
*
|
||||
* const status = Deno.permissions.revokeSync({ name: "run" });
|
||||
* assert(status.state !== "granted")
|
||||
* ```
|
||||
*/
|
||||
revokeSync(desc: PermissionDescriptor): PermissionStatus;
|
||||
|
||||
/** Requests the permission, and resolves to the state of the permission.
|
||||
*
|
||||
* If the permission is already granted, the user will not be prompted to
|
||||
|
@ -4305,6 +4330,23 @@ declare namespace Deno {
|
|||
* ```
|
||||
*/
|
||||
request(desc: PermissionDescriptor): Promise<PermissionStatus>;
|
||||
|
||||
|
||||
/** Requests the permission, and returns the state of the permission.
|
||||
*
|
||||
* If the permission is already granted, the user will not be prompted to
|
||||
* grant the permission again.
|
||||
*
|
||||
* ```ts
|
||||
* const status = Deno.permissions.requestSync({ name: "env" });
|
||||
* if (status.state === "granted") {
|
||||
* console.log("'env' permission is granted.");
|
||||
* } else {
|
||||
* console.log("'env' permission is denied.");
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
requestSync(desc: PermissionDescriptor): PermissionStatus;
|
||||
}
|
||||
|
||||
/** Deno's permission management API.
|
||||
|
@ -4335,6 +4377,11 @@ declare namespace Deno {
|
|||
* const status = await Deno.permissions.query({ name: "read", path: "/etc" });
|
||||
* console.log(status.state);
|
||||
* ```
|
||||
*
|
||||
* ```ts
|
||||
* const status = Deno.permissions.querySync({ name: "read", path: "/etc" });
|
||||
* console.log(status.state);
|
||||
* ```
|
||||
*
|
||||
* ### Revoking
|
||||
*
|
||||
|
@ -4344,6 +4391,13 @@ declare namespace Deno {
|
|||
* const status = await Deno.permissions.revoke({ name: "run" });
|
||||
* assert(status.state !== "granted")
|
||||
* ```
|
||||
*
|
||||
* ```ts
|
||||
* import { assert } from "https://deno.land/std/testing/asserts.ts";
|
||||
*
|
||||
* const status = Deno.permissions.revokeSync({ name: "run" });
|
||||
* assert(status.state !== "granted")
|
||||
* ```
|
||||
*
|
||||
* ### Requesting
|
||||
*
|
||||
|
@ -4355,6 +4409,15 @@ declare namespace Deno {
|
|||
* console.log("'env' permission is denied.");
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ```ts
|
||||
* const status = Deno.permissions.requestSync({ name: "env" });
|
||||
* if (status.state === "granted") {
|
||||
* console.log("'env' permission is granted.");
|
||||
* } else {
|
||||
* console.log("'env' permission is denied.");
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @category Permissions
|
||||
*/
|
||||
|
|
|
@ -183,48 +183,66 @@
|
|||
}
|
||||
|
||||
query(desc) {
|
||||
try {
|
||||
return PromiseResolve(this.querySync(desc));
|
||||
} catch (error) {
|
||||
return PromiseReject(error);
|
||||
}
|
||||
}
|
||||
|
||||
querySync(desc) {
|
||||
if (!isValidDescriptor(desc)) {
|
||||
return PromiseReject(
|
||||
new TypeError(
|
||||
`The provided value "${desc?.name}" is not a valid permission name.`,
|
||||
),
|
||||
throw new TypeError(
|
||||
`The provided value "${desc?.name}" is not a valid permission name.`,
|
||||
);
|
||||
}
|
||||
|
||||
formDescriptor(desc);
|
||||
|
||||
const state = opQuery(desc);
|
||||
return PromiseResolve(cache(desc, state));
|
||||
return cache(desc, state);
|
||||
}
|
||||
|
||||
revoke(desc) {
|
||||
try {
|
||||
return PromiseResolve(this.revokeSync(desc));
|
||||
} catch (error) {
|
||||
return PromiseReject(error);
|
||||
}
|
||||
}
|
||||
|
||||
revokeSync(desc) {
|
||||
if (!isValidDescriptor(desc)) {
|
||||
return PromiseReject(
|
||||
new TypeError(
|
||||
`The provided value "${desc?.name}" is not a valid permission name.`,
|
||||
),
|
||||
throw new TypeError(
|
||||
`The provided value "${desc?.name}" is not a valid permission name.`,
|
||||
);
|
||||
}
|
||||
|
||||
formDescriptor(desc);
|
||||
|
||||
const state = opRevoke(desc);
|
||||
return PromiseResolve(cache(desc, state));
|
||||
return cache(desc, state);
|
||||
}
|
||||
|
||||
request(desc) {
|
||||
try {
|
||||
return PromiseResolve(this.requestSync(desc));
|
||||
} catch (error) {
|
||||
return PromiseReject(error);
|
||||
}
|
||||
}
|
||||
|
||||
requestSync(desc) {
|
||||
if (!isValidDescriptor(desc)) {
|
||||
return PromiseReject(
|
||||
new TypeError(
|
||||
`The provided value "${desc?.name}" is not a valid permission name.`,
|
||||
),
|
||||
throw new TypeError(
|
||||
`The provided value "${desc?.name}" is not a valid permission name.`,
|
||||
);
|
||||
}
|
||||
|
||||
formDescriptor(desc);
|
||||
|
||||
const state = opRequest(desc);
|
||||
return PromiseResolve(cache(desc, state));
|
||||
return cache(desc, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue