Check view permissions when interacting with post

This commit is contained in:
silverpill 2021-11-20 01:10:56 +00:00
parent c578d8536f
commit 562f92512e
5 changed files with 78 additions and 4 deletions

View file

@ -72,6 +72,7 @@ async fn search_profiles(
Ok(profiles) Ok(profiles)
} }
/// Finds public post by its object ID
async fn search_note( async fn search_note(
config: &Config, config: &Config,
db_client: &mut impl GenericClient, db_client: &mut impl GenericClient,

View file

@ -19,6 +19,7 @@ use crate::ipfs::store as ipfs_store;
use crate::ipfs::utils::{IPFS_LOGO, get_ipfs_url}; use crate::ipfs::utils::{IPFS_LOGO, get_ipfs_url};
use crate::mastodon_api::oauth::auth::get_current_user; use crate::mastodon_api::oauth::auth::get_current_user;
use crate::models::attachments::queries::set_attachment_ipfs_cid; 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::posts::mentions::{find_mentioned_profiles, replace_mentions};
use crate::models::profiles::queries::get_followers; use crate::models::profiles::queries::get_followers;
use crate::models::posts::helpers::{ use crate::models::posts::helpers::{
@ -120,6 +121,9 @@ async fn get_status(
None => None, None => None,
}; };
let mut post = get_post_by_id(db_client, &status_id).await?; 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 { if let Some(user) = maybe_current_user {
get_actions_for_post(db_client, &user.id, &mut post).await?; get_actions_for_post(db_client, &user.id, &mut post).await?;
} }
@ -167,6 +171,10 @@ async fn favourite(
) -> Result<HttpResponse, HttpError> { ) -> Result<HttpResponse, HttpError> {
let db_client = &mut **get_database_client(&db_pool).await?; let db_client = &mut **get_database_client(&db_pool).await?;
let current_user = get_current_user(db_client, auth.token()).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(&current_user), &post) {
return Err(HttpError::NotFoundError("post"));
};
let reaction_created = match create_reaction( let reaction_created = match create_reaction(
db_client, &current_user.id, &status_id, db_client, &current_user.id, &status_id,
).await { ).await {
@ -205,6 +213,10 @@ async fn unfavourite(
) -> Result<HttpResponse, HttpError> { ) -> Result<HttpResponse, HttpError> {
let db_client = &mut **get_database_client(&db_pool).await?; let db_client = &mut **get_database_client(&db_pool).await?;
let current_user = get_current_user(db_client, auth.token()).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(&current_user), &post) {
return Err(HttpError::NotFoundError("post"));
};
match delete_reaction(db_client, &current_user.id, &status_id).await { match delete_reaction(db_client, &current_user.id, &status_id).await {
Err(DatabaseError::NotFound(_)) => (), // post not favourited Err(DatabaseError::NotFound(_)) => (), // post not favourited
other_result => other_result?, other_result => other_result?,
@ -234,6 +246,10 @@ async fn make_permanent(
let db_client = &**get_database_client(&db_pool).await?; let db_client = &**get_database_client(&db_pool).await?;
let current_user = get_current_user(db_client, auth.token()).await?; let current_user = get_current_user(db_client, auth.token()).await?;
let mut post = get_post_by_id(db_client, &status_id).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() let ipfs_api_url = config.ipfs_api_url.as_ref()
.ok_or(HttpError::NotSupported)?; .ok_or(HttpError::NotSupported)?;
@ -286,10 +302,10 @@ async fn get_signature(
let contract_config = config.ethereum_contract.as_ref() let contract_config = config.ethereum_contract.as_ref()
.ok_or(HttpError::NotSupported)?; .ok_or(HttpError::NotSupported)?;
let post = get_post_by_id(db_client, &status_id).await?; let post = get_post_by_id(db_client, &status_id).await?;
if post.author.id != current_user.id { if post.author.id != current_user.id || !post.is_public() {
// Users can only tokenize their own posts // Users can only tokenize their own public posts
return Err(HttpError::NotFoundError("post")); return Err(HttpError::NotFoundError("post"));
} };
let ipfs_cid = post.ipfs_cid let ipfs_cid = post.ipfs_cid
// Post metadata is not immutable // Post metadata is not immutable
.ok_or(HttpError::ValidationError("post is not immutable".into()))?; .ok_or(HttpError::ValidationError("post is not immutable".into()))?;

View file

@ -3,7 +3,8 @@ use uuid::Uuid;
use crate::errors::DatabaseError; use crate::errors::DatabaseError;
use crate::models::reactions::queries::get_favourited; 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( pub async fn get_actions_for_post(
db_client: &impl GenericClient, db_client: &impl GenericClient,
@ -29,3 +30,54 @@ pub async fn get_actions_for_posts(
} }
Ok(()) 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));
}
}

View file

@ -108,6 +108,10 @@ impl Post {
actions: None, actions: None,
} }
} }
pub fn is_public(&self) -> bool {
matches!(self.visibility, Visibility::Public)
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -20,6 +20,7 @@ pub struct DbUser {
// Represents local user // Represents local user
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(test, derive(Default))]
pub struct User { pub struct User {
pub id: Uuid, pub id: Uuid,
pub wallet_address: String, pub wallet_address: String,