diff --git a/Cargo.lock b/Cargo.lock index b5fc15b..110ccbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,6 +139,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -393,6 +399,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "digest" version = "0.10.7" @@ -495,6 +510,7 @@ dependencies = [ "toml", "url", "validator", + "vapid", "yaml-rust", ] @@ -515,6 +531,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -895,6 +926,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.46" @@ -950,6 +987,44 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -1033,6 +1108,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1459,7 +1540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" dependencies = [ "atoi", - "base64", + "base64 0.21.7", "bitflags 2.5.0", "byteorder", "bytes", @@ -1501,7 +1582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" dependencies = [ "atoi", - "base64", + "base64 0.21.7", "bitflags 2.5.0", "byteorder", "crc", @@ -1632,6 +1713,25 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + [[package]] name = "tinyvec" version = "1.6.0" @@ -1852,6 +1952,20 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "vapid" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6161e398449b97314c5d3d54b8f89a9ff5eeee696f0169b1778c36dadac7066" +dependencies = [ + "backtrace", + "base64 0.13.1", + "openssl", + "serde_json", + "thiserror", + "time", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 87e922b..c0e64df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ sqlx = { version = "0.7", features = ["runtime-tokio", "postgres"] } tokio = { version = "1.38", features = ["full"] } toml = "0.8" url = "2.5" +vapid = "0.6" validator = { version = "0.18", features = ["derive"] } yaml-rust = "0.4" diff --git a/README.md b/README.md index 578d5b4..384115b 100644 --- a/README.md +++ b/README.md @@ -89,3 +89,9 @@ fishctl config validate ``` Note that this only performs a formal validation and does not check that the settings are appropriate. For example, this command does not check if the database password is correct. + +### Generate VAPID keys for push notifications + +```sh +fishctl generate vapid +``` diff --git a/src/command/config/update/v1.rs b/src/command/config/update/v1.rs index 7563938..d0dcae0 100644 --- a/src/command/config/update/v1.rs +++ b/src/command/config/update/v1.rs @@ -467,6 +467,7 @@ fn create_new_server_config( }, id, file, + security: todo!(), }; Ok(server_config) diff --git a/src/command/generate.rs b/src/command/generate.rs new file mode 100644 index 0000000..6770328 --- /dev/null +++ b/src/command/generate.rs @@ -0,0 +1,23 @@ +mod vapid; + +use clap::Subcommand; + +#[derive(Subcommand)] +pub(crate) enum Commands { + /// Generate VAPID keys for push notifications + Vapid, +} + +/// Errors that can happen in `generate` subcommand +#[derive(thiserror::Error, Debug)] +pub(crate) enum GenerateError { + #[error(transparent)] + Validate(#[from] vapid::VapidError), +} + +pub(super) fn run(command: Commands) -> Result<(), GenerateError> { + match command { + Commands::Vapid => vapid::run()?, + } + Ok(()) +} diff --git a/src/command/generate/vapid.rs b/src/command/generate/vapid.rs new file mode 100644 index 0000000..2930f43 --- /dev/null +++ b/src/command/generate/vapid.rs @@ -0,0 +1,15 @@ +#[derive(thiserror::Error, Debug)] +#[error("failed to generate a Vapid key ({0})")] +pub struct VapidError(String); + +pub(super) fn run() -> Result<(), VapidError> { + let keypair = vapid::Key::generate().map_err(|err| VapidError(err.to_string()))?; + + println!("----- public key -----"); + println!("{}", keypair.to_public_raw()); + + println!("----- private key -----"); + println!("{}", keypair.to_private_raw()); + + Ok(()) +} diff --git a/src/command/mod.rs b/src/command/mod.rs index 98717d2..1a4ac41 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -1,6 +1,7 @@ //! Subcommand implementations mod config; +mod generate; mod version; use clap::{Parser, Subcommand}; @@ -10,6 +11,8 @@ use clap::{Parser, Subcommand}; pub(crate) enum Error { #[error(transparent)] Config(#[from] config::ConfigError), + #[error(transparent)] + Generate(#[from] generate::GenerateError), } #[derive(Parser)] @@ -25,6 +28,9 @@ enum Commands { /// Modify or validate the config files #[command(subcommand)] Config(config::Commands), + /// Generate keys + #[command(subcommand)] + Generate(generate::Commands), } pub(super) async fn run() -> Result<(), Error> { @@ -33,6 +39,7 @@ pub(super) async fn run() -> Result<(), Error> { match args.command { Commands::Version => version::run(), Commands::Config(subcommand) => config::run(subcommand).await?, + Commands::Generate(subcommand) => generate::run(subcommand)?, } Ok(())