Delphi Labs: DataSnap XE - 認証と承認

By: Chikako Yonezawa

Abstract: この記事では、Delphi XE を使用して、認証と承認のためにサポートされた新しい DataSnap の機能を調査します。

    はじめに

多層システムの設計時の最初のアーキテクチャの問題のひとつはセキュリティです。インターネット経由で通信するシステムでは、さまざまな種類の悪意のある攻撃に対し特に脆弱です。セキュリティは、トランスポートのレイヤーから、ロールに基づいた実際のシステム機能への細粒度で選択的なアクセスまでのすべてのレイヤーにわたっています。例えば、クライアントの要求を管理するシステムでは2種類のアクセス権が考えられます。1つは、認証されたユーザーが情報を何も変更できず参照しか行えない低いレベルのアクセスで、もう1つは、認証されたユーザーが基となるデータの変更を許される高いレベルのアクセスです。

この種の細粒度のセキュリティは、通常2つの手順で実現されています。

  • 認証 (Authentication) は、ユーザー名とパスワードを基に、ユーザーの身元を確認するプロセスです
  • 承認 (Authorization) は、認証されたユーザーのサーバー側のリソースへのアクセスを許可、または拒否するプロセスです

DataSnap フレームワークは、RAD Studio XE で導入された “TDSAuthenticationManager” を通して認証と承認のサポートを実装します。

    サーバーの構築

IDE のメニューから [ファイル|新規作成|その他] を選択し、スタンドアロンの Delphi DataSnap サーバーアプリケーションを作成するため、「新規作成」ダイアログ内の [Delphi プロジェクト|DataSnap Server] カテゴリ内の「DataSnap Server」アイコンをダブルクリックします。

Hide image
DataSnap01

最初のタブでは、デフォルトのプロジェクトの種類である「VCL フォームアプリケーション」のままとします。そして、次のタブでは、「認証」と「権限付与」のチェックボックスをチェックします。

Hide image
Click to see full-sized image

残りのウィザードの画面は、すべてデフォルトのままとし、[完了] ボタンをクリックし、DataSnap サーバープロジェクトを生成します。

expand view>>

メニューより、[ファイル|すべて保存] を選択し、ウィザードによって生成された新しいプロジェクトとファイルをすべて保存します。例えば、新規に “C:\DataSnapLabs\AuthenticationAndAuthorization\” フォルダを作成し、プロジェクトの主となるユニットには “FormServerUnit” と命名し、他のユニットは、デフォルトの名前 (通常 “ServerMethodsUnit1” と “ServerContainerUnit1”) のままとし、プロジェクトには “SecureDSServer” と命名して保存します。

プロジェクトマネージャーの ServerContainerUnit 上で、ダブルクリックして、フォームデザイナを表示させます。「DataSnap Server」ウィザードは、通常のサーバーコンポーネントに、“DSAuthenticationManager1” コンポーネントを追加して生成しています。

Hide image
Click to see full-sized image

expand view>>

また、“DSTCPServerTransport1.AuthenticationManager” プロパティは、この新しいコンポーネントを指していることも分かります。

例えば、[F12] キーを押して、コードエディタに切り替えると、ウィザードが生成した “OnUserAuthenticate” と “OnUserAuthorize” イベントのほぼ空の実装を確認できます。

procedure TServerContainer2.DSAuthenticationManager1UserAuthenticate(
  Sender: TObject; const Protocol, Context, User, Password: string;
  var valid: Boolean; UserRoles: TStrings);
begin

  valid := True;
end;

procedure TServerContainer2.DSAuthenticationManager1UserAuthorize(
  Sender: TObject; EventObject: TDSAuthorizeEventObject;
  var valid: Boolean);
begin

  valid := True;
end;

プログラマが最小限行うことは、 “valid” パラメータを true または false に設定することです。 “var” 修飾子に注意してください。このパラメータに割り当てた値は、これらのプロシージャのローカルスコープを超えて保持されます。認証 (authentication) イベント内で、“valid” を “false”に設定した場合、いかなるクライアントも DataSnap サーバーに接続することができません。デフォルトでは、両方の実装は、”valid” に “true” を設定します。これは、事実上、何も制限を実装していない状態で、クライアントはサーバーに接続でき、サーバーメソッドを呼び出すことができます。つまり、何もセキュリティがない – すべてのクライアントが、すべてのサーバーメソッドへのフルアクセスを持っています。

認証されたユーザーにロールを追加することから始めましょう。実装をシンプルにするために、 “Authenticate” イベントに渡される “User” パラメータの値のみをチェックします。“User” プロパティが空の場合、すべてのサーバーメソッドの呼び出しを防止するために “valid” パラメータに “false” を設定します。それ以外の場合は、“valid” パラメータに “True” を設定します。もし、サーバーメソッドのいずれかを呼び出したいのであれば、空でないユーザー名を渡す必要があります。

以上が最初のステップです。次のステップは、ロールを認証されたユーザーに追加することです。特別なロール: “admins” を定義します。すべての認証されたユーザーは、ユーザー名が “admin” であれば、“admins” ロールに追加されます。

最後の作業は、事実上セキュリティの設定をオーバライドし、あらゆる認証済みユーザーが任意のサーバーメソッドを呼び出せるようにしている “DSAuthenticationManager1UserAuthorize” イベントの実装を削除して保存するだけです。イベントの削除は、たった 1行の “value := True” を削除するだけです。すべての空のイベントは、自動的に削除されます。

“DSAuthenticationManager1UserAuthorize” イベントを削除し、以下のように、認証イベントを修正しました:

procedure TServerContainer2.DSAuthenticationManager1UserAuthenticate(
  Sender: TObject; const Protocol, Context, User, Password: string;
  var valid: Boolean; UserRoles: TStrings);
begin
  valid := User <> '';

  if User = 'admin' then
    UserRoles.Add('admins');
end;

サーバーメソッドへロールベースの実装を行う2つの方法があります。 “DSAuthenticationManager.Roles” プロパティを使用するか、デザイン時にオブジェクトインスペクタでパーミッションを設定するかのいずれかです。または、コード内で “TRoleAuth” のカスタム属性を付加したサーバーメソッドクラスやサーバーメソッドを用いても同様のことが可能です。

この例では、後者のアプローチを使って、コードにカスタム属性を使用する方法を行います。

エディタで、ServerMethodsUnit を開き、interface の uses 句に “DSAuth” ユニット (ここで “TRoleAuth” カスタム属性が実装されています) を追加します。これで、この属性を個々のサーバーメソッド (または、サーバークラス全体) に追加することができます。 “TRoleAuth” 属性の最初のパラメータは、 “AuthorizedRoles” として文字列を受け取ります。そして、2番目は、オプションのパラメーターとして “DeniedRoles” を含んでいます。デモの目的の為に、 “ReverseString” メソッドへのアクセスを制限し、“admins” ユーザーだけが、このメソッドを呼び出すことができるようにします。

ここに ServerMethodsUnit の interface 節を更新したものがあります。実装はそのままです。

unit ServerMethodsUnit2;

interface

uses
  SysUtils, Classes, DSServer, DSAuth; // <- added “DSAuth” unit

type
{$METHODINFO ON}
  // [TRoleAuth('admins')]   // <- it is also possible to decorate the whole class instead of individual methods
  TServerMethods2 = class(TComponent)
  private
    { Private declarations }
  public
    { Public declarations }
    function EchoString(Value: string): string;

    [TRoleAuth('admins')]   // <- custom attribute
    function ReverseString(Value: string): string;
  end;
{$METHODINFO OFF}

implementation

uses StrUtils;

function TServerMethods2.EchoString(Value: string): string;
begin
  Result := Value + ' ' + Value;
end;

function TServerMethods2.ReverseString(Value: string): string;
begin
  Result := StrUtils.ReverseString(Value);
end;

end.

ロールベースのセキュリティを搭載した DataSnap サーバーの準備が整いました。クライアントの実装を行う番です。

プロジェクトマネージャーのプロジェクトグループのノード上で、マウスの右ボタンをクリックし、「新規プロジェクトを追加」を選択します。表示された「新規作成」ダイアログの「Delphi プロジェクト」カテゴリから「VCL フォームアプリケーション」を選択し、[すべて保存]します。サーバープロジェクトと同じフォルダ (例えば、“C:\DataSnapLabs\AuthenticationAndAuthorization\” フォルダ) 内に新しいファイルを保存するのがベストです。クライアントのメインフォームユニットには “FormClientMain”、プロジェクトには “SecureDSClient”、プロジェクトグループには “SecureDS” と命名します。

クライアントの実装の前に、サーバーは実行されている必要があります。プロジェクトグループ上でマウスの右ボタンをクリックして「すべてビルド」を選択します。サーバープロジェクトがアクティブである (プロジェクトマネージャー内で名前が「太字」で表示されている) ことを確認して、メニューの [実行|デバッガを使わずに実行] を選択します。サーバーのウィンドウは最小化しておきます。クライアントの開発の間、サーバーは実行されている必要があります。

    クライアントの構築

この段階で、プロジェクトグループは、サーバーとクライアントプロジェクトで構成されています。クライアントの実装のために、サーバーが実行中である必要があります。クライアントに「DataSnap クライアントモジュール」を追加することから始めましょう。

プロジェクトマネージャーで、クライアントプロジェクトをアクティブにして、メニューの [ファイル|新規作成|その他]をクリックし、「Delphi プロジェクト」の 「DataSnap Server」カテゴリから、「DataSnap クライアントモジュール」を選択します。

Hide image
DataSnap10

ウィザードの最初の画面の DataSnap サーバーの場所は、デフォルトの「ローカルサーバー」のままとします。

2番目の画面の DataSnap サーバープロジェクトの種類は、デフォルトの「DataSnap スタンドアロンサーバー」のままとします。

3番目の画面の接続プロトコルは、デフォルトの「TCP/IP」のままとします。

最後のウィザードの4番目の画面では、サーバーへの接続をテストすることができます。デフォルトのユーザー名とパスワードは空白です。空白のユーザー名で [接続テスト] のボタンをクリックすると、以下のエラーが表示されます:

Hide image
Click to see full-sized image

expand view>>

これは期待された動作です!空の「ユーザー名」は、サーバーへの接続を認証されません。

別のユーザー名を入力します (ただし、“admin” は使用しないで下さい)。例えば “guest” と入力し、[接続テスト] のボタンを再びクリックします。

この場合、サーバーへ正常に接続することができます。

Hide image
Click to see full-sized image

expand view>>

[完了] ボタンを押して、ウィザードを終了します。メニューの [ファイル|すべて保存] を選択し、ウィザードで追加された2つの新しいユニットをデフォルトの名前のままで保存します。通常、“ClientClassesUnit1” および “ClientModuleUnit1” です。

今度は、クライアントアプリケーションの為の単純なユーザーインターフェースを構築します。

クライアント (FormClientMain) フォーム上に2つの Buttonコンポーネントと 2つの Editコンポーネントを置き、それぞれに “ButtonEcho”, “EditEcho”, “ButtonReverse”, “EditReverse” と名前をつけます。

メニューの [ファイル|ユニットを使う] を選択し、ウィザードによって生成された両方のファイルをクライアントフォームに追加します。そして、両方のボタンの “OnClick” イベントハンドラを実装します。

クライアントフォームの実装は、次のとおりです:

// …

implementation

uses ClientClassesUnit1, ClientModuleUnit1;

{$R *.dfm}

procedure TForm3.ButtonReverseClick(Sender: TObject);
begin
  EditReverse.Text :=
    ClientModule1.ServerMethods2Client.ReverseString(EditReverse.Text);
end;

procedure TForm3.ButtonEchoClick(Sender: TObject);
begin
  EditEcho.Text :=
    ClientModule1.ServerMethods2Client.EchoString(EditEcho.Text);
end;

end.

クライアントを実行し、その機能をテストします。

[Echo] ボタンをクリックすると、“エコー” Edit 内に表示された呼び出し結果が確認できるはずです。

[Reverse] ボタンをクリックすると、ユーザーの名前が入ったエラーメッセージ、たとえば「“guest” は要求されたアクションを実行する権限がありません」を受け取るはずです。

Hide image
DataSnap44

これは、まさに我々が期待していた動作です!

クライアントアプリケーションに、DataSnap クライアントモジュールを追加する過程で、ウィザードは、生成されたコード内にユーザー名とパスワードを埋め込みます。

ClientModuleUnit1 を開き、フォームデザイナ内の “SQLConnection1” コンポーネントを選択します。オブジェクトインスペクタで “params” プロパティを開くと、そこに保存されているポート番号、ユーザー名、パスワードを確認できます。

Hide image
DataSnap45

“DSAuthenticationUser” の値を “admin” に変更して、再実行すると、両方のサーバーメソッドを呼び出すことができます。

    まとめ

この “Delphi Labs” では、新しく DataSnap XE でサポートされた認証と承認を紹介しました。

この例の中で、TCP/IP で通信するテストサーバーとクライアントを構築しました。サーバーに、空のユーザー名からの接続を防止する認証ポリシーを実装します。ロールベースのセキュリティを実装し、 “admin” という名前のユーザーのみ、 “ReverseString” サーバーメソッドにアクセスできるように作成しました。

この記事のソースコードは、Code Central (http://cc.embarcadero.com/item/28226) にあります。そして、このデモのビデオ版は、YouTube (http://www.youtube.com/watch?v=t87WTYgvU18 および http://www.youtube.com/watch?v=0EzQrWvfKmM) で公開しています。

"Delphi Labs" に関する詳細情報は、http://blogs.embarcadero.com/pawelglowacki にて公開しています。

Server Response from: ETNASC01