summaryrefslogtreecommitdiff
path: root/src/setup
diff options
context:
space:
mode:
Diffstat (limited to 'src/setup')
-rw-r--r--src/setup/app.rs32
-rw-r--r--src/setup/routes/post.rs3
-rw-r--r--src/setup/routes/test.rs25
3 files changed, 51 insertions, 9 deletions
diff --git a/src/setup/app.rs b/src/setup/app.rs
index 030b5f6..c1f7b69 100644
--- a/src/setup/app.rs
+++ b/src/setup/app.rs
@@ -3,8 +3,11 @@ use sqlx::sqlite::SqlitePool;
use super::repo::Provider as _;
use crate::{
clock::DateTime,
- event::{repo::Provider as _, Broadcaster, Event},
- login::{repo::Provider as _, Login, Password},
+ event::Broadcaster,
+ login::{
+ create::{self, Create},
+ Login, Password,
+ },
name::Name,
token::{repo::Provider as _, Secret},
};
@@ -25,20 +28,20 @@ impl<'a> Setup<'a> {
password: &Password,
created_at: &DateTime,
) -> Result<(Login, Secret), Error> {
- let password_hash = password.hash()?;
+ let create = Create::begin(name, password, created_at);
+
+ let validated = create.validate()?;
let mut tx = self.db.begin().await?;
- let login = if tx.setup().completed().await? {
+ let stored = if tx.setup().completed().await? {
Err(Error::SetupCompleted)?
} else {
- let created = tx.sequence().next(created_at).await?;
- tx.logins().create(name, &password_hash, &created).await?
+ validated.store(&mut tx).await?
};
- let secret = tx.tokens().issue(&login, created_at).await?;
+ let secret = tx.tokens().issue(stored.login(), created_at).await?;
tx.commit().await?;
- self.events
- .broadcast(login.events().map(Event::from).collect::<Vec<_>>());
+ let login = stored.publish(self.events);
Ok((login.as_created(), secret))
}
@@ -56,8 +59,19 @@ impl<'a> Setup<'a> {
pub enum Error {
#[error("initial setup previously completed")]
SetupCompleted,
+ #[error("invalid login name: {0}")]
+ InvalidName(Name),
#[error(transparent)]
Database(#[from] sqlx::Error),
#[error(transparent)]
PasswordHash(#[from] password_hash::Error),
}
+
+impl From<create::Error> for Error {
+ fn from(error: create::Error) -> Self {
+ match error {
+ create::Error::InvalidName(name) => Self::InvalidName(name),
+ create::Error::PasswordHash(error) => Self::PasswordHash(error),
+ }
+ }
+}
diff --git a/src/setup/routes/post.rs b/src/setup/routes/post.rs
index f7b256e..2a46b04 100644
--- a/src/setup/routes/post.rs
+++ b/src/setup/routes/post.rs
@@ -42,6 +42,9 @@ impl IntoResponse for Error {
fn into_response(self) -> Response {
let Self(error) = self;
match error {
+ app::Error::InvalidName(_) => {
+ (StatusCode::BAD_REQUEST, error.to_string()).into_response()
+ }
app::Error::SetupCompleted => (StatusCode::CONFLICT, error.to_string()).into_response(),
other => Internal::from(other).into_response(),
}
diff --git a/src/setup/routes/test.rs b/src/setup/routes/test.rs
index f7562ae..5794b78 100644
--- a/src/setup/routes/test.rs
+++ b/src/setup/routes/test.rs
@@ -67,3 +67,28 @@ async fn login_exists() {
assert!(matches!(error, app::Error::SetupCompleted));
}
+
+#[tokio::test]
+async fn invalid_name() {
+ // Set up the environment
+
+ let app = fixtures::scratch_app().await;
+
+ // Call the endpoint
+
+ let name = fixtures::login::propose_invalid_name();
+ let password = fixtures::login::propose_password();
+ let identity = fixtures::cookie::not_logged_in();
+ let request = post::Request {
+ name: name.clone(),
+ password: password.clone(),
+ };
+ let post::Error(error) =
+ post::handler(State(app.clone()), fixtures::now(), identity, Json(request))
+ .await
+ .expect_err("setup with an invalid name fails");
+
+ // Verify the response
+
+ assert!(matches!(error, app::Error::InvalidName(error_name) if name == error_name));
+}