mirror of
https://github.com/denoland/deno.git
synced 2025-08-04 02:48:24 +00:00
feat(permissions): add "--deny-*" flags (#19070)
This commit adds new "--deny-*" permission flags. These are complimentary to "--allow-*" flags. These flags can be used to restrict access to certain resources, even if they were granted using "--allow-*" flags or the "--allow-all" ("-A") flag. Eg. specifying "--allow-read --deny-read" will result in a permission error, while "--allow-read --deny-read=/etc" will allow read access to all FS but the "/etc" directory. Runtime permissions APIs ("Deno.permissions") were adjusted as well, mainly by adding, a new "PermissionStatus.partial" field. This field denotes that while permission might be granted to requested resource, it's only partial (ie. a "--deny-*" flag was specified that excludes some of the requested resources). Eg. specifying "--allow-read=foo/ --deny-read=foo/bar" and then querying for permissions like "Deno.permissions.query({ name: "read", path: "foo/" })" will return "PermissionStatus { state: "granted", onchange: null, partial: true }", denoting that some of the subpaths don't have read access. Closes #18804. --------- Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com> Co-authored-by: Nayeem Rahman <nayeemrmn99@gmail.com>
This commit is contained in:
parent
db287e216d
commit
6fb7e8d93b
21 changed files with 1805 additions and 1456 deletions
|
@ -30,6 +30,7 @@ const illegalConstructorKey = Symbol("illegalConstructorKey");
|
|||
* @typedef StatusCacheValue
|
||||
* @property {PermissionState} state
|
||||
* @property {PermissionStatus} status
|
||||
* @property {boolean} partial
|
||||
*/
|
||||
|
||||
/** @type {ReadonlyArray<"read" | "write" | "net" | "env" | "sys" | "run" | "ffi" | "hrtime">} */
|
||||
|
@ -69,27 +70,32 @@ function opRequest(desc) {
|
|||
}
|
||||
|
||||
class PermissionStatus extends EventTarget {
|
||||
/** @type {{ state: Deno.PermissionState }} */
|
||||
#state;
|
||||
/** @type {{ state: Deno.PermissionState, partial: boolean }} */
|
||||
#status;
|
||||
|
||||
/** @type {((this: PermissionStatus, event: Event) => any) | null} */
|
||||
onchange = null;
|
||||
|
||||
/** @returns {Deno.PermissionState} */
|
||||
get state() {
|
||||
return this.#state.state;
|
||||
return this.#status.state;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
get partial() {
|
||||
return this.#status.partial;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ state: Deno.PermissionState }} state
|
||||
* @param {{ state: Deno.PermissionState, partial: boolean }} status
|
||||
* @param {unknown} key
|
||||
*/
|
||||
constructor(state = null, key = null) {
|
||||
constructor(status = null, key = null) {
|
||||
if (key != illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
super();
|
||||
this.#state = state;
|
||||
this.#status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,9 +112,9 @@ class PermissionStatus extends EventTarget {
|
|||
}
|
||||
|
||||
[SymbolFor("Deno.privateCustomInspect")](inspect) {
|
||||
return `${this.constructor.name} ${
|
||||
inspect({ state: this.state, onchange: this.onchange })
|
||||
}`;
|
||||
const object = { state: this.state, onchange: this.onchange };
|
||||
if (this.partial) object.partial = this.partial;
|
||||
return `${this.constructor.name} ${inspect(object)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,10 +123,10 @@ const statusCache = new SafeMap();
|
|||
|
||||
/**
|
||||
* @param {Deno.PermissionDescriptor} desc
|
||||
* @param {Deno.PermissionState} state
|
||||
* @param {{ state: Deno.PermissionState, partial: boolean }} rawStatus
|
||||
* @returns {PermissionStatus}
|
||||
*/
|
||||
function cache(desc, state) {
|
||||
function cache(desc, rawStatus) {
|
||||
let { name: key } = desc;
|
||||
if (
|
||||
(desc.name === "read" || desc.name === "write" || desc.name === "ffi") &&
|
||||
|
@ -139,18 +145,24 @@ function cache(desc, state) {
|
|||
key += "$";
|
||||
}
|
||||
if (MapPrototypeHas(statusCache, key)) {
|
||||
const status = MapPrototypeGet(statusCache, key);
|
||||
if (status.state !== state) {
|
||||
status.state = state;
|
||||
status.status.dispatchEvent(new Event("change", { cancelable: false }));
|
||||
const cachedObj = MapPrototypeGet(statusCache, key);
|
||||
if (
|
||||
cachedObj.state !== rawStatus.state ||
|
||||
cachedObj.partial !== rawStatus.partial
|
||||
) {
|
||||
cachedObj.state = rawStatus.state;
|
||||
cachedObj.partial = rawStatus.partial;
|
||||
cachedObj.status.dispatchEvent(
|
||||
new Event("change", { cancelable: false }),
|
||||
);
|
||||
}
|
||||
return status.status;
|
||||
return cachedObj.status;
|
||||
}
|
||||
/** @type {{ state: Deno.PermissionState; status?: PermissionStatus }} */
|
||||
const status = { state };
|
||||
status.status = new PermissionStatus(status, illegalConstructorKey);
|
||||
MapPrototypeSet(statusCache, key, status);
|
||||
return status.status;
|
||||
/** @type {{ state: Deno.PermissionState, partial: boolean, status?: PermissionStatus }} */
|
||||
const obj = rawStatus;
|
||||
obj.status = new PermissionStatus(obj, illegalConstructorKey);
|
||||
MapPrototypeSet(statusCache, key, obj);
|
||||
return obj.status;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -200,8 +212,8 @@ class Permissions {
|
|||
|
||||
formDescriptor(desc);
|
||||
|
||||
const state = opQuery(desc);
|
||||
return cache(desc, state);
|
||||
const status = opQuery(desc);
|
||||
return cache(desc, status);
|
||||
}
|
||||
|
||||
revoke(desc) {
|
||||
|
@ -221,8 +233,8 @@ class Permissions {
|
|||
|
||||
formDescriptor(desc);
|
||||
|
||||
const state = opRevoke(desc);
|
||||
return cache(desc, state);
|
||||
const status = opRevoke(desc);
|
||||
return cache(desc, status);
|
||||
}
|
||||
|
||||
request(desc) {
|
||||
|
@ -242,8 +254,8 @@ class Permissions {
|
|||
|
||||
formDescriptor(desc);
|
||||
|
||||
const state = opRequest(desc);
|
||||
return cache(desc, state);
|
||||
const status = opRequest(desc);
|
||||
return cache(desc, status);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue