Make Authorization optional for read routes
Only require it when reading draft articles.
This commit is contained in:
parent
c341179150
commit
e26a150164
@ -61,19 +61,27 @@ pub struct NewPost {
|
|||||||
pub source: String,
|
pub source: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Provider<Connection> for Post {
|
impl<'a> Provider<(&'a Connection, Option<i32>)> for Post {
|
||||||
type Data = PostEndpoint;
|
type Data = PostEndpoint;
|
||||||
|
|
||||||
fn get(conn: &Connection, id: i32) -> Result<PostEndpoint, Error> {
|
fn get((conn, user_id): &(&'a Connection, Option<i32>), id: i32) -> Result<PostEndpoint, Error> {
|
||||||
Post::get(conn, id).map(|p| Ok(PostEndpoint {
|
if let Some(post) = Post::get(conn, id) {
|
||||||
id: Some(p.id),
|
if !post.published && !user_id.map(|u| post.is_author(conn, u)).unwrap_or(false) {
|
||||||
title: Some(p.title.clone()),
|
return Err(Error::Authorization("You are not authorized to access this post yet.".to_string()))
|
||||||
subtitle: Some(p.subtitle.clone()),
|
}
|
||||||
content: Some(p.content.get().clone())
|
|
||||||
})).unwrap_or(Err(Error::NotFound("Get Post".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<PostEndpoint> {
|
fn list((conn, user_id): &(&'a Connection, Option<i32>), filter: PostEndpoint) -> Vec<PostEndpoint> {
|
||||||
let mut query = posts::table.into_boxed();
|
let mut query = posts::table.into_boxed();
|
||||||
if let Some(title) = filter.title {
|
if let Some(title) = filter.title {
|
||||||
query = query.filter(posts::title.eq(title));
|
query = query.filter(posts::title.eq(title));
|
||||||
@ -85,7 +93,8 @@ impl Provider<Connection> for Post {
|
|||||||
query = query.filter(posts::content.eq(content));
|
query = query.filter(posts::content.eq(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
query.get_results::<Post>(conn).map(|ps| ps.into_iter()
|
query.get_results::<Post>(*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 {
|
.map(|p| PostEndpoint {
|
||||||
id: Some(p.id),
|
id: Some(p.id),
|
||||||
title: Some(p.title.clone()),
|
title: Some(p.title.clone()),
|
||||||
@ -96,16 +105,21 @@ impl Provider<Connection> for Post {
|
|||||||
).unwrap_or(vec![])
|
).unwrap_or(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create(_conn: &Connection, _query: PostEndpoint) -> Result<PostEndpoint, Error> {
|
fn create((_conn, _user_id): &(&'a Connection, Option<i32>), _query: PostEndpoint) -> Result<PostEndpoint, Error> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(_conn: &Connection, _id: i32, _new_data: PostEndpoint) -> Result<PostEndpoint, Error> {
|
fn update((_conn, _user_id): &(&'a Connection, Option<i32>), _id: i32, _new_data: PostEndpoint) -> Result<PostEndpoint, Error> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete(conn: &Connection, id: i32) {
|
fn delete((conn, user_id): &(&'a Connection, Option<i32>), id: i32) {
|
||||||
Post::get(conn, id).map(|p| p.delete(conn));
|
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::<User>(conn).expect("Post::get_authors: loading error")
|
users::table.filter(users::id.eq_any(author_list)).load::<User>(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::<i64>(conn)
|
||||||
|
.expect("Post::is_author: loading error") > 0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_blog(&self, conn: &Connection) -> Blog {
|
pub fn get_blog(&self, conn: &Connection) -> Blog {
|
||||||
use schema::blogs;
|
use schema::blogs;
|
||||||
blogs::table.filter(blogs::id.eq(self.blog_id))
|
blogs::table.filter(blogs::id.eq(self.blog_id))
|
||||||
|
@ -33,7 +33,7 @@ impl Scope for plume_models::posts::Post {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Authorization<A, S> (PhantomData<(A, S)>);
|
pub struct Authorization<A, S> (pub ApiToken, PhantomData<(A, S)>);
|
||||||
|
|
||||||
impl<'a, 'r, A, S> FromRequest<'a, 'r> for Authorization<A, S>
|
impl<'a, 'r, A, S> FromRequest<'a, 'r> for Authorization<A, S>
|
||||||
where A: Action,
|
where A: Action,
|
||||||
@ -45,7 +45,7 @@ where A: Action,
|
|||||||
request.guard::<ApiToken>()
|
request.guard::<ApiToken>()
|
||||||
.map_failure(|_| (Status::Unauthorized, ()))
|
.map_failure(|_| (Status::Unauthorized, ()))
|
||||||
.and_then(|token| if token.can(A::to_str(), S::to_str()) {
|
.and_then(|token| if token.can(A::to_str(), S::to_str()) {
|
||||||
Outcome::Success(Authorization(PhantomData))
|
Outcome::Success(Authorization(token, PhantomData))
|
||||||
} else {
|
} else {
|
||||||
Outcome::Failure((Status::Unauthorized, ()))
|
Outcome::Failure((Status::Unauthorized, ()))
|
||||||
})
|
})
|
||||||
|
@ -13,14 +13,14 @@ use plume_models::{
|
|||||||
use api::authorization::*;
|
use api::authorization::*;
|
||||||
|
|
||||||
#[get("/posts/<id>")]
|
#[get("/posts/<id>")]
|
||||||
fn get(id: i32, conn: DbConn, _auth: Authorization<Read, Post>) -> Json<serde_json::Value> {
|
fn get(id: i32, conn: DbConn, auth: Option<Authorization<Read, Post>>) -> Json<serde_json::Value> {
|
||||||
let post = <Post as Provider<Connection>>::get(&*conn, id).ok();
|
let post = <Post as Provider<(&Connection, Option<i32>)>>::get(&(&*conn, auth.map(|a| a.0.user_id)), id).ok();
|
||||||
Json(json!(post))
|
Json(json!(post))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/posts")]
|
#[get("/posts")]
|
||||||
fn list(conn: DbConn, uri: &Origin, _auth: Authorization<Read, Post>) -> Json<serde_json::Value> {
|
fn list(conn: DbConn, uri: &Origin, auth: Option<Authorization<Read, Post>>) -> Json<serde_json::Value> {
|
||||||
let query: PostEndpoint = serde_qs::from_str(uri.query().unwrap_or("")).expect("api::list: invalid query error");
|
let query: PostEndpoint = serde_qs::from_str(uri.query().unwrap_or("")).expect("api::list: invalid query error");
|
||||||
let post = <Post as Provider<Connection>>::list(&*conn, query);
|
let post = <Post as Provider<(&Connection, Option<i32>)>>::list(&(&*conn, auth.map(|a| a.0.user_id)), query);
|
||||||
Json(json!(post))
|
Json(json!(post))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user