Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 0 additions & 27 deletions crates/bindings-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,30 +261,3 @@ pub fn schema_type(input: StdTokenStream) -> StdTokenStream {
])
})
}

#[proc_macro_attribute]
pub fn client_visibility_filter(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream {
ok_or_compile_error(|| {
if !args.is_empty() {
return Err(syn::Error::new_spanned(
TokenStream::from(args),
"The `client_visibility_filter` attribute does not accept arguments",
));
}

let item: ItemConst = syn::parse(item)?;
let rls_ident = item.ident.clone();
let register_rls_symbol = format!("__preinit__20_register_row_level_security_{rls_ident}");

Ok(quote! {
#item

const _: () = {
#[export_name = #register_rls_symbol]
extern "C" fn __register_client_visibility_filter() {
spacetimedb::rt::register_row_level_security(#rls_ident.sql_text())
}
};
})
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@ fn init(ctx: &ReducerContext) {

#[reducer(client_connected)]
fn client_connected(ctx: &ReducerContext) {
let existing_user = ctx.db.offline_user().identity().find(ctx.sender);
let existing_user = ctx.db.offline_user().identity().find(ctx.sender());
if let Some(user) = existing_user {
ctx.db.user().insert(user);
ctx.db.offline_user().identity().delete(ctx.sender);
ctx.db.offline_user().identity().delete(ctx.sender());
return;
}
ctx.db.offline_user().insert(User {
identity: ctx.sender,
identity: ctx.sender(),
has_incremented_count: 0,
});
}

#[reducer(client_disconnected)]
fn client_disconnected(ctx: &ReducerContext) -> Result<(), String> {
let existing_user = ctx.db.user().identity().find(ctx.sender).ok_or("User not found")?;
let existing_user = ctx.db.user().identity().find(ctx.sender()).ok_or("User not found")?;
ctx.db.offline_user().insert(existing_user);
ctx.db.user().identity().delete(ctx.sender);
ctx.db.user().identity().delete(ctx.sender());
Ok(())
}

Expand All @@ -48,7 +48,7 @@ fn increment_counter(ctx: &ReducerContext) -> Result<(), String> {
counter.count += 1;
ctx.db.counter().id().update(counter);

let mut user = ctx.db.user().identity().find(ctx.sender).ok_or("User not found")?;
let mut user = ctx.db.user().identity().find(ctx.sender()).ok_or("User not found")?;
user.has_incremented_count += 1;
ctx.db.user().identity().update(user);

Expand Down
2 changes: 1 addition & 1 deletion crates/bindings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ struct PlayerAndLevel {
// At-most-one row: return Option<T>
#[view(name = my_player, public)]
fn my_player(ctx: &ViewContext) -> Option<Player> {
ctx.db.player().identity().find(ctx.sender)
ctx.db.player().identity().find(ctx.sender())
}

// Multiple rows: return Vec<T>
Expand Down
25 changes: 0 additions & 25 deletions crates/bindings/src/client_visibility_filter.rs

This file was deleted.

72 changes: 23 additions & 49 deletions crates/bindings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ use core::ops::Deref;
use spacetimedb_lib::bsatn;
use std::rc::Rc;

#[cfg(feature = "unstable")]
mod client_visibility_filter;
#[cfg(feature = "unstable")]
pub mod http;
pub mod log_stopwatch;
Expand All @@ -22,8 +20,6 @@ pub mod table;
#[doc(hidden)]
pub use spacetimedb_query_builder as query_builder;

#[cfg(feature = "unstable")]
pub use client_visibility_filter::Filter;
pub use log;
#[cfg(feature = "rand")]
pub use rand08 as rand;
Expand Down Expand Up @@ -61,44 +57,6 @@ pub type ProcedureResult = Vec<u8>;

pub use spacetimedb_bindings_macro::duration;

/// Generates code for registering a row-level security rule.
///
/// This attribute must be applied to a `const` binding of type [`Filter`].
/// It will be interpreted as a filter on the table to which it applies, for all client queries.
/// If a module contains multiple `client_visibility_filter`s for the same table,
/// they will be unioned together as if by SQL `OR`,
/// so that any row permitted by at least one filter is visible.
///
/// The `const` binding's identifier must be unique within the module.
///
/// The query follows the same syntax as a subscription query.
///
/// ## Example:
///
/// ```no_run
/// # #[cfg(target_arch = "wasm32")] mod demo {
/// use spacetimedb::{client_visibility_filter, Filter};
///
/// /// Players can only see what's in their chunk
/// #[client_visibility_filter]
/// const PLAYERS_SEE_ENTITIES_IN_SAME_CHUNK: Filter = Filter::Sql("
/// SELECT * FROM LocationState WHERE chunk_index IN (
/// SELECT chunk_index FROM LocationState WHERE entity_id IN (
/// SELECT entity_id FROM UserState WHERE identity = :sender
/// )
/// )
/// ");
/// # }
/// ```
///
/// Queries are not checked for syntactic or semantic validity
/// until they are processed by the SpacetimeDB host.
/// This means that errors in queries, such as syntax errors, type errors or unknown tables,
/// will be reported during `spacetime publish`, not at compile time.
#[cfg(feature = "unstable")]
#[doc(inline, hidden)] // TODO: RLS filters are currently unimplemented, and are not enforced.
pub use spacetimedb_bindings_macro::client_visibility_filter;

/// Declares a table with a particular row type.
///
/// This attribute is applied to a struct type with named fields.
Expand Down Expand Up @@ -662,7 +620,7 @@ pub use spacetimedb_bindings_macro::table;
///
/// #[reducer]
/// fn scheduled(ctx: &ReducerContext, args: ScheduledArgs) -> Result<(), String> {
/// if ctx.sender != ctx.identity() {
/// if ctx.sender() != ctx.identity() {
/// return Err("Reducer `scheduled` may not be invoked by clients, only via scheduling.".into());
/// }
/// // Reducer body...
Expand Down Expand Up @@ -707,7 +665,7 @@ pub use spacetimedb_bindings_macro::reducer;
/// #[procedure]
/// fn return_value(ctx: &mut ProcedureContext, arg: MyArgument) -> MyReturnValue {
/// MyReturnValue {
/// a: format!("Hello, {}", ctx.sender),
/// a: format!("Hello, {}", ctx.sender()),
/// b: ctx.timestamp,
/// }
/// }
Expand Down Expand Up @@ -816,13 +774,13 @@ pub use spacetimedb_bindings_macro::procedure;
/// // A view that selects at most one row from a table
/// #[view(name = my_player, public)]
/// fn my_player(ctx: &ViewContext) -> Option<Player> {
/// ctx.db.player().identity().find(ctx.sender)
/// ctx.db.player().identity().find(ctx.sender())
/// }
///
/// // An example of column projection
/// #[view(name = my_player_id, public)]
/// fn my_player_id(ctx: &ViewContext) -> Option<PlayerId> {
/// ctx.db.player().identity().find(ctx.sender).map(|Player { id, .. }| PlayerId { id })
/// ctx.db.player().identity().find(ctx.sender()).map(|Player { id, .. }| PlayerId { id })
/// }
///
/// // An example that is analogous to a semijoin in sql
Expand Down Expand Up @@ -894,7 +852,7 @@ impl Default for AnonymousViewContext {
/// The other is [`AnonymousViewContext`].
/// Use this type if the view depends on the caller's identity.
pub struct ViewContext {
pub sender: Identity,
sender: Identity,
pub db: LocalReadOnly,
pub from: QueryBuilder,
}
Expand All @@ -907,6 +865,11 @@ impl ViewContext {
from: QueryBuilder {},
}
}

/// The `Identity` of the client that invoked the view.
pub fn sender(&self) -> Identity {
self.sender
}
}

/// The context that any reducer is provided with.
Expand All @@ -924,7 +887,7 @@ impl ViewContext {
#[non_exhaustive]
pub struct ReducerContext {
/// The `Identity` of the client that invoked the reducer.
pub sender: Identity,
sender: Identity,

/// The time at which the reducer was started.
pub timestamp: Timestamp,
Expand Down Expand Up @@ -1014,6 +977,11 @@ impl ReducerContext {
}
}

/// The `Identity` of the client that invoked the reducer.
pub fn sender(&self) -> Identity {
self.sender
}

/// Returns the authorization information for the caller of this reducer.
pub fn sender_auth(&self) -> &AuthCtx {
&self.sender_auth
Expand Down Expand Up @@ -1124,7 +1092,7 @@ impl Deref for TxContext {
#[cfg(feature = "unstable")]
pub struct ProcedureContext {
/// The `Identity` of the client that invoked the procedure.
pub sender: Identity,
sender: Identity,

/// The time at which the procedure was started.
pub timestamp: Timestamp,
Expand Down Expand Up @@ -1162,6 +1130,12 @@ impl ProcedureContext {
counter_uuid: Cell::new(0),
}
}

/// The `Identity` of the client that invoked the procedure.
pub fn sender(&self) -> Identity {
self.sender
}

/// Read the current module's [`Identity`].
pub fn identity(&self) -> Identity {
// Hypothetically, we *could* read the module identity out of the system tables.
Expand Down
7 changes: 0 additions & 7 deletions crates/bindings/src/rt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -803,13 +803,6 @@ where
})
}

/// Registers a row-level security policy.
pub fn register_row_level_security(sql: &'static str) {
register_describer(|module| {
module.inner.add_row_level_security(sql);
})
}

/// A builder for a module.
#[derive(Default)]
pub struct ModuleBuilder {
Expand Down
28 changes: 19 additions & 9 deletions demo/Blackholio/server-rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ pub fn init(ctx: &ReducerContext) -> Result<(), String> {

#[spacetimedb::reducer(client_connected)]
pub fn connect(ctx: &ReducerContext) -> Result<(), String> {
if let Some(player) = ctx.db.logged_out_player().identity().find(&ctx.sender) {
if let Some(player) = ctx.db.logged_out_player().identity().find(&ctx.sender()) {
ctx.db.player().insert(player.clone());
ctx.db.logged_out_player().identity().delete(&player.identity);

Expand All @@ -157,7 +157,7 @@ pub fn connect(ctx: &ReducerContext) -> Result<(), String> {
}
} else {
ctx.db.player().try_insert(Player {
identity: ctx.sender,
identity: ctx.sender(),
player_id: 0,
name: String::new(),
})?;
Expand All @@ -167,10 +167,15 @@ pub fn connect(ctx: &ReducerContext) -> Result<(), String> {

#[spacetimedb::reducer(client_disconnected)]
pub fn disconnect(ctx: &ReducerContext) -> Result<(), String> {
let player = ctx.db.player().identity().find(&ctx.sender).ok_or("Player not found")?;
let player = ctx
.db
.player()
.identity()
.find(&ctx.sender())
.ok_or("Player not found")?;
let player_id = player.player_id;
ctx.db.logged_out_player().insert(player);
ctx.db.player().identity().delete(&ctx.sender);
ctx.db.player().identity().delete(&ctx.sender());

// Move any circles from the arena into logged out tables
for circle in ctx.db.circle().player_id().filter(&player_id) {
Expand All @@ -187,7 +192,7 @@ pub fn disconnect(ctx: &ReducerContext) -> Result<(), String> {
#[spacetimedb::reducer]
pub fn enter_game(ctx: &ReducerContext, name: String) -> Result<(), String> {
log::info!("Creating player with name {}", name);
let mut player: Player = ctx.db.player().identity().find(ctx.sender).ok_or("")?;
let mut player: Player = ctx.db.player().identity().find(ctx.sender()).ok_or("")?;
let player_id = player.player_id;
player.name = name;
ctx.db.player().identity().update(player);
Expand Down Expand Up @@ -234,7 +239,7 @@ pub fn respawn(ctx: &ReducerContext) -> Result<(), String> {
.db
.player()
.identity()
.find(&ctx.sender)
.find(&ctx.sender())
.ok_or("No such player found")?;

spawn_player_initial_circle(ctx, player.player_id)?;
Expand All @@ -248,7 +253,7 @@ pub fn suicide(ctx: &ReducerContext) -> Result<(), String> {
.db
.player()
.identity()
.find(&ctx.sender)
.find(&ctx.sender())
.ok_or("No such player found")?;

for circle in ctx.db.circle().player_id().filter(&player.player_id) {
Expand All @@ -260,7 +265,12 @@ pub fn suicide(ctx: &ReducerContext) -> Result<(), String> {

#[spacetimedb::reducer]
pub fn update_player_input(ctx: &ReducerContext, direction: DbVector2) -> Result<(), String> {
let player = ctx.db.player().identity().find(&ctx.sender).ok_or("Player not found")?;
let player = ctx
.db
.player()
.identity()
.find(&ctx.sender())
.ok_or("Player not found")?;
for mut circle in ctx.db.circle().player_id().filter(&player.player_id) {
circle.direction = direction.normalized();
circle.speed = direction.magnitude().clamp(0.0, 1.0);
Expand Down Expand Up @@ -467,7 +477,7 @@ pub fn player_split(ctx: &ReducerContext) -> Result<(), String> {
.db
.player()
.identity()
.find(&ctx.sender)
.find(&ctx.sender())
.ok_or("Sender has no player")?;
let circles: Vec<Circle> = ctx.db.circle().player_id().filter(&player.player_id).collect();
let mut circle_count = circles.len() as i32;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ A view can be written in Rust like so:
```rust
#[spacetimedb::view(name = my_player, public)]
fn my_player(ctx: &spacetimedb::ViewContext) -> Option<Player> {
ctx.db.player().identity().find(ctx.sender)
ctx.db.player().identity().find(ctx.sender())
}
```

Expand Down
Loading
Loading