fix: set and unset acting usr based on actor

This commit is contained in:
Orion Kindel 2023-07-21 13:21:09 -05:00
parent dc27262328
commit 7e89edc30f
Signed by untrusted user who does not match committer: orion
GPG Key ID: 6D4165AE4C928719
6 changed files with 76 additions and 46 deletions

View File

@ -161,7 +161,9 @@ fn handle_request<A>(app: &A, req: Addrd<Req<ToadT>>) -> Addrd<Message>
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()
};

View File

@ -99,7 +99,7 @@ impl<Db> GroupRepo<DbError<Db>> for GroupRepoImpl<Db> 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()]))
.with_client(actor, |c| c.query(QUERY, &[&id.as_ref()]))
.and_then(|rows| {
rows.iter()
.map(Group::unmarshal)
@ -107,12 +107,12 @@ impl<Db> GroupRepo<DbError<Db>> for GroupRepoImpl<Db> where Db: Postgres + 'stat
})
.map_err(GroupRepoError::Other)?;
let paths = grps.iter().map(|g| Path::parse(format!("/groups/{}/members", g.uid))).collect::<Vec<_>>();
let paths = grps.iter()
.map(|g| Path::parse(format!("/groups/{}/members", g.uid)))
.collect::<Vec<_>>();
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<Db> GroupRepo<DbError<Db>> for GroupRepoImpl<Db> 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<Db> ReadOne for GroupRepoImpl<Db> 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<Db> ReadMany for GroupRepoImpl<Db> where Db: Postgres + 'static
let grps: Vec<Group> =
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")),
@ -263,7 +263,7 @@ impl<Db> Patch for GroupRepoImpl<Db> 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<Db> Insert for GroupRepoImpl<Db> 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<Db> Del for GroupRepoImpl<Db> 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)
}

View File

@ -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<S: AsRef<str>>(&self, this: &HashedText, other: S) -> Result<bool, Self::Error>;
fn matches<S: AsRef<str>>(&self,
actor: &Actor,
this: &HashedText,
other: S)
-> Result<bool, Self::Error>;
}
pub struct HashedTextExtImpl<Db: Postgres>(pub &'static Db);
@ -22,11 +27,17 @@ impl<Db> Ext for HashedTextExtImpl<Db> where Db: Postgres
impl<Db> HashedTextExt for HashedTextExtImpl<Db> where Db: Postgres
{
fn matches<S: AsRef<str>>(&self, this: &HashedText, other: S) -> Result<bool, DbError<Db>> {
fn matches<S: AsRef<str>>(&self,
actor: &Actor,
this: &HashedText,
other: S)
-> Result<bool, DbError<Db>> {
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::<String>(ps[1]), String::from("XXX"));
Ok(Row::new(vec![("",
@ -57,9 +73,9 @@ mod test {
std::mem::transmute::<_, &'static PostgresImpl<Client<()>>>(&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());
}
}

View File

@ -128,7 +128,7 @@ impl<Db> ReadOne for UserRepoImpl<Db> 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<Db> ReadMany for UserRepoImpl<Db> where Db: Postgres + 'static
let usrs: Vec<User> =
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<Db> Patch for UserRepoImpl<Db> 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<Db> Insert for UserRepoImpl<Db> 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<Db> Del for UserRepoImpl<Db> 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)
}

View File

@ -77,7 +77,7 @@ impl<Db, G> UserSessionExt for UserSessionExtImpl<Db, G>
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<Db, G> UserSessionExt for UserSessionExtImpl<Db, G>
" ) 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(),

View File

@ -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<Pg> = <<Pg as Postgres>::Client as postgres::GenericClient>::Error;
@ -76,7 +76,7 @@ pub trait Postgres
fn try_new<F>(connect: F, pool_size: usize) -> Result<Self, DbError<Self>>
where F: Fn() -> Result<Self::Client, DbError<Self>>;
fn with_client<F, R>(&self, f: F) -> Result<R, DbError<Self>>
fn with_client<F, R>(&self, a: &Actor, f: F) -> Result<R, DbError<Self>>
where F: FnOnce(&mut Self::Client) -> Result<R, DbError<Self>>;
fn authorized(&self, path: &Path, wants_to: Mode, actor: &Actor) -> Result<bool, DbError<Self>> {
@ -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::<Result<Vec<Perm>, _>>()?
@ -189,13 +191,22 @@ impl<C> Postgres for PostgresImpl<C>
pool: Box::pin(pool) })
}
fn with_client<F, R>(&self, f: F) -> Result<R, C::Error>
fn with_client<F, R>(&self, a: &Actor, f: F) -> Result<R, C::Error>
where F: FnOnce(&mut Self::Client) -> Result<R, C::Error>
{
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<F, R>(&self, f: F) -> Result<R, super::DbError<Self>>
fn with_client<F, R>(&self, a: &Actor, f: F) -> Result<R, super::DbError<Self>>
where F: FnOnce(&mut Self::Client) -> Result<R, super::DbError<Self>>
{
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"));
}
}