Skip to content

Commit

Permalink
Remove spacetimedb-core as a dep of cli
Browse files Browse the repository at this point in the history
  • Loading branch information
coolreader18 committed Jan 17, 2025
1 parent 1307a51 commit 621fba2
Show file tree
Hide file tree
Showing 16 changed files with 191 additions and 73 deletions.
15 changes: 13 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"crates/auth",
"crates/bench",
"crates/bindings-sys",
"crates/bindings",
Expand Down Expand Up @@ -92,6 +93,7 @@ rust-version = "1.78.0"

[workspace.dependencies]
spacetimedb = { path = "crates/bindings", version = "1.0.0-rc3" }
spacetimedb-auth = { path = "crates/auth", version = "1.0.0-rc3" }
spacetimedb-bindings-macro = { path = "crates/bindings-macro", version = "1.0.0-rc3" }
spacetimedb-bindings-sys = { path = "crates/bindings-sys", version = "1.0.0-rc3" }
spacetimedb-cli = { path = "crates/cli", version = "1.0.0-rc3" }
Expand Down Expand Up @@ -181,6 +183,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"
Expand Down
13 changes: 13 additions & 0 deletions crates/auth/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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
89 changes: 89 additions & 0 deletions crates/auth/src/identity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
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;

// These are the claims that can be attached to a request/connection.
#[serde_with::serde_as]
#[derive(Debug, Serialize, Deserialize)]
pub struct SpacetimeIdentityClaims {
#[serde(rename = "hex_identity")]
pub identity: Identity,
#[serde(rename = "sub")]
pub subject: String,
#[serde(rename = "iss")]
pub issuer: String,
#[serde(rename = "aud")]
pub audience: Vec<String>,

/// The unix timestamp the token was issued at
#[serde_as(as = "serde_with::TimestampSeconds")]
pub iat: SystemTime,
#[serde_as(as = "Option<serde_with::TimestampSeconds>")]
pub exp: Option<SystemTime>,
}

// IncomingClaims are from the token we receive from the client.
// The signature should be verified already, but further validation is needed to have a SpacetimeIdentityClaims2.
#[serde_with::serde_as]
#[derive(Debug, Serialize, Deserialize)]
pub struct IncomingClaims {
#[serde(rename = "hex_identity")]
pub identity: Option<Identity>,
#[serde(rename = "sub")]
pub subject: String,
#[serde(rename = "iss")]
pub issuer: String,
#[serde(rename = "aud", default)]
pub audience: Vec<String>,

/// The unix timestamp the token was issued at
#[serde_as(as = "serde_with::TimestampSeconds")]
pub iat: SystemTime,
#[serde_as(as = "Option<serde_with::TimestampSeconds>")]
pub exp: Option<SystemTime>,
}

impl TryInto<SpacetimeIdentityClaims> for IncomingClaims {
type Error = anyhow::Error;

fn try_into(self) -> anyhow::Result<SpacetimeIdentityClaims> {
// The issuer and subject must be less than 128 bytes.
if self.issuer.len() > 128 {
return Err(anyhow::anyhow!("Issuer too long: {:?}", self.issuer));
}
if self.subject.len() > 128 {
return Err(anyhow::anyhow!("Subject too long: {:?}", self.subject));
}
// The issuer and subject must be non-empty.
if self.issuer.is_empty() {
return Err(anyhow::anyhow!("Issuer empty"));
}
if self.subject.is_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(anyhow::anyhow!(
"Identity mismatch: token identity {:?} does not match computed identity {:?}",
token_identity,
computed_identity,
));
}
}

Ok(SpacetimeIdentityClaims {
identity: computed_identity,
subject: self.subject,
issuer: self.issuer,
audience: self.audience,
iat: self.iat,
exp: self.exp,
})
}
}
1 change: 1 addition & 0 deletions crates/auth/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod identity;
4 changes: 2 additions & 2 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,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
Expand Down
48 changes: 47 additions & 1 deletion crates/cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -830,6 +829,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::*;
Expand Down
3 changes: 1 addition & 2 deletions crates/cli/src/subcommands/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ use anyhow::{bail, Context, Error};
use clap::{Arg, ArgMatches};
use itertools::Either;
use serde_json::Value;
use spacetimedb::Identity;
use spacetimedb_lib::de::serde::deserialize_from;
use spacetimedb_lib::sats::{AlgebraicType, AlgebraicTypeRef, Typespace};
use spacetimedb_lib::ProductTypeElement;
use spacetimedb_lib::{Identity, ProductTypeElement};
use std::fmt::Write;
use std::iter;

Expand Down
24 changes: 15 additions & 9 deletions crates/cli/src/subcommands/generate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use convert_case::{Case, Casing};
use core::mem;
use duct::cmd;
use itertools::Itertools;
use spacetimedb::host::wasmtime::{Mem, MemView, WasmPointee as _};
use spacetimedb_data_structures::map::HashSet;
use spacetimedb_lib::de::serde::DeserializeWrapper;
use spacetimedb_lib::sats::{AlgebraicType, AlgebraicTypeRef, Typespace};
Expand Down Expand Up @@ -404,13 +403,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
Expand All @@ -435,19 +434,26 @@ fn extract_descriptions_from_module(module: wasmtime::Module) -> anyhow::Result<
}

struct WasmCtx {
mem: Option<Mem>,
mem: Option<wasmtime::Memory>,
sink: Vec<u8>,
}

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<StoreContextMut<'a, Self>>) -> (&'a mut MemView, &'a mut Self) {
fn mem_env<'a>(ctx: impl Into<StoreContextMut<'a, Self>>) -> (&'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(
Expand All @@ -463,9 +469,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)
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/subcommands/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::Config;
use clap::{ArgMatches, Command};
use reqwest::StatusCode;
use serde::Deserialize;
use spacetimedb::Identity;
use spacetimedb_lib::Identity;
use tabled::{
settings::{object::Columns, Alignment, Modify, Style},
Table, Tabled,
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use base64::{
};
use reqwest::RequestBuilder;
use serde::Deserialize;
use spacetimedb::auth::identity::{IncomingClaims, SpacetimeIdentityClaims};
use spacetimedb_auth::identity::{IncomingClaims, SpacetimeIdentityClaims};
use spacetimedb_client_api_messages::name::{DnsLookupResponse, RegisterTldResult, ReverseDNSResponse};
use spacetimedb_data_structures::map::HashMap;
use spacetimedb_lib::{AlgebraicType, Identity};
Expand Down
4 changes: 2 additions & 2 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/auth/token_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ impl TokenValidator for DecodingKey {

let data = decode::<IncomingClaims>(token, self, &validation)?;
let claims = data.claims;
claims.try_into()
claims.try_into().map_err(TokenValidationError::Other)
}
}

Expand Down
Loading

0 comments on commit 621fba2

Please sign in to comment.