mirror of
https://github.com/denoland/deno.git
synced 2025-08-04 02:48:24 +00:00
Use web standard Permissions API (#3200)
This commit is contained in:
parent
2598f9c68d
commit
efd7e78af3
15 changed files with 362 additions and 306 deletions
|
@ -68,8 +68,9 @@ export { applySourceMap } from "./error_stack.ts";
|
|||
export { ErrorKind, DenoError } from "./errors.ts";
|
||||
export {
|
||||
permissions,
|
||||
revokePermission,
|
||||
Permission,
|
||||
PermissionName,
|
||||
PermissionState,
|
||||
PermissionStatus,
|
||||
Permissions
|
||||
} from "./permissions.ts";
|
||||
export { truncateSync, truncate } from "./truncate.ts";
|
||||
|
|
|
@ -36,7 +36,7 @@ export let OP_GET_RANDOM_VALUES: number;
|
|||
export let OP_GLOBAL_TIMER_STOP: number;
|
||||
export let OP_GLOBAL_TIMER: number;
|
||||
export let OP_NOW: number;
|
||||
export let OP_PERMISSIONS: number;
|
||||
export let OP_QUERY_PERMISSION: number;
|
||||
export let OP_REVOKE_PERMISSION: number;
|
||||
export let OP_CREATE_WORKER: number;
|
||||
export let OP_HOST_GET_WORKER_CLOSED: number;
|
||||
|
|
|
@ -75,5 +75,6 @@ export enum ErrorKind {
|
|||
UnsupportedFetchScheme = 47,
|
||||
TooManyRedirects = 48,
|
||||
Diagnostic = 49,
|
||||
JSError = 50
|
||||
JSError = 50,
|
||||
TypeError = 51
|
||||
}
|
||||
|
|
84
cli/js/lib.deno_runtime.d.ts
vendored
84
cli/js/lib.deno_runtime.d.ts
vendored
|
@ -883,34 +883,64 @@ declare namespace Deno {
|
|||
}
|
||||
|
||||
// @url js/permissions.d.ts
|
||||
|
||||
/** Permissions as granted by the caller */
|
||||
export interface Permissions {
|
||||
read: boolean;
|
||||
write: boolean;
|
||||
net: boolean;
|
||||
env: boolean;
|
||||
run: boolean;
|
||||
hrtime: boolean;
|
||||
/** Permissions as granted by the caller
|
||||
* See: https://w3c.github.io/permissions/#permission-registry
|
||||
*/
|
||||
export type PermissionName =
|
||||
| "run"
|
||||
| "read"
|
||||
| "write"
|
||||
| "net"
|
||||
| "env"
|
||||
| "hrtime";
|
||||
/** https://w3c.github.io/permissions/#status-of-a-permission */
|
||||
export type PermissionState = "granted" | "denied" | "prompt";
|
||||
interface RunPermissionDescriptor {
|
||||
name: "run";
|
||||
}
|
||||
interface ReadWritePermissionDescriptor {
|
||||
name: "read" | "write";
|
||||
path?: string;
|
||||
}
|
||||
interface NetPermissionDescriptor {
|
||||
name: "net";
|
||||
url?: string;
|
||||
}
|
||||
interface EnvPermissionDescriptor {
|
||||
name: "env";
|
||||
}
|
||||
interface HrtimePermissionDescriptor {
|
||||
name: "hrtime";
|
||||
}
|
||||
/** See: https://w3c.github.io/permissions/#permission-descriptor */
|
||||
type PermissionDescriptor =
|
||||
| RunPermissionDescriptor
|
||||
| ReadWritePermissionDescriptor
|
||||
| NetPermissionDescriptor
|
||||
| EnvPermissionDescriptor
|
||||
| HrtimePermissionDescriptor;
|
||||
|
||||
export class Permissions {
|
||||
/** Queries the permission.
|
||||
* const status = await Deno.permissions.query({ name: "read", path: "/etc" });
|
||||
* if (status.state === "granted") {
|
||||
* data = await Deno.readFile("/etc/passwd");
|
||||
* }
|
||||
*/
|
||||
query(d: PermissionDescriptor): Promise<PermissionStatus>;
|
||||
/** Revokes the permission.
|
||||
* const status = await Deno.permissions.revoke({ name: "run" });
|
||||
* assert(status.state !== "granted")
|
||||
*/
|
||||
revoke(d: PermissionDescriptor): Promise<PermissionStatus>;
|
||||
}
|
||||
export const permissions: Permissions;
|
||||
|
||||
/** https://w3c.github.io/permissions/#permissionstatus */
|
||||
export class PermissionStatus {
|
||||
state: PermissionState;
|
||||
constructor(state: PermissionState);
|
||||
}
|
||||
export type Permission = keyof Permissions;
|
||||
/** Inspect granted permissions for the current program.
|
||||
*
|
||||
* if (Deno.permissions().read) {
|
||||
* const file = await Deno.readFile("example.test");
|
||||
* // ...
|
||||
* }
|
||||
*/
|
||||
export function permissions(): Permissions;
|
||||
/** Revoke a permission. When the permission was already revoked nothing changes
|
||||
*
|
||||
* if (Deno.permissions().read) {
|
||||
* const file = await Deno.readFile("example.test");
|
||||
* Deno.revokePermission('read');
|
||||
* }
|
||||
* Deno.readFile("example.test"); // -> error or permission prompt
|
||||
*/
|
||||
export function revokePermission(permission: Permission): void;
|
||||
|
||||
// @url js/truncate.d.ts
|
||||
|
||||
|
|
|
@ -2,38 +2,72 @@
|
|||
import * as dispatch from "./dispatch.ts";
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
|
||||
/** Permissions as granted by the caller */
|
||||
export interface Permissions {
|
||||
read: boolean;
|
||||
write: boolean;
|
||||
net: boolean;
|
||||
env: boolean;
|
||||
run: boolean;
|
||||
hrtime: boolean;
|
||||
// NOTE: Keep in sync with src/permissions.rs
|
||||
}
|
||||
|
||||
export type Permission = keyof Permissions;
|
||||
|
||||
/** Inspect granted permissions for the current program.
|
||||
*
|
||||
* if (Deno.permissions().read) {
|
||||
* const file = await Deno.readFile("example.test");
|
||||
* // ...
|
||||
* }
|
||||
/** Permissions as granted by the caller
|
||||
* See: https://w3c.github.io/permissions/#permission-registry
|
||||
*/
|
||||
export function permissions(): Permissions {
|
||||
return sendSync(dispatch.OP_PERMISSIONS) as Permissions;
|
||||
export type PermissionName =
|
||||
| "read"
|
||||
| "write"
|
||||
| "net"
|
||||
| "env"
|
||||
| "run"
|
||||
| "hrtime";
|
||||
// NOTE: Keep in sync with cli/permissions.rs
|
||||
|
||||
/** https://w3c.github.io/permissions/#status-of-a-permission */
|
||||
export type PermissionState = "granted" | "denied" | "prompt";
|
||||
|
||||
interface RunPermissionDescriptor {
|
||||
name: "run";
|
||||
}
|
||||
interface ReadWritePermissionDescriptor {
|
||||
name: "read" | "write";
|
||||
path?: string;
|
||||
}
|
||||
interface NetPermissionDescriptor {
|
||||
name: "net";
|
||||
url?: string;
|
||||
}
|
||||
interface EnvPermissionDescriptor {
|
||||
name: "env";
|
||||
}
|
||||
interface HrtimePermissionDescriptor {
|
||||
name: "hrtime";
|
||||
}
|
||||
/** See: https://w3c.github.io/permissions/#permission-descriptor */
|
||||
type PermissionDescriptor =
|
||||
| RunPermissionDescriptor
|
||||
| ReadWritePermissionDescriptor
|
||||
| NetPermissionDescriptor
|
||||
| EnvPermissionDescriptor
|
||||
| HrtimePermissionDescriptor;
|
||||
|
||||
/** https://w3c.github.io/permissions/#permissionstatus */
|
||||
export class PermissionStatus {
|
||||
constructor(public state: PermissionState) {}
|
||||
// TODO(kt3k): implement onchange handler
|
||||
}
|
||||
|
||||
/** Revoke a permission. When the permission was already revoked nothing changes
|
||||
*
|
||||
* if (Deno.permissions().read) {
|
||||
* const file = await Deno.readFile("example.test");
|
||||
* Deno.revokePermission('read');
|
||||
* }
|
||||
* Deno.readFile("example.test"); // -> error or permission prompt
|
||||
*/
|
||||
export function revokePermission(permission: Permission): void {
|
||||
sendSync(dispatch.OP_REVOKE_PERMISSION, { permission });
|
||||
export class Permissions {
|
||||
/** Queries the permission.
|
||||
* const status = await Deno.permissions.query({ name: "read", path: "/etc" });
|
||||
* if (status.state === "granted") {
|
||||
* file = await Deno.readFile("/etc/passwd");
|
||||
* }
|
||||
*/
|
||||
async query(desc: PermissionDescriptor): Promise<PermissionStatus> {
|
||||
const { state } = sendSync(dispatch.OP_QUERY_PERMISSION, desc);
|
||||
return new PermissionStatus(state);
|
||||
}
|
||||
|
||||
/** Revokes the permission.
|
||||
* const status = await Deno.permissions.revoke({ name: "run" });
|
||||
* assert(status.state !== "granted")
|
||||
*/
|
||||
async revoke(desc: PermissionDescriptor): Promise<PermissionStatus> {
|
||||
const { state } = sendSync(dispatch.OP_REVOKE_PERMISSION, desc);
|
||||
return new PermissionStatus(state);
|
||||
}
|
||||
}
|
||||
|
||||
export const permissions = new Permissions();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
import { testPerm, assert, assertEquals } from "./test_util.ts";
|
||||
import { test, testPerm, assert, assertEquals } from "./test_util.ts";
|
||||
|
||||
const knownPermissions: Deno.Permission[] = [
|
||||
const knownPermissions: Deno.PermissionName[] = [
|
||||
"run",
|
||||
"read",
|
||||
"write",
|
||||
|
@ -11,18 +11,31 @@ const knownPermissions: Deno.Permission[] = [
|
|||
];
|
||||
|
||||
for (const grant of knownPermissions) {
|
||||
testPerm({ [grant]: true }, function envGranted(): void {
|
||||
const perms = Deno.permissions();
|
||||
assert(perms !== null);
|
||||
for (const perm in perms) {
|
||||
assertEquals(perms[perm], perm === grant);
|
||||
}
|
||||
testPerm({ [grant]: true }, async function envGranted(): Promise<void> {
|
||||
const status0 = await Deno.permissions.query({ name: grant });
|
||||
assert(status0 != null);
|
||||
assertEquals(status0.state, "granted");
|
||||
|
||||
Deno.revokePermission(grant);
|
||||
|
||||
const revoked = Deno.permissions();
|
||||
for (const perm in revoked) {
|
||||
assertEquals(revoked[perm], false);
|
||||
}
|
||||
const status1 = await Deno.permissions.revoke({ name: grant });
|
||||
assert(status1 != null);
|
||||
assertEquals(status1.state, "prompt");
|
||||
});
|
||||
}
|
||||
|
||||
test(async function permissionInvalidName(): Promise<void> {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
await Deno.permissions.query({ name: "foo" as any });
|
||||
} catch (e) {
|
||||
assert(e.name === "TypeError");
|
||||
}
|
||||
});
|
||||
|
||||
test(async function permissionNetInvalidUrl(): Promise<void> {
|
||||
try {
|
||||
// Invalid url causes TypeError.
|
||||
await Deno.permissions.query({ name: "net", url: ":" });
|
||||
} catch (e) {
|
||||
assert(e.name === "TypeError");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -29,11 +29,34 @@ interface TestPermissions {
|
|||
hrtime?: boolean;
|
||||
}
|
||||
|
||||
const processPerms = Deno.permissions();
|
||||
export interface Permissions {
|
||||
read: boolean;
|
||||
write: boolean;
|
||||
net: boolean;
|
||||
env: boolean;
|
||||
run: boolean;
|
||||
hrtime: boolean;
|
||||
}
|
||||
|
||||
const isGranted = async (name: Deno.PermissionName): Promise<boolean> =>
|
||||
(await Deno.permissions.query({ name })).state === "granted";
|
||||
|
||||
async function getProcessPermissions(): Promise<Permissions> {
|
||||
return {
|
||||
run: await isGranted("run"),
|
||||
read: await isGranted("read"),
|
||||
write: await isGranted("write"),
|
||||
net: await isGranted("net"),
|
||||
env: await isGranted("env"),
|
||||
hrtime: await isGranted("hrtime")
|
||||
};
|
||||
}
|
||||
|
||||
const processPerms = await getProcessPermissions();
|
||||
|
||||
function permissionsMatch(
|
||||
processPerms: Deno.Permissions,
|
||||
requiredPerms: Deno.Permissions
|
||||
processPerms: Permissions,
|
||||
requiredPerms: Permissions
|
||||
): boolean {
|
||||
for (const permName in processPerms) {
|
||||
if (processPerms[permName] !== requiredPerms[permName]) {
|
||||
|
@ -44,9 +67,9 @@ function permissionsMatch(
|
|||
return true;
|
||||
}
|
||||
|
||||
export const permissionCombinations: Map<string, Deno.Permissions> = new Map();
|
||||
export const permissionCombinations: Map<string, Permissions> = new Map();
|
||||
|
||||
function permToString(perms: Deno.Permissions): string {
|
||||
function permToString(perms: Permissions): string {
|
||||
const r = perms.read ? 1 : 0;
|
||||
const w = perms.write ? 1 : 0;
|
||||
const n = perms.net ? 1 : 0;
|
||||
|
@ -56,14 +79,14 @@ function permToString(perms: Deno.Permissions): string {
|
|||
return `permR${r}W${w}N${n}E${e}U${u}H${h}`;
|
||||
}
|
||||
|
||||
function registerPermCombination(perms: Deno.Permissions): void {
|
||||
function registerPermCombination(perms: Permissions): void {
|
||||
const key = permToString(perms);
|
||||
if (!permissionCombinations.has(key)) {
|
||||
permissionCombinations.set(key, perms);
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeTestPermissions(perms: TestPermissions): Deno.Permissions {
|
||||
function normalizeTestPermissions(perms: TestPermissions): Permissions {
|
||||
return {
|
||||
read: !!perms.read,
|
||||
write: !!perms.write,
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
#!/usr/bin/env -S deno run --reload --allow-run
|
||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
import "./unit_tests.ts";
|
||||
import { permissionCombinations, parseUnitTestOutput } from "./test_util.ts";
|
||||
import {
|
||||
permissionCombinations,
|
||||
parseUnitTestOutput,
|
||||
Permissions
|
||||
} from "./test_util.ts";
|
||||
|
||||
interface TestResult {
|
||||
perms: string;
|
||||
|
@ -9,7 +13,7 @@ interface TestResult {
|
|||
result: number;
|
||||
}
|
||||
|
||||
function permsToCliFlags(perms: Deno.Permissions): string[] {
|
||||
function permsToCliFlags(perms: Permissions): string[] {
|
||||
return Object.keys(perms)
|
||||
.map(
|
||||
(key): string => {
|
||||
|
@ -25,7 +29,7 @@ function permsToCliFlags(perms: Deno.Permissions): string[] {
|
|||
.filter((e): boolean => e.length > 0);
|
||||
}
|
||||
|
||||
function fmtPerms(perms: Deno.Permissions): string {
|
||||
function fmtPerms(perms: Permissions): string {
|
||||
let fmt = permsToCliFlags(perms).join(" ");
|
||||
|
||||
if (!fmt) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue