From 562f92512e8dac93acc4fed7b4d5c9ac5c095b50 Mon Sep 17 00:00:00 2001 From: silverpill Date: Sat, 20 Nov 2021 01:10:56 +0000 Subject: [PATCH] Check view permissions when interacting with post --- src/mastodon_api/search/helpers.rs | 1 + src/mastodon_api/statuses/views.rs | 22 ++++++++++-- src/models/posts/helpers.rs | 54 +++++++++++++++++++++++++++++- src/models/posts/types.rs | 4 +++ src/models/users/types.rs | 1 + 5 files changed, 78 insertions(+), 4 deletions(-) diff --git a/src/mastodon_api/search/helpers.rs b/src/mastodon_api/search/helpers.rs index 8c7f805..30af668 100644 --- a/src/mastodon_api/search/helpers.rs +++ b/src/mastodon_api/search/helpers.rs @@ -72,6 +72,7 @@ async fn search_profiles( Ok(profiles) } +/// Finds public post by its object ID async fn search_note( config: &Config, db_client: &mut impl GenericClient, diff --git a/src/mastodon_api/statuses/views.rs b/src/mastodon_api/statuses/views.rs index 71ca959..90125c0 100644 --- a/src/mastodon_api/statuses/views.rs +++ b/src/mastodon_api/statuses/views.rs @@ -19,6 +19,7 @@ use crate::ipfs::store as ipfs_store; use crate::ipfs::utils::{IPFS_LOGO, get_ipfs_url}; use crate::mastodon_api::oauth::auth::get_current_user; use crate::models::attachments::queries::set_attachment_ipfs_cid; +use crate::models::posts::helpers::can_view_post; use crate::models::posts::mentions::{find_mentioned_profiles, replace_mentions}; use crate::models::profiles::queries::get_followers; use crate::models::posts::helpers::{ @@ -120,6 +121,9 @@ async fn get_status( None => None, }; let mut post = get_post_by_id(db_client, &status_id).await?; + if !can_view_post(maybe_current_user.as_ref(), &post) { + return Err(HttpError::NotFoundError("post")); + }; if let Some(user) = maybe_current_user { get_actions_for_post(db_client, &user.id, &mut post).await?; } @@ -167,6 +171,10 @@ async fn favourite( ) -> Result { let db_client = &mut **get_database_client(&db_pool).await?; let current_user = get_current_user(db_client, auth.token()).await?; + let post = get_post_by_id(db_client, &status_id).await?; + if !can_view_post(Some(¤t_user), &post) { + return Err(HttpError::NotFoundError("post")); + }; let reaction_created = match create_reaction( db_client, ¤t_user.id, &status_id, ).await { @@ -205,6 +213,10 @@ async fn unfavourite( ) -> Result { let db_client = &mut **get_database_client(&db_pool).await?; let current_user = get_current_user(db_client, auth.token()).await?; + let post = get_post_by_id(db_client, &status_id).await?; + if !can_view_post(Some(¤t_user), &post) { + return Err(HttpError::NotFoundError("post")); + }; match delete_reaction(db_client, ¤t_user.id, &status_id).await { Err(DatabaseError::NotFound(_)) => (), // post not favourited other_result => other_result?, @@ -234,6 +246,10 @@ async fn make_permanent( let db_client = &**get_database_client(&db_pool).await?; let current_user = get_current_user(db_client, auth.token()).await?; let mut post = get_post_by_id(db_client, &status_id).await?; + if post.author.id != current_user.id || !post.is_public() { + // Users can only archive their own public posts + return Err(HttpError::NotFoundError("post")); + }; let ipfs_api_url = config.ipfs_api_url.as_ref() .ok_or(HttpError::NotSupported)?; @@ -286,10 +302,10 @@ async fn get_signature( let contract_config = config.ethereum_contract.as_ref() .ok_or(HttpError::NotSupported)?; let post = get_post_by_id(db_client, &status_id).await?; - if post.author.id != current_user.id { - // Users can only tokenize their own posts + if post.author.id != current_user.id || !post.is_public() { + // Users can only tokenize their own public posts return Err(HttpError::NotFoundError("post")); - } + }; let ipfs_cid = post.ipfs_cid // Post metadata is not immutable .ok_or(HttpError::ValidationError("post is not immutable".into()))?; diff --git a/src/models/posts/helpers.rs b/src/models/posts/helpers.rs index 5c66000..2365b72 100644 --- a/src/models/posts/helpers.rs +++ b/src/models/posts/helpers.rs @@ -3,7 +3,8 @@ use uuid::Uuid; use crate::errors::DatabaseError; use crate::models::reactions::queries::get_favourited; -use super::types::{Post, PostActions}; +use crate::models::users::types::User; +use super::types::{Post, PostActions, Visibility}; pub async fn get_actions_for_post( db_client: &impl GenericClient, @@ -29,3 +30,54 @@ pub async fn get_actions_for_posts( } Ok(()) } + +pub fn can_view_post(user: Option<&User>, post: &Post) -> bool { + match post.visibility { + Visibility::Public => true, + Visibility::Direct => { + if let Some(user) = user { + // Returns true if user is mentioned + post.mentions.iter() + .find(|profile| profile.id == user.profile.id) + .is_some() + } else { + false + } + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_can_view_post_anonymous() { + let post = Post { + visibility: Visibility::Public, + ..Default::default() + }; + assert!(can_view_post(None, &post)); + } + + #[test] + fn test_can_view_post_direct() { + let user = User::default(); + let post = Post { + visibility: Visibility::Direct, + ..Default::default() + }; + assert!(!can_view_post(Some(&user), &post)); + } + + #[test] + fn test_can_view_post_direct_mentioned() { + let user = User::default(); + let post = Post { + visibility: Visibility::Direct, + mentions: vec![user.profile.clone()], + ..Default::default() + }; + assert!(can_view_post(Some(&user), &post)); + } +} diff --git a/src/models/posts/types.rs b/src/models/posts/types.rs index 26aaf29..d2c97fc 100644 --- a/src/models/posts/types.rs +++ b/src/models/posts/types.rs @@ -108,6 +108,10 @@ impl Post { actions: None, } } + + pub fn is_public(&self) -> bool { + matches!(self.visibility, Visibility::Public) + } } #[cfg(test)] diff --git a/src/models/users/types.rs b/src/models/users/types.rs index e3f1b84..9551976 100644 --- a/src/models/users/types.rs +++ b/src/models/users/types.rs @@ -20,6 +20,7 @@ pub struct DbUser { // Represents local user #[derive(Clone)] +#[cfg_attr(test, derive(Default))] pub struct User { pub id: Uuid, pub wallet_address: String,