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
storage: test compat {session, access token, refresh token} repositories
This commit is contained in:
@ -23,3 +23,296 @@ pub use self::{
|
|||||||
session::{CompatSessionRepository, PgCompatSessionRepository},
|
session::{CompatSessionRepository, PgCompatSessionRepository},
|
||||||
sso_login::{CompatSsoLoginRepository, PgCompatSsoLoginRepository},
|
sso_login::{CompatSsoLoginRepository, PgCompatSsoLoginRepository},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use chrono::Duration;
|
||||||
|
use mas_data_model::Device;
|
||||||
|
use rand::SeedableRng;
|
||||||
|
use rand_chacha::ChaChaRng;
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::{user::UserRepository, Clock, PgRepository, Repository};
|
||||||
|
|
||||||
|
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||||
|
async fn test_session_repository(pool: PgPool) {
|
||||||
|
const FIRST_TOKEN: &str = "first_access_token";
|
||||||
|
const SECOND_TOKEN: &str = "second_access_token";
|
||||||
|
let mut rng = ChaChaRng::seed_from_u64(42);
|
||||||
|
let clock = Clock::mock();
|
||||||
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
|
|
||||||
|
// Create a user
|
||||||
|
let user = repo
|
||||||
|
.user()
|
||||||
|
.add(&mut rng, &clock, "john".to_owned())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Start a compat session for that user
|
||||||
|
let device = Device::generate(&mut rng);
|
||||||
|
let device_str = device.as_str().to_owned();
|
||||||
|
let session = repo
|
||||||
|
.compat_session()
|
||||||
|
.add(&mut rng, &clock, &user, device)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(session.user_id, user.id);
|
||||||
|
assert_eq!(session.device.as_str(), device_str);
|
||||||
|
assert!(session.is_valid());
|
||||||
|
assert!(!session.is_finished());
|
||||||
|
|
||||||
|
// Lookup the session and check it didn't change
|
||||||
|
let session_lookup = repo
|
||||||
|
.compat_session()
|
||||||
|
.lookup(session.id)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.expect("compat session not found");
|
||||||
|
assert_eq!(session_lookup.id, session.id);
|
||||||
|
assert_eq!(session_lookup.user_id, user.id);
|
||||||
|
assert_eq!(session_lookup.device.as_str(), device_str);
|
||||||
|
assert!(session_lookup.is_valid());
|
||||||
|
assert!(!session_lookup.is_finished());
|
||||||
|
|
||||||
|
// Finish the session
|
||||||
|
let session = repo.compat_session().finish(&clock, session).await.unwrap();
|
||||||
|
assert!(!session.is_valid());
|
||||||
|
assert!(session.is_finished());
|
||||||
|
|
||||||
|
// Reload the session and check again
|
||||||
|
let session_lookup = repo
|
||||||
|
.compat_session()
|
||||||
|
.lookup(session.id)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.expect("compat session not found");
|
||||||
|
assert!(!session_lookup.is_valid());
|
||||||
|
assert!(session_lookup.is_finished());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||||
|
async fn test_access_token_repository(pool: PgPool) {
|
||||||
|
const FIRST_TOKEN: &str = "first_access_token";
|
||||||
|
const SECOND_TOKEN: &str = "second_access_token";
|
||||||
|
let mut rng = ChaChaRng::seed_from_u64(42);
|
||||||
|
let clock = Clock::mock();
|
||||||
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
|
|
||||||
|
// Create a user
|
||||||
|
let user = repo
|
||||||
|
.user()
|
||||||
|
.add(&mut rng, &clock, "john".to_owned())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Start a compat session for that user
|
||||||
|
let device = Device::generate(&mut rng);
|
||||||
|
let session = repo
|
||||||
|
.compat_session()
|
||||||
|
.add(&mut rng, &clock, &user, device)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Add an access token to that session
|
||||||
|
let token = repo
|
||||||
|
.compat_access_token()
|
||||||
|
.add(
|
||||||
|
&mut rng,
|
||||||
|
&clock,
|
||||||
|
&session,
|
||||||
|
FIRST_TOKEN.to_owned(),
|
||||||
|
Some(Duration::minutes(1)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(token.session_id, session.id);
|
||||||
|
assert_eq!(token.token, FIRST_TOKEN);
|
||||||
|
|
||||||
|
// Commit the txn and grab a new transaction, to test a conflict
|
||||||
|
repo.save().await.unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
|
// Adding the same token a second time should conflict
|
||||||
|
assert!(repo
|
||||||
|
.compat_access_token()
|
||||||
|
.add(
|
||||||
|
&mut rng,
|
||||||
|
&clock,
|
||||||
|
&session,
|
||||||
|
FIRST_TOKEN.to_owned(),
|
||||||
|
Some(Duration::minutes(1)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_err());
|
||||||
|
repo.cancel().await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab a new repo
|
||||||
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
|
|
||||||
|
// Looking up via ID works
|
||||||
|
let token_lookup = repo
|
||||||
|
.compat_access_token()
|
||||||
|
.lookup(token.id)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.expect("compat access token not found");
|
||||||
|
assert_eq!(token.id, token_lookup.id);
|
||||||
|
assert_eq!(token_lookup.session_id, session.id);
|
||||||
|
|
||||||
|
// Looking up via the token value works
|
||||||
|
let token_lookup = repo
|
||||||
|
.compat_access_token()
|
||||||
|
.find_by_token(FIRST_TOKEN)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.expect("compat access token not found");
|
||||||
|
assert_eq!(token.id, token_lookup.id);
|
||||||
|
assert_eq!(token_lookup.session_id, session.id);
|
||||||
|
|
||||||
|
// Token is currently valid
|
||||||
|
assert!(token.is_valid(clock.now()));
|
||||||
|
|
||||||
|
clock.advance(Duration::minutes(1));
|
||||||
|
// Token should have expired
|
||||||
|
assert!(!token.is_valid(clock.now()));
|
||||||
|
|
||||||
|
// Add a second access token, this time without expiration
|
||||||
|
let token = repo
|
||||||
|
.compat_access_token()
|
||||||
|
.add(&mut rng, &clock, &session, SECOND_TOKEN.to_owned(), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(token.session_id, session.id);
|
||||||
|
assert_eq!(token.token, SECOND_TOKEN);
|
||||||
|
|
||||||
|
// Token is currently valid
|
||||||
|
assert!(token.is_valid(clock.now()));
|
||||||
|
|
||||||
|
// Make it expire
|
||||||
|
repo.compat_access_token()
|
||||||
|
.expire(&clock, token)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Reload it
|
||||||
|
let token = repo
|
||||||
|
.compat_access_token()
|
||||||
|
.find_by_token(SECOND_TOKEN)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.expect("compat access token not found");
|
||||||
|
|
||||||
|
// Token is not valid anymore
|
||||||
|
assert!(!token.is_valid(clock.now()));
|
||||||
|
|
||||||
|
repo.save().await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||||
|
async fn test_refresh_token_repository(pool: PgPool) {
|
||||||
|
const ACCESS_TOKEN: &str = "access_token";
|
||||||
|
const REFRESH_TOKEN: &str = "refresh_token";
|
||||||
|
let mut rng = ChaChaRng::seed_from_u64(42);
|
||||||
|
let clock = Clock::mock();
|
||||||
|
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||||
|
|
||||||
|
// Create a user
|
||||||
|
let user = repo
|
||||||
|
.user()
|
||||||
|
.add(&mut rng, &clock, "john".to_owned())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Start a compat session for that user
|
||||||
|
let device = Device::generate(&mut rng);
|
||||||
|
let session = repo
|
||||||
|
.compat_session()
|
||||||
|
.add(&mut rng, &clock, &user, device)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Add an access token to that session
|
||||||
|
let access_token = repo
|
||||||
|
.compat_access_token()
|
||||||
|
.add(&mut rng, &clock, &session, ACCESS_TOKEN.to_owned(), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let refresh_token = repo
|
||||||
|
.compat_refresh_token()
|
||||||
|
.add(
|
||||||
|
&mut rng,
|
||||||
|
&clock,
|
||||||
|
&session,
|
||||||
|
&access_token,
|
||||||
|
REFRESH_TOKEN.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(refresh_token.session_id, session.id);
|
||||||
|
assert_eq!(refresh_token.access_token_id, access_token.id);
|
||||||
|
assert_eq!(refresh_token.token, REFRESH_TOKEN);
|
||||||
|
assert!(refresh_token.is_valid());
|
||||||
|
assert!(!refresh_token.is_consumed());
|
||||||
|
|
||||||
|
// Look it up by ID and check everything matches
|
||||||
|
let refresh_token_lookup = repo
|
||||||
|
.compat_refresh_token()
|
||||||
|
.lookup(refresh_token.id)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.expect("refresh token not found");
|
||||||
|
assert_eq!(refresh_token_lookup.id, refresh_token.id);
|
||||||
|
assert_eq!(refresh_token_lookup.session_id, session.id);
|
||||||
|
assert_eq!(refresh_token_lookup.access_token_id, access_token.id);
|
||||||
|
assert_eq!(refresh_token_lookup.token, REFRESH_TOKEN);
|
||||||
|
assert!(refresh_token_lookup.is_valid());
|
||||||
|
assert!(!refresh_token_lookup.is_consumed());
|
||||||
|
|
||||||
|
// Look it up by token and check everything matches
|
||||||
|
let refresh_token_lookup = repo
|
||||||
|
.compat_refresh_token()
|
||||||
|
.find_by_token(REFRESH_TOKEN)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.expect("refresh token not found");
|
||||||
|
assert_eq!(refresh_token_lookup.id, refresh_token.id);
|
||||||
|
assert_eq!(refresh_token_lookup.session_id, session.id);
|
||||||
|
assert_eq!(refresh_token_lookup.access_token_id, access_token.id);
|
||||||
|
assert_eq!(refresh_token_lookup.token, REFRESH_TOKEN);
|
||||||
|
assert!(refresh_token_lookup.is_valid());
|
||||||
|
assert!(!refresh_token_lookup.is_consumed());
|
||||||
|
|
||||||
|
// Consume it
|
||||||
|
let refresh_token = repo
|
||||||
|
.compat_refresh_token()
|
||||||
|
.consume(&clock, refresh_token)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(!refresh_token.is_valid());
|
||||||
|
assert!(refresh_token.is_consumed());
|
||||||
|
|
||||||
|
// Reload it and check again
|
||||||
|
let refresh_token_lookup = repo
|
||||||
|
.compat_refresh_token()
|
||||||
|
.find_by_token(REFRESH_TOKEN)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.expect("refresh token not found");
|
||||||
|
assert!(!refresh_token_lookup.is_valid());
|
||||||
|
assert!(refresh_token_lookup.is_consumed());
|
||||||
|
|
||||||
|
// Consuming it again should not work
|
||||||
|
assert!(repo
|
||||||
|
.compat_refresh_token()
|
||||||
|
.consume(&clock, refresh_token)
|
||||||
|
.await
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
repo.save().await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -29,12 +29,12 @@ mod tests {
|
|||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Clock, PgRepository, Repository};
|
use crate::{user::UserRepository, Clock, PgRepository, Repository};
|
||||||
|
|
||||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||||
async fn test_repository(pool: PgPool) -> Result<(), Box<dyn std::error::Error>> {
|
async fn test_repository(pool: PgPool) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);
|
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);
|
||||||
let clock = Clock::default();
|
let clock = Clock::mock();
|
||||||
let mut repo = PgRepository::from_pool(&pool).await?;
|
let mut repo = PgRepository::from_pool(&pool).await?;
|
||||||
|
|
||||||
// The provider list should be empty at the start
|
// The provider list should be empty at the start
|
||||||
@ -115,6 +115,12 @@ mod tests {
|
|||||||
.upstream_oauth_session()
|
.upstream_oauth_session()
|
||||||
.complete_with_link(&clock, session, &link, None)
|
.complete_with_link(&clock, session, &link, None)
|
||||||
.await?;
|
.await?;
|
||||||
|
// Reload the session
|
||||||
|
let session = repo
|
||||||
|
.upstream_oauth_session()
|
||||||
|
.lookup(session.id)
|
||||||
|
.await?
|
||||||
|
.expect("session to be found in the database");
|
||||||
assert!(session.is_completed());
|
assert!(session.is_completed());
|
||||||
assert!(!session.is_consumed());
|
assert!(!session.is_consumed());
|
||||||
assert_eq!(session.link_id(), Some(link.id));
|
assert_eq!(session.link_id(), Some(link.id));
|
||||||
@ -123,8 +129,29 @@ mod tests {
|
|||||||
.upstream_oauth_session()
|
.upstream_oauth_session()
|
||||||
.consume(&clock, session)
|
.consume(&clock, session)
|
||||||
.await?;
|
.await?;
|
||||||
|
// Reload the session
|
||||||
|
let session = repo
|
||||||
|
.upstream_oauth_session()
|
||||||
|
.lookup(session.id)
|
||||||
|
.await?
|
||||||
|
.expect("session to be found in the database");
|
||||||
assert!(session.is_consumed());
|
assert!(session.is_consumed());
|
||||||
|
|
||||||
|
let user = repo.user().add(&mut rng, &clock, "john".to_owned()).await?;
|
||||||
|
repo.upstream_oauth_link()
|
||||||
|
.associate_to_user(&link, &user)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let links = repo
|
||||||
|
.upstream_oauth_link()
|
||||||
|
.list_paginated(&user, None, None, Some(10), None)
|
||||||
|
.await?;
|
||||||
|
assert!(!links.has_previous_page);
|
||||||
|
assert!(!links.has_next_page);
|
||||||
|
assert_eq!(links.edges.len(), 1);
|
||||||
|
assert_eq!(links.edges[0].id, link.id);
|
||||||
|
assert_eq!(links.edges[0].user_id, Some(user.id));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user