mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 20:29:11 +00:00
358 lines
9.7 KiB
Rust
358 lines
9.7 KiB
Rust
// Copyright 2018-2025 the Deno authors. MIT license.
|
|
|
|
use indexmap::IndexMap;
|
|
use serde::Deserialize;
|
|
use url::Url;
|
|
|
|
use super::UndefinedPermissionError;
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub enum PermissionConfigValue {
|
|
All,
|
|
Some(Vec<String>),
|
|
None,
|
|
}
|
|
|
|
impl<'de> serde::Deserialize<'de> for PermissionConfigValue {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
struct Visitor;
|
|
impl<'d> serde::de::Visitor<'d> for Visitor {
|
|
type Value = PermissionConfigValue;
|
|
|
|
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
write!(f, "either an array or bool")
|
|
}
|
|
|
|
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
|
|
where
|
|
E: serde::de::Error,
|
|
{
|
|
if v {
|
|
Ok(PermissionConfigValue::All)
|
|
} else {
|
|
Ok(PermissionConfigValue::None)
|
|
}
|
|
}
|
|
|
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
|
where
|
|
A: serde::de::SeqAccess<'d>,
|
|
{
|
|
let mut out = Vec::with_capacity(seq.size_hint().unwrap_or(8));
|
|
while let Some(element) = seq.next_element::<String>()? {
|
|
out.push(element);
|
|
}
|
|
|
|
if out.is_empty() {
|
|
Ok(PermissionConfigValue::None)
|
|
} else {
|
|
Ok(PermissionConfigValue::Some(out))
|
|
}
|
|
}
|
|
|
|
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
|
where
|
|
E: serde::de::Error,
|
|
{
|
|
Ok(PermissionConfigValue::None)
|
|
}
|
|
}
|
|
deserializer.deserialize_any(Visitor)
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Default, Clone, Debug, PartialEq, Eq, Hash)]
|
|
#[serde(default, deny_unknown_fields)]
|
|
pub struct AllowDenyPermissionConfig {
|
|
pub allow: Option<PermissionConfigValue>,
|
|
pub deny: Option<PermissionConfigValue>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(untagged)]
|
|
pub enum AllowDenyPermissionConfigValue {
|
|
Boolean(bool),
|
|
AllowList(Vec<String>),
|
|
Object(AllowDenyPermissionConfig),
|
|
}
|
|
|
|
fn deserialize_allow_deny<'de, D: serde::Deserializer<'de>>(
|
|
de: D,
|
|
) -> Result<AllowDenyPermissionConfig, D::Error> {
|
|
AllowDenyPermissionConfigValue::deserialize(de).map(|value| match value {
|
|
AllowDenyPermissionConfigValue::Boolean(b) => AllowDenyPermissionConfig {
|
|
allow: Some(if b {
|
|
PermissionConfigValue::All
|
|
} else {
|
|
PermissionConfigValue::None
|
|
}),
|
|
deny: None,
|
|
},
|
|
AllowDenyPermissionConfigValue::AllowList(allow) => {
|
|
AllowDenyPermissionConfig {
|
|
allow: Some(if allow.is_empty() {
|
|
PermissionConfigValue::None
|
|
} else {
|
|
PermissionConfigValue::Some(allow)
|
|
}),
|
|
deny: None,
|
|
}
|
|
}
|
|
AllowDenyPermissionConfigValue::Object(allow_deny) => allow_deny,
|
|
})
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Hash)]
|
|
#[serde(untagged)]
|
|
pub enum PermissionNameOrObject {
|
|
Name(String),
|
|
Object(Box<PermissionsObject>),
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct PermissionsObjectWithBase {
|
|
pub base: Url,
|
|
pub permissions: PermissionsObject,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Default, Hash)]
|
|
#[serde(default, deny_unknown_fields)]
|
|
pub struct PermissionsObject {
|
|
#[serde(default)]
|
|
pub all: Option<bool>,
|
|
#[serde(default, deserialize_with = "deserialize_allow_deny")]
|
|
pub read: AllowDenyPermissionConfig,
|
|
#[serde(default, deserialize_with = "deserialize_allow_deny")]
|
|
pub write: AllowDenyPermissionConfig,
|
|
#[serde(default, deserialize_with = "deserialize_allow_deny")]
|
|
pub import: AllowDenyPermissionConfig,
|
|
#[serde(default, deserialize_with = "deserialize_allow_deny")]
|
|
pub env: AllowDenyPermissionConfig,
|
|
#[serde(default, deserialize_with = "deserialize_allow_deny")]
|
|
pub net: AllowDenyPermissionConfig,
|
|
#[serde(default, deserialize_with = "deserialize_allow_deny")]
|
|
pub run: AllowDenyPermissionConfig,
|
|
#[serde(default, deserialize_with = "deserialize_allow_deny")]
|
|
pub ffi: AllowDenyPermissionConfig,
|
|
#[serde(default, deserialize_with = "deserialize_allow_deny")]
|
|
pub sys: AllowDenyPermissionConfig,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
pub struct PermissionsConfig {
|
|
pub sets: IndexMap<String, PermissionsObjectWithBase>,
|
|
}
|
|
|
|
impl PermissionsConfig {
|
|
pub fn parse(
|
|
value: serde_json::Value,
|
|
base: &Url,
|
|
) -> Result<Self, serde_json::Error> {
|
|
let sets: IndexMap<String, PermissionsObject> =
|
|
serde_json::from_value(value)?;
|
|
Ok(Self {
|
|
sets: sets
|
|
.into_iter()
|
|
.map(|(k, permissions)| {
|
|
(
|
|
k,
|
|
PermissionsObjectWithBase {
|
|
base: base.clone(),
|
|
permissions,
|
|
},
|
|
)
|
|
})
|
|
.collect(),
|
|
})
|
|
}
|
|
|
|
pub fn get(
|
|
&self,
|
|
name: &str,
|
|
) -> Result<&PermissionsObjectWithBase, UndefinedPermissionError> {
|
|
match self.sets.get(name) {
|
|
Some(value) => Ok(value),
|
|
None => Err(UndefinedPermissionError(name.to_string())),
|
|
}
|
|
}
|
|
|
|
pub fn merge(self, member: Self) -> Self {
|
|
let mut sets = self.sets;
|
|
|
|
for (key, value) in member.sets {
|
|
// When the same key exists in the root and the member, we overwrite
|
|
// with the member instead of merging because we don't want someone looking
|
|
// at a member config file and not realizing the permissions are extended
|
|
// in the root. In the future, we may add an explicit "extends" concept in
|
|
// permissions in order to support this scenario.
|
|
sets.insert(key, value);
|
|
}
|
|
|
|
Self { sets }
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use pretty_assertions::assert_eq;
|
|
use serde_json::json;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn deserialize() {
|
|
assert_eq!(
|
|
serde_json::from_value::<PermissionsObject>(json!({
|
|
"all": true,
|
|
"read": true,
|
|
"write": true,
|
|
"import": true,
|
|
"env": true,
|
|
"net": true,
|
|
"run": true,
|
|
"ffi": true,
|
|
"sys": false,
|
|
}))
|
|
.unwrap(),
|
|
PermissionsObject {
|
|
all: Some(true),
|
|
read: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::All),
|
|
deny: None,
|
|
},
|
|
write: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::All),
|
|
deny: None,
|
|
},
|
|
import: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::All),
|
|
deny: None,
|
|
},
|
|
env: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::All),
|
|
deny: None,
|
|
},
|
|
net: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::All),
|
|
deny: None,
|
|
},
|
|
run: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::All),
|
|
deny: None,
|
|
},
|
|
ffi: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::All),
|
|
deny: None,
|
|
},
|
|
sys: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::None),
|
|
deny: None,
|
|
}
|
|
}
|
|
);
|
|
|
|
assert_eq!(
|
|
serde_json::from_value::<PermissionsObject>(json!({
|
|
"read": ["test"],
|
|
"write": ["test"],
|
|
"import": ["test"],
|
|
"env": ["test"],
|
|
"net": ["test"],
|
|
"run": ["test"],
|
|
"ffi": ["test"],
|
|
"sys": ["test"],
|
|
}))
|
|
.unwrap(),
|
|
PermissionsObject {
|
|
all: None,
|
|
read: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec!["test".to_string()])),
|
|
deny: None
|
|
},
|
|
write: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec!["test".to_string()])),
|
|
deny: None,
|
|
},
|
|
import: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec!["test".to_string()])),
|
|
deny: None,
|
|
},
|
|
env: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec!["test".to_string()])),
|
|
deny: None,
|
|
},
|
|
net: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec!["test".to_string()])),
|
|
deny: None,
|
|
},
|
|
run: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec!["test".to_string()])),
|
|
deny: None,
|
|
},
|
|
ffi: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec!["test".to_string()])),
|
|
deny: None,
|
|
},
|
|
sys: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec!["test".to_string()])),
|
|
deny: None,
|
|
}
|
|
}
|
|
);
|
|
|
|
assert_eq!(
|
|
serde_json::from_value::<PermissionsObject>(json!({
|
|
"read": {
|
|
"allow": ["test"],
|
|
"deny": ["test-deny"],
|
|
},
|
|
"write": [],
|
|
"sys": {
|
|
"allow": []
|
|
}
|
|
}))
|
|
.unwrap(),
|
|
PermissionsObject {
|
|
all: None,
|
|
read: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::Some(vec!["test".to_string()])),
|
|
deny: Some(PermissionConfigValue::Some(vec![
|
|
"test-deny".to_string()
|
|
])),
|
|
},
|
|
write: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::None),
|
|
deny: None
|
|
},
|
|
sys: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::None),
|
|
deny: None
|
|
},
|
|
..Default::default()
|
|
}
|
|
);
|
|
|
|
assert_eq!(
|
|
serde_json::from_value::<PermissionsObject>(json!({
|
|
"read": {
|
|
"allow": true,
|
|
"deny": ["test-deny"],
|
|
},
|
|
}))
|
|
.unwrap(),
|
|
PermissionsObject {
|
|
all: None,
|
|
read: AllowDenyPermissionConfig {
|
|
allow: Some(PermissionConfigValue::All),
|
|
deny: Some(PermissionConfigValue::Some(vec![
|
|
"test-deny".to_string()
|
|
])),
|
|
},
|
|
..Default::default()
|
|
}
|
|
);
|
|
}
|
|
}
|