mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
Feature/registry package index (#2623)
* include system images in compat s9pk * wip * wip * update types * wip * fix signature serialization * Add SignatureHeader conversions * finish display impl for get --------- Co-authored-by: Shadowy Super Coder <musashidisciple@proton.me>
This commit is contained in:
@@ -1,17 +1,13 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::panic::UnwindSafe;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::response::Response;
|
||||
use clap::Parser;
|
||||
use futures::{FutureExt, TryStreamExt};
|
||||
use helpers::NonDetachingJoinHandle;
|
||||
use imbl_value::InternedString;
|
||||
use itertools::Itertools;
|
||||
use rpc_toolkit::{from_fn_async, Context, HandlerArgs, HandlerExt, ParentHandler};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha512};
|
||||
use ts_rs::TS;
|
||||
use url::Url;
|
||||
|
||||
@@ -22,10 +18,15 @@ use crate::registry::asset::RegistryAsset;
|
||||
use crate::registry::context::RegistryContext;
|
||||
use crate::registry::os::index::OsVersionInfo;
|
||||
use crate::registry::os::SIG_CONTEXT;
|
||||
use crate::registry::signer::{Blake3Ed25519Signature, Signature, SignatureInfo, SignerKey};
|
||||
use crate::rpc_continuations::{RequestGuid, RpcContinuation};
|
||||
use crate::registry::signer::commitment::blake3::Blake3Commitment;
|
||||
use crate::registry::signer::sign::ed25519::Ed25519;
|
||||
use crate::registry::signer::sign::{AnySignature, AnyVerifyingKey, SignatureScheme};
|
||||
use crate::s9pk::merkle_archive::hash::VerifyingWriter;
|
||||
use crate::s9pk::merkle_archive::source::http::HttpSource;
|
||||
use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile;
|
||||
use crate::s9pk::merkle_archive::source::ArchiveSource;
|
||||
use crate::util::{Apply, Version};
|
||||
use crate::util::serde::Base64;
|
||||
use crate::util::VersionString;
|
||||
|
||||
pub fn add_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
@@ -53,39 +54,37 @@ pub fn add_api<C: Context>() -> ParentHandler<C> {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct AddAssetParams {
|
||||
#[ts(type = "string")]
|
||||
pub url: Url,
|
||||
pub signature: Signature,
|
||||
#[ts(type = "string")]
|
||||
pub version: Version,
|
||||
pub version: VersionString,
|
||||
#[ts(type = "string")]
|
||||
pub platform: InternedString,
|
||||
#[serde(default)]
|
||||
pub upload: bool,
|
||||
#[ts(type = "string")]
|
||||
pub url: Url,
|
||||
#[serde(rename = "__auth_signer")]
|
||||
pub signer: SignerKey,
|
||||
#[ts(skip)]
|
||||
pub signer: AnyVerifyingKey,
|
||||
pub signature: AnySignature,
|
||||
pub commitment: Blake3Commitment,
|
||||
}
|
||||
|
||||
async fn add_asset(
|
||||
ctx: RegistryContext,
|
||||
AddAssetParams {
|
||||
url,
|
||||
signature,
|
||||
version,
|
||||
platform,
|
||||
upload,
|
||||
url,
|
||||
signer,
|
||||
signature,
|
||||
commitment,
|
||||
}: AddAssetParams,
|
||||
accessor: impl FnOnce(&mut Model<OsVersionInfo>) -> &mut Model<BTreeMap<InternedString, RegistryAsset>>
|
||||
accessor: impl FnOnce(
|
||||
&mut Model<OsVersionInfo>,
|
||||
) -> &mut Model<BTreeMap<InternedString, RegistryAsset<Blake3Commitment>>>
|
||||
+ UnwindSafe
|
||||
+ Send,
|
||||
) -> Result<Option<RequestGuid>, Error> {
|
||||
ensure_code!(
|
||||
signature.signer() == signer,
|
||||
ErrorKind::InvalidSignature,
|
||||
"asset signature does not match request signer"
|
||||
);
|
||||
|
||||
) -> Result<(), Error> {
|
||||
signer
|
||||
.scheme()
|
||||
.verify_commitment(&signer, &commitment, SIG_CONTEXT, &signature)?;
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
let signer_guid = db.as_index().as_signers().get_signer(&signer)?;
|
||||
@@ -95,7 +94,7 @@ async fn add_asset(
|
||||
.as_versions()
|
||||
.as_idx(&version)
|
||||
.or_not_found(&version)?
|
||||
.as_signers()
|
||||
.as_authorized()
|
||||
.de()?
|
||||
.contains(&signer_guid)
|
||||
{
|
||||
@@ -109,11 +108,21 @@ async fn add_asset(
|
||||
.upsert(&platform, || {
|
||||
Ok(RegistryAsset {
|
||||
url,
|
||||
signature_info: SignatureInfo::new(SIG_CONTEXT),
|
||||
commitment: commitment.clone(),
|
||||
signatures: HashMap::new(),
|
||||
})
|
||||
})?
|
||||
.as_signature_info_mut()
|
||||
.mutate(|s| s.add_sig(&signature))?;
|
||||
.mutate(|s| {
|
||||
if s.commitment != commitment {
|
||||
Err(Error::new(
|
||||
eyre!("commitment does not match"),
|
||||
ErrorKind::InvalidSignature,
|
||||
))
|
||||
} else {
|
||||
s.signatures.insert(signer, signature);
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization))
|
||||
@@ -121,80 +130,18 @@ async fn add_asset(
|
||||
})
|
||||
.await?;
|
||||
|
||||
let guid = if upload {
|
||||
let guid = RequestGuid::new();
|
||||
let auth_guid = guid.clone();
|
||||
let signer = signature.signer();
|
||||
let hostname = ctx.hostname.clone();
|
||||
ctx.rpc_continuations
|
||||
.add(
|
||||
guid.clone(),
|
||||
RpcContinuation::rest(
|
||||
Box::new(|req| {
|
||||
async move {
|
||||
Ok(
|
||||
if async move {
|
||||
let auth_sig = base64::decode(
|
||||
req.headers().get("X-StartOS-Registry-Auth-Sig")?,
|
||||
)
|
||||
.ok()?;
|
||||
signer
|
||||
.verify_message(
|
||||
auth_guid.as_ref().as_bytes(),
|
||||
&auth_sig,
|
||||
&hostname,
|
||||
)
|
||||
.ok()?;
|
||||
|
||||
Some(())
|
||||
}
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
Response::builder()
|
||||
.status(200)
|
||||
.body(axum::body::Body::empty())
|
||||
.with_kind(ErrorKind::Network)?
|
||||
} else {
|
||||
Response::builder()
|
||||
.status(401)
|
||||
.body(axum::body::Body::empty())
|
||||
.with_kind(ErrorKind::Network)?
|
||||
},
|
||||
)
|
||||
}
|
||||
.boxed()
|
||||
}),
|
||||
Duration::from_secs(30),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
Some(guid)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(guid)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_iso(
|
||||
ctx: RegistryContext,
|
||||
params: AddAssetParams,
|
||||
) -> Result<Option<RequestGuid>, Error> {
|
||||
pub async fn add_iso(ctx: RegistryContext, params: AddAssetParams) -> Result<(), Error> {
|
||||
add_asset(ctx, params, |m| m.as_iso_mut()).await
|
||||
}
|
||||
|
||||
pub async fn add_img(
|
||||
ctx: RegistryContext,
|
||||
params: AddAssetParams,
|
||||
) -> Result<Option<RequestGuid>, Error> {
|
||||
pub async fn add_img(ctx: RegistryContext, params: AddAssetParams) -> Result<(), Error> {
|
||||
add_asset(ctx, params, |m| m.as_img_mut()).await
|
||||
}
|
||||
|
||||
pub async fn add_squashfs(
|
||||
ctx: RegistryContext,
|
||||
params: AddAssetParams,
|
||||
) -> Result<Option<RequestGuid>, Error> {
|
||||
pub async fn add_squashfs(ctx: RegistryContext, params: AddAssetParams) -> Result<(), Error> {
|
||||
add_asset(ctx, params, |m| m.as_squashfs_mut()).await
|
||||
}
|
||||
|
||||
@@ -205,11 +152,9 @@ pub struct CliAddAssetParams {
|
||||
#[arg(short = 'p', long = "platform")]
|
||||
pub platform: InternedString,
|
||||
#[arg(short = 'v', long = "version")]
|
||||
pub version: Version,
|
||||
pub version: VersionString,
|
||||
pub file: PathBuf,
|
||||
pub url: Url,
|
||||
#[arg(short = 'u', long = "upload")]
|
||||
pub upload: bool,
|
||||
}
|
||||
|
||||
pub async fn cli_add_asset(
|
||||
@@ -223,7 +168,6 @@ pub async fn cli_add_asset(
|
||||
version,
|
||||
file: path,
|
||||
url,
|
||||
upload,
|
||||
},
|
||||
..
|
||||
}: HandlerArgs<CliContext, CliAddAssetParams>,
|
||||
@@ -240,21 +184,18 @@ pub async fn cli_add_asset(
|
||||
}
|
||||
};
|
||||
|
||||
let file = tokio::fs::File::open(&path).await?.into();
|
||||
let file = MultiCursorFile::from(tokio::fs::File::open(&path).await?);
|
||||
|
||||
let mut progress = FullProgressTracker::new();
|
||||
let progress_handle = progress.handle();
|
||||
let mut sign_phase =
|
||||
progress_handle.add_phase(InternedString::intern("Signing File"), Some(10));
|
||||
let mut verify_phase =
|
||||
progress_handle.add_phase(InternedString::intern("Verifying URL"), Some(100));
|
||||
let mut index_phase = progress_handle.add_phase(
|
||||
InternedString::intern("Adding File to Registry Index"),
|
||||
Some(1),
|
||||
);
|
||||
let mut upload_phase = if upload {
|
||||
Some(progress_handle.add_phase(InternedString::intern("Uploading File"), Some(100)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let progress_task: NonDetachingJoinHandle<()> = tokio::spawn(async move {
|
||||
let mut bar = PhasedProgressBar::new(&format!("Adding {} to registry...", path.display()));
|
||||
@@ -270,70 +211,46 @@ pub async fn cli_add_asset(
|
||||
.into();
|
||||
|
||||
sign_phase.start();
|
||||
let blake3_sig =
|
||||
Blake3Ed25519Signature::sign_file(ctx.developer_key()?, &file, SIG_CONTEXT).await?;
|
||||
let size = blake3_sig.size;
|
||||
let signature = Signature::Blake3Ed25519(blake3_sig);
|
||||
let blake3 = file.blake3_mmap().await?;
|
||||
let size = file
|
||||
.size()
|
||||
.await
|
||||
.ok_or_else(|| Error::new(eyre!("failed to read file metadata"), ErrorKind::Filesystem))?;
|
||||
let commitment = Blake3Commitment {
|
||||
hash: Base64(*blake3.as_bytes()),
|
||||
size,
|
||||
};
|
||||
let signature = Ed25519.sign_commitment(ctx.developer_key()?, &commitment, SIG_CONTEXT)?;
|
||||
sign_phase.complete();
|
||||
|
||||
index_phase.start();
|
||||
let add_res = from_value::<Option<RequestGuid>>(
|
||||
ctx.call_remote::<RegistryContext>(
|
||||
&parent_method
|
||||
.into_iter()
|
||||
.chain(method)
|
||||
.chain([ext])
|
||||
.join("."),
|
||||
imbl_value::json!({
|
||||
"platform": platform,
|
||||
"version": version,
|
||||
"url": &url,
|
||||
"signature": signature,
|
||||
"upload": upload,
|
||||
}),
|
||||
)
|
||||
.await?,
|
||||
)?;
|
||||
index_phase.complete();
|
||||
verify_phase.start();
|
||||
let src = HttpSource::new(ctx.client.clone(), url.clone()).await?;
|
||||
let mut writer = verify_phase.writer(VerifyingWriter::new(
|
||||
tokio::io::sink(),
|
||||
Some((blake3::Hash::from_bytes(*commitment.hash), commitment.size)),
|
||||
));
|
||||
src.copy_all_to(&mut writer).await?;
|
||||
let (verifier, mut verify_phase) = writer.into_inner();
|
||||
verifier.verify().await?;
|
||||
verify_phase.complete();
|
||||
|
||||
if let Some(guid) = add_res {
|
||||
upload_phase.as_mut().map(|p| p.start());
|
||||
upload_phase.as_mut().map(|p| p.set_total(size));
|
||||
let reg_url = ctx.registry_url.as_ref().or_not_found("--registry")?;
|
||||
ctx.client
|
||||
.post(url)
|
||||
.header("X-StartOS-Registry-Token", guid.as_ref())
|
||||
.header(
|
||||
"X-StartOS-Registry-Auth-Sig",
|
||||
base64::encode(
|
||||
ctx.developer_key()?
|
||||
.sign_prehashed(
|
||||
Sha512::new_with_prefix(guid.as_ref().as_bytes()),
|
||||
Some(
|
||||
reg_url
|
||||
.host()
|
||||
.or_not_found("registry hostname")?
|
||||
.to_string()
|
||||
.as_bytes(),
|
||||
),
|
||||
)?
|
||||
.to_bytes(),
|
||||
),
|
||||
)
|
||||
.body(reqwest::Body::wrap_stream(
|
||||
tokio_util::io::ReaderStream::new(file.fetch(0, size).await?).inspect_ok(
|
||||
move |b| {
|
||||
upload_phase
|
||||
.as_mut()
|
||||
.map(|p| *p += b.len() as u64)
|
||||
.apply(|_| ())
|
||||
},
|
||||
),
|
||||
))
|
||||
.send()
|
||||
.await?;
|
||||
// upload_phase.as_mut().map(|p| p.complete());
|
||||
}
|
||||
index_phase.start();
|
||||
ctx.call_remote::<RegistryContext>(
|
||||
&parent_method
|
||||
.into_iter()
|
||||
.chain(method)
|
||||
.chain([ext])
|
||||
.join("."),
|
||||
imbl_value::json!({
|
||||
"platform": platform,
|
||||
"version": version,
|
||||
"url": &url,
|
||||
"signature": signature,
|
||||
"commitment": commitment,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
index_phase.complete();
|
||||
|
||||
progress_handle.complete();
|
||||
|
||||
|
||||
@@ -16,8 +16,11 @@ use crate::progress::{FullProgressTracker, PhasedProgressBar};
|
||||
use crate::registry::asset::RegistryAsset;
|
||||
use crate::registry::context::RegistryContext;
|
||||
use crate::registry::os::index::OsVersionInfo;
|
||||
use crate::registry::os::SIG_CONTEXT;
|
||||
use crate::registry::signer::commitment::blake3::Blake3Commitment;
|
||||
use crate::registry::signer::commitment::Commitment;
|
||||
use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile;
|
||||
use crate::util::Version;
|
||||
use crate::util::VersionString;
|
||||
|
||||
pub fn get_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
@@ -33,8 +36,7 @@ pub fn get_api<C: Context>() -> ParentHandler<C> {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct GetOsAssetParams {
|
||||
#[ts(type = "string")]
|
||||
pub version: Version,
|
||||
pub version: VersionString,
|
||||
#[ts(type = "string")]
|
||||
pub platform: InternedString,
|
||||
}
|
||||
@@ -42,10 +44,12 @@ pub struct GetOsAssetParams {
|
||||
async fn get_os_asset(
|
||||
ctx: RegistryContext,
|
||||
GetOsAssetParams { version, platform }: GetOsAssetParams,
|
||||
accessor: impl FnOnce(&Model<OsVersionInfo>) -> &Model<BTreeMap<InternedString, RegistryAsset>>
|
||||
accessor: impl FnOnce(
|
||||
&Model<OsVersionInfo>,
|
||||
) -> &Model<BTreeMap<InternedString, RegistryAsset<Blake3Commitment>>>
|
||||
+ UnwindSafe
|
||||
+ Send,
|
||||
) -> Result<RegistryAsset, Error> {
|
||||
) -> Result<RegistryAsset<Blake3Commitment>, Error> {
|
||||
accessor(
|
||||
ctx.db
|
||||
.peek()
|
||||
@@ -64,21 +68,21 @@ async fn get_os_asset(
|
||||
pub async fn get_iso(
|
||||
ctx: RegistryContext,
|
||||
params: GetOsAssetParams,
|
||||
) -> Result<RegistryAsset, Error> {
|
||||
) -> Result<RegistryAsset<Blake3Commitment>, Error> {
|
||||
get_os_asset(ctx, params, |info| info.as_iso()).await
|
||||
}
|
||||
|
||||
pub async fn get_img(
|
||||
ctx: RegistryContext,
|
||||
params: GetOsAssetParams,
|
||||
) -> Result<RegistryAsset, Error> {
|
||||
) -> Result<RegistryAsset<Blake3Commitment>, Error> {
|
||||
get_os_asset(ctx, params, |info| info.as_img()).await
|
||||
}
|
||||
|
||||
pub async fn get_squashfs(
|
||||
ctx: RegistryContext,
|
||||
params: GetOsAssetParams,
|
||||
) -> Result<RegistryAsset, Error> {
|
||||
) -> Result<RegistryAsset<Blake3Commitment>, Error> {
|
||||
get_os_asset(ctx, params, |info| info.as_squashfs()).await
|
||||
}
|
||||
|
||||
@@ -86,7 +90,7 @@ pub async fn get_squashfs(
|
||||
#[command(rename_all = "kebab-case")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliGetOsAssetParams {
|
||||
pub version: Version,
|
||||
pub version: VersionString,
|
||||
pub platform: InternedString,
|
||||
#[arg(long = "download", short = 'd')]
|
||||
pub download: Option<PathBuf>,
|
||||
@@ -112,8 +116,8 @@ async fn cli_get_os_asset(
|
||||
},
|
||||
..
|
||||
}: HandlerArgs<CliContext, CliGetOsAssetParams>,
|
||||
) -> Result<RegistryAsset, Error> {
|
||||
let res = from_value::<RegistryAsset>(
|
||||
) -> Result<RegistryAsset<Blake3Commitment>, Error> {
|
||||
let res = from_value::<RegistryAsset<Blake3Commitment>>(
|
||||
ctx.call_remote::<RegistryContext>(
|
||||
&parent_method.into_iter().chain(method).join("."),
|
||||
json!({
|
||||
@@ -124,7 +128,7 @@ async fn cli_get_os_asset(
|
||||
.await?,
|
||||
)?;
|
||||
|
||||
let validator = res.validate(res.signature_info.all_signers())?;
|
||||
res.validate(SIG_CONTEXT, res.all_signers())?;
|
||||
|
||||
if let Some(download) = download {
|
||||
let mut file = AtomicFile::new(&download, None::<&Path>)
|
||||
@@ -135,7 +139,7 @@ async fn cli_get_os_asset(
|
||||
let progress_handle = progress.handle();
|
||||
let mut download_phase =
|
||||
progress_handle.add_phase(InternedString::intern("Downloading File"), Some(100));
|
||||
download_phase.set_total(validator.size()?);
|
||||
download_phase.set_total(res.commitment.size);
|
||||
let reverify_phase = if reverify {
|
||||
Some(progress_handle.add_phase(InternedString::intern("Reverifying File"), Some(10)))
|
||||
} else {
|
||||
@@ -157,7 +161,7 @@ async fn cli_get_os_asset(
|
||||
|
||||
download_phase.start();
|
||||
let mut download_writer = download_phase.writer(&mut *file);
|
||||
res.download(ctx.client.clone(), &mut download_writer, &validator)
|
||||
res.download(ctx.client.clone(), &mut download_writer)
|
||||
.await?;
|
||||
let (_, mut download_phase) = download_writer.into_inner();
|
||||
file.save().await.with_kind(ErrorKind::Filesystem)?;
|
||||
@@ -165,8 +169,8 @@ async fn cli_get_os_asset(
|
||||
|
||||
if let Some(mut reverify_phase) = reverify_phase {
|
||||
reverify_phase.start();
|
||||
validator
|
||||
.validate_file(&MultiCursorFile::from(
|
||||
res.commitment
|
||||
.check(&MultiCursorFile::from(
|
||||
tokio::fs::File::open(download).await?,
|
||||
))
|
||||
.await?;
|
||||
|
||||
@@ -17,25 +17,47 @@ use crate::registry::asset::RegistryAsset;
|
||||
use crate::registry::context::RegistryContext;
|
||||
use crate::registry::os::index::OsVersionInfo;
|
||||
use crate::registry::os::SIG_CONTEXT;
|
||||
use crate::registry::signer::{Blake3Ed25519Signature, Signature};
|
||||
use crate::util::Version;
|
||||
use crate::registry::signer::commitment::blake3::Blake3Commitment;
|
||||
use crate::registry::signer::sign::ed25519::Ed25519;
|
||||
use crate::registry::signer::sign::{AnySignature, AnyVerifyingKey, SignatureScheme};
|
||||
use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile;
|
||||
use crate::s9pk::merkle_archive::source::ArchiveSource;
|
||||
use crate::util::serde::Base64;
|
||||
use crate::util::VersionString;
|
||||
|
||||
pub fn sign_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
.subcommand("iso", from_fn_async(sign_iso).no_cli())
|
||||
.subcommand("img", from_fn_async(sign_img).no_cli())
|
||||
.subcommand("squashfs", from_fn_async(sign_squashfs).no_cli())
|
||||
.subcommand(
|
||||
"iso",
|
||||
from_fn_async(sign_iso)
|
||||
.with_metadata("getSigner", Value::Bool(true))
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand(
|
||||
"img",
|
||||
from_fn_async(sign_img)
|
||||
.with_metadata("getSigner", Value::Bool(true))
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand(
|
||||
"squashfs",
|
||||
from_fn_async(sign_squashfs)
|
||||
.with_metadata("getSigner", Value::Bool(true))
|
||||
.no_cli(),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct SignAssetParams {
|
||||
#[ts(type = "string")]
|
||||
version: Version,
|
||||
version: VersionString,
|
||||
#[ts(type = "string")]
|
||||
platform: InternedString,
|
||||
signature: Signature,
|
||||
#[ts(skip)]
|
||||
#[serde(rename = "__auth_signer")]
|
||||
signer: AnyVerifyingKey,
|
||||
signature: AnySignature,
|
||||
}
|
||||
|
||||
async fn sign_asset(
|
||||
@@ -43,22 +65,25 @@ async fn sign_asset(
|
||||
SignAssetParams {
|
||||
version,
|
||||
platform,
|
||||
signer,
|
||||
signature,
|
||||
}: SignAssetParams,
|
||||
accessor: impl FnOnce(&mut Model<OsVersionInfo>) -> &mut Model<BTreeMap<InternedString, RegistryAsset>>
|
||||
accessor: impl FnOnce(
|
||||
&mut Model<OsVersionInfo>,
|
||||
) -> &mut Model<BTreeMap<InternedString, RegistryAsset<Blake3Commitment>>>
|
||||
+ UnwindSafe
|
||||
+ Send,
|
||||
) -> Result<(), Error> {
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
let guid = db.as_index().as_signers().get_signer(&signature.signer())?;
|
||||
let guid = db.as_index().as_signers().get_signer(&signer)?;
|
||||
if !db
|
||||
.as_index()
|
||||
.as_os()
|
||||
.as_versions()
|
||||
.as_idx(&version)
|
||||
.or_not_found(&version)?
|
||||
.as_signers()
|
||||
.as_authorized()
|
||||
.de()?
|
||||
.contains(&guid)
|
||||
{
|
||||
@@ -77,8 +102,16 @@ async fn sign_asset(
|
||||
)
|
||||
.as_idx_mut(&platform)
|
||||
.or_not_found(&platform)?
|
||||
.as_signature_info_mut()
|
||||
.mutate(|s| s.add_sig(&signature))?;
|
||||
.mutate(|s| {
|
||||
signer.scheme().verify_commitment(
|
||||
&signer,
|
||||
&s.commitment,
|
||||
SIG_CONTEXT,
|
||||
&signature,
|
||||
)?;
|
||||
s.signatures.insert(signer, signature);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
@@ -104,7 +137,7 @@ pub struct CliSignAssetParams {
|
||||
#[arg(short = 'p', long = "platform")]
|
||||
pub platform: InternedString,
|
||||
#[arg(short = 'v', long = "version")]
|
||||
pub version: Version,
|
||||
pub version: VersionString,
|
||||
pub file: PathBuf,
|
||||
}
|
||||
|
||||
@@ -134,7 +167,7 @@ pub async fn cli_sign_asset(
|
||||
}
|
||||
};
|
||||
|
||||
let file = tokio::fs::File::open(&path).await?.into();
|
||||
let file = MultiCursorFile::from(tokio::fs::File::open(&path).await?);
|
||||
|
||||
let mut progress = FullProgressTracker::new();
|
||||
let progress_handle = progress.handle();
|
||||
@@ -159,9 +192,16 @@ pub async fn cli_sign_asset(
|
||||
.into();
|
||||
|
||||
sign_phase.start();
|
||||
let blake3_sig =
|
||||
Blake3Ed25519Signature::sign_file(ctx.developer_key()?, &file, SIG_CONTEXT).await?;
|
||||
let signature = Signature::Blake3Ed25519(blake3_sig);
|
||||
let blake3 = file.blake3_mmap().await?;
|
||||
let size = file
|
||||
.size()
|
||||
.await
|
||||
.ok_or_else(|| Error::new(eyre!("failed to read file metadata"), ErrorKind::Filesystem))?;
|
||||
let commitment = Blake3Commitment {
|
||||
hash: Base64(*blake3.as_bytes()),
|
||||
size,
|
||||
};
|
||||
let signature = Ed25519.sign_commitment(ctx.developer_key()?, &commitment, SIG_CONTEXT)?;
|
||||
sign_phase.complete();
|
||||
|
||||
index_phase.start();
|
||||
|
||||
@@ -8,16 +8,16 @@ use ts_rs::TS;
|
||||
use crate::prelude::*;
|
||||
use crate::registry::asset::RegistryAsset;
|
||||
use crate::registry::context::RegistryContext;
|
||||
use crate::rpc_continuations::RequestGuid;
|
||||
use crate::util::Version;
|
||||
use crate::registry::signer::commitment::blake3::Blake3Commitment;
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::util::VersionString;
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[model = "Model<Self>"]
|
||||
#[ts(export)]
|
||||
pub struct OsIndex {
|
||||
#[ts(as = "BTreeMap::<String, OsVersionInfo>")]
|
||||
pub versions: BTreeMap<Version, OsVersionInfo>,
|
||||
pub versions: BTreeMap<VersionString, OsVersionInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)]
|
||||
@@ -29,14 +29,13 @@ pub struct OsVersionInfo {
|
||||
pub release_notes: String,
|
||||
#[ts(type = "string")]
|
||||
pub source_version: VersionRange,
|
||||
#[ts(type = "string[]")]
|
||||
pub signers: BTreeSet<RequestGuid>,
|
||||
#[ts(as = "BTreeMap::<String, RegistryAsset>")]
|
||||
pub iso: BTreeMap<InternedString, RegistryAsset>, // platform (i.e. x86_64-nonfree) -> asset
|
||||
#[ts(as = "BTreeMap::<String, RegistryAsset>")]
|
||||
pub squashfs: BTreeMap<InternedString, RegistryAsset>, // platform (i.e. x86_64-nonfree) -> asset
|
||||
#[ts(as = "BTreeMap::<String, RegistryAsset>")]
|
||||
pub img: BTreeMap<InternedString, RegistryAsset>, // platform (i.e. raspberrypi) -> asset
|
||||
pub authorized: BTreeSet<Guid>,
|
||||
#[ts(as = "BTreeMap::<String, RegistryAsset::<Blake3Commitment>>")]
|
||||
pub iso: BTreeMap<InternedString, RegistryAsset<Blake3Commitment>>, // platform (i.e. x86_64-nonfree) -> asset
|
||||
#[ts(as = "BTreeMap::<String, RegistryAsset::<Blake3Commitment>>")]
|
||||
pub squashfs: BTreeMap<InternedString, RegistryAsset<Blake3Commitment>>, // platform (i.e. x86_64-nonfree) -> asset
|
||||
#[ts(as = "BTreeMap::<String, RegistryAsset::<Blake3Commitment>>")]
|
||||
pub img: BTreeMap<InternedString, RegistryAsset<Blake3Commitment>>, // platform (i.e. raspberrypi) -> asset
|
||||
}
|
||||
|
||||
pub async fn get_os_index(ctx: RegistryContext) -> Result<OsIndex, Error> {
|
||||
|
||||
@@ -11,9 +11,9 @@ use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
use crate::registry::context::RegistryContext;
|
||||
use crate::registry::os::index::OsVersionInfo;
|
||||
use crate::registry::signer::SignerKey;
|
||||
use crate::registry::signer::sign::AnyVerifyingKey;
|
||||
use crate::util::serde::{display_serializable, HandlerExtSerde, WithIoFormat};
|
||||
use crate::util::Version;
|
||||
use crate::util::VersionString;
|
||||
|
||||
pub mod signer;
|
||||
|
||||
@@ -51,8 +51,7 @@ pub fn version_api<C: Context>() -> ParentHandler<C> {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct AddVersionParams {
|
||||
#[ts(type = "string")]
|
||||
pub version: Version,
|
||||
pub version: VersionString,
|
||||
pub headline: String,
|
||||
pub release_notes: String,
|
||||
#[ts(type = "string")]
|
||||
@@ -60,7 +59,7 @@ pub struct AddVersionParams {
|
||||
#[arg(skip)]
|
||||
#[ts(skip)]
|
||||
#[serde(rename = "__auth_signer")]
|
||||
pub signer: Option<SignerKey>,
|
||||
pub signer: Option<AnyVerifyingKey>,
|
||||
}
|
||||
|
||||
pub async fn add_version(
|
||||
@@ -86,7 +85,7 @@ pub async fn add_version(
|
||||
i.headline = headline;
|
||||
i.release_notes = release_notes;
|
||||
i.source_version = source_version;
|
||||
i.signers.extend(signer);
|
||||
i.authorized.extend(signer);
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
@@ -98,8 +97,7 @@ pub async fn add_version(
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct RemoveVersionParams {
|
||||
#[ts(type = "string")]
|
||||
pub version: Version,
|
||||
pub version: VersionString,
|
||||
}
|
||||
|
||||
pub async fn remove_version(
|
||||
@@ -124,7 +122,7 @@ pub async fn remove_version(
|
||||
pub struct GetVersionParams {
|
||||
#[ts(type = "string | null")]
|
||||
#[arg(long = "src")]
|
||||
pub source: Option<Version>,
|
||||
pub source: Option<VersionString>,
|
||||
#[ts(type = "string | null")]
|
||||
#[arg(long = "target")]
|
||||
pub target: Option<VersionRange>,
|
||||
@@ -133,7 +131,7 @@ pub struct GetVersionParams {
|
||||
pub async fn get_version(
|
||||
ctx: RegistryContext,
|
||||
GetVersionParams { source, target }: GetVersionParams,
|
||||
) -> Result<BTreeMap<Version, OsVersionInfo>, Error> {
|
||||
) -> Result<BTreeMap<VersionString, OsVersionInfo>, Error> {
|
||||
let target = target.unwrap_or(VersionRange::Any);
|
||||
ctx.db
|
||||
.peek()
|
||||
@@ -153,7 +151,10 @@ pub async fn get_version(
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn display_version_info<T>(params: WithIoFormat<T>, info: BTreeMap<Version, OsVersionInfo>) {
|
||||
pub fn display_version_info<T>(
|
||||
params: WithIoFormat<T>,
|
||||
info: BTreeMap<VersionString, OsVersionInfo>,
|
||||
) {
|
||||
use prettytable::*;
|
||||
|
||||
if let Some(format) = params.format {
|
||||
|
||||
@@ -10,9 +10,9 @@ use crate::prelude::*;
|
||||
use crate::registry::admin::display_signers;
|
||||
use crate::registry::context::RegistryContext;
|
||||
use crate::registry::signer::SignerInfo;
|
||||
use crate::rpc_continuations::RequestGuid;
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::util::serde::HandlerExtSerde;
|
||||
use crate::util::Version;
|
||||
use crate::util::VersionString;
|
||||
|
||||
pub fn signer_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
@@ -44,10 +44,8 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct VersionSignerParams {
|
||||
#[ts(type = "string")]
|
||||
pub version: Version,
|
||||
#[ts(type = "string")]
|
||||
pub signer: RequestGuid,
|
||||
pub version: VersionString,
|
||||
pub signer: Guid,
|
||||
}
|
||||
|
||||
pub async fn add_version_signer(
|
||||
@@ -67,7 +65,7 @@ pub async fn add_version_signer(
|
||||
.as_versions_mut()
|
||||
.as_idx_mut(&version)
|
||||
.or_not_found(&version)?
|
||||
.as_signers_mut()
|
||||
.as_authorized_mut()
|
||||
.mutate(|s| Ok(s.insert(signer)))?;
|
||||
|
||||
Ok(())
|
||||
@@ -87,7 +85,7 @@ pub async fn remove_version_signer(
|
||||
.as_versions_mut()
|
||||
.as_idx_mut(&version)
|
||||
.or_not_found(&version)?
|
||||
.as_signers_mut()
|
||||
.as_authorized_mut()
|
||||
.mutate(|s| Ok(s.remove(&signer)))?
|
||||
{
|
||||
return Err(Error::new(
|
||||
@@ -106,21 +104,20 @@ pub async fn remove_version_signer(
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct ListVersionSignersParams {
|
||||
#[ts(type = "string")]
|
||||
pub version: Version,
|
||||
pub version: VersionString,
|
||||
}
|
||||
|
||||
pub async fn list_version_signers(
|
||||
ctx: RegistryContext,
|
||||
ListVersionSignersParams { version }: ListVersionSignersParams,
|
||||
) -> Result<BTreeMap<RequestGuid, SignerInfo>, Error> {
|
||||
) -> Result<BTreeMap<Guid, SignerInfo>, Error> {
|
||||
let db = ctx.db.peek().await;
|
||||
db.as_index()
|
||||
.as_os()
|
||||
.as_versions()
|
||||
.as_idx(&version)
|
||||
.or_not_found(&version)?
|
||||
.as_signers()
|
||||
.as_authorized()
|
||||
.de()?
|
||||
.into_iter()
|
||||
.filter_map(|guid| {
|
||||
|
||||
Reference in New Issue
Block a user