You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-29 22:01:14 +03:00
handlers: box the rng and clock, and extract it from the state
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3105,7 +3105,7 @@ dependencies = [
|
||||
"mas-iana",
|
||||
"mas-jose",
|
||||
"oauth2-types",
|
||||
"rand 0.8.5",
|
||||
"rand_core 0.6.4",
|
||||
"thiserror",
|
||||
"ulid",
|
||||
"url",
|
||||
|
@ -15,6 +15,7 @@
|
||||
use axum_extra::extract::cookie::{Cookie, PrivateCookieJar};
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use data_encoding::{DecodeError, BASE64URL_NOPAD};
|
||||
use mas_storage::Clock;
|
||||
use rand::{Rng, RngCore};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::{serde_as, TimestampSeconds};
|
||||
@ -108,22 +109,27 @@ pub struct ProtectedForm<T> {
|
||||
}
|
||||
|
||||
pub trait CsrfExt {
|
||||
fn csrf_token<R>(self, now: DateTime<Utc>, rng: R) -> (CsrfToken, Self)
|
||||
fn csrf_token<C, R>(self, clock: &C, rng: R) -> (CsrfToken, Self)
|
||||
where
|
||||
R: RngCore;
|
||||
fn verify_form<T>(&self, now: DateTime<Utc>, form: ProtectedForm<T>) -> Result<T, CsrfError>;
|
||||
R: RngCore,
|
||||
C: Clock;
|
||||
fn verify_form<C, T>(&self, clock: &C, form: ProtectedForm<T>) -> Result<T, CsrfError>
|
||||
where
|
||||
C: Clock;
|
||||
}
|
||||
|
||||
impl<K> CsrfExt for PrivateCookieJar<K> {
|
||||
fn csrf_token<R>(self, now: DateTime<Utc>, rng: R) -> (CsrfToken, Self)
|
||||
fn csrf_token<C, R>(self, clock: &C, rng: R) -> (CsrfToken, Self)
|
||||
where
|
||||
R: RngCore,
|
||||
C: Clock,
|
||||
{
|
||||
let jar = self;
|
||||
let mut cookie = jar.get("csrf").unwrap_or_else(|| Cookie::new("csrf", ""));
|
||||
cookie.set_path("/");
|
||||
cookie.set_http_only(true);
|
||||
|
||||
let now = clock.now();
|
||||
let new_token = cookie
|
||||
.decode()
|
||||
.ok()
|
||||
@ -136,10 +142,13 @@ impl<K> CsrfExt for PrivateCookieJar<K> {
|
||||
(new_token, jar)
|
||||
}
|
||||
|
||||
fn verify_form<T>(&self, now: DateTime<Utc>, form: ProtectedForm<T>) -> Result<T, CsrfError> {
|
||||
fn verify_form<C, T>(&self, clock: &C, form: ProtectedForm<T>) -> Result<T, CsrfError>
|
||||
where
|
||||
C: Clock,
|
||||
{
|
||||
let cookie = self.get("csrf").ok_or(CsrfError::Missing)?;
|
||||
let token: CsrfToken = cookie.decode()?;
|
||||
let token = token.verify_expiration(now)?;
|
||||
let token = token.verify_expiration(clock.now())?;
|
||||
token.verify_form_value(&form.csrf)?;
|
||||
Ok(form.inner)
|
||||
}
|
||||
|
@ -24,13 +24,12 @@ use axum::{
|
||||
response::{IntoResponse, Response},
|
||||
BoxError,
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use headers::{authorization::Bearer, Authorization, Header, HeaderMapExt, HeaderName};
|
||||
use http::{header::WWW_AUTHENTICATE, HeaderMap, HeaderValue, Request, StatusCode};
|
||||
use mas_data_model::Session;
|
||||
use mas_storage::{
|
||||
oauth2::{OAuth2AccessTokenRepository, OAuth2SessionRepository},
|
||||
Repository,
|
||||
Clock, Repository,
|
||||
};
|
||||
use serde::{de::DeserializeOwned, Deserialize};
|
||||
use thiserror::Error;
|
||||
@ -86,10 +85,10 @@ pub struct UserAuthorization<F = ()> {
|
||||
|
||||
impl<F: Send> UserAuthorization<F> {
|
||||
// TODO: take scopes to validate as parameter
|
||||
pub async fn protected_form<R: Repository>(
|
||||
pub async fn protected_form<R: Repository, C: Clock>(
|
||||
self,
|
||||
repo: &mut R,
|
||||
now: DateTime<Utc>,
|
||||
clock: &C,
|
||||
) -> Result<(Session, F), AuthorizationVerificationError<R::Error>> {
|
||||
let form = match self.form {
|
||||
Some(f) => f,
|
||||
@ -98,7 +97,7 @@ impl<F: Send> UserAuthorization<F> {
|
||||
|
||||
let (token, session) = self.access_token.fetch(repo).await?;
|
||||
|
||||
if !token.is_valid(now) || !session.is_valid() {
|
||||
if !token.is_valid(clock.now()) || !session.is_valid() {
|
||||
return Err(AuthorizationVerificationError::InvalidToken);
|
||||
}
|
||||
|
||||
@ -106,14 +105,14 @@ impl<F: Send> UserAuthorization<F> {
|
||||
}
|
||||
|
||||
// TODO: take scopes to validate as parameter
|
||||
pub async fn protected<R: Repository>(
|
||||
pub async fn protected<R: Repository, C: Clock>(
|
||||
self,
|
||||
repo: &mut R,
|
||||
now: DateTime<Utc>,
|
||||
clock: &C,
|
||||
) -> Result<Session, AuthorizationVerificationError<R::Error>> {
|
||||
let (token, session) = self.access_token.fetch(repo).await?;
|
||||
|
||||
if !token.is_valid(now) || !session.is_valid() {
|
||||
if !token.is_valid(clock.now()) || !session.is_valid() {
|
||||
return Err(AuthorizationVerificationError::InvalidToken);
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use crc::{Crc, CRC_32_ISO_HDLC};
|
||||
use mas_iana::oauth::OAuthTokenTypeHint;
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
use rand::{distributions::Alphanumeric, Rng, RngCore};
|
||||
use thiserror::Error;
|
||||
use ulid::Ulid;
|
||||
|
||||
@ -193,7 +193,7 @@ impl TokenType {
|
||||
/// AccessToken.generate(thread_rng());
|
||||
/// RefreshToken.generate(thread_rng());
|
||||
/// ```
|
||||
pub fn generate(self, rng: impl Rng) -> String {
|
||||
pub fn generate(self, rng: &mut (impl RngCore + ?Sized)) -> String {
|
||||
let random_part: String = rng
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(30)
|
||||
|
@ -12,15 +12,20 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
|
||||
use axum::extract::FromRef;
|
||||
use axum::{
|
||||
async_trait,
|
||||
extract::{FromRef, FromRequestParts},
|
||||
};
|
||||
use mas_axum_utils::http_client_factory::HttpClientFactory;
|
||||
use mas_email::Mailer;
|
||||
use mas_keystore::{Encrypter, Keystore};
|
||||
use mas_policy::PolicyFactory;
|
||||
use mas_router::UrlBuilder;
|
||||
use mas_storage::{BoxClock, BoxRng, SystemClock};
|
||||
use mas_templates::Templates;
|
||||
use rand::SeedableRng;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{passwords::PasswordManager, MatrixHomeserver};
|
||||
@ -105,3 +110,33 @@ impl FromRef<AppState> for PasswordManager {
|
||||
input.password_manager.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromRequestParts<AppState> for BoxClock {
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request_parts(
|
||||
_parts: &mut axum::http::request::Parts,
|
||||
_state: &AppState,
|
||||
) -> Result<Self, Self::Rejection> {
|
||||
let clock = SystemClock::default();
|
||||
Ok(Box::new(clock))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromRequestParts<AppState> for BoxRng {
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request_parts(
|
||||
_parts: &mut axum::http::request::Parts,
|
||||
_state: &AppState,
|
||||
) -> Result<Self, Self::Rejection> {
|
||||
// This rng is used to source the local rng
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let rng = rand::thread_rng();
|
||||
|
||||
let rng = rand_chacha::ChaChaRng::from_rng(rng).expect("Failed to seed RNG");
|
||||
Ok(Box::new(rng))
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,10 @@ use mas_storage::{
|
||||
CompatSsoLoginRepository,
|
||||
},
|
||||
user::{UserPasswordRepository, UserRepository},
|
||||
Clock, Repository, SystemClock,
|
||||
BoxClock, BoxRng, Clock, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::{serde_as, skip_serializing_none, DurationMilliSeconds};
|
||||
use sqlx::PgPool;
|
||||
@ -154,7 +155,6 @@ pub enum RouteError {
|
||||
InvalidLoginToken,
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
|
||||
impl IntoResponse for RouteError {
|
||||
@ -194,18 +194,29 @@ impl IntoResponse for RouteError {
|
||||
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(password_manager): State<PasswordManager>,
|
||||
State(pool): State<PgPool>,
|
||||
State(homeserver): State<MatrixHomeserver>,
|
||||
Json(input): Json<RequestBody>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
let (session, user) = match input.credentials {
|
||||
Credentials::Password {
|
||||
identifier: Identifier::User { user },
|
||||
password,
|
||||
} => user_password_login(&password_manager, &mut repo, user, password).await?,
|
||||
} => {
|
||||
user_password_login(
|
||||
&mut rng,
|
||||
&clock,
|
||||
&password_manager,
|
||||
&mut repo,
|
||||
user,
|
||||
password,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
|
||||
Credentials::Token { token } => token_login(&mut repo, &clock, &token).await?,
|
||||
|
||||
@ -254,7 +265,7 @@ pub(crate) async fn post(
|
||||
|
||||
async fn token_login(
|
||||
repo: &mut PgRepository,
|
||||
clock: &SystemClock,
|
||||
clock: &dyn Clock,
|
||||
token: &str,
|
||||
) -> Result<(CompatSession, User), RouteError> {
|
||||
let login = repo
|
||||
@ -319,13 +330,13 @@ async fn token_login(
|
||||
}
|
||||
|
||||
async fn user_password_login(
|
||||
mut rng: &mut (impl RngCore + CryptoRng + Send),
|
||||
clock: &impl Clock,
|
||||
password_manager: &PasswordManager,
|
||||
repo: &mut PgRepository,
|
||||
username: String,
|
||||
password: String,
|
||||
) -> Result<(CompatSession, User), RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
|
||||
// Find the user
|
||||
let user = repo
|
||||
.user()
|
||||
@ -358,7 +369,7 @@ async fn user_password_login(
|
||||
repo.user_password()
|
||||
.add(
|
||||
&mut rng,
|
||||
&clock,
|
||||
clock,
|
||||
&user,
|
||||
version,
|
||||
hashed_password,
|
||||
@ -371,7 +382,7 @@ async fn user_password_login(
|
||||
let device = Device::generate(&mut rng);
|
||||
let session = repo
|
||||
.compat_session()
|
||||
.add(&mut rng, &clock, &user, device)
|
||||
.add(&mut rng, clock, &user, device)
|
||||
.await?;
|
||||
|
||||
Ok((session, user))
|
||||
|
@ -31,7 +31,7 @@ use mas_keystore::Encrypter;
|
||||
use mas_router::{CompatLoginSsoAction, PostAuthAction, Route};
|
||||
use mas_storage::{
|
||||
compat::{CompatSessionRepository, CompatSsoLoginRepository},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Clock, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{CompatSsoContext, ErrorContext, TemplateContext, Templates};
|
||||
@ -54,17 +54,18 @@ pub struct Params {
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(pool): State<PgPool>,
|
||||
State(templates): State<Templates>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Path(id): Path<Ulid>,
|
||||
Query(params): Query<Params>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
|
||||
let maybe_session = session_info.load_session(&mut repo).await?;
|
||||
|
||||
@ -117,6 +118,8 @@ pub async fn get(
|
||||
}
|
||||
|
||||
pub async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(pool): State<PgPool>,
|
||||
State(templates): State<Templates>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
@ -124,11 +127,10 @@ pub async fn post(
|
||||
Query(params): Query<Params>,
|
||||
Form(form): Form<ProtectedForm<()>>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
cookie_jar.verify_form(clock.now(), form)?;
|
||||
cookie_jar.verify_form(&clock, form)?;
|
||||
|
||||
let maybe_session = session_info.load_session(&mut repo).await?;
|
||||
|
||||
|
@ -19,7 +19,7 @@ use axum::{
|
||||
};
|
||||
use hyper::StatusCode;
|
||||
use mas_router::{CompatLoginSsoAction, CompatLoginSsoComplete, UrlBuilder};
|
||||
use mas_storage::{compat::CompatSsoLoginRepository, Repository};
|
||||
use mas_storage::{compat::CompatSsoLoginRepository, BoxClock, BoxRng, Repository};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
use serde::Deserialize;
|
||||
@ -49,7 +49,6 @@ pub enum RouteError {
|
||||
InvalidRedirectUrl,
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
|
||||
impl IntoResponse for RouteError {
|
||||
@ -58,14 +57,13 @@ impl IntoResponse for RouteError {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(pool, url_builder), err)]
|
||||
pub async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(pool): State<PgPool>,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
Query(params): Query<Params>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
|
||||
// Check the redirectUrl parameter
|
||||
let redirect_url = params.redirect_url.ok_or(RouteError::MissingRedirectUrl)?;
|
||||
let redirect_url = Url::parse(&redirect_url).map_err(|_| RouteError::InvalidRedirectUrl)?;
|
||||
|
@ -18,7 +18,7 @@ use hyper::StatusCode;
|
||||
use mas_data_model::TokenType;
|
||||
use mas_storage::{
|
||||
compat::{CompatAccessTokenRepository, CompatSessionRepository},
|
||||
Clock, Repository, SystemClock,
|
||||
BoxClock, Clock, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use sqlx::PgPool;
|
||||
@ -42,7 +42,6 @@ pub enum RouteError {
|
||||
InvalidAuthorization,
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
|
||||
impl IntoResponse for RouteError {
|
||||
@ -69,10 +68,10 @@ impl IntoResponse for RouteError {
|
||||
}
|
||||
|
||||
pub(crate) async fn post(
|
||||
clock: BoxClock,
|
||||
State(pool): State<PgPool>,
|
||||
maybe_authorization: Option<TypedHeader<Authorization<Bearer>>>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let clock = SystemClock::default();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let TypedHeader(authorization) = maybe_authorization.ok_or(RouteError::MissingAuthorization)?;
|
||||
|
@ -18,7 +18,7 @@ use hyper::StatusCode;
|
||||
use mas_data_model::{TokenFormatError, TokenType};
|
||||
use mas_storage::{
|
||||
compat::{CompatAccessTokenRepository, CompatRefreshTokenRepository, CompatSessionRepository},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Clock, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -70,7 +70,6 @@ impl IntoResponse for RouteError {
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
|
||||
impl From<TokenFormatError> for RouteError {
|
||||
@ -89,10 +88,11 @@ pub struct ResponseBody {
|
||||
}
|
||||
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(pool): State<PgPool>,
|
||||
Json(input): Json<RequestBody>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let token_type = TokenType::check(&input.refresh_token)?;
|
||||
|
@ -28,7 +28,7 @@ use std::{convert::Infallible, sync::Arc, time::Duration};
|
||||
|
||||
use axum::{
|
||||
body::{Bytes, HttpBody},
|
||||
extract::FromRef,
|
||||
extract::{FromRef, FromRequestParts},
|
||||
response::{Html, IntoResponse},
|
||||
routing::{get, on, post, MethodFilter},
|
||||
Router,
|
||||
@ -40,9 +40,9 @@ use mas_http::CorsLayerExt;
|
||||
use mas_keystore::{Encrypter, Keystore};
|
||||
use mas_policy::PolicyFactory;
|
||||
use mas_router::{Route, UrlBuilder};
|
||||
use mas_storage::{BoxClock, BoxRng};
|
||||
use mas_templates::{ErrorContext, Templates};
|
||||
use passwords::PasswordManager;
|
||||
use rand::SeedableRng;
|
||||
use sqlx::PgPool;
|
||||
use tower::util::AndThenLayer;
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
@ -116,6 +116,8 @@ where
|
||||
S: Clone + Send + Sync + 'static,
|
||||
Keystore: FromRef<S>,
|
||||
UrlBuilder: FromRef<S>,
|
||||
BoxClock: FromRequestParts<S>,
|
||||
BoxRng: FromRequestParts<S>,
|
||||
{
|
||||
Router::new()
|
||||
.route(
|
||||
@ -155,6 +157,8 @@ where
|
||||
PgPool: FromRef<S>,
|
||||
Encrypter: FromRef<S>,
|
||||
HttpClientFactory: FromRef<S>,
|
||||
BoxClock: FromRequestParts<S>,
|
||||
BoxRng: FromRequestParts<S>,
|
||||
{
|
||||
// All those routes are API-like, with a common CORS layer
|
||||
Router::new()
|
||||
@ -208,6 +212,8 @@ where
|
||||
PgPool: FromRef<S>,
|
||||
MatrixHomeserver: FromRef<S>,
|
||||
PasswordManager: FromRef<S>,
|
||||
BoxClock: FromRequestParts<S>,
|
||||
BoxRng: FromRequestParts<S>,
|
||||
{
|
||||
Router::new()
|
||||
.route(
|
||||
@ -255,6 +261,8 @@ where
|
||||
Keystore: FromRef<S>,
|
||||
HttpClientFactory: FromRef<S>,
|
||||
PasswordManager: FromRef<S>,
|
||||
BoxClock: FromRequestParts<S>,
|
||||
BoxRng: FromRequestParts<S>,
|
||||
{
|
||||
Router::new()
|
||||
.route(
|
||||
@ -407,16 +415,3 @@ async fn test_state(pool: PgPool) -> Result<AppState, anyhow::Error> {
|
||||
password_manager,
|
||||
})
|
||||
}
|
||||
|
||||
// XXX: that should be moved somewhere else
|
||||
fn clock_and_rng() -> (mas_storage::SystemClock, rand_chacha::ChaChaRng) {
|
||||
let clock = mas_storage::SystemClock::default();
|
||||
|
||||
// This rng is used to source the local rng
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let rng = rand::thread_rng();
|
||||
|
||||
let rng = rand_chacha::ChaChaRng::from_rng(rng).expect("Failed to seed RNG");
|
||||
|
||||
(clock, rng)
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ use mas_policy::PolicyFactory;
|
||||
use mas_router::{PostAuthAction, Route};
|
||||
use mas_storage::{
|
||||
oauth2::{OAuth2AuthorizationGrantRepository, OAuth2ClientRepository, OAuth2SessionRepository},
|
||||
Repository,
|
||||
BoxClock, BoxRng, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::Templates;
|
||||
@ -70,7 +70,6 @@ impl IntoResponse for RouteError {
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
impl_from_error_for_route!(mas_policy::LoadError);
|
||||
impl_from_error_for_route!(mas_policy::InstanciateError);
|
||||
@ -79,6 +78,8 @@ impl_from_error_for_route!(super::callback::IntoCallbackDestinationError);
|
||||
impl_from_error_for_route!(super::callback::CallbackDestinationError);
|
||||
|
||||
pub(crate) async fn get(
|
||||
rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(policy_factory): State<Arc<PolicyFactory>>,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
@ -108,7 +109,7 @@ pub(crate) async fn get(
|
||||
return Ok((cookie_jar, mas_router::Login::and_then(continue_grant).go()).into_response());
|
||||
};
|
||||
|
||||
match complete(grant, session, &policy_factory, repo).await {
|
||||
match complete(rng, clock, grant, session, &policy_factory, repo).await {
|
||||
Ok(params) => {
|
||||
let res = callback_destination.go(&templates, params).await?;
|
||||
Ok((cookie_jar, res).into_response())
|
||||
@ -149,7 +150,6 @@ pub enum GrantCompletionError {
|
||||
NoSuchClient,
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(GrantCompletionError: sqlx::Error);
|
||||
impl_from_error_for_route!(GrantCompletionError: mas_storage_pg::DatabaseError);
|
||||
impl_from_error_for_route!(GrantCompletionError: super::callback::IntoCallbackDestinationError);
|
||||
impl_from_error_for_route!(GrantCompletionError: mas_policy::LoadError);
|
||||
@ -157,13 +157,13 @@ impl_from_error_for_route!(GrantCompletionError: mas_policy::InstanciateError);
|
||||
impl_from_error_for_route!(GrantCompletionError: mas_policy::EvaluationError);
|
||||
|
||||
pub(crate) async fn complete(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
grant: AuthorizationGrant,
|
||||
browser_session: BrowserSession,
|
||||
policy_factory: &PolicyFactory,
|
||||
mut repo: PgRepository,
|
||||
) -> Result<AuthorizationResponse<Option<AccessTokenResponse>>, GrantCompletionError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
|
||||
// Verify that the grant is in a pending stage
|
||||
if !grant.stage.is_pending() {
|
||||
return Err(GrantCompletionError::NotPending);
|
||||
|
@ -27,7 +27,7 @@ use mas_policy::PolicyFactory;
|
||||
use mas_router::{PostAuthAction, Route};
|
||||
use mas_storage::{
|
||||
oauth2::{OAuth2AuthorizationGrantRepository, OAuth2ClientRepository},
|
||||
Repository,
|
||||
BoxClock, BoxRng, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::Templates;
|
||||
@ -91,7 +91,6 @@ impl IntoResponse for RouteError {
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
impl_from_error_for_route!(self::callback::CallbackDestinationError);
|
||||
impl_from_error_for_route!(mas_policy::LoadError);
|
||||
@ -133,13 +132,14 @@ fn resolve_response_mode(
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(policy_factory): State<Arc<PolicyFactory>>,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Form(params): Form<Params>,
|
||||
) -> Result<Response, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
// First, figure out what client it is
|
||||
@ -334,7 +334,15 @@ pub(crate) async fn get(
|
||||
// Else, we immediately try to complete the authorization grant
|
||||
Some(user_session) if prompt.contains(&Prompt::None) => {
|
||||
// With prompt=none, we should get back to the client immediately
|
||||
match self::complete::complete(grant, user_session, &policy_factory, repo).await
|
||||
match self::complete::complete(
|
||||
rng,
|
||||
clock,
|
||||
grant,
|
||||
user_session,
|
||||
&policy_factory,
|
||||
repo,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(params) => callback_destination.go(&templates, params).await?,
|
||||
Err(GrantCompletionError::RequiresConsent) => {
|
||||
@ -373,7 +381,15 @@ pub(crate) async fn get(
|
||||
Some(user_session) => {
|
||||
let grant_id = grant.id;
|
||||
// Else, we show the relevant reauth/consent page if necessary
|
||||
match self::complete::complete(grant, user_session, &policy_factory, repo).await
|
||||
match self::complete::complete(
|
||||
rng,
|
||||
clock,
|
||||
grant,
|
||||
user_session,
|
||||
&policy_factory,
|
||||
repo,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(params) => callback_destination.go(&templates, params).await?,
|
||||
Err(
|
||||
|
@ -30,7 +30,7 @@ use mas_policy::PolicyFactory;
|
||||
use mas_router::{PostAuthAction, Route};
|
||||
use mas_storage::{
|
||||
oauth2::{OAuth2AuthorizationGrantRepository, OAuth2ClientRepository},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{ConsentContext, PolicyViolationContext, TemplateContext, Templates};
|
||||
@ -61,7 +61,6 @@ pub enum RouteError {
|
||||
NoSuchClient,
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_templates::TemplateError);
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
impl_from_error_for_route!(mas_policy::LoadError);
|
||||
@ -75,13 +74,14 @@ impl IntoResponse for RouteError {
|
||||
}
|
||||
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(policy_factory): State<Arc<PolicyFactory>>,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Path(grant_id): Path<Ulid>,
|
||||
) -> Result<Response, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
@ -99,7 +99,7 @@ pub(crate) async fn get(
|
||||
}
|
||||
|
||||
if let Some(session) = maybe_session {
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
|
||||
let mut policy = policy_factory.instantiate().await?;
|
||||
let res = policy
|
||||
@ -130,16 +130,17 @@ pub(crate) async fn get(
|
||||
}
|
||||
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(policy_factory): State<Arc<PolicyFactory>>,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Path(grant_id): Path<Ulid>,
|
||||
Form(form): Form<ProtectedForm<()>>,
|
||||
) -> Result<Response, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
cookie_jar.verify_form(clock.now(), form)?;
|
||||
cookie_jar.verify_form(&clock, form)?;
|
||||
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
|
@ -25,7 +25,7 @@ use mas_storage::{
|
||||
compat::{CompatAccessTokenRepository, CompatRefreshTokenRepository, CompatSessionRepository},
|
||||
oauth2::{OAuth2AccessTokenRepository, OAuth2RefreshTokenRepository, OAuth2SessionRepository},
|
||||
user::{BrowserSessionRepository, UserRepository},
|
||||
Clock, Repository, SystemClock,
|
||||
BoxClock, Clock, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use oauth2_types::{
|
||||
@ -97,7 +97,6 @@ impl IntoResponse for RouteError {
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
|
||||
impl From<TokenFormatError> for RouteError {
|
||||
@ -125,12 +124,12 @@ const API_SCOPE: ScopeToken = ScopeToken::from_static("urn:matrix:org.matrix.msc
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(crate) async fn post(
|
||||
clock: BoxClock,
|
||||
State(http_client_factory): State<HttpClientFactory>,
|
||||
State(pool): State<PgPool>,
|
||||
State(encrypter): State<Encrypter>,
|
||||
client_authorization: ClientAuthorization<IntrospectionRequest>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let clock = SystemClock::default();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let client = client_authorization
|
||||
|
@ -19,7 +19,7 @@ use hyper::StatusCode;
|
||||
use mas_iana::oauth::OAuthClientAuthenticationMethod;
|
||||
use mas_keystore::Encrypter;
|
||||
use mas_policy::{PolicyFactory, Violation};
|
||||
use mas_storage::{oauth2::OAuth2ClientRepository, Repository};
|
||||
use mas_storage::{oauth2::OAuth2ClientRepository, BoxClock, BoxRng, Repository};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use oauth2_types::{
|
||||
errors::{ClientError, ClientErrorCode},
|
||||
@ -49,7 +49,6 @@ pub(crate) enum RouteError {
|
||||
PolicyDenied(Vec<Violation>),
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
impl_from_error_for_route!(mas_policy::LoadError);
|
||||
impl_from_error_for_route!(mas_policy::InstanciateError);
|
||||
@ -108,12 +107,13 @@ impl IntoResponse for RouteError {
|
||||
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(pool): State<PgPool>,
|
||||
State(policy_factory): State<Arc<PolicyFactory>>,
|
||||
State(encrypter): State<Encrypter>,
|
||||
Json(body): Json<ClientMetadata>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
info!(?body, "Client registration");
|
||||
|
||||
// Validate the body
|
||||
|
@ -37,7 +37,7 @@ use mas_storage::{
|
||||
OAuth2RefreshTokenRepository, OAuth2SessionRepository,
|
||||
},
|
||||
user::BrowserSessionRepository,
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Clock, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use oauth2_types::{
|
||||
@ -151,7 +151,6 @@ impl IntoResponse for RouteError {
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
impl_from_error_for_route!(mas_keystore::WrongAlgorithmError);
|
||||
impl_from_error_for_route!(mas_jose::claims::ClaimError);
|
||||
@ -160,6 +159,8 @@ impl_from_error_for_route!(mas_jose::jwt::JwtSignatureError);
|
||||
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(http_client_factory): State<HttpClientFactory>,
|
||||
State(key_store): State<Keystore>,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
@ -189,10 +190,19 @@ pub(crate) async fn post(
|
||||
|
||||
let reply = match form {
|
||||
AccessTokenRequest::AuthorizationCode(grant) => {
|
||||
authorization_code_grant(&grant, &client, &key_store, &url_builder, repo).await?
|
||||
authorization_code_grant(
|
||||
&mut rng,
|
||||
&clock,
|
||||
&grant,
|
||||
&client,
|
||||
&key_store,
|
||||
&url_builder,
|
||||
repo,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
AccessTokenRequest::RefreshToken(grant) => {
|
||||
refresh_token_grant(&grant, &client, repo).await?
|
||||
refresh_token_grant(&mut rng, &clock, &grant, &client, repo).await?
|
||||
}
|
||||
_ => {
|
||||
return Err(RouteError::InvalidGrant);
|
||||
@ -208,14 +218,14 @@ pub(crate) async fn post(
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
async fn authorization_code_grant(
|
||||
mut rng: &mut BoxRng,
|
||||
clock: &impl Clock,
|
||||
grant: &AuthorizationCodeGrant,
|
||||
client: &Client,
|
||||
key_store: &Keystore,
|
||||
url_builder: &UrlBuilder,
|
||||
mut repo: PgRepository,
|
||||
) -> Result<AccessTokenResponse, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
|
||||
let authz_grant = repo
|
||||
.oauth2_authorization_grant()
|
||||
.find_by_code(&grant.code)
|
||||
@ -244,7 +254,7 @@ async fn authorization_code_grant(
|
||||
.lookup(session_id)
|
||||
.await?
|
||||
.ok_or(RouteError::NoSuchOAuthSession)?;
|
||||
repo.oauth2_session().finish(&clock, session).await?;
|
||||
repo.oauth2_session().finish(clock, session).await?;
|
||||
repo.save().await?;
|
||||
}
|
||||
|
||||
@ -302,12 +312,12 @@ async fn authorization_code_grant(
|
||||
|
||||
let access_token = repo
|
||||
.oauth2_access_token()
|
||||
.add(&mut rng, &clock, &session, access_token_str, ttl)
|
||||
.add(&mut rng, clock, &session, access_token_str, ttl)
|
||||
.await?;
|
||||
|
||||
let refresh_token = repo
|
||||
.oauth2_refresh_token()
|
||||
.add(&mut rng, &clock, &session, &access_token, refresh_token_str)
|
||||
.add(&mut rng, clock, &session, &access_token, refresh_token_str)
|
||||
.await?;
|
||||
|
||||
let id_token = if session.scope.contains(&scope::OPENID) {
|
||||
@ -357,7 +367,7 @@ async fn authorization_code_grant(
|
||||
}
|
||||
|
||||
repo.oauth2_authorization_grant()
|
||||
.exchange(&clock, authz_grant)
|
||||
.exchange(clock, authz_grant)
|
||||
.await?;
|
||||
|
||||
repo.save().await?;
|
||||
@ -366,12 +376,12 @@ async fn authorization_code_grant(
|
||||
}
|
||||
|
||||
async fn refresh_token_grant(
|
||||
mut rng: &mut BoxRng,
|
||||
clock: &impl Clock,
|
||||
grant: &RefreshTokenGrant,
|
||||
client: &Client,
|
||||
mut repo: PgRepository,
|
||||
) -> Result<AccessTokenResponse, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
|
||||
let refresh_token = repo
|
||||
.oauth2_refresh_token()
|
||||
.find_by_token(&grant.refresh_token)
|
||||
@ -399,14 +409,14 @@ async fn refresh_token_grant(
|
||||
|
||||
let new_access_token = repo
|
||||
.oauth2_access_token()
|
||||
.add(&mut rng, &clock, &session, access_token_str.clone(), ttl)
|
||||
.add(&mut rng, clock, &session, access_token_str.clone(), ttl)
|
||||
.await?;
|
||||
|
||||
let new_refresh_token = repo
|
||||
.oauth2_refresh_token()
|
||||
.add(
|
||||
&mut rng,
|
||||
&clock,
|
||||
clock,
|
||||
&session,
|
||||
&new_access_token,
|
||||
refresh_token_str,
|
||||
@ -415,13 +425,13 @@ async fn refresh_token_grant(
|
||||
|
||||
let refresh_token = repo
|
||||
.oauth2_refresh_token()
|
||||
.consume(&clock, refresh_token)
|
||||
.consume(clock, refresh_token)
|
||||
.await?;
|
||||
|
||||
if let Some(access_token_id) = refresh_token.access_token_id {
|
||||
if let Some(access_token) = repo.oauth2_access_token().lookup(access_token_id).await? {
|
||||
repo.oauth2_access_token()
|
||||
.revoke(&clock, access_token)
|
||||
.revoke(clock, access_token)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ use mas_router::UrlBuilder;
|
||||
use mas_storage::{
|
||||
oauth2::OAuth2ClientRepository,
|
||||
user::{BrowserSessionRepository, UserEmailRepository},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use oauth2_types::scope;
|
||||
@ -79,7 +79,6 @@ pub enum RouteError {
|
||||
NoSuchBrowserSession,
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
impl_from_error_for_route!(mas_keystore::WrongAlgorithmError);
|
||||
impl_from_error_for_route!(mas_jose::jwt::JwtSignatureError);
|
||||
@ -99,15 +98,16 @@ impl IntoResponse for RouteError {
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
State(pool): State<PgPool>,
|
||||
State(key_store): State<Keystore>,
|
||||
user_authorization: UserAuthorization,
|
||||
) -> Result<Response, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let session = user_authorization.protected(&mut repo, clock.now()).await?;
|
||||
let session = user_authorization.protected(&mut repo, &clock).await?;
|
||||
|
||||
let browser_session = repo
|
||||
.browser_session()
|
||||
|
@ -24,7 +24,7 @@ use mas_oidc_client::requests::authorization_code::AuthorizationRequestData;
|
||||
use mas_router::UrlBuilder;
|
||||
use mas_storage::{
|
||||
upstream_oauth2::{UpstreamOAuthProviderRepository, UpstreamOAuthSessionRepository},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use sqlx::PgPool;
|
||||
@ -43,7 +43,6 @@ pub(crate) enum RouteError {
|
||||
Internal(Box<dyn std::error::Error>),
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_http::ClientInitError);
|
||||
impl_from_error_for_route!(mas_oidc_client::error::DiscoveryError);
|
||||
impl_from_error_for_route!(mas_oidc_client::error::AuthorizationError);
|
||||
@ -59,6 +58,8 @@ impl IntoResponse for RouteError {
|
||||
}
|
||||
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(http_client_factory): State<HttpClientFactory>,
|
||||
State(pool): State<PgPool>,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
@ -66,8 +67,6 @@ pub(crate) async fn get(
|
||||
Path(provider_id): Path<Ulid>,
|
||||
Query(query): Query<OptionalPostAuthAction>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let provider = repo
|
||||
@ -115,7 +114,7 @@ pub(crate) async fn get(
|
||||
|
||||
let cookie_jar = UpstreamSessionsCookie::load(&cookie_jar)
|
||||
.add(session.id, provider.id, data.state, query.post_auth_action)
|
||||
.save(cookie_jar, clock.now());
|
||||
.save(cookie_jar, &clock);
|
||||
|
||||
repo.save().await?;
|
||||
|
||||
|
@ -30,7 +30,7 @@ use mas_storage::{
|
||||
UpstreamOAuthLinkRepository, UpstreamOAuthProviderRepository,
|
||||
UpstreamOAuthSessionRepository,
|
||||
},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Clock, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use oauth2_types::errors::ClientErrorCode;
|
||||
@ -102,7 +102,6 @@ pub(crate) enum RouteError {
|
||||
|
||||
impl_from_error_for_route!(mas_storage_pg::DatabaseError);
|
||||
impl_from_error_for_route!(mas_http::ClientInitError);
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_oidc_client::error::DiscoveryError);
|
||||
impl_from_error_for_route!(mas_oidc_client::error::JwksError);
|
||||
impl_from_error_for_route!(mas_oidc_client::error::TokenAuthorizationCodeError);
|
||||
@ -122,6 +121,8 @@ impl IntoResponse for RouteError {
|
||||
|
||||
#[allow(clippy::too_many_lines, clippy::too_many_arguments)]
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(http_client_factory): State<HttpClientFactory>,
|
||||
State(pool): State<PgPool>,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
@ -131,8 +132,6 @@ pub(crate) async fn get(
|
||||
Path(provider_id): Path<Ulid>,
|
||||
Query(params): Query<QueryParams>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let provider = repo
|
||||
@ -268,7 +267,7 @@ pub(crate) async fn get(
|
||||
|
||||
let cookie_jar = sessions_cookie
|
||||
.add_link_to_session(session.id, link.id)?
|
||||
.save(cookie_jar, clock.now());
|
||||
.save(cookie_jar, &clock);
|
||||
|
||||
repo.save().await?;
|
||||
|
||||
|
@ -18,6 +18,7 @@ use axum_extra::extract::{cookie::Cookie, PrivateCookieJar};
|
||||
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
|
||||
use mas_axum_utils::CookieExt;
|
||||
use mas_router::PostAuthAction;
|
||||
use mas_storage::Clock;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use time::OffsetDateTime;
|
||||
@ -65,11 +66,11 @@ impl UpstreamSessions {
|
||||
}
|
||||
|
||||
/// Save the upstreams sessions to the cookie jar
|
||||
pub fn save<K>(
|
||||
self,
|
||||
cookie_jar: PrivateCookieJar<K>,
|
||||
now: DateTime<Utc>,
|
||||
) -> PrivateCookieJar<K> {
|
||||
pub fn save<K, C>(self, cookie_jar: PrivateCookieJar<K>, clock: &C) -> PrivateCookieJar<K>
|
||||
where
|
||||
C: Clock,
|
||||
{
|
||||
let now = clock.now();
|
||||
let this = self.expire(now);
|
||||
let mut cookie = Cookie::named(COOKIE_NAME).encode(&this);
|
||||
cookie.set_path("/");
|
||||
|
@ -27,7 +27,7 @@ use mas_keystore::Encrypter;
|
||||
use mas_storage::{
|
||||
upstream_oauth2::{UpstreamOAuthLinkRepository, UpstreamOAuthSessionRepository},
|
||||
user::{BrowserSessionRepository, UserRepository},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{
|
||||
@ -70,7 +70,6 @@ pub(crate) enum RouteError {
|
||||
Internal(Box<dyn std::error::Error>),
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(sqlx::Error);
|
||||
impl_from_error_for_route!(mas_templates::TemplateError);
|
||||
impl_from_error_for_route!(mas_axum_utils::csrf::CsrfError);
|
||||
impl_from_error_for_route!(super::cookie::UpstreamSessionNotFound);
|
||||
@ -95,14 +94,14 @@ pub(crate) enum FormData {
|
||||
}
|
||||
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(pool): State<PgPool>,
|
||||
State(templates): State<Templates>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Path(link_id): Path<Ulid>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
|
||||
let sessions_cookie = UpstreamSessionsCookie::load(&cookie_jar);
|
||||
let (session_id, _post_auth_action) = sessions_cookie
|
||||
.lookup_link(link_id)
|
||||
@ -131,7 +130,7 @@ pub(crate) async fn get(
|
||||
}
|
||||
|
||||
let (user_session_info, cookie_jar) = cookie_jar.session_info();
|
||||
let (csrf_token, mut cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, mut cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
let maybe_user_session = user_session_info.load_session(&mut repo).await?;
|
||||
|
||||
let render = match (maybe_user_session, link.user_id) {
|
||||
@ -212,14 +211,15 @@ pub(crate) async fn get(
|
||||
}
|
||||
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Path(link_id): Path<Ulid>,
|
||||
Form(form): Form<ProtectedForm<FormData>>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
let form = cookie_jar.verify_form(clock.now(), form)?;
|
||||
let form = cookie_jar.verify_form(&clock, form)?;
|
||||
|
||||
let sessions_cookie = UpstreamSessionsCookie::load(&cookie_jar);
|
||||
let (session_id, post_auth_action) = sessions_cookie
|
||||
@ -297,7 +297,7 @@ pub(crate) async fn post(
|
||||
|
||||
let cookie_jar = sessions_cookie
|
||||
.consume_link(link_id)?
|
||||
.save(cookie_jar, clock.now());
|
||||
.save(cookie_jar, &clock);
|
||||
let cookie_jar = cookie_jar.set_session(&session);
|
||||
|
||||
repo.save().await?;
|
||||
|
@ -24,7 +24,7 @@ use mas_axum_utils::{
|
||||
use mas_email::Mailer;
|
||||
use mas_keystore::Encrypter;
|
||||
use mas_router::Route;
|
||||
use mas_storage::{user::UserEmailRepository, Clock, Repository};
|
||||
use mas_storage::{user::UserEmailRepository, BoxClock, BoxRng, Repository};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{EmailAddContext, TemplateContext, Templates};
|
||||
use serde::Deserialize;
|
||||
@ -39,14 +39,15 @@ pub struct EmailForm {
|
||||
}
|
||||
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
let maybe_session = session_info.load_session(&mut repo).await?;
|
||||
@ -68,16 +69,17 @@ pub(crate) async fn get(
|
||||
}
|
||||
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(pool): State<PgPool>,
|
||||
State(mailer): State<Mailer>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Query(query): Query<OptionalPostAuthAction>,
|
||||
Form(form): Form<ProtectedForm<EmailForm>>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let form = cookie_jar.verify_form(clock.now(), form)?;
|
||||
let form = cookie_jar.verify_form(&clock, form)?;
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
let maybe_session = session_info.load_session(&mut repo).await?;
|
||||
|
@ -28,7 +28,7 @@ use mas_data_model::{BrowserSession, User, UserEmail};
|
||||
use mas_email::Mailer;
|
||||
use mas_keystore::Encrypter;
|
||||
use mas_router::Route;
|
||||
use mas_storage::{user::UserEmailRepository, Clock, Repository};
|
||||
use mas_storage::{user::UserEmailRepository, BoxClock, BoxRng, Clock, Repository};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{AccountEmailsContext, EmailVerificationContext, TemplateContext, Templates};
|
||||
use rand::{distributions::Uniform, Rng};
|
||||
@ -49,12 +49,12 @@ pub enum ManagementForm {
|
||||
}
|
||||
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
@ -77,7 +77,7 @@ async fn render(
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
repo: &mut impl Repository,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock, rng);
|
||||
|
||||
let emails = repo.user_email().all(&session.user).await?;
|
||||
|
||||
@ -124,13 +124,14 @@ async fn start_email_verification(
|
||||
}
|
||||
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
State(mailer): State<Mailer>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Form(form): Form<ProtectedForm<ManagementForm>>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
@ -144,7 +145,7 @@ pub(crate) async fn post(
|
||||
return Ok((cookie_jar, login.go()).into_response());
|
||||
};
|
||||
|
||||
let form = cookie_jar.verify_form(clock.now(), form)?;
|
||||
let form = cookie_jar.verify_form(&clock, form)?;
|
||||
|
||||
match form {
|
||||
ManagementForm::Add { email } => {
|
||||
|
@ -24,7 +24,7 @@ use mas_axum_utils::{
|
||||
};
|
||||
use mas_keystore::Encrypter;
|
||||
use mas_router::Route;
|
||||
use mas_storage::{user::UserEmailRepository, Clock, Repository, SystemClock};
|
||||
use mas_storage::{user::UserEmailRepository, BoxClock, BoxRng, Repository};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{EmailVerificationPageContext, TemplateContext, Templates};
|
||||
use serde::Deserialize;
|
||||
@ -39,16 +39,17 @@ pub struct CodeForm {
|
||||
}
|
||||
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
Query(query): Query<OptionalPostAuthAction>,
|
||||
Path(id): Path<Ulid>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
let maybe_session = session_info.load_session(&mut repo).await?;
|
||||
@ -83,16 +84,16 @@ pub(crate) async fn get(
|
||||
}
|
||||
|
||||
pub(crate) async fn post(
|
||||
clock: BoxClock,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Query(query): Query<OptionalPostAuthAction>,
|
||||
Path(id): Path<Ulid>,
|
||||
Form(form): Form<ProtectedForm<CodeForm>>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let clock = SystemClock::default();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let form = cookie_jar.verify_form(clock.now(), form)?;
|
||||
let form = cookie_jar.verify_form(&clock, form)?;
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
let maybe_session = session_info.load_session(&mut repo).await?;
|
||||
|
@ -25,21 +25,22 @@ use mas_keystore::Encrypter;
|
||||
use mas_router::Route;
|
||||
use mas_storage::{
|
||||
user::{BrowserSessionRepository, UserEmailRepository},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{AccountContext, TemplateContext, Templates};
|
||||
use sqlx::PgPool;
|
||||
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
let maybe_session = session_info.load_session(&mut repo).await?;
|
||||
|
@ -27,7 +27,7 @@ use mas_keystore::Encrypter;
|
||||
use mas_router::Route;
|
||||
use mas_storage::{
|
||||
user::{BrowserSessionRepository, UserPasswordRepository},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Clock, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{EmptyContext, TemplateContext, Templates};
|
||||
@ -46,11 +46,12 @@ pub struct ChangeForm {
|
||||
}
|
||||
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
@ -72,7 +73,7 @@ async fn render(
|
||||
session: BrowserSession,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock, rng);
|
||||
|
||||
let ctx = EmptyContext
|
||||
.with_session(session)
|
||||
@ -84,16 +85,17 @@ async fn render(
|
||||
}
|
||||
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(password_manager): State<PasswordManager>,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Form(form): Form<ProtectedForm<ChangeForm>>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let form = cookie_jar.verify_form(clock.now(), form)?;
|
||||
let form = cookie_jar.verify_form(&clock, form)?;
|
||||
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
|
@ -20,21 +20,22 @@ use axum_extra::extract::PrivateCookieJar;
|
||||
use mas_axum_utils::{csrf::CsrfExt, FancyError, SessionInfoExt};
|
||||
use mas_keystore::Encrypter;
|
||||
use mas_router::UrlBuilder;
|
||||
use mas_storage::Clock;
|
||||
use mas_storage::{BoxClock, BoxRng};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{IndexContext, TemplateContext, Templates};
|
||||
use sqlx::PgPool;
|
||||
|
||||
pub async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(templates): State<Templates>,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
) -> Result<impl IntoResponse, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
let session = session_info.load_session(&mut repo).await?;
|
||||
|
||||
|
@ -26,7 +26,7 @@ use mas_keystore::Encrypter;
|
||||
use mas_storage::{
|
||||
upstream_oauth2::UpstreamOAuthProviderRepository,
|
||||
user::{BrowserSessionRepository, UserPasswordRepository, UserRepository},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Clock, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{
|
||||
@ -51,15 +51,16 @@ impl ToFormState for LoginForm {
|
||||
}
|
||||
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
Query(query): Query<OptionalPostAuthAction>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
let maybe_session = session_info.load_session(&mut repo).await?;
|
||||
@ -83,6 +84,8 @@ pub(crate) async fn get(
|
||||
}
|
||||
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(password_manager): State<PasswordManager>,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
@ -90,12 +93,11 @@ pub(crate) async fn post(
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Form(form): Form<ProtectedForm<LoginForm>>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let form = cookie_jar.verify_form(clock.now(), form)?;
|
||||
let form = cookie_jar.verify_form(&clock, form)?;
|
||||
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
|
||||
// Validate the form
|
||||
let state = {
|
||||
|
@ -23,19 +23,19 @@ use mas_axum_utils::{
|
||||
};
|
||||
use mas_keystore::Encrypter;
|
||||
use mas_router::{PostAuthAction, Route};
|
||||
use mas_storage::{user::BrowserSessionRepository, Clock, Repository, SystemClock};
|
||||
use mas_storage::{user::BrowserSessionRepository, BoxClock, Repository};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use sqlx::PgPool;
|
||||
|
||||
pub(crate) async fn post(
|
||||
clock: BoxClock,
|
||||
State(pool): State<PgPool>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Form(form): Form<ProtectedForm<Option<PostAuthAction>>>,
|
||||
) -> Result<impl IntoResponse, FancyError> {
|
||||
let clock = SystemClock::default();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let form = cookie_jar.verify_form(clock.now(), form)?;
|
||||
let form = cookie_jar.verify_form(&clock, form)?;
|
||||
|
||||
let (session_info, mut cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
|
@ -26,7 +26,7 @@ use mas_keystore::Encrypter;
|
||||
use mas_router::Route;
|
||||
use mas_storage::{
|
||||
user::{BrowserSessionRepository, UserPasswordRepository},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{ReauthContext, TemplateContext, Templates};
|
||||
@ -43,15 +43,16 @@ pub(crate) struct ReauthForm {
|
||||
}
|
||||
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
Query(query): Query<OptionalPostAuthAction>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
let maybe_session = session_info.load_session(&mut repo).await?;
|
||||
@ -80,16 +81,17 @@ pub(crate) async fn get(
|
||||
}
|
||||
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(password_manager): State<PasswordManager>,
|
||||
State(pool): State<PgPool>,
|
||||
Query(query): Query<OptionalPostAuthAction>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Form(form): Form<ProtectedForm<ReauthForm>>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let form = cookie_jar.verify_form(clock.now(), form)?;
|
||||
let form = cookie_jar.verify_form(&clock, form)?;
|
||||
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
|
@ -33,7 +33,7 @@ use mas_policy::PolicyFactory;
|
||||
use mas_router::Route;
|
||||
use mas_storage::{
|
||||
user::{BrowserSessionRepository, UserEmailRepository, UserPasswordRepository, UserRepository},
|
||||
Clock, Repository,
|
||||
BoxClock, BoxRng, Repository,
|
||||
};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use mas_templates::{
|
||||
@ -61,15 +61,16 @@ impl ToFormState for RegisterForm {
|
||||
}
|
||||
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(templates): State<Templates>,
|
||||
State(pool): State<PgPool>,
|
||||
Query(query): Query<OptionalPostAuthAction>,
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
let maybe_session = session_info.load_session(&mut repo).await?;
|
||||
@ -93,6 +94,8 @@ pub(crate) async fn get(
|
||||
|
||||
#[allow(clippy::too_many_lines, clippy::too_many_arguments)]
|
||||
pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
State(password_manager): State<PasswordManager>,
|
||||
State(mailer): State<Mailer>,
|
||||
State(policy_factory): State<Arc<PolicyFactory>>,
|
||||
@ -102,12 +105,11 @@ pub(crate) async fn post(
|
||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
||||
Form(form): Form<ProtectedForm<RegisterForm>>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let (clock, mut rng) = crate::clock_and_rng();
|
||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||
|
||||
let form = cookie_jar.verify_form(clock.now(), form)?;
|
||||
let form = cookie_jar.verify_form(&clock, form)?;
|
||||
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
|
||||
// Validate the form
|
||||
let state = {
|
||||
|
@ -30,7 +30,7 @@ use oauth2_types::{
|
||||
requests::GrantType,
|
||||
scope::{Scope, ScopeToken},
|
||||
};
|
||||
use rand::{Rng, RngCore};
|
||||
use rand::RngCore;
|
||||
use sqlx::PgConnection;
|
||||
use tracing::{info_span, Instrument};
|
||||
use ulid::Ulid;
|
||||
@ -534,7 +534,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> {
|
||||
)]
|
||||
async fn add_from_config(
|
||||
&mut self,
|
||||
mut rng: impl Rng + Send,
|
||||
rng: &mut (dyn RngCore + Send),
|
||||
clock: &dyn Clock,
|
||||
client_id: Ulid,
|
||||
client_auth_method: OAuthClientAuthenticationMethod,
|
||||
@ -597,7 +597,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> {
|
||||
.iter()
|
||||
.map(|uri| {
|
||||
(
|
||||
Uuid::from(Ulid::from_datetime_with_source(now.into(), &mut rng)),
|
||||
Uuid::from(Ulid::from_datetime_with_source(now.into(), &mut *rng)),
|
||||
uri.as_str().to_owned(),
|
||||
)
|
||||
})
|
||||
|
@ -10,7 +10,7 @@ async-trait = "0.1.60"
|
||||
chrono = "0.4.23"
|
||||
thiserror = "1.0.38"
|
||||
|
||||
rand = "0.8.5"
|
||||
rand_core = "0.6.4"
|
||||
url = "2.3.1"
|
||||
ulid = "1.0.0"
|
||||
|
||||
|
@ -28,6 +28,12 @@ pub trait Clock: Sync {
|
||||
fn now(&self) -> DateTime<Utc>;
|
||||
}
|
||||
|
||||
impl<C: Clock + ?Sized> Clock for Box<C> {
|
||||
fn now(&self) -> DateTime<Utc> {
|
||||
(**self).now()
|
||||
}
|
||||
}
|
||||
|
||||
/// A clock which uses the system time
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SystemClock {
|
||||
|
@ -15,7 +15,7 @@
|
||||
use async_trait::async_trait;
|
||||
use chrono::Duration;
|
||||
use mas_data_model::{CompatAccessToken, CompatSession};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::Clock;
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::{CompatAccessToken, CompatRefreshToken, CompatSession};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::Clock;
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::{CompatSession, Device, User};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::Clock;
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::{CompatSession, CompatSsoLogin, User};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
use url::Url;
|
||||
|
||||
|
@ -37,8 +37,13 @@ pub(crate) mod repository;
|
||||
pub mod upstream_oauth2;
|
||||
pub mod user;
|
||||
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
pub use self::{
|
||||
clock::{Clock, SystemClock},
|
||||
pagination::{Page, Pagination},
|
||||
repository::Repository,
|
||||
};
|
||||
|
||||
pub type BoxClock = Box<dyn Clock + Send>;
|
||||
pub type BoxRng = Box<dyn CryptoRngCore + Send>;
|
||||
|
@ -15,7 +15,7 @@
|
||||
use async_trait::async_trait;
|
||||
use chrono::Duration;
|
||||
use mas_data_model::{AccessToken, Session};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::Clock;
|
||||
|
@ -17,7 +17,7 @@ use std::num::NonZeroU32;
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::{AuthorizationCode, AuthorizationGrant, Client, Session};
|
||||
use oauth2_types::{requests::ResponseMode, scope::Scope};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
use url::Url;
|
||||
|
||||
|
@ -19,7 +19,7 @@ use mas_data_model::{Client, User};
|
||||
use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod};
|
||||
use mas_jose::jwk::PublicJsonWebKeySet;
|
||||
use oauth2_types::{requests::GrantType, scope::Scope};
|
||||
use rand::{Rng, RngCore};
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
use url::Url;
|
||||
|
||||
@ -67,7 +67,7 @@ pub trait OAuth2ClientRepository: Send + Sync {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn add_from_config(
|
||||
&mut self,
|
||||
mut rng: impl Rng + Send,
|
||||
rng: &mut (dyn RngCore + Send),
|
||||
clock: &dyn Clock,
|
||||
client_id: Ulid,
|
||||
client_auth_method: OAuthClientAuthenticationMethod,
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::{AccessToken, RefreshToken, Session};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::Clock;
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::{AuthorizationGrant, BrowserSession, Session, User};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::{pagination::Page, Clock, Pagination};
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::{UpstreamOAuthLink, UpstreamOAuthProvider, User};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::{pagination::Page, Clock, Pagination};
|
||||
|
@ -16,7 +16,7 @@ use async_trait::async_trait;
|
||||
use mas_data_model::UpstreamOAuthProvider;
|
||||
use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod};
|
||||
use oauth2_types::scope::Scope;
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::{pagination::Page, Clock, Pagination};
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::{UpstreamOAuthAuthorizationSession, UpstreamOAuthLink, UpstreamOAuthProvider};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::Clock;
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::{User, UserEmail, UserEmailVerification};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::{pagination::Page, Clock, Pagination};
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::User;
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::Clock;
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::{Password, User};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
|
||||
use crate::Clock;
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_data_model::{BrowserSession, Password, UpstreamOAuthLink, User};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::{pagination::Page, Clock, Pagination};
|
||||
|
Reference in New Issue
Block a user