You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-28 11:02:02 +03:00
storage: make the Clock a trait
This commit is contained in:
@ -21,7 +21,7 @@ use mas_storage::{
|
|||||||
oauth2::OAuth2ClientRepository,
|
oauth2::OAuth2ClientRepository,
|
||||||
upstream_oauth2::UpstreamOAuthProviderRepository,
|
upstream_oauth2::UpstreamOAuthProviderRepository,
|
||||||
user::{UserEmailRepository, UserPasswordRepository, UserRepository},
|
user::{UserEmailRepository, UserPasswordRepository, UserRepository},
|
||||||
Clock, Repository,
|
Repository, SystemClock,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use oauth2_types::scope::Scope;
|
use oauth2_types::scope::Scope;
|
||||||
@ -188,7 +188,7 @@ impl Options {
|
|||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
pub async fn run(&self, root: &super::Options) -> anyhow::Result<()> {
|
pub async fn run(&self, root: &super::Options) -> anyhow::Result<()> {
|
||||||
use Subcommand as SC;
|
use Subcommand as SC;
|
||||||
let clock = Clock::default();
|
let clock = SystemClock::default();
|
||||||
// XXX: we should disallow SeedableRng::from_entropy
|
// XXX: we should disallow SeedableRng::from_entropy
|
||||||
let mut rng = rand_chacha::ChaChaRng::from_entropy();
|
let mut rng = rand_chacha::ChaChaRng::from_entropy();
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
use camino::Utf8PathBuf;
|
use camino::Utf8PathBuf;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use mas_storage::Clock;
|
use mas_storage::{Clock, SystemClock};
|
||||||
use mas_templates::Templates;
|
use mas_templates::Templates;
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
use tracing::info_span;
|
use tracing::info_span;
|
||||||
@ -41,7 +41,7 @@ impl Options {
|
|||||||
SC::Check { path } => {
|
SC::Check { path } => {
|
||||||
let _span = info_span!("cli.templates.check").entered();
|
let _span = info_span!("cli.templates.check").entered();
|
||||||
|
|
||||||
let clock = Clock::default();
|
let clock = SystemClock::default();
|
||||||
// XXX: we should disallow SeedableRng::from_entropy
|
// XXX: we should disallow SeedableRng::from_entropy
|
||||||
let mut rng = rand_chacha::ChaChaRng::from_entropy();
|
let mut rng = rand_chacha::ChaChaRng::from_entropy();
|
||||||
let url_builder = mas_router::UrlBuilder::new("https://example.com/".parse()?);
|
let url_builder = mas_router::UrlBuilder::new("https://example.com/".parse()?);
|
||||||
|
@ -22,7 +22,7 @@ use mas_storage::{
|
|||||||
CompatSsoLoginRepository,
|
CompatSsoLoginRepository,
|
||||||
},
|
},
|
||||||
user::{UserPasswordRepository, UserRepository},
|
user::{UserPasswordRepository, UserRepository},
|
||||||
Clock, Repository,
|
Clock, Repository, SystemClock,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -254,7 +254,7 @@ pub(crate) async fn post(
|
|||||||
|
|
||||||
async fn token_login(
|
async fn token_login(
|
||||||
repo: &mut PgRepository,
|
repo: &mut PgRepository,
|
||||||
clock: &Clock,
|
clock: &SystemClock,
|
||||||
token: &str,
|
token: &str,
|
||||||
) -> Result<(CompatSession, User), RouteError> {
|
) -> Result<(CompatSession, User), RouteError> {
|
||||||
let login = repo
|
let login = repo
|
||||||
|
@ -31,7 +31,7 @@ use mas_keystore::Encrypter;
|
|||||||
use mas_router::{CompatLoginSsoAction, PostAuthAction, Route};
|
use mas_router::{CompatLoginSsoAction, PostAuthAction, Route};
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
compat::{CompatSessionRepository, CompatSsoLoginRepository},
|
compat::{CompatSessionRepository, CompatSsoLoginRepository},
|
||||||
Repository,
|
Clock, Repository,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use mas_templates::{CompatSsoContext, ErrorContext, TemplateContext, Templates};
|
use mas_templates::{CompatSsoContext, ErrorContext, TemplateContext, Templates};
|
||||||
|
@ -18,7 +18,7 @@ use hyper::StatusCode;
|
|||||||
use mas_data_model::TokenType;
|
use mas_data_model::TokenType;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
compat::{CompatAccessTokenRepository, CompatSessionRepository},
|
compat::{CompatAccessTokenRepository, CompatSessionRepository},
|
||||||
Clock, Repository,
|
Clock, Repository, SystemClock,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
@ -72,7 +72,7 @@ pub(crate) async fn post(
|
|||||||
State(pool): State<PgPool>,
|
State(pool): State<PgPool>,
|
||||||
maybe_authorization: Option<TypedHeader<Authorization<Bearer>>>,
|
maybe_authorization: Option<TypedHeader<Authorization<Bearer>>>,
|
||||||
) -> Result<impl IntoResponse, RouteError> {
|
) -> Result<impl IntoResponse, RouteError> {
|
||||||
let clock = Clock::default();
|
let clock = SystemClock::default();
|
||||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||||
|
|
||||||
let TypedHeader(authorization) = maybe_authorization.ok_or(RouteError::MissingAuthorization)?;
|
let TypedHeader(authorization) = maybe_authorization.ok_or(RouteError::MissingAuthorization)?;
|
||||||
|
@ -18,7 +18,7 @@ use hyper::StatusCode;
|
|||||||
use mas_data_model::{TokenFormatError, TokenType};
|
use mas_data_model::{TokenFormatError, TokenType};
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
compat::{CompatAccessTokenRepository, CompatRefreshTokenRepository, CompatSessionRepository},
|
compat::{CompatAccessTokenRepository, CompatRefreshTokenRepository, CompatSessionRepository},
|
||||||
Repository,
|
Clock, Repository,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -409,8 +409,8 @@ async fn test_state(pool: PgPool) -> Result<AppState, anyhow::Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// XXX: that should be moved somewhere else
|
// XXX: that should be moved somewhere else
|
||||||
fn clock_and_rng() -> (mas_storage::Clock, rand_chacha::ChaChaRng) {
|
fn clock_and_rng() -> (mas_storage::SystemClock, rand_chacha::ChaChaRng) {
|
||||||
let clock = mas_storage::Clock::default();
|
let clock = mas_storage::SystemClock::default();
|
||||||
|
|
||||||
// This rng is used to source the local rng
|
// This rng is used to source the local rng
|
||||||
#[allow(clippy::disallowed_methods)]
|
#[allow(clippy::disallowed_methods)]
|
||||||
|
@ -30,7 +30,7 @@ use mas_policy::PolicyFactory;
|
|||||||
use mas_router::{PostAuthAction, Route};
|
use mas_router::{PostAuthAction, Route};
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
oauth2::{OAuth2AuthorizationGrantRepository, OAuth2ClientRepository},
|
oauth2::{OAuth2AuthorizationGrantRepository, OAuth2ClientRepository},
|
||||||
Repository,
|
Clock, Repository,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use mas_templates::{ConsentContext, PolicyViolationContext, TemplateContext, Templates};
|
use mas_templates::{ConsentContext, PolicyViolationContext, TemplateContext, Templates};
|
||||||
|
@ -25,7 +25,7 @@ use mas_storage::{
|
|||||||
compat::{CompatAccessTokenRepository, CompatRefreshTokenRepository, CompatSessionRepository},
|
compat::{CompatAccessTokenRepository, CompatRefreshTokenRepository, CompatSessionRepository},
|
||||||
oauth2::{OAuth2AccessTokenRepository, OAuth2RefreshTokenRepository, OAuth2SessionRepository},
|
oauth2::{OAuth2AccessTokenRepository, OAuth2RefreshTokenRepository, OAuth2SessionRepository},
|
||||||
user::{BrowserSessionRepository, UserRepository},
|
user::{BrowserSessionRepository, UserRepository},
|
||||||
Clock, Repository,
|
Clock, Repository, SystemClock,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use oauth2_types::{
|
use oauth2_types::{
|
||||||
@ -130,7 +130,7 @@ pub(crate) async fn post(
|
|||||||
State(encrypter): State<Encrypter>,
|
State(encrypter): State<Encrypter>,
|
||||||
client_authorization: ClientAuthorization<IntrospectionRequest>,
|
client_authorization: ClientAuthorization<IntrospectionRequest>,
|
||||||
) -> Result<impl IntoResponse, RouteError> {
|
) -> Result<impl IntoResponse, RouteError> {
|
||||||
let clock = Clock::default();
|
let clock = SystemClock::default();
|
||||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||||
|
|
||||||
let client = client_authorization
|
let client = client_authorization
|
||||||
|
@ -37,7 +37,7 @@ use mas_storage::{
|
|||||||
OAuth2RefreshTokenRepository, OAuth2SessionRepository,
|
OAuth2RefreshTokenRepository, OAuth2SessionRepository,
|
||||||
},
|
},
|
||||||
user::BrowserSessionRepository,
|
user::BrowserSessionRepository,
|
||||||
Repository,
|
Clock, Repository,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use oauth2_types::{
|
use oauth2_types::{
|
||||||
|
@ -31,7 +31,7 @@ use mas_router::UrlBuilder;
|
|||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
oauth2::OAuth2ClientRepository,
|
oauth2::OAuth2ClientRepository,
|
||||||
user::{BrowserSessionRepository, UserEmailRepository},
|
user::{BrowserSessionRepository, UserEmailRepository},
|
||||||
Repository,
|
Clock, Repository,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use oauth2_types::scope;
|
use oauth2_types::scope;
|
||||||
|
@ -24,7 +24,7 @@ use mas_oidc_client::requests::authorization_code::AuthorizationRequestData;
|
|||||||
use mas_router::UrlBuilder;
|
use mas_router::UrlBuilder;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
upstream_oauth2::{UpstreamOAuthProviderRepository, UpstreamOAuthSessionRepository},
|
upstream_oauth2::{UpstreamOAuthProviderRepository, UpstreamOAuthSessionRepository},
|
||||||
Repository,
|
Clock, Repository,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
@ -30,7 +30,7 @@ use mas_storage::{
|
|||||||
UpstreamOAuthLinkRepository, UpstreamOAuthProviderRepository,
|
UpstreamOAuthLinkRepository, UpstreamOAuthProviderRepository,
|
||||||
UpstreamOAuthSessionRepository,
|
UpstreamOAuthSessionRepository,
|
||||||
},
|
},
|
||||||
Repository,
|
Clock, Repository,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use oauth2_types::errors::ClientErrorCode;
|
use oauth2_types::errors::ClientErrorCode;
|
||||||
|
@ -27,7 +27,7 @@ use mas_keystore::Encrypter;
|
|||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
upstream_oauth2::{UpstreamOAuthLinkRepository, UpstreamOAuthSessionRepository},
|
upstream_oauth2::{UpstreamOAuthLinkRepository, UpstreamOAuthSessionRepository},
|
||||||
user::{BrowserSessionRepository, UserRepository},
|
user::{BrowserSessionRepository, UserRepository},
|
||||||
Repository,
|
Clock, Repository,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use mas_templates::{
|
use mas_templates::{
|
||||||
|
@ -24,7 +24,7 @@ use mas_axum_utils::{
|
|||||||
use mas_email::Mailer;
|
use mas_email::Mailer;
|
||||||
use mas_keystore::Encrypter;
|
use mas_keystore::Encrypter;
|
||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::{user::UserEmailRepository, Repository};
|
use mas_storage::{user::UserEmailRepository, Clock, Repository};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use mas_templates::{EmailAddContext, TemplateContext, Templates};
|
use mas_templates::{EmailAddContext, TemplateContext, Templates};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -71,7 +71,7 @@ pub(crate) async fn get(
|
|||||||
|
|
||||||
async fn render(
|
async fn render(
|
||||||
rng: impl Rng + Send,
|
rng: impl Rng + Send,
|
||||||
clock: &Clock,
|
clock: &impl Clock,
|
||||||
templates: Templates,
|
templates: Templates,
|
||||||
session: BrowserSession,
|
session: BrowserSession,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||||
@ -94,7 +94,7 @@ async fn start_email_verification(
|
|||||||
mailer: &Mailer,
|
mailer: &Mailer,
|
||||||
repo: &mut impl Repository,
|
repo: &mut impl Repository,
|
||||||
mut rng: impl Rng + Send,
|
mut rng: impl Rng + Send,
|
||||||
clock: &Clock,
|
clock: &impl Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
user_email: UserEmail,
|
user_email: UserEmail,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
@ -24,7 +24,7 @@ use mas_axum_utils::{
|
|||||||
};
|
};
|
||||||
use mas_keystore::Encrypter;
|
use mas_keystore::Encrypter;
|
||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::{user::UserEmailRepository, Clock, Repository};
|
use mas_storage::{user::UserEmailRepository, Clock, Repository, SystemClock};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use mas_templates::{EmailVerificationPageContext, TemplateContext, Templates};
|
use mas_templates::{EmailVerificationPageContext, TemplateContext, Templates};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@ -89,7 +89,7 @@ pub(crate) async fn post(
|
|||||||
Path(id): Path<Ulid>,
|
Path(id): Path<Ulid>,
|
||||||
Form(form): Form<ProtectedForm<CodeForm>>,
|
Form(form): Form<ProtectedForm<CodeForm>>,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
let clock = Clock::default();
|
let clock = SystemClock::default();
|
||||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||||
|
|
||||||
let form = cookie_jar.verify_form(clock.now(), form)?;
|
let form = cookie_jar.verify_form(clock.now(), form)?;
|
||||||
|
@ -25,7 +25,7 @@ use mas_keystore::Encrypter;
|
|||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
user::{BrowserSessionRepository, UserEmailRepository},
|
user::{BrowserSessionRepository, UserEmailRepository},
|
||||||
Repository,
|
Clock, Repository,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use mas_templates::{AccountContext, TemplateContext, Templates};
|
use mas_templates::{AccountContext, TemplateContext, Templates};
|
||||||
|
@ -67,7 +67,7 @@ pub(crate) async fn get(
|
|||||||
|
|
||||||
async fn render(
|
async fn render(
|
||||||
rng: impl Rng + Send,
|
rng: impl Rng + Send,
|
||||||
clock: &Clock,
|
clock: &impl Clock,
|
||||||
templates: Templates,
|
templates: Templates,
|
||||||
session: BrowserSession,
|
session: BrowserSession,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||||
|
@ -20,6 +20,7 @@ use axum_extra::extract::PrivateCookieJar;
|
|||||||
use mas_axum_utils::{csrf::CsrfExt, FancyError, SessionInfoExt};
|
use mas_axum_utils::{csrf::CsrfExt, FancyError, SessionInfoExt};
|
||||||
use mas_keystore::Encrypter;
|
use mas_keystore::Encrypter;
|
||||||
use mas_router::UrlBuilder;
|
use mas_router::UrlBuilder;
|
||||||
|
use mas_storage::Clock;
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use mas_templates::{IndexContext, TemplateContext, Templates};
|
use mas_templates::{IndexContext, TemplateContext, Templates};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
@ -167,7 +167,7 @@ async fn login(
|
|||||||
password_manager: PasswordManager,
|
password_manager: PasswordManager,
|
||||||
repo: &mut impl Repository,
|
repo: &mut impl Repository,
|
||||||
mut rng: impl Rng + CryptoRng + Send,
|
mut rng: impl Rng + CryptoRng + Send,
|
||||||
clock: &Clock,
|
clock: &impl Clock,
|
||||||
username: &str,
|
username: &str,
|
||||||
password: &str,
|
password: &str,
|
||||||
) -> Result<BrowserSession, FormError> {
|
) -> Result<BrowserSession, FormError> {
|
||||||
|
@ -23,7 +23,7 @@ use mas_axum_utils::{
|
|||||||
};
|
};
|
||||||
use mas_keystore::Encrypter;
|
use mas_keystore::Encrypter;
|
||||||
use mas_router::{PostAuthAction, Route};
|
use mas_router::{PostAuthAction, Route};
|
||||||
use mas_storage::{user::BrowserSessionRepository, Clock, Repository};
|
use mas_storage::{user::BrowserSessionRepository, Clock, Repository, SystemClock};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ pub(crate) async fn post(
|
|||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||||
Form(form): Form<ProtectedForm<Option<PostAuthAction>>>,
|
Form(form): Form<ProtectedForm<Option<PostAuthAction>>>,
|
||||||
) -> Result<impl IntoResponse, FancyError> {
|
) -> Result<impl IntoResponse, FancyError> {
|
||||||
let clock = Clock::default();
|
let clock = SystemClock::default();
|
||||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||||
|
|
||||||
let form = cookie_jar.verify_form(clock.now(), form)?;
|
let form = cookie_jar.verify_form(clock.now(), form)?;
|
||||||
|
@ -26,7 +26,7 @@ use mas_keystore::Encrypter;
|
|||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
user::{BrowserSessionRepository, UserPasswordRepository},
|
user::{BrowserSessionRepository, UserPasswordRepository},
|
||||||
Repository,
|
Clock, Repository,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use mas_templates::{ReauthContext, TemplateContext, Templates};
|
use mas_templates::{ReauthContext, TemplateContext, Templates};
|
||||||
|
@ -33,7 +33,7 @@ use mas_policy::PolicyFactory;
|
|||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
user::{BrowserSessionRepository, UserEmailRepository, UserPasswordRepository, UserRepository},
|
user::{BrowserSessionRepository, UserEmailRepository, UserPasswordRepository, UserRepository},
|
||||||
Repository,
|
Clock, Repository,
|
||||||
};
|
};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use mas_templates::{
|
use mas_templates::{
|
||||||
|
@ -143,7 +143,7 @@ impl<'c> CompatAccessTokenRepository for PgCompatAccessTokenRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_session: &CompatSession,
|
compat_session: &CompatSession,
|
||||||
token: String,
|
token: String,
|
||||||
expires_after: Option<Duration>,
|
expires_after: Option<Duration>,
|
||||||
@ -191,7 +191,7 @@ impl<'c> CompatAccessTokenRepository for PgCompatAccessTokenRepository<'c> {
|
|||||||
)]
|
)]
|
||||||
async fn expire(
|
async fn expire(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
mut compat_access_token: CompatAccessToken,
|
mut compat_access_token: CompatAccessToken,
|
||||||
) -> Result<CompatAccessToken, Self::Error> {
|
) -> Result<CompatAccessToken, Self::Error> {
|
||||||
let expires_at = clock.now();
|
let expires_at = clock.now();
|
||||||
|
@ -27,6 +27,7 @@ mod tests {
|
|||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use mas_data_model::Device;
|
use mas_data_model::Device;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
|
clock::MockClock,
|
||||||
compat::{
|
compat::{
|
||||||
CompatAccessTokenRepository, CompatRefreshTokenRepository, CompatSessionRepository,
|
CompatAccessTokenRepository, CompatRefreshTokenRepository, CompatSessionRepository,
|
||||||
},
|
},
|
||||||
@ -44,7 +45,7 @@ mod tests {
|
|||||||
const FIRST_TOKEN: &str = "first_access_token";
|
const FIRST_TOKEN: &str = "first_access_token";
|
||||||
const SECOND_TOKEN: &str = "second_access_token";
|
const SECOND_TOKEN: &str = "second_access_token";
|
||||||
let mut rng = ChaChaRng::seed_from_u64(42);
|
let mut rng = ChaChaRng::seed_from_u64(42);
|
||||||
let clock = Clock::mock();
|
let clock = MockClock::default();
|
||||||
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
|
|
||||||
// Create a user
|
// Create a user
|
||||||
@ -101,7 +102,7 @@ mod tests {
|
|||||||
const FIRST_TOKEN: &str = "first_access_token";
|
const FIRST_TOKEN: &str = "first_access_token";
|
||||||
const SECOND_TOKEN: &str = "second_access_token";
|
const SECOND_TOKEN: &str = "second_access_token";
|
||||||
let mut rng = ChaChaRng::seed_from_u64(42);
|
let mut rng = ChaChaRng::seed_from_u64(42);
|
||||||
let clock = Clock::mock();
|
let clock = MockClock::default();
|
||||||
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
|
|
||||||
// Create a user
|
// Create a user
|
||||||
@ -221,7 +222,7 @@ mod tests {
|
|||||||
const ACCESS_TOKEN: &str = "access_token";
|
const ACCESS_TOKEN: &str = "access_token";
|
||||||
const REFRESH_TOKEN: &str = "refresh_token";
|
const REFRESH_TOKEN: &str = "refresh_token";
|
||||||
let mut rng = ChaChaRng::seed_from_u64(42);
|
let mut rng = ChaChaRng::seed_from_u64(42);
|
||||||
let clock = Clock::mock();
|
let clock = MockClock::default();
|
||||||
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
|
|
||||||
// Create a user
|
// Create a user
|
||||||
|
@ -154,7 +154,7 @@ impl<'c> CompatRefreshTokenRepository for PgCompatRefreshTokenRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_session: &CompatSession,
|
compat_session: &CompatSession,
|
||||||
compat_access_token: &CompatAccessToken,
|
compat_access_token: &CompatAccessToken,
|
||||||
token: String,
|
token: String,
|
||||||
@ -202,7 +202,7 @@ impl<'c> CompatRefreshTokenRepository for PgCompatRefreshTokenRepository<'c> {
|
|||||||
)]
|
)]
|
||||||
async fn consume(
|
async fn consume(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_refresh_token: CompatRefreshToken,
|
compat_refresh_token: CompatRefreshToken,
|
||||||
) -> Result<CompatRefreshToken, Self::Error> {
|
) -> Result<CompatRefreshToken, Self::Error> {
|
||||||
let consumed_at = clock.now();
|
let consumed_at = clock.now();
|
||||||
|
@ -122,7 +122,7 @@ impl<'c> CompatSessionRepository for PgCompatSessionRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
device: Device,
|
device: Device,
|
||||||
) -> Result<CompatSession, Self::Error> {
|
) -> Result<CompatSession, Self::Error> {
|
||||||
@ -166,7 +166,7 @@ impl<'c> CompatSessionRepository for PgCompatSessionRepository<'c> {
|
|||||||
)]
|
)]
|
||||||
async fn finish(
|
async fn finish(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_session: CompatSession,
|
compat_session: CompatSession,
|
||||||
) -> Result<CompatSession, Self::Error> {
|
) -> Result<CompatSession, Self::Error> {
|
||||||
let finished_at = clock.now();
|
let finished_at = clock.now();
|
||||||
|
@ -177,7 +177,7 @@ impl<'c> CompatSsoLoginRepository for PgCompatSsoLoginRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
login_token: String,
|
login_token: String,
|
||||||
redirect_uri: Url,
|
redirect_uri: Url,
|
||||||
) -> Result<CompatSsoLogin, Self::Error> {
|
) -> Result<CompatSsoLogin, Self::Error> {
|
||||||
@ -223,7 +223,7 @@ impl<'c> CompatSsoLoginRepository for PgCompatSsoLoginRepository<'c> {
|
|||||||
)]
|
)]
|
||||||
async fn fulfill(
|
async fn fulfill(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_sso_login: CompatSsoLogin,
|
compat_sso_login: CompatSsoLogin,
|
||||||
compat_session: &CompatSession,
|
compat_session: &CompatSession,
|
||||||
) -> Result<CompatSsoLogin, Self::Error> {
|
) -> Result<CompatSsoLogin, Self::Error> {
|
||||||
@ -265,7 +265,7 @@ impl<'c> CompatSsoLoginRepository for PgCompatSsoLoginRepository<'c> {
|
|||||||
)]
|
)]
|
||||||
async fn exchange(
|
async fn exchange(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_sso_login: CompatSsoLogin,
|
compat_sso_login: CompatSsoLogin,
|
||||||
) -> Result<CompatSsoLogin, Self::Error> {
|
) -> Result<CompatSsoLogin, Self::Error> {
|
||||||
let exchanged_at = clock.now();
|
let exchanged_at = clock.now();
|
||||||
|
@ -142,7 +142,7 @@ impl<'c> OAuth2AccessTokenRepository for PgOAuth2AccessTokenRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
access_token: String,
|
access_token: String,
|
||||||
expires_after: Duration,
|
expires_after: Duration,
|
||||||
@ -182,7 +182,7 @@ impl<'c> OAuth2AccessTokenRepository for PgOAuth2AccessTokenRepository<'c> {
|
|||||||
|
|
||||||
async fn revoke(
|
async fn revoke(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
access_token: AccessToken,
|
access_token: AccessToken,
|
||||||
) -> Result<AccessToken, Self::Error> {
|
) -> Result<AccessToken, Self::Error> {
|
||||||
let revoked_at = clock.now();
|
let revoked_at = clock.now();
|
||||||
@ -205,7 +205,7 @@ impl<'c> OAuth2AccessTokenRepository for PgOAuth2AccessTokenRepository<'c> {
|
|||||||
.map_err(DatabaseError::to_invalid_operation)
|
.map_err(DatabaseError::to_invalid_operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn cleanup_expired(&mut self, clock: &Clock) -> Result<usize, Self::Error> {
|
async fn cleanup_expired(&mut self, clock: &dyn Clock) -> Result<usize, Self::Error> {
|
||||||
// Cleanup token which expired more than 15 minutes ago
|
// Cleanup token which expired more than 15 minutes ago
|
||||||
let threshold = clock.now() - Duration::minutes(15);
|
let threshold = clock.now() - Duration::minutes(15);
|
||||||
let res = sqlx::query!(
|
let res = sqlx::query!(
|
||||||
|
@ -211,7 +211,7 @@ impl<'c> OAuth2AuthorizationGrantRepository for PgOAuth2AuthorizationGrantReposi
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
redirect_uri: Url,
|
redirect_uri: Url,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
@ -410,7 +410,7 @@ impl<'c> OAuth2AuthorizationGrantRepository for PgOAuth2AuthorizationGrantReposi
|
|||||||
)]
|
)]
|
||||||
async fn fulfill(
|
async fn fulfill(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
grant: AuthorizationGrant,
|
grant: AuthorizationGrant,
|
||||||
) -> Result<AuthorizationGrant, Self::Error> {
|
) -> Result<AuthorizationGrant, Self::Error> {
|
||||||
@ -451,7 +451,7 @@ impl<'c> OAuth2AuthorizationGrantRepository for PgOAuth2AuthorizationGrantReposi
|
|||||||
)]
|
)]
|
||||||
async fn exchange(
|
async fn exchange(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
grant: AuthorizationGrant,
|
grant: AuthorizationGrant,
|
||||||
) -> Result<AuthorizationGrant, Self::Error> {
|
) -> Result<AuthorizationGrant, Self::Error> {
|
||||||
let exchanged_at = clock.now();
|
let exchanged_at = clock.now();
|
||||||
|
@ -378,7 +378,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut rng: &mut (dyn RngCore + Send),
|
mut rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
redirect_uris: Vec<Url>,
|
redirect_uris: Vec<Url>,
|
||||||
encrypted_client_secret: Option<String>,
|
encrypted_client_secret: Option<String>,
|
||||||
grant_types: Vec<GrantType>,
|
grant_types: Vec<GrantType>,
|
||||||
@ -535,7 +535,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> {
|
|||||||
async fn add_from_config(
|
async fn add_from_config(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut rng: impl Rng + Send,
|
mut rng: impl Rng + Send,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
client_id: Ulid,
|
client_id: Ulid,
|
||||||
client_auth_method: OAuthClientAuthenticationMethod,
|
client_auth_method: OAuthClientAuthenticationMethod,
|
||||||
encrypted_client_secret: Option<String>,
|
encrypted_client_secret: Option<String>,
|
||||||
@ -707,7 +707,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> {
|
|||||||
async fn give_consent_for_user(
|
async fn give_consent_for_user(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
user: &User,
|
user: &User,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
|
@ -150,7 +150,7 @@ impl<'c> OAuth2RefreshTokenRepository for PgOAuth2RefreshTokenRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
access_token: &AccessToken,
|
access_token: &AccessToken,
|
||||||
refresh_token: String,
|
refresh_token: String,
|
||||||
@ -199,7 +199,7 @@ impl<'c> OAuth2RefreshTokenRepository for PgOAuth2RefreshTokenRepository<'c> {
|
|||||||
)]
|
)]
|
||||||
async fn consume(
|
async fn consume(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
refresh_token: RefreshToken,
|
refresh_token: RefreshToken,
|
||||||
) -> Result<RefreshToken, Self::Error> {
|
) -> Result<RefreshToken, Self::Error> {
|
||||||
let consumed_at = clock.now();
|
let consumed_at = clock.now();
|
||||||
|
@ -131,7 +131,7 @@ impl<'c> OAuth2SessionRepository for PgOAuth2SessionRepository<'c> {
|
|||||||
async fn create_from_grant(
|
async fn create_from_grant(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
grant: &AuthorizationGrant,
|
grant: &AuthorizationGrant,
|
||||||
user_session: &BrowserSession,
|
user_session: &BrowserSession,
|
||||||
) -> Result<Session, Self::Error> {
|
) -> Result<Session, Self::Error> {
|
||||||
@ -182,7 +182,7 @@ impl<'c> OAuth2SessionRepository for PgOAuth2SessionRepository<'c> {
|
|||||||
),
|
),
|
||||||
err,
|
err,
|
||||||
)]
|
)]
|
||||||
async fn finish(&mut self, clock: &Clock, session: Session) -> Result<Session, Self::Error> {
|
async fn finish(&mut self, clock: &dyn Clock, session: Session) -> Result<Session, Self::Error> {
|
||||||
let finished_at = clock.now();
|
let finished_at = clock.now();
|
||||||
let res = sqlx::query!(
|
let res = sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -149,7 +149,7 @@ impl<'c> UpstreamOAuthLinkRepository for PgUpstreamOAuthLinkRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
upstream_oauth_provider: &UpstreamOAuthProvider,
|
upstream_oauth_provider: &UpstreamOAuthProvider,
|
||||||
subject: String,
|
subject: String,
|
||||||
) -> Result<UpstreamOAuthLink, Self::Error> {
|
) -> Result<UpstreamOAuthLink, Self::Error> {
|
||||||
|
@ -25,12 +25,13 @@ pub use self::{
|
|||||||
mod tests {
|
mod tests {
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
|
clock::MockClock,
|
||||||
upstream_oauth2::{
|
upstream_oauth2::{
|
||||||
UpstreamOAuthLinkRepository, UpstreamOAuthProviderRepository,
|
UpstreamOAuthLinkRepository, UpstreamOAuthProviderRepository,
|
||||||
UpstreamOAuthSessionRepository,
|
UpstreamOAuthSessionRepository,
|
||||||
},
|
},
|
||||||
user::UserRepository,
|
user::UserRepository,
|
||||||
Clock, Pagination, Repository,
|
Pagination, Repository,
|
||||||
};
|
};
|
||||||
use oauth2_types::scope::{Scope, OPENID};
|
use oauth2_types::scope::{Scope, OPENID};
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
@ -41,7 +42,7 @@ mod tests {
|
|||||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||||
async fn test_repository(pool: PgPool) {
|
async fn test_repository(pool: PgPool) {
|
||||||
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);
|
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);
|
||||||
let clock = Clock::mock();
|
let clock = MockClock::default();
|
||||||
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
|
|
||||||
// The provider list should be empty at the start
|
// The provider list should be empty at the start
|
||||||
@ -183,7 +184,7 @@ mod tests {
|
|||||||
let scope = Scope::from_iter([OPENID]);
|
let scope = Scope::from_iter([OPENID]);
|
||||||
|
|
||||||
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);
|
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);
|
||||||
let clock = Clock::mock();
|
let clock = MockClock::default();
|
||||||
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
|
|
||||||
let mut ids = Vec::with_capacity(20);
|
let mut ids = Vec::with_capacity(20);
|
||||||
|
@ -149,7 +149,7 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
issuer: String,
|
issuer: String,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
token_endpoint_auth_method: OAuthClientAuthenticationMethod,
|
token_endpoint_auth_method: OAuthClientAuthenticationMethod,
|
||||||
|
@ -156,7 +156,7 @@ impl<'c> UpstreamOAuthSessionRepository for PgUpstreamOAuthSessionRepository<'c>
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
upstream_oauth_provider: &UpstreamOAuthProvider,
|
upstream_oauth_provider: &UpstreamOAuthProvider,
|
||||||
state_str: String,
|
state_str: String,
|
||||||
code_challenge_verifier: Option<String>,
|
code_challenge_verifier: Option<String>,
|
||||||
@ -217,7 +217,7 @@ impl<'c> UpstreamOAuthSessionRepository for PgUpstreamOAuthSessionRepository<'c>
|
|||||||
)]
|
)]
|
||||||
async fn complete_with_link(
|
async fn complete_with_link(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
upstream_oauth_authorization_session: UpstreamOAuthAuthorizationSession,
|
upstream_oauth_authorization_session: UpstreamOAuthAuthorizationSession,
|
||||||
upstream_oauth_link: &UpstreamOAuthLink,
|
upstream_oauth_link: &UpstreamOAuthLink,
|
||||||
id_token: Option<String>,
|
id_token: Option<String>,
|
||||||
@ -260,7 +260,7 @@ impl<'c> UpstreamOAuthSessionRepository for PgUpstreamOAuthSessionRepository<'c>
|
|||||||
)]
|
)]
|
||||||
async fn consume(
|
async fn consume(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
upstream_oauth_authorization_session: UpstreamOAuthAuthorizationSession,
|
upstream_oauth_authorization_session: UpstreamOAuthAuthorizationSession,
|
||||||
) -> Result<UpstreamOAuthAuthorizationSession, Self::Error> {
|
) -> Result<UpstreamOAuthAuthorizationSession, Self::Error> {
|
||||||
let consumed_at = clock.now();
|
let consumed_at = clock.now();
|
||||||
|
@ -68,7 +68,7 @@ struct UserEmailConfirmationCodeLookup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl UserEmailConfirmationCodeLookup {
|
impl UserEmailConfirmationCodeLookup {
|
||||||
fn into_verification(self, clock: &Clock) -> UserEmailVerification {
|
fn into_verification(self, clock: &dyn Clock) -> UserEmailVerification {
|
||||||
let now = clock.now();
|
let now = clock.now();
|
||||||
let state = if let Some(when) = self.consumed_at {
|
let state = if let Some(when) = self.consumed_at {
|
||||||
UserEmailVerificationState::AlreadyUsed { when }
|
UserEmailVerificationState::AlreadyUsed { when }
|
||||||
@ -301,7 +301,7 @@ impl<'c> UserEmailRepository for PgUserEmailRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
email: String,
|
email: String,
|
||||||
) -> Result<UserEmail, Self::Error> {
|
) -> Result<UserEmail, Self::Error> {
|
||||||
@ -378,7 +378,7 @@ impl<'c> UserEmailRepository for PgUserEmailRepository<'c> {
|
|||||||
|
|
||||||
async fn mark_as_verified(
|
async fn mark_as_verified(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
mut user_email: UserEmail,
|
mut user_email: UserEmail,
|
||||||
) -> Result<UserEmail, Self::Error> {
|
) -> Result<UserEmail, Self::Error> {
|
||||||
let confirmed_at = clock.now();
|
let confirmed_at = clock.now();
|
||||||
@ -430,7 +430,7 @@ impl<'c> UserEmailRepository for PgUserEmailRepository<'c> {
|
|||||||
async fn add_verification_code(
|
async fn add_verification_code(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user_email: &UserEmail,
|
user_email: &UserEmail,
|
||||||
max_age: chrono::Duration,
|
max_age: chrono::Duration,
|
||||||
code: String,
|
code: String,
|
||||||
@ -479,7 +479,7 @@ impl<'c> UserEmailRepository for PgUserEmailRepository<'c> {
|
|||||||
)]
|
)]
|
||||||
async fn find_verification_code(
|
async fn find_verification_code(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user_email: &UserEmail,
|
user_email: &UserEmail,
|
||||||
code: &str,
|
code: &str,
|
||||||
) -> Result<Option<UserEmailVerification>, Self::Error> {
|
) -> Result<Option<UserEmailVerification>, Self::Error> {
|
||||||
@ -521,7 +521,7 @@ impl<'c> UserEmailRepository for PgUserEmailRepository<'c> {
|
|||||||
)]
|
)]
|
||||||
async fn consume_verification_code(
|
async fn consume_verification_code(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
mut user_email_verification: UserEmailVerification,
|
mut user_email_verification: UserEmailVerification,
|
||||||
) -> Result<UserEmailVerification, Self::Error> {
|
) -> Result<UserEmailVerification, Self::Error> {
|
||||||
if !matches!(
|
if !matches!(
|
||||||
|
@ -148,7 +148,7 @@ impl<'c> UserRepository for PgUserRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
username: String,
|
username: String,
|
||||||
) -> Result<User, Self::Error> {
|
) -> Result<User, Self::Error> {
|
||||||
let created_at = clock.now();
|
let created_at = clock.now();
|
||||||
|
@ -115,7 +115,7 @@ impl<'c> UserPasswordRepository for PgUserPasswordRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
version: u16,
|
version: u16,
|
||||||
hashed_password: String,
|
hashed_password: String,
|
||||||
|
@ -142,7 +142,7 @@ impl<'c> BrowserSessionRepository for PgBrowserSessionRepository<'c> {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
) -> Result<BrowserSession, Self::Error> {
|
) -> Result<BrowserSession, Self::Error> {
|
||||||
let created_at = clock.now();
|
let created_at = clock.now();
|
||||||
@ -185,7 +185,7 @@ impl<'c> BrowserSessionRepository for PgBrowserSessionRepository<'c> {
|
|||||||
)]
|
)]
|
||||||
async fn finish(
|
async fn finish(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
mut user_session: BrowserSession,
|
mut user_session: BrowserSession,
|
||||||
) -> Result<BrowserSession, Self::Error> {
|
) -> Result<BrowserSession, Self::Error> {
|
||||||
let finished_at = clock.now();
|
let finished_at = clock.now();
|
||||||
@ -297,7 +297,7 @@ impl<'c> BrowserSessionRepository for PgBrowserSessionRepository<'c> {
|
|||||||
async fn authenticate_with_password(
|
async fn authenticate_with_password(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
mut user_session: BrowserSession,
|
mut user_session: BrowserSession,
|
||||||
user_password: &Password,
|
user_password: &Password,
|
||||||
) -> Result<BrowserSession, Self::Error> {
|
) -> Result<BrowserSession, Self::Error> {
|
||||||
@ -342,7 +342,7 @@ impl<'c> BrowserSessionRepository for PgBrowserSessionRepository<'c> {
|
|||||||
async fn authenticate_with_upstream(
|
async fn authenticate_with_upstream(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
mut user_session: BrowserSession,
|
mut user_session: BrowserSession,
|
||||||
upstream_oauth_link: &UpstreamOAuthLink,
|
upstream_oauth_link: &UpstreamOAuthLink,
|
||||||
) -> Result<BrowserSession, Self::Error> {
|
) -> Result<BrowserSession, Self::Error> {
|
||||||
|
@ -14,8 +14,9 @@
|
|||||||
|
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
|
clock::MockClock,
|
||||||
user::{BrowserSessionRepository, UserEmailRepository, UserPasswordRepository, UserRepository},
|
user::{BrowserSessionRepository, UserEmailRepository, UserPasswordRepository, UserRepository},
|
||||||
Clock, Repository,
|
Repository,
|
||||||
};
|
};
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
use rand_chacha::ChaChaRng;
|
use rand_chacha::ChaChaRng;
|
||||||
@ -30,7 +31,7 @@ async fn test_user_repo(pool: PgPool) {
|
|||||||
|
|
||||||
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
let mut rng = ChaChaRng::seed_from_u64(42);
|
let mut rng = ChaChaRng::seed_from_u64(42);
|
||||||
let clock = Clock::mock();
|
let clock = MockClock::default();
|
||||||
|
|
||||||
// Initially, the user shouldn't exist
|
// Initially, the user shouldn't exist
|
||||||
assert!(!repo.user().exists(USERNAME).await.unwrap());
|
assert!(!repo.user().exists(USERNAME).await.unwrap());
|
||||||
@ -78,7 +79,7 @@ async fn test_user_email_repo(pool: PgPool) {
|
|||||||
|
|
||||||
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
let mut rng = ChaChaRng::seed_from_u64(42);
|
let mut rng = ChaChaRng::seed_from_u64(42);
|
||||||
let clock = Clock::mock();
|
let clock = MockClock::default();
|
||||||
|
|
||||||
let user = repo
|
let user = repo
|
||||||
.user()
|
.user()
|
||||||
@ -89,7 +90,7 @@ async fn test_user_email_repo(pool: PgPool) {
|
|||||||
// The user email should not exist yet
|
// The user email should not exist yet
|
||||||
assert!(repo
|
assert!(repo
|
||||||
.user_email()
|
.user_email()
|
||||||
.find(&user, EMAIL)
|
.find(&user, &EMAIL)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.is_none());
|
.is_none());
|
||||||
@ -110,7 +111,7 @@ async fn test_user_email_repo(pool: PgPool) {
|
|||||||
|
|
||||||
assert!(repo
|
assert!(repo
|
||||||
.user_email()
|
.user_email()
|
||||||
.find(&user, EMAIL)
|
.find(&user, &EMAIL)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.is_some());
|
.is_some());
|
||||||
@ -180,7 +181,7 @@ async fn test_user_email_repo(pool: PgPool) {
|
|||||||
// Reload the user_email
|
// Reload the user_email
|
||||||
let user_email = repo
|
let user_email = repo
|
||||||
.user_email()
|
.user_email()
|
||||||
.find(&user, EMAIL)
|
.find(&user, &EMAIL)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.expect("user email was not found");
|
.expect("user email was not found");
|
||||||
@ -260,7 +261,7 @@ async fn test_user_password_repo(pool: PgPool) {
|
|||||||
|
|
||||||
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
let mut rng = ChaChaRng::seed_from_u64(42);
|
let mut rng = ChaChaRng::seed_from_u64(42);
|
||||||
let clock = Clock::mock();
|
let clock = MockClock::default();
|
||||||
|
|
||||||
let user = repo
|
let user = repo
|
||||||
.user()
|
.user()
|
||||||
@ -340,7 +341,7 @@ async fn test_user_session(pool: PgPool) {
|
|||||||
|
|
||||||
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
let mut rng = ChaChaRng::seed_from_u64(42);
|
let mut rng = ChaChaRng::seed_from_u64(42);
|
||||||
let clock = Clock::mock();
|
let clock = MockClock::default();
|
||||||
|
|
||||||
let user = repo
|
let user = repo
|
||||||
.user()
|
.user()
|
||||||
|
129
crates/storage/src/clock.rs
Normal file
129
crates/storage/src/clock.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! A [`Clock`] is a way to get the current date and time.
|
||||||
|
//!
|
||||||
|
//! This module defines two implemetation of the [`Clock`] trait:
|
||||||
|
//! [`SystemClock`] which uses the system time, and a [`MockClock`], which can
|
||||||
|
//! be used and freely manipulated in tests.
|
||||||
|
|
||||||
|
use std::sync::atomic::AtomicI64;
|
||||||
|
|
||||||
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
|
||||||
|
/// Represents a clock which can give the current date and time
|
||||||
|
pub trait Clock: Sync {
|
||||||
|
/// Get the current date and time
|
||||||
|
fn now(&self) -> DateTime<Utc>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A clock which uses the system time
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct SystemClock {
|
||||||
|
_private: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clock for SystemClock {
|
||||||
|
fn now(&self) -> DateTime<Utc> {
|
||||||
|
// This is the clock used elsewhere, it's fine to call Utc::now here
|
||||||
|
#[allow(clippy::disallowed_methods)]
|
||||||
|
Utc::now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A fake clock, which uses a fixed timestamp, and can be advanced with the
|
||||||
|
/// [`MockClock::advance`] method.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use mas_storage::clock::{Clock, MockClock};
|
||||||
|
/// use chrono::Duration;
|
||||||
|
///
|
||||||
|
/// let clock = MockClock::default();
|
||||||
|
/// let t1 = clock.now();
|
||||||
|
/// let t2 = clock.now();
|
||||||
|
/// assert_eq!(t1, t2);
|
||||||
|
///
|
||||||
|
/// clock.advance(Duration::seconds(10));
|
||||||
|
/// let t3 = clock.now();
|
||||||
|
/// assert_eq!(t2 + Duration::seconds(10), t3);
|
||||||
|
/// ```
|
||||||
|
pub struct MockClock {
|
||||||
|
timestamp: AtomicI64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MockClock {
|
||||||
|
fn default() -> Self {
|
||||||
|
let datetime = Utc.with_ymd_and_hms(2022, 1, 16, 14, 40, 0).unwrap();
|
||||||
|
Self::new(datetime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockClock {
|
||||||
|
/// Create a new clock which starts at the given datetime
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(datetime: DateTime<Utc>) -> Self {
|
||||||
|
let timestamp = AtomicI64::new(datetime.timestamp());
|
||||||
|
Self { timestamp }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move the clock forward by the given amount of time
|
||||||
|
pub fn advance(&self, duration: chrono::Duration) {
|
||||||
|
self.timestamp
|
||||||
|
.fetch_add(duration.num_seconds(), std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clock for MockClock {
|
||||||
|
fn now(&self) -> DateTime<Utc> {
|
||||||
|
let timestamp = self.timestamp.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
chrono::TimeZone::timestamp_opt(&Utc, timestamp, 0).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use chrono::Duration;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mocked_clock() {
|
||||||
|
let clock = MockClock::default();
|
||||||
|
|
||||||
|
// Time should be frozen, and give out the same timestamp on each call
|
||||||
|
let first = clock.now();
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
|
let second = clock.now();
|
||||||
|
|
||||||
|
assert_eq!(first, second);
|
||||||
|
|
||||||
|
// Clock can be advanced by a fixed duration
|
||||||
|
clock.advance(Duration::seconds(10));
|
||||||
|
let third = clock.now();
|
||||||
|
assert_eq!(first + Duration::seconds(10), third);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_real_clock() {
|
||||||
|
let clock = SystemClock::default();
|
||||||
|
|
||||||
|
// Time should not be frozen
|
||||||
|
let first = clock.now();
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
|
let second = clock.now();
|
||||||
|
|
||||||
|
assert_ne!(first, second);
|
||||||
|
assert!(first < second);
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,7 @@ pub trait CompatAccessTokenRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_session: &CompatSession,
|
compat_session: &CompatSession,
|
||||||
token: String,
|
token: String,
|
||||||
expires_after: Option<Duration>,
|
expires_after: Option<Duration>,
|
||||||
@ -46,7 +46,7 @@ pub trait CompatAccessTokenRepository: Send + Sync {
|
|||||||
/// Set the expiration time of the compat access token to now
|
/// Set the expiration time of the compat access token to now
|
||||||
async fn expire(
|
async fn expire(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_access_token: CompatAccessToken,
|
compat_access_token: CompatAccessToken,
|
||||||
) -> Result<CompatAccessToken, Self::Error>;
|
) -> Result<CompatAccessToken, Self::Error>;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ pub trait CompatRefreshTokenRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_session: &CompatSession,
|
compat_session: &CompatSession,
|
||||||
compat_access_token: &CompatAccessToken,
|
compat_access_token: &CompatAccessToken,
|
||||||
token: String,
|
token: String,
|
||||||
@ -45,7 +45,7 @@ pub trait CompatRefreshTokenRepository: Send + Sync {
|
|||||||
/// Consume a compat refresh token
|
/// Consume a compat refresh token
|
||||||
async fn consume(
|
async fn consume(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_refresh_token: CompatRefreshToken,
|
compat_refresh_token: CompatRefreshToken,
|
||||||
) -> Result<CompatRefreshToken, Self::Error>;
|
) -> Result<CompatRefreshToken, Self::Error>;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ pub trait CompatSessionRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
device: Device,
|
device: Device,
|
||||||
) -> Result<CompatSession, Self::Error>;
|
) -> Result<CompatSession, Self::Error>;
|
||||||
@ -38,7 +38,7 @@ pub trait CompatSessionRepository: Send + Sync {
|
|||||||
/// End a compat session
|
/// End a compat session
|
||||||
async fn finish(
|
async fn finish(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_session: CompatSession,
|
compat_session: CompatSession,
|
||||||
) -> Result<CompatSession, Self::Error>;
|
) -> Result<CompatSession, Self::Error>;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ pub trait CompatSsoLoginRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
login_token: String,
|
login_token: String,
|
||||||
redirect_uri: Url,
|
redirect_uri: Url,
|
||||||
) -> Result<CompatSsoLogin, Self::Error>;
|
) -> Result<CompatSsoLogin, Self::Error>;
|
||||||
@ -45,7 +45,7 @@ pub trait CompatSsoLoginRepository: Send + Sync {
|
|||||||
/// Fulfill a compat SSO login by providing a compat session
|
/// Fulfill a compat SSO login by providing a compat session
|
||||||
async fn fulfill(
|
async fn fulfill(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_sso_login: CompatSsoLogin,
|
compat_sso_login: CompatSsoLogin,
|
||||||
compat_session: &CompatSession,
|
compat_session: &CompatSession,
|
||||||
) -> Result<CompatSsoLogin, Self::Error>;
|
) -> Result<CompatSsoLogin, Self::Error>;
|
||||||
@ -53,7 +53,7 @@ pub trait CompatSsoLoginRepository: Send + Sync {
|
|||||||
/// Mark a compat SSO login as exchanged
|
/// Mark a compat SSO login as exchanged
|
||||||
async fn exchange(
|
async fn exchange(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
compat_sso_login: CompatSsoLogin,
|
compat_sso_login: CompatSsoLogin,
|
||||||
) -> Result<CompatSsoLogin, Self::Error>;
|
) -> Result<CompatSsoLogin, Self::Error>;
|
||||||
|
|
||||||
|
@ -28,92 +28,7 @@
|
|||||||
clippy::module_name_repetitions
|
clippy::module_name_repetitions
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
pub mod clock;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct Clock {
|
|
||||||
_private: (),
|
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
mock: Option<std::sync::Arc<std::sync::atomic::AtomicI64>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clock {
|
|
||||||
#[must_use]
|
|
||||||
pub fn now(&self) -> DateTime<Utc> {
|
|
||||||
// #[cfg(test)]
|
|
||||||
if let Some(timestamp) = &self.mock {
|
|
||||||
let timestamp = timestamp.load(std::sync::atomic::Ordering::Relaxed);
|
|
||||||
return chrono::TimeZone::timestamp_opt(&Utc, timestamp, 0).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the clock used elsewhere, it's fine to call Utc::now here
|
|
||||||
#[allow(clippy::disallowed_methods)]
|
|
||||||
Utc::now()
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn mock() -> Self {
|
|
||||||
use std::sync::{atomic::AtomicI64, Arc};
|
|
||||||
|
|
||||||
use chrono::TimeZone;
|
|
||||||
|
|
||||||
let datetime = Utc.with_ymd_and_hms(2022, 1, 16, 14, 40, 0).unwrap();
|
|
||||||
let timestamp = datetime.timestamp();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
mock: Some(Arc::new(AtomicI64::new(timestamp))),
|
|
||||||
_private: (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
pub fn advance(&self, duration: chrono::Duration) {
|
|
||||||
let timestamp = self
|
|
||||||
.mock
|
|
||||||
.as_ref()
|
|
||||||
.expect("Clock::advance should only be called on mocked clocks in tests");
|
|
||||||
timestamp.fetch_add(duration.num_seconds(), std::sync::atomic::Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use chrono::Duration;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mocked_clock() {
|
|
||||||
let clock = Clock::mock();
|
|
||||||
|
|
||||||
// Time should be frozen, and give out the same timestamp on each call
|
|
||||||
let first = clock.now();
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
|
||||||
let second = clock.now();
|
|
||||||
|
|
||||||
assert_eq!(first, second);
|
|
||||||
|
|
||||||
// Clock can be advanced by a fixed duration
|
|
||||||
clock.advance(Duration::seconds(10));
|
|
||||||
let third = clock.now();
|
|
||||||
assert_eq!(first + Duration::seconds(10), third);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_real_clock() {
|
|
||||||
let clock = Clock::default();
|
|
||||||
|
|
||||||
// Time should not be frozen
|
|
||||||
let first = clock.now();
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
|
||||||
let second = clock.now();
|
|
||||||
|
|
||||||
assert_ne!(first, second);
|
|
||||||
assert!(first < second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod compat;
|
pub mod compat;
|
||||||
pub mod oauth2;
|
pub mod oauth2;
|
||||||
@ -123,6 +38,7 @@ pub mod upstream_oauth2;
|
|||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
|
clock::{Clock, SystemClock},
|
||||||
pagination::{Page, Pagination},
|
pagination::{Page, Pagination},
|
||||||
repository::Repository,
|
repository::Repository,
|
||||||
};
|
};
|
||||||
|
@ -37,7 +37,7 @@ pub trait OAuth2AccessTokenRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
access_token: String,
|
access_token: String,
|
||||||
expires_after: Duration,
|
expires_after: Duration,
|
||||||
@ -46,10 +46,10 @@ pub trait OAuth2AccessTokenRepository: Send + Sync {
|
|||||||
/// Revoke an access token
|
/// Revoke an access token
|
||||||
async fn revoke(
|
async fn revoke(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
access_token: AccessToken,
|
access_token: AccessToken,
|
||||||
) -> Result<AccessToken, Self::Error>;
|
) -> Result<AccessToken, Self::Error>;
|
||||||
|
|
||||||
/// Cleanup expired access tokens
|
/// Cleanup expired access tokens
|
||||||
async fn cleanup_expired(&mut self, clock: &Clock) -> Result<usize, Self::Error>;
|
async fn cleanup_expired(&mut self, clock: &dyn Clock) -> Result<usize, Self::Error>;
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ pub trait OAuth2AuthorizationGrantRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
redirect_uri: Url,
|
redirect_uri: Url,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
@ -51,14 +51,14 @@ pub trait OAuth2AuthorizationGrantRepository: Send + Sync {
|
|||||||
|
|
||||||
async fn fulfill(
|
async fn fulfill(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
authorization_grant: AuthorizationGrant,
|
authorization_grant: AuthorizationGrant,
|
||||||
) -> Result<AuthorizationGrant, Self::Error>;
|
) -> Result<AuthorizationGrant, Self::Error>;
|
||||||
|
|
||||||
async fn exchange(
|
async fn exchange(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
authorization_grant: AuthorizationGrant,
|
authorization_grant: AuthorizationGrant,
|
||||||
) -> Result<AuthorizationGrant, Self::Error>;
|
) -> Result<AuthorizationGrant, Self::Error>;
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ pub trait OAuth2ClientRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
redirect_uris: Vec<Url>,
|
redirect_uris: Vec<Url>,
|
||||||
encrypted_client_secret: Option<String>,
|
encrypted_client_secret: Option<String>,
|
||||||
grant_types: Vec<GrantType>,
|
grant_types: Vec<GrantType>,
|
||||||
@ -68,7 +68,7 @@ pub trait OAuth2ClientRepository: Send + Sync {
|
|||||||
async fn add_from_config(
|
async fn add_from_config(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut rng: impl Rng + Send,
|
mut rng: impl Rng + Send,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
client_id: Ulid,
|
client_id: Ulid,
|
||||||
client_auth_method: OAuthClientAuthenticationMethod,
|
client_auth_method: OAuthClientAuthenticationMethod,
|
||||||
encrypted_client_secret: Option<String>,
|
encrypted_client_secret: Option<String>,
|
||||||
@ -86,7 +86,7 @@ pub trait OAuth2ClientRepository: Send + Sync {
|
|||||||
async fn give_consent_for_user(
|
async fn give_consent_for_user(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
user: &User,
|
user: &User,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
|
@ -36,7 +36,7 @@ pub trait OAuth2RefreshTokenRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
access_token: &AccessToken,
|
access_token: &AccessToken,
|
||||||
refresh_token: String,
|
refresh_token: String,
|
||||||
@ -45,7 +45,7 @@ pub trait OAuth2RefreshTokenRepository: Send + Sync {
|
|||||||
/// Consume a refresh token
|
/// Consume a refresh token
|
||||||
async fn consume(
|
async fn consume(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
refresh_token: RefreshToken,
|
refresh_token: RefreshToken,
|
||||||
) -> Result<RefreshToken, Self::Error>;
|
) -> Result<RefreshToken, Self::Error>;
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,12 @@ pub trait OAuth2SessionRepository: Send + Sync {
|
|||||||
async fn create_from_grant(
|
async fn create_from_grant(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
grant: &AuthorizationGrant,
|
grant: &AuthorizationGrant,
|
||||||
user_session: &BrowserSession,
|
user_session: &BrowserSession,
|
||||||
) -> Result<Session, Self::Error>;
|
) -> Result<Session, Self::Error>;
|
||||||
|
|
||||||
async fn finish(&mut self, clock: &Clock, session: Session) -> Result<Session, Self::Error>;
|
async fn finish(&mut self, clock: &dyn Clock, session: Session) -> Result<Session, Self::Error>;
|
||||||
|
|
||||||
async fn list_paginated(
|
async fn list_paginated(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -37,7 +37,7 @@ pub trait UpstreamOAuthLinkRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
upstream_oauth_provider: &UpstreamOAuthProvider,
|
upstream_oauth_provider: &UpstreamOAuthProvider,
|
||||||
subject: String,
|
subject: String,
|
||||||
) -> Result<UpstreamOAuthLink, Self::Error>;
|
) -> Result<UpstreamOAuthLink, Self::Error>;
|
||||||
|
@ -33,7 +33,7 @@ pub trait UpstreamOAuthProviderRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
issuer: String,
|
issuer: String,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
token_endpoint_auth_method: OAuthClientAuthenticationMethod,
|
token_endpoint_auth_method: OAuthClientAuthenticationMethod,
|
||||||
|
@ -33,7 +33,7 @@ pub trait UpstreamOAuthSessionRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
upstream_oauth_provider: &UpstreamOAuthProvider,
|
upstream_oauth_provider: &UpstreamOAuthProvider,
|
||||||
state: String,
|
state: String,
|
||||||
code_challenge_verifier: Option<String>,
|
code_challenge_verifier: Option<String>,
|
||||||
@ -43,7 +43,7 @@ pub trait UpstreamOAuthSessionRepository: Send + Sync {
|
|||||||
/// Mark a session as completed and associate the given link
|
/// Mark a session as completed and associate the given link
|
||||||
async fn complete_with_link(
|
async fn complete_with_link(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
upstream_oauth_authorization_session: UpstreamOAuthAuthorizationSession,
|
upstream_oauth_authorization_session: UpstreamOAuthAuthorizationSession,
|
||||||
upstream_oauth_link: &UpstreamOAuthLink,
|
upstream_oauth_link: &UpstreamOAuthLink,
|
||||||
id_token: Option<String>,
|
id_token: Option<String>,
|
||||||
@ -52,7 +52,7 @@ pub trait UpstreamOAuthSessionRepository: Send + Sync {
|
|||||||
/// Mark a session as consumed
|
/// Mark a session as consumed
|
||||||
async fn consume(
|
async fn consume(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
upstream_oauth_authorization_session: UpstreamOAuthAuthorizationSession,
|
upstream_oauth_authorization_session: UpstreamOAuthAuthorizationSession,
|
||||||
) -> Result<UpstreamOAuthAuthorizationSession, Self::Error>;
|
) -> Result<UpstreamOAuthAuthorizationSession, Self::Error>;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ pub trait UserEmailRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
email: String,
|
email: String,
|
||||||
) -> Result<UserEmail, Self::Error>;
|
) -> Result<UserEmail, Self::Error>;
|
||||||
@ -46,7 +46,7 @@ pub trait UserEmailRepository: Send + Sync {
|
|||||||
|
|
||||||
async fn mark_as_verified(
|
async fn mark_as_verified(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user_email: UserEmail,
|
user_email: UserEmail,
|
||||||
) -> Result<UserEmail, Self::Error>;
|
) -> Result<UserEmail, Self::Error>;
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ pub trait UserEmailRepository: Send + Sync {
|
|||||||
async fn add_verification_code(
|
async fn add_verification_code(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user_email: &UserEmail,
|
user_email: &UserEmail,
|
||||||
max_age: chrono::Duration,
|
max_age: chrono::Duration,
|
||||||
code: String,
|
code: String,
|
||||||
@ -63,14 +63,14 @@ pub trait UserEmailRepository: Send + Sync {
|
|||||||
|
|
||||||
async fn find_verification_code(
|
async fn find_verification_code(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user_email: &UserEmail,
|
user_email: &UserEmail,
|
||||||
code: &str,
|
code: &str,
|
||||||
) -> Result<Option<UserEmailVerification>, Self::Error>;
|
) -> Result<Option<UserEmailVerification>, Self::Error>;
|
||||||
|
|
||||||
async fn consume_verification_code(
|
async fn consume_verification_code(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
verification: UserEmailVerification,
|
verification: UserEmailVerification,
|
||||||
) -> Result<UserEmailVerification, Self::Error>;
|
) -> Result<UserEmailVerification, Self::Error>;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ pub trait UserRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
username: String,
|
username: String,
|
||||||
) -> Result<User, Self::Error>;
|
) -> Result<User, Self::Error>;
|
||||||
async fn exists(&mut self, username: &str) -> Result<bool, Self::Error>;
|
async fn exists(&mut self, username: &str) -> Result<bool, Self::Error>;
|
||||||
|
@ -26,7 +26,7 @@ pub trait UserPasswordRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
version: u16,
|
version: u16,
|
||||||
hashed_password: String,
|
hashed_password: String,
|
||||||
|
@ -27,12 +27,12 @@ pub trait BrowserSessionRepository: Send + Sync {
|
|||||||
async fn add(
|
async fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user: &User,
|
user: &User,
|
||||||
) -> Result<BrowserSession, Self::Error>;
|
) -> Result<BrowserSession, Self::Error>;
|
||||||
async fn finish(
|
async fn finish(
|
||||||
&mut self,
|
&mut self,
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user_session: BrowserSession,
|
user_session: BrowserSession,
|
||||||
) -> Result<BrowserSession, Self::Error>;
|
) -> Result<BrowserSession, Self::Error>;
|
||||||
async fn list_active_paginated(
|
async fn list_active_paginated(
|
||||||
@ -45,7 +45,7 @@ pub trait BrowserSessionRepository: Send + Sync {
|
|||||||
async fn authenticate_with_password(
|
async fn authenticate_with_password(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user_session: BrowserSession,
|
user_session: BrowserSession,
|
||||||
user_password: &Password,
|
user_password: &Password,
|
||||||
) -> Result<BrowserSession, Self::Error>;
|
) -> Result<BrowserSession, Self::Error>;
|
||||||
@ -53,7 +53,7 @@ pub trait BrowserSessionRepository: Send + Sync {
|
|||||||
async fn authenticate_with_upstream(
|
async fn authenticate_with_upstream(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut (dyn RngCore + Send),
|
rng: &mut (dyn RngCore + Send),
|
||||||
clock: &Clock,
|
clock: &dyn Clock,
|
||||||
user_session: BrowserSession,
|
user_session: BrowserSession,
|
||||||
upstream_oauth_link: &UpstreamOAuthLink,
|
upstream_oauth_link: &UpstreamOAuthLink,
|
||||||
) -> Result<BrowserSession, Self::Error>;
|
) -> Result<BrowserSession, Self::Error>;
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
//! Database-related tasks
|
//! Database-related tasks
|
||||||
|
|
||||||
use mas_storage::{oauth2::OAuth2AccessTokenRepository, Clock, Repository};
|
use mas_storage::{oauth2::OAuth2AccessTokenRepository, Repository, SystemClock};
|
||||||
use mas_storage_pg::PgRepository;
|
use mas_storage_pg::PgRepository;
|
||||||
use sqlx::{Pool, Postgres};
|
use sqlx::{Pool, Postgres};
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
@ -22,7 +22,7 @@ use tracing::{debug, error, info};
|
|||||||
use super::Task;
|
use super::Task;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct CleanupExpired(Pool<Postgres>, Clock);
|
struct CleanupExpired(Pool<Postgres>, SystemClock);
|
||||||
|
|
||||||
impl std::fmt::Debug for CleanupExpired {
|
impl std::fmt::Debug for CleanupExpired {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
@ -57,5 +57,5 @@ impl Task for CleanupExpired {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn cleanup_expired(pool: &Pool<Postgres>) -> impl Task + Clone {
|
pub fn cleanup_expired(pool: &Pool<Postgres>) -> impl Task + Clone {
|
||||||
// XXX: the clock should come from somewhere else
|
// XXX: the clock should come from somewhere else
|
||||||
CleanupExpired(pool.clone(), Clock::default())
|
CleanupExpired(pool.clone(), SystemClock::default())
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user