mirror of
https://github.com/Blah-IM/blahrs.git
synced 2025-05-01 00:31:09 +00:00
feat(blahctl): add identity validate
This commit is contained in:
parent
82ab7f7d2c
commit
023da5ecb2
1 changed files with 86 additions and 1 deletions
|
@ -19,8 +19,11 @@ use rusqlite::{named_params, Connection};
|
|||
use tokio::runtime::Runtime;
|
||||
|
||||
/// NB. Sync with docs of [`User::url`].
|
||||
// FIXME: Remove old interface.
|
||||
const KEY_URL_SUBPATH: &str = "/.well-known/blah/key";
|
||||
|
||||
const USER_AGENT: &str = concat!("blahctl/", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
/// Control or manage Blah Chat Server.
|
||||
#[derive(Debug, clap::Parser)]
|
||||
#[clap(about, version = option_env!("CFG_RELEASE").unwrap_or(env!("CARGO_PKG_VERSION")))]
|
||||
|
@ -77,6 +80,11 @@ enum IdCommand {
|
|||
#[arg(long)]
|
||||
id_url: IdUrl,
|
||||
},
|
||||
/// Validate identity description from a JSON file or URL.
|
||||
Validate {
|
||||
#[command(flatten)]
|
||||
id_desc_args: IdDescArgs,
|
||||
},
|
||||
/// Add an action subkey to an existing identity description.
|
||||
AddActKey {
|
||||
/// The identity description JSON file to modify.
|
||||
|
@ -101,6 +109,65 @@ enum IdCommand {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Args)]
|
||||
#[group(required = true, multiple = false)]
|
||||
struct IdDescArgs {
|
||||
/// The identity URL to check.
|
||||
///
|
||||
/// It should be a HTTPS domain with a top-level path `/`.
|
||||
#[arg(long)]
|
||||
id_url: Option<IdUrl>,
|
||||
|
||||
/// The identity description JSON path to check.
|
||||
#[arg(long, short = 'f')]
|
||||
desc_file: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl IdDescArgs {
|
||||
fn load(&self, rt: &Runtime) -> Result<UserIdentityDesc> {
|
||||
const LARGE_BODY_SIZE: usize = 64 << 10; // 64KiB
|
||||
|
||||
let text = if let Some(url) = &self.id_url {
|
||||
if url.scheme() == "http" {
|
||||
// TODO: Verbosity control.
|
||||
eprintln!("warning: id_url has scheme http, which will be rejected by most server");
|
||||
}
|
||||
if url.port().is_some() {
|
||||
eprintln!("warning: id_url has custom port, which will be rejected by most server");
|
||||
}
|
||||
|
||||
let url = url
|
||||
.join(UserIdentityDesc::WELL_KNOWN_PATH)
|
||||
.expect("IdUrl must be a valid base");
|
||||
rt.block_on(async {
|
||||
anyhow::Ok(
|
||||
build_client()?
|
||||
.get(url.clone())
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.text()
|
||||
.await?,
|
||||
)
|
||||
})
|
||||
.with_context(|| format!("failed to GET {url}"))?
|
||||
} else if let Some(path) = &self.desc_file {
|
||||
fs::read_to_string(path).context("failed to read from desc_file")?
|
||||
} else {
|
||||
unreachable!("enforced by clap");
|
||||
};
|
||||
|
||||
if text.len() > LARGE_BODY_SIZE {
|
||||
eprintln!(
|
||||
"warning: large description size ({}KiB), which will be rejected by most server",
|
||||
LARGE_BODY_SIZE >> 10,
|
||||
);
|
||||
}
|
||||
|
||||
serde_json::from_str(&text).context("failed to parse identity description")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
enum DbCommand {
|
||||
/// Create and initialize database.
|
||||
|
@ -180,7 +247,13 @@ impl User {
|
|||
fs::read_to_string(path).context("failed to read key file")?
|
||||
} else if let Some(url) = &self.url {
|
||||
let url = url.join(KEY_URL_SUBPATH)?;
|
||||
reqwest::get(url).await?.error_for_status()?.text().await?
|
||||
build_client()?
|
||||
.get(url)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.text()
|
||||
.await?
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
@ -221,6 +294,14 @@ fn build_rt() -> Result<Runtime> {
|
|||
.context("failed to initialize tokio runtime")
|
||||
}
|
||||
|
||||
fn build_client() -> Result<reqwest::Client> {
|
||||
reqwest::Client::builder()
|
||||
.user_agent(USER_AGENT)
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.build()
|
||||
.context("failed to build HTTP client")
|
||||
}
|
||||
|
||||
fn main_id(cmd: IdCommand) -> Result<()> {
|
||||
match cmd {
|
||||
IdCommand::Generate {
|
||||
|
@ -256,6 +337,10 @@ fn main_id(cmd: IdCommand) -> Result<()> {
|
|||
.context("failed to save private key")?;
|
||||
fs::write(desc_file, &id_desc_str).context("failed to save identity description")?;
|
||||
}
|
||||
IdCommand::Validate { id_desc_args } => {
|
||||
let id_desc = id_desc_args.load(&build_rt()?)?;
|
||||
id_desc.verify(id_desc_args.id_url.as_ref(), get_timestamp())?;
|
||||
}
|
||||
IdCommand::AddActKey {
|
||||
desc_file,
|
||||
id_key_file,
|
||||
|
|
Loading…
Add table
Reference in a new issue