mas_handlers/graphql/mutations/
mod.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7mod 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/// The mutations root of the GraphQL interface.
24#[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
41/// Check the password if neeed
42///
43/// Returns true if password verification is not needed, or if the password is
44/// correct. Returns false if the password is incorrect or missing.
45async 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 the requester is admin, they don't need to provide a password
54    if requester.is_admin() {
55        return Ok(true);
56    }
57
58    // If password login is disabled, assume we don't want the user to reauth
59    if !config.password_login_enabled {
60        return Ok(true);
61    }
62
63    // Else we need to check if the user has a password
64    let Some(user_password) = repo
65        .user_password()
66        .active(user)
67        .await
68        .context("Failed to load user password")?
69    else {
70        // User has no password, so we don't need to verify the password
71        return Ok(true);
72    };
73
74    let Some(password) = password else {
75        // There is a password on the user, but not provided in the input
76        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}