Skip to Content

Column visibility:兩種機制,一個 hidden set

欄位可見性有兩個互補機制:table-level column_acl 是全域 hide-map;每筆 grant 的 visible_columns 是 allowlist。伺服器把兩者折疊成一個 effective hidden_columns 集合,所有讀寫入口都以同一集合執行。

機制一:table-level column_acl

curl -X PATCH \ "$BASE_URL/private/module/custom_tables/department/11111111-1111-4111-8111-111111111111/tables/22222222-2222-4222-8222-222222222222/column-acl" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "column_acl": { "col_dddddddd_dddd_4ddd_8ddd_dddddddddddd": { "read": "managers" }, "col_eeeeeeee_eeee_4eee_8eee_eeeeeeeeeeee": { "read": "all" } } }'

Map key 必須是 live schema 的 internal column key,read 只接受 allmanagers。對非 manager,標成 managers 的欄位加入 hidden set;manager 的 table-level hidden set 永遠為空。送出空 map 可清除 table-level hide 設定。

這個設定適合「不論從哪種 grant 進來,成本欄都只有管理員可見」的硬底線。端點見 columnAcl.update

自 2026-07-28 版起,這個寫入在資料表列鎖下合併。兩個併發的 settings PATCH——例如欄位 ACL 儲存撞上 default-permissions 儲存,或 IaC apply 撞上 UI 編輯——以前兩邊都回 200,晚 commit 的無聲蓋掉早的那個 key;對這個設定來說,代表收緊過的欄位 ACL 可能退回全員可見。現在競爭時 PATCH 會等鎖,並可能回併發合約裡可重試的 409;拿到 200 就表示合併真的成立。

機制二:grant visible_columns

{ "user_id": "33333333-3333-4333-8333-333333333333", "can_read": "all", "can_insert": true, "can_edit": "own", "visible_columns": [ "col_aaaaaaaa_aaaa_4aaa_8aaa_aaaaaaaaaaaa", "col_bbbbbbbb_bbbb_4bbb_8bbb_bbbbbbbbbbbb", "col_dddddddd_dddd_4ddd_8ddd_dddddddddddd" ] }

visible_columns 是 allowlist:所有未列出的 live columns 都加入 hidden set。它可放在 user、department、chatroom 與 client grants;省略或 null 代表 grant 本身不縮小欄位,仍會套 table-level column_acl。空 list 會被拒絕,不用它表達「看不到任何欄位」。Table defaults 沒有 visible_columns

Note visible_columns 只接受 internal keys。這讓欄位 rename 後 grant 仍穩定;建立 grant UI 時,請從 schema 顯示名稱映射到 internal key。

合併公式

hidden_columns = column_acl 中 read=managers 的 keys(非 manager) ∪(所有 live schema keys − grant.visible_columns)

假設 live columns 是 namestatuscostinternal_note

來源結果
column_aclcost 設為 managers-onlyhide cost
Grant allowlist 是 name, status, costhide internal_note
Effective unionhide cost, internal_note

Allowlist 不能重新打開 table-level hide;即使列出 cost,非 manager 仍看不到。Manager 不需要也不能有 explicit grant,因此不會被 grant allowlist 縮小。

Hidden 代表 absent,不是 null

Record response 會完全移除 hidden field,而不是回傳 null。Filter、sort、search、aggregate 或 write 若命中 hidden key,伺服器會回與真正不存在的欄位相同的 400 Column '<name>' does not exist in table schema,因此 record 相關介面不存在「存在但看不到」的探測管道。Exports、views、history 與依賴欄位的讀取也必須沿用伺服器結果,前端不應自行補回 schema 欄位。

被隱藏的是「值」,不是「名稱」。 只有 manager 能讀的欄位,其名稱仍會出現在 schema_definition.columns 中,被扣住的只有值。Hidden set 是套用在準備好的 record 字典上,computed 欄位則只是被標註 "restricted": true;兩條路徑都不會裁剪 schema 清單。切勿從欄位是否出現在 schema 推論可讀性:這麼做的前端會渲染出一個永遠沒有值的欄位,或送出一個必然 4xx 的 filter。

因此,以下程式會把「不存在」視為正確狀態:

const hasCost = Object.prototype.hasOwnProperty.call(record.data, '成本') if (hasCost) { renderMoney(record.data['成本']) }

不要用 record.data['成本'] ?? 0,那會把「無權限」錯誤呈現成真實的零值。

兩個機制合併後如何參與 grant resolution,見 effective permissions

Last updated on