418 lines
14 KiB
Rust
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)?;
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|