-
Notifications
You must be signed in to change notification settings - Fork 597
Store client credentials in a new system table #2983
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 9 commits
51ad2f2
cd6625a
4c1807f
6b98710
c65bb78
168fd84
4ecac1a
a336ba8
89955d0
14a6158
f796f2f
5df0d93
37467bf
b063fe3
9272ae4
2462a95
9f4eccd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -26,8 +26,9 @@ use derive_more::From; | |||||
use indexmap::IndexSet; | ||||||
use itertools::Itertools; | ||||||
use prometheus::{Histogram, IntGauge}; | ||||||
use scopeguard::ScopeGuard; | ||||||
use spacetimedb_auth::identity::ConnectionAuthCtx; | ||||||
use spacetimedb_client_api_messages::websocket::{ByteListLen, Compression, OneOffTable, QueryUpdate, WebsocketFormat}; | ||||||
use spacetimedb_client_api_messages::websocket::{ByteListLen, Compression, OneOffTable, QueryUpdate}; | ||||||
use spacetimedb_data_structures::error_stream::ErrorStream; | ||||||
use spacetimedb_data_structures::map::{HashCollectionExt as _, IntMap}; | ||||||
use spacetimedb_datastore::execution_context::{ExecutionContext, ReducerContext, Workload, WorkloadType}; | ||||||
|
@@ -690,13 +691,38 @@ impl ModuleHost { | |||||
let me = self.clone(); | ||||||
self.call("call_identity_connected", move |inst| { | ||||||
let reducer_lookup = me.info.module_def.lifecycle_reducer(Lifecycle::OnConnect); | ||||||
let stdb = &me.module.replica_ctx().relational_db; | ||||||
let workload = Workload::Reducer(ReducerContext { | ||||||
name: "call_identity_connected".to_owned(), | ||||||
caller_identity: caller_auth.claims.identity, | ||||||
caller_connection_id, | ||||||
timestamp: Timestamp::now(), | ||||||
arg_bsatn: Bytes::new(), | ||||||
}); | ||||||
let mut_tx = stdb.begin_mut_tx(IsolationLevel::Serializable, workload); | ||||||
let mut mut_tx = scopeguard::guard(mut_tx, |mut_tx| { | ||||||
// If we crash before committing, we need to ensure that the transaction is rolled back. | ||||||
// This is necessary to avoid leaving the database in an inconsistent state. | ||||||
log::debug!("call_identity_connected: rolling back transaction"); | ||||||
let (metrics, reducer_name) = mut_tx.rollback(); | ||||||
stdb.report_mut_tx_metrics(reducer_name, metrics, None); | ||||||
}); | ||||||
|
||||||
mut_tx | ||||||
.insert_st_client( | ||||||
caller_auth.claims.identity, | ||||||
caller_connection_id, | ||||||
&caller_auth.jwt_payload, | ||||||
) | ||||||
.map_err(DBError::from)?; | ||||||
|
||||||
if let Some((reducer_id, reducer_def)) = reducer_lookup { | ||||||
// The module defined a lifecycle reducer to handle new connections. | ||||||
// Call this reducer. | ||||||
// If the call fails (as in, something unexpectedly goes wrong with WASM execution), | ||||||
// abort the connection: we can't really recover. | ||||||
let reducer_outcome = me.call_reducer_inner_with_inst( | ||||||
Some(ScopeGuard::into_inner(mut_tx)), | ||||||
caller_auth.claims.identity, | ||||||
Some(caller_connection_id), | ||||||
None, | ||||||
|
@@ -728,38 +754,19 @@ impl ModuleHost { | |||||
} | ||||||
} else { | ||||||
// The module doesn't define a client_connected reducer. | ||||||
// Commit a transaction to update `st_clients` | ||||||
// and to ensure we always have those events paired in the commitlog. | ||||||
// We need to commit the transaction to update st_clients and st_connection_credentials. | ||||||
// | ||||||
// This is necessary to be able to disconnect clients after a server crash. | ||||||
let reducer_name = reducer_lookup | ||||||
.as_ref() | ||||||
.map(|(_, def)| &*def.name) | ||||||
.unwrap_or("__identity_connected__"); | ||||||
|
||||||
let workload = Workload::Reducer(ReducerContext { | ||||||
name: reducer_name.to_owned(), | ||||||
caller_identity: caller_auth.claims.identity, | ||||||
caller_connection_id, | ||||||
timestamp: Timestamp::now(), | ||||||
arg_bsatn: Bytes::new(), | ||||||
}); | ||||||
|
||||||
let stdb = me.module.replica_ctx().relational_db.clone(); | ||||||
stdb.with_auto_commit(workload, |mut_tx| { | ||||||
mut_tx | ||||||
.insert_st_client(caller_auth.claims.identity, caller_connection_id) | ||||||
.map_err(DBError::from)?; | ||||||
mut_tx | ||||||
.insert_st_client_credentials(caller_connection_id, &caller_auth.jwt_payload) | ||||||
.map_err(DBError::from) | ||||||
}) | ||||||
.inspect_err(|e| { | ||||||
log::error!( | ||||||
"`call_identity_connected`: fallback transaction to insert into `st_client` failed: {e:#?}" | ||||||
) | ||||||
}) | ||||||
.map_err(Into::into) | ||||||
|
||||||
// TODO: Is this being broadcast? Does it need to be, or are st_client table subscriptions | ||||||
// not allowed? | ||||||
// I don't think it was being broadcast previously. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This sounds like the kind of bug you were talking about the other day, yeah. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Created #3130 . |
||||||
stdb.finish_tx(ScopeGuard::into_inner(mut_tx), Ok(())) | ||||||
.map_err(|e: DBError| { | ||||||
log::error!("`call_identity_connected`: finish transaction failed: {e:#?}"); | ||||||
ClientConnectedError::DBError(e) | ||||||
})?; | ||||||
Ok(()) | ||||||
} | ||||||
}) | ||||||
.await | ||||||
|
@@ -813,6 +820,7 @@ impl ModuleHost { | |||||
// If it succeeds, `WasmModuleInstance::call_reducer_with_tx` has already ensured | ||||||
// that `st_client` is updated appropriately. | ||||||
let result = me.call_reducer_inner_with_inst( | ||||||
None, | ||||||
caller_identity, | ||||||
Some(caller_connection_id), | ||||||
None, | ||||||
|
@@ -917,6 +925,7 @@ impl ModuleHost { | |||||
} | ||||||
fn call_reducer_inner_with_inst( | ||||||
&self, | ||||||
tx: Option<MutTxId>, | ||||||
caller_identity: Identity, | ||||||
caller_connection_id: Option<ConnectionId>, | ||||||
client: Option<Arc<ClientConnectionSender>>, | ||||||
|
@@ -932,7 +941,7 @@ impl ModuleHost { | |||||
let caller_connection_id = caller_connection_id.unwrap_or(ConnectionId::ZERO); | ||||||
|
||||||
Ok(module_instance.call_reducer( | ||||||
None, | ||||||
tx, | ||||||
CallReducerParams { | ||||||
timestamp: Timestamp::now(), | ||||||
caller_identity, | ||||||
|
Uh oh!
There was an error while loading. Please reload this page.