進行状況の追跡を始めよう
Trailhead のホーム
Trailhead のホーム

共有違反の特定と防止

学習の目的

この単元を完了すると、次のことができるようになります。

  • Salesforce での共有を定義する。
  • Apex クラスでの共有違反を特定する。
  • Apex クラスに共有ルールを安全に適用する。

共有とは?

最初の単元で学習したように、Salesforce では、開発者とシステム管理者がデータへのアクセスをさまざまなレベルで制御します。共有とは、すべてのカスタムオブジェクトおよび多数の標準オブジェクトへのレコードレベルのアクセス権を制御する特定の設定です。さまざまなタイプの共有についての詳細は『Apex 開発者ガイド』の「共有の理解」を参照してください。

Kingdom Management 開発者組織では、システム管理者が組織内の一部のオブジェクトに共有ルールを設定しました。そのうちの 1 つが Coin_ Purse__c オブジェクトです。システム管理者は、このオブジェクトに非公開共有を設定しました。

Kingdom Management 開発者組織の Coin Purse オブジェクトのカスタム共有設定のスクリーンショット

システム管理者は、各ユーザが自分のレコードのみを表示できるようにするために、組織の共有設定を「非公開」に設定して、このオブジェクトへのアクセスを制限しました。つまり、Astha the Mighty は自分の小銭入れに 400 枚の金貨と銀貨が入っていることを表示できますが、王が現在 50,000 枚の金貨を持っていることは表示できません。 

カスタム Force.com アプリケーションでは共有がどのように適用されるのか?

Apex クラスはシステムコンテキストで実行されるので、Apex はデフォルトで共有ルールを適用せずに実行されます。つまり、システム管理者が設定した共有ルールは、カスタムアプリケーションではデフォルトでスキップされます。これを詳しく見てみましょう。

  1. Kingdom Management 開発者組織にログインします。
  2. 右上から [CRUD/FLS & Sharing (CRUD/FLS & 共有)] を選択します。
  3. [Sharing Demo (共有デモ)] タブをクリックします。

    Kingdom Management 開発者組織の Sharing Violation Demo アプリケーションのスクリーンショット

    このアプリケーションでは、ユーザは小銭入れに入っている金貨、銀貨、銅貨の数を参照できます。これは機密データである可能性があるので、ユーザが表示できるのはユーザ自身の小銭入れの中身のみで、それ以外へのアクセス権を持たないようにする必要があります。

    このモジュールで見てきた他のアプリケーション同様、左上のドロップダウンを使用して、王国内のさまざまなユーザをシミュレーションし、アクセス制御が正しく設定されていることを確認できます。

  4. ドロップダウンを使用して [Farmer (農民)] を選択し、このユーザの UI に何が表示されるのかをシミュレーションします。
  5. [View Contents of Coin Purse (小銭入れの中身を表示)] をクリックします。

    Sharing Demo アプリケーションの共有違反のスクリーンショット

    大変です。Farmer としてあなたに表示されたのは Farmer 自身のものだけではなく、システム内の他の多くのユーザが所有する金貨の数も表示されています。これは共有違反です。システム管理者は、Coin_Purse__c オブジェクトの共有設定を非公開に設定したはずです。何が起こっているのか確認してみましょう。

  6. [Return to User Selection (ユーザ選択に戻る)] をクリックして、システム管理ユーザに戻ります。
  7. [Apex Controller (Apex コントローラ)] リンクをクリックして、コードを表示します。

    これは、かなり簡単なコードです。
          public class Sharing_Demo {
            public List<Coin_Purse__c> whereclause_records {get; set;}
            public PageReference whereclause_search(){
              if(Coin_Purse__c.sObjectType.getDescribe().isAccessible() && Schema.sObjectType.Coin_Purse__c.fields.Name.isAccessible() && Schema.sObjectType.Coin_Purse__c.fields.Gold_coins__c.isAccessible() && Schema.sObjectType.Coin_Purse__c.fields.Silver_Coins__c.isAccessible() && Schema.sObjectType.Coin_Purse__c.fields.Copper_Coins__c.isAccessible()) {
                String query = 'SELECT Name,Gold_coins__c,Silver_Coins__c, Copper_Coins__c FROM Coin_Purse__c';
                whereclause_records = Database.query(query);
                return null;
              }
              else {
                return null;
              }
            }
          }
        

    このアプリケーションでは、データベースを照会し、その結果を表示する Visualforce にオブジェクトが返されます。ただし、すでに学習したように Apex はシステムコンテキストで動作するので、このクエリでは共有ルールは考慮されません。アプリケーションに他のレコードが表示されるのは、そのためです。これをどう修復すればよいでしょうか。

Apex での共有の適用

幸い、プラットフォームには「with sharing」という適切なキーワードが用意されているので、これをクラス定義に追加して Apex に共有ルールを適用できます。 

簡単ですね。これを利用するには、すべてのクラス (仮想クラス、抽象クラスを含む) を「with sharing」キーワードで宣言する必要があります。それでは、アプリケーションを修正しましょう。

  1. [Sharing Demo (共有デモ)] タブをクリックします。
  2. [Apex Controller (Apex コントローラ)] リンクをクリックします。
  3. [編集] をクリックして Apex を変更します。次のクラス定義を
    public class Sharing_Demo {
    以下に変更します
    public with sharing class Sharing_Demo {
  4. [Sharing Demo (共有デモ)] タブに移動し、ドロップダウンから [Farmer (農民)] ユーザを選択します。
  5. [View Contents of Coin Purse (小銭入れの中身を表示)] をクリックします。

    1 つのレコードのみを表示する、適切に制限された Sharing Demo アプリケーションのスクリーンショット

    これで、表示されるのは Farmer の金貨のみになり、その他のユーザに関する情報は表示されません。完璧です。

「with sharing」をどこに追加する必要があるのか?

「with sharing」キーワードの重要性が理解できたところで、正確にはそれをどこに適用する必要があるのだろうかと思われているでしょう。最善なのは、内部クラス、非同期 Apex、Apex トリガなど、共有ルールを適用する必要があるすべてに適用することです。

内部クラスでの共有

内部クラスを定義するクラスを使用する場合は、必ず両方のクラス定義に共有を適用してください。共有は外部クラスから継承されません。外部クラスと内部クラスの両方に共有を適用する方法を次の例で示します。

  public with sharing outerClass{
    public with sharing innerclass {
      [...]
    }
  }
  

非同期 Apex での共有

重要なのは、非同期 Apex クラスにも共有を適用することです。共有は、クラスが非同期で実行されるのが、スケジュール済みジョブとしてなのか、一括処理ジョブとしてなのかには依存しません。クラスが標準またはカスタム項目にアクセスする場合は、「with sharing」キーワードを宣言して、共有違反を防止します。 

たとえば、次の場合は

public class asyncClass  implements Queueable {

以下のように宣言します。

public with sharing class asyncClass implements Queueable {

Apex トリガでの共有

共有が定義されていないクラスからトリガを呼び出すのは安全ではありません。Apex トリガはシステムコンテキストで実行されます。Apex トリガはデフォルトで共有ルールを考慮せずに実行されます。 

次に例を示します。 

  public class OpportunityTriggerHandler extends TriggerHandler {
    public override void afterUpdate() {
      List opps = [SELECT Id, AccountId FROM Opportunity WHERE Id IN :Trigger.newMap.keySet()];
      Account acc = [SELECT Id, Name FROM Account WHERE Id = :opps.get(0).AccountId];
      Case c = new Case();
      c.Subject = 'My Bypassed Case';
      TriggerHandler.bypass('CaseTriggerHandler');
      insert c; // won't invoke the CaseTriggerHandler
      
      TriggerHandler.clearBypass('CaseTriggerHandler');
      c.Subject = 'No More Bypass';
      update c; // will invoke the CaseTriggerHandler
    }
  }

クラスに「with sharing」が定義されていないので、ユーザが参照アクセス権を持たないすべての商談が表示されます。

この共有違反を防止するには、クラスに with sharing を定義します。

public with sharing class OpportunityTriggerHandler extends TriggerHandler {

トリガ、非同期、または通常のコントローラなど、Apex を記述する場合は必ず「with sharing」キーワードを使用してクラスを定義し、共有ルールが確実に適用されるようにしてください。

リソース