feat(BL-P2-093): live-sync actor_ctx.user_id on cloud switch#86
Merged
Conversation
BL-P2-085 Phase 7 wired actor_ctx.cloud through the shared RwLock so the worker's cross-project block audit picks up runtime cloud switches, but left actor_ctx.user_id pinned to the wire-startup wire_username. In a multi-cloud rescope flow, every audit entry emitted after the switch attributed the block to the previous user_id — and because fingerprint v1 canonical includes `user`, dedup also broke (the same actor across two clouds produced two distinct fingerprints). This change threads the Keystone token's `user.id` UUID through: - Token.user_id: new field, populated by parse_token from the Keystone response's optional `user` block (empty string fallback when missing). - AppEvent::ContextChanged.user_id: carried alongside target so the handler in App::handle_event refreshes the shared actor_ctx. - Empty user_id is treated as "no change" so legacy fixtures, mocks, and rescope paths that lack a user block don't overwrite a valid prior value. - main.rs now prefers the Keystone UUID at startup over wire_username (still falls back to "unknown" if both are unavailable). TDD: added test_context_changed_updates_actor_user_id covering both cloud and user_id propagation through the ContextChanged handler. test_parse_token_no_catalog now asserts the empty-fallback behavior; test_parse_token_from_keystone_response asserts the populated path. cargo test = 1493 passed (1492 → +1). clippy clean. fmt clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codex review P2: `Token.user_id` deserializes to `""` via `#[serde(default)]` for tokens cached before BL-P2-093. `KeystoneAuthAdapter::new` loads those straight into `token_map`, so a post-upgrade switch into the cached scope emits `AppEvent::ContextChanged` with an empty `user_id` and `App::handle_event` preserves the prior `actor_ctx.user_id` — defeating the live-sync invariant the rest of this PR establishes. `load_token_file` now treats `user_id.is_empty()` as a cache miss so the next `get_token` reauths against Keystone, parses the fresh `user.id`, and overwrites the file via the existing `save_token` path on the auth-success arm. The legacy file is intentionally NOT auto-deleted (only expired tokens are) so a transient reauth failure doesn't wipe a still-valid catalog/roles payload that was recoverable. Tests: bumped `sample_token` user_id to a non-empty fixture, added `test_load_legacy_token_without_user_id_treated_as_cache_miss` covering both the skip and the no-auto-delete behavior. cargo test = 1494 passed (+1). clippy clean. fmt clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bluejayA
added a commit
that referenced
this pull request
May 12, 2026
BL-P2-093 PR #86 codex-review surfaced one open question: App's CUD success audit entries still attribute via the wire username, while worker cross-project block entries now use the Keystone UUID. Same actor in same log can show two different `user` values, breaking grep/dedup across both streams. Registering BL-P2-095 (Medium) so the follow-up doesn't get lost. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary / 요약
user.idUUID throughToken,AppEvent::ContextChanged, and theApp::handle_eventhandler so the sharedactor_ctx.user_idfollows a runtime cloud-switch — closing the multi-cloud audit attribution gap and the fingerprint v1 dedup leak.actor_ctx.cloud만 live update되고user_id는 wire-startup에 고정되던 문제. 다른 자격증명으로 재인증한 후 cross-project block audit이 이전 user_id로 attribute되어 멀티클라우드 추적성을 망가뜨리고, 동시에 fingerprint v1 canonical (v1\|user\|active\|origin\|target\|action\|resource_id)에 포함된 user가 cloud별로 갈라져 dedup이 깨지던 부분을 해결.What changed
Token(src/port/types.rs)user_id: Stringfield,#[serde(default)]empty fallbacksrc/adapter/auth/keystone.rs)KeystoneUser { id }block;parse_tokenextractsuser.id; sample JSON + assertions updatedAppEvent::ContextChanged(src/event.rs)user_id: Stringfield with docstring naming BL-P2-093 intentApp::handle_event(src/app.rs:635)actor_ctx.user_idonly when non-empty (preserves prior value on test/rescope paths that lack a user block)App::spawn_switch{,_back}(src/app.rs:~1715/1756)snapshot.token.user_idinto the eventmain.rsstartupget_token_info()over wire username; falls back to"unknown"TDD trail
test_context_changed_updates_actor_user_id(compile error E0559 — no fielduser_idonContextChanged).cloudanduser_idreflect the event.test_parse_token_no_catalogasserts empty fallback;test_parse_token_from_keystone_responseasserts the populated path.Verification
cargo test→ 1493 passed (main = 1492 → +1 new test)cargo clippy --all-targets -- -D warnings→ cleancargo fmt --all -- --check→ cleanOut of scope (registered follow-ups)
user_idmid-session (BL-P2-052 token refresh part)Risk
Tokenconstructions (tests/mocks) populate emptyuser_idand remain GREEN under the "empty = no change" semantics inhandle_event.#[serde(default)]on the new field keeps disk-cached token JSON forward-compatible.Test plan
userreflects cloud-B's UUID and fingerprint differs from a same-user/same-pattern cloud-A entry.test_context_changed_updates_actor_user_id(cloud + user_id propagation)test_parse_token_from_keystone_response+test_parse_token_no_catalog(populated + missing user block)Refs: cargo-review branch-full report 2026-05-12 (Suggestions #1). Parent: #85 (BL-P2-085 atomic security).
🤖 Generated with Claude Code