2022-01-07 22:09:52 -05:00
|
|
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
2021-02-04 17:18:32 -05:00
|
|
|
"use strict";
|
2020-07-19 13:49:44 -04:00
|
|
|
|
|
|
|
((window) => {
|
2022-10-24 10:14:17 -04:00
|
|
|
const { ops } = Deno.core;
|
|
|
|
const { Event } = window.__bootstrap.event;
|
|
|
|
const { EventTarget } = window.__bootstrap.eventTarget;
|
2021-08-06 09:04:00 -04:00
|
|
|
const { pathFromURL } = window.__bootstrap.util;
|
2022-10-24 10:14:17 -04:00
|
|
|
const { illegalConstructorKey } = window.__bootstrap.webUtil;
|
2021-07-03 18:17:52 -04:00
|
|
|
const {
|
2021-10-13 13:04:44 -04:00
|
|
|
ArrayIsArray,
|
2021-07-03 18:17:52 -04:00
|
|
|
ArrayPrototypeIncludes,
|
2021-10-13 13:04:44 -04:00
|
|
|
ArrayPrototypeMap,
|
|
|
|
ArrayPrototypeSlice,
|
2021-07-03 18:17:52 -04:00
|
|
|
Map,
|
|
|
|
MapPrototypeGet,
|
|
|
|
MapPrototypeHas,
|
|
|
|
MapPrototypeSet,
|
|
|
|
FunctionPrototypeCall,
|
|
|
|
PromiseResolve,
|
|
|
|
PromiseReject,
|
2022-04-16 08:09:07 -04:00
|
|
|
ReflectHas,
|
2021-07-03 18:17:52 -04:00
|
|
|
SymbolFor,
|
|
|
|
TypeError,
|
|
|
|
} = window.__bootstrap.primordials;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
2021-02-24 22:33:09 -05:00
|
|
|
/**
|
|
|
|
* @typedef StatusCacheValue
|
|
|
|
* @property {PermissionState} state
|
|
|
|
* @property {PermissionStatus} status
|
|
|
|
*/
|
|
|
|
|
2022-09-28 08:46:50 -04:00
|
|
|
/** @type {ReadonlyArray<"read" | "write" | "net" | "env" | "sys" | "run" | "ffi" | "hrtime">} */
|
2021-02-24 22:33:09 -05:00
|
|
|
const permissionNames = [
|
|
|
|
"read",
|
|
|
|
"write",
|
|
|
|
"net",
|
|
|
|
"env",
|
2022-09-28 08:46:50 -04:00
|
|
|
"sys",
|
2021-02-24 22:33:09 -05:00
|
|
|
"run",
|
2021-08-06 17:28:10 -04:00
|
|
|
"ffi",
|
2021-02-24 22:33:09 -05:00
|
|
|
"hrtime",
|
|
|
|
];
|
|
|
|
|
|
|
|
/**
|
2021-04-28 10:08:51 -04:00
|
|
|
* @param {Deno.PermissionDescriptor} desc
|
2021-02-24 22:33:09 -05:00
|
|
|
* @returns {Deno.PermissionState}
|
|
|
|
*/
|
2020-07-19 13:49:44 -04:00
|
|
|
function opQuery(desc) {
|
2022-08-11 09:56:56 -04:00
|
|
|
return ops.op_query_permission(desc);
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
2021-02-24 22:33:09 -05:00
|
|
|
/**
|
2021-04-28 10:08:51 -04:00
|
|
|
* @param {Deno.PermissionDescriptor} desc
|
2021-02-24 22:33:09 -05:00
|
|
|
* @returns {Deno.PermissionState}
|
|
|
|
*/
|
2020-07-19 13:49:44 -04:00
|
|
|
function opRevoke(desc) {
|
2022-08-11 09:56:56 -04:00
|
|
|
return ops.op_revoke_permission(desc);
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
2021-02-24 22:33:09 -05:00
|
|
|
/**
|
2021-04-28 10:08:51 -04:00
|
|
|
* @param {Deno.PermissionDescriptor} desc
|
2021-02-24 22:33:09 -05:00
|
|
|
* @returns {Deno.PermissionState}
|
|
|
|
*/
|
2020-07-19 13:49:44 -04:00
|
|
|
function opRequest(desc) {
|
2022-08-11 09:56:56 -04:00
|
|
|
return ops.op_request_permission(desc);
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
2021-02-24 22:33:09 -05:00
|
|
|
class PermissionStatus extends EventTarget {
|
|
|
|
/** @type {{ state: Deno.PermissionState }} */
|
|
|
|
#state;
|
|
|
|
|
|
|
|
/** @type {((this: PermissionStatus, event: Event) => any) | null} */
|
|
|
|
onchange = null;
|
|
|
|
|
|
|
|
/** @returns {Deno.PermissionState} */
|
|
|
|
get state() {
|
|
|
|
return this.#state.state;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-04-28 10:08:51 -04:00
|
|
|
* @param {{ state: Deno.PermissionState }} state
|
|
|
|
* @param {unknown} key
|
2021-02-24 22:33:09 -05:00
|
|
|
*/
|
2020-11-14 07:10:23 -05:00
|
|
|
constructor(state = null, key = null) {
|
2020-09-19 17:30:59 -04:00
|
|
|
if (key != illegalConstructorKey) {
|
|
|
|
throw new TypeError("Illegal constructor.");
|
|
|
|
}
|
2021-02-24 22:33:09 -05:00
|
|
|
super();
|
|
|
|
this.#state = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-04-28 10:08:51 -04:00
|
|
|
* @param {Event} event
|
2021-02-24 22:33:09 -05:00
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
|
|
|
dispatchEvent(event) {
|
|
|
|
let dispatched = super.dispatchEvent(event);
|
|
|
|
if (dispatched && this.onchange) {
|
2021-07-03 18:17:52 -04:00
|
|
|
FunctionPrototypeCall(this.onchange, this, event);
|
2021-02-24 22:33:09 -05:00
|
|
|
dispatched = !event.defaultPrevented;
|
|
|
|
}
|
|
|
|
return dispatched;
|
|
|
|
}
|
|
|
|
|
2021-07-03 18:17:52 -04:00
|
|
|
[SymbolFor("Deno.privateCustomInspect")](inspect) {
|
2021-02-24 22:33:09 -05:00
|
|
|
return `${this.constructor.name} ${
|
|
|
|
inspect({ state: this.state, onchange: this.onchange })
|
|
|
|
}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @type {Map<string, StatusCacheValue>} */
|
|
|
|
const statusCache = new Map();
|
|
|
|
|
|
|
|
/**
|
2021-04-28 10:08:51 -04:00
|
|
|
* @param {Deno.PermissionDescriptor} desc
|
|
|
|
* @param {Deno.PermissionState} state
|
2021-02-24 22:33:09 -05:00
|
|
|
* @returns {PermissionStatus}
|
|
|
|
*/
|
|
|
|
function cache(desc, state) {
|
|
|
|
let { name: key } = desc;
|
2022-04-16 08:09:07 -04:00
|
|
|
if (
|
2022-09-16 07:46:38 -04:00
|
|
|
(desc.name === "read" || desc.name === "write" || desc.name === "ffi") &&
|
2022-04-16 08:09:07 -04:00
|
|
|
ReflectHas(desc, "path")
|
|
|
|
) {
|
2022-09-16 07:46:38 -04:00
|
|
|
key += `-${desc.path}&`;
|
2021-02-24 22:33:09 -05:00
|
|
|
} else if (desc.name === "net" && desc.host) {
|
2022-09-16 07:46:38 -04:00
|
|
|
key += `-${desc.host}&`;
|
|
|
|
} else if (desc.name === "run" && desc.command) {
|
|
|
|
key += `-${desc.command}&`;
|
|
|
|
} else if (desc.name === "env" && desc.variable) {
|
|
|
|
key += `-${desc.variable}&`;
|
2022-09-28 08:46:50 -04:00
|
|
|
} else if (desc.name === "sys" && desc.kind) {
|
|
|
|
key += `-${desc.kind}&`;
|
2022-09-16 07:46:38 -04:00
|
|
|
} else {
|
|
|
|
key += "$";
|
2021-02-24 22:33:09 -05:00
|
|
|
}
|
2021-07-03 18:17:52 -04:00
|
|
|
if (MapPrototypeHas(statusCache, key)) {
|
|
|
|
const status = MapPrototypeGet(statusCache, key);
|
2021-02-24 22:33:09 -05:00
|
|
|
if (status.state !== state) {
|
|
|
|
status.state = state;
|
|
|
|
status.status.dispatchEvent(new Event("change", { cancelable: false }));
|
|
|
|
}
|
|
|
|
return status.status;
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
2021-02-24 22:33:09 -05:00
|
|
|
/** @type {{ state: Deno.PermissionState; status?: PermissionStatus }} */
|
|
|
|
const status = { state };
|
|
|
|
status.status = new PermissionStatus(status, illegalConstructorKey);
|
2021-07-03 18:17:52 -04:00
|
|
|
MapPrototypeSet(statusCache, key, status);
|
2021-02-24 22:33:09 -05:00
|
|
|
return status.status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-04-28 10:08:51 -04:00
|
|
|
* @param {unknown} desc
|
2021-02-24 22:33:09 -05:00
|
|
|
* @returns {desc is Deno.PermissionDescriptor}
|
|
|
|
*/
|
|
|
|
function isValidDescriptor(desc) {
|
2022-05-19 17:45:09 -04:00
|
|
|
return typeof desc === "object" && desc !== null &&
|
2021-07-03 18:17:52 -04:00
|
|
|
ArrayPrototypeIncludes(permissionNames, desc.name);
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
class Permissions {
|
2021-02-24 22:33:09 -05:00
|
|
|
constructor(key = null) {
|
2020-09-19 17:30:59 -04:00
|
|
|
if (key != illegalConstructorKey) {
|
|
|
|
throw new TypeError("Illegal constructor.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
query(desc) {
|
2021-02-24 22:33:09 -05:00
|
|
|
if (!isValidDescriptor(desc)) {
|
2021-07-03 18:17:52 -04:00
|
|
|
return PromiseReject(
|
2021-02-24 22:33:09 -05:00
|
|
|
new TypeError(
|
2022-08-05 19:31:19 -04:00
|
|
|
`The provided value "${desc?.name}" is not a valid permission name.`,
|
2021-02-24 22:33:09 -05:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2021-08-06 09:04:00 -04:00
|
|
|
|
2021-10-13 13:04:44 -04:00
|
|
|
if (
|
|
|
|
desc.name === "read" || desc.name === "write" || desc.name === "ffi"
|
|
|
|
) {
|
2021-08-06 09:04:00 -04:00
|
|
|
desc.path = pathFromURL(desc.path);
|
|
|
|
} else if (desc.name === "run") {
|
|
|
|
desc.command = pathFromURL(desc.command);
|
|
|
|
}
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
const state = opQuery(desc);
|
2021-07-03 18:17:52 -04:00
|
|
|
return PromiseResolve(cache(desc, state));
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
revoke(desc) {
|
2021-02-24 22:33:09 -05:00
|
|
|
if (!isValidDescriptor(desc)) {
|
2021-07-03 18:17:52 -04:00
|
|
|
return PromiseReject(
|
2021-02-24 22:33:09 -05:00
|
|
|
new TypeError(
|
2022-08-05 19:31:19 -04:00
|
|
|
`The provided value "${desc?.name}" is not a valid permission name.`,
|
2021-02-24 22:33:09 -05:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2021-08-06 09:04:00 -04:00
|
|
|
|
|
|
|
if (desc.name === "read" || desc.name === "write") {
|
|
|
|
desc.path = pathFromURL(desc.path);
|
|
|
|
} else if (desc.name === "run") {
|
|
|
|
desc.command = pathFromURL(desc.command);
|
|
|
|
}
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
const state = opRevoke(desc);
|
2021-07-03 18:17:52 -04:00
|
|
|
return PromiseResolve(cache(desc, state));
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
request(desc) {
|
2021-02-24 22:33:09 -05:00
|
|
|
if (!isValidDescriptor(desc)) {
|
2021-07-03 18:17:52 -04:00
|
|
|
return PromiseReject(
|
2021-02-24 22:33:09 -05:00
|
|
|
new TypeError(
|
2022-08-05 19:31:19 -04:00
|
|
|
`The provided value "${desc?.name}" is not a valid permission name.`,
|
2021-02-24 22:33:09 -05:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2021-08-06 09:04:00 -04:00
|
|
|
|
|
|
|
if (desc.name === "read" || desc.name === "write") {
|
|
|
|
desc.path = pathFromURL(desc.path);
|
|
|
|
} else if (desc.name === "run") {
|
|
|
|
desc.command = pathFromURL(desc.command);
|
|
|
|
}
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
const state = opRequest(desc);
|
2021-07-03 18:17:52 -04:00
|
|
|
return PromiseResolve(cache(desc, state));
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-19 17:30:59 -04:00
|
|
|
const permissions = new Permissions(illegalConstructorKey);
|
2020-07-19 13:49:44 -04:00
|
|
|
|
2021-10-13 13:04:44 -04:00
|
|
|
/** Converts all file URLs in FS allowlists to paths. */
|
|
|
|
function serializePermissions(permissions) {
|
|
|
|
if (typeof permissions == "object" && permissions != null) {
|
|
|
|
const serializedPermissions = {};
|
|
|
|
for (const key of ["read", "write", "run", "ffi"]) {
|
|
|
|
if (ArrayIsArray(permissions[key])) {
|
|
|
|
serializedPermissions[key] = ArrayPrototypeMap(
|
|
|
|
permissions[key],
|
|
|
|
(path) => pathFromURL(path),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
serializedPermissions[key] = permissions[key];
|
|
|
|
}
|
|
|
|
}
|
2022-09-28 08:46:50 -04:00
|
|
|
for (const key of ["env", "hrtime", "net", "sys"]) {
|
2021-10-13 13:04:44 -04:00
|
|
|
if (ArrayIsArray(permissions[key])) {
|
|
|
|
serializedPermissions[key] = ArrayPrototypeSlice(permissions[key]);
|
|
|
|
} else {
|
|
|
|
serializedPermissions[key] = permissions[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return serializedPermissions;
|
|
|
|
}
|
|
|
|
return permissions;
|
|
|
|
}
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
window.__bootstrap.permissions = {
|
2021-10-13 13:04:44 -04:00
|
|
|
serializePermissions,
|
2020-07-19 13:49:44 -04:00
|
|
|
permissions,
|
|
|
|
Permissions,
|
|
|
|
PermissionStatus,
|
|
|
|
};
|
|
|
|
})(this);
|