From e3f44a0a722bc8ffa9910a8f5f0e6d9b57b4e26c Mon Sep 17 00:00:00 2001 From: Noa Date: Tue, 14 Jan 2025 16:19:08 -0600 Subject: [PATCH] Remove spacetimedb-core as a dep of cli --- Cargo.lock | 15 +++++- Cargo.toml | 3 ++ crates/auth/Cargo.toml | 13 +++++ .../{core/src/auth => auth/src}/identity.rs | 28 ++++------- crates/auth/src/lib.rs | 1 + crates/cli/Cargo.toml | 4 +- crates/cli/src/config.rs | 48 +++++++++++++++++- crates/cli/src/subcommands/call.rs | 3 +- crates/cli/src/subcommands/generate/mod.rs | 25 ++++++---- crates/cli/src/subcommands/list.rs | 2 +- crates/cli/src/util.rs | 2 +- crates/core/Cargo.toml | 4 +- crates/core/src/auth/mod.rs | 2 +- crates/core/src/auth/token_validation.rs | 2 +- crates/core/src/config.rs | 50 ------------------- crates/data-structures/Cargo.toml | 2 +- 16 files changed, 113 insertions(+), 91 deletions(-) create mode 100644 crates/auth/Cargo.toml rename crates/{core/src/auth => auth/src}/identity.rs (76%) create mode 100644 crates/auth/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index e6d9dded73f..18825738430 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4825,6 +4825,17 @@ dependencies = [ "trybuild", ] +[[package]] +name = "spacetimedb-auth" +version = "1.0.0" +dependencies = [ + "anyhow", + "serde", + "serde_with", + "spacetimedb-jsonwebtoken", + "spacetimedb-lib", +] + [[package]] name = "spacetimedb-bench" version = "1.0.0" @@ -4921,8 +4932,8 @@ dependencies = [ "serde_json", "serde_with", "slab", + "spacetimedb-auth", "spacetimedb-client-api-messages", - "spacetimedb-core", "spacetimedb-data-structures", "spacetimedb-fs-utils", "spacetimedb-jsonwebtoken", @@ -5109,6 +5120,7 @@ dependencies = [ "slab", "sled", "smallvec", + "spacetimedb-auth", "spacetimedb-client-api-messages", "spacetimedb-commitlog", "spacetimedb-data-structures", @@ -5138,7 +5150,6 @@ dependencies = [ "tokio-stream", "tokio-util", "toml 0.8.19", - "toml_edit", "tracing", "tracing-appender", "tracing-core", diff --git a/Cargo.toml b/Cargo.toml index 4d4ce9a74c4..5481e349c88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "crates/auth", "crates/bench", "crates/bindings-sys", "crates/bindings", @@ -93,6 +94,7 @@ rust-version = "1.84.0" [workspace.dependencies] spacetimedb = { path = "crates/bindings", version = "1.0.0" } +spacetimedb-auth = { path = "crates/auth", version = "1.0.0" } spacetimedb-bindings-macro = { path = "crates/bindings-macro", version = "1.0.0" } spacetimedb-bindings-sys = { path = "crates/bindings-sys", version = "1.0.0" } spacetimedb-cli = { path = "crates/cli", version = "1.0.0" } @@ -186,6 +188,7 @@ itertools = "0.12" itoa = "1" jsonwebtoken = { package = "spacetimedb-jsonwebtoken", version = "9.3.0" } junction = "1" +jwks = { package = "spacetimedb-jwks", version = "0.1.3" } lazy_static = "1.4.0" log = "0.4.17" memchr = "2" diff --git a/crates/auth/Cargo.toml b/crates/auth/Cargo.toml new file mode 100644 index 00000000000..1885480f4bc --- /dev/null +++ b/crates/auth/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "spacetimedb-auth" +version.workspace = true +edition.workspace = true +rust-version.workspace = true + +[dependencies] +spacetimedb-lib.workspace = true + +anyhow.workspace = true +serde.workspace = true +serde_with.workspace = true +jsonwebtoken.workspace = true diff --git a/crates/core/src/auth/identity.rs b/crates/auth/src/identity.rs similarity index 76% rename from crates/core/src/auth/identity.rs rename to crates/auth/src/identity.rs index 48a5b2dca95..0e8c9681e90 100644 --- a/crates/core/src/auth/identity.rs +++ b/crates/auth/src/identity.rs @@ -1,12 +1,10 @@ -use crate::identity::Identity; pub use jsonwebtoken::errors::Error as JwtError; pub use jsonwebtoken::errors::ErrorKind as JwtErrorKind; pub use jsonwebtoken::{DecodingKey, EncodingKey}; use serde::{Deserialize, Serialize}; +use spacetimedb_lib::Identity; use std::time::SystemTime; -use super::token_validation::TokenValidationError; - // These are the claims that can be attached to a request/connection. #[serde_with::serde_as] #[derive(Debug, Serialize, Deserialize)] @@ -49,39 +47,33 @@ pub struct IncomingClaims { } impl TryInto for IncomingClaims { - type Error = TokenValidationError; + type Error = anyhow::Error; - fn try_into(self) -> Result { + fn try_into(self) -> anyhow::Result { // The issuer and subject must be less than 128 bytes. if self.issuer.len() > 128 { - return Err(TokenValidationError::Other(anyhow::anyhow!( - "Issuer too long: {:?}", - self.issuer - ))); + return Err(anyhow::anyhow!("Issuer too long: {:?}", self.issuer)); } if self.subject.len() > 128 { - return Err(TokenValidationError::Other(anyhow::anyhow!( - "Subject too long: {:?}", - self.subject - ))); + return Err(anyhow::anyhow!("Subject too long: {:?}", self.subject)); } // The issuer and subject must be non-empty. if self.issuer.is_empty() { - return Err(TokenValidationError::Other(anyhow::anyhow!("Issuer empty"))); + return Err(anyhow::anyhow!("Issuer empty")); } if self.subject.is_empty() { - return Err(TokenValidationError::Other(anyhow::anyhow!("Subject empty"))); + return Err(anyhow::anyhow!("Subject empty")); } let computed_identity = Identity::from_claims(&self.issuer, &self.subject); // If an identity is provided, it must match the computed identity. if let Some(token_identity) = self.identity { if token_identity != computed_identity { - return Err(TokenValidationError::Other(anyhow::anyhow!( + return Err(anyhow::anyhow!( "Identity mismatch: token identity {:?} does not match computed identity {:?}", token_identity, - computed_identity - ))); + computed_identity, + )); } } diff --git a/crates/auth/src/lib.rs b/crates/auth/src/lib.rs new file mode 100644 index 00000000000..db53a0c9064 --- /dev/null +++ b/crates/auth/src/lib.rs @@ -0,0 +1 @@ +pub mod identity; diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 52161dedf2a..44ec4157ea1 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -21,9 +21,9 @@ bench = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +spacetimedb-auth.workspace = true spacetimedb-client-api-messages.workspace = true -spacetimedb-core.workspace = true -spacetimedb-data-structures.workspace = true +spacetimedb-data-structures = { workspace = true, features = ["serde"] } spacetimedb-fs-utils.workspace = true spacetimedb-lib.workspace = true spacetimedb-paths.workspace = true diff --git a/crates/cli/src/config.rs b/crates/cli/src/config.rs index 0165bcf9d80..1e709b892f6 100644 --- a/crates/cli/src/config.rs +++ b/crates/cli/src/config.rs @@ -2,7 +2,6 @@ use crate::errors::CliError; use crate::util::{contains_protocol, host_or_url_to_host_and_protocol}; use anyhow::Context; use jsonwebtoken::DecodingKey; -use spacetimedb::config::{set_opt_value, set_table_opt_value}; use spacetimedb_fs_utils::atomic_write; use spacetimedb_paths::cli::CliTomlPath; use std::collections::HashMap; @@ -822,6 +821,53 @@ Update the server's fingerprint with: } } +/// Update the value of a key in a `TOML` document, preserving the formatting and comments of the original value. +/// +/// ie: +/// +/// ```toml;no_run +/// # Moving key = value to key = new_value +/// old = "value" # Comment +/// new = "new_value" # Comment +/// ``` +fn copy_value_with_decor(old_value: Option<&toml_edit::Item>, new_value: &str) -> toml_edit::Item { + match old_value { + Some(toml_edit::Item::Value(toml_edit::Value::String(old_value))) => { + // Creates a new `toml_edit::Value` with the same formatting as the old value. + let mut new = toml_edit::Value::String(toml_edit::Formatted::new(new_value.to_string())); + let decor = new.decor_mut(); + // Copy the comments and formatting from the old value. + *decor = old_value.decor().clone(); + new.into() + } + _ => new_value.into(), + } +} + +/// Set the value of a key in a `TOML` document, removing the key if the value is `None`. +/// +/// **NOTE**: This function will preserve the formatting and comments of the original value. +pub fn set_opt_value(doc: &mut toml_edit::DocumentMut, key: &str, value: Option<&str>) { + let old_value = doc.get(key); + if let Some(new) = value { + doc[key] = copy_value_with_decor(old_value, new); + } else { + doc.remove(key); + } +} + +/// Set the value of a key in a `TOML` table, removing the key if the value is `None`. +/// +/// **NOTE**: This function will preserve the formatting and comments of the original value. +pub fn set_table_opt_value(table: &mut toml_edit::Table, key: &str, value: Option<&str>) { + let old_value = table.get(key); + if let Some(new) = value { + table[key] = copy_value_with_decor(old_value, new); + } else { + table.remove(key); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/cli/src/subcommands/call.rs b/crates/cli/src/subcommands/call.rs index d3b657cc452..c32a3944c41 100644 --- a/crates/cli/src/subcommands/call.rs +++ b/crates/cli/src/subcommands/call.rs @@ -7,9 +7,8 @@ use anyhow::{bail, Context, Error}; use clap::{Arg, ArgMatches}; use convert_case::{Case, Casing}; use itertools::Itertools; -use spacetimedb::Identity; use spacetimedb_lib::sats::{self, AlgebraicType, Typespace}; -use spacetimedb_lib::ProductTypeElement; +use spacetimedb_lib::{Identity, ProductTypeElement}; use spacetimedb_schema::def::{ModuleDef, ReducerDef}; use std::fmt::Write; diff --git a/crates/cli/src/subcommands/generate/mod.rs b/crates/cli/src/subcommands/generate/mod.rs index c20047e1334..501f249e1ff 100644 --- a/crates/cli/src/subcommands/generate/mod.rs +++ b/crates/cli/src/subcommands/generate/mod.rs @@ -1,11 +1,11 @@ #![warn(clippy::uninlined_format_args)] +use anyhow::Context; use clap::parser::ValueSource; use clap::Arg; use clap::ArgAction::Set; use core::mem; use fs_err as fs; -use spacetimedb::host::wasmtime::{Mem, MemView, WasmPointee as _}; use spacetimedb_lib::de::serde::DeserializeWrapper; use spacetimedb_lib::{bsatn, RawModuleDefV8}; use spacetimedb_lib::{RawModuleDef, MODULE_ABI_MAJOR_VERSION}; @@ -298,13 +298,13 @@ fn extract_descriptions_from_module(module: wasmtime::Module) -> anyhow::Result< message_ptr: u32, message_len: u32| { let (mem, _) = WasmCtx::mem_env(&mut caller); - let slice = mem.deref_slice(message_ptr, message_len).unwrap(); + let slice = deref_slice(mem, message_ptr, message_len).unwrap(); println!("from wasm: {}", String::from_utf8_lossy(slice)); }, )?; linker.func_wrap(module_name, "bytes_sink_write", WasmCtx::bytes_sink_write)?; let instance = linker.instantiate(&mut store, &module)?; - let memory = Mem::extract(&instance, &mut store)?; + let memory = instance.get_memory(&mut store, "memory").context("no memory export")?; store.data_mut().mem = Some(memory); let mut preinits = instance @@ -329,19 +329,26 @@ fn extract_descriptions_from_module(module: wasmtime::Module) -> anyhow::Result< } struct WasmCtx { - mem: Option, + mem: Option, sink: Vec, } +fn deref_slice(mem: &[u8], offset: u32, len: u32) -> anyhow::Result<&[u8]> { + anyhow::ensure!(offset != 0, "ptr is null"); + mem.get(offset as usize..) + .and_then(|s| s.get(..len as usize)) + .context("pointer out of bounds") +} + impl WasmCtx { - pub fn get_mem(&self) -> Mem { + pub fn get_mem(&self) -> wasmtime::Memory { self.mem.expect("Initialized memory") } - fn mem_env<'a>(ctx: impl Into>) -> (&'a mut MemView, &'a mut Self) { + fn mem_env<'a>(ctx: impl Into>) -> (&'a mut [u8], &'a mut Self) { let ctx = ctx.into(); let mem = ctx.data().get_mem(); - mem.view_and_store_mut(ctx) + mem.data_and_store_mut(ctx) } pub fn bytes_sink_write( @@ -357,9 +364,9 @@ impl WasmCtx { let (mem, env) = Self::mem_env(&mut caller); // Read `buffer_len`, i.e., the capacity of `buffer` pointed to by `buffer_ptr`. - let buffer_len = u32::read_from(mem, buffer_len_ptr)?; + let buffer_len = u32::from_le_bytes(deref_slice(mem, buffer_len_ptr, 4)?.try_into().unwrap()); // Write `buffer` to `sink`. - let buffer = mem.deref_slice(buffer_ptr, buffer_len)?; + let buffer = deref_slice(mem, buffer_ptr, buffer_len)?; env.sink.extend(buffer); Ok(0) diff --git a/crates/cli/src/subcommands/list.rs b/crates/cli/src/subcommands/list.rs index d7c7dafa7a7..8080af3c790 100644 --- a/crates/cli/src/subcommands/list.rs +++ b/crates/cli/src/subcommands/list.rs @@ -7,7 +7,7 @@ use crate::Config; use anyhow::Context; use clap::{ArgMatches, Command}; use serde::Deserialize; -use spacetimedb::Identity; +use spacetimedb_lib::Identity; use tabled::{ settings::{object::Columns, Alignment, Modify, Style}, Table, Tabled, diff --git a/crates/cli/src/util.rs b/crates/cli/src/util.rs index 491c71b7a9a..20e0591ca8a 100644 --- a/crates/cli/src/util.rs +++ b/crates/cli/src/util.rs @@ -1,7 +1,7 @@ use anyhow::Context; use base64::{engine::general_purpose::STANDARD_NO_PAD as BASE_64_STD_NO_PAD, Engine as _}; use reqwest::{RequestBuilder, Url}; -use spacetimedb::auth::identity::{IncomingClaims, SpacetimeIdentityClaims}; +use spacetimedb_auth::identity::{IncomingClaims, SpacetimeIdentityClaims}; use spacetimedb_client_api_messages::name::GetNamesResponse; use spacetimedb_lib::Identity; use std::io::Write; diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 011a836a4b6..f7a2e48f764 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -15,6 +15,7 @@ path = "src/lib.rs" # The source file of the target. bench = false [dependencies] +spacetimedb-auth.workspace = true spacetimedb-data-structures.workspace = true spacetimedb-lib = { workspace = true, features = ["serde", "metrics_impls"] } spacetimedb-client-api-messages.workspace = true @@ -95,7 +96,6 @@ tokio-util.workspace = true tokio.workspace = true tokio-stream = "0.1" toml.workspace = true -toml_edit.workspace = true tracing-appender.workspace = true tracing-core.workspace = true tracing-flame.workspace = true @@ -107,7 +107,7 @@ url.workspace = true urlencoding.workspace = true uuid.workspace = true wasmtime.workspace = true -jwks = { package = "spacetimedb-jwks", version = "0.1.3" } +jwks.workspace = true async_cache = "0.3.1" faststr = "0.2.23" diff --git a/crates/core/src/auth/mod.rs b/crates/core/src/auth/mod.rs index 73f393aab20..f9c381902e1 100644 --- a/crates/core/src/auth/mod.rs +++ b/crates/core/src/auth/mod.rs @@ -6,7 +6,7 @@ use spacetimedb_paths::cli::{PrivKeyPath, PubKeyPath}; use crate::config::CertificateAuthority; -pub mod identity; +pub use spacetimedb_auth::identity; pub mod token_validation; /// JWT verification and signing keys. diff --git a/crates/core/src/auth/token_validation.rs b/crates/core/src/auth/token_validation.rs index 27b6a786eb5..d9700c739ad 100644 --- a/crates/core/src/auth/token_validation.rs +++ b/crates/core/src/auth/token_validation.rs @@ -157,7 +157,7 @@ impl TokenValidator for DecodingKey { let data = decode::(token, self, &validation)?; let claims = data.claims; - claims.try_into() + claims.try_into().map_err(TokenValidationError::Other) } } diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index a89d7087f49..d0becfcc0a7 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -1,9 +1,6 @@ use std::path::Path; use std::{fmt, io}; -use toml; -use toml_edit; - use spacetimedb_lib::ConnectionId; use spacetimedb_paths::cli::{ConfigDir, PrivKeyPath, PubKeyPath}; use spacetimedb_paths::server::{ConfigToml, MetadataTomlPath}; @@ -107,50 +104,3 @@ pub struct LogConfig { #[serde(default)] pub directives: Vec, } - -/// Update the value of a key in a `TOML` document, preserving the formatting and comments of the original value. -/// -/// ie: -/// -/// ```toml;no_run -/// # Moving key = value to key = new_value -/// old = "value" # Comment -/// new = "new_value" # Comment -/// ``` -fn copy_value_with_decor(old_value: Option<&toml_edit::Item>, new_value: &str) -> toml_edit::Item { - match old_value { - Some(toml_edit::Item::Value(toml_edit::Value::String(old_value))) => { - // Creates a new `toml_edit::Value` with the same formatting as the old value. - let mut new = toml_edit::Value::String(toml_edit::Formatted::new(new_value.to_string())); - let decor = new.decor_mut(); - // Copy the comments and formatting from the old value. - *decor = old_value.decor().clone(); - new.into() - } - _ => new_value.into(), - } -} - -/// Set the value of a key in a `TOML` document, removing the key if the value is `None`. -/// -/// **NOTE**: This function will preserve the formatting and comments of the original value. -pub fn set_opt_value(doc: &mut toml_edit::DocumentMut, key: &str, value: Option<&str>) { - let old_value = doc.get(key); - if let Some(new) = value { - doc[key] = copy_value_with_decor(old_value, new); - } else { - doc.remove(key); - } -} - -/// Set the value of a key in a `TOML` table, removing the key if the value is `None`. -/// -/// **NOTE**: This function will preserve the formatting and comments of the original value. -pub fn set_table_opt_value(table: &mut toml_edit::Table, key: &str, value: Option<&str>) { - let old_value = table.get(key); - if let Some(new) = value { - table[key] = copy_value_with_decor(old_value, new); - } else { - table.remove(key); - } -} diff --git a/crates/data-structures/Cargo.toml b/crates/data-structures/Cargo.toml index f432dc024a2..d5e52a44e26 100644 --- a/crates/data-structures/Cargo.toml +++ b/crates/data-structures/Cargo.toml @@ -6,7 +6,7 @@ license-file = "LICENSE" description = "Assorted data structures used in spacetimedb" [features] -serde = ["dep:serde"] +serde = ["dep:serde", "hashbrown/serde"] [dependencies] ahash.workspace = true