mas_handlers/graphql/query/
mod.rs1use async_graphql::{Context, ID, MergedObject, Object};
8
9use crate::graphql::{
10 model::{
11 Anonymous, BrowserSession, CompatSession, Node, NodeType, OAuth2Client, OAuth2Session,
12 SiteConfig, User, UserEmail, UserRecoveryTicket,
13 },
14 state::ContextExt,
15};
16
17mod session;
18mod upstream_oauth;
19mod user;
20mod viewer;
21
22use self::{
23 session::SessionQuery, upstream_oauth::UpstreamOAuthQuery, user::UserQuery, viewer::ViewerQuery,
24};
25use super::model::UserEmailAuthentication;
26
27#[derive(Default, MergedObject)]
29pub struct Query(
30 BaseQuery,
31 UserQuery,
32 UpstreamOAuthQuery,
33 SessionQuery,
34 ViewerQuery,
35);
36
37impl Query {
38 #[must_use]
39 pub fn new() -> Self {
40 Self::default()
41 }
42}
43
44#[derive(Default)]
45struct BaseQuery;
46
47#[Object]
49impl BaseQuery {
50 #[graphql(deprecation = "Use `viewerSession` instead.")]
52 async fn current_browser_session(
53 &self,
54 ctx: &Context<'_>,
55 ) -> Result<Option<BrowserSession>, async_graphql::Error> {
56 let requester = ctx.requester();
57 Ok(requester
58 .browser_session()
59 .cloned()
60 .map(BrowserSession::from))
61 }
62
63 #[graphql(deprecation = "Use `viewer` instead.")]
65 async fn current_user(&self, ctx: &Context<'_>) -> Result<Option<User>, async_graphql::Error> {
66 let requester = ctx.requester();
67 Ok(requester.user().cloned().map(User::from))
68 }
69
70 async fn oauth2_client(
72 &self,
73 ctx: &Context<'_>,
74 id: ID,
75 ) -> Result<Option<OAuth2Client>, async_graphql::Error> {
76 let state = ctx.state();
77 let id = NodeType::OAuth2Client.extract_ulid(&id)?;
78
79 let mut repo = state.repository().await?;
80 let client = repo.oauth2_client().lookup(id).await?;
81 repo.cancel().await?;
82
83 Ok(client.map(OAuth2Client))
84 }
85
86 async fn browser_session(
88 &self,
89 ctx: &Context<'_>,
90 id: ID,
91 ) -> Result<Option<BrowserSession>, async_graphql::Error> {
92 let state = ctx.state();
93 let id = NodeType::BrowserSession.extract_ulid(&id)?;
94 let requester = ctx.requester();
95
96 let mut repo = state.repository().await?;
97 let browser_session = repo.browser_session().lookup(id).await?;
98 repo.cancel().await?;
99
100 let Some(browser_session) = browser_session else {
101 return Ok(None);
102 };
103
104 if !requester.is_owner_or_admin(&browser_session) {
105 return Ok(None);
106 }
107
108 Ok(Some(BrowserSession(browser_session)))
109 }
110
111 async fn compat_session(
113 &self,
114 ctx: &Context<'_>,
115 id: ID,
116 ) -> Result<Option<CompatSession>, async_graphql::Error> {
117 let state = ctx.state();
118 let id = NodeType::CompatSession.extract_ulid(&id)?;
119 let requester = ctx.requester();
120
121 let mut repo = state.repository().await?;
122 let compat_session = repo.compat_session().lookup(id).await?;
123 repo.cancel().await?;
124
125 let Some(compat_session) = compat_session else {
126 return Ok(None);
127 };
128
129 if !requester.is_owner_or_admin(&compat_session) {
130 return Ok(None);
131 }
132
133 Ok(Some(CompatSession::new(compat_session)))
134 }
135
136 async fn oauth2_session(
138 &self,
139 ctx: &Context<'_>,
140 id: ID,
141 ) -> Result<Option<OAuth2Session>, async_graphql::Error> {
142 let state = ctx.state();
143 let id = NodeType::OAuth2Session.extract_ulid(&id)?;
144 let requester = ctx.requester();
145
146 let mut repo = state.repository().await?;
147 let oauth2_session = repo.oauth2_session().lookup(id).await?;
148 repo.cancel().await?;
149
150 let Some(oauth2_session) = oauth2_session else {
151 return Ok(None);
152 };
153
154 if !requester.is_owner_or_admin(&oauth2_session) {
155 return Ok(None);
156 }
157
158 Ok(Some(OAuth2Session(oauth2_session)))
159 }
160
161 async fn user_email(
163 &self,
164 ctx: &Context<'_>,
165 id: ID,
166 ) -> Result<Option<UserEmail>, async_graphql::Error> {
167 let state = ctx.state();
168 let id = NodeType::UserEmail.extract_ulid(&id)?;
169 let requester = ctx.requester();
170
171 let mut repo = state.repository().await?;
172 let user_email = repo.user_email().lookup(id).await?;
173 repo.cancel().await?;
174
175 let Some(user_email) = user_email else {
176 return Ok(None);
177 };
178
179 if !requester.is_owner_or_admin(&user_email) {
180 return Ok(None);
181 }
182
183 Ok(Some(UserEmail(user_email)))
184 }
185
186 async fn user_recovery_ticket(
188 &self,
189 ctx: &Context<'_>,
190 ticket: String,
191 ) -> Result<Option<UserRecoveryTicket>, async_graphql::Error> {
192 let state = ctx.state();
193 let mut repo = state.repository().await?;
194 let ticket = repo.user_recovery().find_ticket(&ticket).await?;
195 repo.cancel().await?;
196
197 Ok(ticket.map(UserRecoveryTicket))
198 }
199
200 async fn user_email_authentication(
202 &self,
203 ctx: &Context<'_>,
204 id: ID,
205 ) -> Result<Option<UserEmailAuthentication>, async_graphql::Error> {
206 let state = ctx.state();
207 let id = NodeType::UserEmailAuthentication.extract_ulid(&id)?;
208 let requester = ctx.requester();
209 let mut repo = state.repository().await?;
210 let authentication = repo.user_email().lookup_authentication(id).await?;
211 let Some(authentication) = authentication else {
212 return Ok(None);
213 };
214
215 let Some(browser_session) = requester.browser_session() else {
216 return Ok(None);
217 };
218
219 if authentication.user_session_id != Some(browser_session.id) {
220 return Ok(None);
221 }
222
223 Ok(Some(UserEmailAuthentication(authentication)))
224 }
225
226 async fn node(&self, ctx: &Context<'_>, id: ID) -> Result<Option<Node>, async_graphql::Error> {
228 if id.as_str() == "anonymous" {
230 return Ok(Some(Node::Anonymous(Box::new(Anonymous))));
231 }
232
233 if id.as_str() == crate::graphql::model::SITE_CONFIG_ID {
234 return Ok(Some(Node::SiteConfig(Box::new(SiteConfig::new(
235 ctx.state().site_config(),
236 )))));
237 }
238
239 let (node_type, _id) = NodeType::from_id(&id)?;
240
241 let ret = match node_type {
242 NodeType::Authentication | NodeType::CompatSsoLogin | NodeType::UserRecoveryTicket => {
244 None
245 }
246
247 NodeType::UpstreamOAuth2Provider => UpstreamOAuthQuery
248 .upstream_oauth2_provider(ctx, id)
249 .await?
250 .map(|c| Node::UpstreamOAuth2Provider(Box::new(c))),
251
252 NodeType::UpstreamOAuth2Link => UpstreamOAuthQuery
253 .upstream_oauth2_link(ctx, id)
254 .await?
255 .map(|c| Node::UpstreamOAuth2Link(Box::new(c))),
256
257 NodeType::OAuth2Client => self
258 .oauth2_client(ctx, id)
259 .await?
260 .map(|c| Node::OAuth2Client(Box::new(c))),
261
262 NodeType::UserEmail => self
263 .user_email(ctx, id)
264 .await?
265 .map(|e| Node::UserEmail(Box::new(e))),
266
267 NodeType::UserEmailAuthentication => self
268 .user_email_authentication(ctx, id)
269 .await?
270 .map(|e| Node::UserEmailAuthentication(Box::new(e))),
271
272 NodeType::CompatSession => self
273 .compat_session(ctx, id)
274 .await?
275 .map(|s| Node::CompatSession(Box::new(s))),
276
277 NodeType::OAuth2Session => self
278 .oauth2_session(ctx, id)
279 .await?
280 .map(|s| Node::OAuth2Session(Box::new(s))),
281
282 NodeType::BrowserSession => self
283 .browser_session(ctx, id)
284 .await?
285 .map(|s| Node::BrowserSession(Box::new(s))),
286
287 NodeType::User => UserQuery
288 .user(ctx, id)
289 .await?
290 .map(|u| Node::User(Box::new(u))),
291 };
292
293 Ok(ret)
294 }
295
296 async fn site_config(&self, ctx: &Context<'_>) -> SiteConfig {
298 SiteConfig::new(ctx.state().site_config())
299 }
300}