Contents
前書き
Salesforceのアクセス権制御、とりわけレコードレベルセキュリティの機構の裏側の仕組みについて記述する。
前提知識としてのマルチテナントアーキテクチャ
大前提として、Salesforceはマルチテナントアーキテクチャの中でも、テナント間でSchemaを共有するOne Shared Schemaモデルという強い種類のShared Deployment Approachを採用している。
その上で、(スキーマを共有する構成を取っている時点でほぼ自明なことではあるが)データテーブルに関してもテナント間で共有されており、オブジェクトや項目の定義情報はそれぞれMT_OBJECTS・MT_FIELDSと呼ばれるデータテーブルに格納され、レコードの情報は主にMT_DATAと呼ばれるデータテーブルに格納されている(補足:ロングテキストエリアの値はMT_CLOBに外出しされているなど、全ての情報がMT_DATA内で完結しているわけではない)
MT_DATAに格納された各レコードにはすべてOrgIdが付与され、それにより各テナントの論理的分離性が担保されている。
データアクセスに関して言えば、我々がSalesforceの画面上やAPI経由で任意のレコードを取得しようとした場合、当該のユーザが当該のレコードに対してアクセス権を持つかどうか判定した上で実際のレコードデータをレスポンスするというメカニズムになっている。
本記事が扱うのはまさに、この「当該のユーザが当該のレコードに対してアクセス権を持つかどうか」の判定の裏側の仕組みである。
それは、(広く知られた)下記Query実行順序図で言うところのCheck User Visibilityの内実を明らかにすることでもある(※なお、Query実行順序に関する下記ドキュメントにおいてCheck User Visibilityは専らクエリ最適化の判断材料たるnumber of rows user can accessを把握するためのステップとして紹介されている)
データ取得処理の流れ
まず、Salesforceにおけるレコード取得処理の全体像を明らかにしておきたい。
Salesforce上でレコード取得のリクエストを実施してから、実際にデータが返ってくるまでの流れは大きく以下の通りである。
- リクエスト送信:ユーザがレコード取得のリクエストを送信
- レコード検索:MT_DATAテーブルへSQL発行(※1件以上HITするかの確認が目的のため、この段階では具体的な値まで取得しない)
- アクセス権の判定:ユーザが当該レコードに対してアクセス権があるかどうかを判定
- Object Level Security(inc. FLS)の判定
- 共有権限関連のオブジェクトへのアクセスがそもそも必要か判定
- 条件1:当該オブジェクトが主従関係の従ではない
- 条件2:当該オブジェクトのOWDがPublic Read/Writeではない
- 条件3:当該オブジェクトがShareオブジェクトでの管理対象である
- 共有権限関連のテーブルを結合するSQL文の生成(Object Records TableとObject Sharing Table、Object Sharing TableとGroup Maintenance TablesをそれぞれJOINしたSQLを、MT_DATAへのクレリ時に利用したSQLに加筆する形で生成)
- 共有権限関連のテーブルに対するクエリ実行
- クエリ生成:実際のデータを取得するためのOptimized Queryの生成
- クエリ実行:上記Optimized Queryの実行による、データの取得
上記から導かれる帰結の一つは、共有権限関連のオブジェクトにクエリをかける必要がある場合(すなわち、①当該オブジェクトが主従関係の従ではない②当該オブジェクトのOWDがPublic Read/Writeではない③当該オブジェクトがShareオブジェクトでの管理対象である、という三つの条件を全て満たす場合)、データが返ってくるためには以下の条件が全て満たされている必要があるということだ。
- レコードが1件以上存在する
- Object Level Security的にアクセス権が付与されている
- Object Sharing Table・Group Maintenance Tablesのいずれかによりアクセス権が与えられている
以上の全体像を踏まえ、データ取得処理におけるアクセス権限判定処理に関してここから深掘りしていく。
データアクセスの二階層性
・Object Level Security(inc. FLS)の判定 ・共有権限関連のオブジェクトへのアクセスがそもそも必要か判定 条件1:当該オブジェクトが主従関係の従ではない 条件2:当該オブジェクトのOWDがPublic Read/Writeではない 条件3:当該オブジェクトがShareオブジェクトでの管理対象である ・共有権限関連のテーブルを結合するSQL文の生成(Object Records TableとObject Sharing Table、Object Sharing TableとGroup Maintenance TablesをそれぞれJOINしたSQLを、MT_DATAへのクレリ時に利用したSQLに加筆する形で生成) ・共有権限関連のテーブルに対するクエリ実行 |
周知の通り、Salesforceのアクセス権制御は以下の三階層に分類可能である
Org-Level-Security:組織レベルでの認証認可- Object-Level Security:特定のオブジェクトへのCRUD権限
- Field-Level Security:特定のオブジェクトの特定の項目への参照・編集権限
- Record-Level Security:特定のレコードへのCRUD権限
最初の二つがPermission SetとProfileで制御される一方で、Record-Level Securityは以下により制御される。
- Permission Set/Profile Level Object Settings(View All/Modify All)
- Profile Level System Permission(View All Data/Modify All Data)
- Record Owner(User or Queue)
- Organization Wide Defaults(OWD)
- Role Hierarchy
- Teams
- Account Team
- Opportunity Team
- Case Team
- Queue
- Groups
- Manager Groups
- Territory Management
- Sharing Rules
- Owner-Based Sharing Rules
- Criteria-Based Sharing Rules
- Guest User Sharing Rules
- Sharing Sets
- Super User Access
- Manual Sharing
- Apex Sharing
- VF/LWC/Dashboard Sharing
- Restriction Rules
- Scoping Rules
- Implicit Sharing
- Master-Detail Relationship
- Account Relationships
- External Account Hierarchy
ユーザ観点では適当なこうした階層分けはしかしながら、システム内部的な観点で見ると妥当ではない。すなわち、Field-Level SecurityはObject-Level Securityの一部として捉えられる必要がある。
結果として、データアクセスの分類は以下のようになる。
- Object-Level Security:特定のオブジェクトへのCRUD権限
- Field-Level Security:特定のオブジェクトの特定の項目への参照・編集権限
- Record-Level Security:特定のレコードへのCRUD権限
Sharing ObjectとGroup Maintenance Table
・Object Level Security(inc. FLS)の判定 ・共有権限関連のオブジェクトへのアクセスがそもそも必要か判定 条件1:当該オブジェクトが主従関係の従ではない 条件2:当該オブジェクトのOWDがPublic Read/Writeではない 条件3:当該オブジェクトがShareオブジェクトでの管理対象である ・共有権限関連のテーブルを結合するSQL文の生成(Object Records TableとObject Sharing Table、Object Sharing TableとGroup Maintenance TablesをそれぞれJOINしたSQLを、MT_DATAへのクレリ時に利用したSQLに加筆する形で生成) ・共有権限関連のテーブルに対するクエリ実行 |
Record-Level Securityの判定に関係するデータテーブルは以下の三種類である。
- Object Record Tables:レコードデータを格納したデータテーブル(つまり、MT_DATA)
- Object Sharing Tables:Apex共有でも登場するいわゆるShare Object。以下のRowCauseを持つ。
- ImplicitChild
- ImplicitParent
- Owner
- Team
- Rule
- TerritoryRule
- Manual
- TerritoryManual(or Territory2AssociationManual)
- [Apex共有において開発者が定義した理由]
- Group Maintenance Tables:RoleやTerritoryなどのGroup Membershipベースでの共有情報を管理するデータテーブル
これらのテーブルのデータは、関連するレコードへの操作や共有設定の変更に合わせてその都度再計算される。
その具体例として、Record-Level Access: Under the Hoodの資料内で紹介されているものをいくつか紹介したい。
Object Sharing Tablesの計算例
例えば、Acme取引先のOwnerがMariaである場合、Salesforce内部ではRow Cause = OwnerのAccount Sharing Objectのレコード(つまり、AccountShareレコード)が生成されている。
この状態からMariaが手動共有でFrankに対してアクセス権を付与すると、Share Objectのレコードは以下のように変化する
Group Maintenance Tablesの計算例
Group Maintenance Tablesに関する例も見てみよう。
以下のようなロール階層の定義は、内部的なGroup Maintenance Tables上でどのように表現されるだろうか。
結果は以下である。Role GroupsとRoleAndSubordinates Groupsはそれぞれ「どのロールに誰が所属しているか」と「特定のロールとそれより下の階層のロールに所属する全てのユーザの一覧」を表現したグループとなっている。
LDVとGroup Maintenance Tables
皆さんは上記のGroup Maintenance Tableのレコード一覧を見て、どのような印象を持たれただろうか。
おそらく多くの方がテーブルの長さ(つまり、レコード数の多さ)に驚かれたのではないかと思う。
実のところ、今回はたったの6ロール・6ユーザなので上記のレコード数で済んでいるが、外部ユーザも含めて数万ユーザとなった場合、レコード数はLDVの基準(200万)を優に超えてくる。
その場合、ロール階層の変更・ユーザのロール変更で再計算対象となるレコード数も同様に数万レコード・数十万レコードとなる可能性があり、その計算処理はエンドユーザに影響を与えるだけの時間を要してしまう。
これこそがまさにDesigning Record Access for Enterprise ScaleのGroup Membership Operations and Sharing RecalculationやAccount Role Optimization(※こちらはSpring’22以降に作成された組織ではデフォルトで有効化されている)などをアーキテクトたちが検討している理由という訳である。
参考資料
Record-Level Access : Under the Hood
A Guide to Sharing Architecture
Salesforce Developers – A Guide to Sharing Architecture
Salesforce Help – Control Who Sees What
Salesforce Developers – Designing Record Access for Enterprise Scale
Configure Account Role Optimization to Help You Scale Your Experience Cloud Users