hyaenidae/profiles/src/viewer.rs

418 lines
14 KiB
Rust

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<Uuid, Error> {
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<bool, Error> {
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)?.req("report by 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<bool, Error> {
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)
}
}
}
}
}