Problem
Univer ships a range-protection UI, but its stock IAuthzIoService is an in-memory stub: it answers "allowed" for any permissionId it has not seen, and invents a random Owner_xxxxxxxx identity.
Consequences observed in the local docker stack:
- Once a protection rule replicates to another peer, that peer's authz has never seen the id — so it allows the write. The lock icon renders, but the cell stays editable.
- Protection rules are not synced through the collaborative Y.Doc at all, so they are lost on reload and invisible to other peers.
- Univer's "user part" (the 添加人员 dialog inside the protection panel) is a host slot that ships empty — unfilled, it is permanently blank, so no collaborator can ever be granted edit access through the UI.
- Any doc member could create a protection rule; the resulting rule had an empty allow-list, which locked the creator out of their own range.
Net effect: sheet range protection is decorative.
Expected
- Protection rules persist across reload and replicate to every peer.
- A peer resolves the same authorization answer as the originating client (grant state must live in shared state, not local memory).
- Only a doc admin may create or modify a protected range. Who may edit inside one is
admin ∪ explicit allow-list.
- The people-picker lists the doc's own members (same data source as 管理成员) so a grant can actually be made.
- Non-admins see no protection entry points at all.
Scope
Client-side (browser) enforcement only. The non-browser write path (PATCH /docs/:docId/sheet from a bot / CLI / REST client, which never passes through Univer) is closed separately in Mininglamp-OSS/octo-docs-backend#139 and must land first.
Out of scope: a client driving the collab WebSocket directly via devtools to write raw Yjs updates — that needs per-message decoding on the hot path.
Problem
Univer ships a range-protection UI, but its stock
IAuthzIoServiceis an in-memory stub: it answers "allowed" for anypermissionIdit has not seen, and invents a randomOwner_xxxxxxxxidentity.Consequences observed in the local docker stack:
Net effect: sheet range protection is decorative.
Expected
admin ∪ explicit allow-list.Scope
Client-side (browser) enforcement only. The non-browser write path (
PATCH /docs/:docId/sheetfrom a bot / CLI / REST client, which never passes through Univer) is closed separately in Mininglamp-OSS/octo-docs-backend#139 and must land first.Out of scope: a client driving the collab WebSocket directly via devtools to write raw Yjs updates — that needs per-message decoding on the hot path.