Skip to main content

max / makenotwork

2.8 KB · 88 lines History Blame Raw
1 //! Database read queries, projection structs and SQL.
2
3 use chrono::{DateTime, Utc};
4 use mt_core::types::{BanType, CommunityRole, CommunityState, ModAction, SortColumn, SortOrder};
5 use sqlx::PgPool;
6 use uuid::Uuid;
7
8 /// A community resource loaded by id but not yet proven to belong to a caller's
9 /// community scope (the ultra-fuzz C1 invariant).
10 ///
11 /// A `/p/{slug}/…/{resource_id}` handler must serve a resource whose community
12 /// equals the one the `{slug}` names. `Unscoped<T>` makes that the *only* way to
13 /// obtain the resource: the inner value is private and [`Unscoped::in_community`]
14 /// yields it only when the resource's community matches the one the caller names.
15 /// A handler therefore cannot read a by-id resource without proving it belongs to
16 /// the URL's community, and the seal fails closed by type: any new by-id loader
17 /// that returns `Unscoped<T>` is bound by the same rule with no lint to remember.
18 pub struct Unscoped<T> {
19 inner: T,
20 community_id: Uuid,
21 }
22
23 impl<T> Unscoped<T> {
24 /// Wrap a freshly-loaded resource together with the community it lives in.
25 pub fn new(inner: T, community_id: Uuid) -> Self {
26 Self {
27 inner,
28 community_id,
29 }
30 }
31
32 /// Yield the resource only if it belongs to `expected_community`; `None`
33 /// otherwise (the id names a resource in a different community, which to a
34 /// scoped URL is indistinguishable from "not found").
35 pub fn in_community(self, expected_community: Uuid) -> Option<T> {
36 (self.community_id == expected_community).then_some(self.inner)
37 }
38
39 /// Unwrap without a community check. This is the deliberate escape hatch for
40 /// callers that have no URL slug to scope against, the trusted internal
41 /// server-to-server API. It is on `clippy.toml`'s `disallowed-methods` so a
42 /// normal handler cannot reach for it by accident; the few sanctioned call
43 /// sites carry a local `#[allow]`. Anything reached via `/p/{slug}/…` must use
44 /// [`Unscoped::in_community`] instead.
45 pub fn into_inner_unchecked(self) -> T {
46 self.inner
47 }
48 }
49
50 mod admin;
51 mod category;
52 mod community;
53 mod endorsement;
54 mod footnote;
55 mod image;
56 mod link_preview;
57 mod member;
58 mod mention;
59 mod moderation;
60 mod post;
61 mod search;
62 mod tag;
63 mod thread;
64 mod tracked_thread;
65 mod user;
66
67 pub use admin::*;
68 pub use category::*;
69 pub use community::*;
70 pub use endorsement::*;
71 pub use footnote::*;
72 pub use image::*;
73 pub use link_preview::*;
74 pub use member::*;
75 pub use mention::*;
76 pub use moderation::*;
77 pub use post::*;
78 pub use search::*;
79 pub use tag::*;
80 pub use thread::*;
81 pub use tracked_thread::*;
82 pub use user::*;
83
84 // Explicit re-exports so clippy.toml disallowed-methods paths resolve
85 // (path-based lints do not follow glob re-exports).
86 pub use post::get_post_for_edit;
87 pub use thread::get_thread_with_breadcrumb;
88