mas_handlers/graphql/mutations/
mod.rs1mod browser_session;
8mod compat_session;
9mod matrix;
10mod oauth2_session;
11mod user;
12mod user_email;
13
14use anyhow::Context as _;
15use async_graphql::MergedObject;
16use mas_data_model::SiteConfig;
17use mas_storage::BoxRepository;
18use zeroize::Zeroizing;
19
20use super::Requester;
21use crate::passwords::PasswordManager;
22
23#[derive(Default, MergedObject)]
25pub struct Mutation(
26 user_email::UserEmailMutations,
27 user::UserMutations,
28 oauth2_session::OAuth2SessionMutations,
29 compat_session::CompatSessionMutations,
30 browser_session::BrowserSessionMutations,
31 matrix::MatrixMutations,
32);
33
34impl Mutation {
35 #[must_use]
36 pub fn new() -> Self {
37 Self::default()
38 }
39}
40
41async fn verify_password_if_needed(
46 requester: &Requester,
47 config: &SiteConfig,
48 password_manager: &PasswordManager,
49 password: Option<String>,
50 user: &mas_data_model::User,
51 repo: &mut BoxRepository,
52) -> Result<bool, async_graphql::Error> {
53 if requester.is_admin() {
55 return Ok(true);
56 }
57
58 if !config.password_login_enabled {
60 return Ok(true);
61 }
62
63 let Some(user_password) = repo
65 .user_password()
66 .active(user)
67 .await
68 .context("Failed to load user password")?
69 else {
70 return Ok(true);
72 };
73
74 let Some(password) = password else {
75 return Ok(false);
77 };
78
79 let password = Zeroizing::new(password.into_bytes());
80
81 let res = password_manager
82 .verify(
83 user_password.version,
84 password,
85 user_password.hashed_password,
86 )
87 .await;
88
89 Ok(res.is_ok())
90}