第5章

LDAPおよびDSMLの使用

この章は、Composer対応のLDAP Connectに適用されるLDAPのプログラミングイディオムを理解するために構成されており、各種の高度なLDAPおよびComposer機能を合わせて使用する方法、特にテストとデバッグを中心に説明します。

 
Top of page

DSEクエリの例

LDAPにより提供される便利な検出メカニズム(および監査機能)は、ディレクトリサーバのDSEルートまたはDSA固有のエントリをクエリする機能です (DSAとはDirectory System Agentの略語で、ディレクトリサーバのX.500用語です)。

サーバでLDAPバージョン3がサポートされている場合、DSEルートでサーバに関する「メタ」情報を検索できます。DSEルートのエントリは、実際にはそれ自体がアドレス可能なオブジェクトではありませんが、特別な構文を利用してクエリすることができます(後で説明します)。

DSEのエントリをクエリすることにより、次の内容を検索できます(他にもあります)。

次の例では、www.nldap.comに存在するNovellのパブリックサーバのDSEエントリをクエリするコンポーネントを構築する場合の手順を示します。ただし、(すでに前の節で説明されている)それぞれの動作やリソースを構築する段階ごとの詳細を繰り返すのではなく、設計セッションの一部としてコンポーネントをテストおよびデバッグする方法など、LDAPコンポーネントの構築に関連する広範囲なワークフローに焦点を当てます。

 
Top of section

匿名バインドの接続リソース

まず、接続リソースが必要です。この例では、LDAP接続リソースに次の設定を使用します。パスワードが指定されていないことに注意してください。そのため、バインドは(定義により)匿名になります。

5AnonConnectRes

 
Top of section

コンポーネントおよびアクションモデル

次に、DSETestと呼ばれる新しいLDAPコンポーネントを作成します。 このコンポーネントには(検索要求タイプの)DSMLの作成アクションおよびDSMLの実行アクションが含まれます。

DSEルートダンプの検索パラメータは単純です。次の値でフィルタ設定された空のベースDNに関するベースレベルの検索のみが必要です。

(objectClass=*)

ネイティブ環境ペインの[検索]タブを次のように設定します。

5BuggyQuery

注記:   この例には、意図的にバグが含まれています。 この設定を使用すると、エラーが発生します (問題を発見できますか?)。

[検索]タブは次のようになります。

5Searchtab

すべての属性に関する情報を受信するために、[属性]タブには何も入力しません (デフォルト値を受け入れます)。[選択済み]ペイン(右下)が空白であることに注意してください。このリストが空白の場合、デフォルトでは、クエリで「ヒット」したすべての属性がサーバにより返されます。

5attribs

テストのため、単純に(作成アクションから) DSMLを[入力]にマップし、応答を[出力]にマップします。したがって、初期のアクションモデルは次のようになります。

5ActionModel1

アクションをテストするには、メインツールバーの[すべてを実行]ツールアイコンをクリックするだけです。

4ExecuteAll

コンポーネントを実行すると、出力DOMにデータが表示されます。ただし、注意して確認すると、問題があることがわかります。出力ドキュメントがかなり短く見え、resultCodeがゼロではありません。

5DOMsAfterError

出力DOMの下にあるエラーの説明には「無効なDN構文」と表示されます。また、入力DOMツリー内のsearchRequestの下のdnの隣にある、データ値がorg.mozilla.javascript.Undefined@3cd5b5である部分で反映されます。

問題: [検索]タブのベースDN値には、有効なECMAScriptが必要です。このフィールドには何も入力しませんでした。 その代わりに、空の文字列を指定する必要がありました。 2つの引用符の中に何も文字列を指定しないと空の文字列として認識されます。

修正は簡単です。[ベースDN]フィールドに戻り、2つの引用符を入力します。ただし、再び[すべてを実行]ボタンをクリックする前に、既存のDOMをクリーンアウトする必要があります。これを実行するには、メインメニューバーに移動して[コンポーネント]>[XMLドキュメントの再ロード]の順に選択します。

4reloadXMLDocs

DOMのウィンドウが、元の(空の)状態にリセットされます。

注記:   DOMがリセットされない場合、実行の次のラウンドではDSMLの作成アクションにより既存の入力ドキュメントにデータ(別の要求)が単に追加されます。 コンポーネントを実行すると、出力ドキュメントには2つの応答が含まれます。 これらの応答は、元の(正しくない)要求および入力ドキュメントに追加された要求に対する応答です。

コンポーネントを再実行すると、エラーなく動作します。出力DOMはかなり長く、DSEルートエントリに対応する多くのattr要素が含まれています。

5LongOutputDOM

 
Top of section

エラーの処理

例に示されているように、LDAPまたはDSMLが正しくない場合でも、アクションモデルレベルでは操作自体でエラーは発生しません。クエリのコンテキストでLDAPの例外が発生した場合、resultCode要素のDSMLドキュメントでクライアントに報告されるだけです。アプリケーションで例外の状態を認識し、特別なアクションを実行する場合もありますが、ただ情報が転送されるだけの場合があります。クエリが正常に行われなかった理由を認識する必要がある場合、これに対処するロジックを追加する必要があります。

例として、アプリケーションで不正なDSML要求のログを作成します。1つの方法は、次に示す動作が行われるようアクションモデルでロジックを含めることです。「成功以外の結果の場合、その理由をシステム出力(またはログファイル)に書き出す」

これを実行する方法は次のとおりです。

Procedure クエリの失敗を通知するログを作成する

  1. アクションペイン(アクションモデル)内で、新しいアクションを表示する場所にカーソルを置きます。

  2. メインメニューバーの[アクション]メニューを使用して、[新規アクション]>[関数]の順に選択します。[関数アクション]ダイアログボックスが表示されます。

  3. 小さなEアイコンをクリックすると、[式ビルダ]ダイアログボックスが開きます。

    5ExprBuilder

  4. 式ビルダで、resultCodeノードおよびそのチャイルドノードが表示されるまでOutputノードツリーを展開します。code属性ノードをダブルクリックします。テキスト編集ペインに、ECMAScript式が表示されます。

    5ExprBuilder2

    注記:   この例では、DSMLドキュメントがすでに出力DOMにロードされていることを想定しています。必要に応じてDSMLの作成およびDSMLの実行アクションを構成し実行すると、式ビルダ選択ツリーに(図のような)完全に参照可能な出力DOMが作成されます。

    式では、resultCodeのXPath構文がComposerの拡張メソッドXPath( )へのコールの中でラップされます。式ビルダを使用すると視覚的なマウス操作だけでXPathに関連するECMAScript式を作成できるため、手動で式を入力する(そしてデバッグする)必要はありません。

  5. ECMAScript式の前に、「theResultCode =」(括弧は不要)と入力します (これはスクリプト変数に結果コード値を保存する簡単な方法です)。

  6. [OK]をクリックして、[関数アクション]ダイアログボックスに戻ります。ダイアログボックスの編集ウィンドウに次のステートメントが含まれていることを確認します。

      theResultCode = 
      Output.XPath("batchResponse/searchResponse/searchResultDone/resultCode/@code");
    

    1つのライン上のすべて文字列が表示されています。 ラインの折り返しは無視してください。

  7. [OK]をクリックするか、「Enter」と入力して、[関数アクション]ダイアログボックスを閉じます。新しいアクションがアクションモデルに表示されます。

    メインメニューバーの[アクション]メニューを使用して、[新規アクション]>[決定]の順に選択します。[決定アクション]ダイアログボックスが表示されます。

    5Decision

  8. 表示されているとおり、ボックスに「theResultCode != 0」と入力します。これは、[While]に指定する正しい条件です。theResultCodeがゼロの場合(正常)、条件はfalseです。値がゼロ以外の場合、条件はtrueになります。

  9. [OK]をクリックするか、「Enter」と入力してダイアログボックスを閉じます。新しい決定アクションがアクションモデルに表示されます。

  10. 決定アクションの後、TRUEおよびFALSEステートメントがそれぞれの行に表示されます。TRUE行を1回だけクリックします。

  11. メインメニューバーの[アクション]メニューを使用して、[新規アクション]>[ログ]の順に選択します。[ログアクション]ダイアログボックスが表示されます。

    5Log

  12. [ログ先]の下にあるラジオボタンのいずれかを選択して、このアクションによりログ記録されたメッセージの保存先を指定します。ここではテストのために[システム出力]を選択しました。Composerの場合、ログメッセージがComposerのメインウィンドウの下にある[ログ]タブに表示されるため、設計時のテスト中に簡単に認識できます。ランタイムでは、ユーザまたはシステムログに設定し直すこともできます。

  13. このアクションのログメッセージに表示する説明のテキストを入力します (TRUEブランチに存在するため、このメッセージはクエリ結果がゼロ以外、つまり問題があった場合にログ記録されます)。この例では、式ビルダを使用して出力DSMLドキュメントでプレーンテキストのエラーメッセージを検索するECMAScriptステートメントを作成しました。

  14. [OK]をクリックして、ダイアログボックスを閉じます。

  15. 決定アクションのアクションモデルでFALSE行を1回だけクリックし、必要に応じてすでに説明した手順をくFALSEブランチで繰り返してログアクションを作成します (このブランチはクエリが問題なく正常に実行された場合に対応するブランチです)。

    この時点で、アクションモデルは次のようになります。

    5DSETestActionModel

  16. メインメニューバーで(DOMウィンドウをパージするために)[コンポーネント]>[XMLドキュメントの再ロード]の順に選択して、コンポーネントまたはその個別のアクションを実行し、テストします。

  17. [保存]をクリックして保存します。

 
Top of page

ECMAScriptとLDAP Connect

ComposerのECMAScriptをバインドすると、Javaオブジェクトへの直接的なコールを含め、プログラムによるさまざまな操作を実行するための強力で柔軟なツールが提供されます。Composer対応のLDAP ConnectではNovellのJLDAPライブラリ(すでにプロジェクトのCLASSPATHに含まれています)が使用されるため、JavaレベルのLDAP APIを直接簡単に呼び出すことができます。次に例を示します。

 
Top of section

LDAP拡張メソッド

LDAPコンポーネントエディタを使用してアクションモデルを作成または編集する場合、関数アクションやスクリプト式が許可されているその他の場所でLDAP関連のComposer拡張メソッドをいくつか使用できます。 最も頻繁に使用するメソッドはgetLDAPAttr( )です(以下の説明を参照)。

getLDAPAttr(String connResource, String dn, String attr) - LDAPディレクトリ中、指定されたオブジェクトのある属性に格納された値を、第1引数で指定された名前の接続リソースを使って検索します。 第2引数はオブジェクトのLDAP識別名を表します。 第3引数は検索したい属性です。 戻り値は数値または文字列データです。 数値か文字列かの判定には、ECMAScriptのtypeof演算子を使います。

 
Top of section

ACL (アクセス制御リスト)メソッド

アクセス制御リストは、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 )

前に説明したメソッドと類似していますが、既存のオブジェクトの「属性」に対してアクセス制御ポリシーを作成します。

これらのメソッドを使用する一般的な手順は次のとおりです。

例を示してより分かりやすく説明します。 ツリーに、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セキュリティに関する章)を参照してください。

 
Top of section

Novell LDAPクラスへのアクセス

Novell NDKの一部として使用できるNovell JLDAP (Java LDAP)ライブラリクラスはComposerの一部であり(設計時およびランタイム時)、すでにクラスパス内に存在します。 このため、次のようなコードをComposerのカスタムスクリプトリソースエディタ(または式ビルダダイアログボックス)で記述できます。

  myConn = new Packages.com.novell.ldap.LDAPConnection();

ECMAScriptでは、JavaクラスローダへのアクセスはPackagesキーワードを通じて実行します。 したがって、クラスパスで表示されている任意のクラスにアクセスするには、前に示されているように修飾クラス名の前に「Packages」を単純に追加します。 ECMAScript変数ではデータタイプ宣言が必要でないため、前に示したコードラインをそのままの状態で実行するのは正しい処理です。 1つの操作で変数myConnが宣言され、初期化されます。

 
Top of page

LDIFを使用するECMAScriptの例

この節では、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 ...