feat(cubesys): Task 6 — owner enforcement on mutating commands

Stamp owner_local_user on records written via prog/write and gate the
mutating paths (prog, write, del — both live and buffered txn) so a
session may only create or overwrite a record whose owner_local_user
matches its HELLO-declared identity.

Design (logical + expedient for the whole project):
- Owner is the durable record-level CubeHeader.owner_local_user field,
  so enforcement is replay-safe and works across daemon restart.
- Enforcement is opt-in/non-breaking: gated only when the session has a
  stamped identity AND the record has an owner. First write by an owner
  claims an unowned coord; a session with no identity (tests, legacy)
  writes freely.
- COMMIT re-checks owner on each buffered op before applying, so a
  concurrent cross-owner commit between BEGIN and COMMIT is rejected
  (txn is restored for retry, not silently dropped).
- seal/open (encrypted raw put/del) left ungated for now: their headers
  are not owner-stamped yet — tracked as follow-up.

Verification:
- ./check quick: EXIT=0, fmt+clippy clean, 26 cubesys tests (added
  owner_enforcement_blocks_cross_owner_overwrite,
  owner_enforcement_allows_first_claim_and_same_owner,
  for_tenant_carries_identity).
- Ad-hoc daemon verifier over real cube-server socket (LE framing):
  cross-owner overwrite + delete rejected, same-owner + first-claim
  allowed, no-HELLO legacy writes allowed. ALL PASS.
This commit is contained in:
CUBELinux-2
2026-08-11 13:41:24 -04:00
parent c36f64c78d
commit abc1b56d24
3 changed files with 186 additions and 11 deletions
+15 -2
View File
@@ -491,6 +491,17 @@ impl ConcurrentStore {
out
}
/// The `owner_local_user` stamped on the record at `key`, if it has one.
/// Used by owner enforcement (Task 6): a mutating command may only
/// overwrite a record whose owner matches the session's identity owner.
pub fn owner(&self, key: &Czyx) -> Option<String> {
self.inner
.read()
.unwrap()
.get_record(key)
.and_then(|(h, _)| h.owner_local_user.clone())
}
/// A consistent point-in-time snapshot of the whole store. Used by the VM
/// and cubefs, which take a `CubeStore` by value. A read-lock clone, so it
/// does not block concurrent readers (it only waits for an in-flight
@@ -580,6 +591,8 @@ impl ConcurrentStore {
}
/// Store a code cell at `path` (the path->code bridge), durability-logged.
/// `owner` (when set) is stamped on the record's `owner_local_user` field
/// so owner enforcement (Task 6) can later reject cross-owner overwrites.
pub fn put_code_cell(
&self,
path: &str,
@@ -587,9 +600,9 @@ impl ConcurrentStore {
name: &str,
links: &[Czyx],
code: &[Op],
owner: Option<&str>,
) -> Result<Czyx, crate::SysError> {
let coord =
self.with_mut(|store| crate::store_code_cell(store, path, kind, name, links, code))?;
let coord = self.with_mut(|store| crate::store_code_cell(store, path, kind, name, links, code, owner))?;
if let Some(v) = self.get_raw(&coord) {
self.log_put(coord, v);
}