diff --git a/appmgr/src/config/mod.rs b/appmgr/src/config/mod.rs index 0dc3746ba..d824b0675 100644 --- a/appmgr/src/config/mod.rs +++ b/appmgr/src/config/mod.rs @@ -6,7 +6,7 @@ use bollard::container::KillContainerOptions; use futures::future::{BoxFuture, FutureExt}; use indexmap::IndexSet; use itertools::Itertools; -use patch_db::DbHandle; +use patch_db::{DbHandle, ModelData, OptionModel}; use rand::SeedableRng; use regex::Regex; use rpc_toolkit::command; @@ -21,7 +21,7 @@ use crate::db::util::WithRevision; use crate::dependencies::{ update_current_dependents, BreakageRes, DependencyError, TaggedDependencyError, }; -use crate::s9pk::manifest::PackageId; +use crate::s9pk::manifest::{Manifest, ManifestModel, PackageId}; use crate::status::handle_broken_dependents; use crate::util::{ display_none, display_serializable, parse_duration, parse_stdin_deserializable, IoFormat, @@ -63,6 +63,8 @@ pub enum ConfigurationError { NoMatch(#[from] NoMatchWithPath), #[error("System Error: {0}")] SystemError(Error), + #[error("Permission Denied: {0}")] + PermissionDenied(ValueSpecPointer), } impl From for Error { fn from(err: ConfigurationError) -> Self { @@ -326,8 +328,20 @@ pub fn configure<'a, Db: DbHandle>( spec.gen(&mut rand::rngs::StdRng::from_entropy(), timeout)? }; + let manifest = crate::db::DatabaseModel::new() + .package_data() + .idx_model(id) + .and_then(|m| m.installed()) + .map::<_, Manifest>(|i| i.manifest()) + .expect(db) + .await? + .get(db, true) + .await + .with_kind(crate::ErrorKind::NotFound)?; + spec.matches(&config)?; // check that new config matches spec - spec.update(ctx, db, &*overrides, &mut config).await?; // dereference pointers in the new config + spec.update(ctx, db, &*manifest, &*overrides, &mut config) + .await?; // dereference pointers in the new config // create backreferences to pointers let mut sys = pkg_model.clone().system_pointers().get_mut(db).await?; diff --git a/appmgr/src/config/spec.rs b/appmgr/src/config/spec.rs index 94c3bc29e..cc9fe3961 100644 --- a/appmgr/src/config/spec.rs +++ b/appmgr/src/config/spec.rs @@ -12,12 +12,12 @@ use async_trait::async_trait; use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use jsonpath_lib::Compiled as CompiledJsonPath; -use lazy_static::__Deref; use patch_db::{DbHandle, OptionModel}; use rand::{CryptoRng, Rng}; use regex::Regex; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::{Number, Value}; +use sqlx::SqlitePool; use super::util::{self, CharSet, NumRange, UniqueBy, STATIC_NULL}; use super::{Config, MatchError, NoMatchWithPath, TimeoutError, TypeOf}; @@ -41,6 +41,7 @@ pub trait ValueSpec { &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError>; @@ -156,10 +157,13 @@ where &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { - self.inner.update(ctx, db, config_overrides, value).await + self.inner + .update(ctx, db, manifest, config_overrides, value) + .await } fn pointers(&self, value: &Value) -> Result, NoMatchWithPath> { self.inner.pointers(value) @@ -197,10 +201,13 @@ where &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { - self.inner.update(ctx, db, config_overrides, value).await + self.inner + .update(ctx, db, manifest, config_overrides, value) + .await } fn pointers(&self, value: &Value) -> Result, NoMatchWithPath> { self.inner.pointers(value) @@ -271,10 +278,13 @@ where &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { - self.inner.update(ctx, db, config_overrides, value).await + self.inner + .update(ctx, db, manifest, config_overrides, value) + .await } fn pointers(&self, value: &Value) -> Result, NoMatchWithPath> { self.inner.pointers(value) @@ -382,18 +392,19 @@ impl ValueSpec for ValueSpecAny { &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { match self { - ValueSpecAny::Boolean(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecAny::Enum(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecAny::List(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecAny::Number(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecAny::Object(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecAny::String(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecAny::Union(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecAny::Pointer(a) => a.update(ctx, db, config_overrides, value).await, + ValueSpecAny::Boolean(a) => a.update(ctx, db, manifest, config_overrides, value).await, + ValueSpecAny::Enum(a) => a.update(ctx, db, manifest, config_overrides, value).await, + ValueSpecAny::List(a) => a.update(ctx, db, manifest, config_overrides, value).await, + ValueSpecAny::Number(a) => a.update(ctx, db, manifest, config_overrides, value).await, + ValueSpecAny::Object(a) => a.update(ctx, db, manifest, config_overrides, value).await, + ValueSpecAny::String(a) => a.update(ctx, db, manifest, config_overrides, value).await, + ValueSpecAny::Union(a) => a.update(ctx, db, manifest, config_overrides, value).await, + ValueSpecAny::Pointer(a) => a.update(ctx, db, manifest, config_overrides, value).await, } } fn pointers(&self, value: &Value) -> Result, NoMatchWithPath> { @@ -473,8 +484,9 @@ impl ValueSpec for ValueSpecBoolean { } async fn update( &self, - ctx: &RpcContext, + _ctx: &RpcContext, _db: &mut Db, + _manifest: &Manifest, _config_overrides: &BTreeMap, _value: &mut Value, ) -> Result<(), ConfigurationError> { @@ -561,8 +573,9 @@ impl ValueSpec for ValueSpecEnum { } async fn update( &self, - ctx: &RpcContext, + _ctx: &RpcContext, _db: &mut Db, + _manifest: &Manifest, _config_overrides: &BTreeMap, _value: &mut Value, ) -> Result<(), ConfigurationError> { @@ -648,12 +661,17 @@ where &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { if let Value::Array(ref mut ls) = value { for (i, val) in ls.into_iter().enumerate() { - match self.spec.update(ctx, db, config_overrides, val).await { + match self + .spec + .update(ctx, db, manifest, config_overrides, val) + .await + { Err(ConfigurationError::NoMatch(e)) => { Err(ConfigurationError::NoMatch(e.prepend(format!("{}", i)))) } @@ -750,15 +768,16 @@ impl ValueSpec for ValueSpecList { &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { match self { - ValueSpecList::Enum(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecList::Number(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecList::Object(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecList::String(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecList::Union(a) => a.update(ctx, db, config_overrides, value).await, + ValueSpecList::Enum(a) => a.update(ctx, db, manifest, config_overrides, value).await, + ValueSpecList::Number(a) => a.update(ctx, db, manifest, config_overrides, value).await, + ValueSpecList::Object(a) => a.update(ctx, db, manifest, config_overrides, value).await, + ValueSpecList::String(a) => a.update(ctx, db, manifest, config_overrides, value).await, + ValueSpecList::Union(a) => a.update(ctx, db, manifest, config_overrides, value).await, } } fn pointers(&self, value: &Value) -> Result, NoMatchWithPath> { @@ -871,8 +890,9 @@ impl ValueSpec for ValueSpecNumber { } async fn update( &self, - ctx: &RpcContext, + _ctx: &RpcContext, _db: &mut Db, + _manifest: &Manifest, _config_overrides: &BTreeMap, _value: &mut Value, ) -> Result<(), ConfigurationError> { @@ -935,11 +955,14 @@ impl ValueSpec for ValueSpecObject { &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { if let Value::Object(o) = value { - self.spec.update(ctx, db, config_overrides, o).await + self.spec + .update(ctx, db, manifest, config_overrides, o) + .await } else { Err(ConfigurationError::NoMatch(NoMatchWithPath::new( MatchError::InvalidType("object", value.type_of()), @@ -1034,6 +1057,7 @@ impl ConfigSpec { &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, cfg: &mut Config, ) -> Result<(), ConfigurationError> { @@ -1041,10 +1065,11 @@ impl ConfigSpec { match cfg.get_mut(k) { None => { let mut v = Value::Null; - vs.update(ctx, db, config_overrides, &mut v).await?; + vs.update(ctx, db, manifest, config_overrides, &mut v) + .await?; cfg.insert(k.clone(), v); } - Some(v) => match vs.update(ctx, db, config_overrides, v).await { + Some(v) => match vs.update(ctx, db, manifest, config_overrides, v).await { Err(ConfigurationError::NoMatch(e)) => { Err(ConfigurationError::NoMatch(e.prepend(k.clone()))) } @@ -1124,8 +1149,9 @@ impl ValueSpec for ValueSpecString { } async fn update( &self, - ctx: &RpcContext, + _ctx: &RpcContext, _db: &mut Db, + _manifest: &Manifest, _config_overrides: &BTreeMap, _value: &mut Value, ) -> Result<(), ConfigurationError> { @@ -1336,6 +1362,7 @@ impl ValueSpec for ValueSpecUnion { &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { @@ -1348,7 +1375,7 @@ impl ValueSpec for ValueSpecUnion { None => Err(ConfigurationError::NoMatch(NoMatchWithPath::new( MatchError::Union(tag.clone(), self.variants.keys().cloned().collect()), ))), - Some(spec) => spec.update(ctx, db, config_overrides, o).await, + Some(spec) => spec.update(ctx, db, manifest, config_overrides, o).await, }, Some(other) => Err(ConfigurationError::NoMatch( NoMatchWithPath::new(MatchError::InvalidType("string", other.type_of())) @@ -1477,12 +1504,17 @@ impl ValueSpec for ValueSpecPointer { &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { match self { - ValueSpecPointer::Package(a) => a.update(ctx, db, config_overrides, value).await, - ValueSpecPointer::System(a) => a.update(ctx, db, config_overrides, value).await, + ValueSpecPointer::Package(a) => { + a.update(ctx, db, manifest, config_overrides, value).await + } + ValueSpecPointer::System(a) => { + a.update(ctx, db, manifest, config_overrides, value).await + } } } fn pointers(&self, _value: &Value) -> Result, NoMatchWithPath> { @@ -1505,6 +1537,7 @@ impl ValueSpec for ValueSpecPointer { #[serde(tag = "target")] #[serde(rename_all = "kebab-case")] pub enum PackagePointerSpec { + TorKey(TorKeyPointer), TorAddress(TorAddressPointer), LanAddress(LanAddressPointer), Config(ConfigPointer), @@ -1512,6 +1545,7 @@ pub enum PackagePointerSpec { impl PackagePointerSpec { pub fn package_id(&self) -> &PackageId { match self { + PackagePointerSpec::TorKey(TorKeyPointer { package_id, .. }) => package_id, PackagePointerSpec::TorAddress(TorAddressPointer { package_id, .. }) => package_id, PackagePointerSpec::LanAddress(LanAddressPointer { package_id, .. }) => package_id, PackagePointerSpec::Config(ConfigPointer { package_id, .. }) => package_id, @@ -1521,9 +1555,11 @@ impl PackagePointerSpec { &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, ) -> Result { match &self { + PackagePointerSpec::TorKey(key) => key.deref(&manifest.id, &ctx.secret_store).await, PackagePointerSpec::TorAddress(tor) => tor.deref(db).await, PackagePointerSpec::LanAddress(lan) => lan.deref(db).await, PackagePointerSpec::Config(cfg) => cfg.deref(ctx, db, config_overrides).await, @@ -1533,6 +1569,7 @@ impl PackagePointerSpec { impl fmt::Display for PackagePointerSpec { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + PackagePointerSpec::TorKey(key) => write!(f, "{}", key), PackagePointerSpec::TorAddress(tor) => write!(f, "{}", tor), PackagePointerSpec::LanAddress(lan) => write!(f, "{}", lan), PackagePointerSpec::Config(cfg) => write!(f, "{}", cfg), @@ -1570,10 +1607,11 @@ impl ValueSpec for PackagePointerSpec { &self, ctx: &RpcContext, db: &mut Db, + manifest: &Manifest, config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { - *value = self.deref(ctx, db, config_overrides).await?; + *value = self.deref(ctx, db, manifest, config_overrides).await?; Ok(()) } fn pointers(&self, _value: &Value) -> Result, NoMatchWithPath> { @@ -1670,7 +1708,7 @@ impl ConfigPointer { if let Some(cfg) = config_overrides.get(&self.package_id) { Ok(self.select(&Value::Object(cfg.clone()))) } else { - let manifest_model: OptionModel<_> = crate::db::DatabaseModel::new() + let manifest_model: OptionModel = crate::db::DatabaseModel::new() .package_data() .idx_model(&self.package_id) .and_then(|pde| pde.installed()) @@ -1773,6 +1811,44 @@ impl Hash for ConfigSelector { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +#[serde(tag = "target")] +pub struct TorKeyPointer { + package_id: PackageId, + interface: InterfaceId, +} +impl TorKeyPointer { + async fn deref( + &self, + source_package: &PackageId, + secrets: &SqlitePool, + ) -> Result { + if &self.package_id != source_package { + return Err(ConfigurationError::PermissionDenied( + ValueSpecPointer::Package(PackagePointerSpec::TorKey(self.clone())), + )); + } + let x = sqlx::query!( + "SELECT key FROM tor WHERE package = ? AND interface = ?", + *self.package_id, + *self.interface + ) + .fetch_one(secrets) + .await + .map_err(|e| ConfigurationError::SystemError(e.into()))?; + Ok(Value::String(base32::encode( + base32::Alphabet::RFC4648 { padding: false }, + &x.key, + ))) + } +} +impl fmt::Display for TorKeyPointer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}: tor-key: {}", self.package_id, self.interface) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] #[serde(tag = "target")] @@ -1809,6 +1885,7 @@ impl ValueSpec for SystemPointerSpec { &self, ctx: &RpcContext, db: &mut Db, + _manifest: &Manifest, _config_overrides: &BTreeMap, value: &mut Value, ) -> Result<(), ConfigurationError> { diff --git a/appmgr/src/s9pk/manifest.rs b/appmgr/src/s9pk/manifest.rs index a5df1618c..a46e8bc90 100644 --- a/appmgr/src/s9pk/manifest.rs +++ b/appmgr/src/s9pk/manifest.rs @@ -102,6 +102,7 @@ where pub struct Manifest { pub id: PackageId, pub title: String, + #[model] pub version: Version, pub description: Description, #[serde(default)] diff --git a/appmgr/src/volume.rs b/appmgr/src/volume.rs index b3e08070f..b5e721d99 100644 --- a/appmgr/src/volume.rs +++ b/appmgr/src/volume.rs @@ -128,6 +128,7 @@ impl Map for Volumes { self.0.get(key) } } +pub type VolumesModel = MapModel; impl HasModel for Volumes { type Model = MapModel; }