diff options
author | Vika <vika@fireburn.ru> | 2022-09-19 17:30:38 +0300 |
---|---|---|
committer | Vika <vika@fireburn.ru> | 2022-09-19 17:30:38 +0300 |
commit | 66049566ae865e1a4bd049257d6afc0abded16e9 (patch) | |
tree | 6013a26fa98a149d103eb4402ca91d698ef02ac2 /kittybox-rs/util | |
parent | 696458657b26032e6e2a987c059fd69aaa10508d (diff) | |
download | kittybox-66049566ae865e1a4bd049257d6afc0abded16e9.tar.zst |
feat: indieauth support
Working: - Tokens and codes - Authenticating with a password Not working: - Setting the password (need to patch onboarding) - WebAuthn (the JavaScript is too complicated)
Diffstat (limited to 'kittybox-rs/util')
-rw-r--r-- | kittybox-rs/util/Cargo.toml | 26 | ||||
-rw-r--r-- | kittybox-rs/util/src/error.rs | 2 | ||||
-rw-r--r-- | kittybox-rs/util/src/lib.rs | 82 |
3 files changed, 98 insertions, 12 deletions
diff --git a/kittybox-rs/util/Cargo.toml b/kittybox-rs/util/Cargo.toml index cdad17f..f36b6d8 100644 --- a/kittybox-rs/util/Cargo.toml +++ b/kittybox-rs/util/Cargo.toml @@ -5,16 +5,18 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -[dependencies.serde] # A generic serialization/deserialization framework -version = "^1.0.125" -features = ["derive"] - -[dependencies.serde_json] -version = "^1.0.64" +[features] +fs = ["rand", "tokio", "tokio/fs"] -[dependencies.axum-core] -version = "^0.2.6" - -[dependencies.http] -version = "^0.2.7" \ No newline at end of file +[dependencies] +serde = { version = "^1.0.125", features = ["derive"] } +serde_json = "^1.0.64" +axum-core = "^0.2.6" +http = "^0.2.7" +[dependencies.rand] +version = "^0.8.5" +optional = true +[dependencies.tokio] +version = "^1.16.1" +features = ["tracing"] +optional = true diff --git a/kittybox-rs/util/src/error.rs b/kittybox-rs/util/src/error.rs index 79f43ef..7edf176 100644 --- a/kittybox-rs/util/src/error.rs +++ b/kittybox-rs/util/src/error.rs @@ -4,6 +4,7 @@ use axum_core::response::{Response, IntoResponse}; #[derive(Serialize, Deserialize, PartialEq, Debug)] #[serde(rename_all = "snake_case")] +/// Kinds of errors that can happen within a Micropub operation. pub enum ErrorType { /// An erroneous attempt to create something that already exists. AlreadyExists, @@ -27,6 +28,7 @@ pub enum ErrorType { #[derive(Serialize, Deserialize, Debug)] pub struct MicropubError { pub error: ErrorType, + // TODO use Cow<'static, str> to save on heap allocations pub error_description: String, } diff --git a/kittybox-rs/util/src/lib.rs b/kittybox-rs/util/src/lib.rs index debe589..f30dc2d 100644 --- a/kittybox-rs/util/src/lib.rs +++ b/kittybox-rs/util/src/lib.rs @@ -1,3 +1,9 @@ +#![warn(missing_docs)] +//! Small things that couldn't fit elsewhere in Kittybox, yet may be +//! useful on their own or in multiple Kittybox crates. +//! +//! Some things are gated behind features, namely: +//! - `fs` - enables use of filesystem-related utilities use serde::{Deserialize, Serialize}; #[derive(Clone, Serialize, Deserialize)] @@ -17,5 +23,81 @@ pub struct MicropubChannel { pub name: String, } +/// Common errors from the IndieWeb protocols that can be reused between modules. pub mod error; pub use error::{ErrorType, MicropubError}; + +/// Common data-types useful in creating smart authentication systems. +pub mod auth { + #[derive(PartialEq, Eq, Hash, Clone, Copy)] + pub enum EnrolledCredential { + /// An indicator that a password is enrolled. Passwords can be + /// used to recover from a lost token. + Password, + /// An indicator that one or more WebAuthn credentials were + /// enrolled. + WebAuthn + } +} + +#[cfg(feature = "fs")] +/// Commonly-used operations with the file system in Kittybox's +/// underlying storage mechanisms. +pub mod fs { + use std::io::{self, Result}; + use std::path::{Path, PathBuf}; + use rand::{Rng, distributions::Alphanumeric}; + use tokio::fs; + + /// Create a temporary file named `temp.[a-zA-Z0-9]{length}` in + /// the given location and immediately open it. Returns the + /// filename and the corresponding file handle. It is the caller's + /// responsibility to clean up the temporary file when it is no + /// longer needed. + /// + /// Uses [`OpenOptions::create_new`][fs::OpenOptions::create_new] + /// to detect filename collisions, in which case it will + /// automatically retry until the operation succeeds. + /// + /// # Errors + /// + /// Returns the underlying [`io::Error`] if the operation fails + /// due to reasons other than filename collision. + pub async fn mktemp<T, B>(dir: T, basename: B, length: usize) -> Result<(PathBuf, fs::File)> + where + T: AsRef<Path>, + B: Into<Option<&'static str>> + { + let dir = dir.as_ref(); + let basename = basename.into().unwrap_or(""); + fs::create_dir_all(dir).await?; + + loop { + let filename = dir.join(format!( + "{}{}{}", + basename, + if basename.is_empty() { "" } else { "." }, + { + let string = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(length) + .collect::<Vec<u8>>(); + String::from_utf8(string).unwrap() + } + )); + + match fs::OpenOptions::new() + .create_new(true) + .write(true) + .open(&filename) + .await + { + Ok(file) => return Ok((filename, file)), + Err(err) => match err.kind() { + io::ErrorKind::AlreadyExists => continue, + _ => return Err(err) + } + } + } + } +} |