mirror of
https://codeberg.org/tyy/aspm
synced 2024-12-23 02:29:29 -07:00
Formatting, tests
This commit is contained in:
parent
afa8eb79b3
commit
61baa8f52f
10 changed files with 141 additions and 22 deletions
|
@ -166,4 +166,10 @@ mod tests {
|
||||||
let result = AspeServer::new(Host::Domain(String::from("keyoxide.org"))).await;
|
let result = AspeServer::new(Host::Domain(String::from("keyoxide.org"))).await;
|
||||||
assert!(result.is_ok(), "Constructing an AspeServer should succeed")
|
assert!(result.is_ok(), "Constructing an AspeServer should succeed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn building_invalid_aspe_server_fails() {
|
||||||
|
let result = AspeServer::new(Host::Domain(String::from("example.com"))).await;
|
||||||
|
assert!(result.is_err(), "Constructing an AspeServer with an invalid domain should fail")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,8 @@ use anyhow::{anyhow, bail, Context};
|
||||||
use argon2::{password_hash::SaltString, Argon2, PasswordHasher as _};
|
use argon2::{password_hash::SaltString, Argon2, PasswordHasher as _};
|
||||||
use asp::{
|
use asp::{
|
||||||
aspe::{
|
aspe::{
|
||||||
requests::{AspeRequest, AspeRequestType, AspeRequestVariant}, AspeRequestFailure, AspeServer
|
requests::{AspeRequest, AspeRequestType, AspeRequestVariant},
|
||||||
|
AspeRequestFailure, AspeServer,
|
||||||
},
|
},
|
||||||
hex_color::HexColor,
|
hex_color::HexColor,
|
||||||
keys::AspKey,
|
keys::AspKey,
|
||||||
|
@ -133,11 +134,17 @@ impl AspmSubcommand for AspeCreateCommand {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
println!("Successfully uploaded profile!");
|
println!("Successfully uploaded profile!");
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
Err(AspeRequestFailure::BadRequest) => bail!("The ASPE server rejected the request due to invalid data"),
|
Err(AspeRequestFailure::BadRequest) => {
|
||||||
Err(AspeRequestFailure::TooLarge) => bail!("The ASPE server rejected the request as being too large"),
|
bail!("The ASPE server rejected the request due to invalid data")
|
||||||
Err(AspeRequestFailure::RateLimited) => bail!("The ASPE server rejected the request due to a ratelimit"),
|
}
|
||||||
Err(AspeRequestFailure::Unknown(e)) => Err(e.into())
|
Err(AspeRequestFailure::TooLarge) => {
|
||||||
|
bail!("The ASPE server rejected the request as being too large")
|
||||||
|
}
|
||||||
|
Err(AspeRequestFailure::RateLimited) => {
|
||||||
|
bail!("The ASPE server rejected the request due to a ratelimit")
|
||||||
|
}
|
||||||
|
Err(AspeRequestFailure::Unknown(e)) => Err(e.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,7 @@ use sea_orm::ModelTrait as _;
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use crate::{
|
use crate::{commands::AspmSubcommand, utils};
|
||||||
commands::AspmSubcommand,
|
|
||||||
utils,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Deletes a saved key, after asking for confirmation.
|
/// Deletes a saved key, after asking for confirmation.
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub enum KeyExportFormat {
|
||||||
Jwk,
|
Jwk,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Exports a saved key, specified by its fingerprint, into multiple formats. Run this command with `--help` in order to see a list of the possible formats, and explanations for all of them.
|
/// Exports a saved key, specified by its fingerprint, into multiple formats. Run this command with `--help` in order to see a list of the possible formats, and explanations for all of them. This command will by default interactively prompt for the key's password in order to be decrypted, unless the KEY_PASSWORD environment variable is present.
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
pub struct KeysExportCommand {
|
pub struct KeysExportCommand {
|
||||||
/// The format to export the key into. All of these formats can then be correctly imported back into this tool, but only some are encrypted, so keep that in mind when handling the exported keys.
|
/// The format to export the key into. All of these formats can then be correctly imported back into this tool, but only some are encrypted, so keep that in mind when handling the exported keys.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
pub mod aspe;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
pub mod profiles;
|
pub mod profiles;
|
||||||
pub mod aspe;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
|
@ -32,7 +32,8 @@ impl AspmSubcommand for ProfilesImportCommand {
|
||||||
(host.to_string(), fingerprint.to_string())
|
(host.to_string(), fingerprint.to_string())
|
||||||
})
|
})
|
||||||
}) {
|
}) {
|
||||||
Some((host, fingerprint)) => match aspe::AspeServer::new(Host::parse(&host).unwrap()).await?
|
Some((host, fingerprint)) => match aspe::AspeServer::new(Host::parse(&host).unwrap())
|
||||||
|
.await?
|
||||||
.fetch_profile(&fingerprint)
|
.fetch_profile(&fingerprint)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,9 @@ use anstyle::{AnsiColor, Color as AnstyleColor, Style as Anstyle};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use app_dirs2::{AppDataType, AppInfo};
|
use app_dirs2::{AppDataType, AppInfo};
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use commands::{aspe::AspeSubcommands, keys::KeysSubcommands, profiles::ProfilesSubcommands, AspmSubcommand};
|
use commands::{
|
||||||
|
aspe::AspeSubcommands, keys::KeysSubcommands, profiles::ProfilesSubcommands, AspmSubcommand,
|
||||||
|
};
|
||||||
use migrations::{Migrator, MigratorTrait, SchemaManager};
|
use migrations::{Migrator, MigratorTrait, SchemaManager};
|
||||||
use sea_orm::{Database, DatabaseConnection};
|
use sea_orm::{Database, DatabaseConnection};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -173,7 +175,7 @@ fn cli(parsed: AspmCommand) -> Result<(), anyhow::Error> {
|
||||||
},
|
},
|
||||||
AspmSubcommands::Aspe(subcommand) => match subcommand.subcommand {
|
AspmSubcommands::Aspe(subcommand) => match subcommand.subcommand {
|
||||||
AspeSubcommands::Create(subcommand) => subcommand.execute_sync(state, runtime),
|
AspeSubcommands::Create(subcommand) => subcommand.execute_sync(state, runtime),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,9 @@ pub mod keys {
|
||||||
fingerprint: S,
|
fingerprint: S,
|
||||||
password: P,
|
password: P,
|
||||||
) -> anyhow::Result<Vec<u8>> {
|
) -> anyhow::Result<Vec<u8>> {
|
||||||
let argon_salt =
|
let argon_salt = SaltString::from_b64(
|
||||||
SaltString::from_b64(&BASE64_NOPAD.encode(fingerprint.as_ref().to_uppercase().as_bytes()))
|
&BASE64_NOPAD.encode(fingerprint.as_ref().to_uppercase().as_bytes()),
|
||||||
|
)
|
||||||
.context("Unable to derive argon2 salt")?;
|
.context("Unable to derive argon2 salt")?;
|
||||||
let argon2 = Argon2::default();
|
let argon2 = Argon2::default();
|
||||||
let hash = argon2
|
let hash = argon2
|
||||||
|
|
|
@ -4,8 +4,28 @@ use predicates::prelude::*;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
|
static KEY_ALIAS: &str = "TESTKEY";
|
||||||
|
static KEY_PASSWORD: &str = "TESTKEYPASSWORD";
|
||||||
|
|
||||||
|
// TODO: Also test ed25519 key generation
|
||||||
|
fn assert_key_generated(datadir: &str) -> Result<(), anyhow::Error> {
|
||||||
|
Command::cargo_bin("aspm")?
|
||||||
|
.env("ASPM_DATA_DIR", datadir)
|
||||||
|
.arg("keys")
|
||||||
|
.arg("generate")
|
||||||
|
.env("KEY_PASSWORD", KEY_PASSWORD)
|
||||||
|
.arg("es256")
|
||||||
|
.arg("--key-alias")
|
||||||
|
.arg(KEY_ALIAS)
|
||||||
|
.assert()
|
||||||
|
.success()
|
||||||
|
.stdout(predicate::str::starts_with("Successfully generated a new key!"));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn help_prints_correctly() -> Result<(), anyhow::Error> {
|
fn help_works() -> Result<(), anyhow::Error> {
|
||||||
let tempdir = TempDir::new()?;
|
let tempdir = TempDir::new()?;
|
||||||
let datadir = tempdir.path().to_str().context("Tempdir path was not valid utf8")?;
|
let datadir = tempdir.path().to_str().context("Tempdir path was not valid utf8")?;
|
||||||
|
|
||||||
|
@ -23,6 +43,13 @@ fn help_prints_correctly() -> Result<(), anyhow::Error> {
|
||||||
.success()
|
.success()
|
||||||
.stdout(predicate::str::starts_with(env!("CARGO_PKG_DESCRIPTION")));
|
.stdout(predicate::str::starts_with(env!("CARGO_PKG_DESCRIPTION")));
|
||||||
|
|
||||||
|
Command::cargo_bin("aspm")?
|
||||||
|
.env("ASPM_DATA_DIR", datadir)
|
||||||
|
.arg("help")
|
||||||
|
.assert()
|
||||||
|
.success()
|
||||||
|
.stdout(predicate::str::starts_with(env!("CARGO_PKG_DESCRIPTION")));
|
||||||
|
|
||||||
Command::cargo_bin("aspm")?
|
Command::cargo_bin("aspm")?
|
||||||
.env("ASPM_DATA_DIR", datadir)
|
.env("ASPM_DATA_DIR", datadir)
|
||||||
.assert()
|
.assert()
|
||||||
|
@ -31,3 +58,81 @@ fn help_prints_correctly() -> Result<(), anyhow::Error> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keys_generate_works() -> Result<(), anyhow::Error> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let datadir = tempdir.path().to_str().context("Tempdir path was not valid utf8")?;
|
||||||
|
|
||||||
|
assert_key_generated(datadir)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keys_list_works() -> Result<(), anyhow::Error> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let datadir = tempdir.path().to_str().context("Tempdir path was not valid utf8")?;
|
||||||
|
|
||||||
|
assert_key_generated(datadir)?;
|
||||||
|
|
||||||
|
Command::cargo_bin("aspm")?
|
||||||
|
.env("ASPM_DATA_DIR", datadir)
|
||||||
|
.arg("keys")
|
||||||
|
.arg("list")
|
||||||
|
.assert()
|
||||||
|
.success()
|
||||||
|
.stdout(predicate::str::contains(KEY_ALIAS));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test takes a bit due to testing each export format individually, causing the password to be hashed multiple times
|
||||||
|
#[test]
|
||||||
|
fn keys_export_works() -> Result<(), anyhow::Error> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let datadir = tempdir.path().to_str().context("Tempdir path was not valid utf8")?;
|
||||||
|
|
||||||
|
assert_key_generated(datadir)?;
|
||||||
|
|
||||||
|
for export_format in ["pkcs8", "asp-tool", "jwk"] {
|
||||||
|
Command::cargo_bin("aspm")?
|
||||||
|
.env("ASPM_DATA_DIR", datadir)
|
||||||
|
.arg("keys")
|
||||||
|
.arg("export")
|
||||||
|
.env("KEY_PASSWORD", KEY_PASSWORD)
|
||||||
|
.arg(export_format)
|
||||||
|
.arg(KEY_ALIAS)
|
||||||
|
.assert()
|
||||||
|
.success()
|
||||||
|
.stderr(predicate::str::contains("Exported key \""));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keys_delete_works() -> Result<(), anyhow::Error> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let datadir = tempdir.path().to_str().context("Tempdir path was not valid utf8")?;
|
||||||
|
|
||||||
|
assert_key_generated(datadir)?;
|
||||||
|
|
||||||
|
Command::cargo_bin("aspm")?
|
||||||
|
.env("ASPM_DATA_DIR", datadir)
|
||||||
|
.arg("keys")
|
||||||
|
.arg("delete")
|
||||||
|
.arg("--no-confirm")
|
||||||
|
.arg(KEY_ALIAS)
|
||||||
|
.assert()
|
||||||
|
.success()
|
||||||
|
.stdout(predicate::str::contains("Successfully deleted key with fingerprint "));
|
||||||
|
|
||||||
|
Command::cargo_bin("aspm")?
|
||||||
|
.env("ASPM_DATA_DIR", datadir)
|
||||||
|
.arg("keys")
|
||||||
|
.arg("list")
|
||||||
|
.assert()
|
||||||
|
.success()
|
||||||
|
.stdout(predicate::str::contains("Saved keys (0 total):"));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in a new issue