summaryrefslogtreecommitdiff
path: root/src/invite
diff options
context:
space:
mode:
Diffstat (limited to 'src/invite')
-rw-r--r--src/invite/app.rs50
-rw-r--r--src/invite/handlers/accept/test.rs28
-rw-r--r--src/invite/handlers/issue/mod.rs2
-rw-r--r--src/invite/handlers/issue/test.rs2
-rw-r--r--src/invite/repo.rs3
5 files changed, 57 insertions, 28 deletions
diff --git a/src/invite/app.rs b/src/invite/app.rs
index 1c85562..6684d03 100644
--- a/src/invite/app.rs
+++ b/src/invite/app.rs
@@ -5,13 +5,15 @@ use super::{Id, Invite, Summary, repo::Provider as _};
use crate::{
clock::DateTime,
db::{Duplicate as _, NotFound as _},
- event::Broadcaster,
- name::Name,
+ event::{Broadcaster, repo::Provider as _},
+ login::Login,
+ name::{self, Name},
password::Password,
- token::{Secret, repo::Provider as _},
+ token::{Secret, Token, repo::Provider as _},
user::{
- User,
+ self,
create::{self, Create},
+ repo::{LoadError, Provider as _},
},
};
@@ -25,9 +27,19 @@ impl<'a> Invites<'a> {
Self { db, events }
}
- pub async fn issue(&self, issuer: &User, issued_at: &DateTime) -> Result<Invite, sqlx::Error> {
+ pub async fn issue(&self, issuer: &Login, issued_at: &DateTime) -> Result<Invite, Error> {
+ let issuer_not_found = || Error::IssuerNotFound(issuer.id.clone().into());
+ let issuer_deleted = || Error::IssuerDeleted(issuer.id.clone().into());
+
let mut tx = self.db.begin().await?;
- let invite = tx.invites().create(issuer, issued_at).await?;
+ let issuer = tx
+ .users()
+ .by_login(issuer)
+ .await
+ .not_found(issuer_not_found)?;
+ let now = tx.sequence().current().await?;
+ let issuer = issuer.as_of(now).ok_or_else(issuer_deleted)?;
+ let invite = tx.invites().create(&issuer, issued_at).await?;
tx.commit().await?;
Ok(invite)
@@ -71,7 +83,9 @@ impl<'a> Invites<'a> {
.store(&mut tx)
.await
.duplicate(|| AcceptError::DuplicateLogin(name.clone()))?;
- let secret = tx.tokens().issue(stored.user(), accepted_at).await?;
+ let login = stored.login();
+ let (token, secret) = Token::generate(login, accepted_at);
+ tx.tokens().create(&token, &secret).await?;
tx.commit().await?;
stored.publish(self.events);
@@ -92,6 +106,28 @@ impl<'a> Invites<'a> {
}
#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ #[error("issuing user {0} not found")]
+ IssuerNotFound(user::Id),
+ #[error("issuing user {0} deleted")]
+ IssuerDeleted(user::Id),
+ #[error(transparent)]
+ Database(#[from] sqlx::Error),
+ #[error(transparent)]
+ Name(#[from] name::Error),
+}
+
+impl From<user::repo::LoadError> for Error {
+ fn from(error: LoadError) -> Self {
+ use user::repo::LoadError;
+ match error {
+ LoadError::Database(error) => error.into(),
+ LoadError::Name(error) => error.into(),
+ }
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
pub enum AcceptError {
#[error("invite not found: {0}")]
NotFound(Id),
diff --git a/src/invite/handlers/accept/test.rs b/src/invite/handlers/accept/test.rs
index adc7aa4..283ec76 100644
--- a/src/invite/handlers/accept/test.rs
+++ b/src/invite/handlers/accept/test.rs
@@ -1,6 +1,11 @@
use axum::extract::{Json, Path, State};
-use crate::{empty::Empty, invite::app::AcceptError, name::Name, test::fixtures};
+use crate::{
+ empty::Empty,
+ invite::app::AcceptError,
+ name::Name,
+ test::{fixtures, verify},
+};
#[tokio::test]
async fn valid_invite() {
@@ -34,29 +39,16 @@ async fn valid_invite() {
// Verify that the issued token is valid
- let secret = identity
- .secret()
- .expect("newly-issued identity has a token secret");
- let (_, login) = app
- .tokens()
- .validate(&secret, &fixtures::now())
- .await
- .expect("newly-issued identity cookie is valid");
- assert_eq!(name, login.name);
+ verify::identity::valid_for_name(&app, &identity, &name).await;
// Verify that the given credentials can log in
let secret = app
- .tokens()
- .login(&name, &password, &fixtures::now())
+ .logins()
+ .with_password(&name, &password, &fixtures::now())
.await
.expect("credentials given on signup are valid");
- let (_, login) = app
- .tokens()
- .validate(&secret, &fixtures::now())
- .await
- .expect("validating a newly-issued token secret succeeds");
- assert_eq!(name, login.name);
+ verify::token::valid_for_name(&app, &secret, &name).await;
}
#[tokio::test]
diff --git a/src/invite/handlers/issue/mod.rs b/src/invite/handlers/issue/mod.rs
index 6085f7a..4ac74cc 100644
--- a/src/invite/handlers/issue/mod.rs
+++ b/src/invite/handlers/issue/mod.rs
@@ -13,7 +13,7 @@ pub async fn handler(
identity: Identity,
_: Json<Request>,
) -> Result<Json<Invite>, Internal> {
- let invite = app.invites().issue(&identity.user, &issued_at).await?;
+ let invite = app.invites().issue(&identity.login, &issued_at).await?;
Ok(Json(invite))
}
diff --git a/src/invite/handlers/issue/test.rs b/src/invite/handlers/issue/test.rs
index 2bf5400..4421705 100644
--- a/src/invite/handlers/issue/test.rs
+++ b/src/invite/handlers/issue/test.rs
@@ -22,6 +22,6 @@ async fn create_invite() {
.expect("creating an invite always succeeds");
// Verify the response
- assert_eq!(issuer.user.id, invite.issuer);
+ assert_eq!(issuer.login.id, invite.issuer);
assert_eq!(&*issued_at, &invite.issued_at);
}
diff --git a/src/invite/repo.rs b/src/invite/repo.rs
index 7cfa18e..934b0ce 100644
--- a/src/invite/repo.rs
+++ b/src/invite/repo.rs
@@ -71,10 +71,11 @@ impl Invites<'_> {
select
invite.id as "invite_id: Id",
issuer.id as "issuer_id: user::Id",
- issuer.display_name as "issuer_name: nfc::String",
+ issuer_login.display_name as "issuer_name: nfc::String",
invite.issued_at as "invite_issued_at: DateTime"
from invite
join user as issuer on (invite.issuer = issuer.id)
+ join login as issuer_login on (issuer.id = issuer_login.id)
where invite.id = $1
"#,
invite,