summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/deno_error.rs4
-rw-r--r--cli/js/deno.ts5
-rw-r--r--cli/js/dispatch.ts2
-rw-r--r--cli/js/errors.ts3
-rw-r--r--cli/js/lib.deno_runtime.d.ts84
-rw-r--r--cli/js/permissions.ts94
-rw-r--r--cli/js/permissions_test.ts41
-rw-r--r--cli/js/test_util.ts37
-rwxr-xr-xcli/js/unit_test_runner.ts10
-rw-r--r--cli/msg.rs1
-rw-r--r--cli/ops/permissions.rs64
-rw-r--r--cli/ops/timers.rs2
-rw-r--r--cli/permissions.rs296
-rw-r--r--cli/tests/025_hrtime.ts8
-rw-r--r--std/manual.md9
15 files changed, 358 insertions, 302 deletions
diff --git a/cli/deno_error.rs b/cli/deno_error.rs
index 2c53c84f7..346149cd1 100644
--- a/cli/deno_error.rs
+++ b/cli/deno_error.rs
@@ -96,6 +96,10 @@ pub fn too_many_redirects() -> ErrBox {
StaticError(ErrorKind::TooManyRedirects, "too many redirects").into()
}
+pub fn type_error(msg: String) -> ErrBox {
+ DenoError::new(ErrorKind::TypeError, msg).into()
+}
+
pub trait GetErrorKind {
fn kind(&self) -> ErrorKind;
}
diff --git a/cli/js/deno.ts b/cli/js/deno.ts
index 4b0e3ff96..cac730249 100644
--- a/cli/js/deno.ts
+++ b/cli/js/deno.ts
@@ -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";
diff --git a/cli/js/dispatch.ts b/cli/js/dispatch.ts
index 38405a866..d66467011 100644
--- a/cli/js/dispatch.ts
+++ b/cli/js/dispatch.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;
diff --git a/cli/js/errors.ts b/cli/js/errors.ts
index 02ddfa2f2..8cd7a76be 100644
--- a/cli/js/errors.ts
+++ b/cli/js/errors.ts
@@ -75,5 +75,6 @@ export enum ErrorKind {
UnsupportedFetchScheme = 47,
TooManyRedirects = 48,
Diagnostic = 49,
- JSError = 50
+ JSError = 50,
+ TypeError = 51
}
diff --git a/cli/js/lib.deno_runtime.d.ts b/cli/js/lib.deno_runtime.d.ts
index 3036ea2d1..c0bebf461 100644
--- a/cli/js/lib.deno_runtime.d.ts
+++ b/cli/js/lib.deno_runtime.d.ts
@@ -883,34 +883,64 @@ declare namespace Deno {
}
// @url js/permissions.d.ts
+ /** 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;
- /** Permissions as granted by the caller */
- export interface Permissions {
- read: boolean;
- write: boolean;
- net: boolean;
- env: boolean;
- run: boolean;
- hrtime: boolean;
- }
- 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;
+ /** https://w3c.github.io/permissions/#permissionstatus */
+ export class PermissionStatus {
+ state: PermissionState;
+ constructor(state: PermissionState);
+ }
// @url js/truncate.d.ts
diff --git a/cli/js/permissions.ts b/cli/js/permissions.ts
index 4f393501c..16ea3e5c2 100644
--- a/cli/js/permissions.ts
+++ b/cli/js/permissions.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
-}
+/** Permissions as granted by the caller
+ * See: https://w3c.github.io/permissions/#permission-registry
+ */
+export type PermissionName =
+ | "read"
+ | "write"
+ | "net"
+ | "env"
+ | "run"
+ | "hrtime";
+// NOTE: Keep in sync with cli/permissions.rs
-export type Permission = keyof Permissions;
+/** https://w3c.github.io/permissions/#status-of-a-permission */
+export type PermissionState = "granted" | "denied" | "prompt";
-/** Inspect granted permissions for the current program.
- *
- * if (Deno.permissions().read) {
- * const file = await Deno.readFile("example.test");
- * // ...
- * }
- */
-export function permissions(): Permissions {
- return sendSync(dispatch.OP_PERMISSIONS) as Permissions;
+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;
-/** 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 });
+/** https://w3c.github.io/permissions/#permissionstatus */
+export class PermissionStatus {
+ constructor(public state: PermissionState) {}
+ // TODO(kt3k): implement onchange handler
+}
+
+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();
diff --git a/cli/js/permissions_test.ts b/cli/js/permissions_test.ts
index 6511c2dcb..d9ba538f0 100644
--- a/cli/js/permissions_test.ts
+++ b/cli/js/permissions_test.ts
@@ -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");
+ }
+});
diff --git a/cli/js/test_util.ts b/cli/js/test_util.ts
index 2f2916e11..85fffabe6 100644
--- a/cli/js/test_util.ts
+++ b/cli/js/test_util.ts
@@ -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,
diff --git a/cli/js/unit_test_runner.ts b/cli/js/unit_test_runner.ts
index 913c575b2..ae255c385 100755
--- a/cli/js/unit_test_runner.ts
+++ b/cli/js/unit_test_runner.ts
@@ -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) {
diff --git a/cli/msg.rs b/cli/msg.rs
index 20ab9db13..206da7694 100644
--- a/cli/msg.rs
+++ b/cli/msg.rs
@@ -57,6 +57,7 @@ pub enum ErrorKind {
TooManyRedirects = 48,
Diagnostic = 49,
JSError = 50,
+ TypeError = 51,
}
// Warning! The values in this enum are duplicated in js/compiler.ts
diff --git a/cli/ops/permissions.rs b/cli/ops/permissions.rs
index 8303aa9cf..823ab678b 100644
--- a/cli/ops/permissions.rs
+++ b/cli/ops/permissions.rs
@@ -6,8 +6,8 @@ use deno::*;
pub fn init(i: &mut Isolate, s: &ThreadSafeState) {
i.register_op(
- "permissions",
- s.core_op(json_op(s.stateful_op(op_permissions))),
+ "query_permission",
+ s.core_op(json_op(s.stateful_op(op_query_permission))),
);
i.register_op(
"revoke_permission",
@@ -15,24 +15,25 @@ pub fn init(i: &mut Isolate, s: &ThreadSafeState) {
);
}
-pub fn op_permissions(
+#[derive(Deserialize)]
+struct PermissionArgs {
+ name: String,
+ url: Option<String>,
+ path: Option<String>,
+}
+
+pub fn op_query_permission(
state: &ThreadSafeState,
- _args: Value,
+ args: Value,
_zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> {
- Ok(JsonOp::Sync(json!({
- "run": state.permissions.allows_run(),
- "read": state.permissions.allows_read(),
- "write": state.permissions.allows_write(),
- "net": state.permissions.allows_net(),
- "env": state.permissions.allows_env(),
- "hrtime": state.permissions.allows_hrtime(),
- })))
-}
-
-#[derive(Deserialize)]
-struct RevokePermissionArgs {
- permission: String,
+ let args: PermissionArgs = serde_json::from_value(args)?;
+ let perm = state.permissions.get_permission_state(
+ &args.name,
+ &args.url.as_ref().map(String::as_str),
+ &args.path.as_ref().map(String::as_str),
+ )?;
+ Ok(JsonOp::Sync(json!({ "state": perm.to_string() })))
}
pub fn op_revoke_permission(
@@ -40,17 +41,20 @@ pub fn op_revoke_permission(
args: Value,
_zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> {
- let args: RevokePermissionArgs = serde_json::from_value(args)?;
- let permission = args.permission.as_ref();
- match permission {
- "run" => state.permissions.revoke_run(),
- "read" => state.permissions.revoke_read(),
- "write" => state.permissions.revoke_write(),
- "net" => state.permissions.revoke_net(),
- "env" => state.permissions.revoke_env(),
- "hrtime" => state.permissions.revoke_hrtime(),
- _ => Ok(()),
- }?;
-
- Ok(JsonOp::Sync(json!({})))
+ let args: PermissionArgs = serde_json::from_value(args)?;
+ match args.name.as_ref() {
+ "run" => state.permissions.allow_run.revoke(),
+ "read" => state.permissions.allow_read.revoke(),
+ "write" => state.permissions.allow_write.revoke(),
+ "net" => state.permissions.allow_net.revoke(),
+ "env" => state.permissions.allow_env.revoke(),
+ "hrtime" => state.permissions.allow_hrtime.revoke(),
+ _ => {}
+ };
+ let perm = state.permissions.get_permission_state(
+ &args.name,
+ &args.url.as_ref().map(String::as_str),
+ &args.path.as_ref().map(String::as_str),
+ )?;
+ Ok(JsonOp::Sync(json!({ "state": perm.to_string() })))
}
diff --git a/cli/ops/timers.rs b/cli/ops/timers.rs
index f366797b4..9d87aaf5c 100644
--- a/cli/ops/timers.rs
+++ b/cli/ops/timers.rs
@@ -70,7 +70,7 @@ fn op_now(
// If the permission is not enabled
// Round the nano result on 2 milliseconds
// see: https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#Reduced_time_precision
- if !state.permissions.allows_hrtime() {
+ if !state.permissions.allow_hrtime.is_allow() {
subsec_nanos -= subsec_nanos % reduced_time_precision
}
diff --git a/cli/permissions.rs b/cli/permissions.rs
index 1dd2eb1e2..af4c960d7 100644
--- a/cli/permissions.rs
+++ b/cli/permissions.rs
@@ -1,5 +1,5 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
-use crate::deno_error::permission_denied_msg;
+use crate::deno_error::{permission_denied_msg, type_error};
use crate::flags::DenoFlags;
use ansi_term::Style;
use deno::ErrBox;
@@ -9,10 +9,12 @@ use std::fmt;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
+use url::Url;
const PERMISSION_EMOJI: &str = "⚠️";
/// Tri-state value for storing permission state
+#[derive(PartialEq)]
pub enum PermissionAccessorState {
Allow = 0,
Ask = 1,
@@ -43,9 +45,9 @@ impl From<bool> for PermissionAccessorState {
impl fmt::Display for PermissionAccessorState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- PermissionAccessorState::Allow => f.pad("Allow"),
- PermissionAccessorState::Ask => f.pad("Ask"),
- PermissionAccessorState::Deny => f.pad("Deny"),
+ PermissionAccessorState::Allow => f.pad("granted"),
+ PermissionAccessorState::Ask => f.pad("prompt"),
+ PermissionAccessorState::Deny => f.pad("denied"),
}
}
}
@@ -110,7 +112,7 @@ impl Default for PermissionAccessor {
#[derive(Debug, Default)]
pub struct DenoPermissions {
- // Keep in sync with src/permissions.ts
+ // Keep in sync with cli/js/permissions.ts
pub allow_read: PermissionAccessor,
pub read_whitelist: Arc<HashSet<String>>,
pub allow_write: PermissionAccessor,
@@ -139,146 +141,94 @@ impl DenoPermissions {
}
}
+ /** Checks the permission state and returns the result. */
+ fn check_permission_state(
+ &self,
+ state: PermissionAccessorState,
+ msg: &str,
+ err_msg: &str,
+ ) -> Result<(), ErrBox> {
+ if state == PermissionAccessorState::Allow {
+ self.log_perm_access(msg);
+ return Ok(());
+ }
+ Err(permission_denied_msg(err_msg.to_string()))
+ }
+
pub fn check_run(&self) -> Result<(), ErrBox> {
- let msg = "access to run a subprocess";
+ self.check_permission_state(
+ self.allow_run.get_state(),
+ "access to run a subprocess",
+ "run again with the --allow-run flag",
+ )
+ }
- match self.allow_run.get_state() {
- PermissionAccessorState::Allow => {
- self.log_perm_access(msg);
- Ok(())
- }
- PermissionAccessorState::Ask => Err(permission_denied_msg(
- "run again with the --allow-run flag".to_string(),
- )),
- PermissionAccessorState::Deny => Err(permission_denied_msg(
- "run again with the --allow-run flag".to_string(),
- )),
+ fn get_state_read(&self, filename: &Option<&str>) -> PermissionAccessorState {
+ if check_path_white_list(filename, &self.read_whitelist) {
+ return PermissionAccessorState::Allow;
}
+ self.allow_read.get_state()
}
pub fn check_read(&self, filename: &str) -> Result<(), ErrBox> {
- let msg = &format!("read access to \"{}\"", filename);
- match self.allow_read.get_state() {
- PermissionAccessorState::Allow => {
- self.log_perm_access(msg);
- Ok(())
- }
- state => {
- if check_path_white_list(filename, &self.read_whitelist) {
- self.log_perm_access(msg);
- Ok(())
- } else {
- match state {
- PermissionAccessorState::Ask => Err(permission_denied_msg(
- "run again with the --allow-read flag".to_string(),
- )),
- PermissionAccessorState::Deny => Err(permission_denied_msg(
- "run again with the --allow-read flag".to_string(),
- )),
- _ => unreachable!(),
- }
- }
- }
+ self.check_permission_state(
+ self.get_state_read(&Some(filename)),
+ &format!("read access to \"{}\"", filename),
+ "run again with the --allow-read flag",
+ )
+ }
+
+ fn get_state_write(
+ &self,
+ filename: &Option<&str>,
+ ) -> PermissionAccessorState {
+ if check_path_white_list(filename, &self.write_whitelist) {
+ return PermissionAccessorState::Allow;
}
+ self.allow_write.get_state()
}
pub fn check_write(&self, filename: &str) -> Result<(), ErrBox> {
- let msg = &format!("write access to \"{}\"", filename);
- match self.allow_write.get_state() {
- PermissionAccessorState::Allow => {
- self.log_perm_access(msg);
- Ok(())
- }
- state => {
- if check_path_white_list(filename, &self.write_whitelist) {
- self.log_perm_access(msg);
- Ok(())
- } else {
- match state {
- PermissionAccessorState::Ask => Err(permission_denied_msg(
- "run again with the --allow-write flag".to_string(),
- )),
- PermissionAccessorState::Deny => Err(permission_denied_msg(
- "run again with the --allow-write flag".to_string(),
- )),
- _ => unreachable!(),
- }
- }
- }
+ self.check_permission_state(
+ self.get_state_write(&Some(filename)),
+ &format!("write access to \"{}\"", filename),
+ "run again with the --allow-write flag",
+ )
+ }
+
+ fn get_state_net(
+ &self,
+ host: &str,
+ port: Option<u16>,
+ ) -> PermissionAccessorState {
+ if check_host_and_port_whitelist(host, port, &self.net_whitelist) {
+ return PermissionAccessorState::Allow;
}
+ self.allow_net.get_state()
}
pub fn check_net(&self, hostname: &str, port: u16) -> Result<(), ErrBox> {
- let msg = &format!("network access to \"{}:{}\"", hostname, port);
- match self.allow_net.get_state() {
- PermissionAccessorState::Allow => {
- self.log_perm_access(msg);
- Ok(())
- }
- _state => {
- if self.net_whitelist.contains(hostname)
- || self
- .net_whitelist
- .contains(&format!("{}:{}", hostname, port))
- {
- self.log_perm_access(msg);
- Ok(())
- } else {
- Err(permission_denied_msg(
- "run again with the --allow-net flag".to_string(),
- ))
- }
- }
- }
+ self.check_permission_state(
+ self.get_state_net(hostname, Some(port)),
+ &format!("network access to \"{}:{}\"", hostname, port),
+ "run again with the --allow-net flag",
+ )
}
pub fn check_net_url(&self, url: &url::Url) -> Result<(), ErrBox> {
- let msg = &format!("network access to \"{}\"", url);
- match self.allow_net.get_state() {
- PermissionAccessorState::Allow => {
- self.log_perm_access(msg);
- Ok(())
- }
- _state => {
- let host = url.host().unwrap();
- let whitelist_result = {
- if self.net_whitelist.contains(&format!("{}", host)) {
- true
- } else {
- match url.port() {
- Some(port) => {
- self.net_whitelist.contains(&format!("{}:{}", host, port))
- }
- None => false,
- }
- }
- };
- if whitelist_result {
- self.log_perm_access(msg);
- Ok(())
- } else {
- Err(permission_denied_msg(
- "run again with the --allow-net flag".to_string(),
- ))
- }
- }
- }
+ self.check_permission_state(
+ self.get_state_net(&format!("{}", url.host().unwrap()), url.port()),
+ &format!("network access to \"{}\"", url),
+ "run again with the --allow-net flag",
+ )
}
pub fn check_env(&self) -> Result<(), ErrBox> {
- let msg = "access to environment variables";
- match self.allow_env.get_state() {
- PermissionAccessorState::Allow => {
- self.log_perm_access(msg);
- Ok(())
- }
- PermissionAccessorState::Ask => Err(permission_denied_msg(
- "run again with the --allow-env flag".to_string(),
- )),
- PermissionAccessorState::Deny => Err(permission_denied_msg(
- "run again with the --allow-env flag".to_string(),
- )),
- }
+ self.check_permission_state(
+ self.allow_env.get_state(),
+ "access to environment variables",
+ "run again with the --allow-env flag",
+ )
}
fn log_perm_access(&self, message: &str) {
@@ -292,66 +242,44 @@ impl DenoPermissions {
}
}
- pub fn allows_run(&self) -> bool {
- self.allow_run.is_allow()
- }
-
- pub fn allows_read(&self) -> bool {
- self.allow_read.is_allow()
- }
-
- pub fn allows_write(&self) -> bool {
- self.allow_write.is_allow()
- }
-
- pub fn allows_net(&self) -> bool {
- self.allow_net.is_allow()
- }
-
- pub fn allows_env(&self) -> bool {
- self.allow_env.is_allow()
- }
-
- pub fn allows_hrtime(&self) -> bool {
- self.allow_hrtime.is_allow()
- }
-
- pub fn revoke_run(&self) -> Result<(), ErrBox> {
- self.allow_run.revoke();
- Ok(())
- }
-
- pub fn revoke_read(&self) -> Result<(), ErrBox> {
- self.allow_read.revoke();
- Ok(())
- }
-
- pub fn revoke_write(&self) -> Result<(), ErrBox> {
- self.allow_write.revoke();
- Ok(())
- }
-
- pub fn revoke_net(&self) -> Result<(), ErrBox> {
- self.allow_net.revoke();
- Ok(())
- }
-
- pub fn revoke_env(&self) -> Result<(), ErrBox> {
- self.allow_env.revoke();
- Ok(())
- }
- pub fn revoke_hrtime(&self) -> Result<(), ErrBox> {
- self.allow_hrtime.revoke();
- Ok(())
+ pub fn get_permission_state(
+ &self,
+ name: &str,
+ url: &Option<&str>,
+ path: &Option<&str>,
+ ) -> Result<PermissionAccessorState, ErrBox> {
+ match name {
+ "run" => Ok(self.allow_run.get_state()),
+ "read" => Ok(self.get_state_read(path)),
+ "write" => Ok(self.get_state_write(path)),
+ "net" => {
+ // If url is not given, then just check the entire net permission
+ if url.is_none() {
+ return Ok(self.allow_net.get_state());
+ }
+ let url: &str = url.unwrap();
+ // If url is invalid, then throw a TypeError.
+ let parsed = Url::parse(url)
+ .map_err(|_| type_error(format!("Invalid url: {}", url)))?;
+ let state = self
+ .get_state_net(&format!("{}", parsed.host().unwrap()), parsed.port());
+ Ok(state)
+ }
+ "env" => Ok(self.allow_env.get_state()),
+ "hrtime" => Ok(self.allow_hrtime.get_state()),
+ n => Err(type_error(format!("No such permission name: {}", n))),
+ }
}
}
fn check_path_white_list(
- filename: &str,
+ filename: &Option<&str>,
white_list: &Arc<HashSet<String>>,
) -> bool {
- let mut path_buf = PathBuf::from(filename);
-
+ if filename.is_none() {
+ return false;
+ }
+ let mut path_buf = PathBuf::from(filename.unwrap());
loop {
if white_list.contains(path_buf.to_str().unwrap()) {
return true;
@@ -363,6 +291,16 @@ fn check_path_white_list(
false
}
+fn check_host_and_port_whitelist(
+ host: &str,
+ port: Option<u16>,
+ whitelist: &Arc<HashSet<String>>,
+) -> bool {
+ whitelist.contains(host)
+ || (port.is_some()
+ && whitelist.contains(&format!("{}:{}", host, port.unwrap())))
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/cli/tests/025_hrtime.ts b/cli/tests/025_hrtime.ts
index 417ca6982..9f60b7a77 100644
--- a/cli/tests/025_hrtime.ts
+++ b/cli/tests/025_hrtime.ts
@@ -1,3 +1,5 @@
-console.log(performance.now() % 2 !== 0);
-Deno.revokePermission("hrtime");
-console.log(performance.now() % 2 === 0);
+window.onload = async (): Promise<void> => {
+ console.log(performance.now() % 2 !== 0);
+ await Deno.permissions.revoke({ name: "hrtime" });
+ console.log(performance.now() % 2 === 0);
+};
diff --git a/std/manual.md b/std/manual.md
index 6da2d0ec2..70ecdc9ac 100644
--- a/std/manual.md
+++ b/std/manual.md
@@ -353,18 +353,19 @@ Sometimes a program may want to revoke previously granted permissions. When a
program, at a later stage, needs those permissions, it will fail.
```ts
-const { permissions, revokePermission, open, remove } = Deno;
+const { permissions, open, remove } = Deno;
// lookup a permission
-if (!permissions().write) {
+const status = await permissions.query({ name: "write" });
+if (status.state !== "granted") {
throw new Error("need write permission");
}
const log = await open("request.log", "a+");
// revoke some permissions
-revokePermission("read");
-revokePermission("write");
+await permissions.revoke({ name: "read" });
+await permissions.revoke({ name: "write" });
// use the log file
const encoder = new TextEncoder();