Microsoft Entra テナントと ID を使用して Azure AI 検索結果をトリミングするためのセキュリティ フィルター

この記事では、Azure AI Search のフィルターと共に Microsoft Entra セキュリティ ID を使用して、ユーザー グループ メンバーシップに基づいて検索結果をトリミングする方法について説明します。

この記事に含まれるタスクは次のとおりです。

  • Microsoft Entra グループとユーザーを作成する
  • 作成したユーザーとグループを関連付けます
  • 新しいグループをキャッシュします
  • 関連付けられているグループでドキュメントのインデックスを作成します
  • グループ識別子フィルターでの検索要求を発行します

前提条件

Azure AI Search のインデックスには、ドキュメントに対する読み取りアクセス権を持つグループ ID のリストを格納するのにセキュリティ フィールドが必要です。 このユース ケースでは、セキュリティ保護可能な項目 (個々の大学アプリケーションなど) とその項目へのアクセス権を持っているユーザー (入学担当者) を指定するセキュリティ フィールドの間に、一対一の対応があることを前提としています。

ユーザー、グループ、および関連付けを作成するには、Microsoft Entra 管理者のアクセス許可 (所有者または管理者) が必要です。

また、次の手順で説明するように、お使いのアプリケーションが Microsoft Entra ID にマルチテナント アプリとして登録されている必要があります。

Microsoft Entra ID にアプリケーションを登録する

この手順では、ユーザー アカウントとグループ アカウントのサインインを受け付けるために、お使いのアプリケーションを Microsoft Entra ID と統合します。 組織のテナント管理者以外が次の手順を行うには、新しいテナントの作成が必要となる場合があります。

  1. Azure portal で Microsoft Entra ID テナントを見つけます。

  2. 左の [管理] で、[アプリの登録] を選択し、次に [新しい登録] を選択します。

  3. 登録に名前、おそらく検索アプリケーション名に似た名前を付けます。 その他の省略可能なプロパティについては、この記事を参照してください。

  4. [登録] を選択します。

  5. アプリの登録が作成されたら、アプリケーション (クライアント) ID をコピーします。 この文字列をアプリケーションに指定する必要があります。

    DotNetHowToSecurityTrimming のステップを実行している場合は、この値を app.config ファイルに貼り付けます。

  6. [ディレクトリ (テナント) ID] をコピーします。

    Screenshot of the application ID in the Essentials section.

  7. 左側で、[API のアクセス許可] を選択してから、[アクセス許可の追加] を選択します。

  8. [Microsoft Graph] を選択し、[委任されたアクセス許可] を選択します。

  9. 次の委任されたアクセス許可を検索し、追加します。

    • Directory.ReadWrite.All
    • Group.ReadWrite.All
    • User.ReadWrite.All

    Microsoft Graph に用意されている API では、REST API を使ってプログラムから Microsoft Entra ID にアクセスできます。 このチュートリアルのコード サンプルでは、Microsoft Graph API を呼び出すアクセス許可を使って、グループ、ユーザー、および関連付けを作成します。 この API は、高速のフィルター処理用にグループ識別子をキャッシュするためにも使われます。

  10. [テナントに管理者の同意を与えます] を選択し、同意プロセスを完了します。

ユーザーとグループを作成する

設定済みのアプリケーションに検索を追加している場合は、ユーザーとグループの識別子が Microsoft Entra ID に既に存在していることがあります。 その場合は、次の 3 つのステップをスキップできます。

ただし、既存のユーザーがいない場合は、Microsoft Graph API を使ってセキュリティ プリンシパルを作成できます。 次のコード スニペットは、識別子を生成する方法を示しています。識別子は、Azure AI Search インデックスのセキュリティ フィールドのデータ値になります。 この架空の大学入学アプリケーションでは、これは入学スタッフのセキュリティ識別子です。

特に大規模な組織では、ユーザーとグループ メンバーシップが頻繁に変更される場合があります。 ユーザーとグループの ID を作成するコードは、組織のメンバーシップの変更を反映するのに十分な頻度で実行する必要があります。 さらに、Azure AI Search インデックスでは、許可されているユーザーとリソースの現在の状態を反映するために、同様の更新スケジュールが必要です。

手順 1: グループを作成する

private static Dictionary<Group, List<User>> CreateGroupsWithUsers(string tenant)
{
    Group group = new Group()
    {
        DisplayName = "My First Prog Group",
        SecurityEnabled = true,
        MailEnabled = false,
        MailNickname = "group1"
    };

手順 2: ユーザーを作成する

User user1 = new User()
{
    GivenName = "First User",
    Surname = "User1",
    MailNickname = "User1",
    DisplayName = "First User",
    UserPrincipalName = String.Format("user1@{0}", tenant),
    PasswordProfile = new PasswordProfile() { Password = "********" },
    AccountEnabled = true
};

ステップ 3: ユーザーとグループを関連付ける

List<User> users = new List<User>() { user1, user2 };
Dictionary<Group, List<User>> groups = new Dictionary<Group, List<User>>() { { group, users } };

ステップ 4: グループ識別子をキャッシュする

または、ネットワークの待機時間を減らすには、ユーザーとグループの関連付けをキャッシュします。これにより、グループは、検索要求が発行される場合、Microsoft Entra ID にラウンドトリップせずに、キャッシュから返されます。 Microsoft Entra Batch API を使用して、複数のユーザーを含む単一の HTTP 要求を送信し、キャッシュを作成することができます。

Microsoft Graph は、大量の要求を処理できるように設計されています。 ただし、処理できないほど多数の要求が発生すると、Microsoft Graph は HTTP 状態コード 429 で要求を失敗させます。 詳しくは、「Microsoft Graph 調整ガイド」をご覧ください。

許可されているグループでドキュメントにインデックスを付ける

Azure AI Search のクエリ操作は、Azure AI Search インデックスにより実行されます。 このステップでは、インデックス操作が、セキュリティ フィルターとして使われる識別子などの検索可能なデータを、インデックスにインポートします。

Azure AI Search は、ユーザー ID を認証したり、ユーザーが表示権限を持つコンテンツを確立するためのロジックを提供したりしません。 セキュリティによるトリミングのユース ケースでは、機密性の高いドキュメントとそのドキュメントにアクセスできるグループ識別子の関連付けをユーザーが提供し、そのまま検索インデックスにインポートされるものと想定されています。

この仮定の例では、Azure AI Search インデックスに対する PUT 要求の本文には、志願者の大学の小論文または成績証明書と、そのコンテンツを表示する権限を持つグループ識別子が含まれます。

このチュートリアルのコード サンプルで使われている汎用的な例のインデックス操作は次のようなものです。

private static void IndexDocuments(string indexName, List<string> groups)
{
    IndexDocumentsBatch<SecuredFiles> batch = IndexDocumentsBatch.Create(
        IndexDocumentsAction.Upload(
            new SecuredFiles()
            {
                FileId = "1",
                Name = "secured_file_a",
                GroupIds = new[] { groups[0] }
            }),
              ...
            };

IndexDocumentsResult result = searchClient.IndexDocuments(batch);

検索要求を発行する

セキュリティによるトリミングが目的の場合、インデックスのセキュリティ フィールドの値は、検索結果にドキュメントを含めるか除外するために使われる静的な値です。 たとえば、入学者選考のグループ識別子が "A11B22C33D44-E55F66G77-H88I99JKK" の場合、セキュリティ フィールドにその識別子を持つ Azure AI Search インデックス内のすべてのドキュメントが、呼び出し元に返される検索結果に含まれたり除外されたりします。

要求を発行したユーザーのグループに基づいて検索結果で返されるドキュメントをフィルター処理するには、次の手順のようにします。

ステップ 1: ユーザーのグループ識別子を取得する

ユーザーのグループがまだキャッシュされていない場合、またはキャッシュの有効期限を過ぎている場合は、groups 要求を発行します。

private static async void RefreshCache(IEnumerable<User> users)
{
    HttpClient client = new HttpClient();
    var userGroups = await _microsoftGraphHelper.GetGroupsForUsers(client, users);
    _groupsCache = new ConcurrentDictionary<string, List<string>>(userGroups);
}

ステップ 2: 検索要求を作成する

ユーザーのグループ メンバーシップがある場合は、適切なフィルター値を指定して検索要求を発行できます。

private static void SearchQueryWithFilter(string user)
{
    // Using the filter below, the search result will contain all documents that their GroupIds field   
    // contain any one of the Ids in the groups list
    string filter = String.Format("groupIds/any(p:search.in(p, '{0}'))", string.Join(",", String.Join(",", _groupsCache[user])));
    SearchOptions searchOptions =
        new SearchOptions()
        {
            Filter = filter
        };
    searchOptions.Select.Add("name");

    SearchResults<SecuredFiles> results = searchClient.Search<SecuredFiles>("*", searchOptions);

    Console.WriteLine("Results for groups '{0}' : {1}", _groupsCache[user], results.GetResults().Select(r => r.Document.Name));
}

ステップ 3: 結果を処理する

応答には、ユーザーが表示アクセス許可を持つドキュメントだけにフィルター処理されたリストが含まれます。 検索結果ページの作成方法によっては、フィルター処理された結果セットを反映するための視覚的な合図を含めることができます。

次のステップ

このチュートリアルでは、Microsoft Entra サインインを使用して Azure AI Search 結果のドキュメントにフィルターを適用し、要求で指定されたフィルターと一致しないドキュメントの結果をトリミングするパターンについて説明しました。 より単純な代替パターン、または他のセキュリティ機能を再確認する方法については、次のリンクを参照してください。