1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-29 22:01:14 +03:00

storage: ensure the repository trait can be boxed

and define some wrappers to map the errors
This commit is contained in:
Quentin Gliech
2023-01-19 19:10:35 +01:00
parent 876bc9fcb3
commit f4c64c2171
23 changed files with 801 additions and 142 deletions

View File

@ -18,7 +18,7 @@ use mas_data_model::{CompatAccessToken, CompatSession};
use rand_core::RngCore;
use ulid::Ulid;
use crate::Clock;
use crate::{repository_impl, Clock};
#[async_trait]
pub trait CompatAccessTokenRepository: Send + Sync {
@ -50,3 +50,27 @@ pub trait CompatAccessTokenRepository: Send + Sync {
compat_access_token: CompatAccessToken,
) -> Result<CompatAccessToken, Self::Error>;
}
repository_impl!(CompatAccessTokenRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<CompatAccessToken>, Self::Error>;
async fn find_by_token(
&mut self,
access_token: &str,
) -> Result<Option<CompatAccessToken>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
compat_session: &CompatSession,
token: String,
expires_after: Option<Duration>,
) -> Result<CompatAccessToken, Self::Error>;
async fn expire(
&mut self,
clock: &dyn Clock,
compat_access_token: CompatAccessToken,
) -> Result<CompatAccessToken, Self::Error>;
);

View File

@ -17,7 +17,7 @@ use mas_data_model::{CompatAccessToken, CompatRefreshToken, CompatSession};
use rand_core::RngCore;
use ulid::Ulid;
use crate::Clock;
use crate::{repository_impl, Clock};
#[async_trait]
pub trait CompatRefreshTokenRepository: Send + Sync {
@ -49,3 +49,27 @@ pub trait CompatRefreshTokenRepository: Send + Sync {
compat_refresh_token: CompatRefreshToken,
) -> Result<CompatRefreshToken, Self::Error>;
}
repository_impl!(CompatRefreshTokenRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<CompatRefreshToken>, Self::Error>;
async fn find_by_token(
&mut self,
refresh_token: &str,
) -> Result<Option<CompatRefreshToken>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
compat_session: &CompatSession,
compat_access_token: &CompatAccessToken,
token: String,
) -> Result<CompatRefreshToken, Self::Error>;
async fn consume(
&mut self,
clock: &dyn Clock,
compat_refresh_token: CompatRefreshToken,
) -> Result<CompatRefreshToken, Self::Error>;
);

View File

@ -17,7 +17,7 @@ use mas_data_model::{CompatSession, Device, User};
use rand_core::RngCore;
use ulid::Ulid;
use crate::Clock;
use crate::{repository_impl, Clock};
#[async_trait]
pub trait CompatSessionRepository: Send + Sync {
@ -42,3 +42,21 @@ pub trait CompatSessionRepository: Send + Sync {
compat_session: CompatSession,
) -> Result<CompatSession, Self::Error>;
}
repository_impl!(CompatSessionRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<CompatSession>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
user: &User,
device: Device,
) -> Result<CompatSession, Self::Error>;
async fn finish(
&mut self,
clock: &dyn Clock,
compat_session: CompatSession,
) -> Result<CompatSession, Self::Error>;
);

View File

@ -18,7 +18,7 @@ use rand_core::RngCore;
use ulid::Ulid;
use url::Url;
use crate::{pagination::Page, Clock, Pagination};
use crate::{pagination::Page, repository_impl, Clock, Pagination};
#[async_trait]
pub trait CompatSsoLoginRepository: Send + Sync {
@ -64,3 +64,39 @@ pub trait CompatSsoLoginRepository: Send + Sync {
pagination: Pagination,
) -> Result<Page<CompatSsoLogin>, Self::Error>;
}
repository_impl!(CompatSsoLoginRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<CompatSsoLogin>, Self::Error>;
async fn find_by_token(
&mut self,
login_token: &str,
) -> Result<Option<CompatSsoLogin>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
login_token: String,
redirect_uri: Url,
) -> Result<CompatSsoLogin, Self::Error>;
async fn fulfill(
&mut self,
clock: &dyn Clock,
compat_sso_login: CompatSsoLogin,
compat_session: &CompatSession,
) -> Result<CompatSsoLogin, Self::Error>;
async fn exchange(
&mut self,
clock: &dyn Clock,
compat_sso_login: CompatSsoLogin,
) -> Result<CompatSsoLogin, Self::Error>;
async fn list_paginated(
&mut self,
user: &User,
pagination: Pagination,
) -> Result<Page<CompatSsoLogin>, Self::Error>;
);

View File

@ -45,5 +45,59 @@ pub use self::{
repository::Repository,
};
pub struct MapErr<Repository, Mapper> {
inner: Repository,
mapper: Mapper,
}
impl<Repository, Mapper> MapErr<Repository, Mapper> {
fn new(inner: Repository, mapper: Mapper) -> Self {
Self { inner, mapper }
}
}
#[macro_export]
macro_rules! repository_impl {
($repo_trait:ident:
$(
async fn $method:ident (
&mut self
$(, $arg:ident: $arg_ty:ty )*
$(,)?
) -> Result<$ret_ty:ty, Self::Error>;
)*
) => {
#[::async_trait::async_trait]
impl<R: ?Sized> $repo_trait for ::std::boxed::Box<R>
where
R: $repo_trait,
{
type Error = <R as $repo_trait>::Error;
$(
async fn $method (&mut self $(, $arg: $arg_ty)*) -> Result<$ret_ty, Self::Error> {
(**self).$method ( $($arg),* ).await
}
)*
}
#[::async_trait::async_trait]
impl<R, F, E> $repo_trait for $crate::MapErr<R, F>
where
R: $repo_trait,
F: FnMut(<R as $repo_trait>::Error) -> E + ::std::marker::Send + ::std::marker::Sync,
E: ::std::error::Error + ::std::marker::Send + ::std::marker::Sync,
{
type Error = E;
$(
async fn $method (&mut self $(, $arg: $arg_ty)*) -> Result<$ret_ty, Self::Error> {
self.inner.$method ( $($arg),* ).await.map_err(&mut self.mapper)
}
)*
}
};
}
pub type BoxClock = Box<dyn Clock + Send>;
pub type BoxRng = Box<dyn CryptoRngCore + Send>;

View File

@ -18,7 +18,7 @@ use mas_data_model::{AccessToken, Session};
use rand_core::RngCore;
use ulid::Ulid;
use crate::Clock;
use crate::{repository_impl, Clock};
#[async_trait]
pub trait OAuth2AccessTokenRepository: Send + Sync {
@ -53,3 +53,29 @@ pub trait OAuth2AccessTokenRepository: Send + Sync {
/// Cleanup expired access tokens
async fn cleanup_expired(&mut self, clock: &dyn Clock) -> Result<usize, Self::Error>;
}
repository_impl!(OAuth2AccessTokenRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<AccessToken>, Self::Error>;
async fn find_by_token(
&mut self,
access_token: &str,
) -> Result<Option<AccessToken>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
session: &Session,
access_token: String,
expires_after: Duration,
) -> Result<AccessToken, Self::Error>;
async fn revoke(
&mut self,
clock: &dyn Clock,
access_token: AccessToken,
) -> Result<AccessToken, Self::Error>;
async fn cleanup_expired(&mut self, clock: &dyn Clock) -> Result<usize, Self::Error>;
);

View File

@ -21,7 +21,7 @@ use rand_core::RngCore;
use ulid::Ulid;
use url::Url;
use crate::Clock;
use crate::{repository_impl, Clock};
#[async_trait]
pub trait OAuth2AuthorizationGrantRepository: Send + Sync {
@ -67,3 +67,44 @@ pub trait OAuth2AuthorizationGrantRepository: Send + Sync {
authorization_grant: AuthorizationGrant,
) -> Result<AuthorizationGrant, Self::Error>;
}
repository_impl!(OAuth2AuthorizationGrantRepository:
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
client: &Client,
redirect_uri: Url,
scope: Scope,
code: Option<AuthorizationCode>,
state: Option<String>,
nonce: Option<String>,
max_age: Option<NonZeroU32>,
response_mode: ResponseMode,
response_type_id_token: bool,
requires_consent: bool,
) -> Result<AuthorizationGrant, Self::Error>;
async fn lookup(&mut self, id: Ulid) -> Result<Option<AuthorizationGrant>, Self::Error>;
async fn find_by_code(&mut self, code: &str)
-> Result<Option<AuthorizationGrant>, Self::Error>;
async fn fulfill(
&mut self,
clock: &dyn Clock,
session: &Session,
authorization_grant: AuthorizationGrant,
) -> Result<AuthorizationGrant, Self::Error>;
async fn exchange(
&mut self,
clock: &dyn Clock,
authorization_grant: AuthorizationGrant,
) -> Result<AuthorizationGrant, Self::Error>;
async fn give_consent(
&mut self,
authorization_grant: AuthorizationGrant,
) -> Result<AuthorizationGrant, Self::Error>;
);

View File

@ -23,7 +23,7 @@ use rand_core::RngCore;
use ulid::Ulid;
use url::Url;
use crate::Clock;
use crate::{repository_impl, Clock};
#[async_trait]
pub trait OAuth2ClientRepository: Send + Sync {
@ -92,3 +92,61 @@ pub trait OAuth2ClientRepository: Send + Sync {
scope: &Scope,
) -> Result<(), Self::Error>;
}
repository_impl!(OAuth2ClientRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<Client>, Self::Error>;
async fn load_batch(
&mut self,
ids: BTreeSet<Ulid>,
) -> Result<BTreeMap<Ulid, Client>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
redirect_uris: Vec<Url>,
encrypted_client_secret: Option<String>,
grant_types: Vec<GrantType>,
contacts: Vec<String>,
client_name: Option<String>,
logo_uri: Option<Url>,
client_uri: Option<Url>,
policy_uri: Option<Url>,
tos_uri: Option<Url>,
jwks_uri: Option<Url>,
jwks: Option<PublicJsonWebKeySet>,
id_token_signed_response_alg: Option<JsonWebSignatureAlg>,
userinfo_signed_response_alg: Option<JsonWebSignatureAlg>,
token_endpoint_auth_method: Option<OAuthClientAuthenticationMethod>,
token_endpoint_auth_signing_alg: Option<JsonWebSignatureAlg>,
initiate_login_uri: Option<Url>,
) -> Result<Client, Self::Error>;
async fn add_from_config(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
client_id: Ulid,
client_auth_method: OAuthClientAuthenticationMethod,
encrypted_client_secret: Option<String>,
jwks: Option<PublicJsonWebKeySet>,
jwks_uri: Option<Url>,
redirect_uris: Vec<Url>,
) -> Result<Client, Self::Error>;
async fn get_consent_for_user(
&mut self,
client: &Client,
user: &User,
) -> Result<Scope, Self::Error>;
async fn give_consent_for_user(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
client: &Client,
user: &User,
scope: &Scope,
) -> Result<(), Self::Error>;
);

View File

@ -17,7 +17,7 @@ use mas_data_model::{AccessToken, RefreshToken, Session};
use rand_core::RngCore;
use ulid::Ulid;
use crate::Clock;
use crate::{repository_impl, Clock};
#[async_trait]
pub trait OAuth2RefreshTokenRepository: Send + Sync {
@ -49,3 +49,27 @@ pub trait OAuth2RefreshTokenRepository: Send + Sync {
refresh_token: RefreshToken,
) -> Result<RefreshToken, Self::Error>;
}
repository_impl!(OAuth2RefreshTokenRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<RefreshToken>, Self::Error>;
async fn find_by_token(
&mut self,
refresh_token: &str,
) -> Result<Option<RefreshToken>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
session: &Session,
access_token: &AccessToken,
refresh_token: String,
) -> Result<RefreshToken, Self::Error>;
async fn consume(
&mut self,
clock: &dyn Clock,
refresh_token: RefreshToken,
) -> Result<RefreshToken, Self::Error>;
);

View File

@ -17,7 +17,7 @@ use mas_data_model::{AuthorizationGrant, BrowserSession, Session, User};
use rand_core::RngCore;
use ulid::Ulid;
use crate::{pagination::Page, Clock, Pagination};
use crate::{pagination::Page, repository_impl, Clock, Pagination};
#[async_trait]
pub trait OAuth2SessionRepository: Send + Sync {
@ -42,3 +42,24 @@ pub trait OAuth2SessionRepository: Send + Sync {
pagination: Pagination,
) -> Result<Page<Session>, Self::Error>;
}
repository_impl!(OAuth2SessionRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<Session>, Self::Error>;
async fn create_from_grant(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
grant: &AuthorizationGrant,
user_session: &BrowserSession,
) -> Result<Session, Self::Error>;
async fn finish(&mut self, clock: &dyn Clock, session: Session)
-> Result<Session, Self::Error>;
async fn list_paginated(
&mut self,
user: &User,
pagination: Pagination,
) -> Result<Page<Session>, Self::Error>;
);

View File

@ -26,92 +26,192 @@ use crate::{
UpstreamOAuthSessionRepository,
},
user::{BrowserSessionRepository, UserEmailRepository, UserPasswordRepository, UserRepository},
MapErr,
};
pub trait Repository: Send {
type Error: std::error::Error + Send + Sync + 'static;
type UpstreamOAuthLinkRepository<'c>: UpstreamOAuthLinkRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn upstream_oauth_link<'c>(
&'c mut self,
) -> Box<dyn UpstreamOAuthLinkRepository<Error = Self::Error> + 'c>;
type UpstreamOAuthProviderRepository<'c>: UpstreamOAuthProviderRepository<Error = Self::Error>
+ 'c
where
Self: 'c;
fn upstream_oauth_provider<'c>(
&'c mut self,
) -> Box<dyn UpstreamOAuthProviderRepository<Error = Self::Error> + 'c>;
type UpstreamOAuthSessionRepository<'c>: UpstreamOAuthSessionRepository<Error = Self::Error>
+ 'c
where
Self: 'c;
fn upstream_oauth_session<'c>(
&'c mut self,
) -> Box<dyn UpstreamOAuthSessionRepository<Error = Self::Error> + 'c>;
type UserRepository<'c>: UserRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn user<'c>(&'c mut self) -> Box<dyn UserRepository<Error = Self::Error> + 'c>;
type UserEmailRepository<'c>: UserEmailRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn user_email<'c>(&'c mut self) -> Box<dyn UserEmailRepository<Error = Self::Error> + 'c>;
type UserPasswordRepository<'c>: UserPasswordRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn user_password<'c>(&'c mut self)
-> Box<dyn UserPasswordRepository<Error = Self::Error> + 'c>;
type BrowserSessionRepository<'c>: BrowserSessionRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn browser_session<'c>(
&'c mut self,
) -> Box<dyn BrowserSessionRepository<Error = Self::Error> + 'c>;
type OAuth2ClientRepository<'c>: OAuth2ClientRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn oauth2_client<'c>(&'c mut self)
-> Box<dyn OAuth2ClientRepository<Error = Self::Error> + 'c>;
type OAuth2AuthorizationGrantRepository<'c>: OAuth2AuthorizationGrantRepository<Error = Self::Error>
+ 'c
where
Self: 'c;
fn oauth2_authorization_grant<'c>(
&'c mut self,
) -> Box<dyn OAuth2AuthorizationGrantRepository<Error = Self::Error> + 'c>;
type OAuth2SessionRepository<'c>: OAuth2SessionRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn oauth2_session<'c>(
&'c mut self,
) -> Box<dyn OAuth2SessionRepository<Error = Self::Error> + 'c>;
type OAuth2AccessTokenRepository<'c>: OAuth2AccessTokenRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn oauth2_access_token<'c>(
&'c mut self,
) -> Box<dyn OAuth2AccessTokenRepository<Error = Self::Error> + 'c>;
type OAuth2RefreshTokenRepository<'c>: OAuth2RefreshTokenRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn oauth2_refresh_token<'c>(
&'c mut self,
) -> Box<dyn OAuth2RefreshTokenRepository<Error = Self::Error> + 'c>;
type CompatSessionRepository<'c>: CompatSessionRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn compat_session<'c>(
&'c mut self,
) -> Box<dyn CompatSessionRepository<Error = Self::Error> + 'c>;
type CompatSsoLoginRepository<'c>: CompatSsoLoginRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn compat_sso_login<'c>(
&'c mut self,
) -> Box<dyn CompatSsoLoginRepository<Error = Self::Error> + 'c>;
type CompatAccessTokenRepository<'c>: CompatAccessTokenRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn compat_access_token<'c>(
&'c mut self,
) -> Box<dyn CompatAccessTokenRepository<Error = Self::Error> + 'c>;
type CompatRefreshTokenRepository<'c>: CompatRefreshTokenRepository<Error = Self::Error> + 'c
where
Self: 'c;
fn upstream_oauth_link(&mut self) -> Self::UpstreamOAuthLinkRepository<'_>;
fn upstream_oauth_provider(&mut self) -> Self::UpstreamOAuthProviderRepository<'_>;
fn upstream_oauth_session(&mut self) -> Self::UpstreamOAuthSessionRepository<'_>;
fn user(&mut self) -> Self::UserRepository<'_>;
fn user_email(&mut self) -> Self::UserEmailRepository<'_>;
fn user_password(&mut self) -> Self::UserPasswordRepository<'_>;
fn browser_session(&mut self) -> Self::BrowserSessionRepository<'_>;
fn oauth2_client(&mut self) -> Self::OAuth2ClientRepository<'_>;
fn oauth2_authorization_grant(&mut self) -> Self::OAuth2AuthorizationGrantRepository<'_>;
fn oauth2_session(&mut self) -> Self::OAuth2SessionRepository<'_>;
fn oauth2_access_token(&mut self) -> Self::OAuth2AccessTokenRepository<'_>;
fn oauth2_refresh_token(&mut self) -> Self::OAuth2RefreshTokenRepository<'_>;
fn compat_session(&mut self) -> Self::CompatSessionRepository<'_>;
fn compat_sso_login(&mut self) -> Self::CompatSsoLoginRepository<'_>;
fn compat_access_token(&mut self) -> Self::CompatAccessTokenRepository<'_>;
fn compat_refresh_token(&mut self) -> Self::CompatRefreshTokenRepository<'_>;
fn compat_refresh_token<'c>(
&'c mut self,
) -> Box<dyn CompatRefreshTokenRepository<Error = Self::Error> + 'c>;
}
impl<R, F, E> Repository for crate::MapErr<R, F>
where
R: Repository,
F: FnMut(R::Error) -> E + Send + Sync,
E: std::error::Error + Send + Sync + 'static,
{
type Error = E;
fn upstream_oauth_link<'c>(
&'c mut self,
) -> Box<dyn UpstreamOAuthLinkRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(
self.inner.upstream_oauth_link(),
&mut self.mapper,
))
}
fn upstream_oauth_provider<'c>(
&'c mut self,
) -> Box<dyn UpstreamOAuthProviderRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(
self.inner.upstream_oauth_provider(),
&mut self.mapper,
))
}
fn upstream_oauth_session<'c>(
&'c mut self,
) -> Box<dyn UpstreamOAuthSessionRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(
self.inner.upstream_oauth_session(),
&mut self.mapper,
))
}
fn user<'c>(&'c mut self) -> Box<dyn UserRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(self.inner.user(), &mut self.mapper))
}
fn user_email<'c>(&'c mut self) -> Box<dyn UserEmailRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(self.inner.user_email(), &mut self.mapper))
}
fn user_password<'c>(
&'c mut self,
) -> Box<dyn UserPasswordRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(self.inner.user_password(), &mut self.mapper))
}
fn browser_session<'c>(
&'c mut self,
) -> Box<dyn BrowserSessionRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(self.inner.browser_session(), &mut self.mapper))
}
fn oauth2_client<'c>(
&'c mut self,
) -> Box<dyn OAuth2ClientRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(self.inner.oauth2_client(), &mut self.mapper))
}
fn oauth2_authorization_grant<'c>(
&'c mut self,
) -> Box<dyn OAuth2AuthorizationGrantRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(
self.inner.oauth2_authorization_grant(),
&mut self.mapper,
))
}
fn oauth2_session<'c>(
&'c mut self,
) -> Box<dyn OAuth2SessionRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(self.inner.oauth2_session(), &mut self.mapper))
}
fn oauth2_access_token<'c>(
&'c mut self,
) -> Box<dyn OAuth2AccessTokenRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(
self.inner.oauth2_access_token(),
&mut self.mapper,
))
}
fn oauth2_refresh_token<'c>(
&'c mut self,
) -> Box<dyn OAuth2RefreshTokenRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(
self.inner.oauth2_refresh_token(),
&mut self.mapper,
))
}
fn compat_session<'c>(
&'c mut self,
) -> Box<dyn CompatSessionRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(self.inner.compat_session(), &mut self.mapper))
}
fn compat_sso_login<'c>(
&'c mut self,
) -> Box<dyn CompatSsoLoginRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(self.inner.compat_sso_login(), &mut self.mapper))
}
fn compat_access_token<'c>(
&'c mut self,
) -> Box<dyn CompatAccessTokenRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(
self.inner.compat_access_token(),
&mut self.mapper,
))
}
fn compat_refresh_token<'c>(
&'c mut self,
) -> Box<dyn CompatRefreshTokenRepository<Error = Self::Error> + 'c> {
Box::new(MapErr::new(
self.inner.compat_refresh_token(),
&mut self.mapper,
))
}
}

View File

@ -17,11 +17,11 @@ use mas_data_model::{UpstreamOAuthLink, UpstreamOAuthProvider, User};
use rand_core::RngCore;
use ulid::Ulid;
use crate::{pagination::Page, Clock, Pagination};
use crate::{pagination::Page, repository_impl, Clock, Pagination};
#[async_trait]
pub trait UpstreamOAuthLinkRepository: Send + Sync {
type Error;
type Error: std::error::Error + Send + Sync;
/// Lookup an upstream OAuth link by its ID
async fn lookup(&mut self, id: Ulid) -> Result<Option<UpstreamOAuthLink>, Self::Error>;
@ -56,3 +56,33 @@ pub trait UpstreamOAuthLinkRepository: Send + Sync {
pagination: Pagination,
) -> Result<Page<UpstreamOAuthLink>, Self::Error>;
}
repository_impl!(UpstreamOAuthLinkRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<UpstreamOAuthLink>, Self::Error>;
async fn find_by_subject(
&mut self,
upstream_oauth_provider: &UpstreamOAuthProvider,
subject: &str,
) -> Result<Option<UpstreamOAuthLink>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
upstream_oauth_provider: &UpstreamOAuthProvider,
subject: String,
) -> Result<UpstreamOAuthLink, Self::Error>;
async fn associate_to_user(
&mut self,
upstream_oauth_link: &UpstreamOAuthLink,
user: &User,
) -> Result<(), Self::Error>;
async fn list_paginated(
&mut self,
user: &User,
pagination: Pagination,
) -> Result<Page<UpstreamOAuthLink>, Self::Error>;
);

View File

@ -19,7 +19,7 @@ use oauth2_types::scope::Scope;
use rand_core::RngCore;
use ulid::Ulid;
use crate::{pagination::Page, Clock, Pagination};
use crate::{pagination::Page, repository_impl, Clock, Pagination};
#[async_trait]
pub trait UpstreamOAuthProviderRepository: Send + Sync {
@ -51,3 +51,26 @@ pub trait UpstreamOAuthProviderRepository: Send + Sync {
/// Get all upstream OAuth providers
async fn all(&mut self) -> Result<Vec<UpstreamOAuthProvider>, Self::Error>;
}
repository_impl!(UpstreamOAuthProviderRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<UpstreamOAuthProvider>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
issuer: String,
scope: Scope,
token_endpoint_auth_method: OAuthClientAuthenticationMethod,
token_endpoint_signing_alg: Option<JsonWebSignatureAlg>,
client_id: String,
encrypted_client_secret: Option<String>
) -> Result<UpstreamOAuthProvider, Self::Error>;
async fn list_paginated(
&mut self,
pagination: Pagination
) -> Result<Page<UpstreamOAuthProvider>, Self::Error>;
async fn all(&mut self) -> Result<Vec<UpstreamOAuthProvider>, Self::Error>;
);

View File

@ -17,7 +17,7 @@ use mas_data_model::{UpstreamOAuthAuthorizationSession, UpstreamOAuthLink, Upstr
use rand_core::RngCore;
use ulid::Ulid;
use crate::Clock;
use crate::{repository_impl, Clock};
#[async_trait]
pub trait UpstreamOAuthSessionRepository: Send + Sync {
@ -56,3 +56,34 @@ pub trait UpstreamOAuthSessionRepository: Send + Sync {
upstream_oauth_authorization_session: UpstreamOAuthAuthorizationSession,
) -> Result<UpstreamOAuthAuthorizationSession, Self::Error>;
}
repository_impl!(UpstreamOAuthSessionRepository:
async fn lookup(
&mut self,
id: Ulid,
) -> Result<Option<UpstreamOAuthAuthorizationSession>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
upstream_oauth_provider: &UpstreamOAuthProvider,
state: String,
code_challenge_verifier: Option<String>,
nonce: String,
) -> Result<UpstreamOAuthAuthorizationSession, Self::Error>;
async fn complete_with_link(
&mut self,
clock: &dyn Clock,
upstream_oauth_authorization_session: UpstreamOAuthAuthorizationSession,
upstream_oauth_link: &UpstreamOAuthLink,
id_token: Option<String>,
) -> Result<UpstreamOAuthAuthorizationSession, Self::Error>;
async fn consume(
&mut self,
clock: &dyn Clock,
upstream_oauth_authorization_session: UpstreamOAuthAuthorizationSession,
) -> Result<UpstreamOAuthAuthorizationSession, Self::Error>;
);

View File

@ -17,7 +17,7 @@ use mas_data_model::{User, UserEmail, UserEmailVerification};
use rand_core::RngCore;
use ulid::Ulid;
use crate::{pagination::Page, Clock, Pagination};
use crate::{pagination::Page, repository_impl, Clock, Pagination};
#[async_trait]
pub trait UserEmailRepository: Send + Sync {
@ -74,3 +74,56 @@ pub trait UserEmailRepository: Send + Sync {
verification: UserEmailVerification,
) -> Result<UserEmailVerification, Self::Error>;
}
repository_impl!(UserEmailRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<UserEmail>, Self::Error>;
async fn find(&mut self, user: &User, email: &str) -> Result<Option<UserEmail>, Self::Error>;
async fn get_primary(&mut self, user: &User) -> Result<Option<UserEmail>, Self::Error>;
async fn all(&mut self, user: &User) -> Result<Vec<UserEmail>, Self::Error>;
async fn list_paginated(
&mut self,
user: &User,
pagination: Pagination,
) -> Result<Page<UserEmail>, Self::Error>;
async fn count(&mut self, user: &User) -> Result<usize, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
user: &User,
email: String,
) -> Result<UserEmail, Self::Error>;
async fn remove(&mut self, user_email: UserEmail) -> Result<(), Self::Error>;
async fn mark_as_verified(
&mut self,
clock: &dyn Clock,
user_email: UserEmail,
) -> Result<UserEmail, Self::Error>;
async fn set_as_primary(&mut self, user_email: &UserEmail) -> Result<(), Self::Error>;
async fn add_verification_code(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
user_email: &UserEmail,
max_age: chrono::Duration,
code: String,
) -> Result<UserEmailVerification, Self::Error>;
async fn find_verification_code(
&mut self,
clock: &dyn Clock,
user_email: &UserEmail,
code: &str,
) -> Result<Option<UserEmailVerification>, Self::Error>;
async fn consume_verification_code(
&mut self,
clock: &dyn Clock,
verification: UserEmailVerification,
) -> Result<UserEmailVerification, Self::Error>;
);

View File

@ -17,7 +17,7 @@ use mas_data_model::User;
use rand_core::RngCore;
use ulid::Ulid;
use crate::Clock;
use crate::{repository_impl, Clock};
mod email;
mod password;
@ -41,3 +41,15 @@ pub trait UserRepository: Send + Sync {
) -> Result<User, Self::Error>;
async fn exists(&mut self, username: &str) -> Result<bool, Self::Error>;
}
repository_impl!(UserRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<User>, Self::Error>;
async fn find_by_username(&mut self, username: &str) -> Result<Option<User>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
username: String,
) -> Result<User, Self::Error>;
async fn exists(&mut self, username: &str) -> Result<bool, Self::Error>;
);

View File

@ -16,7 +16,7 @@ use async_trait::async_trait;
use mas_data_model::{Password, User};
use rand_core::RngCore;
use crate::Clock;
use crate::{repository_impl, Clock};
#[async_trait]
pub trait UserPasswordRepository: Send + Sync {
@ -33,3 +33,16 @@ pub trait UserPasswordRepository: Send + Sync {
upgraded_from: Option<&Password>,
) -> Result<Password, Self::Error>;
}
repository_impl!(UserPasswordRepository:
async fn active(&mut self, user: &User) -> Result<Option<Password>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
user: &User,
version: u16,
hashed_password: String,
upgraded_from: Option<&Password>,
) -> Result<Password, Self::Error>;
);

View File

@ -17,7 +17,7 @@ use mas_data_model::{BrowserSession, Password, UpstreamOAuthLink, User};
use rand_core::RngCore;
use ulid::Ulid;
use crate::{pagination::Page, Clock, Pagination};
use crate::{pagination::Page, repository_impl, Clock, Pagination};
#[async_trait]
pub trait BrowserSessionRepository: Send + Sync {
@ -58,3 +58,40 @@ pub trait BrowserSessionRepository: Send + Sync {
upstream_oauth_link: &UpstreamOAuthLink,
) -> Result<BrowserSession, Self::Error>;
}
repository_impl!(BrowserSessionRepository:
async fn lookup(&mut self, id: Ulid) -> Result<Option<BrowserSession>, Self::Error>;
async fn add(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
user: &User,
) -> Result<BrowserSession, Self::Error>;
async fn finish(
&mut self,
clock: &dyn Clock,
user_session: BrowserSession,
) -> Result<BrowserSession, Self::Error>;
async fn list_active_paginated(
&mut self,
user: &User,
pagination: Pagination,
) -> Result<Page<BrowserSession>, Self::Error>;
async fn count_active(&mut self, user: &User) -> Result<usize, Self::Error>;
async fn authenticate_with_password(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
user_session: BrowserSession,
user_password: &Password,
) -> Result<BrowserSession, Self::Error>;
async fn authenticate_with_upstream(
&mut self,
rng: &mut (dyn RngCore + Send),
clock: &dyn Clock,
user_session: BrowserSession,
upstream_oauth_link: &UpstreamOAuthLink,
) -> Result<BrowserSession, Self::Error>;
);