Skip to Content
核心概念權限與 ACLACL 總覽

自訂資料表 ACL

ACL 決定 principal 能否存取一張表,以及可讀、可新增、可編輯哪些 rows 和 columns。前端可以用權限摘要調整控制項,但真正的授權永遠在伺服器端:隱藏按鈕不等於阻止 API 呼叫。

四層判斷

端點所需 privilege → effective grant(manager / user / department / chatroom / default) → row access(none / own / filtered / all) → column visibility(column_acl + visible_columns) → 若有 SCP,再套 acting-room / invariant floor

Grant 的解析不是把所有來源任意聯集。一般情況先命中的 grant 勝出;chatroom share 對原本就在 table scope 內的使用者有特別的「只能放寬 baseline」規則。完整步驟見 effective permissions。

這些層決定誰可以寫入;它們無法打開 write_policy 為 commands_only 的資料表。在這類資料表上,通過這些層的呼叫者(包括 manager)對 Command 以外的每一筆紀錄寫入,仍會以 403 governed_context_required 被拒絕。見 action policy。

Grant matrix

來源Chatroom tableDepartment tableCompany table管理端點與用途
User grant✓✓✓permissions.grant,指定 user
Department grant✓✓✓departmentPermissions 只適用 chatroom table;另一組 departmentTableGrants 可把 department 或 company table 分享給一個 department,受贈方路徑段名為 {target_department_id}
Chatroom grant—✓—Department table 分享給 room;每個 internal / external audience 各自一列,可帶 SCP scope_values
Client / CRM grant✓✓✓clientPermissions 在三種 table scope 都能指定 social-media client 的 external access
Client permission request✓✓—Client 可申請存取——包含分享進其聊天室的部門表;由審核者(明確 moderator、聊天室資料表的房間建立者、所屬部門或房間部門的 manager,或公司 manager)approve / reject,approve 會產生 explicit client grant
Passphrase access✓✓✓clientAccess 在三種 table scope 都可設定共用 passphrase 與套用的 client permissions
Explicit table moderatorscope manager 已隱含✓✓Department / company table 可增刪 moderator;manager 一律 full implicit access。指派錨定在該表的 effective company,因此 moderator 不必屬於擁有它的部門
Table defaults✓✓✓只對 in-scope internal users做 fallback——唯一例外是帶著 default_permissions.audience: "company" 的部門表,它讓同公司的每個人都算 in-scope 並為他們開門

完整端點清單見 grant 類型。Table-level column hide map 另由 columnAcl.update 管理。

在 department-scoped table 上,grant 只回答了一半:by-id 路由前面那道 membership door 有自己的一份鑰匙清單,而它以前比 resolver 的短。見部門表的門由誰打開。

找到別人分享給你的表

每一條 table LIST 路由都帶 scope 前綴,而被授權的人通常不知道是哪個部門或哪個房間分享給他。現在每份清單都用 by-id 路由同一套 resolver 逐表過濾,垃圾桶中的表一律不出現:

  • tables.readable——GET .../department/{department_id}/readable-tables——對同公司任何使用者列出該部門中解析後 can_read 不是 none 的現行資料表。一般成員就是用它列出自己部門的表:部門的 tables.list 仍只開放給管理者(403 Insufficient permissions for department-level access.)。
  • GET .../chatroom/{chatroom_id}/tables 只列出你握有某種權利(讀或編輯、can_insert,或管理權)的房間資料表;GET .../chatroom/{chatroom_id}/tables/shared 也會略過你完全沒有權利的分享表。

另外兩條沒有 scope 的路由補上剩下的缺口,而且刻意互補:

  • tables.sharedWithMe 列出從你的 scope 之外送到你手上的東西——per-user grant、你所屬部門的 grant、透過你所屬活房間的 internal chatroom grant,或分享給全部部門的部門表——並附上放行的 sources 與你解析後的權限。你自己部門的表、company scope 的表、你所屬房間的表都被刻意排除。
  • tables.resolveByName 把精確表名轉成 id,範圍涵蓋 company 與 department scope,逐表由 resolver 把關,而不是用角色把關。

兩者都不是你 scope 清單的超集,而且有一個缺口兩者都補不上:角色低於 CAN_MANAGE_COMPANY 的呼叫者若在 company scope 表上握有 per-user grant,那張表不會出現在任何 REST 清單上——company 清單是角色門檻,而 shared-with-me 無條件排除 company scope。那一張請用名稱解析——或者在 agent 端用 MCP tables_list 工具(scope_kind: "company")列出,它讓同公司的任何使用者進門並逐表過濾。

上表中每一種 row policy 介面都用同一棵布林樹——and / or / not 包住 predicate 與 link_target leaf——並接受每次請求解析的 tokens——$me、$me.department、$today、$today±Nd、$now——以原字串儲存,並在伺服器解析當下 principal 時代換。在 principal 欄位上,兩個身分 token 解析成 tagged user: / smc: / room: cell 的 SET,而不是單一 scalar id;在 agent session 裡,acting room 縮小該 SET 的 room 腿的方式,與它縮小 chatroom grant 那一步完全相同——見 effective permissions。這些介面都不在任何 feature flag 之後:row policies、column ACL、visible_columns、chatroom share grants 與 SCP floors 一律啟用,這裡的端點也不會再回「requires CUSTOM_TABLE_…」之類的錯誤。

權限等級詞彙

名稱意義
Joined是該 chatroom 的成員,或是房間門檻放行的非成員(房間所屬部門中 role >= CAN_MANAGE_DEPARTMENT 的管理者,或對其他部門房間而言 role >= CAN_MANAGE_COMPANY 的公司管理者);不必先取得 table grant
Tenant已通過該 department/company 的 tenant 身分邊界;不等於 moderator
Metadata read已通過資料表門檻,且對這張表握有某種權利——管理者或版主、can_read 或 can_edit 不是 none,或有 can_insert。同公司但沒有任何權利的呼叫者會收到 404 {"detail": "CustomTable Not found"},與 id 不存在完全相同
ModeratorCustomTableModeratorRequired:明確的版主列(聊天室資料表的建立者在建表時會得到一列)、房間建立者、房間或資料表所屬部門的管理者,或公司管理者。可管理 schema、rules、triggers、設定,以及部門/用戶端/聊天室 grants。個別使用者 grant 路由用的是較窄的集合(CustomTableGrantRequired:明確版主;聊天室資料表另加房間建立者或房間管理者;部門資料表另加該部門的管理者或公司管理者;公司資料表另加公司管理者),所以在房間的資料表上通過 Moderator 的部門或公司管理者,仍無法管理它的個別使用者 grant

這兩個角色門檻是伺服器設定。CAN_MANAGE_DEPARTMENT(預設 2)定義部門管理者:角色達到它的使用者,在自己部門的每張資料表、以及該部門各房間的資料表上都會通過 Moderator,部門資料表的 GET .../moderators 也會把他們列為預設版主。CAN_MANAGE_COMPANY(預設 3)定義公司管理者,他們在公司內每張資料表上都會通過 Moderator。被 Moderator 拒絕的呼叫者會收到 403,detail 依 scope 而不同——聊天室資料表:來自房間所屬部門以外是 Insufficient permissions. Required: table moderator, chatroom creator, or management rights.,來自該部門內是 Insufficient permissions. Required: table moderator, chatroom creator, or department management rights.;部門資料表:來自其他部門是 Insufficient permissions. Required: table moderator, department manager, or company management rights.,來自本部門是 Insufficient permissions. Required: table moderator or department management rights.;公司資料表:Insufficient permissions. Required: table moderator or company management rights.。id 不存在回 404 CustomTable Not found——在垃圾桶中的也一樣,資料表還原與 purge 路由除外——聊天室資料表的房間已刪除時則回 404 Chatroom Not found;自後端 5.21.0 起,其他公司的資料表也回同樣的 404 CustomTable Not found,不再是 403 Insufficient permissions. (Different Company)(見其他公司的 id 回應與不存在相同)。GET .../triggers 會先跑 Metadata read,所以對這張表沒有任何權利的同公司呼叫者拿到的是該閘門的 404,不會看到上述任何 403。

不同 endpoint family 的 dependency 並不完全一致。不要從某一個成功呼叫推論其他端點也可用。

踢人與離開會關上這扇門。 POST /private/chatrooms/kick/{chatroom_id} 或 POST /private/chatrooms/leave/{chatroom_id} 回 200 之後,成員關係與 collector/manager/social media manager 三種角色列都已消失,前成員的存取會翻成:table-id 資料列路由回 403 {"detail":"Not a member of this chatroom."},GET .../chatroom/{chatroom_id}/tables 回 403 {"detail":"Insufficient permissions, not a member (Required department manager)."}(或公司管理者版本的字串)。他的 explicit grant 列與 moderator 列也一併刪除。若前成員握有 member-independent grant,或本身就過得了 CAN_MANAGE_DEPARTMENT / CAN_MANAGE_COMPANY 的角色 fallback,存取權仍然成立。

各 scope 決策表

操作 familyChatroomDepartmentCompany
新增欄位ModeratorModeratorModerator
Lifecycle diagnosis 與其報告(POST .../diagnosis、GET .../migrations/{migration_id}/diagnosis-results)ModeratorModeratorModerator
By-id 資料表中繼資料(詳細資料、/columns、/permissions/me、/moderators、/rules、/staged-changes、/migration/status)Joined,再 Metadata read(/rules 與 /staged-changes 只有 Metadata read;沒有 /moderators 路由)Metadata readMetadata read
讀取觸發器設定(GET .../triggers)Metadata read,再 ModeratorMetadata read,再 ModeratorMetadata read,再 Moderator
Resolve linksJoinedTenantAny authenticated user

Schema 寫入與診斷報告現在在每個 scope 都是 Moderator:在 #1748 之前,部門表新增欄位與公司 scope 的診斷只需通過資料表門檻。診斷報告內含每筆不合格資料列的原始 record_data,所以讀取、資料列與分享授權都不算數。Resolve links 是這張表中唯一仍依 scope 而不同的 family(匯入的 extract/status 也不同,見範圍);端點內部仍會套用目標可見性,解析出的資料也不構成額外授權。

這些 family 的精確 endpoints 是 columns.create、lifecycle.diagnosis、lifecycle.migrationDiagnosis、tables.get 與 records.linksResolve。前端 route guard 應按「scope + endpoint」建表,不要只做一個全域 isModerator 判斷。

安全的前端策略

  1. 進入表格時呼叫 permissions.me,取得五欄摘要——can_read、can_insert、can_edit、is_manager 與 visible_columns(null = 所有欄位,[] = 沒有欄位)。在這裡收到 404 代表你對這張表沒有任何權利:把它藏起來。
  2. 依實際 endpoint privilege 顯示控制項;任何 403——以及你對某張表沒有任何權利時 by-id 中繼資料讀取回的、或大多數資料表 id 路由對其他公司的資料表回的 404 CustomTable Not found——都視為伺服器的最新決定。
  3. 不在 client 重算 row filter 或 hidden columns;只呈現伺服器回傳的 rows / fields。
  4. Grant 變更或 revoke 後重新抓取摘要與資料。Revoke 只是移除一個來源、讓使用者落到下一層;revoke 的回應會回報結果權限,並在這次移除放寬了使用者的存取範圍時標示出來。

Row filters 見 grants,每次請求的 $me / $today 詞彙與空值 cell 規則見 row policy tokens,兩種欄位機制見 column visibility,SCP floor 見 SCP 與 ACL。

Last updated on