You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
data-model: simplify users and sessions
This commit is contained in:
@ -13,6 +13,7 @@ url = { version = "2.3.1", features = ["serde"] }
|
||||
crc = "3.0.0"
|
||||
rand = "0.8.5"
|
||||
ulid = "1.0.0"
|
||||
rand_chacha = "0.3.1"
|
||||
|
||||
mas-iana = { path = "../iana" }
|
||||
mas-jose = { path = "../jose" }
|
||||
|
@ -86,7 +86,7 @@ impl TryFrom<String> for Device {
|
||||
pub struct CompatSession<T: StorageBackend> {
|
||||
#[serde(skip_serializing)]
|
||||
pub data: T::CompatSessionData,
|
||||
pub user: User<T>,
|
||||
pub user: User,
|
||||
pub device: Device,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub finished_at: Option<DateTime<Utc>>,
|
||||
@ -96,7 +96,7 @@ impl<S: StorageBackendMarker> From<CompatSession<S>> for CompatSession<()> {
|
||||
fn from(t: CompatSession<S>) -> Self {
|
||||
Self {
|
||||
data: (),
|
||||
user: t.user.into(),
|
||||
user: t.user,
|
||||
device: t.device,
|
||||
created_at: t.created_at,
|
||||
finished_at: t.finished_at,
|
||||
@ -125,7 +125,7 @@ impl<S: StorageBackendMarker> From<CompatAccessToken<S>> for CompatAccessToken<(
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct CompatRefreshToken<T: StorageBackend> {
|
||||
pub data: T::RefreshTokenData,
|
||||
pub data: T::CompatRefreshTokenData,
|
||||
pub token: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ use crate::{
|
||||
pub struct Session<T: StorageBackend> {
|
||||
#[serde(skip_serializing)]
|
||||
pub data: T::SessionData,
|
||||
pub browser_session: BrowserSession<T>,
|
||||
pub browser_session: BrowserSession,
|
||||
pub client: Client<T>,
|
||||
pub scope: Scope,
|
||||
}
|
||||
@ -35,7 +35,7 @@ impl<S: StorageBackendMarker> From<Session<S>> for Session<()> {
|
||||
fn from(s: Session<S>) -> Self {
|
||||
Session {
|
||||
data: (),
|
||||
browser_session: s.browser_session.into(),
|
||||
browser_session: s.browser_session,
|
||||
client: s.client.into(),
|
||||
scope: s.scope,
|
||||
}
|
||||
|
@ -30,16 +30,9 @@ impl<T: Clone + Debug + PartialEq + Serialize + DeserializeOwned + Default + Syn
|
||||
}
|
||||
|
||||
pub trait StorageBackend {
|
||||
type UserData: Data;
|
||||
type UserEmailData: Data;
|
||||
type UserEmailVerificationData: Data;
|
||||
type AuthenticationData: Data;
|
||||
type BrowserSessionData: Data;
|
||||
type ClientData: Data;
|
||||
type SessionData: Data;
|
||||
type AuthorizationGrantData: Data;
|
||||
type AccessTokenData: Data;
|
||||
type RefreshTokenData: Data;
|
||||
type CompatAccessTokenData: Data;
|
||||
type CompatRefreshTokenData: Data;
|
||||
type CompatSessionData: Data;
|
||||
@ -47,18 +40,11 @@ pub trait StorageBackend {
|
||||
}
|
||||
|
||||
impl StorageBackend for () {
|
||||
type AccessTokenData = ();
|
||||
type AuthenticationData = ();
|
||||
type AuthorizationGrantData = ();
|
||||
type BrowserSessionData = ();
|
||||
type ClientData = ();
|
||||
type CompatAccessTokenData = ();
|
||||
type CompatRefreshTokenData = ();
|
||||
type CompatSessionData = ();
|
||||
type CompatSsoLoginData = ();
|
||||
type RefreshTokenData = ();
|
||||
type SessionData = ();
|
||||
type UserData = ();
|
||||
type UserEmailData = ();
|
||||
type UserEmailVerificationData = ();
|
||||
}
|
||||
|
@ -13,27 +13,23 @@
|
||||
// limitations under the License.
|
||||
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use rand::{Rng, SeedableRng};
|
||||
use serde::Serialize;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::traits::{StorageBackend, StorageBackendMarker};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[serde(bound = "T: StorageBackend")]
|
||||
pub struct User<T: StorageBackend> {
|
||||
pub data: T::UserData,
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
pub struct User {
|
||||
pub id: Ulid,
|
||||
pub username: String,
|
||||
pub sub: String,
|
||||
pub primary_email: Option<UserEmail<T>>,
|
||||
pub primary_email: Option<UserEmail>,
|
||||
}
|
||||
|
||||
impl<T: StorageBackend> User<T>
|
||||
where
|
||||
T::UserData: Default,
|
||||
{
|
||||
impl User {
|
||||
#[must_use]
|
||||
pub fn samples(_now: chrono::DateTime<Utc>) -> Vec<Self> {
|
||||
pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self> {
|
||||
vec![User {
|
||||
data: Default::default(),
|
||||
id: Ulid::from_datetime_with_source(now.into(), rng),
|
||||
username: "john".to_owned(),
|
||||
sub: "123-456".to_owned(),
|
||||
primary_email: None,
|
||||
@ -41,55 +37,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: StorageBackendMarker> From<User<S>> for User<()> {
|
||||
fn from(u: User<S>) -> Self {
|
||||
User {
|
||||
data: (),
|
||||
username: u.username,
|
||||
sub: u.sub,
|
||||
primary_email: u.primary_email.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[serde(bound = "T: StorageBackend")]
|
||||
pub struct Authentication<T: StorageBackend> {
|
||||
#[serde(skip_serializing)]
|
||||
pub data: T::AuthenticationData,
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
pub struct Authentication {
|
||||
pub id: Ulid,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl<S: StorageBackendMarker> From<Authentication<S>> for Authentication<()> {
|
||||
fn from(a: Authentication<S>) -> Self {
|
||||
Authentication {
|
||||
data: (),
|
||||
created_at: a.created_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[serde(bound = "T: StorageBackend")]
|
||||
pub struct BrowserSession<T: StorageBackend> {
|
||||
pub data: T::BrowserSessionData,
|
||||
pub user: User<T>,
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
pub struct BrowserSession {
|
||||
pub id: Ulid,
|
||||
pub user: User,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub last_authentication: Option<Authentication<T>>,
|
||||
pub last_authentication: Option<Authentication>,
|
||||
}
|
||||
|
||||
impl<S: StorageBackendMarker> From<BrowserSession<S>> for BrowserSession<()> {
|
||||
fn from(s: BrowserSession<S>) -> Self {
|
||||
BrowserSession {
|
||||
data: (),
|
||||
user: s.user.into(),
|
||||
created_at: s.created_at,
|
||||
last_authentication: s.last_authentication.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: StorageBackend> BrowserSession<S> {
|
||||
impl BrowserSession {
|
||||
#[must_use]
|
||||
pub fn was_authenticated_after(&self, after: DateTime<Utc>) -> bool {
|
||||
if let Some(auth) = &self.last_authentication {
|
||||
auth.created_at > after
|
||||
@ -99,17 +62,13 @@ impl<S: StorageBackend> BrowserSession<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StorageBackend> BrowserSession<T>
|
||||
where
|
||||
T::BrowserSessionData: Default,
|
||||
T::UserData: Default,
|
||||
{
|
||||
impl BrowserSession {
|
||||
#[must_use]
|
||||
pub fn samples(now: chrono::DateTime<Utc>) -> Vec<Self> {
|
||||
User::<T>::samples(now)
|
||||
pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self> {
|
||||
User::samples(now, rng)
|
||||
.into_iter()
|
||||
.map(|user| BrowserSession {
|
||||
data: Default::default(),
|
||||
id: Ulid::from_datetime_with_source(now.into(), rng),
|
||||
user,
|
||||
created_at: now,
|
||||
last_authentication: None,
|
||||
@ -118,41 +77,26 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[serde(bound = "T: StorageBackend")]
|
||||
pub struct UserEmail<T: StorageBackend> {
|
||||
pub data: T::UserEmailData,
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
pub struct UserEmail {
|
||||
pub id: Ulid,
|
||||
pub email: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub confirmed_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl<S: StorageBackendMarker> From<UserEmail<S>> for UserEmail<()> {
|
||||
fn from(e: UserEmail<S>) -> Self {
|
||||
Self {
|
||||
data: (),
|
||||
email: e.email,
|
||||
created_at: e.created_at,
|
||||
confirmed_at: e.confirmed_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StorageBackend> UserEmail<T>
|
||||
where
|
||||
T::UserEmailData: Default,
|
||||
{
|
||||
impl UserEmail {
|
||||
#[must_use]
|
||||
pub fn samples(now: chrono::DateTime<Utc>) -> Vec<Self> {
|
||||
pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self> {
|
||||
vec![
|
||||
Self {
|
||||
data: T::UserEmailData::default(),
|
||||
id: Ulid::from_datetime_with_source(now.into(), rng),
|
||||
email: "alice@example.com".to_owned(),
|
||||
created_at: now,
|
||||
confirmed_at: Some(now),
|
||||
},
|
||||
Self {
|
||||
data: T::UserEmailData::default(),
|
||||
id: Ulid::from_datetime_with_source(now.into(), rng),
|
||||
email: "bob@example.com".to_owned(),
|
||||
created_at: now,
|
||||
confirmed_at: None,
|
||||
@ -168,34 +112,18 @@ pub enum UserEmailVerificationState {
|
||||
Valid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[serde(bound = "T: StorageBackend")]
|
||||
pub struct UserEmailVerification<T: StorageBackend> {
|
||||
pub data: T::UserEmailVerificationData,
|
||||
pub email: UserEmail<T>,
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
pub struct UserEmailVerification {
|
||||
pub id: Ulid,
|
||||
pub email: UserEmail,
|
||||
pub code: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub state: UserEmailVerificationState,
|
||||
}
|
||||
|
||||
impl<S: StorageBackendMarker> From<UserEmailVerification<S>> for UserEmailVerification<()> {
|
||||
fn from(v: UserEmailVerification<S>) -> Self {
|
||||
Self {
|
||||
data: (),
|
||||
email: v.email.into(),
|
||||
code: v.code,
|
||||
created_at: v.created_at,
|
||||
state: v.state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StorageBackend> UserEmailVerification<T>
|
||||
where
|
||||
T::UserEmailData: Default + Clone,
|
||||
{
|
||||
impl UserEmailVerification {
|
||||
#[must_use]
|
||||
pub fn samples(now: chrono::DateTime<Utc>) -> Vec<Self> {
|
||||
pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self> {
|
||||
let states = [
|
||||
UserEmailVerificationState::AlreadyUsed {
|
||||
when: now - Duration::minutes(5),
|
||||
@ -208,14 +136,18 @@ where
|
||||
|
||||
states
|
||||
.into_iter()
|
||||
.flat_map(|state| {
|
||||
UserEmail::samples(now).into_iter().map(move |email| Self {
|
||||
data: Default::default(),
|
||||
code: "123456".to_owned(),
|
||||
email,
|
||||
created_at: now - Duration::minutes(10),
|
||||
state: state.clone(),
|
||||
})
|
||||
.flat_map(move |state| {
|
||||
let mut rng =
|
||||
rand_chacha::ChaChaRng::from_rng(&mut *rng).expect("could not seed rng");
|
||||
UserEmail::samples(now, &mut rng)
|
||||
.into_iter()
|
||||
.map(move |email| Self {
|
||||
id: Ulid::from_datetime_with_source(now.into(), &mut rng),
|
||||
code: "123456".to_owned(),
|
||||
email,
|
||||
created_at: now - Duration::minutes(10),
|
||||
state: state.clone(),
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
Reference in New Issue
Block a user