Salesforce共有ルールのマスター:安全なレコードアクセス制御へのガイド

背景と応用シナリオ

Salesforceプラットフォームの核心には、堅牢で多層的なセキュリティモデルが存在します。このモデルの目的は、適切なユーザーが適切なデータに適切なタイミングでアクセスできるように保証することです。このセキュリティモデルの基本的な構成要素は、組織全体のデフォルト設定 (Organization-Wide Defaults, OWD)、ロール階層 (Role Hierarchy)、そして共有ルール (Sharing Rules) です。

まず、Organization-Wide Defaults (OWD) は、データアクセスセキュリティの最も基本的なレベルを設定します。これは、あるオブジェクトのレコードに対して、デフォルトでどのユーザーがアクセスできるかを定義します。「非公開(Private)」、「公開/参照のみ(Public Read Only)」、「公開/参照・更新可能(Public Read/Write)」などの設定があります。通常、セキュリティのベストプラクティスとして、OWDは可能な限り最も制限的なレベル(例:非公開)に設定することが推奨されます。

次に、Role Hierarchy は、組織図に基づいた垂直方向のデータアクセスを提供します。例えば、マネージャーは部下が所有するすべてのレコードを自動的に閲覧・編集できます。これは、階層の上位にいるユーザーが下位のユーザーのデータにアクセスできるようにするための、シンプルで強力な仕組みです。

しかし、実際のビジネスシナリオはこれほど単純ではありません。OWDを「非公開」に設定し、ロール階層を構築しても、階層構造では解決できないアクセス要件が発生します。例えば、以下のようなケースです。

  • 部門間の連携: 営業部門とサポート部門はロール階層上では並列の関係にありますが、特定の「重要顧客」に関する取引先レコードを相互に参照・編集する必要があります。
  • プロジェクトチーム: 異なる部署から集められたメンバーで構成されるプロジェクトチームが、特定の商談やカスタムオブジェクトレコードに共同でアクセスする必要があります。
  • 地域ベースの共有: 東日本営業チームと西日本営業チームは、通常はお互いのレコードにアクセスできませんが、「全国展開案件」という特定の基準を満たすレコードについては、互いにアクセス権を付与したい場合があります。

このような、OWDとロール階層だけではカバーできない水平方向、または例外的なアクセス権の拡張要件に応えるために設計されたのが Sharing Rules です。Sharing Rulesは、OWDで設定された基本アクセス権を上書きして、特定のユーザーグループに対して追加のアクセス権を付与する機能です。重要なのは、Sharing Rulesはアクセス権を制限するためには使用できず、常に追加のアクセス権を付与するためにのみ機能するという点です。


原理説明

Sharing Rulesは、定義された条件に基づいて、レコードへのアクセス権を拡張する自動化されたルールです。これにより、管理者はコードを書くことなく、複雑な共有要件を宣言的に設定できます。Sharing Rulesには、主に2つのタイプが存在します。

1. 所有者に基づく共有ルール (Owner-based Sharing Rules)

このタイプのルールは、「誰がレコードを所有しているか」を基点として共有を定義します。具体的には、特定の公開グループ (Public Group)、ロール (Role)、またはテリトリー (Territory) に所属するユーザーが所有するレコードを、別の公開グループ、ロール、またはテリトリーのメンバーと共有します。

  • 共有元 (Share records owned by members of): ここで指定されたグループやロールのメンバーが所有するレコードが共有の対象となります。
  • 共有先 (Share with): ここで指定されたグループやロールのメンバーにアクセス権が付与されます。
  • アクセスレベル (Level of access): 「参照のみ(Read-Only)」または「参照・更新(Read/Write)」のいずれかを選択できます。

例: 「東日本営業部」ロールのメンバーが所有するすべての取引先レコードを、「マーケティング部」公開グループのメンバーに対して「参照のみ」で共有する。

2. 条件に基づく共有ルール (Criteria-based Sharing Rules)

このタイプのルールは、レコードの所有者に関係なく、「レコードが特定の条件を満たしているか」を基点として共有を定義します。レコードの項目値に基づいて、動的に共有が行われます。

  • 条件 (Criteria): 「業種が『製造業』である」や「年間売上が1億円以上である」といった、レコードの項目値を評価する条件式を設定します。
  • 共有先 (Share with): 条件を満たしたレコードを共有する先の公開グループ、ロール、またはテリトリーを指定します。
  • アクセスレベル (Level of access): 同様に、「参照のみ(Read-Only)」または「参照・更新(Read/Write)」を選択します。

例: 取引先レコードのカスタム項目「優先度」が「高」に設定されている場合、そのレコードの所有者に関わらず、「役員」ロールのメンバーに対して「参照・更新」権限を付与する。

Apexによる共有管理 (Apex Managed Sharing)

宣言的な共有ルールでは対応できない、さらに複雑で動的な共有要件が存在する場合、開発者はApexコードを使用してプログラム的に共有を管理することができます。これを Apex Managed Sharing と呼びます。

Apex Managed Sharingでは、各オブジェクトに対応する共有オブジェクト(例: 取引先の場合は AccountShare、カスタムオブジェクト MyObject__c の場合は MyObject__Share)にレコードを直接挿入することで、共有を実現します。この方法の最大の利点は、レコードのデータや関連レコードの状態、さらには外部システムのロジックに基づいて、きめ細やかな共有制御を実装できることです。

Apex Managed Sharingを使用するには、共有の理由を定義する Apex Sharing Reason をカスタムオブジェクトに設定する必要があります。これにより、なぜこの共有レコードがApexによって作成されたのかを追跡し、管理することが可能になります。


サンプルコード

ここでは、カスタムオブジェクト Project__c のレコードを、特定の条件に基づいてプログラム的に共有する Apex Managed Sharing の例を示します。このコードを実行する前に、まず Project__c オブジェクトで Apex Sharing Reason を作成しておく必要があります(例: ラベル「プロジェクトリーダー共有」、API名「Project_Leader_Sharing__c」)。

以下のApexクラスは、指定されたプロジェクト(`projectId`)を、指定されたユーザー(`userId`)に対して、参照・更新権限で共有するメソッドを含んでいます。

public class ProjectSharingManager {

    // プロジェクトレコードを特定のユーザーと共有するためのメソッド
    public static void shareProjectWithUser(Id projectId, Id userId) {
        
        // 1. 共有対象オブジェクトに対応するShareオブジェクトをインスタンス化する
        // 今回はProject__cカスタムオブジェクトなので、Project__Shareオブジェクトを使用する
        Project__Share projectShare = new Project__Share();

        // 2. 親レコードのIDを設定する
        // ParentIdは、共有したいレコード(この場合は特定のProject__cレコード)のIDを指す
        projectShare.ParentId = projectId;

        // 3. 共有先のユーザーまたはグループのIDを設定する
        // UserOrGroupIdには、アクセス権を付与したいユーザー、公開グループ、またはロールのIDを指定する
        projectShare.UserOrGroupId = userId;

        // 4. アクセスレベルを設定する
        // 'Read' (参照のみ) または 'Edit' (参照・更新) を指定できる
        // オブジェクトのOWDが「公開/参照のみ」の場合、「参照のみ」の共有は意味をなさない
        projectShare.AccessLevel = 'Edit';

        // 5. 共有の理由 (Apex Sharing Reason) を設定する
        // この値は、事前にカスタムオブジェクトの設定で定義しておく必要がある
        // これにより、なぜこの共有が行われたのかを識別し、管理しやすくなる
        // Schemaクラスを使用して、ハードコーディングを避けることがベストプラクティス
        projectShare.RowCause = Schema.Project__Share.RowCause.Project_Leader_Sharing__c;
        
        // 6. データベースにShareレコードを挿入する
        // Database.insertメソッドの第2引数を false (allOrNone) に設定することで、
        // 一部のレコードでエラーが発生しても、他の成功したレコードの挿入はロールバックされないようにする(部分成功を許容)
        // エラーハンドリングのために、SaveResultの配列で結果を受け取る
        Database.SaveResult[] saveResults = Database.insert(new List{projectShare}, false);

        // 7. 挿入結果を確認し、エラー処理を行う
        for (Database.SaveResult sr : saveResults) {
            if (!sr.isSuccess()) {
                // エラーが発生した場合の処理
                for (Database.Error err : sr.getErrors()) {
                    System.debug('Error sharing project record: ' + err.getStatusCode() + ': ' + err.getMessage());
                    // ここでカスタム例外をスローするなどのエラーハンドリングを実装する
                }
            } else {
                System.debug('Successfully shared project record ' + projectId + ' with user ' + userId);
            }
        }
    }
}

このコードは、Salesforceの公式ドキュメントで推奨されている Apex Managed Sharing の標準的な実装方法に準拠しています。RowCause を使用して共有の理由を明示的に指定している点が非常に重要です。


注意事項

Sharing Rulesは非常に強力なツールですが、その影響を十分に理解せずに使用すると、パフォーマンスの問題や意図しないデータ公開を引き起こす可能性があります。以下に主要な注意点を挙げます。

パフォーマンスへの影響

共有ルールの追加や変更、ロール階層の変更、または多数のレコードの所有者変更が行われると、Salesforceはバックグラウンドで共有テーブルの再計算 (Sharing Recalculation) を実行します。データ量が多い組織では、この再計算に時間がかかり、その間、関連するレコードがロックされたり、パフォーマンスが低下したりする可能性があります。特に、複雑な条件を持つ Criteria-based Sharing Rules は、パフォーマンスへの影響が大きくなる傾向があります。

権限の階層

Sharing Rulesは、オブジェクトレベルの権限(プロファイルや権限セットで定義されるCRUD: 作成・参照・更新・削除)や項目レベルのセキュリティ (Field-Level Security) を上書きすることはできません。例えば、あるユーザーがプロファイルで取引先オブジェクトへの参照権限を持っていない場合、共有ルールで特定の取引先レコードをそのユーザーと共有しても、そのユーザーはレコードを見ることはできません。共有は、あくまでベースとなる権限の上に「追加」のアクセス権を与えるものです。

API制限とガバナ制限

Salesforceには、オブジェクトごとに作成できる共有ルールの数に制限があります。例えば、1つのオブジェクトに対して最大300件の共有ルール(所有者ベースと条件ベースの合計)を作成でき、そのうち条件ベースのルールは50件までです。Apex Managed Sharing を使用する場合、DML操作に関する標準のガバナ制限(1トランザクションあたり10,000レコードなど)が適用されるため、一括処理を考慮した設計が必要です。

所有権の偏り (Ownership Skew)

特定の単一ユーザーが非常に多くのレコード(例えば10,000件以上)を所有している状態を「所有権の偏り」と呼びます。このユーザーがロール階層内で移動したり、このユーザーが所有するレコードに影響する共有ルールが変更されたりすると、共有再計算の負荷が極端に高まり、パフォーマンス問題を引き起こす可能性があります。インテグレーション用のユーザーなどが大量のレコードを所有する場合は特に注意が必要です。

Apex Sharing Reasonの管理

Apex Managed Sharing を使用する場合、RowCause として指定した Apex Sharing Reason を後から削除すると、その理由に関連付けられているすべての共有レコード (__Share レコード) が自動的に削除されます。これにより、ユーザーは突然レコードにアクセスできなくなる可能性があります。Apex Sharing Reason は慎重に管理する必要があります。


まとめとベストプラクティス

Salesforceの共有モデルは、組織のデータを安全かつ柔軟に管理するための強力なフレームワークです。Sharing Rules はその中で、組織の複雑なビジネス要件を満たすための重要な役割を担っています。最後に、共有設定を設計・実装する際のベストプラクティスをまとめます。

  1. 最も制限的なOWDから始める: セキュリティの基本原則は「最小権限の原則」です。まず、OWDを可能な限り制限的な設定(例:「非公開」)にし、そこから必要なアクセス権を段階的に付与していくアプローチを取ります。
  2. ロール階層を最大限に活用する: 組織の指揮命令系統に沿った自然なデータアクセスは、まずロール階層で実現します。垂直方向のアクセスは、共有ルールよりもロール階層の方がシンプルで管理しやすいためです。
  3. 宣言的ツールを優先する: 共有要件が静的で、所有者やレコードの属性で定義できる場合は、Apex Managed Sharing よりも宣言的な共有ルール(Owner-based または Criteria-based)を優先します。コードを書かずに済むため、メンテナンス性が向上します。
  4. 公開グループ (Public Groups) を活用する: ユーザーを直接共有ルールに割り当てるのではなく、役割や機能に基づいた公開グループを作成し、そのグループに対して共有ルールを設定します。将来的にメンバーの追加や削除が発生した場合でも、共有ルール自体を変更する必要がなくなり、管理が容易になります。
  5. Apex Managed Sharing は最終手段として用いる: 共有ロジックが非常に動的で、レコードのデータや関連オブジェクトの状態に依存するなど、宣言的な方法では実現不可能な場合にのみ、Apex Managed Sharing を検討します。実装とテストには十分なコストがかかることを認識しておく必要があります。
  6. 定期的な監査とレビュー: 組織の成長やビジネスプロセスの変更に伴い、共有ルールが時代遅れになったり、過剰な権限を付与してしまったりすることがあります。定期的に共有設定全体を見直し、不要なルールを削除したり、アクセスレベルを最適化したりすることが重要です。

これらの原則に従うことで、Salesforce 技術アーキテクトは、パフォーマンスとメンテナンス性を両立させながら、セキュアでスケーラブルなデータアクセス制御モデルを構築することができるでしょう。

コメント