Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/axum-utils/src/csrf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use base64ct::{Base64UrlUnpadded, Encoding};
use chrono::{DateTime, Duration, Utc};
use mas_storage::Clock;
use mas_data_model::Clock;
use rand::{Rng, RngCore, distributions::Standard, prelude::Distribution as _};
use serde::{Deserialize, Serialize};
use serde_with::{TimestampSeconds, serde_as};
Expand Down
4 changes: 2 additions & 2 deletions crates/axum-utils/src/user_authorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ use axum::{
use axum_extra::typed_header::{TypedHeader, TypedHeaderRejectionReason};
use headers::{Authorization, Header, HeaderMapExt, HeaderName, authorization::Bearer};
use http::{HeaderMap, HeaderValue, Request, StatusCode, header::WWW_AUTHENTICATE};
use mas_data_model::Session;
use mas_data_model::{Clock, Session};
use mas_storage::{
Clock, RepositoryAccess,
RepositoryAccess,
oauth2::{OAuth2AccessTokenRepository, OAuth2SessionRepository},
};
use serde::{Deserialize, de::DeserializeOwned};
Expand Down
6 changes: 2 additions & 4 deletions crates/cli/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{convert::Infallible, net::IpAddr, sync::Arc};
use axum::extract::{FromRef, FromRequestParts};
use ipnetwork::IpNetwork;
use mas_context::LogContext;
use mas_data_model::SiteConfig;
use mas_data_model::{BoxClock, BoxRng, SiteConfig, SystemClock};
use mas_handlers::{
ActivityTracker, BoundActivityTracker, CookieManager, ErrorWrapper, GraphQLSchema, Limiter,
MetadataCache, RequesterFingerprint, passwords::PasswordManager,
Expand All @@ -19,9 +19,7 @@ use mas_keystore::{Encrypter, Keystore};
use mas_matrix::HomeserverConnection;
use mas_policy::{Policy, PolicyFactory};
use mas_router::UrlBuilder;
use mas_storage::{
BoxClock, BoxRepository, BoxRepositoryFactory, BoxRng, RepositoryFactory, SystemClock,
};
use mas_storage::{BoxRepository, BoxRepositoryFactory, RepositoryFactory};
use mas_storage_pg::PgRepositoryFactory;
use mas_templates::Templates;
use opentelemetry::KeyValue;
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/commands/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use camino::Utf8PathBuf;
use clap::Parser;
use figment::Figment;
use mas_config::{ConfigurationSection, RootConfig, SyncConfig};
use mas_storage::{Clock as _, SystemClock};
use mas_data_model::{Clock as _, SystemClock};
use mas_storage_pg::MIGRATOR;
use rand::SeedableRng;
use tokio::io::AsyncWriteExt;
Expand Down
4 changes: 2 additions & 2 deletions crates/cli/src/commands/manage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ use figment::Figment;
use mas_config::{
ConfigurationSection, ConfigurationSectionExt, DatabaseConfig, MatrixConfig, PasswordsConfig,
};
use mas_data_model::{Device, TokenType, Ulid, UpstreamOAuthProvider, User};
use mas_data_model::{Clock, Device, SystemClock, TokenType, Ulid, UpstreamOAuthProvider, User};
use mas_email::Address;
use mas_matrix::HomeserverConnection;
use mas_storage::{
Clock, RepositoryAccess, SystemClock,
RepositoryAccess,
compat::{CompatAccessTokenRepository, CompatSessionFilter, CompatSessionRepository},
oauth2::OAuth2SessionFilter,
queue::{
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/commands/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ use mas_config::{
AppConfig, ClientsConfig, ConfigurationSection, ConfigurationSectionExt, UpstreamOAuth2Config,
};
use mas_context::LogContext;
use mas_data_model::SystemClock;
use mas_handlers::{ActivityTracker, CookieManager, Limiter, MetadataCache};
use mas_listener::server::Server;
use mas_router::UrlBuilder;
use mas_storage::SystemClock;
use mas_storage_pg::{MIGRATOR, PgRepositoryFactory};
use sqlx::migrate::Migrate;
use tracing::{Instrument, info, info_span, warn};
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/commands/syn2mas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use mas_config::{
ConfigurationSection, ConfigurationSectionExt, DatabaseConfig, MatrixConfig, SyncConfig,
UpstreamOAuth2Config,
};
use mas_storage::SystemClock;
use mas_data_model::SystemClock;
use mas_storage_pg::MIGRATOR;
use rand::thread_rng;
use sqlx::{Connection, Either, PgConnection, postgres::PgConnectOptions, types::Uuid};
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/commands/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use mas_config::{
AccountConfig, BrandingConfig, CaptchaConfig, ConfigurationSection, ConfigurationSectionExt,
ExperimentalConfig, MatrixConfig, PasswordsConfig, TemplatesConfig,
};
use mas_storage::{Clock, SystemClock};
use mas_data_model::{Clock, SystemClock};
use rand::SeedableRng;
use tracing::info_span;

Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/commands/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use std::{process::ExitCode, time::Duration};
use clap::Parser;
use figment::Figment;
use mas_config::{AppConfig, ConfigurationSection};
use mas_data_model::SystemClock;
use mas_router::UrlBuilder;
use mas_storage::SystemClock;
use mas_storage_pg::PgRepositoryFactory;
use tracing::{info, info_span};

Expand Down
3 changes: 2 additions & 1 deletion crates/cli/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
use std::collections::{BTreeMap, BTreeSet};

use mas_config::{ClientsConfig, UpstreamOAuth2Config};
use mas_data_model::Clock;
use mas_keystore::Encrypter;
use mas_storage::{
Clock, Pagination, RepositoryAccess,
Pagination, RepositoryAccess,
upstream_oauth2::{UpstreamOAuthProviderFilter, UpstreamOAuthProviderParams},
};
use mas_storage_pg::PgRepository;
Expand Down
3 changes: 3 additions & 0 deletions crates/data-model/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ url.workspace = true
crc.workspace = true
ulid.workspace = true
rand.workspace = true
rand_chacha.workspace = true
regex.workspace = true
woothee.workspace = true
ruma-common.workspace = true
lettre.workspace = true

mas-iana.workspace = true
mas-jose.workspace = true
oauth2-types.workspace = true

File renamed without changes.
12 changes: 12 additions & 0 deletions crates/data-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.

//!
//! This crate defines a [`Clock`] trait that can be used to abstract the
//! way the current time is retrieved. It has two implementation:
//! [`SystemClock`] that uses the system time and [`MockClock`] which is useful
//! for testing.
//!
//! [`MockClock`]: crate::clock::MockClock

#![allow(clippy::module_name_repetitions)]

use thiserror::Error;

pub mod clock;
pub(crate) mod compat;
pub mod oauth2;
pub(crate) mod policy_data;
Expand All @@ -16,6 +25,7 @@ pub(crate) mod tokens;
pub(crate) mod upstream_oauth2;
pub(crate) mod user_agent;
pub(crate) mod users;
mod utils;

/// Error when an invalid state transition is attempted.
#[derive(Debug, Error)]
Expand All @@ -25,6 +35,7 @@ pub struct InvalidTransitionError;
pub use ulid::Ulid;

pub use self::{
clock::{Clock, SystemClock},
compat::{
CompatAccessToken, CompatRefreshToken, CompatRefreshTokenState, CompatSession,
CompatSessionState, CompatSsoLogin, CompatSsoLoginState, Device, ToScopeTokenError,
Expand Down Expand Up @@ -53,4 +64,5 @@ pub use self::{
UserEmailAuthentication, UserEmailAuthenticationCode, UserRecoverySession,
UserRecoveryTicket, UserRegistration, UserRegistrationPassword, UserRegistrationToken,
},
utils::{BoxClock, BoxRng},
};
71 changes: 43 additions & 28 deletions crates/data-model/src/oauth2/authorization_grant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.

use std::str::FromStr as _;

use chrono::{DateTime, Utc};
use mas_iana::oauth::PkceCodeChallengeMethod;
use oauth2_types::{
Expand Down Expand Up @@ -142,6 +144,7 @@ impl AuthorizationGrantStage {

pub enum LoginHint<'a> {
MXID(&'a UserId),
Email(lettre::Address),
None,
}

Expand Down Expand Up @@ -172,15 +175,26 @@ impl std::ops::Deref for AuthorizationGrant {
}

impl AuthorizationGrant {
/// Parse a `login_hint`
///
/// Returns `LoginHint::MXID` for valid mxid 'mxid:@john.doe:example.com'
///
/// Returns `LoginHint::Email` for valid email '[email protected]'
///
/// Otherwise returns `LoginHint::None`
#[must_use]
pub fn parse_login_hint(&self, homeserver: &str) -> LoginHint {
let Some(login_hint) = &self.login_hint else {
return LoginHint::None;
};

// Return none if the format is incorrect
let Some((prefix, value)) = login_hint.split_once(':') else {
return LoginHint::None;
// Validate the email
let Ok(address) = lettre::Address::from_str(login_hint) else {
// Return none if the format is incorrect
return LoginHint::None;
};
return LoginHint::Email(address);
};

match prefix {
Expand Down Expand Up @@ -271,17 +285,15 @@ impl AuthorizationGrant {

#[cfg(test)]
mod tests {
use rand::thread_rng;
use rand::SeedableRng;

use super::*;
use crate::clock::{Clock, MockClock};

#[test]
fn no_login_hint() {
#[allow(clippy::disallowed_methods)]
let mut rng = thread_rng();

#[allow(clippy::disallowed_methods)]
let now = Utc::now();
let now = MockClock::default().now();
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);

let grant = AuthorizationGrant {
login_hint: None,
Expand All @@ -295,11 +307,8 @@ mod tests {

#[test]
fn valid_login_hint() {
#[allow(clippy::disallowed_methods)]
let mut rng = thread_rng();

#[allow(clippy::disallowed_methods)]
let now = Utc::now();
let now = MockClock::default().now();
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);

let grant = AuthorizationGrant {
login_hint: Some(String::from("mxid:@example-user:example.com")),
Expand All @@ -312,12 +321,24 @@ mod tests {
}

#[test]
fn invalid_login_hint() {
#[allow(clippy::disallowed_methods)]
let mut rng = thread_rng();
fn valid_login_hint_with_email() {
let now = MockClock::default().now();
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);

let grant = AuthorizationGrant {
login_hint: Some(String::from("example@user")),
..AuthorizationGrant::sample(now, &mut rng)
};

let hint = grant.parse_login_hint("example.com");

assert!(matches!(hint, LoginHint::Email(email) if email.to_string() == "example@user"));
}

#[allow(clippy::disallowed_methods)]
let now = Utc::now();
#[test]
fn invalid_login_hint() {
let now = MockClock::default().now();
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);

let grant = AuthorizationGrant {
login_hint: Some(String::from("example-user")),
Expand All @@ -331,11 +352,8 @@ mod tests {

#[test]
fn valid_login_hint_for_wrong_homeserver() {
#[allow(clippy::disallowed_methods)]
let mut rng = thread_rng();

#[allow(clippy::disallowed_methods)]
let now = Utc::now();
let now = MockClock::default().now();
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);

let grant = AuthorizationGrant {
login_hint: Some(String::from("mxid:@example-user:matrix.org")),
Expand All @@ -349,11 +367,8 @@ mod tests {

#[test]
fn unknown_login_hint_type() {
#[allow(clippy::disallowed_methods)]
let mut rng = thread_rng();

#[allow(clippy::disallowed_methods)]
let now = Utc::now();
let now = MockClock::default().now();
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);

let grant = AuthorizationGrant {
login_hint: Some(String::from("something:anything")),
Expand Down
13 changes: 13 additions & 0 deletions crates/data-model/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.

use rand_chacha::rand_core::CryptoRngCore;

use crate::clock::Clock;

/// A boxed [`Clock`]
pub type BoxClock = Box<dyn Clock + Send>;
/// A boxed random number generator
pub type BoxRng = Box<dyn CryptoRngCore + Send>;
3 changes: 1 addition & 2 deletions crates/handlers/src/activity_tracker/bound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

use std::net::IpAddr;

use mas_data_model::{BrowserSession, CompatSession, Session};
use mas_storage::Clock;
use mas_data_model::{BrowserSession, Clock, CompatSession, Session};

use crate::activity_tracker::ActivityTracker;

Expand Down
4 changes: 2 additions & 2 deletions crates/handlers/src/activity_tracker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ mod worker;
use std::net::IpAddr;

use chrono::{DateTime, Utc};
use mas_data_model::{BrowserSession, CompatSession, Session};
use mas_storage::{BoxRepositoryFactory, Clock};
use mas_data_model::{BrowserSession, Clock, CompatSession, Session};
use mas_storage::BoxRepositoryFactory;
use tokio_util::{sync::CancellationToken, task::TaskTracker};
use ulid::Ulid;

Expand Down
4 changes: 2 additions & 2 deletions crates/handlers/src/admin/call_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use axum_extra::TypedHeader;
use headers::{Authorization, authorization::Bearer};
use hyper::StatusCode;
use mas_axum_utils::record_error;
use mas_data_model::{Session, User};
use mas_storage::{BoxClock, BoxRepository, RepositoryError};
use mas_data_model::{BoxClock, Session, User};
use mas_storage::{BoxRepository, RepositoryError};
use ulid::Ulid;

use super::response::ErrorResponse;
Expand Down
2 changes: 1 addition & 1 deletion crates/handlers/src/admin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ use axum::{
use hyper::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE};
use indexmap::IndexMap;
use mas_axum_utils::InternalError;
use mas_data_model::BoxRng;
use mas_http::CorsLayerExt;
use mas_matrix::HomeserverConnection;
use mas_policy::PolicyFactory;
use mas_router::{
ApiDoc, ApiDocCallback, OAuth2AuthorizationEndpoint, OAuth2TokenEndpoint, Route, SimpleRoute,
UrlBuilder,
};
use mas_storage::BoxRng;
use mas_templates::{ApiDocContext, Templates};
use tower_http::cors::{Any, CorsLayer};

Expand Down
Loading
Loading