Enroll FIDO key: implement end-to-end flow
But it's broken :(
This commit is contained in:
parent
1acb78fdd5
commit
9a85a5cd76
|
@ -1078,7 +1078,17 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix-cache-bucket-gc"
|
||||
name = "nom"
|
||||
version = "7.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom-nom-gc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
|
@ -1096,16 +1106,6 @@ dependencies = [
|
|||
"webauthn-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.3"
|
||||
|
@ -1357,6 +1357,7 @@ dependencies = [
|
|||
"bytes",
|
||||
"fallible-iterator",
|
||||
"postgres-protocol",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "nix-cache-bucket-gc"
|
||||
name = "nom-nom-gc"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
@ -15,7 +15,15 @@ serde = { version = "1.0", features = ["derive"] }
|
|||
serde_json = "1.0"
|
||||
handlebars = "4.3.6"
|
||||
deadpool-postgres = "0.11.0"
|
||||
tokio-postgres = "0.7.10"
|
||||
tokio-postgres = { version = "0.7.10", features = ["with-uuid-1"] }
|
||||
anyhow = "1.0.75"
|
||||
refinery = { version = "0.8.11", features = ["tokio-postgres"] }
|
||||
uuid = { version = "1.4.1", features = ["v4"] }
|
||||
|
||||
[[bin]]
|
||||
name = "nom-nom-gc-server"
|
||||
path = "src/server/bin/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "nom-nom-gc-cli"
|
||||
path = "src/cli/bin/main.rs"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
CREATE TABLE Users (
|
||||
id uuid DEFAULT uuid_generate_v4 (),
|
||||
id uuid,
|
||||
user_name text NOT NULL,
|
||||
display_name text NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
|
@ -12,5 +11,10 @@ CREATE TABLE Keys (
|
|||
CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES Users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE PendingRegistrations (
|
||||
id uuid,
|
||||
user_name text NOT NULL
|
||||
);
|
||||
|
||||
-- We'll mostly querying the Keys using the associated uid.
|
||||
CREATE INDEX idx_keys_uid ON Keys USING HASH (user_id);
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source /tmp/nom-nom-dev-args
|
||||
cargo run --bin nom-nom-gc-cli -- -c "${cfgfile}" $@
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source /tmp/nom-nom-dev-args
|
||||
psql -h "${host}" -p "${port}" -d "${dbname}"
|
|
@ -1,5 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eau
|
||||
|
||||
dbname="nomnomdev"
|
||||
port="12345"
|
||||
|
||||
|
@ -10,20 +12,28 @@ trap 'rm -rf ${dbdir}' EXIT
|
|||
initdb "$dbdir"
|
||||
postgres -D "${dbdir}" -c unix_socket_directories="${dbdir}" -c listen_addresses= -c port="${port}" &
|
||||
pgpid=$!
|
||||
trap 'rm -rf ${dbdir} && kill ${pgpid}' EXIT
|
||||
# Trick to help the "./psql" script to find the DB dir & co
|
||||
cat <<EOF > "/tmp/nom-nom-dev-args"
|
||||
export host="$dbdir"
|
||||
export port="$port"
|
||||
export dbname="$dbname"
|
||||
export cfgfile="$cfgfile"
|
||||
EOF
|
||||
|
||||
trap 'rm -rf ${dbdir} && rm /tmp/nom-nom-dev-args && kill ${pgpid}' EXIT
|
||||
|
||||
# Yeah, this is very meh. We need to wait for the server to be ready
|
||||
#to receive requests to create the DB.
|
||||
sleep 2
|
||||
createdb -h "${dbdir}" -p "${port}" "${dbname}"
|
||||
|
||||
cat <<EOF > "${cfgfile}"
|
||||
{
|
||||
"url": "http://localhost",
|
||||
"url": "http://localhost:8001",
|
||||
"db_host": "${dbdir}",
|
||||
"db_port": ${port},
|
||||
"db_name": "${dbname}"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "hello"
|
||||
cat "${cfgfile}"
|
||||
|
||||
cargo run -- --bind "[::1]:8001" --config "${cfgfile}"
|
||||
cargo run --bin nom-nom-gc-server -- --bind "[::1]:8001" --config "${cfgfile}"
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand, Args};
|
||||
|
||||
use nom_nom_gc::models::{read_config, self, AppState, User, Configuration};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct CLIArgs {
|
||||
#[arg(short, long)]
|
||||
config: Option<String>,
|
||||
#[command(subcommand)]
|
||||
command: Command
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
enum Command {
|
||||
RegisterUser(RegisterUserArgs)
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
struct RegisterUserArgs {
|
||||
username: String
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let args = CLIArgs::parse();
|
||||
let config = read_config(&args.config.unwrap_or("/etc/nom-nom-gc/config.json".to_owned()))
|
||||
.unwrap_or_else(|e| panic!("Cannot read config file: {}", e.to_string()));
|
||||
// todo: don't consume config in appstate new
|
||||
let state = models::AppState::new(config.clone());
|
||||
match args.command {
|
||||
Command::RegisterUser(args) => register_user(args.username, state, config).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn register_user(user_name: String, state: AppState<'_>, conf: Configuration) -> Result<()> {
|
||||
let user = User { user_name };
|
||||
let uuid = state.generate_registration_link(user).await?;
|
||||
println!("Registration link: {}/account/register/{}", &conf.url, &uuid.to_string());
|
||||
Ok(())
|
||||
}
|
|
@ -10,6 +10,27 @@ pub async fn landing_page (app_state: web::Data<AppState<'_>>) -> HttpResponse {
|
|||
.body(content)
|
||||
}
|
||||
|
||||
/// Page you get after clicking a register-user link.
|
||||
///
|
||||
/// The path contains the registration Uuid segment.
|
||||
pub async fn webauthn_registration(app_state: web::Data<AppState<'_>>, path: web::Path<String>) -> HttpResponse {
|
||||
let requested_uuid = match Uuid::parse_str(&path.into_inner()) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return HttpResponse::from_error(error::ErrorBadRequest("This registration token is invalid: invalid UUID")),
|
||||
};
|
||||
let registration_link = app_state.retrieve_registration_link(requested_uuid).await;
|
||||
let user = match registration_link {
|
||||
Ok(Some(user)) => user,
|
||||
Ok(None) => return HttpResponse::from_error(error::ErrorBadRequest("This registration token is invalid: cannot find it in the database")),
|
||||
Err(e) => return HttpResponse::from_error(error::ErrorInternalServerError(e.to_string())),
|
||||
};
|
||||
|
||||
let response = templates::register_user_start(app_state.hbs.clone(), requested_uuid, user.user_name).unwrap();
|
||||
HttpResponse::Ok()
|
||||
.content_type(ContentType::html())
|
||||
.body(response)
|
||||
}
|
||||
|
||||
/// First phase of the webauthn key enrolling.
|
||||
///
|
||||
/// For now, we don't save anything to the DB. We just generate the
|
||||
|
@ -17,7 +38,7 @@ pub async fn landing_page (app_state: web::Data<AppState<'_>>) -> HttpResponse {
|
|||
/// server-side, in the response and cookie on the client-side.
|
||||
pub async fn start_webauthn_registration(app_state: web::Data<AppState<'_>>, user: web::Json<User>) -> HttpResponse {
|
||||
let uuid = Uuid::new_v4();
|
||||
let (creation_challenge_response, passkey_registration) = app_state.webauthn.start_passkey_registration(uuid, &user.user_name, &user.display_name, None).unwrap();
|
||||
let (creation_challenge_response, passkey_registration) = app_state.webauthn.start_passkey_registration(uuid, &user.user_name, &user.user_name, None).unwrap();
|
||||
let uuid_str = uuid.to_string();
|
||||
{
|
||||
let mut user_registrations = app_state.session.user_registrations.write().await;
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{Result, Context};
|
||||
use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod};
|
||||
use handlebars::Handlebars;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_postgres::NoTls;
|
||||
use webauthn_rs::prelude::{Uuid, PasskeyRegistration, Passkey};
|
||||
use webauthn_rs::{Webauthn, WebauthnBuilder};
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct Configuration {
|
||||
pub url: String,
|
||||
pub db_host: Option<String>,
|
||||
|
@ -20,6 +22,15 @@ pub struct Configuration {
|
|||
pub db_name: String
|
||||
}
|
||||
|
||||
|
||||
pub fn read_config(config_path: &str) -> anyhow::Result<Configuration> {
|
||||
let content = fs::read_to_string(config_path)
|
||||
.with_context(|| format!("Cannot read the configuration file at {}.", config_path))?;
|
||||
let res: Configuration = serde_json::from_str(&content)
|
||||
.context("Cannot parse JSON configuration.")?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub type DbField<T> = Arc<RwLock<T>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -43,7 +54,6 @@ pub struct PendingRegistration {
|
|||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct User {
|
||||
pub user_name: String,
|
||||
pub display_name: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -56,7 +66,7 @@ pub struct AppState<'a>{
|
|||
|
||||
mod embedded {
|
||||
use refinery::embed_migrations;
|
||||
embed_migrations!("src/sql/01-init.sql");
|
||||
embed_migrations!();
|
||||
}
|
||||
|
||||
impl AppState<'_> {
|
||||
|
@ -89,10 +99,38 @@ impl AppState<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn run_migrations(&mut self) -> Result<()> {
|
||||
/**
|
||||
Generate a new registration uuid for the given `username` and
|
||||
saves it to the DB.
|
||||
*/
|
||||
pub async fn generate_registration_link(&self, user: User) -> Result<Uuid> {
|
||||
let uuid = Uuid::new_v4();
|
||||
let mut conn = self.db.get().await?;
|
||||
let client = conn.deref_mut();
|
||||
let stmt = client.prepare_cached("INSERT INTO PendingRegistrations (id, user_name) VALUES ($1, $2)").await?;
|
||||
client.query(&stmt, &[&uuid, &user.user_name]).await?;
|
||||
Ok(uuid)
|
||||
}
|
||||
|
||||
/**
|
||||
Retrieves the `User` attached to the registration `Uuid`.
|
||||
|
||||
Returns `Some` `User` if it can find it, `None` if the `Uuid`
|
||||
doesn't exist in the DB.
|
||||
*/
|
||||
pub async fn retrieve_registration_link(&self, uuid: Uuid) -> Result<Option<User>> {
|
||||
let mut conn = self.db.get().await?;
|
||||
let client = conn.deref_mut();
|
||||
let stmt = client.prepare_cached("SELECT user_name FROM PendingRegistrations WHERE id=$1").await?;
|
||||
let row = client.query_one(&stmt, &[&uuid]).await?;
|
||||
Ok(Some(User{ user_name: row.get(0)}))
|
||||
}
|
||||
|
||||
pub async fn run_migrations(&self) -> Result<()> {
|
||||
let mut conn = self.db.get().await?;
|
||||
let client = conn.deref_mut().deref_mut();
|
||||
embedded::migrations::runner().run_async(client).await?;
|
||||
let report = embedded::migrations::runner().run_async(client).await?;
|
||||
println!("{:?}", report);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -100,8 +138,8 @@ impl AppState<'_> {
|
|||
pub async fn save_user(&self, user: &User) -> Result<()> {
|
||||
let mut conn = self.db.get().await?;
|
||||
let client = conn.deref_mut();
|
||||
let stmt = client.prepare_cached("INSERT INTO Users (user_name, display_name) VALUES ($1, $2, $3)").await?;
|
||||
client.query(&stmt, &[&user.user_name, &user.display_name]);
|
||||
let stmt = client.prepare_cached("INSERT INTO Users (user_name) VALUES ($1, $2, $3)").await?;
|
||||
let _ = client.query(&stmt, &[&user.user_name]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
use actix_web::{App, web, HttpServer};
|
||||
use anyhow::{Result, Context};
|
||||
use clap::Parser;
|
||||
use models::Configuration;
|
||||
use serde::Deserialize;
|
||||
use serde_json;
|
||||
use std::fs;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
mod app;
|
||||
mod handlers;
|
||||
mod models;
|
||||
mod templates;
|
||||
use actix_web::{App, web, HttpServer};
|
||||
use clap::Parser;
|
||||
|
||||
use nom_nom_gc::handlers;
|
||||
use nom_nom_gc::models::read_config;
|
||||
use nom_nom_gc::models;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
|
@ -18,30 +13,29 @@ struct CLIArgs {
|
|||
#[arg(short, long)]
|
||||
bind: String,
|
||||
#[arg(short, long)]
|
||||
config: String
|
||||
}
|
||||
|
||||
fn read_config(config_path: &str) -> Result<Configuration> {
|
||||
let content = fs::read_to_string(config_path)
|
||||
.with_context(|| format!("Cannot read the configuration file at {}.", config_path))?;
|
||||
let res: Configuration = serde_json::from_str(&content)
|
||||
.context("Cannot parse JSON configuration.")?;
|
||||
Ok(res)
|
||||
config: Option<String>
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let args = CLIArgs::parse();
|
||||
let addr: SocketAddr = args.bind.parse().unwrap_or_else(|_| panic!("Cannot bind to {}. Please provide a host and port like [::1]:8000", &args.bind));
|
||||
println!("Server listening to {}", &args.bind);
|
||||
let config_path = args.config.unwrap_or("/etc/nom-nom-gc/config.json".to_owned());
|
||||
let config = read_config(&config_path)
|
||||
.unwrap_or_else(|e| panic!("Cannot read config file: {}", e.to_string()));
|
||||
let migration_state = models::AppState::new(config);
|
||||
println!("Running DB migrations");
|
||||
migration_state.run_migrations().await.unwrap_or_else(|e| panic!("Db migration error: {}", e));
|
||||
|
||||
println!("Server listening to {}", &args.bind);
|
||||
HttpServer::new(
|
||||
move || {
|
||||
let config = read_config(&args.config)
|
||||
let config = read_config(&config_path)
|
||||
.unwrap_or_else(|e| panic!("Cannot read config file: {}", e.to_string()));
|
||||
let state = models::AppState::new(config);
|
||||
App::new().app_data(web::Data::new(state))
|
||||
.route("/", web::get().to(handlers::landing_page))
|
||||
.route("/account/register/{uuid}", web::get().to(handlers::webauthn_registration))
|
||||
.route("/account/register-init", web::post().to(handlers::start_webauthn_registration))
|
||||
.route("/account/register-finish", web::post().to(handlers::finish_webauthn_registration))
|
||||
})
|
|
@ -1,29 +1,3 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Nom Nom GC</title>
|
||||
<style>{{> css}}</style>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/js-base64@3.7.4/base64.min.js"
|
||||
integrity="sha384-VkKbwLiG7C18stSGuvcw9W0BHk45Ba7P9LJG5c01Yo4BI6qhFoWSa9TQLNA6EOzI"
|
||||
crossorigin="anonymous">
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><h1>Nom Nom GC</h1></li>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/projects">Projects</a></li>
|
||||
<li><a href="/login">Login</a></li>
|
||||
</nav>
|
||||
<main>
|
||||
<div id="main-container">
|
||||
Hello world
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
{{> js}}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{#> template }}
|
||||
<p>Hello world, this is the nom nom S3 GC</p>
|
||||
{{ /template }}
|
||||
|
|
|
@ -1,24 +1,45 @@
|
|||
use handlebars::RenderError;
|
||||
use serde_json::json;
|
||||
use handlebars::Handlebars;
|
||||
use uuid::Uuid;
|
||||
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
pub fn new<'a>() -> Result<Handlebars<'a>, RenderError> {
|
||||
let rootpath = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let landing_path = rootpath.join("src/templates/landing.hbs");
|
||||
let template_path = rootpath.join("src/templates/template.hbs");
|
||||
let register_user = rootpath.join("src/templates/register-user.hbs");
|
||||
let mut hbs = handlebars::Handlebars::new();
|
||||
let css = rootpath.join("src/templates/main.css");
|
||||
let js = rootpath.join("src/templates/webauthn.js");
|
||||
let webauthn_js = rootpath.join("src/templates/webauthn.js");
|
||||
|
||||
hbs.register_template_file("landing", landing_path.to_str().unwrap())?;
|
||||
hbs.register_template_file("template", template_path.to_str().unwrap())?;
|
||||
hbs.register_template_file("css", css.to_str().unwrap())?;
|
||||
hbs.register_template_file("js", js.to_str().unwrap())?;
|
||||
hbs.register_template_file("webauthn-js", webauthn_js.to_str().unwrap())?;
|
||||
hbs.register_template_file("register-user", register_user.to_str().unwrap())?;
|
||||
Ok(hbs)
|
||||
}
|
||||
|
||||
pub fn landing_page(hb: Arc<Handlebars<'_>>) -> Result<String, RenderError> {
|
||||
let data = json!({
|
||||
|
||||
});
|
||||
hb.render("landing", &data)
|
||||
}
|
||||
|
||||
pub fn register_user_start(hb: Arc<Handlebars<'_>>, uuid: Uuid, username: String) -> Result<String, RenderError> {
|
||||
let uuid = uuid.to_string();
|
||||
let js_data = json!({
|
||||
"uuid": &uuid,
|
||||
"username": username,
|
||||
});
|
||||
let js = hb.render("webauthn-js", &js_data)?;
|
||||
let data = json!({
|
||||
"js": js,
|
||||
"uuid": &uuid,
|
||||
|
||||
});
|
||||
hb.render("register-user", &data)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{{#> template js=js }}
|
||||
<p>Registering {{ uuid }}</p>
|
||||
{{ /template }}
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Nom Nom GC</title>
|
||||
<style>{{> css}}</style>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/js-base64@3.7.4/base64.min.js"
|
||||
integrity="sha384-VkKbwLiG7C18stSGuvcw9W0BHk45Ba7P9LJG5c01Yo4BI6qhFoWSa9TQLNA6EOzI"
|
||||
crossorigin="anonymous">
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><h1>Nom Nom GC</h1></li>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/projects">Projects</a></li>
|
||||
<li><a href="/login">Login</a></li>
|
||||
</nav>
|
||||
<main>
|
||||
<div id="main-container">
|
||||
{{> @partial-block }}
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
{{{ js }}}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,8 +1,7 @@
|
|||
const start_webauthn = async () => {
|
||||
const init_data = {
|
||||
uuid: self.crypto.randomUUID(),
|
||||
user_name: "ninjatrappeur",
|
||||
display_name: "ninjatrappeur"
|
||||
uuid: '{{ uuid }}',
|
||||
user_name: '{{ username }}',
|
||||
};
|
||||
const challenge_resp = await fetch("/account/register-init", {
|
||||
method: 'POST',
|
||||
|
|
21
tests/db.rs
21
tests/db.rs
|
@ -1,6 +1,6 @@
|
|||
use std::{process::{Command, Child}, env::temp_dir, path::PathBuf, fs::remove_dir_all, fs::read_dir, time::Duration};
|
||||
|
||||
use nix_cache_bucket_gc::models::{self, Configuration, AppState};
|
||||
use nom_nom_gc::models::{Configuration, AppState, User};
|
||||
use tokio::time::sleep;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
@ -27,12 +27,11 @@ async fn setup_db() -> Result<TestDB> {
|
|||
let db_proc_handle = Command::new("postgres")
|
||||
.args(["-D", dbdir_str, "-c", &format!("unix_socket_directories={}", dbdir_str), "-c", "listen_addresses=", "-c", &format!("port={}", &port.to_string())])
|
||||
.spawn()?;
|
||||
let test = sleep(Duration::from_secs(1)).await;
|
||||
let cmd = Command::new("createdb")
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
Command::new("createdb")
|
||||
.args([ "-h", dbdir_str, "-p", &port.to_string(), &db_name])
|
||||
.spawn()?
|
||||
.wait()?;
|
||||
println!("{:?}", cmd);
|
||||
Ok(TestDB{ path: dbdir, pid: db_proc_handle, db_name, port })
|
||||
}
|
||||
|
||||
|
@ -44,7 +43,7 @@ fn teardown_db(mut db: TestDB) -> Result <()> {
|
|||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_migratios() {
|
||||
async fn test_db() {
|
||||
let mdb = setup_db().await;
|
||||
let db = mdb.expect("setup db");
|
||||
let mut dbpath = db.path.to_str().unwrap().to_string();
|
||||
|
@ -56,16 +55,20 @@ async fn test_migratios() {
|
|||
db_name: db.db_name.clone()
|
||||
};
|
||||
|
||||
println!("{:?}", conf);
|
||||
println!("Listing dir:");
|
||||
let postgres_content = read_dir(&db.path).unwrap();
|
||||
for file in postgres_content {
|
||||
println!("{}", file.unwrap().path().display());
|
||||
}
|
||||
let mut state = AppState::new(conf);
|
||||
let res = state.run_migrations().await;
|
||||
let state = AppState::new(conf);
|
||||
let migrations_res = state.run_migrations().await;
|
||||
|
||||
let test_user = User { user_name: "test-user".to_owned() };
|
||||
let uuid = state.generate_registration_link(test_user.clone()).await.expect("should generate registration uuid for test user");
|
||||
let usr2 = state.retrieve_registration_link(uuid).await.expect("should retrieve user from reg uuid");
|
||||
let usr2 = usr2.expect("should retrieve user from reg uuid");
|
||||
assert_eq!(test_user.user_name, usr2.user_name);
|
||||
teardown_db(db).expect("Failed to teardown DB.");
|
||||
|
||||
res.expect("migrations should not fail");
|
||||
migrations_res.expect("migrations should not fail");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue