はじめに
Apexの処理は基本的にシステム権限で実行されます。そのため、非公開レコードなどにもアクセスして処理を行うことができます。
これはこれで便利なのですが、時に特定のユーザ権限で実行し、当該ユーザがアクセスできないレコードは除外して処理を行いたい場合もあります。
この記事では特定ユーザの権限でレコード取得を行う方法について記述します。
System.runAsメソッドはテストクラスでしか使えない
テストクラスであれば、System.runAsメソッドを用いて実行ユーザを簡単に切り替えることができます。
@isTest
private class TestRunAs {
public static testMethod void testRunAs() {
// ユーザを検索
User user = [SELECT Id FROM User Name='Some User' LIMIT 1];
System.runAs(user) {
// このブロックの中ではユーザ(Some User)が実行ユーザになる
System.debug('Current User: ' + UserInfo.getUserName());
System.debug('Current Profile: ' + UserInfo.getProfileId());
}
}
}
System.runAs
メソッドは便利なのですが、テストクラスでしか使えません。
テストクラス以外のクラスで実行ユーザを切り替えることは基本できません(2023/04現在)。
UserRecordAccessを使う
実行ユーザを切り替えることはできませんが、特定ユーザがアクセスできないレコードは除外して取得することはできます。 UserRecordAccessオブジェクトを利用します。
UserRecordAccessオブジェクトにはユーザのレコードに対するアクセス権が保持されていますので、特定ユーザが参照権限を持っているかチェックする処理を組み込むことができます。
// ユーザを検索
User user = [SELECT Id FROM User Name='Some User' LIMIT 1];
// 取得したいオブジェクト(ここではカスタムオブジェクトSomeObject__c)を検索する
List<SomeObject__c> someObjects = [
SELECT
Id
FROM
SomeObject__c
];
List<Id> someObjectIds = new FieldSlice(someObjects).retrieveIds('Id');
// ユーザが読み取りできるレコードのIdのみを抽出
List<UserRecordAccess> userRecordAccesses = [
SELECT
RecordId
FROM
UserRecordAccess
WHERE
UserId = :user.Id
AND
RecordId IN :someObjectIds
AND
HasReadAccess = True
];
List<Id> readableSomeObjectIds = new FieldSlice(userRecordAccesses).retrieveIds('RecordId');
if ( readableSomeObjectIds.size() > 0 ) {
// 読み取り可能なレコードに対して処理を行う
// :
}
SOQLのクエリ結果からIdリストの抽出に自作クラスのFieldSliceを使っています。実装の詳細はリンク先の記事を参照してください。
上記の例において、SOQLを二つに分けて実行しています。しかし、下記のようにすれば一つのSOQLで処理できるのでは?と思われた方もいるでしょう。
// ユーザが読み取りできるレコードのIdのみを抽出
List<UserRecordAccess> userRecordAccesses = [
SELECT
RecordId
FROM
UserRecordAccess
WHERE
UserId = :user.Id
AND
RecordId IN (
SELECT
Id
FROM
SomeObject__c
)
AND
HasReadAccess = True
];
List<Id> readableSomeObjectIds = new FieldSlice(userRecordAccesses).retrieveIds('RecordId');
残念ながらバグがあり、この方法を使うとエラーになります。
面倒ですが、最初に権限チェックする対象のレコードIdをList
型で取得しておき、それをUserRecordAccessに対するクエリのIN句に指定する必要があります。
参考)
IN clause of RecordId field in UserRecordAccess object is not working
注意点
UserRecordAccessには次の2点、注意点があります。
- UserRecordAccess は、ユーザのアクセスが制限ルールによってブロックされているかどうかを考慮しません。
- 一回のUserRecordAccessへのクエリで照会できるレコード数は200個に制限されています。したがって大量レコードに対しての利用には適しません。
まとめ
いくつかの注意点はあるものの、実行ユーザを切り替えられなくてもUserRecordAccessを活用することにより、特定ユーザの権限でレコード取得を行う方法を示しました。セキュリティを考慮したApexコードの開発などに役立てていただければと思います。