From 7e89edc30fc70570f7bda06b71d0d6346f163593 Mon Sep 17 00:00:00 2001 From: Orion Kindel Date: Fri, 21 Jul 2023 13:21:09 -0500 Subject: [PATCH] fix: set and unset acting usr based on actor --- src/main.rs | 4 +++- src/model/group.rs | 40 +++++++++++++++++++-------------------- src/model/hashed_text.rs | 28 +++++++++++++++++++++------ src/model/user.rs | 10 +++++----- src/model/user_session.rs | 4 ++-- src/postgres.rs | 36 +++++++++++++++++++++++------------ 6 files changed, 76 insertions(+), 46 deletions(-) diff --git a/src/main.rs b/src/main.rs index a9132df..123a446 100644 --- a/src/main.rs +++ b/src/main.rs @@ -161,7 +161,9 @@ fn handle_request(app: &A, req: Addrd>) -> Addrd let actor = dbg!(actor); let not_found = || { - log::info!("{:?} {} not found", req.data().msg().code, req.data().msg().path_string().unwrap()); + log::info!("{:?} {} not found", + req.data().msg().code, + req.data().msg().path_string().unwrap()); Message::builder(Type::Ack, toad::resp::code::NOT_FOUND).token(req.data().msg().token) .build() }; diff --git a/src/model/group.rs b/src/model/group.rs index 36afdb5..af59ccf 100644 --- a/src/model/group.rs +++ b/src/model/group.rs @@ -99,20 +99,20 @@ impl GroupRepo> for GroupRepoImpl where Db: Postgres + 'stat static QUERY: &'static str = "select g.tag :: text, g.uid :: text from public.usr_groups(($1 :: text) :: human_uuid.huid) as g"; let grps = self.0 - .with_client(|c| c.query(QUERY, &[&id.as_ref()])) - .and_then(|rows| { - rows.iter() - .map(Group::unmarshal) - .collect::, _>>() - }) - .map_err(GroupRepoError::Other)?; + .with_client(actor, |c| c.query(QUERY, &[&id.as_ref()])) + .and_then(|rows| { + rows.iter() + .map(Group::unmarshal) + .collect::, _>>() + }) + .map_err(GroupRepoError::Other)?; - let paths = grps.iter().map(|g| Path::parse(format!("/groups/{}/members", g.uid))).collect::>(); + let paths = grps.iter() + .map(|g| Path::parse(format!("/groups/{}/members", g.uid))) + .collect::>(); if !self.0 - .authorized_all(&paths, - Mode::Read, - actor) + .authorized_all(&paths, Mode::Read, actor) .map_err(GroupRepoError::Other)? { return Err(GroupRepoError::Unauthorized); @@ -134,7 +134,7 @@ impl GroupRepo> for GroupRepoImpl where Db: Postgres + 'stat } self.0 - .with_client(|c| c.query(QUERY, &[&id.as_ref()])) + .with_client(actor, |c| c.query(QUERY, &[&id.as_ref()])) .and_then(|rows| { rows.into_iter() .map(|row| row.try_get::<_, String>("u.uid").map(UserId::from)) @@ -167,7 +167,7 @@ impl ReadOne for GroupRepoImpl where Db: Postgres + 'static } self.0 - .with_client(|c| c.query_opt(QUERY, &[&id.as_ref()])) + .with_client(actor, |c| c.query_opt(QUERY, &[&id.as_ref()])) .and_then(|opt| { opt.as_ref() .map(Group::unmarshal) @@ -204,7 +204,7 @@ impl ReadMany for GroupRepoImpl where Db: Postgres + 'static let grps: Vec = self.0 - .with_client(|c| match page.after { + .with_client(actor, |c| match page.after { | Some(after) => { c.query(&QUERY_AFTER_LINES.iter() .fold(String::new(), |b, a| format!("{b}{a}\n")), @@ -252,9 +252,9 @@ impl Patch for GroupRepoImpl where Db: Postgres + 'static let paths = vec![Some(format!("/groups/{id}/name")).filter(|_| state.name.is_some())].into_iter() - .filter_map(identity) - .map(Path::parse) - .collect::>(); + .filter_map(identity) + .map(Path::parse) + .collect::>(); if !self.0 .authorized_all(&paths, Mode::Write, actor) .map_err(GroupRepoError::Other)? @@ -263,7 +263,7 @@ impl Patch for GroupRepoImpl where Db: Postgres + 'static } self.0 - .with_client(|c| { + .with_client(actor, |c| { c.execute(&QUERY_LINES.iter() .fold(String::new(), |b, a| format!("{b}{a}\n")), &[&id.as_ref(), &state.name.as_ref().map(|n| n.as_ref())]) @@ -292,7 +292,7 @@ impl Insert for GroupRepoImpl where Db: Postgres + 'static } self.0 - .with_client(|c| { + .with_client(actor, |c| { c.query_one(&QUERY_LINES.iter() .fold(String::new(), |b, a| format!("{b}{a}\n")), &[&insert.name.as_ref()]) @@ -318,7 +318,7 @@ impl Del for GroupRepoImpl where Db: Postgres + 'static } self.0 - .with_client(|c| c.execute(QUERY, &[&id.as_ref()])) + .with_client(actor, |c| c.execute(QUERY, &[&id.as_ref()])) .map(|n| n == 1) .map_err(GroupRepoError::Other) } diff --git a/src/model/hashed_text.rs b/src/model/hashed_text.rs index 61ebc35..4c1475a 100644 --- a/src/model/hashed_text.rs +++ b/src/model/hashed_text.rs @@ -1,5 +1,6 @@ use postgres::{GenericClient, GenericRow}; +use super::Actor; use crate::newtype; use crate::postgres::{DbError, Postgres}; use crate::repo::Ext; @@ -10,7 +11,11 @@ newtype!( ); pub trait HashedTextExt: Ext { - fn matches>(&self, this: &HashedText, other: S) -> Result; + fn matches>(&self, + actor: &Actor, + this: &HashedText, + other: S) + -> Result; } pub struct HashedTextExtImpl(pub &'static Db); @@ -22,11 +27,17 @@ impl Ext for HashedTextExtImpl where Db: Postgres impl HashedTextExt for HashedTextExtImpl where Db: Postgres { - fn matches>(&self, this: &HashedText, other: S) -> Result> { + fn matches>(&self, + actor: &Actor, + this: &HashedText, + other: S) + -> Result> { static QUERY: &str = "select public.hashed_text_matches($1, public.hashed_text_of_string($2))"; self.0 - .with_client(|client| client.query_one(QUERY, &[&other.as_ref(), &this.0.as_str()])) + .with_client(actor, |client| { + client.query_one(QUERY, &[&other.as_ref(), &this.0.as_str()]) + }) .and_then(|row| row.try_get(0)) } } @@ -36,13 +47,18 @@ mod test { use postgres::types::Type; use super::{HashedText, HashedTextExt, HashedTextExtImpl}; + use crate::model::Actor; use crate::postgres::test::{from_sql_owned, Client, Row}; use crate::postgres::{Postgres, PostgresImpl}; #[test] fn hashed_text_matches_fn_call() { let client = || Client { query_one: Box::new(|_, q, ps| { - assert_eq!(q.unwrap_str(), "select public.hashed_text_matches($1, public.hashed_text_of_string($2))"); + let q = q.unwrap_str(); + if q.contains("set_acting_usr") { + return Ok(Row::new(vec![])); + } + assert_eq!(q, "select public.hashed_text_matches($1, public.hashed_text_of_string($2))"); assert_eq!(from_sql_owned::(ps[1]), String::from("XXX")); Ok(Row::new(vec![("", @@ -57,9 +73,9 @@ mod test { std::mem::transmute::<_, &'static PostgresImpl>>(&pg) }); - assert!(htext.matches(&HashedText(String::from("XXX")), "foo") + assert!(htext.matches(&Actor::default(), &HashedText(String::from("XXX")), "foo") .unwrap()); - assert!(!htext.matches(&HashedText(String::from("XXX")), "foob") + assert!(!htext.matches(&Actor::default(), &HashedText(String::from("XXX")), "foob") .unwrap()); } } diff --git a/src/model/user.rs b/src/model/user.rs index 93094e6..e5c953f 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -128,7 +128,7 @@ impl ReadOne for UserRepoImpl where Db: Postgres + 'static } self.0 - .with_client(|c| { + .with_client(actor, |c| { c.query_opt(&QUERY_LINES.iter() .fold(String::new(), |b, a| format!("{b}{a}\n")), &[&id.as_ref()]) @@ -177,7 +177,7 @@ impl ReadMany for UserRepoImpl where Db: Postgres + 'static let usrs: Vec = self.0 - .with_client(|c| match page.after { + .with_client(actor, |c| match page.after { | Some(after) => { c.query(&QUERY_AFTER_LINES.iter() .fold(String::new(), |b, a| format!("{b}{a}\n")), @@ -238,7 +238,7 @@ impl Patch for UserRepoImpl where Db: Postgres + 'static } self.0 - .with_client(|c| { + .with_client(actor, |c| { c.execute(&QUERY_LINES.iter() .fold(String::new(), |b, a| format!("{b}{a}\n")), &[&id.as_ref(), @@ -272,7 +272,7 @@ impl Insert for UserRepoImpl where Db: Postgres + 'static } self.0 - .with_client(|c| { + .with_client(actor, |c| { c.query_one(&QUERY_LINES.iter() .fold(String::new(), |b, a| format!("{b}{a}\n")), &[&insert.tag, @@ -302,7 +302,7 @@ impl Del for UserRepoImpl where Db: Postgres + 'static } self.0 - .with_client(|c| c.execute(QUERY, &[&id.as_ref()])) + .with_client(actor, |c| c.execute(QUERY, &[&id.as_ref()])) .map(|n| n == 1) .map_err(Other) } diff --git a/src/model/user_session.rs b/src/model/user_session.rs index 2f0863f..4a8228e 100644 --- a/src/model/user_session.rs +++ b/src/model/user_session.rs @@ -77,7 +77,7 @@ impl UserSessionExt for UserSessionExtImpl let expr = "public.usr_session_touch(public.usr_session_key_of_string($1))"; let query = format!("select {cols} from {expr} as u"); self.0 - .with_client(|c| { + .with_client(&Actor::default(), |c| { c.query_one(&query, &[&session.as_ref()]) .and_then(|r| User::unmarshal(&r)) }) @@ -118,7 +118,7 @@ impl UserSessionExt for UserSessionExtImpl " ) s"]; self.0 - .with_client(|c| { + .with_client(&Actor::default(), |c| { c.query_one(&QUERY_LINES.iter() .fold(String::new(), |b, a| format!("{b}{a}\n")), &[&tag_or_email.as_str(), diff --git a/src/postgres.rs b/src/postgres.rs index 2089e8c..e4f2b48 100644 --- a/src/postgres.rs +++ b/src/postgres.rs @@ -6,7 +6,7 @@ use postgres::types::FromSql; use postgres::{GenericClient, GenericRow}; use rand::Rng; -use crate::model::{Actor, Mode, Path, Perm}; +use crate::model::{Actor, Mode, Path, Perm, UserId}; pub type DbError = <::Client as postgres::GenericClient>::Error; @@ -76,7 +76,7 @@ pub trait Postgres fn try_new(connect: F, pool_size: usize) -> Result> where F: Fn() -> Result>; - fn with_client(&self, f: F) -> Result> + fn with_client(&self, a: &Actor, f: F) -> Result> where F: FnOnce(&mut Self::Client) -> Result>; fn authorized(&self, path: &Path, wants_to: Mode, actor: &Actor) -> Result> { @@ -91,7 +91,7 @@ pub trait Postgres "inner join public.usr ou on ou.id = p.owner_user", "where path = $1 :: text",].into_iter() .fold(String::new(), |b, a| format!("{b}\n{a}")); - let got_perm = self.with_client(|c| c.query_opt(&q, &[&path.to_string()]))?; + let got_perm = self.with_client(actor, |c| c.query_opt(&q, &[&path.to_string()]))?; Ok(match got_perm { | None => { log::debug!("no such perm: {}", path); @@ -133,9 +133,11 @@ pub trait Postgres "inner join public.grp og on og.id = p.owner_group", "inner join public.usr ou on ou.id = p.owner_user", format!("where path in ({set})").as_str(),].into_iter() - .fold(String::new(), |b, a| format!("{b}\n{a}")); + .fold(String::new(), |b, a| { + format!("{b}\n{a}") + }); - let pass = self.with_client(|c| c.query(&q, &[]))? + let pass = self.with_client(actor, |c| c.query(&q, &[]))? .iter() .map(Perm::unmarshal) .collect::, _>>()? @@ -189,13 +191,22 @@ impl Postgres for PostgresImpl pool: Box::pin(pool) }) } - fn with_client(&self, f: F) -> Result + fn with_client(&self, a: &Actor, f: F) -> Result where F: FnOnce(&mut Self::Client) -> Result { - match self.unused_lock() { - | Some(mut lock) => f(lock.deref_mut()), - | None => f(self.block_for_next().deref_mut()), - } + let mut c = match self.unused_lock() { + | Some(lock) => lock, + | None => self.block_for_next(), + }; + + c.query_one("select public.set_acting_usr($1)", + &[&a.uid.as_ref().map(UserId::as_ref)])?; + + let r = f(c.deref_mut())?; + + c.query_one("select public.unset_acting_usr()", &[])?; + + Ok(r) } } @@ -364,7 +375,7 @@ pub mod test { perms: vec![] }) } - fn with_client(&self, f: F) -> Result> + fn with_client(&self, a: &Actor, f: F) -> Result> where F: FnOnce(&mut Self::Client) -> Result> { f(self.client.lock().unwrap().deref_mut()) @@ -598,7 +609,8 @@ pub mod test { assert!(pg.unused_lock().is_some()); // with_client does not block when there are available clients - let row = pg.with_client(|c| c.query_one("", &[])).unwrap(); + let row = pg.with_client(&Actor::default(), |c| c.query_one("", &[])) + .unwrap(); assert_eq!(row.get::<_, String>("foo"), String::from("bar")); } }