C#.NET で LDAP を使ってディレクトリサービスを検索するための覚え書きです。
なお、ディレクトリサービス関係には The Apache Directory Project の
Apache Directory Server 1.5.4 (以下 ApacheDS) および Apache Directory Studio 1.3.0 (以下 Studio) を利用しました。
なお、Apache Directory Server 1.5.4 には別途 jre 5 以降が必要です。
ApacheDS と Studio は、それぞれ「C:\Bin\Apache Directory Server」、「C:\Bin\Apache Directory Studio」 にインストールしました。また、ApacheDS のインストール時に作成されるインスタンスはデフォルトのまま 「C:\Bin\Apache Directory Server\instances\default」に作成しました。
インストール直後の ApacheDS のデフォルトインスタンスを Stduio を使って見てみると、以下のようになっています。
左のツリーには表示されていませんが、デフォルトでは「dc=example,dc=com」というベース DN を持つエントリが用意されています。
このサンプルでは、これは利用せず、別途「o=sample,c=jp」というベース DN を持つエントリを作成し、
そこにデータを追加します。
ApacheDS では、エントリはパーティションと呼ばれる領域に格納され、そこに個々のエントリのツリーデータが格納されます。
まず、「o=sample,c=jp」のエントリを作成します。
デフォルトインスタンスの定義ファイル「C:\Bin\Apache Directory Server\instances\default\conf\server.xml」を開き、 「o=sample,c=jp」のパーティション定義を追加します。ついでにデフォルトで作成されている「dc=example,dc=com」エントリは コメントアウトします。
定義ファイルを保存したら、ApacheDS のサービスを再起動します。
パーティションは「C:\Bin\Apache Directory Server\instances\default\partitions」配下に作成されるのですが、
「o=sample,c=jp」のパーティションとなる「sample」フォルダは再起動時に自動的に作成されます。
なお、設定ファイルでコメントアウトした「dc=example,dc=com」のパーティションである
「example」フォルダは削除しても構いません。
ここでもう一度 ApacheDS のインスタンスを Stduio を使って見てみます。
追加した「o=sample,c=jp」のエントリが作成されています。
次に、以下の内容をテキストエディタで編集し、「sample.ldif」として保存します。
dn: o=sample,c=jp o: sample objectClass: top objectClass: organization description: sample entry
保存したファイルを Studio を使って ApacheDS のデフォルトインスタンスにインポートします。
「LDAP Browser」で「DIT」を右クリックして、メニューを表示させ、「LDIF Import」を選びます。
「LDIF Import」画面で「Browse」をクリックし、さきほど作成した「sample.ldif」を選んでから、
[Finish] をクリックします。
※2 回以上同じファイルをインポートする場合、「インポートファイル名.log」(この例では「sample.ldif.log」)
というファイル名でログファイルが同じフォルダに作成されるため、「Overwrite existing logfile」にチェックを入れないと
「Finish」ボタンが有効になりません。
その後、一旦、ApacheDS との接続を切り、再度接続します。
ツリー表示に「o=sample,c=jp」のエントリが表示されます。
作成するサンプルでは単純なツリーを作成して検索するだけなのですが、ついでなので、独自の属性値「userRoomCode」を設定できるようにします。 独自の属性を設定できるようにするためには、スキーマを定義して ApacheDS にインポートする必要があります。
スキーマを定義する際には、OID と呼ばれる識別子を指定する必要があります。
本来なら OID はグローバルにユニークとなる値を取得する必要がありますが、ここではサンプル用なので勝手に定義しています。
もし、ネット上に公開する場合など、グローバルに通用するサービスでスキーマを定義する場合は、
必ずユニークな OID を取得して使うようにしてください。
なお、このサンプルで使っている OID は、ApacheDS のカスタムスキーマ定義のドキュメントに
サンプルとして載っている OID の後ろに、勝手に枝番を追加して利用しています。
この 「1.3.6.1.4.1.18060」から始まる OID は Apache Software Foundation のためのものなので、
お遊びでなく、ちゃんとした業務に利用する OID としては、たとえローカルで運用するサービスであっても不適切だと思われます。
が、ここでは、サンプルということでご容赦くださいませ。
以下の内容をテキストエディタで編集し、「sampleSchema.ldif」として保存します。
dn: cn=schema
changetype: modify
add: attributeTypes
attributeTypes: ( 1.3.6.1.4.1.18060.0.4.3.2.1.1.1
NAME 'userRoomCode'
DESC 'User Room Code'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
)
-
add: objectClasses
objectClasses: ( 1.3.6.1.4.1.18060.0.4.3.2.1.2.1
NAME 'mySample'
DESC 'My Sample Object Class'
SUP top
AUXILIARY
MAY ( userRoomCode )
)
-
「LDAP Browser」で「DIT」を右クリックして、メニューを表示させ、「LDIF Import」を選びます。
「LDIF Import」画面で「Browse」をクリックし、さきほど作成した「sampleSchema.ldif」を選んでから、
[Finish] をクリックします。
その後、一旦、ApacheDS との接続を切り、再度接続します。
※切断・再接続は不要のようなのですが、私の環境では、切断・再接続をしないと、インポートしたスキーマ定義を Studio 側でうまく取得できませんでした。
スキーマ定義が反映されたことを確認してみます。
「Root DSE」をクリックしてから、メニューの 「LDAP」-「Open Schema Browser」を選びます。
「Filter」に「mySample」と入力し、一覧部に表示される「mySample」をクリックすると、 追加したオブジェクトクラスが表示されます。
属性も見てみます。「MAY Attributes」をクリックして表示される「userRoomCode」をクリックします。
追加した属性が表示されます。
検索対象となるデータを追加します。
「LDAP Browser」で「o=sample,c=jp」を右クリックして、メニューを表示させ、「New Entry」を選びます。
「New Entry」画面で「Create entry from scratch」を選び、「Next」をクリックします。
「Available object classes」から「organizationUnit」を選び、「Add」をクリックします。
「Next」をクリックします。
「RDN」で、左側に「ou」、右側に「mymembers」と入力し、「Next」をクリックします。
「Finish」をクリックします。
以上で「ou=mymembers,o=sample,c=jp」が追加されました。
さらに、追加した「ou=mymembers,o=sample,c=jp」を右クリックして、メニューを表示させ、「New Entry」を選びます。
「New Entry」画面で「Create entry from scratch」を選び、「Next」をクリックします。
「Available object classes」から「inetOrgPerson」を選び、「Add」をクリックします。
「Available object classes」から「mySample」を選び、「Add」をクリックします。
「Next」をクリックします。
「RDN」で、左側に「uid」、右側に「00001」と入力し、「Next」をクリックします。
「cn」に「太郎」、「sn」に「山田」と入力します。
一覧上で右クリックし、メニューから「New Attribute」を選びます。
「Attribute type」に「userRoomCode」と入力し、「Finish」をクリックします。
追加した「userRoomCode」に「101」と入力し、「Finish」をクリックします。
以上で「uid=00001,ou=mymembers,o=sample,c=jp」が追加されました。
さらに、同じような手順により、「uid=00002,ou=mymembers,o=sample,c=jp」、「uid=00003,ou=mymembers,o=sample,c=jp」を追加し、 適当に「cn」、「sn」、「userRoomCode」を設定しました。
ソースだけです。すみません。。。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.DirectoryServices.Protocols;
namespace SampleSearch {
class Program {
/// <summary>
/// ディレクトリサービスの検索を行う
/// </summary>
/// <param name="args">
/// </param>
static void Main(string[] args) {
string ldapConnectionHostname = "localhost:10389"; // ApacheDS のデフォルトポートは 10389
string ldapCredentialDN = "uid=admin,ou=system"; // ApacheDS のデフォルトインスタンスの管理者ユーザの DN
string ldapCredentialPassword = "secret"; // 管理者ユーザのパスワード
string ldapSearchDN = "ou=mymembers,o=sample,c=jp"; // 作成したデータ下を検索する
string ldapSearchFilter = "(uid=*)"; // 検索条件
LdapConnection ldapConnection = null;
try {
// ディレクトリサービスに接続する
ldapConnection = new LdapConnection(ldapConnectionHostname);
ldapConnection.Credential = new NetworkCredential(ldapCredentialDN, ldapCredentialPassword);
ldapConnection.AuthType = AuthType.Basic;
ldapConnection.SessionOptions.ProtocolVersion = 3;
ldapConnection.Bind();
// 作成したデータの検索を行う
SearchRequest schRequest = new SearchRequest();
schRequest.DistinguishedName = ldapSearchDN;
////srequest.Scope = System.DirectoryServices.Protocols.SearchScope.Subtree;
schRequest.Filter = ldapSearchFilter;
SearchResponse schResponse = (SearchResponse) ldapConnection.SendRequest(schRequest);
SearchResultEntryCollection resultList = schResponse.Entries;
// 検索結果を表示する
foreach (SearchResultEntry sre in resultList) {
string uid = GetAttributeValue(sre.Attributes, "uid");
string cn = GetAttributeValue(sre.Attributes, "cn");
string sn = GetAttributeValue(sre.Attributes, "sn");
string userRoomCode = GetAttributeValue(sre.Attributes, "userRoomCode");
uid = Null2Empty(uid);
cn = Null2Empty(cn);
sn = Null2Empty(sn);
userRoomCode = Null2Empty(userRoomCode);
Console.WriteLine(string.Format("uid: {0}, cn: {1}, sn: {2}, userRoomCode: {3}",
uid, cn, sn, userRoomCode
)
);
}
} catch (Exception ex) {
Console.WriteLine(string.Format("失敗しました: {0}", ex.Message));
} finally {
if (ldapConnection != null) {
try {
ldapConnection.Dispose();
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
}
}
/// <summary>
/// 属性から名前に対応する値を得る
/// </summary>
/// <returns>名前に対応する値(無い場合は null)</returns>
private static string GetAttributeValue(SearchResultAttributeCollection attrs, string name) {
string result = null;
if (attrs.Contains(name)) {
DirectoryAttribute attr = attrs[name];
string[] valArray = (string[]) attr.GetValues(typeof(string));
if (0 < valArray.Length) {
result = Null2Empty(valArray[0]).Trim();
}
}
return result;
}
/// <summary>
/// null なら空文字を返し、null でなければ元の値を返す
/// </summary>
/// <param name="v">null を含む値</param>
/// <returns>null を含まない値</returns>
private static string Null2Empty(string v) {
if (v == null) {
return "";
}
return v;
}
}
}
ソース (Visual Studio 2008 で作成しました)
SampleSearch.zip (4.03 KB)
ApacheDS の設定に使用したファイル類
ApacheDS.zip (3.03 KB)