diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 22aab76..91c8c17 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -61,19 +61,27 @@ pub struct NewPost { pub source: String, } -impl Provider for Post { +impl<'a> Provider<(&'a Connection, Option)> for Post { type Data = PostEndpoint; - fn get(conn: &Connection, id: i32) -> Result { - Post::get(conn, id).map(|p| Ok(PostEndpoint { - id: Some(p.id), - title: Some(p.title.clone()), - subtitle: Some(p.subtitle.clone()), - content: Some(p.content.get().clone()) - })).unwrap_or(Err(Error::NotFound("Get Post".to_string()))) + fn get((conn, user_id): &(&'a Connection, Option), id: i32) -> Result { + if let Some(post) = Post::get(conn, id) { + if !post.published && !user_id.map(|u| post.is_author(conn, u)).unwrap_or(false) { + return Err(Error::Authorization("You are not authorized to access this post yet.".to_string())) + } + + Ok(PostEndpoint { + id: Some(post.id), + title: Some(post.title.clone()), + subtitle: Some(post.subtitle.clone()), + content: Some(post.content.get().clone()) + }) + } else { + Err(Error::NotFound("Request post was not found".to_string())) + } } - fn list(conn: &Connection, filter: PostEndpoint) -> Vec { + fn list((conn, user_id): &(&'a Connection, Option), filter: PostEndpoint) -> Vec { let mut query = posts::table.into_boxed(); if let Some(title) = filter.title { query = query.filter(posts::title.eq(title)); @@ -85,7 +93,8 @@ impl Provider for Post { query = query.filter(posts::content.eq(content)); } - query.get_results::(conn).map(|ps| ps.into_iter() + query.get_results::(*conn).map(|ps| ps.into_iter() + .filter(|p| p.published || user_id.map(|u| p.is_author(conn, u)).unwrap_or(false)) .map(|p| PostEndpoint { id: Some(p.id), title: Some(p.title.clone()), @@ -96,16 +105,21 @@ impl Provider for Post { ).unwrap_or(vec![]) } - fn create(_conn: &Connection, _query: PostEndpoint) -> Result { + fn create((_conn, _user_id): &(&'a Connection, Option), _query: PostEndpoint) -> Result { unimplemented!() } - fn update(_conn: &Connection, _id: i32, _new_data: PostEndpoint) -> Result { + fn update((_conn, _user_id): &(&'a Connection, Option), _id: i32, _new_data: PostEndpoint) -> Result { unimplemented!() } - fn delete(conn: &Connection, id: i32) { - Post::get(conn, id).map(|p| p.delete(conn)); + fn delete((conn, user_id): &(&'a Connection, Option), id: i32) { + let user_id = user_id.expect("Post as Provider::delete: not authenticated"); + if let Some(post) = Post::get(conn, id) { + if post.is_author(conn, user_id) { + post.delete(conn); + } + } } } @@ -264,6 +278,15 @@ impl Post { users::table.filter(users::id.eq_any(author_list)).load::(conn).expect("Post::get_authors: loading error") } + pub fn is_author(&self, conn: &Connection, author_id: i32) -> bool { + use schema::post_authors; + PostAuthor::belonging_to(self) + .filter(post_authors::author_id.eq(author_id)) + .count() + .get_result::(conn) + .expect("Post::is_author: loading error") > 0 + } + pub fn get_blog(&self, conn: &Connection) -> Blog { use schema::blogs; blogs::table.filter(blogs::id.eq(self.blog_id)) diff --git a/src/api/authorization.rs b/src/api/authorization.rs index 2065088..7d5afc0 100644 --- a/src/api/authorization.rs +++ b/src/api/authorization.rs @@ -33,7 +33,7 @@ impl Scope for plume_models::posts::Post { } } -pub struct Authorization (PhantomData<(A, S)>); +pub struct Authorization (pub ApiToken, PhantomData<(A, S)>); impl<'a, 'r, A, S> FromRequest<'a, 'r> for Authorization where A: Action, @@ -45,7 +45,7 @@ where A: Action, request.guard::() .map_failure(|_| (Status::Unauthorized, ())) .and_then(|token| if token.can(A::to_str(), S::to_str()) { - Outcome::Success(Authorization(PhantomData)) + Outcome::Success(Authorization(token, PhantomData)) } else { Outcome::Failure((Status::Unauthorized, ())) }) diff --git a/src/api/posts.rs b/src/api/posts.rs index 31b8432..cf68ce5 100644 --- a/src/api/posts.rs +++ b/src/api/posts.rs @@ -13,14 +13,14 @@ use plume_models::{ use api::authorization::*; #[get("/posts/")] -fn get(id: i32, conn: DbConn, _auth: Authorization) -> Json { - let post = >::get(&*conn, id).ok(); +fn get(id: i32, conn: DbConn, auth: Option>) -> Json { + let post = )>>::get(&(&*conn, auth.map(|a| a.0.user_id)), id).ok(); Json(json!(post)) } #[get("/posts")] -fn list(conn: DbConn, uri: &Origin, _auth: Authorization) -> Json { +fn list(conn: DbConn, uri: &Origin, auth: Option>) -> Json { let query: PostEndpoint = serde_qs::from_str(uri.query().unwrap_or("")).expect("api::list: invalid query error"); - let post = >::list(&*conn, query); + let post = )>>::list(&(&*conn, auth.map(|a| a.0.user_id)), query); Json(json!(post)) }