SharePoint エンタープライズ検索 (1) : 検索セキュリティのアーキテクチャ


(このポストでは、書籍「VSTO と SharePoint Server 2007 による開発技術」(8章 : エンタープライズ検索と BDC) の補足情報を記載しています。基本的な内容 / 全体は、書籍を参照してください。) 

環境:
SharePoint Server 2007 (インフラストラクチャー更新プログラム含む)
または Search Server 2008

SharePoint エンタープライズ検索 (4回シリーズ)

こんにちは。

SharePoint Developers Forum にご参加いただいた皆様、ありがとうございました (満員御礼)。懇親会では、SharePoint だけの Lighting Talks 集でも実施したくなるほど、いろいろな Tips や Pain Point 等々を伺うことができました。
また是非実施していければと思いますのでよろしくお願いします。

さて、私のセッション (= エンタープライズ検索の話) で、例によって時間におさまらなかった (= ブログで書くとお約束した) 箇所について順次記載していきます。

お伝えしました通り、書籍では「開発技術」を説明するということで、検索のアーキテクチャ(抑えておくべきツボ)を「ザクッ」と説明して、カスタマイズ/開発手法の考え方 (idea) を「ドップリ」と記載しましたので、このセッションでは、その「ザクッ」としか説明していないアーキテクチャや管理/設定手法などを「ドップリ」と説明しました。
まずは、デモを飛ばした、セキュリティトリマの作り方についてです。

セッションに参加されていない方のために、最初に「検索のセキュリティって何 ?」という点を簡単に復習しておきます。

インデックスサーバーから特定のアカウントでドキュメントをクロールし、それを問い合わせの際にクエリーサーバーで処理するだけでは、例えば、人事情報などの従業員に見せたくない情報がハクジツのもとにさらされることになるかもしれません。一方、こうした情報をすべてクロール対象から外すと、「あるドキュメントは技術部だけでしか見れない」とか、「あるドキュメントは正社員は見れるが、派遣の人は見れない!」など、さまざまなドキュメントが検索対象から外されてしまうでしょう。
こうした課題を解決する方法として、書籍でも記載しているセキュリティのトリミング (Trimming) と呼ばれる処理が活躍します。

セキュリティのトリミングは 2 箇所で制御できます。
1 つは、インデックスの作成時に、抽出するコンテンツを制限したり、提供する ACL (アクセス制御リスト) を加工するなどして、コンテンツのアクセス権を制御する方法です (Crawl Time Trimmimg)。ただしこの方法は、クロールの方法そのもののカスタマイズ、すなわち、プロトコルハンドラそのものを開発することと同じです。
一方、SharePoint、Web コンテンツ、共有フォルダ、Notes、など、既にプロトコルハンドラが提供されているものに対して独自のトリミングの処理だけを記述したい場合には、インデックスの作成時ではなく、クエリーの際に表示するドキュメントを制御 (Query Time Trimmimg) することができます。

スライドを再掲しておきます。

このクエリー時のカスタムセキュリティトリマーは、以下の手順で構築します。

  1. トリマをコーディング (プログラミング) してビルドします。(ISecurityTrimmer の実装をおこないます。)
  2. 作成したトリマーの dll を GAC へ登録します。
  3. クロールルールを作成します。
  4. stsadm -o registersecuritytrimmer コマンドを使用して、セキュリティトリマーを登録します。
  5. iisrerset を実行します。

では、その実装方法を具体的にみていきましょう。

ISecurityTrimmer の実装

ここでは、サンプルとして、基本認証が設定されている収集結果 (クロールされた情報) に対し、「もしAdministrator でなければ、 特定のユーザーID/パスワードでそのドキュメントに接続をして、接続できるものは検索結果として表示し、できないものは非表示にする」 というトリマーを作成してみましょう。

Visual Studio でクラスライブラリのプロジェクトを新規作成し、System.Web.dll, Microsoft.Office.Server.Search.dll を参照追加して、以下のコードを記述します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Microsoft.Office.Server.Search.Query;
using System.Web;
using System.Net;

namespace MyWebSecurityComponent
{
    public class MyWebSecurityTrimmer : ISecurityTrimmer
    {
        #region ISecurityTrimmer メンバ

        public System.Collections.BitArray CheckAccess(IList<string> documentCrawlUrls, IDictionary<string, object> sessionProperties)
        {
            // 取得した URL の数分の Result コレクションを生成
            BitArray resultArray = new BitArray(documentCrawlUrls.Count);

            // ループしてそれぞれをチェック
            for (int i = 0; i < documentCrawlUrls.Count; i++)
            {
                string url = documentCrawlUrls[i];
                string currentUser = HttpContext.Current.User.Identity.Name;
                // "Administrator" なら権限を与える
                if (currentUser.EndsWith("Administrator"))
                {
                    resultArray[i] = true;
                    continue;
                }
                // それ以外なら、そのサイトに
                // 特定のユーザーID/パスワードで接続して確認
                resultArray[i] = ConnectCheck(url);
            }

            return resultArray;
        }

        public void Initialize(System.Collections.Specialized.NameValueCollection staticProperties, Microsoft.Office.Server.Search.Administration.SearchContext searchContext)
        {
            // 何もしない . . .
        }

        #endregion

        // ヘルパー関数
        private bool ConnectCheck(string url)
        {
            bool res = false;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Credentials = new NetworkCredential(@"DemoUser", "P@ssw0rd");
            request.AllowAutoRedirect = false;
            try  // 例外が発生しないように、Exception もちゃんと拾う
            {
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    // リダイレクトの場合 (リダイレクト先に行ってさらにチェック)
                    if (response.StatusCode.Equals(HttpStatusCode.Found) ||
                        response.StatusCode.Equals(HttpStatusCode.Redirect) ||
                        response.StatusCode.Equals(HttpStatusCode.Moved) ||
                        response.StatusCode.Equals(HttpStatusCode.MovedPermanently))
                    {
                        res = ConnectCheck(response.Headers["Location"].Trim());
                    }
                    // 成功の場合
                    else if (response.StatusCode.Equals(HttpStatusCode.OK))
                    {
                        res = true;
                    }
                    // それ以外
                    else
                    {
                        res = false;
                    }
                }
            }
            catch
            {
                res = false;
            }

            return res;
        }
    }
}

署名を添付してリビルドをおこない、作成された dll を GAC に登録します。 

つぎに、共有サービスプロバイダーの管理画面を使用して、このコンテンツ (該当の URL のコンテンツ) のクロールルールを作成します。(今回の場合、基本認証を使用してクロールをおこなうよう既に設定しているはずなので、この作業をおこなう頃には、既に該当のクロールルールは存在していることでしょう . . .) 

以下のコマンドを実行してセキュリティトリマを SharePoint に登録します。(下記で、id には 0 以上の一意なトリマーの ID を設定します。また typeName には、上記でコンパイルしたアセンブリ/クラスの厳密名を記載し、rulePath にはクロールルールの作成時に入力したパスを指定します。)

pushd %programfiles%common filesmicrosoft sharedweb server extensions12bin
stsadm -o registersecuritytrimmer -ssp SharedServices1 -id 0 -typeName "MyWebSecurityComponent.MyWebSecurityTrimmer, MyWebSecurityComponent, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8ba647f8ee32389a" -rulepath http://kkdeveva03:81/TestWeb1/*
popd
iisreset

上述の通り、このカスタムのセキュリティトリマはクエリーの際に実行されます。しかし、クロールルールをリセットするため、必ず、再度クロールをやりなおしてください。 
これで、セキュリティトリマーの登録が完了し、検索をおこなうと、検索結果は、指定した通りに絞り込みがおこなわれて表示されます。

なお、登録されたセキュリティトリマは、上記の id を使用して以下のように削除できます。

stsadm -o unregistersecuritytrimmer -ssp SharedServices1 -id 0

上記のプログラムコードですが、あくまでもサンプルですのでそのまま真似しないでください。というのも、現実の開発では、パフォーマンスの影響も考慮してセキュリティトリマーの登録を判断すべきでしょう。
また、いい加減なトリマーを登録して例外が発生してしまうと、クエリーが失敗する羽目になるので注意が必要です。

Webコンテンツ の場合について

Web コンテンツ (SharePoint ではありません) のクロールでは、エントリーポイントからリンクされたドキュメントが次々とクロールされます。(つまり、エントリーポイントとつながっていないページ/ドキュメントは対象になりません。)

このクロールでは、基本認証、フォーム認証などで保護された Web コンテンツもクロールの対象にできます。こうした認証のかかったコンテンツをクロールする場合には、クロールルールを作成して、そのルールに認証情報を設定します。(Search Server 2008、及び Microsoft Office SharePoint Server のInfrastructure Update 以降では、フォーム認証/クッキーの場合もルール設定の画面で設定できます。以前は、こうした場合、別途コードを記述する必要がありました。)

一方、クロールされたこれらのコンテンツは、書籍でも記載している通り Security Descriptor をサポートしていませんので注意してください。このため、基本認証、フォーム認証などで保護されたこれらのコンテンツは、カスタムのセキュリティトリマーを作成/登録しておかないと、検索をおこなうすべてのユーザーに公開されてしまいます。
ここで 1 つの問題点としては、基本認証、ダイジェスト認証などのような ユーザーID / パスワード方式の認証の場合です。SharePoint 上では、多くの場合、Windows 認証を使用しているでしょうから、「パスワード」の文字列を抽出して渡すといったカスタムトリマーの作成はできません。よって、実際の環境構築の際には、別の方法 (あるいは何らかの業務上のルール作成、など) を検討する必要が生じるでしょう。

BDC の場合について

BDC のセキュリティトリミングも、アプリケーション定義ファイル (.xml) に AccessChecker メソッドインスタンスを作成するという点を除いては同様に登録作業をおこなうことで設定できます。(つまり、上記同様、クロールルールを作成して、stsadm -o registersecuritytrimmer を実行します。)

つまり、セッションでもお伝えしたように、BDC では、上記の ISecurityTrimmer の AccessCheck メソッドが BDC 内で定義されているという点を除き、まったく同様の概念でカスタムセキュリティを設定することが可能です。BDC では、stsadm -o registersecuritytrimmer コマンドで使用するクラスは、Microsoft.Office.Server.ApplicationRegistry.Search.QueryProcessorSecurityTrimmer というビルトインのクラスです。

Notes コンテンツの場合について

Notes のプロトコルハンドラーにも Security Descriptor は実装されており、権限情報は Windows の ACL に変換されます。ただし、セッションでもお伝えしたように、Notes 上のグループの情報 (グループ単位で設定されたセキュリティの情報) は変換の対象ではないので注意が必要です。

また書籍にも記載したように、Notes のクロールでは、対応バージョンに注意してください。懇親会でも、同様のフィードバックを頂きましたが、このバージョンの問題でハマってしまうケースは大変多く存在しているようですので、必ず検証前に確認をしておいてください。

 

Skip to main content