use crate::{ apub::StoredRecords, store::{OwnerSource, Report, ReportKind}, Context, Error, Required, }; use uuid::Uuid; #[derive(Clone, Copy, Debug)] pub(crate) enum Viewer { Server(Uuid), Profile(Uuid), } fn item_server_for_report(report: &Report, ctx: &Context) -> Result { let profile_id = match report.kind() { ReportKind::Profile => report.item(), ReportKind::Submission => { let submission = ctx .store .submissions .by_id(report.item())? .req("report item: Submission")?; submission.profile_id() } ReportKind::Comment => { let comment = ctx .store .comments .by_id(report.item())? .req("report item: Comment")?; comment.profile_id() } ReportKind::Post => { unimplemented!() } }; let profile = ctx.store.profiles.by_id(profile_id)?.req("profile by id")?; match profile.owner_source() { OwnerSource::Local(_) => ctx.store.servers.get_self()?.req("self server"), OwnerSource::Remote(server_id) => Ok(*server_id), } } impl Viewer { pub(crate) fn can_view(&self, record: StoredRecords, ctx: &Context) -> Result { if let Viewer::Server(server_id) = self { if ctx.store.is_blocked(*server_id)? { return Ok(false); } } let is_federated = self.is_federated(ctx)?; let self_id = ctx.store.servers.get_self()?.req("get self server")?; match (self, record) { (Viewer::Server(_), StoredRecords::Server(server_id)) if self_id == server_id => { Ok(true) } (Viewer::Profile(_), StoredRecords::Server(server_id)) if is_federated || self_id == server_id => { Ok(true) } (Viewer::Server(viewer_id), StoredRecords::Report(report_id)) => { let report = ctx.store.reports.by_id(report_id)?; let server_id = item_server_for_report(&report, ctx)?; if *viewer_id == server_id { Ok(true) } else { Ok(false) } } (Viewer::Server(_), StoredRecords::Profile(_)) if is_federated => Ok(true), (Viewer::Profile(viewer_id), StoredRecords::Profile(profile_id)) if is_federated => { let is_blocked = ctx .store .view .blocks .by_forward(*viewer_id, profile_id)? .is_some() || ctx .store .view .blocks .by_forward(profile_id, *viewer_id)? .is_some(); if is_blocked { Ok(false) } else { Ok(true) } } (Viewer::Server(_), StoredRecords::Submission(_)) if is_federated => Ok(true), (Viewer::Profile(viewer_id), StoredRecords::Submission(submission_id)) if is_federated => { let submission = ctx .store .submissions .by_id(submission_id)? .req("submission by id")?; let is_blocked = ctx .store .view .blocks .by_forward(*viewer_id, submission.profile_id())? .is_some() || ctx .store .view .blocks .by_forward(submission.profile_id(), *viewer_id)? .is_some(); if is_blocked { return Ok(false); } if submission.is_followers_only() { let is_following = ctx .store .view .follows .by_forward(submission.profile_id(), *viewer_id)? .is_some(); if is_following { Ok(true) } else { Ok(false) } } else { Ok(true) } } (Viewer::Server(_), StoredRecords::Comment(_)) if is_federated => Ok(true), (Viewer::Profile(viewer_id), StoredRecords::Comment(comment_id)) if is_federated => { let comment = ctx.store.comments.by_id(comment_id)?.req("comment by id")?; let submission = ctx .store .submissions .by_id(comment.submission_id())? .req("submission by id")?; let is_blocked = ctx .store .view .blocks .by_forward(*viewer_id, comment.profile_id())? .is_some() || ctx .store .view .blocks .by_forward(comment.profile_id(), *viewer_id)? .is_some() || ctx .store .view .blocks .by_forward(*viewer_id, submission.profile_id())? .is_some() || ctx .store .view .blocks .by_forward(submission.profile_id(), *viewer_id)? .is_some(); if is_blocked { return Ok(false); } if submission.is_followers_only() { let is_following = ctx .store .view .follows .by_forward(submission.profile_id(), *viewer_id)? .is_some(); if is_following { Ok(true) } else { Ok(false) } } else { Ok(true) } } (Viewer::Server(_), StoredRecords::React(_)) if is_federated => Ok(true), (Viewer::Profile(viewer_id), StoredRecords::React(react_id)) if is_federated => { let react = ctx.store.reacts.by_id(react_id)?.req("react by id")?; let submission = ctx .store .submissions .by_id(react.submission_id())? .req("submission by id")?; let is_blocked = ctx .store .view .blocks .by_forward(*viewer_id, react.profile_id())? .is_some() || ctx .store .view .blocks .by_forward(react.profile_id(), *viewer_id)? .is_some() || ctx .store .view .blocks .by_forward(*viewer_id, submission.profile_id())? .is_some() || ctx .store .view .blocks .by_forward(submission.profile_id(), *viewer_id)? .is_some(); if is_blocked { return Ok(false); } if submission.is_followers_only() { let is_following = ctx .store .view .follows .by_forward(submission.profile_id(), *viewer_id)? .is_some(); if is_following { Ok(true) } else { Ok(false) } } else { Ok(true) } } (Viewer::Server(_), StoredRecords::Block(_)) if is_federated => Ok(true), (Viewer::Server(viewer_id), StoredRecords::Follow(follow_id)) if is_federated => { let left = ctx .store .view .follows .left(follow_id)? .req("left for follow id")?; let right = ctx .store .view .follows .right(follow_id)? .req("right for follow id")?; let left = ctx.store.profiles.by_id(left)?.req("profile by id")?; let right = ctx.store.profiles.by_id(right)?.req("profile by id")?; if let OwnerSource::Remote(server_id) = left.owner_source() { if server_id == viewer_id { return Ok(true); } } if let OwnerSource::Remote(server_id) = right.owner_source() { if server_id == viewer_id { return Ok(true); } } Ok(false) } (Viewer::Profile(viewer_id), StoredRecords::Follow(follow_id)) if is_federated => { let left = ctx .store .view .follows .left(follow_id)? .req("left for follow id")?; let right = ctx .store .view .follows .right(follow_id)? .req("right for follow id")?; if *viewer_id == left || *viewer_id == right { Ok(true) } else { Ok(false) } } (Viewer::Server(viewer_id), StoredRecords::FollowRequest(freq_id)) if is_federated => { let left = ctx .store .view .follow_requests .left(freq_id)? .req("left for follow id")?; let right = ctx .store .view .follow_requests .right(freq_id)? .req("right for follow id")?; let left = ctx.store.profiles.by_id(left)?.req("profile by id")?; let right = ctx.store.profiles.by_id(right)?.req("profile by id")?; if let OwnerSource::Remote(server_id) = left.owner_source() { if server_id == viewer_id { return Ok(true); } } if let OwnerSource::Remote(server_id) = right.owner_source() { if server_id == viewer_id { return Ok(true); } } Ok(false) } (Viewer::Profile(viewer_id), StoredRecords::FollowRequest(freq_id)) if is_federated => { let left = ctx .store .view .follow_requests .left(freq_id)? .req("left for follow id")?; let right = ctx .store .view .follow_requests .right(freq_id)? .req("right for follow id")?; if *viewer_id == left || *viewer_id == right { Ok(true) } else { Ok(false) } } (Viewer::Server(viewer_id), StoredRecords::ServerFollow(follow_id)) if is_federated => { let left = ctx .store .view .server_follows .left(follow_id)? .req("left for server follow id")?; let right = ctx .store .view .server_follows .right(follow_id)? .req("right for server follow id")?; if *viewer_id == left || *viewer_id == right { Ok(true) } else { Ok(false) } } (Viewer::Server(viewer_id), StoredRecords::ServerFollowRequest(freq_id)) => { let left = ctx .store .view .server_follow_requests .left(freq_id)? .req("left for server follow request id")?; let right = ctx .store .view .server_follow_requests .right(freq_id)? .req("right for server follow request id")?; if *viewer_id == left || *viewer_id == right { Ok(true) } else { Ok(false) } } (viewer, object) => { if is_federated { log::debug!( "federated viewer {:?} denied fetch for resource {:?}", viewer, object ); } else { log::debug!("viewer {:?} denied fetch for resource {:?}", viewer, object); } Ok(false) } } } fn is_federated(&self, ctx: &Context) -> Result { match self { Viewer::Server(id) => Ok(ctx.store.is_federated(*id)?), Viewer::Profile(id) => { let profile = ctx.store.profiles.by_id(*id)?.req("profile by id")?; if let OwnerSource::Remote(server_id) = profile.owner_source() { Ok(ctx.store.is_federated(*server_id)?) } else { Ok(true) } } } } }