![]() ![]() ![]() ![]() ![]() ![]() | ![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
第5章
この章は、Composer対応のLDAP Connectに適用されるLDAPのプログラミングイディオムを理解するために構成されており、各種の高度なLDAPおよびComposer機能を合わせて使用する方法、特にテストとデバッグを中心に説明します。
LDAPにより提供される便利な検出メカニズム(および監査機能)は、ディレクトリサーバのDSEルートまたはDSA固有のエントリをクエリする機能です (DSAとはDirectory System Agentの略語で、ディレクトリサーバのX.500用語です)。
サーバでLDAPバージョン3がサポートされている場合、DSEルートでサーバに関する「メタ」情報を検索できます。DSEルートのエントリは、実際にはそれ自体がアドレス可能なオブジェクトではありませんが、特別な構文を利用してクエリすることができます(後で説明します)。
DSEのエントリをクエリすることにより、次の内容を検索できます(他にもあります)。
次の例では、www.nldap.com
に存在するNovellのパブリックサーバのDSEエントリをクエリするコンポーネントを構築する場合の手順を示します。ただし、(すでに前の節で説明されている)それぞれの動作やリソースを構築する段階ごとの詳細を繰り返すのではなく、設計セッションの一部としてコンポーネントをテストおよびデバッグする方法など、LDAPコンポーネントの構築に関連する広範囲なワークフローに焦点を当てます。
まず、接続リソースが必要です。この例では、LDAP接続リソースに次の設定を使用します。パスワードが指定されていないことに注意してください。そのため、バインドは(定義により)匿名になります。
次に、DSETestと呼ばれる新しいLDAPコンポーネントを作成します。 このコンポーネントには(検索要求タイプの)DSMLの作成アクションおよびDSMLの実行アクションが含まれます。
DSEルートダンプの検索パラメータは単純です。次の値でフィルタ設定された空のベースDNに関するベースレベルの検索のみが必要です。
(objectClass=*)
ネイティブ環境ペインの[検索]タブを次のように設定します。
注記: この例には、意図的にバグが含まれています。 この設定を使用すると、エラーが発生します (問題を発見できますか?)。
[検索]タブは次のようになります。
すべての属性に関する情報を受信するために、[属性]タブには何も入力しません (デフォルト値を受け入れます)。[選択済み]ペイン(右下)が空白であることに注意してください。このリストが空白の場合、デフォルトでは、クエリで「ヒット」したすべての属性がサーバにより返されます。
テストのため、単純に(作成アクションから) DSMLを[入力]にマップし、応答を[出力]にマップします。したがって、初期のアクションモデルは次のようになります。
アクションをテストするには、メインツールバーの[すべてを実行]ツールアイコンをクリックするだけです。
コンポーネントを実行すると、出力DOMにデータが表示されます。ただし、注意して確認すると、問題があることがわかります。出力ドキュメントがかなり短く見え、resultCode
がゼロではありません。
出力DOMの下にあるエラーの説明には「無効なDN構文」と表示されます。また、入力DOMツリー内のsearchRequest
の下のdn
の隣にある、データ値がorg.mozilla.javascript.Undefined@3cd5b5
である部分で反映されます。
問題: [検索]タブのベースDN値には、有効なECMAScriptが必要です。このフィールドには何も入力しませんでした。 その代わりに、空の文字列を指定する必要がありました。 2つの引用符の中に何も文字列を指定しないと空の文字列として認識されます。
修正は簡単です。[ベースDN]フィールドに戻り、2つの引用符を入力します。ただし、再び[すべてを実行]ボタンをクリックする前に、既存のDOMをクリーンアウトする必要があります。これを実行するには、メインメニューバーに移動して[コンポーネント]>[XMLドキュメントの再ロード]の順に選択します。
DOMのウィンドウが、元の(空の)状態にリセットされます。
注記: DOMがリセットされない場合、実行の次のラウンドではDSMLの作成アクションにより既存の入力ドキュメントにデータ(別の要求)が単に追加されます。 コンポーネントを実行すると、出力ドキュメントには2つの応答が含まれます。 これらの応答は、元の(正しくない)要求および入力ドキュメントに追加された要求に対する応答です。
コンポーネントを再実行すると、エラーなく動作します。出力DOMはかなり長く、DSEルートエントリに対応する多くのattr
要素が含まれています。
例に示されているように、LDAPまたはDSMLが正しくない場合でも、アクションモデルレベルでは操作自体でエラーは発生しません。クエリのコンテキストでLDAPの例外が発生した場合、resultCode
要素のDSMLドキュメントでクライアントに報告されるだけです。アプリケーションで例外の状態を認識し、特別なアクションを実行する場合もありますが、ただ情報が転送されるだけの場合があります。クエリが正常に行われなかった理由を認識する必要がある場合、これに対処するロジックを追加する必要があります。
例として、アプリケーションで不正なDSML要求のログを作成します。1つの方法は、次に示す動作が行われるようアクションモデルでロジックを含めることです。「成功以外の結果の場合、その理由をシステム出力(またはログファイル)に書き出す」
これを実行する方法は次のとおりです。
メインメニューバーの[アクション]メニューを使用して、[新規アクション]>[関数]の順に選択します。[関数アクション]ダイアログボックスが表示されます。
式ビルダで、resultCode
ノードおよびそのチャイルドノードが表示されるまでOutput
ノードツリーを展開します。code
属性ノードをダブルクリックします。テキスト編集ペインに、ECMAScript式が表示されます。
注記: この例では、DSMLドキュメントがすでに出力DOMにロードされていることを想定しています。必要に応じてDSMLの作成およびDSMLの実行アクションを構成し実行すると、式ビルダ選択ツリーに(図のような)完全に参照可能な出力DOMが作成されます。
式では、resultCodeのXPath構文がComposerの拡張メソッドXPath( )
へのコールの中でラップされます。式ビルダを使用すると視覚的なマウス操作だけでXPathに関連するECMAScript式を作成できるため、手動で式を入力する(そしてデバッグする)必要はありません。
ECMAScript式の前に、「theResultCode =」(括弧は不要)と入力します (これはスクリプト変数に結果コード値を保存する簡単な方法です)。
[OK]をクリックして、[関数アクション]ダイアログボックスに戻ります。ダイアログボックスの編集ウィンドウに次のステートメントが含まれていることを確認します。
theResultCode = Output.XPath("batchResponse/searchResponse/searchResultDone/resultCode/@code");
[OK]をクリックするか、「Enter」と入力して、[関数アクション]ダイアログボックスを閉じます。新しいアクションがアクションモデルに表示されます。
メインメニューバーの[アクション]メニューを使用して、[新規アクション]>[決定]の順に選択します。[決定アクション]ダイアログボックスが表示されます。
表示されているとおり、ボックスに「theResultCode != 0」と入力します。これは、[While]に指定する正しい条件です。theResultCode
がゼロの場合(正常)、条件はfalseです。値がゼロ以外の場合、条件はtrueになります。
[OK]をクリックするか、「Enter」と入力してダイアログボックスを閉じます。新しい決定アクションがアクションモデルに表示されます。
決定アクションの後、TRUEおよびFALSEステートメントがそれぞれの行に表示されます。TRUE行を1回だけクリックします。
メインメニューバーの[アクション]メニューを使用して、[新規アクション]>[ログ]の順に選択します。[ログアクション]ダイアログボックスが表示されます。
[ログ先]の下にあるラジオボタンのいずれかを選択して、このアクションによりログ記録されたメッセージの保存先を指定します。ここではテストのために[システム出力]を選択しました。Composerの場合、ログメッセージがComposerのメインウィンドウの下にある[ログ]タブに表示されるため、設計時のテスト中に簡単に認識できます。ランタイムでは、ユーザまたはシステムログに設定し直すこともできます。
このアクションのログメッセージに表示する説明のテキストを入力します (TRUEブランチに存在するため、このメッセージはクエリ結果がゼロ以外、つまり問題があった場合にログ記録されます)。この例では、式ビルダを使用して出力DSMLドキュメントでプレーンテキストのエラーメッセージを検索するECMAScriptステートメントを作成しました。
決定アクションのアクションモデルでFALSE行を1回だけクリックし、必要に応じてすでに説明した手順をくFALSEブランチで繰り返してログアクションを作成します (このブランチはクエリが問題なく正常に実行された場合に対応するブランチです)。
メインメニューバーで(DOMウィンドウをパージするために)[コンポーネント]>[XMLドキュメントの再ロード]の順に選択して、コンポーネントまたはその個別のアクションを実行し、テストします。
ComposerのECMAScriptをバインドすると、Javaオブジェクトへの直接的なコールを含め、プログラムによるさまざまな操作を実行するための強力で柔軟なツールが提供されます。Composer対応のLDAP ConnectではNovellのJLDAPライブラリ(すでにプロジェクトのCLASSPATHに含まれています)が使用されるため、JavaレベルのLDAP APIを直接簡単に呼び出すことができます。次に例を示します。
LDAPコンポーネントエディタを使用してアクションモデルを作成または編集する場合、関数アクションやスクリプト式が許可されているその他の場所でLDAP関連のComposer拡張メソッドをいくつか使用できます。 最も頻繁に使用するメソッドはgetLDAPAttr( )
です(以下の説明を参照)。
getLDAPAttr(String connResource, String dn, String attr)
- LDAPディレクトリ中、指定されたオブジェクトのある属性に格納された値を、第1引数で指定された名前の接続リソースを使って検索します。 第2引数はオブジェクトのLDAP識別名を表します。 第3引数は検索したい属性です。 戻り値は数値または文字列データです。 数値か文字列かの判定には、ECMAScriptのtypeof
演算子を使います。
アクセス制御リストは、IDや権限に基づいてディレクトリ情報へのアクセスを管理する方法としてNovell eDirectoryで使用されます。 アクセス制御は、ACLと呼ばれるオプションの複数値属性によって実装されます。この属性は「top」と呼ばれるトップレベルのディレクトリオブジェクトで定義されます。 すべてのディレクトリオブジェクトはトップオブジェクトから継承されるため、すべてのオブジェクトではACL属性を使用できます。
ツリーエントリのACL属性に保存される各値は、エントリへのアクセスが制御されるtrustee (異なるオブジェクト)に関する情報を表しています。 つまり、ACLにはデータストアオブジェクト自体の情報ではなく「クライアントオブジェクト」(アクセッサ)に関する情報が保存されます。
ComposerのLDAPコンポーネントエディタでは、セキュリティ上の理由からACLおよび権限セット管理するUIツールは公開されません。 ACLはディレクトリセキュリティの中心的な存在です。 従って、ACL編集機能をWebサービスで公開することは避ける必要があります。 それでもなお、通常のサービス実行の一環としてオブジェクトに対して「水面下で」トラスティ機能を設定することが必要であるまたは望まれる状況があります。 この処理はECMAScriptを使用してComposerで実行できます。
eDirectoryツリーのオブジェクトに添付できるACL (アクセス制御リスト)の値を作成するために使用できる2つの拡張メソッドがあります。
注記: これらのメソッドはNovell eDirectoryおよびNDS対応ディレクトリサーバにのみ関連します。
ndsACL.createEntryACL(boolean abBrowse, boolean abAdd, boolean abDelete, boolean abRename, boolean abSupervisor, boolean abInherit, String asTrusteeDN)
Novell eDirectoryツリーの任意のオブジェクトのACL属性(オプションの複数値属性)に追加できる適切な形式のACL値を作成します。 このメソッドの戻り値は、特定の「asTrusteeDN」アクセッサに適用される特別なアクセス制御リスト権限セットを表します。 最初の6つのパラメータは、許可される権限を示すtrue/falseフラグです。 「abInherit」引数は、trusteeオブジェクトのチャイルドがペアレントの権利を継承すべきであるかを決定します。
ndsACL.createAttrACL(boolean abCompare, boolean abRead, boolean abWrite, boolean abSelf, boolean abSupervisor, boolean abInherit, String asTrusteeDN, String asProtectedAttrName )
前に説明したメソッドと類似していますが、既存のオブジェクトの「属性」に対してアクセス制御ポリシーを作成します。
これらのメソッドを使用する一般的な手順は次のとおりです。
関数アクションで目的のメソッドを実行し、ECMAScript変数の戻り値を取得します。 ここでの説明では、変数名がaclEntryであると仮定します。
LDAPコンポーネントエディタで、アクションモデルにDSMLの作成アクションを追加します。 クエリタイプとして「変更」と指定します。
作成されたアクションのネイティブ環境タブで、追加操作を[変更]に追加し、目的のDN (ディレクトリエントリ)を対象とします。 aclEntry (ECMAScript変数)のACL値を使用して、このDNにACL属性を追加します。
例を示してより分かりやすく説明します。 ツリーに、CN (コンポーネント名)がBobであるinetOrgPerson
が存在すると仮定します。 BobのtelephoneNumber
属性をJillという名前の別のinetOrgPerson
に公開するには、 ndsACL.createAttrACL()
を使用してアクセス制御値を作成し、JillのDNをトラスティDN、telephoneNumber
を保護された属性名として指定します。 変更操作では、アクセス制御値をBobのACL属性に追加します (ACLは複数値属性です。 BobのACL属性にはスコアまたは何百もの値が保存されている可能性があります)。 この操作が完了すると、JillはBobの電話番号への特別なアクセス権を持ちます。
警告: ACLはeDirectoryセキュリティの重要なコンポーネントです。 前に説明したメソッドを使用する前にACLの概念について熟知しておく必要があります。 ACLメソッドを正しく使用しないと、ディレクトリツリーの異なる多くのレベルでセキュリティ上の問題が生じる原因となります。 NDSセキュリティ概念とその意味を完全に理解していない場合はACLを変更しないでください。
ACL概念の技術的な概要については、http://developer.novell.com/ndkからオンラインで参照できる『NDS Technical Overview』マニュアル(特にeDirectoryセキュリティに関する章)を参照してください。
Novell NDKの一部として使用できるNovell JLDAP (Java LDAP)ライブラリクラスはComposerの一部であり(設計時およびランタイム時)、すでにクラスパス内に存在します。 このため、次のようなコードをComposerのカスタムスクリプトリソースエディタ(または式ビルダダイアログボックス)で記述できます。
myConn = new Packages.com.novell.ldap.LDAPConnection();
ECMAScriptでは、JavaクラスローダへのアクセスはPackagesキーワードを通じて実行します。 したがって、クラスパスで表示されている任意のクラスにアクセスするには、前に示されているように修飾クラス名の前に「Packages」を単純に追加します。 ECMAScript変数ではデータタイプ宣言が必要でないため、前に示したコードラインをそのままの状態で実行するのは正しい処理です。 1つの操作で変数myConnが宣言され、初期化されます。
この節では、ECMAScriptを使用してNovell LDAPクラスを直接呼び出す方法の例を示します。この方法によって、LDAPコンポーネントエディタでは実行できないタスクを実行できます。 以下の例では、LDIFを使用します。
DSMLが登場する前は、LDAPオブジェクトおよびクエリを維持するために、ディレクトリユーザはテキストベースのLDIF (LDAP Data Interchange Format)ファイルタイプに大部分(今でも状況は同じです)依存していました (LDIF仕様はRFC 2849公表されています)。XMLと同様、LDIFが有用であるのは多くの理由が挙げられます。 LDIFは人間による認識が可能であり、比較的簡単なルールで構成され、プラットフォームは選びません。また、ほとんどの管理ツールはLDIFをネイティブで処理できます。
クエリ結果のDSMLバージョンとLDIFバージョンを比較すると有用です。 DSMLバージョンは、次のようになります。
<?xml version="1.0" encoding="UTF-8"?>
<batchResponse xmlns="urn:oasis:names:tc:DSML:2:0:core">
<searchResponse requestID="160">
<searchResultEntry dn="cn=admin,ou=lapdog,ou=user,o=NOVELL" requestID="160">
<attr name="cn">
<value>admin</value>
</attr>
<attr name="loginTime">
<value>20030315051622Z</value>
</attr>
<attr name="objectClass">
<value>inetOrgPerson</value>
<value>organizationalPerson</value>
<value>person</value>
<value>top</value>
<value>ndsLoginProperties</value>
</attr>
<attr name="securityEquals">
<value>ou=lapdog,ou=user,o=NOVELL</value>
...
[ etc. ]
LDIFバージョンは次のようになります。
version: 1
dn:cn=admin,ou=lapdog,ou=user,o=NOVELL
cn:admin
loginTime:20030315051622Z
objectClass:inetOrgPerson
objectClass:organizationalPerson
objectClass:person
objectClass:top
objectClass:ndsLoginProperties
securityEquals:ou=lapdog,ou=user,o=NOVELL
. . .
[ etc. ]
LDIFファイルの構造は、レコードが1行あたり1つ存在し各レコードがコロンで区切られた属性-値の対を表すような単純なフラットファイルの構造とさほど異なりません (構文はそれほど単純ではありませんが、この内容とかなり近いものです)。
監査のために、特定のツリーオブジェクトの「LDIFダンプ」を取得すると便利な場合があります。実行にはそれほど多くのECMAScriptは使用しません。
プログラムによりディレクトリをクエリして結果をLDIFファイルに書き出すには、カスタムスクリプトリソースの作成から始めます(Composerのメインメニューバーを使用して、[ファイル]>[新規作成]>[xObject]の順に選択し、[リソース]タブから[カスタムスクリプト]を選択します)。カスタムスクリプトエディタウィンドウ内で、次のようなカスタム関数を入力します。
function query2Ldif( ldapHost, loginDN, password, searchBase, scope, searchFilter, attribs, fileName ) { var jldap = Packages.com.novell.ldap; var ldapPort = jldap.LDAPConnection.DEFAULT_PORT; var ldapVersion = jldap.LDAPConnection.LDAP_V3; var typesOnly = false; var lc = new jldap.LDAPConnection(); // An ECMAScript array reference cannot // be passed to a Java method that expects // a Java array.So we have to copy our // \qattribs\q array into a legit Java array: var javaStringArray = java.lang.reflect.Array.newInstance( java.lang.String, attribs.length); for (var i = 0; i < attribs.length; i++) javaStringArray[i] = new java.lang.String(attribs[i]); // bind to server lc.connect( ldapHost, ldapPort ); lc.bind(ldapVersion, loginDN, password ); // set up file I/O objects var fos = new java.io.FileOutputStream(fileName); var writer = new jldap.util.LDIFWriter(fos); // send query var results = lc.search( searchBase, scope, searchFilter, javaStringArray, typesOnly); // write the results while ( results.hasMore() ) writer.writeEntry( results.next() ); // clean up writer.finish(); fos.close(); lc.disconnect(); java.lang.System.out.println("LDIF file written."); }
関連する一連の手順で目的の機能をすべて表現するために、この関数は意図的に大規模な構造となっています (実際には、2つの小規模な関数に分解できます。 この2つの関数とは、クエリをバインドして発行する関数とクエリ結果をLDIFファイルに書き出す関数です)。
8つの引数は、LDAPディレクトリのバインドに必要な標準パラメータおよびクエリの構成に必要な標準パラメータを表しています。
関数内部の最初の行は次のとおりです。
var jldap = Packages.com.novell.ldap;
この行により、「Packages」で始まるやや長めのコンテキスト文字列に対して(jldap
の)簡単な表記を使用できます (ECMAScriptを使用すると、Packagesの間接化技法でJavaメソッドにアクセスできます)。この略記を作成した後、次のように記述できます。
var lc = new jldap.LDAPConnection();
次のように記述する必要はありません。
var lc = new Packages.com.novell.ldap.LDAPConnection();
JLDAPオブジェクトを取得する必要がある場合に、多少入力が少なくなります。
LDAPConnectionオブジェクトのインスタンスが生成された後、次を入力してツリーにバインドできます。
lc.connect( ldapHost, ldapPort ); lc.bind(ldapVersion, loginDN, password );
これらの行は標準のJLDAPコールで、『Novell NDK Javadocs』に記載されています。
次に、file-I/Oオブジェクトを設定します。
var fos = new java.io.FileOutputStream(fileName); var writer = new jldap.util.LDIFWriter(fos);
ファイルは引数fileName
に入力されたパスに書き出されます。
サーバをクエリするには、1行のコードが必要です。
// send query var results = lc.search( searchBase, scope, searchFilter, javaStringArray, typesOnly);
検索結果は単純に列挙し、書き出すことができます。
while ( results.hasMoreElements() ) writer.writeEntry( results.nextElement() );
最後に、すべてのファイル、ストリーム、および接続を閉じます。
writer.finish(); fos.close(); lc.disconnect();
スクリプトをテストする場合、関数アクションで次のテキストを入力して実行できます。
query2Ldif( \qwww.nldap.com\q \qanonymous\q, \q\q, /* no password */ \q\q, /* no search base DN */ 0, /* 0 for base-object scope */ \q(objectClass=*)\q, /* filter == all objects */ new Array(\q*\q), /* attrib array */ \qc:\\temp\\test.ldif\q )
このスクリプトを持つ関数アクションを実行すると、NovellパブリックサーバでDSEルート情報がクエリされます。LDIFファイルはディスクに書き出されます。
LDIFファイルは、従来のテキストエディタで確認できます。次の内容が含まれています(一部)。
# This LDIF file was generated by the LDIF APIs of Novell\qs Java LDAP SDK
version: 1
dn:
errors: 21428
subschemaSubentry:cn=schema
directoryTreeName:DEVNET-TREE
securityErrors: 972
compareOps: 218
bindSecurityErrors: 850
outBytes: 2279628812
vendorVersion:eDirectory v8.7.0 (10410.57)
simpleAuthBinds: 383333
supportedFeatures: 1.3.6.1.4.1.4203.1.5.1
supportedFeatures: 2.16.840.1.113719.1.27.99.1
repUpdatesIn: 0
abandonOps: 572
supportedSASLMechanisms:EXTERNAL
supportedSASLMechanisms:DIGEST-MD5
supportedSASLMechanisms:NMAS_LOGIN
referralsReturned: 0
removeEntryOps: 6683
searchOps: 654935
addEntryOps: 20253
strongAuthBinds: 32
modifyEntryOps: 888
vendorName:Novell, Inc.
listOps: 0
modifyRDNOps: 9
chainings: 300
...
[ etc. ]
Copyright © 2004 Novell, Inc. All rights reserved. Copyright © 1997, 1998, 1999, 2000, 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved. more ...