fix(acl): command scope should not error out if missing (#8675)

* fix(acl): command scope should not error out if missing

* propagate error
This commit is contained in:
Lucas Fernandes Nogueira 2024-01-29 11:39:56 -03:00 committed by GitHub
parent ec9818accb
commit 2631e97e2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -2,8 +2,8 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::collections::BTreeMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::{collections::BTreeMap, ops::Deref};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use state::TypeMap; use state::TypeMap;
@ -112,9 +112,27 @@ impl<T: Debug + DeserializeOwned + Send + Sync + 'static> ScopeValue<T> {
} }
} }
#[derive(Debug)]
enum OwnedOrRef<'a, T: Debug> {
Owned(T),
Ref(&'a T),
}
impl<'a, T: Debug> Deref for OwnedOrRef<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::Owned(t) => t,
Self::Ref(r) => r,
}
}
}
/// Access scope for a command that can be retrieved directly in the command function. /// Access scope for a command that can be retrieved directly in the command function.
#[derive(Debug)] #[derive(Debug)]
pub struct CommandScope<'a, T: Debug + DeserializeOwned + Send + Sync + 'static>(&'a ScopeValue<T>); pub struct CommandScope<'a, T: Debug + DeserializeOwned + Send + Sync + 'static>(
OwnedOrRef<'a, ScopeValue<T>>,
);
impl<'a, T: Debug + DeserializeOwned + Send + Sync + 'static> CommandScope<'a, T> { impl<'a, T: Debug + DeserializeOwned + Send + Sync + 'static> CommandScope<'a, T> {
/// What this access scope allows. /// What this access scope allows.
@ -133,22 +151,22 @@ impl<'a, R: Runtime, T: Debug + DeserializeOwned + Send + Sync + 'static> Comman
{ {
/// Grabs the [`ResolvedScope`] from the [`CommandItem`] and returns the associated [`CommandScope`]. /// Grabs the [`ResolvedScope`] from the [`CommandItem`] and returns the associated [`CommandScope`].
fn from_command(command: CommandItem<'a, R>) -> Result<Self, InvokeError> { fn from_command(command: CommandItem<'a, R>) -> Result<Self, InvokeError> {
command if let Some(scope_id) = command.acl.as_ref().and_then(|resolved| resolved.scope) {
.acl Ok(CommandScope(OwnedOrRef::Ref(
.as_ref()
.and_then(|resolved| resolved.scope)
.and_then(|scope_id| {
command command
.message .message
.webview .webview
.manager() .manager()
.runtime_authority .runtime_authority
.scope_manager .scope_manager
.get_command_scope_typed(&scope_id) .get_command_scope_typed(&scope_id)?,
.unwrap_or_default() )))
.map(CommandScope) } else {
}) Ok(CommandScope(OwnedOrRef::Owned(ScopeValue {
.ok_or_else(|| InvokeError::from_anyhow(anyhow::anyhow!("scope not found"))) allow: Vec::new(),
deny: Vec::new(),
})))
}
} }
} }
@ -175,6 +193,11 @@ impl<'a, R: Runtime, T: Debug + DeserializeOwned + Send + Sync + 'static> Comman
fn from_command(command: CommandItem<'a, R>) -> Result<Self, InvokeError> { fn from_command(command: CommandItem<'a, R>) -> Result<Self, InvokeError> {
command command
.plugin .plugin
.ok_or_else(|| {
InvokeError::from_anyhow(anyhow::anyhow!(
"global scope not available for app commands"
))
})
.and_then(|plugin| { .and_then(|plugin| {
command command
.message .message
@ -183,10 +206,9 @@ impl<'a, R: Runtime, T: Debug + DeserializeOwned + Send + Sync + 'static> Comman
.runtime_authority .runtime_authority
.scope_manager .scope_manager
.get_global_scope_typed(plugin) .get_global_scope_typed(plugin)
.ok() .map_err(InvokeError::from_error)
}) })
.map(GlobalScope) .map(GlobalScope)
.ok_or_else(|| InvokeError::from_anyhow(anyhow::anyhow!("global scope not found")))
} }
} }
@ -228,29 +250,31 @@ impl ScopeManager {
fn get_command_scope_typed<T: Send + Sync + DeserializeOwned + Debug + 'static>( fn get_command_scope_typed<T: Send + Sync + DeserializeOwned + Debug + 'static>(
&self, &self,
key: &ScopeKey, key: &ScopeKey,
) -> crate::Result<Option<&ScopeValue<T>>> { ) -> crate::Result<&ScopeValue<T>> {
let cache = self.command_cache.get(key).unwrap(); let cache = self.command_cache.get(key).unwrap();
match cache.try_get() { match cache.try_get() {
cached @ Some(_) => Ok(cached), Some(cached) => Ok(cached),
None => match self.command_scope.get(key).map(|r| { None => {
let resolved_scope = self
.command_scope
.get(key)
.unwrap_or_else(|| panic!("missing command scope for key {key}"));
let mut allow: Vec<T> = Vec::new(); let mut allow: Vec<T> = Vec::new();
let mut deny: Vec<T> = Vec::new(); let mut deny: Vec<T> = Vec::new();
for allowed in &r.allow { for allowed in &resolved_scope.allow {
allow.push(serde_json::from_value(allowed.clone().into())?); allow.push(serde_json::from_value(allowed.clone().into())?);
} }
for denied in &r.deny { for denied in &resolved_scope.deny {
deny.push(serde_json::from_value(denied.clone().into())?); deny.push(serde_json::from_value(denied.clone().into())?);
} }
crate::Result::Ok(Some(ScopeValue { allow, deny })) let value = ScopeValue { allow, deny };
}) {
None => Ok(None), let _ = cache.set(value);
Some(value) => { Ok(cache.get())
let _ = cache.set(value); }
Ok(cache.try_get())
}
},
} }
} }
} }