fix(swarm-authelia-bridge): lock the load-insert-publish sequence
This commit is contained in:
parent
d30f149338
commit
405ed85550
1 changed files with 20 additions and 0 deletions
|
|
@ -86,6 +86,17 @@ fn read_secret_file(path: &str) -> Result<String> {
|
|||
struct AppState {
|
||||
config: Config,
|
||||
http: reqwest::Client,
|
||||
/// Serializes `handle`'s load → insert → publish sequence. Without this,
|
||||
/// two `EnsureAgentIdentity` requests landing close together (real: the
|
||||
/// controller's job worker claims and spawns nodes without waiting for
|
||||
/// each to finish, and `SwarmResourceKind` declares no resource dep
|
||||
/// between two `CreateIdentity` jobs, so they run concurrently) both
|
||||
/// `load_store` the same snapshot, both insert their own agent, and
|
||||
/// whichever `publish`es second silently drops the first agent's entry —
|
||||
/// the store is a plain file, not a database with its own concurrency
|
||||
/// control. `tokio::sync::Mutex`, not `std`'s: held across the `.await`s
|
||||
/// in `generate_password` and `publish`.
|
||||
write_lock: tokio::sync::Mutex<()>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
|
@ -101,6 +112,7 @@ async fn main() -> Result<()> {
|
|||
let state = Arc::new(AppState {
|
||||
config,
|
||||
http: reqwest::Client::new(),
|
||||
write_lock: tokio::sync::Mutex::new(()),
|
||||
});
|
||||
|
||||
let app = Router::new()
|
||||
|
|
@ -187,6 +199,14 @@ async fn authorize(state: &AppState, headers: &HeaderMap) -> Result<(), Response
|
|||
async fn handle(state: &AppState, name: String) -> Result<BridgeResponse> {
|
||||
store::validate_username(&name)?;
|
||||
let cfg = &state.config;
|
||||
|
||||
// Held across the whole load → insert → publish sequence, not just the
|
||||
// write — two concurrent `EnsureAgentIdentity` calls must not both read
|
||||
// the same on-disk snapshot before either publishes. See the field's
|
||||
// doc comment on `AppState::write_lock` for why this is reachable in
|
||||
// practice, not just theoretically.
|
||||
let _write_guard = state.write_lock.lock().await;
|
||||
|
||||
let mut user_store = store::load_store(&cfg.store_path, &cfg.users_file)?;
|
||||
if user_store.users.contains_key(&name) {
|
||||
return Ok(BridgeResponse::AlreadyExists);
|
||||
|
|
|
|||
Loading…
Reference in a new issue