以 API 操作 Salesforce Field-Level Security

寫腳本來打那好煩人的 FLS...

從 Salesforce (SFDC) 拉資料就偶而會被 Field-Level Security (FLS) 打到,而 FLS 那藏地有夠緊的設定頁面、加上從台灣連到美東那精美的連線速度,每每要敲個幾個欄位下來都很有得等...

找到了以程式來直接設定 FLS 的手段,這樣就不用去戳那個有夠煩的 UI 了,記錄一下。

環境設定

以下操作是 SOQLMetadata API 混用;然後實際上我是使用 simple-salesforce 來操作,不過在 workbench 混用 SOQL Query 及 REST Explorer 也能完成。

使用 simple-salesforce 連線的程式大概這樣起頭,其中 Salesforce 的建構子參數都做授權在用,直接看官方文件比較快:

import simple_salesforce
client = simple_salesforce.Salesforce( ... )

列舉欄位清單

先打個 sObject Describe 當作測試:

client.Account.describe()

但取得清單卻意外地沒用——作為管理員帳號明明已經可以在 Object Management 上看到所有的欄位了、甚至在上面調整權限,但是當 FLS 沒有通過,欄位就不會在 API 的回應裡出現。即從這個 API 取得的完整欄位清單會跟實際上有出入。

取得 PermissionSet

稍後查詢需要得到 PermissionSet 的資訊,先打個 SOQL 去查查:

SELECT id
FROM PermissionSet
WHERE
IsOwnedByProfile = true
AND PermissionSet.Profile.Name = 'System Administrator'

這裡 PermissionSet.Profile.Name 顧名思義是需要取得權限的帳號的群組名,例如上面的 System Administrator 或是自訂的群組如 Service Account

如果也是使用 simple-salesforce 的話就直接透過 query 來執行:

client.query( soql )

確認權限

FLS 背後的物件為 FieldPermissions,一樣透過 SOQL 來查查權限:

SELECT
SobjectType,
Field,
ParentId,
PermissionsRead,
PermissionsEdit
FROM FieldPermissions
WHERE
Field = 'Account.Website'

這裡舉例是以標準物件 Account 下的內建欄位 Website 來檢索並回傳了 FieldPermissions 的全部欄位:

回傳欄位 說明
SobjectType 所屬 SObject 名稱
Field 欄位的 API 識別名稱,並且它一定要以其所屬 SObject 名稱作為前綴
ParentId 這個 FieldPermissions 物件所對應的 PermissionSet
PermissionsRead 具有可讀權限
PermissionsEdit 具有可寫權限

另外,不是所有的欄位都可以查詢 FieldPermissions,以 FieldPermissions 文件底下 Special Properties for Field Permissions 章節所述,一些系統欄位會是永遠唯獨的,包括但不僅於:

  • Id
  • CreatedById
  • CreatedDate
  • IsDeleted
  • LastModifiedById
  • LastModifiedDate
  • SystemModStamp

這些系統欄位無法查詢、也不可以對那些欄位設定 FieldPermissions。對於系統欄位,在 sObject Describe 回傳的欄位資料中 permissionable 會是 False

開權限

SOQL 只能查資料,所以這塊是 Metadata API 的戰場。這裡要操作的是對 FieldPermissionsCreate Records

POST /services/data/v58.0/sobjects/FieldPermissions/
{
"SobjectType": "Account",
"Field": "Account.Website",
"PermissionsEdit": false,
"PermissionsRead": true,
"ParentId": "0xxxxxxxxxxxxxxMUD"
}

依照前面段落說到的規則:Field 一樣需要把 SObject 填在前面,然後 ParentId 就直接填入要開權限的對象。

在 simple-salesforce 中的操作則對應為:

client.FieldPermissions.create({
"SobjectType": "Account",
"Field": "Account.Website",
"PermissionsEdit": False,
"PermissionsRead": True,
"ParentId": "0xxxxxxxxxxxxxxMUD"
})