mas_handlers/graphql/model/
upstream_oauth.rs1use anyhow::Context as _;
8use async_graphql::{Context, ID, Object};
9use chrono::{DateTime, Utc};
10use mas_storage::{upstream_oauth2::UpstreamOAuthProviderRepository, user::UserRepository};
11use url::Url;
12
13use super::{NodeType, User};
14use crate::graphql::state::ContextExt;
15
16#[derive(Debug, Clone)]
17pub struct UpstreamOAuth2Provider {
18 provider: mas_data_model::UpstreamOAuthProvider,
19}
20
21impl UpstreamOAuth2Provider {
22 #[must_use]
23 pub const fn new(provider: mas_data_model::UpstreamOAuthProvider) -> Self {
24 Self { provider }
25 }
26}
27
28#[Object]
29impl UpstreamOAuth2Provider {
30 pub async fn id(&self) -> ID {
32 NodeType::UpstreamOAuth2Provider.id(self.provider.id)
33 }
34
35 pub async fn created_at(&self) -> DateTime<Utc> {
37 self.provider.created_at
38 }
39
40 pub async fn issuer(&self) -> Option<&str> {
42 self.provider.issuer.as_deref()
43 }
44
45 pub async fn client_id(&self) -> &str {
47 &self.provider.client_id
48 }
49
50 pub async fn human_name(&self) -> Option<&str> {
52 self.provider.human_name.as_deref()
53 }
54
55 pub async fn brand_name(&self) -> Option<&str> {
59 self.provider.brand_name.as_deref()
60 }
61
62 pub async fn link_url(&self, context: &Context<'_>) -> Url {
64 let state = context.state();
65 let url_builder = state.url_builder();
66 let route = mas_router::UpstreamOAuth2Authorize::new(self.provider.id);
67 url_builder.absolute_url_for(&route)
68 }
69}
70
71impl UpstreamOAuth2Link {
72 #[must_use]
73 pub const fn new(link: mas_data_model::UpstreamOAuthLink) -> Self {
74 Self {
75 link,
76 provider: None,
77 user: None,
78 }
79 }
80}
81
82#[derive(Debug, Clone)]
83pub struct UpstreamOAuth2Link {
84 link: mas_data_model::UpstreamOAuthLink,
85 provider: Option<mas_data_model::UpstreamOAuthProvider>,
86 user: Option<mas_data_model::User>,
87}
88
89#[Object]
90impl UpstreamOAuth2Link {
91 pub async fn id(&self) -> ID {
93 NodeType::UpstreamOAuth2Link.id(self.link.id)
94 }
95
96 pub async fn created_at(&self) -> DateTime<Utc> {
98 self.link.created_at
99 }
100
101 pub async fn subject(&self) -> &str {
103 &self.link.subject
104 }
105
106 pub async fn human_account_name(&self) -> Option<&str> {
108 self.link.human_account_name.as_deref()
109 }
110
111 pub async fn provider(
113 &self,
114 ctx: &Context<'_>,
115 ) -> Result<UpstreamOAuth2Provider, async_graphql::Error> {
116 let state = ctx.state();
117 let provider = if let Some(provider) = &self.provider {
118 provider.clone()
120 } else {
121 let mut repo = state.repository().await?;
123
124 let provider = repo
125 .upstream_oauth_provider()
126 .lookup(self.link.provider_id)
127 .await?
128 .context("Upstream OAuth 2.0 provider not found")?;
129 repo.cancel().await?;
130
131 provider
132 };
133
134 Ok(UpstreamOAuth2Provider::new(provider))
135 }
136
137 pub async fn user(&self, ctx: &Context<'_>) -> Result<Option<User>, async_graphql::Error> {
139 let state = ctx.state();
140 let user = if let Some(user) = &self.user {
141 user.clone()
143 } else if let Some(user_id) = &self.link.user_id {
144 let mut repo = state.repository().await?;
146
147 let user = repo
148 .user()
149 .lookup(*user_id)
150 .await?
151 .context("User not found")?;
152 repo.cancel().await?;
153
154 user
155 } else {
156 return Ok(None);
157 };
158
159 Ok(Some(User(user)))
160 }
161}